SSL/TLS: Add option to disable reverse DNS resolution of hostname

This change adds the option to disable reverse DNS lookup of a hostname from
an IP address. This is needed if only an IP address is found in a SSL certificate
and hostname verification is enabled.

Closes elastic/elasticsearch#575

Original commit: elastic/x-pack-elasticsearch@07356bc885
This commit is contained in:
jaymode 2015-01-19 10:36:06 -05:00
parent 02682ff4ec
commit 166514651a
5 changed files with 130 additions and 9 deletions

View File

@ -29,6 +29,7 @@ import java.net.InetSocketAddress;
public class NettySecuredTransport extends NettyTransport {
public static final String HOSTNAME_VERIFICATION_SETTING = "shield.ssl.hostname_verification";
public static final String HOSTNAME_VERIFICATION_RESOLVE_NAME_SETTING = "shield.ssl.hostname_verification.resolve_name";
private final SSLService sslService;
private final @Nullable IPFilter authenticator;
@ -112,9 +113,7 @@ public class NettySecuredTransport extends NettyTransport {
SSLEngine sslEngine;
if (settings.getAsBoolean(HOSTNAME_VERIFICATION_SETTING, true)) {
InetSocketAddress inetSocketAddress = (InetSocketAddress) e.getValue();
String hostname = inetSocketAddress.getHostName();
int port = inetSocketAddress.getPort();
sslEngine = sslService.createSSLEngine(ImmutableSettings.EMPTY, hostname, port);
sslEngine = sslService.createSSLEngine(ImmutableSettings.EMPTY, getHostname(inetSocketAddress), inetSocketAddress.getPort());
SSLParameters parameters = new SSLParameters();
parameters.setEndpointIdentificationAlgorithm("HTTPS");
sslEngine.setSSLParameters(parameters);
@ -128,6 +127,20 @@ public class NettySecuredTransport extends NettyTransport {
ctx.sendDownstream(e);
}
private String getHostname(InetSocketAddress inetSocketAddress) {
String hostname;
if (settings.getAsBoolean(HOSTNAME_VERIFICATION_RESOLVE_NAME_SETTING, true)) {
hostname = inetSocketAddress.getHostName();
} else {
hostname = inetSocketAddress.getHostString();
}
if (logger.isTraceEnabled()) {
logger.trace("resolved hostname [{}] for address [{}] to be used in ssl hostname verification", hostname, inetSocketAddress);
}
return hostname;
}
}
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.shield.transport.netty;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ElasticsearchIntegrationTest;
import org.elasticsearch.test.ElasticsearchIntegrationTest.ClusterScope;
import org.elasticsearch.test.ShieldIntegrationTest;
import org.junit.Test;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
import static org.hamcrest.CoreMatchers.is;
@ClusterScope(scope = ElasticsearchIntegrationTest.Scope.SUITE)
public class IPHostnameVerificationIntegrationTests extends ShieldIntegrationTest {
static Path keystore;
@Override
protected boolean sslTransportEnabled() {
return true;
}
@Override
protected Settings nodeSettings(int nodeOrdinal) {
Settings settings = super.nodeSettings(nodeOrdinal);
// The default Unicast test behavior is to use 'localhost' with the port number. For this test we need to use IP
String[] unicastAddresses = settings.getAsArray("discovery.zen.ping.unicast.hosts");
for (int i = 0; i < unicastAddresses.length; i++) {
String address = unicastAddresses[i];
unicastAddresses[i] = address.replace("localhost", "127.0.0.1");
}
ImmutableSettings.Builder settingsBuilder = settingsBuilder()
.put(settings)
.putArray("discovery.zen.ping.unicast.hosts", unicastAddresses);
try {
//This keystore uses a cert with a CN of "Elasticsearch Test Node" and IPv4+IPv6 ip addresses as SubjectAlternativeNames
keystore = Paths.get(getClass().getResource("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode-ip-only.jks").toURI());
assertThat(Files.exists(keystore), is(true));
} catch (Exception e) {
throw new RuntimeException(e);
}
return settingsBuilder.put("shield.ssl.keystore.path", keystore.toAbsolutePath()) // settings for client truststore
.put("shield.ssl.keystore.password", "testnode-ip-only")
.put("shield.ssl.truststore.path", keystore.toAbsolutePath()) // settings for client truststore
.put("shield.ssl.truststore.password", "testnode-ip-only")
.put("transport.host", "127.0.0.1")
.put("network.host", "127.0.0.1")
.put("shield.ssl.client.auth", "false")
.put(NettySecuredTransport.HOSTNAME_VERIFICATION_SETTING, true)
.put(NettySecuredTransport.HOSTNAME_VERIFICATION_RESOLVE_NAME_SETTING, false)
.build();
}
@Override
protected Settings transportClientSettings() {
return ImmutableSettings.builder().put(super.transportClientSettings())
.put(NettySecuredTransport.HOSTNAME_VERIFICATION_SETTING, true)
.put(NettySecuredTransport.HOSTNAME_VERIFICATION_RESOLVE_NAME_SETTING, false)
.put("shield.ssl.keystore.path", keystore.toAbsolutePath())
.put("shield.ssl.keystore.password", "testnode-ip-only")
.put("shield.ssl.truststore.path", keystore.toAbsolutePath())
.put("shield.ssl.truststore.password", "testnode-ip-only")
.build();
}
@Test
public void testTransportClientConnectionWorksWithIPOnlyHostnameVerification() throws Exception {
Client client = internalCluster().transportClient();
assertGreenClusterState(client);
}
}

View File

@ -67,6 +67,7 @@ public class ShieldSettingsSource extends ClusterDiscoveryConfiguration.UnicastZ
private final byte[] systemKey;
private final boolean sslTransportEnabled;
private final boolean hostnameVerificationEnabled;
private final boolean hostnameVerificationResolveNameEnabled;
/**
* Creates a new {@link org.elasticsearch.test.SettingsSource} for the shield configuration.
@ -85,7 +86,8 @@ public class ShieldSettingsSource extends ClusterDiscoveryConfiguration.UnicastZ
this.parentFolder = parentFolder;
this.subfolderPrefix = scope.name();
this.sslTransportEnabled = sslTransportEnabled;
hostnameVerificationEnabled = randomBoolean();
this.hostnameVerificationEnabled = randomBoolean();
this.hostnameVerificationResolveNameEnabled = randomBoolean();
}
@Override
@ -190,11 +192,11 @@ public class ShieldSettingsSource extends ClusterDiscoveryConfiguration.UnicastZ
}
private Settings getNodeSSLSettings() {
return getSSLSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks", "testnode", sslTransportEnabled, hostnameVerificationEnabled);
return getSSLSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testnode.jks", "testnode", sslTransportEnabled, hostnameVerificationEnabled, hostnameVerificationResolveNameEnabled);
}
private Settings getClientSSLSettings() {
return getSSLSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.jks", "testclient", sslTransportEnabled, hostnameVerificationEnabled);
return getSSLSettingsForStore("/org/elasticsearch/shield/transport/ssl/certs/simple/testclient.jks", "testclient", sslTransportEnabled, hostnameVerificationEnabled, hostnameVerificationResolveNameEnabled);
}
/**
@ -205,10 +207,10 @@ public class ShieldSettingsSource extends ClusterDiscoveryConfiguration.UnicastZ
* @return the configuration settings
*/
public static Settings getSSLSettingsForStore(String resourcePathToStore, String password) {
return getSSLSettingsForStore(resourcePathToStore, password, true, true);
return getSSLSettingsForStore(resourcePathToStore, password, true, true, true);
}
private static Settings getSSLSettingsForStore(String resourcePathToStore, String password, boolean sslTransportEnabled, boolean hostnameVerificationEnabled) {
private static Settings getSSLSettingsForStore(String resourcePathToStore, String password, boolean sslTransportEnabled, boolean hostnameVerificationEnabled, boolean hostnameVerificationResolveNameEnabled) {
File store;
try {
store = new File(ShieldSettingsSource.class.getResource(resourcePathToStore).toURI());
@ -227,7 +229,8 @@ public class ShieldSettingsSource extends ClusterDiscoveryConfiguration.UnicastZ
if (sslTransportEnabled) {
builder.put("shield.ssl.keystore.path", store.getPath())
.put("shield.ssl.keystore.password", password)
.put(NettySecuredTransport.HOSTNAME_VERIFICATION_SETTING, hostnameVerificationEnabled);
.put(NettySecuredTransport.HOSTNAME_VERIFICATION_SETTING, hostnameVerificationEnabled)
.put(NettySecuredTransport.HOSTNAME_VERIFICATION_RESOLVE_NAME_SETTING, hostnameVerificationResolveNameEnabled);
}
if (sslTransportEnabled && randomBoolean()) {

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDYjCCAkqgAwIBAgIJAKP3WbDN1WxEMA0GCSqGSIb3DQEBCwUAMEgxDDAKBgNV
BAoTA29yZzEWMBQGA1UECxMNZWxhc3RpY3NlYXJjaDEgMB4GA1UEAxMXRWxhc3Rp
Y3NlYXJjaCBUZXN0IE5vZGUwHhcNMTUwMTE5MTQwNDE3WhcNMTkwMTE4MTQwNDE3
WjBIMQwwCgYDVQQKEwNvcmcxFjAUBgNVBAsTDWVsYXN0aWNzZWFyY2gxIDAeBgNV
BAMTF0VsYXN0aWNzZWFyY2ggVGVzdCBOb2RlMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEA3a5+yBl2rVEGwlOjw6Ji43+iqvaAbmVhnCk6laEa3GpzothX
7HhtGGDfjdhaLzWF5PWP8SvMM8g4f1PLN0hGSR7vrWjlnpvUDXIHsoIRqWfYdwDA
RNiUvOI4FBjN4pZ4sXyUYsTpw80l6W0r3zopyycE4+4HJv55U1Yy2/3qzv1IITqD
LwRt6VpbPGVyzDSBMQXEgfT7sfaJB9Ru+A/onIpEicrWhgCPHrBnSUkKCKNj9AX/
RV6/yQYnS/KhLx/eQTP7NVcbrC2J4fFOLX9oZAj6dir/tYQ6rDAMqBTnbhGygDqP
0RgCVf82n6mA23n7l5DaZ4RZl+ssN3fNqDyDpQIDAQABo08wTTAJBgNVHRMEAjAA
MB0GA1UdDgQWBBSDFYaN/Od9ad7Kztv6cGjd2X4w1TAhBgNVHREEGjAYhwR/AAAB
hxAAAAAAAAAAAAAAAAAAAAACMA0GCSqGSIb3DQEBCwUAA4IBAQCbxk4VHMcdD2yU
VpLSBxHBdWY/Gn3f7k0WWhQAmRPR+S6vSr89hVO8UIkqEFzc+D19s9h0XvAmo2QO
G80OLTcHIjxiVqAVWoGUPH4D7FdG7sSBbrJbweIBMW8Ba4kefWGcI0KlUWTssFyE
YLJQIUCIdKtVf/qmcItvrEXw8ucSYaExvizMClTvf2fZxRws8Omo3dxn+ifUH5nn
evQW8Tawlx2ql5P6wHTSUclGLv1CXtMAnuOlyaYY/UbslNhSXigxYOZCI3ole3qL
Z0dBah+eCspOit14mi7jHPoS1Yji/CSh33KZD6ZESuv/V1B7oy6zZWei/b4vllW5
RwZx1Y/r
-----END CERTIFICATE-----