Skip to content

Commit

Permalink
Merge pull request #1001 from mattnelson/discovery_config_ssl_hostname
Browse files Browse the repository at this point in the history
Support configuration of custom SSLContext/HostnameVerifier
  • Loading branch information
qiangdavidliu authored Oct 27, 2017
2 parents d4fcefc + f10b374 commit 6efea5d
Show file tree
Hide file tree
Showing 12 changed files with 170 additions and 27 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ allprojects {
servletVersion = '2.5'
jerseyVersion = '1.19.1'
jettisonVersion = '1.3.7'
apacheHttpClientVersion = '4.3.4'
apacheHttpClientVersion = '4.4.1'
guiceVersion = '4.1.0'
servoVersion = '0.10.1'
governatorVersion = '1.12.10'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.netflix.discovery.shared.transport.jersey2;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
Expand All @@ -31,6 +32,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import com.netflix.appinfo.AbstractEurekaIdentity;
import com.netflix.appinfo.EurekaAccept;
Expand Down Expand Up @@ -79,13 +81,25 @@ public static Jersey2ApplicationClientFactory create(EurekaClientConfig clientCo
Collection<ClientRequestFilter> additionalFilters,
InstanceInfo myInstanceInfo,
AbstractEurekaIdentity clientIdentity) {
return create(clientConfig, additionalFilters, myInstanceInfo, clientIdentity, Optional.empty(), Optional.empty());
}

public static Jersey2ApplicationClientFactory create(EurekaClientConfig clientConfig,
Collection<ClientRequestFilter> additionalFilters,
InstanceInfo myInstanceInfo,
AbstractEurekaIdentity clientIdentity,
Optional<SSLContext> sslContext,
Optional<HostnameVerifier> hostnameVerifier) {
Jersey2ApplicationClientFactoryBuilder clientBuilder = newBuilder();
clientBuilder.withAdditionalFilters(additionalFilters);
clientBuilder.withMyInstanceInfo(myInstanceInfo);
clientBuilder.withUserAgent("Java-EurekaClient");
clientBuilder.withClientConfig(clientConfig);
clientBuilder.withClientIdentity(clientIdentity);

sslContext.ifPresent(clientBuilder::withSSLContext);
hostnameVerifier.ifPresent(clientBuilder::withHostnameVerifier);

if ("true".equals(System.getProperty("com.netflix.eureka.shouldSSLConnectionsUseSystemSocketFactory"))) {
clientBuilder.withClientName("DiscoveryClient-HTTPClient-System").withSystemSSLConfiguration();
} else if (clientConfig.getProxyHost() != null && clientConfig.getProxyPort() != null) {
Expand Down Expand Up @@ -137,6 +151,10 @@ public Jersey2ApplicationClientFactory build() {
addProviders(clientConfig);
addSSLConfiguration(clientBuilder);
addProxyConfiguration(clientConfig);

if (hostnameVerifier != null) {
clientBuilder.hostnameVerifier(hostnameVerifier);
}

// Common properties to all clients
final String fullUserAgentName = (userAgent == null ? clientName : userAgent) + "/v" + buildVersion();
Expand Down Expand Up @@ -182,6 +200,8 @@ private void addSSLConfiguration(ClientBuilder clientBuilder) {
FileInputStream fin = new FileInputStream(trustStoreFileName);
trustStore.load(fin, trustStorePassword.toCharArray());
clientBuilder.trustStore(trustStore);
} else if (sslContext != null) {
clientBuilder.sslContext(sslContext);
}
} catch (Exception ex) {
throw new IllegalArgumentException("Cannot setup SSL for Jersey2 client", ex);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.netflix.discovery.shared.transport.jersey2;

import java.util.Collection;
import java.util.Optional;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.ws.rs.client.ClientRequestFilter;

import com.netflix.appinfo.EurekaClientIdentity;
Expand All @@ -26,11 +29,20 @@ public static Jersey2TransportClientFactories getInstance() {
public TransportClientFactory newTransportClientFactory(final EurekaClientConfig clientConfig,
final Collection<ClientRequestFilter> additionalFilters,
final InstanceInfo myInstanceInfo) {
return newTransportClientFactory(clientConfig, additionalFilters, myInstanceInfo, Optional.empty(), Optional.empty());
}

@Override
public TransportClientFactory newTransportClientFactory(EurekaClientConfig clientConfig,
Collection<ClientRequestFilter> additionalFilters, InstanceInfo myInstanceInfo,
Optional<SSLContext> sslContext, Optional<HostnameVerifier> hostnameVerifier) {
final TransportClientFactory jerseyFactory = Jersey2ApplicationClientFactory.create(
clientConfig,
additionalFilters,
myInstanceInfo,
new EurekaClientIdentity(myInstanceInfo.getIPAddr(), "Jersey2DefaultClient")
new EurekaClientIdentity(myInstanceInfo.getIPAddr(), "Jersey2DefaultClient"),
sslContext,
hostnameVerifier
);
final TransportClientFactory metricsFactory = MetricsCollectingEurekaHttpClient.createFactory(jerseyFactory);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

import javax.inject.Provider;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;

import com.google.inject.Inject;
import com.netflix.appinfo.HealthCheckCallback;
Expand Down Expand Up @@ -35,6 +38,10 @@ public abstract class AbstractDiscoveryClientOptionalArgs<T> {

private Set<EurekaEventListener> eventListeners;

private Optional<SSLContext> sslContext = Optional.empty();

private Optional<HostnameVerifier> hostnameVerifier = Optional.empty();

@Inject(optional = true)
public void setEventListeners(Set<EurekaEventListener> listeners) {
if (eventListeners == null) {
Expand Down Expand Up @@ -95,4 +102,22 @@ public TransportClientFactories getTransportClientFactories() {
public void setTransportClientFactories(TransportClientFactories transportClientFactories) {
this.transportClientFactories = transportClientFactories;
}

public Optional<SSLContext> getSSLContext() {
return sslContext;
}

@Inject(optional = true)
public void setSSLContext(SSLContext sslContext) {
this.sslContext = Optional.of(sslContext);
}

public Optional<HostnameVerifier> getHostnameVerifier() {
return hostnameVerifier;
}

@Inject(optional = true)
public void setHostnameVerifier(HostnameVerifier hostnameVerifier) {
this.hostnameVerifier = Optional.of(hostnameVerifier);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
Expand All @@ -46,10 +46,10 @@
import javax.annotation.PreDestroy;
import javax.inject.Provider;
import javax.inject.Singleton;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.ws.rs.core.Response.Status;

import com.netflix.discovery.shared.transport.jersey.Jersey1DiscoveryClientOptionalArgs;
import com.netflix.discovery.shared.transport.jersey.Jersey1TransportClientFactories;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -76,6 +76,8 @@
import com.netflix.discovery.shared.transport.EurekaTransportConfig;
import com.netflix.discovery.shared.transport.TransportClientFactory;
import com.netflix.discovery.shared.transport.jersey.EurekaJerseyClient;
import com.netflix.discovery.shared.transport.jersey.Jersey1DiscoveryClientOptionalArgs;
import com.netflix.discovery.shared.transport.jersey.Jersey1TransportClientFactories;
import com.netflix.discovery.shared.transport.jersey.TransportClientFactories;
import com.netflix.discovery.util.ThresholdLevelsMetric;
import com.netflix.servo.annotations.DataSourceType;
Expand Down Expand Up @@ -470,10 +472,17 @@ private void scheduleServerEndpointTask(EurekaTransport eurekaTransport,
TransportClientFactories transportClientFactories = argsTransportClientFactories == null
? new Jersey1TransportClientFactories()
: argsTransportClientFactories;

Optional<SSLContext> sslContext = args == null
? Optional.empty()
: args.getSSLContext();
Optional<HostnameVerifier> hostnameVerifier = args == null
? Optional.empty()
: args.getHostnameVerifier();

// If the transport factory was not supplied with args, assume they are using jersey 1 for passivity
eurekaTransport.transportClientFactory = providedJerseyClient == null
? transportClientFactories.newTransportClientFactory(clientConfig, additionalFilters, applicationInfoManager.getInfo())
? transportClientFactories.newTransportClientFactory(clientConfig, additionalFilters, applicationInfoManager.getInfo(), sslContext, hostnameVerifier)
: transportClientFactories.newTransportClientFactory(additionalFilters, providedJerseyClient);

ApplicationsResolver.ApplicationsSource applicationsSource = new ApplicationsResolver.ApplicationsSource() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.netflix.discovery.shared.transport;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;

import com.netflix.appinfo.AbstractEurekaIdentity;
Expand Down Expand Up @@ -40,6 +41,7 @@ public abstract class EurekaClientFactoryBuilder<F, B extends EurekaClientFactor
protected EncoderWrapper encoderWrapper;
protected DecoderWrapper decoderWrapper;
protected AbstractEurekaIdentity clientIdentity;
protected HostnameVerifier hostnameVerifier;

public B withClientConfig(EurekaClientConfig clientConfig) {
withClientAccept(EurekaAccept.fromString(clientConfig.getClientDataAccept()));
Expand Down Expand Up @@ -116,6 +118,11 @@ public B withSSLContext(SSLContext sslContext) {
this.sslContext = sslContext;
return self();
}

public B withHostnameVerifier(HostnameVerifier hostnameVerifier) {
this.hostnameVerifier = hostnameVerifier;
return self();
}

/**
* Use {@link #withSSLContext(SSLContext)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.netflix.discovery.shared.transport.jersey;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
Expand Down Expand Up @@ -94,6 +95,7 @@ public static class EurekaJerseyClientBuilder {
private EncoderWrapper encoderWrapper;
private DecoderWrapper decoderWrapper;
private SSLContext sslContext;
private HostnameVerifier hostnameVerifier;

public EurekaJerseyClientBuilder withClientName(String clientName) {
this.clientName = clientName;
Expand Down Expand Up @@ -187,7 +189,7 @@ class MyDefaultApacheHttpClient4Config extends DefaultApacheHttpClient4Config {

if (systemSSL) {
cm = createSystemSslCM();
} else if (sslContext != null || trustStoreFileName != null) {
} else if (sslContext != null || hostnameVerifier != null || trustStoreFileName != null) {
cm = createCustomSslCM();
} else {
cm = createDefaultSslCM();
Expand Down Expand Up @@ -254,7 +256,11 @@ private MonitoredConnectionManager createCustomSslCM() {

sslContext.init(null, trustManagers, null);
}
X509HostnameVerifier hostnameVerifier = SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;

if (hostnameVerifier == null) {
hostnameVerifier = SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
}

SSLConnectionSocketFactory customSslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
SSLSocketFactory sslSocketFactory = new SSLSocketFactoryAdapter(customSslSocketFactory);
SchemeRegistry sslSchemeRegistry = new SchemeRegistry();
Expand Down Expand Up @@ -282,8 +288,16 @@ private MonitoredConnectionManager createDefaultSslCM() {
new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
registry.register(
new Scheme("https", 443, new SSLSocketFactoryAdapter(SSLConnectionSocketFactory.getSocketFactory())));

return new MonitoredConnectionManager(clientName, registry);
}
}

/**
* @param hostnameVerifier
*/
public void withHostnameVerifier(HostnameVerifier hostnameVerifier) {
this.hostnameVerifier = hostnameVerifier;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.netflix.discovery.shared.transport.jersey;

import java.util.Collection;
import java.util.Optional;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;

import com.netflix.appinfo.EurekaClientIdentity;
import com.netflix.appinfo.InstanceInfo;
Expand Down Expand Up @@ -45,12 +49,22 @@ public void shutdown() {
public TransportClientFactory newTransportClientFactory(final EurekaClientConfig clientConfig,
final Collection<ClientFilter> additionalFilters,
final InstanceInfo myInstanceInfo) {
return newTransportClientFactory(clientConfig, additionalFilters, myInstanceInfo, Optional.empty(), Optional.empty());
}

@Override
public TransportClientFactory newTransportClientFactory(EurekaClientConfig clientConfig,
Collection<ClientFilter> additionalFilters, InstanceInfo myInstanceInfo, Optional<SSLContext> sslContext,
Optional<HostnameVerifier> hostnameVerifier) {
final TransportClientFactory jerseyFactory = JerseyEurekaHttpClientFactory.create(
clientConfig,
additionalFilters,
myInstanceInfo,
new EurekaClientIdentity(myInstanceInfo.getIPAddr())
new EurekaClientIdentity(myInstanceInfo.getIPAddr()),
sslContext,
hostnameVerifier
);

final TransportClientFactory metricsFactory = MetricsCollectingEurekaHttpClient.createFactory(jerseyFactory);

return new TransportClientFactory() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;

import static com.netflix.discovery.util.DiscoveryBuildInfo.buildVersion;

Expand Down Expand Up @@ -108,17 +112,29 @@ public void shutdown() {
apacheClient.destroy();
}
}

public static JerseyEurekaHttpClientFactory create(EurekaClientConfig clientConfig,
Collection<ClientFilter> additionalFilters,
InstanceInfo myInstanceInfo,
AbstractEurekaIdentity clientIdentity) {
return create(clientConfig, additionalFilters, myInstanceInfo, clientIdentity, Optional.empty(), Optional.empty());
}

public static JerseyEurekaHttpClientFactory create(EurekaClientConfig clientConfig,
Collection<ClientFilter> additionalFilters,
InstanceInfo myInstanceInfo,
AbstractEurekaIdentity clientIdentity) {
AbstractEurekaIdentity clientIdentity,
Optional<SSLContext> sslContext,
Optional<HostnameVerifier> hostnameVerifier) {
JerseyEurekaHttpClientFactoryBuilder clientBuilder = newBuilder()
.withAdditionalFilters(additionalFilters)
.withMyInstanceInfo(myInstanceInfo)
.withUserAgent("Java-EurekaClient")
.withClientConfig(clientConfig)
.withClientIdentity(clientIdentity);

sslContext.ifPresent(clientBuilder::withSSLContext);
hostnameVerifier.ifPresent(clientBuilder::withHostnameVerifier);

if ("true".equals(System.getProperty("com.netflix.eureka.shouldSSLConnectionsUseSystemSocketFactory"))) {
clientBuilder.withClientName("DiscoveryClient-HTTPClient-System").withSystemSSLConfiguration();
Expand Down Expand Up @@ -192,6 +208,12 @@ private JerseyEurekaHttpClientFactory buildLegacy(Map<String, String> additional

if (systemSSL) {
clientBuilder.withSystemSSLConfiguration();
} else if (sslContext != null) {
clientBuilder.withCustomSSL(sslContext);
}

if (hostnameVerifier != null) {
clientBuilder.withHostnameVerifier(hostnameVerifier);
}

EurekaJerseyClient jerseyClient = clientBuilder.build();
Expand Down
Loading

0 comments on commit 6efea5d

Please sign in to comment.