Skip to content

Commit

Permalink
Provide TrustManager to OkHttpClient
Browse files Browse the repository at this point in the history
  - When loading from keystore.
  - When loading from the realmn data.
  • Loading branch information
josejulio committed Feb 24, 2017
1 parent 53e6e7b commit 1309eea
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 Red Hat, Inc. and/or its affiliates
* Copyright 2015-2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -17,18 +17,21 @@
package org.hawkular.agent.monitor.cmd;

import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;

import org.hawkular.agent.monitor.extension.MonitorServiceConfiguration.StorageAdapterConfiguration;
import org.hawkular.agent.monitor.util.BaseHttpClientGenerator;

public class WebSocketClientBuilder extends BaseHttpClientGenerator {

public WebSocketClientBuilder(StorageAdapterConfiguration storageAdapter, SSLContext sslContext) {
public WebSocketClientBuilder(StorageAdapterConfiguration storageAdapter, SSLContext sslContext,
X509TrustManager x509TrustManager) {
super(new BaseHttpClientGenerator.Configuration.Builder()
.username(storageAdapter.getUsername())
.password(storageAdapter.getPassword())
.useSsl(storageAdapter.isUseSSL())
.sslContext(sslContext)
.x509TrustManager(x509TrustManager)
.keystorePath(storageAdapter.getKeystorePath())
.keystorePassword(storageAdapter.getKeystorePassword())
.connectTimeout(storageAdapter.getConnectTimeoutSeconds())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
import java.util.concurrent.atomic.AtomicReference;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.hawkular.agent.monitor.api.Avail;
import org.hawkular.agent.monitor.api.HawkularWildFlyAgentContext;
Expand Down Expand Up @@ -84,6 +86,7 @@
import org.jboss.as.controller.client.ModelControllerClient;
import org.jboss.as.domain.management.SecurityRealm;
import org.jboss.as.domain.management.security.SSLContextService;
import org.jboss.as.domain.management.security.SecurityRealmService;
import org.jboss.as.host.controller.DomainModelControllerService;
import org.jboss.as.host.controller.HostControllerEnvironment;
import org.jboss.as.naming.ManagedReferenceFactory;
Expand All @@ -100,6 +103,7 @@
import org.jboss.msc.service.AbstractServiceListener;
import org.jboss.msc.service.Service;
import org.jboss.msc.service.ServiceBuilder;
import org.jboss.msc.service.ServiceContainer;
import org.jboss.msc.service.ServiceController;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.ServiceTarget;
Expand Down Expand Up @@ -230,6 +234,23 @@ private static SSLContext getSslContext(MonitorServiceConfiguration configuratio
return result;
}

private static X509TrustManager getX509TrustManager(MonitorServiceConfiguration configuration,
Map<String, InjectedValue<TrustManager[]>> trustOnlyTrustManagersValue) {
X509TrustManager result = null;
String bootSecurityRealm = configuration.getStorageAdapter().getSecurityRealm();
if (bootSecurityRealm != null) {
TrustManager[] tms = trustOnlyTrustManagersValue.get(bootSecurityRealm).getOptionalValue();
if (tms != null) {
for (TrustManager tm : tms) {
if (tm instanceof X509TrustManager) {
result = (X509TrustManager) tm;
}
}
}
}
return result;
}

private final InjectedValue<ModelController> modelControllerValue = new InjectedValue<>();
private final InjectedValue<ServerEnvironment> serverEnvironmentValue = new InjectedValue<>();
private final InjectedValue<ControlledProcessStateService> processStateValue = new InjectedValue<>();
Expand All @@ -238,6 +259,7 @@ private static SSLContext getSslContext(MonitorServiceConfiguration configuratio
private final InjectedValue<OutboundSocketBinding> serverOutboundSocketBindingValue = new InjectedValue<>();
// key=securityRealm name as a String
private final Map<String, InjectedValue<SSLContext>> trustOnlySSLContextValues = new HashMap<>();
private final Map<String, InjectedValue<TrustManager[]>> trustOnlyTrustManagersValue = new HashMap<>();

private AtomicReference<ServiceStatus> agentServiceStatus = new AtomicReference<>(ServiceStatus.INITIAL);
private PropertyChangeListener serverStateListener;
Expand Down Expand Up @@ -526,6 +548,16 @@ private boolean isRunning(Object state) {
}

}

ServiceContainer serviceContainer = startContext.getController().getServiceContainer();
for (String realmName : trustOnlySSLContextValues.keySet()) {
ServiceName sn = SSLContextService.ServiceUtil.createServiceName(
SecurityRealmService.ServiceUtil.createServiceName(realmName),
true);
SSLContextService sslContextService = (SSLContextService) (serviceContainer.getRequiredService(sn).getService());
trustOnlyTrustManagersValue.put(realmName, sslContextService.getTrustManagerInjector());
}

// deferred startup: must wait for server to be running before we can monitor the subsystems
ControlledProcessStateService stateService = processStateValue.getValue();
CustomPropertyChangeListener listener = new CustomPropertyChangeListener();
Expand Down Expand Up @@ -627,7 +659,10 @@ public void startMonitorService(MonitorServiceConfiguration newBootConfiguration

// prepare the builder that will create our HTTP/REST clients to the hawkular server infrastructure
SSLContext ssl = getSslContext(this.configuration, this.trustOnlySSLContextValues);
this.httpClientBuilder = new HttpClientBuilder(this.configuration.getStorageAdapter(), ssl);
X509TrustManager x509TrustManager = getX509TrustManager(this.configuration,
this.trustOnlyTrustManagersValue);
this.httpClientBuilder = new HttpClientBuilder(this.configuration.getStorageAdapter(), ssl,
x509TrustManager);

// get our self identifiers
this.localModelControllerClientFactory = ModelControllerClientFactory
Expand Down Expand Up @@ -669,7 +704,7 @@ public void startMonitorService(MonitorServiceConfiguration newBootConfiguration
// try to connect to the server via command-gateway channel; keep going on error
try {
this.webSocketClientBuilder = new WebSocketClientBuilder(
this.configuration.getStorageAdapter(), ssl);
this.configuration.getStorageAdapter(), ssl, x509TrustManager);
this.feedComm = new FeedCommProcessor(this.webSocketClientBuilder, this.configuration,
this.feedId, this);
this.feedComm.connect();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 Red Hat, Inc. and/or its affiliates
* Copyright 2015-2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -19,6 +19,7 @@
import java.util.Map;

import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;

import org.hawkular.agent.monitor.extension.MonitorServiceConfiguration.StorageAdapterConfiguration;
import org.hawkular.agent.monitor.util.BaseHttpClientGenerator;
Expand Down Expand Up @@ -48,12 +49,14 @@ public class HttpClientBuilder extends BaseHttpClientGenerator {
* @param sslContext if not null, and if the configuration tells use to use SSL, this will
* be the SSL context to use.
*/
public HttpClientBuilder(StorageAdapterConfiguration storageAdapter, SSLContext sslContext) {
public HttpClientBuilder(StorageAdapterConfiguration storageAdapter, SSLContext sslContext,
X509TrustManager x509TrustManager) {
super(new BaseHttpClientGenerator.Configuration.Builder()
.username(storageAdapter.getUsername())
.password(storageAdapter.getPassword())
.useSsl(storageAdapter.isUseSSL())
.sslContext(sslContext)
.x509TrustManager(x509TrustManager)
.keystorePath(storageAdapter.getKeystorePath())
.keystorePassword(storageAdapter.getKeystorePassword())
.connectTimeout(storageAdapter.getConnectTimeoutSeconds())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 Red Hat, Inc. and/or its affiliates
* Copyright 2015-2017 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -26,7 +26,9 @@

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import org.hawkular.agent.monitor.log.AgentLoggers;
import org.hawkular.agent.monitor.log.MsgLogger;
Expand Down Expand Up @@ -57,6 +59,7 @@ public static class Builder {
private String keystorePath;
private String keystorePassword;
private SSLContext sslContext;
private X509TrustManager x509TrustManager;
private Optional<Integer> connectTimeoutSeconds = Optional.empty();
private Optional<Integer> readTimeoutSeconds = Optional.empty();

Expand All @@ -65,7 +68,7 @@ public Builder() {

public Configuration build() {
return new Configuration(username, password, useSSL, keystorePath, keystorePassword, sslContext,
connectTimeoutSeconds, readTimeoutSeconds);
x509TrustManager, connectTimeoutSeconds, readTimeoutSeconds);
}

public Builder username(String s) {
Expand Down Expand Up @@ -98,6 +101,11 @@ public Builder sslContext(SSLContext s) {
return this;
}

public Builder x509TrustManager(X509TrustManager x509TrustManager) {
this.x509TrustManager = x509TrustManager;
return this;
}

public Builder connectTimeout(int connectTimeoutSeconds) {
this.connectTimeoutSeconds = Optional.of(connectTimeoutSeconds);
return this;
Expand All @@ -115,18 +123,21 @@ public Builder readTimeout(int readTimeoutSeconds) {
private final String keystorePath;
private final String keystorePassword;
private final SSLContext sslContext;
private final X509TrustManager x509TrustManager;
private final Optional<Integer> connectTimeoutSeconds;
private final Optional<Integer> readTimeoutSeconds;

private Configuration(String username, String password, boolean useSSL, String keystorePath,
String keystorePassword, SSLContext sslContext, Optional<Integer> connectTimeoutSeconds,
String keystorePassword, SSLContext sslContext, X509TrustManager x509TrustManager,
Optional<Integer> connectTimeoutSeconds,
Optional<Integer> readTimeoutSeconds) {
this.username = username;
this.password = password;
this.useSSL = useSSL;
this.keystorePath = keystorePath;
this.keystorePassword = keystorePassword;
this.sslContext = sslContext;
this.x509TrustManager = x509TrustManager;
this.connectTimeoutSeconds = connectTimeoutSeconds;
this.readTimeoutSeconds = readTimeoutSeconds;
}
Expand Down Expand Up @@ -155,6 +166,10 @@ public SSLContext getSslContext() {
return sslContext;
}

public X509TrustManager getX509TrustManager() {
return x509TrustManager;
}

public Optional<Integer> getConnectTimeoutSeconds() {
return connectTimeoutSeconds;
}
Expand Down Expand Up @@ -183,20 +198,33 @@ public BaseHttpClientGenerator(Configuration configuration) {

if (this.configuration.isUseSSL()) {
SSLContext theSslContextToUse;
X509TrustManager theTrustManagerToUse = null;

if (this.configuration.getSslContext() == null) {
if (this.configuration.getKeystorePath() != null) {
theSslContextToUse = buildSSLContext(this.configuration.getKeystorePath(),
KeyStore keystore = loadKeystore(this.configuration.getKeystorePath(),
this.configuration.getKeystorePassword());
TrustManager[] trustManagers = buildTrustManagers(keystore);
theSslContextToUse = buildSSLContext(keystore,
this.configuration.getKeystorePassword(), trustManagers);

for (TrustManager tm : trustManagers) {
if (tm instanceof X509TrustManager) {
theTrustManagerToUse = (X509TrustManager) tm;
break;
}
}

} else {
theSslContextToUse = null; // rely on the JVM default
}
} else {
theSslContextToUse = this.configuration.getSslContext();
theTrustManagerToUse = this.configuration.getX509TrustManager();
}

if (theSslContextToUse != null) {
httpClientBldr.sslSocketFactory(theSslContextToUse.getSocketFactory());
if (theSslContextToUse != null && theTrustManagerToUse != null) {
httpClientBldr.sslSocketFactory(theSslContextToUse.getSocketFactory(), theTrustManagerToUse);
}

// does not perform any hostname verification when looking at the remote end's cert
Expand Down Expand Up @@ -265,21 +293,37 @@ public String buildBase64Credentials() {
return Util.base64Encode(this.configuration.getUsername() + ":" + this.configuration.getPassword());
}

private SSLContext buildSSLContext(String keystorePath, String keystorePassword) {
private KeyStore loadKeystore(String keystorePath, String keystorePassword) {
try {
return readKeyStore(keystorePath, keystorePassword);
} catch (Exception e) {
throw new RuntimeException(String.format("Cannot load keystore [%s]", keystorePath), e);
}
}

private SSLContext buildSSLContext(KeyStore keyStore, String keystorePassword, TrustManager[] trustManagers) {
try {
KeyStore keyStore = readKeyStore(keystorePath, keystorePassword);

SSLContext sslContext = SSLContext.getInstance("SSL");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory
.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keystorePassword.toCharArray());
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(),
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagers,
new SecureRandom());
return sslContext;
} catch (Exception e) {
throw new RuntimeException(String.format("Cannot create SSL context from keystore [%s]", keystorePath), e);
throw new RuntimeException(String.format("Cannot create SSL context from keystore"), e);
}
}

private TrustManager[] buildTrustManagers(KeyStore keyStore) {
try {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
return trustManagerFactory.getTrustManagers();
} catch (Exception e) {
throw new RuntimeException("Cannot build X509TrustManager", e);
}
}

Expand Down

0 comments on commit 1309eea

Please sign in to comment.