Skip to content

Commit 012746d

Browse files
authored
Send hostname in SNI header in simple remote mode (#50247)
Currently an intermediate proxy must route conncctions to the appropriate remote cluster when using simple mode. This commit offers a additional mechanism for the proxy to route the connections by including the hostname in the TLS SNI header.
1 parent 114d8ef commit 012746d

File tree

7 files changed

+82
-11
lines changed

7 files changed

+82
-11
lines changed

server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java

+1
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ public void apply(Settings value, Settings current, Settings previous) {
295295
RemoteConnectionStrategy.REMOTE_CONNECTION_MODE,
296296
SimpleConnectionStrategy.REMOTE_CLUSTER_ADDRESSES,
297297
SimpleConnectionStrategy.REMOTE_SOCKET_CONNECTIONS,
298+
SimpleConnectionStrategy.INCLUDE_SERVER_NAME,
298299
SniffConnectionStrategy.REMOTE_CLUSTER_SEEDS_OLD,
299300
SniffConnectionStrategy.REMOTE_CLUSTERS_PROXY,
300301
SniffConnectionStrategy.REMOTE_CLUSTER_SEEDS,

server/src/main/java/org/elasticsearch/common/settings/Setting.java

+4
Original file line numberDiff line numberDiff line change
@@ -1257,6 +1257,10 @@ public static Setting<Boolean> boolSetting(String key, Setting<Boolean> fallback
12571257
return new Setting<>(key, fallbackSetting, b -> parseBoolean(b, key, isFiltered(properties)), properties);
12581258
}
12591259

1260+
public static Setting<Boolean> boolSetting(String key, boolean defaultValue, Validator<Boolean> validator, Property... properties) {
1261+
return new Setting<>(key, Boolean.toString(defaultValue), b -> parseBoolean(b, key, isFiltered(properties)), validator, properties);
1262+
}
1263+
12601264
public static Setting<Boolean> boolSetting(String key, Function<Settings, String> defaultValueFn, Property... properties) {
12611265
return new Setting<>(key, defaultValueFn, b -> parseBoolean(b, key, isFiltered(properties)), properties);
12621266
}

server/src/main/java/org/elasticsearch/transport/RemoteClusterAware.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ public void listenForUpdates(ClusterSettings clusterSettings) {
111111
SniffConnectionStrategy.REMOTE_CLUSTER_SEEDS_OLD,
112112
SniffConnectionStrategy.REMOTE_NODE_CONNECTIONS,
113113
SimpleConnectionStrategy.REMOTE_CLUSTER_ADDRESSES,
114-
SimpleConnectionStrategy.REMOTE_SOCKET_CONNECTIONS);
114+
SimpleConnectionStrategy.REMOTE_SOCKET_CONNECTIONS,
115+
SimpleConnectionStrategy.INCLUDE_SERVER_NAME);
115116
clusterSettings.addAffixGroupUpdateConsumer(remoteClusterSettings, this::validateAndUpdateRemoteCluster);
116117
}
117118

server/src/main/java/org/elasticsearch/transport/RemoteConnectionStrategy.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ private static <T> Stream<String> getClusterAlias(Settings settings, Setting.Aff
170170
return allConcreteSettings.map(affixSetting::getNamespace);
171171
}
172172

173-
static InetSocketAddress parseSeedAddress(String remoteHost) {
173+
static InetSocketAddress parseConfiguredAddress(String remoteHost) {
174174
final Tuple<String, Integer> hostPort = parseHostPort(remoteHost);
175175
final String host = hostPort.v1();
176176
assert hostPort.v2() != null : remoteHost;

server/src/main/java/org/elasticsearch/transport/SimpleConnectionStrategy.java

+35-5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.elasticsearch.action.ActionListener;
2727
import org.elasticsearch.cluster.ClusterName;
2828
import org.elasticsearch.cluster.node.DiscoveryNode;
29+
import org.elasticsearch.cluster.node.DiscoveryNodeRole;
2930
import org.elasticsearch.common.io.stream.StreamInput;
3031
import org.elasticsearch.common.io.stream.StreamOutput;
3132
import org.elasticsearch.common.io.stream.Writeable;
@@ -40,6 +41,7 @@
4041
import java.util.Collections;
4142
import java.util.HashSet;
4243
import java.util.List;
44+
import java.util.Map;
4345
import java.util.Objects;
4446
import java.util.Set;
4547
import java.util.concurrent.atomic.AtomicInteger;
@@ -49,6 +51,7 @@
4951
import java.util.stream.Collectors;
5052
import java.util.stream.Stream;
5153

54+
import static org.elasticsearch.common.settings.Setting.boolSetting;
5255
import static org.elasticsearch.common.settings.Setting.intSetting;
5356

5457
public class SimpleConnectionStrategy extends RemoteConnectionStrategy {
@@ -76,6 +79,15 @@ public class SimpleConnectionStrategy extends RemoteConnectionStrategy {
7679
(ns, key) -> intSetting(key, 18, 1, new StrategyValidator<>(ns, key, ConnectionStrategy.SIMPLE),
7780
Setting.Property.Dynamic, Setting.Property.NodeScope));
7881

82+
/**
83+
* Whether to include the hostname as a server_name attribute
84+
*/
85+
public static final Setting.AffixSetting<Boolean> INCLUDE_SERVER_NAME = Setting.affixKeySetting(
86+
"cluster.remote.",
87+
"simple.include_server_name",
88+
(ns, key) -> boolSetting(key, false, new StrategyValidator<>(ns, key, ConnectionStrategy.SIMPLE),
89+
Setting.Property.Dynamic, Setting.Property.NodeScope));
90+
7991
static final int CHANNELS_PER_CONNECTION = 1;
8092

8193
private static final int MAX_CONNECT_ATTEMPTS_PER_RUN = 3;
@@ -84,6 +96,7 @@ public class SimpleConnectionStrategy extends RemoteConnectionStrategy {
8496
private final int maxNumConnections;
8597
private final AtomicLong counter = new AtomicLong(0);
8698
private final List<String> configuredAddresses;
99+
private final boolean includeServerName;
87100
private final List<Supplier<TransportAddress>> addresses;
88101
private final AtomicReference<ClusterName> remoteClusterName = new AtomicReference<>();
89102
private final ConnectionProfile profile;
@@ -96,21 +109,31 @@ public class SimpleConnectionStrategy extends RemoteConnectionStrategy {
96109
transportService,
97110
connectionManager,
98111
REMOTE_SOCKET_CONNECTIONS.getConcreteSettingForNamespace(clusterAlias).get(settings),
99-
REMOTE_CLUSTER_ADDRESSES.getConcreteSettingForNamespace(clusterAlias).get(settings));
112+
REMOTE_CLUSTER_ADDRESSES.getConcreteSettingForNamespace(clusterAlias).get(settings),
113+
INCLUDE_SERVER_NAME.getConcreteSettingForNamespace(clusterAlias).get(settings));
100114
}
101115

102116
SimpleConnectionStrategy(String clusterAlias, TransportService transportService, RemoteConnectionManager connectionManager,
103117
int maxNumConnections, List<String> configuredAddresses) {
104118
this(clusterAlias, transportService, connectionManager, maxNumConnections, configuredAddresses,
105119
configuredAddresses.stream().map(address ->
106-
(Supplier<TransportAddress>) () -> resolveAddress(address)).collect(Collectors.toList()));
120+
(Supplier<TransportAddress>) () -> resolveAddress(address)).collect(Collectors.toList()), false);
107121
}
108122

109123
SimpleConnectionStrategy(String clusterAlias, TransportService transportService, RemoteConnectionManager connectionManager,
110-
int maxNumConnections, List<String> configuredAddresses, List<Supplier<TransportAddress>> addresses) {
124+
int maxNumConnections, List<String> configuredAddresses, boolean includeServerName) {
125+
this(clusterAlias, transportService, connectionManager, maxNumConnections, configuredAddresses,
126+
configuredAddresses.stream().map(address ->
127+
(Supplier<TransportAddress>) () -> resolveAddress(address)).collect(Collectors.toList()), includeServerName);
128+
}
129+
130+
SimpleConnectionStrategy(String clusterAlias, TransportService transportService, RemoteConnectionManager connectionManager,
131+
int maxNumConnections, List<String> configuredAddresses, List<Supplier<TransportAddress>> addresses,
132+
boolean includeServerName) {
111133
super(clusterAlias, transportService, connectionManager);
112134
this.maxNumConnections = maxNumConnections;
113135
this.configuredAddresses = configuredAddresses;
136+
this.includeServerName = includeServerName;
114137
assert addresses.isEmpty() == false : "Cannot use simple connection strategy with no configured addresses";
115138
this.addresses = addresses;
116139
// TODO: Move into the ConnectionManager
@@ -207,7 +230,14 @@ public void onFailure(Exception e) {
207230
for (int i = 0; i < remaining; ++i) {
208231
TransportAddress address = nextAddress(resolved);
209232
String id = clusterAlias + "#" + address;
210-
DiscoveryNode node = new DiscoveryNode(id, address, Version.CURRENT.minimumCompatibilityVersion());
233+
Map<String, String> attributes;
234+
if (includeServerName) {
235+
attributes = Collections.singletonMap("server_name", address.address().getHostString());
236+
} else {
237+
attributes = Collections.emptyMap();
238+
}
239+
DiscoveryNode node = new DiscoveryNode(id, address, attributes, DiscoveryNodeRole.BUILT_IN_ROLES,
240+
Version.CURRENT.minimumCompatibilityVersion());
211241

212242
connectionManager.connectToNode(node, profile, clusterNameValidator, new ActionListener<>() {
213243
@Override
@@ -243,7 +273,7 @@ private TransportAddress nextAddress(List<TransportAddress> resolvedAddresses) {
243273
}
244274

245275
private static TransportAddress resolveAddress(String address) {
246-
return new TransportAddress(parseSeedAddress(address));
276+
return new TransportAddress(parseConfiguredAddress(address));
247277
}
248278

249279
private boolean addressesChanged(final List<String> oldAddresses, final List<String> newAddresses) {

server/src/main/java/org/elasticsearch/transport/SniffConnectionStrategy.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -425,11 +425,11 @@ public String toString() {
425425

426426
private static DiscoveryNode resolveSeedNode(String clusterAlias, String address, String proxyAddress) {
427427
if (proxyAddress == null || proxyAddress.isEmpty()) {
428-
TransportAddress transportAddress = new TransportAddress(parseSeedAddress(address));
428+
TransportAddress transportAddress = new TransportAddress(parseConfiguredAddress(address));
429429
return new DiscoveryNode(clusterAlias + "#" + transportAddress.toString(), transportAddress,
430430
Version.CURRENT.minimumCompatibilityVersion());
431431
} else {
432-
TransportAddress transportAddress = new TransportAddress(parseSeedAddress(proxyAddress));
432+
TransportAddress transportAddress = new TransportAddress(parseConfiguredAddress(proxyAddress));
433433
String hostName = address.substring(0, indexOfPortSeparator(address));
434434
return new DiscoveryNode("", clusterAlias + "#" + address, UUIDs.randomBase64UUID(), hostName, address,
435435
transportAddress, Collections.singletonMap("server_name", hostName), DiscoveryNodeRole.BUILT_IN_ROLES,
@@ -460,7 +460,7 @@ private static DiscoveryNode maybeAddProxyAddress(String proxyAddress, Discovery
460460
return node;
461461
} else {
462462
// resolve proxy address lazy here
463-
InetSocketAddress proxyInetAddress = parseSeedAddress(proxyAddress);
463+
InetSocketAddress proxyInetAddress = parseConfiguredAddress(proxyAddress);
464464
return new DiscoveryNode(node.getName(), node.getId(), node.getEphemeralId(), node.getHostName(), node
465465
.getHostAddress(), new TransportAddress(proxyInetAddress), node.getAttributes(), node.getRoles(), node.getVersion());
466466
}

server/src/test/java/org/elasticsearch/transport/SimpleConnectionStrategyTests.java

+36-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.elasticsearch.Version;
2323
import org.elasticsearch.action.support.PlainActionFuture;
2424
import org.elasticsearch.cluster.ClusterName;
25+
import org.elasticsearch.cluster.node.DiscoveryNode;
2526
import org.elasticsearch.common.Strings;
2627
import org.elasticsearch.common.collect.Tuple;
2728
import org.elasticsearch.common.settings.AbstractScopedSettings;
@@ -35,6 +36,7 @@
3536
import org.elasticsearch.threadpool.TestThreadPool;
3637
import org.elasticsearch.threadpool.ThreadPool;
3738

39+
import java.util.ArrayList;
3840
import java.util.Arrays;
3941
import java.util.Collections;
4042
import java.util.HashSet;
@@ -291,7 +293,7 @@ public void testSimpleStrategyWillResolveAddressesEachConnect() throws Exception
291293
int numOfConnections = randomIntBetween(4, 8);
292294
try (RemoteConnectionManager remoteConnectionManager = new RemoteConnectionManager(clusterAlias, connectionManager);
293295
SimpleConnectionStrategy strategy = new SimpleConnectionStrategy(clusterAlias, localService, remoteConnectionManager,
294-
numOfConnections, addresses(address), Collections.singletonList(addressSupplier))) {
296+
numOfConnections, addresses(address), Collections.singletonList(addressSupplier), false)) {
295297
PlainActionFuture<Void> connectFuture = PlainActionFuture.newFuture();
296298
strategy.connect(connectFuture);
297299
connectFuture.actionGet();
@@ -387,6 +389,39 @@ public void testModeSettingsCannotBeUsedWhenInDifferentMode() {
387389
}
388390
}
389391

392+
public void testServerNameAttributes() {
393+
Settings bindSettings = Settings.builder().put(TransportSettings.BIND_HOST.getKey(), "localhost").build();
394+
try (MockTransportService transport1 = startTransport("node1", Version.CURRENT, bindSettings)) {
395+
TransportAddress address1 = transport1.boundAddress().publishAddress();
396+
397+
try (MockTransportService localService = MockTransportService.createNewService(Settings.EMPTY, Version.CURRENT, threadPool)) {
398+
localService.start();
399+
localService.acceptIncomingRequests();
400+
401+
ArrayList<String> addresses = new ArrayList<>();
402+
addresses.add("localhost:" + address1.getPort());
403+
404+
ConnectionManager connectionManager = new ConnectionManager(profile, localService.transport);
405+
int numOfConnections = randomIntBetween(4, 8);
406+
try (RemoteConnectionManager remoteConnectionManager = new RemoteConnectionManager(clusterAlias, connectionManager);
407+
SimpleConnectionStrategy strategy = new SimpleConnectionStrategy(clusterAlias, localService, remoteConnectionManager,
408+
numOfConnections, addresses, true)) {
409+
assertFalse(connectionManager.getAllConnectedNodes().stream().anyMatch(n -> n.getAddress().equals(address1)));
410+
411+
PlainActionFuture<Void> connectFuture = PlainActionFuture.newFuture();
412+
strategy.connect(connectFuture);
413+
connectFuture.actionGet();
414+
415+
assertTrue(connectionManager.getAllConnectedNodes().stream().anyMatch(n -> n.getAddress().equals(address1)));
416+
assertTrue(strategy.assertNoRunningConnections());
417+
418+
DiscoveryNode discoveryNode = connectionManager.getAllConnectedNodes().stream().findFirst().get();
419+
assertEquals("localhost", discoveryNode.getAttributes().get("server_name"));
420+
}
421+
}
422+
}
423+
}
424+
390425
private static List<String> addresses(final TransportAddress... addresses) {
391426
return Arrays.stream(addresses).map(TransportAddress::toString).collect(Collectors.toList());
392427
}

0 commit comments

Comments
 (0)