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;
   }