You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by kr...@apache.org on 2019/03/11 18:46:50 UTC
[knox] branch master updated: KNOX-1812 - The Knox Gateway
truststore should be configurable (#69)
This is an automated email from the ASF dual-hosted git repository.
krisden pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git
The following commit(s) were added to refs/heads/master by this push:
new c217936 KNOX-1812 - The Knox Gateway truststore should be configurable (#69)
c217936 is described below
commit c21793602344d213554926cba35f00c24c282491
Author: Robert Levas <rl...@apache.org>
AuthorDate: Mon Mar 11 14:46:46 2019 -0400
KNOX-1812 - The Knox Gateway truststore should be configurable (#69)
---
.../org/apache/knox/gateway/GatewayMessages.java | 9 ++
.../gateway/config/impl/GatewayConfigImpl.java | 26 ++-
.../security/impl/DefaultKeystoreService.java | 101 ++++++++----
.../services/security/impl/JettySSLService.java | 6 +-
.../gateway/config/impl/GatewayConfigImplTest.java | 39 +++++
.../security/impl/DefaultKeystoreServiceTest.java | 178 +++++++++++++++++++++
.../security/impl/JettySSLServiceTest.java | 39 +++--
.../apache/knox/gateway/config/GatewayConfig.java | 43 +++++
.../gateway/dispatch/DefaultHttpClientFactory.java | 27 ++--
.../gateway/services/security/KeystoreService.java | 9 ++
.../org/apache/knox/gateway/GatewayTestConfig.java | 20 +++
11 files changed, 424 insertions(+), 73 deletions(-)
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java
index 74d662d..1aef1c3 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java
@@ -626,4 +626,13 @@ public interface GatewayMessages {
text = "Remote Alias Service enabled")
void remoteAliasServiceEnabled();
+ @Message( level = MessageLevel.ERROR, text = "The path to the keystore file does not exist: {0}" )
+ void keystoreFileDoesNotExist(String path);
+
+ @Message( level = MessageLevel.ERROR, text = "The path to the keystore file is not a file: {0}" )
+ void keystoreFileIsNotAFile(String path);
+
+ @Message( level = MessageLevel.ERROR, text = "The path to the keystore is not accessible: {0}" )
+ void keystoreFileIsNotAccessible(String path);
+
}
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java b/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java
index 833de2b..df7503b 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java
@@ -105,8 +105,6 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig {
private static final String TRUST_ALL_CERTS = GATEWAY_CONFIG_FILE_PREFIX + ".trust.all.certs";
private static final String CLIENT_AUTH_NEEDED = GATEWAY_CONFIG_FILE_PREFIX + ".client.auth.needed";
private static final String CLIENT_AUTH_WANTED = GATEWAY_CONFIG_FILE_PREFIX + ".client.auth.wanted";
- private static final String TRUSTSTORE_PATH = GATEWAY_CONFIG_FILE_PREFIX + ".truststore.path";
- private static final String TRUSTSTORE_TYPE = GATEWAY_CONFIG_FILE_PREFIX + ".truststore.type";
private static final String KEYSTORE_TYPE = GATEWAY_CONFIG_FILE_PREFIX + ".keystore.type";
private static final String XFORWARDED_ENABLED = GATEWAY_CONFIG_FILE_PREFIX + ".xforwarded.enabled";
private static final String EPHEMERAL_DH_KEY_SIZE = GATEWAY_CONFIG_FILE_PREFIX + ".jdk.tls.ephemeralDHKeySize";
@@ -543,7 +541,7 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig {
@Override
public String getTruststorePath() {
- return get( TRUSTSTORE_PATH, null);
+ return get( GATEWAY_TRUSTSTORE_PATH, null);
}
@Override
@@ -553,7 +551,12 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig {
@Override
public String getTruststoreType() {
- return get( TRUSTSTORE_TYPE, "JKS");
+ return get( GATEWAY_TRUSTSTORE_TYPE, DEFAULT_GATEWAY_TRUSTSTORE_TYPE);
+ }
+
+ @Override
+ public String getTruststorePasswordAlias() {
+ return get( GATEWAY_TRUSTSTORE_PASSWORD_ALIAS, DEFAULT_GATEWAY_TRUSTSTORE_PASSWORD_ALIAS);
}
@Override
@@ -605,6 +608,21 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig {
}
@Override
+ public String getHttpClientTruststorePath() {
+ return get(HTTP_CLIENT_TRUSTSTORE_PATH);
+ }
+
+ @Override
+ public String getHttpClientTruststoreType() {
+ return get(HTTP_CLIENT_TRUSTSTORE_TYPE, DEFAULT_HTTP_CLIENT_TRUSTSTORE_TYPE);
+ }
+
+ @Override
+ public String getHttpClientTruststorePasswordAlias() {
+ return get(HTTP_CLIENT_TRUSTSTORE_PASSWORD_ALIAS, DEFAULT_HTTP_CLIENT_TRUSTSTORE_PASSWORD_ALIAS);
+ }
+
+ @Override
public int getThreadPoolMax() {
int i = getInt( THREAD_POOL_MAX, 254 );
// Testing has shown that a value lower than 5 prevents Jetty from servicing request.
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreService.java
index aac4129..a681bed 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreService.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreService.java
@@ -33,6 +33,7 @@ import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.security.Key;
@@ -64,8 +65,8 @@ public class DefaultKeystoreService extends BaseKeystoreService implements
private static final String CERT_GEN_MODE = "hadoop.gateway.cert.gen.mode";
private static final String CERT_GEN_MODE_LOCALHOST = "localhost";
private static final String CERT_GEN_MODE_HOSTNAME = "hostname";
- private static GatewayMessages LOG = MessagesFactory.get( GatewayMessages.class );
- private static GatewayResources RES = ResourcesFactory.get( GatewayResources.class );
+ private static GatewayMessages LOG = MessagesFactory.get(GatewayMessages.class);
+ private static GatewayResources RES = ResourcesFactory.get(GatewayResources.class);
private GatewayConfig config;
private Map<String, Map<String, String>> cache = new ConcurrentHashMap<>();
@@ -111,13 +112,17 @@ public class DefaultKeystoreService extends BaseKeystoreService implements
@Override
public KeyStore getKeystoreForGateway() throws KeystoreServiceException {
- final File keyStoreFile = new File( config.getIdentityKeystorePath() );
- readLock.lock();
- try {
- return getKeystore(keyStoreFile, config.getIdentityKeystoreType(), getKeystorePassword(config.getIdentityKeystorePasswordAlias()));
- }
- finally {
- readLock.unlock();
+ return getKeystore(Paths.get(config.getIdentityKeystorePath()), config.getIdentityKeystoreType(), config.getIdentityKeystorePasswordAlias(), true);
+ }
+
+ @Override
+ public KeyStore getTruststoreForHttpClient() throws KeystoreServiceException {
+ String trustStorePath = config.getHttpClientTruststorePath();
+ if (trustStorePath == null) {
+ // If the trustStorePath is null, fallback to behavior before KNOX-1812
+ return getKeystoreForGateway();
+ } else {
+ return getKeystore(Paths.get(trustStorePath), config.getHttpClientTruststoreType(), config.getHttpClientTruststorePasswordAlias(), true);
}
}
@@ -128,30 +133,19 @@ public class DefaultKeystoreService extends BaseKeystoreService implements
@Override
public KeyStore getSigningKeystore(String keystoreName) throws KeystoreServiceException {
- File keyStoreFile;
+ Path keyStoreFile;
String keyStoreType;
- char[] password;
+ String passwordAlias;
if(keystoreName != null) {
- keyStoreFile = new File(keyStoreDir, keystoreName + ".jks");
+ keyStoreFile = Paths.get(keyStoreDir, keystoreName + ".jks");
keyStoreType = "jks";
- password = masterService.getMasterSecret();
+ passwordAlias = null;
} else {
- keyStoreFile = new File(config.getSigningKeystorePath());
+ keyStoreFile = Paths.get(config.getSigningKeystorePath());
keyStoreType = config.getSigningKeystoreType();
- password = getKeystorePassword(config.getSigningKeystorePasswordAlias());
- }
-
- // make sure the keystore exists
- if (!keyStoreFile.exists()) {
- throw new KeystoreServiceException("Configured signing keystore does not exist.");
- }
- readLock.lock();
- try {
- return getKeystore(keyStoreFile, keyStoreType, password);
- }
- finally {
- readLock.unlock();
+ passwordAlias = config.getSigningKeystorePasswordAlias();
}
+ return getKeystore(keyStoreFile, keyStoreType, passwordAlias, true);
}
@Override
@@ -327,14 +321,9 @@ public class DefaultKeystoreService extends BaseKeystoreService implements
@Override
public KeyStore getCredentialStoreForCluster(String clusterName)
throws KeystoreServiceException {
- final File keyStoreFile = new File( keyStoreDir, clusterName + CREDENTIALS_SUFFIX );
- readLock.lock();
- try {
- return getKeystore(keyStoreFile, "JCEKS", masterService.getMasterSecret());
- }
- finally {
- readLock.unlock();
- }
+ // Do not fail getting the credential store if the keystore file does not exist. The returned
+ // KeyStore will be empty. This seems like a potential bug, but is the behavior before KNOX-1812
+ return getKeystore(Paths.get(keyStoreDir, clusterName + CREDENTIALS_SUFFIX), "JCEKS", null, false);
}
@Override
@@ -448,6 +437,48 @@ public class DefaultKeystoreService extends BaseKeystoreService implements
return config.getIdentityKeystorePath();
}
+ /**
+ * Loads a keystore file.
+ * <p>
+ * if <code>failIfNotAccessible</code> is <code>true</code>, then the path to the keystore file
+ * (keystorePath) is validated such that it exists, is a file and can be read by the process. If
+ * any of these checks fail, a {@link KeystoreServiceException} is thrown in dicatating the exact
+ * reason.
+ * <p>
+ * Before the keystore file is loaded, the service's read lock is locked to prevent concurrent
+ * reads on the file.
+ *
+ * @param keystorePath the path to the keystore file
+ * @param keystoreType the type of keystore file
+ * @param alias the alias for the password to the keystore file (see {@link #getKeystorePassword(String)})
+ * @param failIfNotAccessible <code>true</code> to ensure the keystore file exists and is readable; <code>false</code> to not check
+ * @return a {@link KeyStore}, or <code>null</code> if the requested keystore cannot be created
+ * @throws KeystoreServiceException if an error occurs loading the keystore file
+ */
+ private KeyStore getKeystore(Path keystorePath, String keystoreType, String alias, boolean failIfNotAccessible) throws KeystoreServiceException {
+ File keystoreFile = keystorePath.toFile();
+
+ if (failIfNotAccessible) {
+ if (!keystoreFile.exists()) {
+ LOG.keystoreFileDoesNotExist(keystorePath.toString());
+ throw new KeystoreServiceException("The keystore file does not exist: " + keystoreFile.getAbsolutePath());
+ } else if (!keystoreFile.isFile()) {
+ LOG.keystoreFileIsNotAFile(keystorePath.toString());
+ throw new KeystoreServiceException("The keystore file is not a file: " + keystoreFile.getAbsolutePath());
+ } else if (!keystoreFile.canRead()) {
+ LOG.keystoreFileIsNotAccessible(keystorePath.toString());
+ throw new KeystoreServiceException("The keystore file cannot be read: " + keystoreFile.getAbsolutePath());
+ }
+ }
+
+ readLock.lock();
+ try {
+ return getKeystore(keystoreFile, keystoreType, getKeystorePassword(alias));
+ } finally {
+ readLock.unlock();
+ }
+ }
+
private char[] getKeystorePassword(String alias) throws KeystoreServiceException {
char[] password = null;
if (StringUtils.isNotEmpty(alias)) {
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/JettySSLService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/JettySSLService.java
index 180c1b8..b705922 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/JettySSLService.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/JettySSLService.java
@@ -40,7 +40,6 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
public class JettySSLService implements SSLService {
private static final String EPHEMERAL_DH_KEY_SIZE_PROPERTY = "jdk.tls.ephemeralDHKeySize";
- private static final String GATEWAY_TRUSTSTORE_PASSWORD = "gateway-truststore-password";
private static final String GATEWAY_CREDENTIAL_STORE_NAME = "__gateway";
private static GatewayMessages log = MessagesFactory.get( GatewayMessages.class );
@@ -171,12 +170,13 @@ public class JettySSLService implements SSLService {
char[] truststorePassword;
if (truststorePath != null) {
+ String trustStorePasswordAlias = config.getTruststorePasswordAlias();
trustStoreType = config.getTruststoreType();
try {
- truststorePassword = as.getPasswordFromAliasForGateway(GATEWAY_TRUSTSTORE_PASSWORD);
+ truststorePassword = as.getPasswordFromAliasForGateway(trustStorePasswordAlias);
} catch (AliasServiceException e) {
- log.failedToGetPasswordForGatewayTruststore(GATEWAY_TRUSTSTORE_PASSWORD, e);
+ log.failedToGetPasswordForGatewayTruststore(trustStorePasswordAlias, e);
throw e;
}
}
diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/config/impl/GatewayConfigImplTest.java b/gateway-server/src/test/java/org/apache/knox/gateway/config/impl/GatewayConfigImplTest.java
index dd85793..65a9b17 100644
--- a/gateway-server/src/test/java/org/apache/knox/gateway/config/impl/GatewayConfigImplTest.java
+++ b/gateway-server/src/test/java/org/apache/knox/gateway/config/impl/GatewayConfigImplTest.java
@@ -348,4 +348,43 @@ public class GatewayConfigImplTest {
assertEquals("custom_keystore_password_alias", config.getSigningKeystorePasswordAlias());
}
+ @Test
+ public void testHttpClientTruststoreOptions() {
+ GatewayConfigImpl config = new GatewayConfigImpl();
+
+ // Validate default options (backwards compatibility)
+ assertEquals("gateway-httpclient-truststore-password", config.getHttpClientTruststorePasswordAlias());
+ assertEquals("JKS", config.getHttpClientTruststoreType());
+ assertNull(config.getHttpClientTruststorePath());
+
+ // Validate changed options
+ config.set("gateway.httpclient.truststore.password.alias", "custom_password_alias");
+ config.set("gateway.httpclient.truststore.path", "custom_path");
+ config.set("gateway.httpclient.truststore.type", "custom_type");
+
+ assertEquals("custom_password_alias", config.getHttpClientTruststorePasswordAlias());
+ assertEquals("custom_path", config.getHttpClientTruststorePath());
+ assertEquals("custom_type", config.getHttpClientTruststoreType());
+ }
+
+ @Test
+ public void testGatewayTruststoreOptions() {
+ GatewayConfigImpl config = new GatewayConfigImpl();
+
+ // Validate default options (backwards compatibility)
+ assertEquals("gateway-truststore-password", config.getTruststorePasswordAlias());
+ assertEquals("JKS", config.getTruststoreType());
+ assertNull(config.getTruststorePath());
+
+ // Validate changed options
+ config.set("gateway.truststore.password.alias", "custom_password_alias");
+ config.set("gateway.truststore.path", "custom_path");
+ config.set("gateway.truststore.type", "custom_type");
+
+ assertEquals("custom_password_alias", config.getTruststorePasswordAlias());
+ assertEquals("custom_path", config.getTruststorePath());
+ assertEquals("custom_type", config.getTruststoreType());
+ }
+
+
}
diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreServiceTest.java b/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreServiceTest.java
new file mode 100644
index 0000000..52efc40
--- /dev/null
+++ b/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreServiceTest.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *
+ */
+
+package org.apache.knox.gateway.services.security.impl;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.createMockBuilder;
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+
+import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.config.impl.GatewayConfigImpl;
+import org.apache.knox.gateway.services.security.AliasService;
+import org.apache.knox.gateway.services.security.KeystoreServiceException;
+import org.apache.knox.gateway.services.security.MasterService;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.nio.file.Paths;
+import java.security.KeyStore;
+import java.util.Collections;
+
+public class DefaultKeystoreServiceTest {
+ @Rule
+ public final TemporaryFolder testFolder = new TemporaryFolder();
+
+ @Test
+ public void testGetTruststoreForHttpClientDefaults() throws Exception {
+ final File dataDir = testFolder.newFolder();
+
+ GatewayConfigImpl config = new GatewayConfigImpl();
+ config.set("gateway.data.dir", dataDir.getAbsolutePath());
+
+ KeyStore keystore = createNiceMock(KeyStore.class);
+
+ DefaultKeystoreService keystoreService = createMockBuilder(DefaultKeystoreService.class)
+ .addMockedMethod("getKeystoreForGateway")
+ .createMock();
+ expect(keystoreService.getKeystoreForGateway()).andReturn(keystore).once();
+
+ replay(keystore, keystoreService);
+
+ keystoreService.init(config, Collections.emptyMap());
+
+ assertEquals(keystore, keystoreService.getTruststoreForHttpClient());
+
+ verify(keystore, keystoreService);
+ }
+
+ @Test
+ public void testGetTruststoreForHttpClientCustomTrustStore() throws Exception {
+ final File dataDir = testFolder.newFolder();
+ final File truststoreFile = testFolder.newFile();
+ final String truststoreType = "jks";
+ final String truststorePasswordAlias = "password-alias";
+ final char[] truststorePassword = "truststore_password".toCharArray();
+
+ GatewayConfigImpl config = new GatewayConfigImpl();
+ config.set("gateway.data.dir", dataDir.getAbsolutePath());
+ config.set("gateway.httpclient.truststore.path", truststoreFile.getAbsolutePath());
+ config.set("gateway.httpclient.truststore.type", truststoreType);
+ config.set("gateway.httpclient.truststore.password.alias", truststorePasswordAlias);
+
+ KeyStore keystore = createNiceMock(KeyStore.class);
+
+ DefaultKeystoreService keystoreService = createMockBuilder(DefaultKeystoreService.class)
+ .addMockedMethod("getKeystore", File.class, String.class, char[].class)
+ .addMockedMethod("getCredentialForCluster", String.class, String.class)
+ .createMock();
+ expect(keystoreService.getKeystore(eq(truststoreFile), eq(truststoreType), eq(truststorePassword)))
+ .andReturn(keystore)
+ .once();
+ expect(keystoreService.getCredentialForCluster(eq(AliasService.NO_CLUSTER_NAME), eq(truststorePasswordAlias)))
+ .andReturn(truststorePassword)
+ .once();
+
+ replay(keystore, keystoreService);
+
+ keystoreService.init(config, Collections.emptyMap());
+
+ assertEquals(keystore, keystoreService.getTruststoreForHttpClient());
+
+ verify(keystore, keystoreService);
+ }
+
+ @Test(expected = KeystoreServiceException.class)
+ public void testGetTruststoreForHttpClientMissingCustomTrustStore() throws Exception {
+ final File dataDir = testFolder.newFolder();
+ final String truststoreType = "jks";
+ final String truststorePasswordAlias = "password-alias";
+ final char[] truststorePassword = "truststore_password".toCharArray();
+
+ GatewayConfigImpl config = new GatewayConfigImpl();
+ config.set("gateway.data.dir", dataDir.getAbsolutePath());
+ config.set("gateway.httpclient.truststore.path", Paths.get(dataDir.getAbsolutePath(), "missing_file.jks").toString());
+ config.set("gateway.httpclient.truststore.type", truststoreType);
+ config.set("gateway.httpclient.truststore.password.alias", truststorePasswordAlias);
+
+ DefaultKeystoreService keystoreService = createMockBuilder(DefaultKeystoreService.class)
+ .addMockedMethod("getKeystore", File.class, String.class, char[].class)
+ .addMockedMethod("getCredentialForCluster", String.class, String.class)
+ .createMock();
+ expect(keystoreService.getCredentialForCluster(eq(AliasService.NO_CLUSTER_NAME), eq(truststorePasswordAlias)))
+ .andReturn(truststorePassword)
+ .once();
+
+ replay(keystoreService);
+
+ keystoreService.init(config, Collections.emptyMap());
+
+ keystoreService.getTruststoreForHttpClient();
+
+ verify(keystoreService);
+ }
+
+ @Test
+ public void testGetTruststoreForHttpClientCustomTrustStoreMissingPasswordAlias() throws Exception {
+ final File dataDir = testFolder.newFolder();
+ final File truststoreFile = testFolder.newFile();
+ final String truststoreType = "jks";
+ final char[] masterSecret = "master_secret".toCharArray();
+
+ GatewayConfigImpl config = new GatewayConfigImpl();
+ config.set("gateway.data.dir", dataDir.getAbsolutePath());
+ config.set("gateway.httpclient.truststore.path", truststoreFile.getAbsolutePath());
+ config.set("gateway.httpclient.truststore.type", truststoreType);
+
+ KeyStore keystore = createNiceMock(KeyStore.class);
+
+ DefaultKeystoreService keystoreService = createMockBuilder(DefaultKeystoreService.class)
+ .addMockedMethod("getKeystore", File.class, String.class, char[].class)
+ .addMockedMethod("getCredentialForCluster", String.class, String.class)
+ .withConstructor()
+ .createMock();
+ expect(keystoreService.getKeystore(eq(truststoreFile), eq(truststoreType), eq(masterSecret)))
+ .andReturn(keystore)
+ .once();
+ expect(keystoreService.getCredentialForCluster(eq(AliasService.NO_CLUSTER_NAME), eq(GatewayConfig.DEFAULT_HTTP_CLIENT_TRUSTSTORE_PASSWORD_ALIAS)))
+ .andReturn(null)
+ .once();
+
+ MasterService masterService = createMock(MasterService.class);
+ expect(masterService.getMasterSecret()).andReturn(masterSecret);
+
+ replay(keystore, keystoreService, masterService);
+
+ keystoreService.init(config, Collections.emptyMap());
+ keystoreService.setMasterService(masterService);
+
+ assertEquals(keystore, keystoreService.getTruststoreForHttpClient());
+
+ verify(keystore, keystoreService, masterService);
+ }
+
+}
\ No newline at end of file
diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/JettySSLServiceTest.java b/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/JettySSLServiceTest.java
index 628076b..0bbdf4a 100644
--- a/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/JettySSLServiceTest.java
+++ b/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/JettySSLServiceTest.java
@@ -59,8 +59,9 @@ public class JettySSLServiceTest {
String identityKeyAlias = "server";
Path truststorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-truststore.jks");
String truststoreType = "jks";
+ String truststorePasswordAlias = "trust_store_password";
- GatewayConfig config = createGatewayConfig(false, false, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType);
+ GatewayConfig config = createGatewayConfig(false, false, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType, truststorePasswordAlias);
AliasService aliasService = createMock(AliasService.class);
expect(aliasService.getGatewayIdentityKeystorePassword()).andReturn(identityKeystorePassword).atLeastOnce();
@@ -108,8 +109,9 @@ public class JettySSLServiceTest {
String identityKeyAlias = "server";
Path truststorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-truststore.jks");
String truststoreType = "jks";
+ String truststorePasswordAlias = "trust_store_password";
- GatewayConfig config = createGatewayConfig(false, false, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType);
+ GatewayConfig config = createGatewayConfig(false, false, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType, truststorePasswordAlias);
AliasService aliasService = createMock(AliasService.class);
expect(aliasService.getGatewayIdentityKeystorePassword()).andThrow(new AliasServiceException(null)).atLeastOnce();
@@ -141,8 +143,9 @@ public class JettySSLServiceTest {
String identityKeyAlias = "server";
Path truststorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-truststore.jks");
String truststoreType = "jks";
+ String truststorePasswordAlias = "trust_store_password";
- GatewayConfig config = createGatewayConfig(false, false, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType);
+ GatewayConfig config = createGatewayConfig(false, false, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType, truststorePasswordAlias);
AliasService aliasService = createMock(AliasService.class);
expect(aliasService.getGatewayIdentityKeystorePassword()).andReturn(null).atLeastOnce();
@@ -191,8 +194,9 @@ public class JettySSLServiceTest {
String identityKeyAlias = "server";
Path truststorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-truststore.jks");
String truststoreType = "jks";
+ String truststorePasswordAlias = "trust_store_password";
- GatewayConfig config = createGatewayConfig(false, false, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType);
+ GatewayConfig config = createGatewayConfig(false, false, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType, truststorePasswordAlias);
AliasService aliasService = createMock(AliasService.class);
expect(aliasService.getGatewayIdentityKeystorePassword()).andReturn(null).atLeastOnce();
@@ -230,8 +234,9 @@ public class JettySSLServiceTest {
String identityKeyAlias = "server";
Path truststorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-truststore.jks");
String truststoreType = "jks";
+ String truststorePasswordAlias = "trust_store_password";
- GatewayConfig config = createGatewayConfig(true, false, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType);
+ GatewayConfig config = createGatewayConfig(true, false, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType, truststorePasswordAlias);
AliasService aliasService = createMock(AliasService.class);
expect(aliasService.getGatewayIdentityKeystorePassword()).andReturn(identityKeystorePassword).atLeastOnce();
@@ -281,13 +286,14 @@ public class JettySSLServiceTest {
Path truststorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-truststore.jks");
String truststoreType = "jks";
char[] truststorePassword = "horton".toCharArray();
+ String truststorePasswordAlias = "trust_store_password";
- GatewayConfig config = createGatewayConfig(true, true, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType);
+ GatewayConfig config = createGatewayConfig(true, true, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType, truststorePasswordAlias);
AliasService aliasService = createMock(AliasService.class);
expect(aliasService.getGatewayIdentityKeystorePassword()).andReturn(identityKeystorePassword).atLeastOnce();
expect(aliasService.getGatewayIdentityPassphrase()).andReturn(identityKeyPassphrase).atLeastOnce();
- expect(aliasService.getPasswordFromAliasForGateway(eq("gateway-truststore-password"))).andReturn(truststorePassword).atLeastOnce();
+ expect(aliasService.getPasswordFromAliasForGateway(eq(truststorePasswordAlias))).andReturn(truststorePassword).atLeastOnce();
KeystoreService keystoreService = createMock(KeystoreService.class);
@@ -333,13 +339,14 @@ public class JettySSLServiceTest {
String identityKeyAlias = "server";
Path truststorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-truststore.jks");
String truststoreType = "jks";
+ String truststorePasswordAlias = "trust_store_password";
- GatewayConfig config = createGatewayConfig(true, true, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType);
+ GatewayConfig config = createGatewayConfig(true, true, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType, truststorePasswordAlias);
AliasService aliasService = createMock(AliasService.class);
expect(aliasService.getGatewayIdentityKeystorePassword()).andReturn(identityKeystorePassword).atLeastOnce();
expect(aliasService.getGatewayIdentityPassphrase()).andReturn(identityKeyPassphrase).atLeastOnce();
- expect(aliasService.getPasswordFromAliasForGateway(eq("gateway-truststore-password"))).andThrow(new AliasServiceException(null)).atLeastOnce();
+ expect(aliasService.getPasswordFromAliasForGateway(eq(truststorePasswordAlias))).andThrow(new AliasServiceException(null)).atLeastOnce();
KeystoreService keystoreService = createMock(KeystoreService.class);
@@ -368,13 +375,14 @@ public class JettySSLServiceTest {
String identityKeyAlias = "server";
Path truststorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-truststore.jks");
String truststoreType = "jks";
+ String truststorePasswordAlias = "trust_store_password";
- GatewayConfig config = createGatewayConfig(true, true, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType);
+ GatewayConfig config = createGatewayConfig(true, true, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType, truststorePasswordAlias);
AliasService aliasService = createMock(AliasService.class);
expect(aliasService.getGatewayIdentityKeystorePassword()).andReturn(identityKeystorePassword).atLeastOnce();
expect(aliasService.getGatewayIdentityPassphrase()).andReturn(identityKeyPassphrase).atLeastOnce();
- expect(aliasService.getPasswordFromAliasForGateway(eq("gateway-truststore-password"))).andReturn(null).atLeastOnce();
+ expect(aliasService.getPasswordFromAliasForGateway(eq(truststorePasswordAlias))).andReturn(null).atLeastOnce();
KeystoreService keystoreService = createMock(KeystoreService.class);
@@ -420,13 +428,14 @@ public class JettySSLServiceTest {
String identityKeyAlias = "server";
Path truststorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-truststore.jks");
String truststoreType = "jks";
+ String truststorePasswordAlias = "trust_store_password";
- GatewayConfig config = createGatewayConfig(true, true, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType);
+ GatewayConfig config = createGatewayConfig(true, true, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType, truststorePasswordAlias);
AliasService aliasService = createMock(AliasService.class);
expect(aliasService.getGatewayIdentityKeystorePassword()).andReturn(null).atLeastOnce();
expect(aliasService.getGatewayIdentityPassphrase()).andReturn(null).atLeastOnce();
- expect(aliasService.getPasswordFromAliasForGateway(eq("gateway-truststore-password"))).andReturn(null).atLeastOnce();
+ expect(aliasService.getPasswordFromAliasForGateway(eq(truststorePasswordAlias))).andReturn(null).atLeastOnce();
KeystoreService keystoreService = createMock(KeystoreService.class);
@@ -448,7 +457,8 @@ public class JettySSLServiceTest {
private GatewayConfig createGatewayConfig(boolean isClientAuthNeeded, boolean isExplicitTruststore,
Path identityKeystorePath, String identityKeystoreType,
- String identityKeyAlias, Path truststorePath, String truststoreType) {
+ String identityKeyAlias, Path truststorePath,
+ String truststoreType, String trustStorePasswordAlias) {
GatewayConfig config = createMock(GatewayConfig.class);
expect(config.getIdentityKeystorePath()).andReturn(identityKeystorePath.toString()).atLeastOnce();
expect(config.getIdentityKeystoreType()).andReturn(identityKeystoreType).atLeastOnce();
@@ -460,6 +470,7 @@ public class JettySSLServiceTest {
if (isExplicitTruststore) {
expect(config.getTruststorePath()).andReturn(truststorePath.toString()).atLeastOnce();
expect(config.getTruststoreType()).andReturn(truststoreType).atLeastOnce();
+ expect(config.getTruststorePasswordAlias()).andReturn(trustStorePasswordAlias).atLeastOnce();
} else {
expect(config.getTruststorePath()).andReturn(null).atLeastOnce();
}
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java b/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java
index 40bbc97..f9d32ce 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java
@@ -73,6 +73,18 @@ public interface GatewayConfig {
String DEFAULT_SIGNING_KEY_ALIAS = "gateway-identity";
String DEFAULT_SIGNING_KEY_PASSPHRASE_ALIAS = "signing.key.passphrase";
+ String GATEWAY_TRUSTSTORE_PASSWORD_ALIAS = "gateway.truststore.password.alias";
+ String GATEWAY_TRUSTSTORE_PATH = "gateway.truststore.path";
+ String GATEWAY_TRUSTSTORE_TYPE = "gateway.truststore.type";
+ String DEFAULT_GATEWAY_TRUSTSTORE_TYPE = "JKS";
+ String DEFAULT_GATEWAY_TRUSTSTORE_PASSWORD_ALIAS = "gateway-truststore-password";
+
+ String HTTP_CLIENT_TRUSTSTORE_PASSWORD_ALIAS = "gateway.httpclient.truststore.password.alias";
+ String HTTP_CLIENT_TRUSTSTORE_PATH = "gateway.httpclient.truststore.path";
+ String HTTP_CLIENT_TRUSTSTORE_TYPE = "gateway.httpclient.truststore.type";
+ String DEFAULT_HTTP_CLIENT_TRUSTSTORE_TYPE = "JKS";
+ String DEFAULT_HTTP_CLIENT_TRUSTSTORE_PASSWORD_ALIAS = "gateway-httpclient-truststore-password";
+
String REMOTE_CONFIG_REGISTRY_TYPE = "type";
String REMOTE_CONFIG_REGISTRY_ADDRESS = "address";
String REMOTE_CONFIG_REGISTRY_NAMESPACE = "namespace";
@@ -173,6 +185,14 @@ public interface GatewayConfig {
String getTruststoreType();
+ /**
+ * Returns the configured value for the alias name to use when to looking up the Gateway's
+ * truststore password.
+ *
+ * @return an alias name
+ */
+ String getTruststorePasswordAlias();
+
boolean isXForwardedEnabled();
String getEphemeralDHKeySize();
@@ -183,6 +203,29 @@ public interface GatewayConfig {
int getHttpClientSocketTimeout();
+ /**
+ * Returns the configured value for the path to the truststore to be used by the HTTP client instance
+ * connecting to a service from the Gateway.
+ *
+ * @return a path to the trust file; or <code>null</code> if not set
+ */
+ String getHttpClientTruststorePath();
+
+ /**
+ * Returns the configured value for the type of the truststore specified by {@link #getHttpClientTruststorePath()}.
+ *
+ * @return a truststore type
+ */
+ String getHttpClientTruststoreType();
+
+ /**
+ * Returns the configured value for the alias name to use when to looking up the HTTP client's
+ * truststore password.
+ *
+ * @return an alias name
+ */
+ String getHttpClientTruststorePasswordAlias();
+
int getThreadPoolMax();
int getHttpServerRequestBuffer();
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/DefaultHttpClientFactory.java b/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/DefaultHttpClientFactory.java
index da03850..0ea3f9c 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/DefaultHttpClientFactory.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/dispatch/DefaultHttpClientFactory.java
@@ -28,9 +28,7 @@ import javax.net.ssl.SSLContext;
import javax.servlet.FilterConfig;
import org.apache.knox.gateway.services.security.AliasService;
-import org.apache.knox.gateway.services.security.AliasServiceException;
import org.apache.knox.gateway.services.security.KeystoreService;
-import org.apache.knox.gateway.services.security.MasterService;
import org.apache.knox.gateway.config.GatewayConfig;
import org.apache.knox.gateway.services.GatewayServices;
import org.apache.knox.gateway.services.metrics.MetricsService;
@@ -79,26 +77,21 @@ public class DefaultHttpClientFactory implements HttpClientFactory {
builder = HttpClients.custom();
}
if (Boolean.parseBoolean(filterConfig.getInitParameter("useTwoWaySsl"))) {
- char[] keypass = null;
- MasterService ms = services.getService(GatewayServices.MASTER_SERVICE);
AliasService as = services.getService(GatewayServices.ALIAS_SERVICE);
- try {
- keypass = as.getGatewayIdentityPassphrase();
- } catch (AliasServiceException e) {
- // nop - default passphrase will be used
- }
- if (keypass == null) {
- // there has been no alias created for the key - let's assume it is the same as the keystore password
- keypass = ms.getMasterSecret();
- }
-
KeystoreService ks = services.getService(GatewayServices.KEYSTORE_SERVICE);
final SSLContext sslcontext;
try {
- KeyStore keystoreForGateway = ks.getKeystoreForGateway();
+ KeyStore identityKeystore = ks.getKeystoreForGateway();
+ char[] identityKeyPassphrase = as.getGatewayIdentityPassphrase();
+
+ // The trustKeystore will be the same as the identityKeystore if a truststore was not explicitly
+ // configured in gateway-site (gateway.truststore.password.alias, gateway.truststore.path, gateway.truststore.type)
+ // This was the behavior before KNOX-1812
+ KeyStore trustKeystore = ks.getTruststoreForHttpClient();
+
sslcontext = SSLContexts.custom()
- .loadTrustMaterial(keystoreForGateway, new TrustSelfSignedStrategy())
- .loadKeyMaterial(keystoreForGateway, keypass)
+ .loadTrustMaterial(trustKeystore, new TrustSelfSignedStrategy())
+ .loadKeyMaterial(identityKeystore, identityKeyPassphrase)
.build();
} catch (Exception e) {
throw new IllegalArgumentException("Unable to create SSLContext", e);
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/KeystoreService.java b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/KeystoreService.java
index a1a843f..e904ff7 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/KeystoreService.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/KeystoreService.java
@@ -32,6 +32,15 @@ public interface KeystoreService {
KeyStore getKeystoreForGateway() throws KeystoreServiceException;
+ /**
+ * Gets the configured keystore instance that contains trust data.
+ * <p>
+ * If not configured, the Gateway's identity keystore should be returned. See {@link #getKeystoreForGateway()}
+ *
+ * @return a {@link KeyStore}
+ */
+ KeyStore getTruststoreForHttpClient() throws KeystoreServiceException;
+
KeyStore getSigningKeystore() throws KeystoreServiceException;
KeyStore getSigningKeystore(String keystoreName) throws KeystoreServiceException;
diff --git a/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java b/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
index 3f376f3..e5647b6 100644
--- a/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
+++ b/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
@@ -316,6 +316,11 @@ public class GatewayTestConfig extends Configuration implements GatewayConfig {
return truststoreType;
}
+ @Override
+ public String getTruststorePasswordAlias() {
+ return null;
+ }
+
public void setTruststoreType( String truststoreType ) {
this.truststoreType = truststoreType;
}
@@ -389,6 +394,21 @@ public class GatewayTestConfig extends Configuration implements GatewayConfig {
}
@Override
+ public String getHttpClientTruststorePath() {
+ return null;
+ }
+
+ @Override
+ public String getHttpClientTruststoreType() {
+ return null;
+ }
+
+ @Override
+ public String getHttpClientTruststorePasswordAlias() {
+ return null;
+ }
+
+ @Override
public int getThreadPoolMax() {
return 254;
}