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/06 16:16:11 UTC

[knox] branch master updated: KNOX-1687 - Hashicorp Vault RemoteAliasService provider

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 08dea10  KNOX-1687 - Hashicorp Vault RemoteAliasService provider
08dea10 is described below

commit 08dea106dbef920258095a9fc30325a2ea50b810
Author: Kevin Risden <kr...@apache.org>
AuthorDate: Fri Jan 11 15:58:38 2019 -0500

    KNOX-1687 - Hashicorp Vault RemoteAliasService provider
    
    Signed-off-by: Kevin Risden <kr...@apache.org>
---
 .../deploy/JWTAccessTokenAssertionContributor.java |   5 +-
 gateway-release/pom.xml                            |   4 +
 .../gateway/config/impl/GatewayConfigImpl.java     |   2 +-
 .../security/impl/DefaultAliasService.java         |   4 +-
 .../security/impl/DefaultKeystoreService.java      |   3 +-
 .../services/security/impl/RemoteAliasService.java |   5 +-
 .../security/impl/ZookeeperRemoteAliasService.java |  10 +-
 .../impl/RemoteAliasServiceTestProvider.java       |   4 +-
 gateway-service-hashicorp-vault/pom.xml            |  78 ++++++
 .../vault/HashicorpVaultAliasService.java          | 250 ++++++++++++++++++
 .../HashicorpVaultRemoteAliasServiceProvider.java  |  34 +++
 ...HashicorpVaultClientAuthenticationProvider.java |  34 +++
 ...HashicorpVaultClientAuthenticationProvider.java |  68 +++++
 ...HashicorpVaultClientAuthenticationProvider.java |  62 +++++
 ...tion.HashicorpVaultClientAuthenticationProvider |  20 ++
 ...nox.gateway.security.RemoteAliasServiceProvider |  19 ++
 .../vault/TestHashicorpVaultAliasService.java      | 282 +++++++++++++++++++++
 .../src/test/resources/log4j.properties            |  24 ++
 .../gateway/services/security/AliasService.java    |   3 +-
 pom.xml                                            |  45 ++++
 20 files changed, 938 insertions(+), 18 deletions(-)

diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/deploy/JWTAccessTokenAssertionContributor.java b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/deploy/JWTAccessTokenAssertionContributor.java
index 0a35064..d4f0c65 100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/deploy/JWTAccessTokenAssertionContributor.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/deploy/JWTAccessTokenAssertionContributor.java
@@ -27,10 +27,11 @@ import org.apache.knox.gateway.services.security.CryptoService;
 import org.apache.knox.gateway.topology.Provider;
 import org.apache.knox.gateway.topology.Service;
 
+import static org.apache.knox.gateway.services.security.AliasService.NO_CLUSTER_NAME;
+
 public class JWTAccessTokenAssertionContributor extends
     ProviderDeploymentContributorBase {
   private static final String ENCRYPT_ACCESS_TOKENS = "encrypt_access_tokens";
-  private static final String GATEWAY = "__gateway";
   private static final String FILTER_CLASSNAME = "org.apache.knox.gateway.provider.federation.jwt.filter.JWTAccessTokenAssertionFilter";
   private CryptoService crypto;
 
@@ -47,7 +48,7 @@ public class JWTAccessTokenAssertionContributor extends
   @Override
   public void initializeContribution(DeploymentContext context) {
     super.initializeContribution(context);
-    crypto.createAndStoreEncryptionKeyForCluster(GATEWAY, ENCRYPT_ACCESS_TOKENS);
+    crypto.createAndStoreEncryptionKeyForCluster(NO_CLUSTER_NAME, ENCRYPT_ACCESS_TOKENS);
   }
 
   @Override
diff --git a/gateway-release/pom.xml b/gateway-release/pom.xml
index 3902c99..0af744f 100644
--- a/gateway-release/pom.xml
+++ b/gateway-release/pom.xml
@@ -340,5 +340,9 @@
             <groupId>org.apache.knox</groupId>
             <artifactId>gateway-adapter</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.knox</groupId>
+            <artifactId>gateway-service-hashicorp-vault</artifactId>
+        </dependency>
     </dependencies>
 </project>
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 f68b536..5bb5243 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
@@ -229,7 +229,7 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig {
   static final String DISPATCH_HOST_WHITELIST_SERVICES = DISPATCH_HOST_WHITELIST + ".services";
 
   static final String REMOTE_ALIAS_SERVICE_CONFIG_PREFIX = GATEWAY_CONFIG_FILE_PREFIX + ".remote.alias.service.config.prefix";
-  static final String REMOTE_ALIAS_SERVICE_CONFIG_PREFIX_DEFAULT = GATEWAY_CONFIG_FILE_PREFIX + ".remote.alias.service.config";
+  static final String REMOTE_ALIAS_SERVICE_CONFIG_PREFIX_DEFAULT = GATEWAY_CONFIG_FILE_PREFIX + ".remote.alias.service.config.";
 
   private static final List<String> DEFAULT_GLOBAL_RULES_SERVICES = Arrays.asList(
       "NAMENODE", "JOBTRACKER", "WEBHDFS", "WEBHCAT",
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultAliasService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultAliasService.java
index 2f0dae5..7f40e3b 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultAliasService.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultAliasService.java
@@ -174,13 +174,13 @@ public class DefaultAliasService implements AliasService {
   @Override
   public char[] getPasswordFromAliasForGateway(String alias)
       throws AliasServiceException {
-    return getPasswordFromAliasForCluster("__gateway", alias);
+    return getPasswordFromAliasForCluster(NO_CLUSTER_NAME, alias);
   }
 
   @Override
   public void generateAliasForGateway(String alias)
       throws AliasServiceException {
-    generateAliasForCluster("__gateway", alias);
+    generateAliasForCluster(NO_CLUSTER_NAME, alias);
   }
 
   @Override
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 7f42bbd..aac4129 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
@@ -54,12 +54,13 @@ import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
+import static org.apache.knox.gateway.services.security.AliasService.NO_CLUSTER_NAME;
+
 public class DefaultKeystoreService extends BaseKeystoreService implements
     KeystoreService, Service {
 
   private static final String dnTemplate = "CN={0},OU=Test,O=Hadoop,L=Test,ST=Test,C=US";
   private static final String CREDENTIALS_SUFFIX = "-credentials.jceks";
-  private static final String NO_CLUSTER_NAME = "__gateway";
   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";
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/RemoteAliasService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/RemoteAliasService.java
index 13f7332..5182fd1 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/RemoteAliasService.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/RemoteAliasService.java
@@ -47,7 +47,6 @@ import java.util.ServiceLoader;
  * @since 1.1.0
  */
 public class RemoteAliasService implements AliasService {
-  public static final String DEFAULT_CLUSTER_NAME = "__gateway";
   public static final String REMOTE_ALIAS_SERVICE_TYPE = "type";
 
   private static final GatewayMessages LOG = MessagesFactory.get(GatewayMessages.class);
@@ -173,7 +172,7 @@ public class RemoteAliasService implements AliasService {
   @Override
   public char[] getPasswordFromAliasForGateway(String alias)
       throws AliasServiceException {
-    return getPasswordFromAliasForCluster(DEFAULT_CLUSTER_NAME, alias);
+    return getPasswordFromAliasForCluster(NO_CLUSTER_NAME, alias);
   }
 
   @Override
@@ -235,7 +234,7 @@ public class RemoteAliasService implements AliasService {
   @Override
   public void generateAliasForGateway(final String alias)
       throws AliasServiceException {
-    generateAliasForCluster(DEFAULT_CLUSTER_NAME, alias);
+    generateAliasForCluster(NO_CLUSTER_NAME, alias);
   }
 
   @Override
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/ZookeeperRemoteAliasService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/ZookeeperRemoteAliasService.java
index 2758114..f2327e8 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/ZookeeperRemoteAliasService.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/ZookeeperRemoteAliasService.java
@@ -40,8 +40,6 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
-import static org.apache.knox.gateway.services.security.impl.RemoteAliasService.DEFAULT_CLUSTER_NAME;
-
 /**
  * An {@link AliasService} implementation based on zookeeper remote service registry.
  */
@@ -155,7 +153,7 @@ public class ZookeeperRemoteAliasService implements AliasService {
     ensureEntry(PATH_KNOX_SECURITY, remoteClient);
     ensureEntry(PATH_KNOX_ALIAS_STORE_TOPOLOGY, remoteClient);
     ensureEntry(
-        PATH_KNOX_ALIAS_STORE_TOPOLOGY + PATH_SEPARATOR + DEFAULT_CLUSTER_NAME,
+        PATH_KNOX_ALIAS_STORE_TOPOLOGY + PATH_SEPARATOR + NO_CLUSTER_NAME,
         remoteClient);
   }
 
@@ -280,7 +278,7 @@ public class ZookeeperRemoteAliasService implements AliasService {
   @Override
   public char[] getPasswordFromAliasForGateway(String alias)
       throws AliasServiceException {
-    return getPasswordFromAliasForCluster(DEFAULT_CLUSTER_NAME, alias);
+    return getPasswordFromAliasForCluster(NO_CLUSTER_NAME, alias);
   }
 
   @Override
@@ -305,7 +303,7 @@ public class ZookeeperRemoteAliasService implements AliasService {
 
   @Override
   public void generateAliasForGateway(final String alias) throws AliasServiceException {
-    generateAliasForCluster(DEFAULT_CLUSTER_NAME, alias);
+    generateAliasForCluster(NO_CLUSTER_NAME, alias);
   }
 
   @Override
@@ -411,7 +409,7 @@ public class ZookeeperRemoteAliasService implements AliasService {
     ensureEntry(PATH_KNOX_SECURITY, remoteClient);
     ensureEntry(PATH_KNOX_ALIAS_STORE_TOPOLOGY, remoteClient);
     ensureEntry(
-        PATH_KNOX_ALIAS_STORE_TOPOLOGY + PATH_SEPARATOR + DEFAULT_CLUSTER_NAME,
+        PATH_KNOX_ALIAS_STORE_TOPOLOGY + PATH_SEPARATOR + NO_CLUSTER_NAME,
         remoteClient);
   }
 
diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/RemoteAliasServiceTestProvider.java b/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/RemoteAliasServiceTestProvider.java
index 75f73cb..523c1e2 100644
--- a/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/RemoteAliasServiceTestProvider.java
+++ b/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/RemoteAliasServiceTestProvider.java
@@ -84,7 +84,7 @@ public class RemoteAliasServiceTestProvider implements RemoteAliasServiceProvide
 
     @Override
     public char[] getPasswordFromAliasForGateway(String alias) {
-      return getPasswordFromAliasForCluster(RemoteAliasService.DEFAULT_CLUSTER_NAME, alias);
+      return getPasswordFromAliasForCluster(NO_CLUSTER_NAME, alias);
     }
 
     @Override
@@ -109,7 +109,7 @@ public class RemoteAliasServiceTestProvider implements RemoteAliasServiceProvide
 
     @Override
     public void generateAliasForGateway(String alias) {
-      generateAliasForCluster(RemoteAliasService.DEFAULT_CLUSTER_NAME, alias);
+      generateAliasForCluster(NO_CLUSTER_NAME, alias);
     }
 
     @Override
diff --git a/gateway-service-hashicorp-vault/pom.xml b/gateway-service-hashicorp-vault/pom.xml
new file mode 100644
index 0000000..afa5435
--- /dev/null
+++ b/gateway-service-hashicorp-vault/pom.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.knox</groupId>
+        <artifactId>gateway</artifactId>
+        <version>1.3.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>gateway-service-hashicorp-vault</artifactId>
+    <name>gateway-service-hashicorp-vault</name>
+    <description>A hashicorp vault backend</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.knox</groupId>
+            <artifactId>gateway-spi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.knox</groupId>
+            <artifactId>gateway-util-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.vault</groupId>
+            <artifactId>spring-vault-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.testcontainers</groupId>
+            <artifactId>testcontainers</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.testcontainers</groupId>
+            <artifactId>vault</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.knox</groupId>
+            <artifactId>gateway-test-utils</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/gateway-service-hashicorp-vault/src/main/java/org/apache/knox/gateway/backend/hashicorp/vault/HashicorpVaultAliasService.java b/gateway-service-hashicorp-vault/src/main/java/org/apache/knox/gateway/backend/hashicorp/vault/HashicorpVaultAliasService.java
new file mode 100644
index 0000000..e9e3851
--- /dev/null
+++ b/gateway-service-hashicorp-vault/src/main/java/org/apache/knox/gateway/backend/hashicorp/vault/HashicorpVaultAliasService.java
@@ -0,0 +1,250 @@
+/*
+ * 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.backend.hashicorp.vault;
+
+import org.apache.knox.gateway.backend.hashicorp.vault.authentication.HashicorpVaultClientAuthenticationProvider;
+import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.services.ServiceLifecycleException;
+import org.apache.knox.gateway.services.security.AliasService;
+import org.apache.knox.gateway.services.security.AliasServiceException;
+import org.apache.knox.gateway.util.PasswordUtils;
+import org.springframework.vault.VaultException;
+import org.springframework.vault.authentication.ClientAuthentication;
+import org.springframework.vault.client.VaultEndpoint;
+import org.springframework.vault.core.VaultTemplate;
+import org.springframework.vault.core.VaultVersionedKeyValueOperations;
+import org.springframework.vault.support.Versioned;
+
+import java.net.URI;
+import java.security.cert.Certificate;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
+
+public class HashicorpVaultAliasService implements AliasService {
+  public static final String TYPE = "hashicorp.vault";
+  public static final String VAULT_CONFIG_PREFIX = "hashicorp.vault.";
+  public static final String VAULT_ADDRESS_KEY = VAULT_CONFIG_PREFIX + "address";
+
+  private static final String KEY = "data";
+
+  static final String VAULT_SEPARATOR = "/";
+  static final String VAULT_SECRETS_ENGINE_KEY = VAULT_CONFIG_PREFIX + "secrets.engine";
+  static final String VAULT_PATH_PREFIX_KEY = VAULT_CONFIG_PREFIX + "path.prefix";
+
+  private final AliasService localAliasService;
+
+  private VaultVersionedKeyValueOperations vault;
+  private String vaultPathPrefix;
+  private GatewayConfig config;
+
+  public HashicorpVaultAliasService(AliasService localAliasService) {
+    this.localAliasService = localAliasService;
+  }
+
+  private String getPath(String clusterName) {
+    return vaultPathPrefix + clusterName;
+  }
+
+  private String getPath(String clusterName, String alias) {
+    return getPath(clusterName) + VAULT_SEPARATOR + alias;
+  }
+
+  @Override
+  public List<String> getAliasesForCluster(String clusterName) throws AliasServiceException {
+    try {
+      List<String> aliases = vault.list(getPath(clusterName));
+      if(aliases == null) {
+        return Collections.emptyList();
+      }
+
+      // Required to check if list entries are valid since versioned KV does a soft delete
+      // Keys are still listed but do not have a value
+      for (Iterator<String> iterator = aliases.iterator(); iterator.hasNext();) {
+        String alias = iterator.next();
+        if (getPasswordFromAliasForCluster(clusterName, alias) == null) {
+          // Remove the current element from the iterator and the list.
+          iterator.remove();
+        }
+      }
+
+      return aliases;
+    } catch (VaultException e) {
+      throw new AliasServiceException(e);
+    }
+  }
+
+  @Override
+  public void addAliasForCluster(String clusterName, String alias, String value) throws AliasServiceException {
+    try {
+      vault.put(getPath(clusterName, alias), Collections.singletonMap(KEY, value));
+    } catch (VaultException e) {
+      throw new AliasServiceException(e);
+    }
+  }
+
+  @Override
+  public void removeAliasForCluster(String clusterName, String alias) throws AliasServiceException {
+    // Delete is by default a soft delete with versioned KV in Vault
+    // https://learn.hashicorp.com/vault/secrets-management/sm-versioned-kv#step-6-permanently-delete-data
+    // Below is an example of how to programmatically delete all versions
+    /*
+    vaultTemplate.doWithSession(restOperations -> {
+      restOperations.delete(VAULT_SEPARATOR + vaultSecretsEngine + "/metadata/" + clusterName + VAULT_SEPARATOR + alias);
+      return null;
+    });
+     */
+    try {
+      vault.delete(getPath(clusterName, alias));
+    } catch (VaultException e) {
+      throw new AliasServiceException(e);
+    }
+  }
+
+  @Override
+  public char[] getPasswordFromAliasForCluster(String clusterName, String alias) throws AliasServiceException {
+    try {
+      Versioned<Map<String, Object>> mapVersioned = vault.get(getPath(clusterName, alias));
+      if(mapVersioned != null && mapVersioned.hasData()) {
+        Map<String, Object> data = mapVersioned.getData();
+        if(data != null && data.containsKey(KEY)) {
+          return String.valueOf(data.get(KEY)).toCharArray();
+        }
+      }
+      return null;
+    } catch (VaultException e) {
+      throw new AliasServiceException(e);
+    }
+  }
+
+  @Override
+  public char[] getPasswordFromAliasForCluster(String clusterName, String alias, boolean generate) throws AliasServiceException {
+    if(generate) {
+      getPasswordFromAliasForCluster(clusterName, alias);
+    }
+    return getPasswordFromAliasForCluster(clusterName, alias);
+  }
+
+  @Override
+  public void generateAliasForCluster(String clusterName, String alias) throws AliasServiceException {
+    addAliasForCluster(clusterName, alias, PasswordUtils.generatePassword(16));
+  }
+
+  @Override
+  public char[] getPasswordFromAliasForGateway(String alias) throws AliasServiceException {
+    return getPasswordFromAliasForCluster(NO_CLUSTER_NAME, alias);
+  }
+
+  @Override
+  public char[] getGatewayIdentityPassphrase() throws AliasServiceException {
+    return getPasswordFromAliasForGateway(config.getIdentityKeyPassphraseAlias());
+  }
+
+  @Override
+  public char[] getGatewayIdentityKeystorePassword() throws AliasServiceException {
+    return getPasswordFromAliasForGateway(config.getIdentityKeystorePasswordAlias());
+  }
+
+  @Override
+  public char[] getSigningKeyPassphrase() throws AliasServiceException {
+    return getPasswordFromAliasForGateway(config.getSigningKeyPassphraseAlias());
+  }
+
+  @Override
+  public char[] getSigningKeystorePassword() throws AliasServiceException {
+    return getPasswordFromAliasForGateway(config.getSigningKeystorePasswordAlias());
+  }
+
+  @Override
+  public void generateAliasForGateway(String alias) throws AliasServiceException {
+    generateAliasForCluster(NO_CLUSTER_NAME, alias);
+  }
+
+  @Override
+  public Certificate getCertificateForGateway(String alias) throws AliasServiceException {
+    throw new AliasServiceException(new UnsupportedOperationException());
+  }
+
+  @Override
+  public void init(GatewayConfig config, Map<String, String> options) throws ServiceLifecycleException {
+    this.config = config;
+    Map<String, String> remoteAliasServiceConfiguration = config.getRemoteAliasServiceConfiguration();
+    Map<String, String> vaultConfiguration = new HashMap<>();
+    for(Map.Entry<String, String> entry : remoteAliasServiceConfiguration.entrySet()) {
+      if(entry.getKey().startsWith(VAULT_CONFIG_PREFIX)) {
+        vaultConfiguration.put(entry.getKey(),
+            entry.getValue());
+      }
+    }
+
+    String vaultAddress = vaultConfiguration.get(VAULT_ADDRESS_KEY);
+    String vaultSecretsEngine = vaultConfiguration.get(VAULT_SECRETS_ENGINE_KEY);
+    vaultPathPrefix = getVaultPathPrefix(vaultConfiguration);
+
+    VaultEndpoint vaultEndpoint;
+    try {
+      vaultEndpoint = VaultEndpoint.from(new URI(vaultAddress));
+      ClientAuthentication vaultAuthentication = getClientAuthentication(vaultConfiguration);
+      VaultTemplate vaultTemplate = new VaultTemplate(vaultEndpoint, vaultAuthentication);
+      vault = vaultTemplate.opsForVersionedKeyValue(vaultSecretsEngine);
+    } catch (Exception e) {
+      throw new ServiceLifecycleException("Failed to init", e);
+    }
+  }
+
+  private String getVaultPathPrefix(Map<String, String> properties) {
+    String vaultPathPrefix = properties.get(VAULT_PATH_PREFIX_KEY);
+    if(vaultPathPrefix == null) {
+      return "";
+    }
+    if(vaultPathPrefix.startsWith(VAULT_SEPARATOR)) {
+      vaultPathPrefix = vaultPathPrefix.replaceFirst(VAULT_SEPARATOR, "");
+    }
+    if(vaultPathPrefix.endsWith(VAULT_SEPARATOR)) {
+      return vaultPathPrefix;
+    }
+    return vaultPathPrefix + VAULT_SEPARATOR;
+  }
+
+  private ClientAuthentication getClientAuthentication(Map<String, String> properties)
+      throws Exception {
+    String authenticationType = properties.get(
+        HashicorpVaultClientAuthenticationProvider.AUTHENTICATION_TYPE_KEY);
+
+    ServiceLoader<HashicorpVaultClientAuthenticationProvider> providers =
+        ServiceLoader.load(HashicorpVaultClientAuthenticationProvider.class);
+    for (HashicorpVaultClientAuthenticationProvider provider : providers) {
+      if(authenticationType.equals(provider.getType())) {
+        return provider.newInstance(localAliasService, properties);
+      }
+    }
+
+    throw new IllegalStateException("Not able to find client authentication provider");
+  }
+
+  @Override
+  public void start() throws ServiceLifecycleException {
+  }
+
+  @Override
+  public void stop() throws ServiceLifecycleException {
+  }
+}
diff --git a/gateway-service-hashicorp-vault/src/main/java/org/apache/knox/gateway/backend/hashicorp/vault/HashicorpVaultRemoteAliasServiceProvider.java b/gateway-service-hashicorp-vault/src/main/java/org/apache/knox/gateway/backend/hashicorp/vault/HashicorpVaultRemoteAliasServiceProvider.java
new file mode 100644
index 0000000..894e510
--- /dev/null
+++ b/gateway-service-hashicorp-vault/src/main/java/org/apache/knox/gateway/backend/hashicorp/vault/HashicorpVaultRemoteAliasServiceProvider.java
@@ -0,0 +1,34 @@
+/*
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.backend.hashicorp.vault;
+
+import org.apache.knox.gateway.security.RemoteAliasServiceProvider;
+import org.apache.knox.gateway.services.security.AliasService;
+import org.apache.knox.gateway.services.security.MasterService;
+
+public class HashicorpVaultRemoteAliasServiceProvider implements RemoteAliasServiceProvider {
+  @Override
+  public String getType() {
+    return HashicorpVaultAliasService.TYPE;
+  }
+
+  @Override
+  public AliasService newInstance(AliasService localAliasService, MasterService ms) {
+    return new HashicorpVaultAliasService(localAliasService);
+  }
+}
diff --git a/gateway-service-hashicorp-vault/src/main/java/org/apache/knox/gateway/backend/hashicorp/vault/authentication/HashicorpVaultClientAuthenticationProvider.java b/gateway-service-hashicorp-vault/src/main/java/org/apache/knox/gateway/backend/hashicorp/vault/authentication/HashicorpVaultClientAuthenticationProvider.java
new file mode 100644
index 0000000..419d7c2
--- /dev/null
+++ b/gateway-service-hashicorp-vault/src/main/java/org/apache/knox/gateway/backend/hashicorp/vault/authentication/HashicorpVaultClientAuthenticationProvider.java
@@ -0,0 +1,34 @@
+/*
+ * 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.backend.hashicorp.vault.authentication;
+
+import org.apache.knox.gateway.services.security.AliasService;
+import org.springframework.vault.authentication.ClientAuthentication;
+
+import java.util.Map;
+
+import static org.apache.knox.gateway.backend.hashicorp.vault.HashicorpVaultAliasService.VAULT_CONFIG_PREFIX;
+
+public interface HashicorpVaultClientAuthenticationProvider {
+  String AUTHENTICATION_CONFIG_PREFIX = VAULT_CONFIG_PREFIX + "authentication.";
+  String AUTHENTICATION_TYPE_KEY = AUTHENTICATION_CONFIG_PREFIX + "type";
+
+  String getType();
+  ClientAuthentication newInstance(AliasService localAliasService, Map<String, String> properties)
+      throws Exception;
+}
diff --git a/gateway-service-hashicorp-vault/src/main/java/org/apache/knox/gateway/backend/hashicorp/vault/authentication/KubernetesHashicorpVaultClientAuthenticationProvider.java b/gateway-service-hashicorp-vault/src/main/java/org/apache/knox/gateway/backend/hashicorp/vault/authentication/KubernetesHashicorpVaultClientAuthenticationProvider.java
new file mode 100644
index 0000000..11f3047
--- /dev/null
+++ b/gateway-service-hashicorp-vault/src/main/java/org/apache/knox/gateway/backend/hashicorp/vault/authentication/KubernetesHashicorpVaultClientAuthenticationProvider.java
@@ -0,0 +1,68 @@
+/*
+ * 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.backend.hashicorp.vault.authentication;
+
+import org.apache.knox.gateway.backend.hashicorp.vault.HashicorpVaultAliasService;
+import org.apache.knox.gateway.services.security.AliasService;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.vault.authentication.ClientAuthentication;
+import org.springframework.vault.authentication.KubernetesAuthentication;
+import org.springframework.vault.authentication.KubernetesAuthenticationOptions;
+import org.springframework.vault.client.SimpleVaultEndpointProvider;
+import org.springframework.vault.client.VaultClients;
+import org.springframework.vault.client.VaultEndpoint;
+import org.springframework.vault.client.VaultEndpointProvider;
+import org.springframework.vault.config.ClientHttpRequestFactoryFactory;
+import org.springframework.vault.support.ClientOptions;
+import org.springframework.vault.support.SslConfiguration;
+import org.springframework.web.client.RestOperations;
+
+import java.net.URI;
+import java.util.Map;
+
+public class KubernetesHashicorpVaultClientAuthenticationProvider
+    implements HashicorpVaultClientAuthenticationProvider {
+  public static final String TYPE = "kubernetes";
+  public static final String KUBERNETES_ROLE_KEY = AUTHENTICATION_CONFIG_PREFIX + "kubernetes.role";
+
+  @Override
+  public String getType() {
+    return TYPE;
+  }
+
+  @Override
+  public ClientAuthentication newInstance(AliasService localAliasService,
+                                          Map<String, String> properties) throws Exception {
+    String role = properties.get(KUBERNETES_ROLE_KEY);
+    KubernetesAuthenticationOptions kubernetesAuthenticationOptions =
+        KubernetesAuthenticationOptions.builder().role(role).build();
+    return new KubernetesAuthentication(kubernetesAuthenticationOptions,
+        getRestOperations(properties));
+  }
+
+  private RestOperations getRestOperations(Map<String, String> properties) throws Exception {
+    String vaultAddress = properties.get(HashicorpVaultAliasService.VAULT_ADDRESS_KEY);
+    VaultEndpoint vaultEndpoint = VaultEndpoint.from(new URI(vaultAddress));
+    VaultEndpointProvider vaultEndpointProvider = SimpleVaultEndpointProvider.of(vaultEndpoint);
+    ClientOptions clientOptions = new ClientOptions();
+    SslConfiguration sslConfiguration = SslConfiguration.unconfigured();
+    ClientHttpRequestFactory clientHttpRequestFactory = ClientHttpRequestFactoryFactory.create(
+        clientOptions, sslConfiguration);
+    return VaultClients.createRestTemplate(vaultEndpointProvider, clientHttpRequestFactory);
+  }
+}
diff --git a/gateway-service-hashicorp-vault/src/main/java/org/apache/knox/gateway/backend/hashicorp/vault/authentication/TokenHashicorpVaultClientAuthenticationProvider.java b/gateway-service-hashicorp-vault/src/main/java/org/apache/knox/gateway/backend/hashicorp/vault/authentication/TokenHashicorpVaultClientAuthenticationProvider.java
new file mode 100644
index 0000000..f790441
--- /dev/null
+++ b/gateway-service-hashicorp-vault/src/main/java/org/apache/knox/gateway/backend/hashicorp/vault/authentication/TokenHashicorpVaultClientAuthenticationProvider.java
@@ -0,0 +1,62 @@
+/*
+ * 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.backend.hashicorp.vault.authentication;
+
+import org.apache.knox.gateway.services.security.AliasService;
+import org.springframework.vault.authentication.ClientAuthentication;
+import org.springframework.vault.authentication.TokenAuthentication;
+
+import java.util.Map;
+
+public class TokenHashicorpVaultClientAuthenticationProvider
+    implements HashicorpVaultClientAuthenticationProvider {
+  public static final String TYPE = "token";
+  public static final String TOKEN_KEY = AUTHENTICATION_CONFIG_PREFIX + "token";
+
+  @Override
+  public String getType() {
+    return TYPE;
+  }
+
+  @Override
+  public ClientAuthentication newInstance(AliasService localAliasService,
+                                          Map<String, String> properties) throws Exception {
+    String vaultToken = getVaultToken(localAliasService, properties);
+    return new TokenAuthentication(vaultToken);
+  }
+
+  /**
+   * Returns the Vault token from the properties and looks it up in the AliasService
+   * if it is an alias.
+   *
+   * @param localAliasService alias service to use to look up the token
+   * @param properties properties for the Hashicorp Vault remote alias service
+   * @return string of the Vault token
+   * @throws Exception exception if there is an error retrieving the Vault token
+   */
+  private String getVaultToken(AliasService localAliasService, Map<String, String> properties)
+      throws Exception {
+    String vaultToken = properties.get(TOKEN_KEY);
+    if(vaultToken.startsWith("${ALIAS=") && vaultToken.endsWith("}")) {
+      // Strip off ${ALIAS= and } from the value before looking it up
+      String vaultTokenAlias = vaultToken.substring(8, vaultToken.length()-1);
+      return new String(localAliasService.getPasswordFromAliasForGateway(vaultTokenAlias));
+    }
+    return vaultToken;
+  }
+}
diff --git a/gateway-service-hashicorp-vault/src/main/resources/META-INF/services/org.apache.knox.gateway.backend.hashicorp.vault.authentication.HashicorpVaultClientAuthenticationProvider b/gateway-service-hashicorp-vault/src/main/resources/META-INF/services/org.apache.knox.gateway.backend.hashicorp.vault.authentication.HashicorpVaultClientAuthenticationProvider
new file mode 100644
index 0000000..7d67bb9
--- /dev/null
+++ b/gateway-service-hashicorp-vault/src/main/resources/META-INF/services/org.apache.knox.gateway.backend.hashicorp.vault.authentication.HashicorpVaultClientAuthenticationProvider
@@ -0,0 +1,20 @@
+##########################################################################
+# 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.
+##########################################################################
+
+org.apache.knox.gateway.backend.hashicorp.vault.authentication.KubernetesHashicorpVaultClientAuthenticationProvider
+org.apache.knox.gateway.backend.hashicorp.vault.authentication.TokenHashicorpVaultClientAuthenticationProvider
diff --git a/gateway-service-hashicorp-vault/src/main/resources/META-INF/services/org.apache.knox.gateway.security.RemoteAliasServiceProvider b/gateway-service-hashicorp-vault/src/main/resources/META-INF/services/org.apache.knox.gateway.security.RemoteAliasServiceProvider
new file mode 100644
index 0000000..da06228
--- /dev/null
+++ b/gateway-service-hashicorp-vault/src/main/resources/META-INF/services/org.apache.knox.gateway.security.RemoteAliasServiceProvider
@@ -0,0 +1,19 @@
+##########################################################################
+# 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.
+##########################################################################
+
+org.apache.knox.gateway.backend.hashicorp.vault.HashicorpVaultRemoteAliasServiceProvider
diff --git a/gateway-service-hashicorp-vault/src/test/java/org/apache/knox/gateway/backend/hashicorp/vault/TestHashicorpVaultAliasService.java b/gateway-service-hashicorp-vault/src/test/java/org/apache/knox/gateway/backend/hashicorp/vault/TestHashicorpVaultAliasService.java
new file mode 100644
index 0000000..85a0186
--- /dev/null
+++ b/gateway-service-hashicorp-vault/src/test/java/org/apache/knox/gateway/backend/hashicorp/vault/TestHashicorpVaultAliasService.java
@@ -0,0 +1,282 @@
+/*
+ * 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.backend.hashicorp.vault;
+
+import org.apache.knox.gateway.backend.hashicorp.vault.authentication.HashicorpVaultClientAuthenticationProvider;
+import org.apache.knox.gateway.backend.hashicorp.vault.authentication.TokenHashicorpVaultClientAuthenticationProvider;
+import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.services.security.AliasService;
+import org.apache.knox.gateway.services.security.AliasServiceException;
+import org.apache.knox.test.category.VerifyTest;
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.containers.Container;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.images.builder.Transferable;
+import org.testcontainers.vault.VaultContainer;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Random;
+
+import static org.apache.knox.gateway.backend.hashicorp.vault.HashicorpVaultAliasService.VAULT_SEPARATOR;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeNoException;
+
+@Category(VerifyTest.class)
+public class TestHashicorpVaultAliasService {
+  private static final Logger LOG = LoggerFactory.getLogger(TestHashicorpVaultAliasService.class);
+
+  private static final Random RANDOM = new Random();
+  private static final String vaultVersion = "1.0.3";
+  private static final String vaultImage = "vault:" + vaultVersion;
+  private static final Integer vaultPort = 8200;
+  private static final String vaultToken = "myroot";
+  private String vaultSecretsEngine;
+
+  private static GenericContainer vaultContainer;
+  private static String vaultAddress;
+
+  @BeforeClass
+  public static void setUpClass() {
+    try {
+      vaultContainer = new VaultContainer(vaultImage)
+                           .withVaultPort(vaultPort)
+                           .withVaultToken(vaultToken)
+                           .waitingFor(Wait.forHttp("/"));
+    } catch (IllegalStateException e) {
+      assumeNoException(e);
+    }
+
+    vaultContainer.start();
+    vaultAddress = String.format(Locale.ROOT,
+        "http://%s:%s",
+        vaultContainer.getContainerIpAddress(),
+        vaultContainer.getMappedPort(vaultPort));
+
+    assertTrue(vaultContainer.isRunning());
+  }
+
+  @Before
+  public void setUp() throws Exception {
+    vaultSecretsEngine = "knox-secret-" + RANDOM.nextInt(100);
+
+    setupVaultSecretsEngine();
+  }
+
+  private void setupVaultSecretsEngine() throws Exception {
+    vaultContainer.execInContainer("vault", "secrets", "enable", "-path=" + vaultSecretsEngine,
+        "-version=2", "kv");
+    LOG.debug("created KV secrets engine %s", vaultSecretsEngine);
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    cleanupVaultPolicy();
+    cleanupVaultSecretsEngine();
+
+    vaultSecretsEngine = null;
+  }
+
+  private void cleanupVaultSecretsEngine() throws Exception {
+    vaultContainer.execInContainer("vault", "secrets", "disable", vaultSecretsEngine);
+    LOG.debug("deleted KV secrets engine %s", vaultSecretsEngine);
+  }
+
+  @AfterClass
+  public static void tearDownClass() {
+    if(vaultContainer != null) {
+      vaultContainer.stop();
+    }
+  }
+
+  private String getKnoxToken(boolean forceKnoxSpecifcToken) throws Exception {
+    if(forceKnoxSpecifcToken) {
+      LOG.info("Using Knox specific token");
+      Container.ExecResult tokenCreationExecResult = vaultContainer.execInContainer("vault", "token",
+          "create", "-policy=" + getVaultPolicy(), "-field=token");
+      return tokenCreationExecResult.getStdout().trim();
+    }
+    LOG.info("Using root token");
+    return vaultToken;
+  }
+
+  private String getVaultPolicy() {
+    return vaultSecretsEngine + "-policy";
+  }
+
+  @Test
+  public void testVaultIntegration() throws Exception {
+    String vaultPathPrefix = generatePathPrefix();
+    setupVaultPolicy(VAULT_SEPARATOR);
+
+    GatewayConfig gatewayConfig = EasyMock.createNiceMock(GatewayConfig.class);
+
+    Map<String, String> remoteAliasServiceConfiguration = new HashMap<>();
+    remoteAliasServiceConfiguration.put(HashicorpVaultAliasService.VAULT_ADDRESS_KEY,
+        vaultAddress);
+    remoteAliasServiceConfiguration.put(HashicorpVaultAliasService.VAULT_SECRETS_ENGINE_KEY,
+        vaultSecretsEngine);
+    remoteAliasServiceConfiguration.put(HashicorpVaultAliasService.VAULT_PATH_PREFIX_KEY,
+        vaultPathPrefix);
+    remoteAliasServiceConfiguration.put(HashicorpVaultClientAuthenticationProvider.AUTHENTICATION_TYPE_KEY,
+        TokenHashicorpVaultClientAuthenticationProvider.TYPE);
+    remoteAliasServiceConfiguration.put(TokenHashicorpVaultClientAuthenticationProvider.TOKEN_KEY,
+        getKnoxToken(RANDOM.nextBoolean()));
+
+    EasyMock.expect(gatewayConfig.getRemoteAliasServiceConfiguration())
+        .andReturn(remoteAliasServiceConfiguration).anyTimes();
+    EasyMock.replay(gatewayConfig);
+
+    AliasService localAliasService = EasyMock.createNiceMock(AliasService.class);
+
+    AliasService aliasService = new HashicorpVaultAliasService(localAliasService);
+    aliasService.init(gatewayConfig, Collections.emptyMap());
+    aliasService.start();
+
+    String clusterName = "test-" + RANDOM.nextInt(100);
+    String alias = "abc-" + RANDOM.nextInt(100);
+    String aliasPassword = "def-" + RANDOM.nextInt(100);
+
+    assertEquals(0, aliasService.getAliasesForCluster(clusterName).size());
+
+    aliasService.addAliasForCluster(clusterName, alias, aliasPassword);
+
+    assertEquals(1, aliasService.getAliasesForCluster(clusterName).size());
+
+    char[] vaultAliasPassword = aliasService.getPasswordFromAliasForCluster(clusterName, alias);
+    assertEquals(aliasPassword, String.valueOf(vaultAliasPassword));
+
+    aliasService.removeAliasForCluster(clusterName, alias);
+    assertNull(aliasService.getPasswordFromAliasForCluster(clusterName, alias));
+    assertEquals(0, aliasService.getAliasesForCluster(clusterName).size());
+
+    aliasService.stop();
+  }
+
+  @Test
+  public void testVaultIntegrationPermissions() throws Exception {
+    String vaultPathPrefix = generatePathPrefix();
+    setupVaultPolicy("/invalidPrefix/");
+
+    GatewayConfig gatewayConfig = EasyMock.createNiceMock(GatewayConfig.class);
+
+    Map<String, String> remoteAliasServiceConfiguration = new HashMap<>();
+    remoteAliasServiceConfiguration.put(HashicorpVaultAliasService.VAULT_ADDRESS_KEY, vaultAddress);
+    remoteAliasServiceConfiguration.put(HashicorpVaultAliasService.VAULT_SECRETS_ENGINE_KEY,
+        vaultSecretsEngine);
+    remoteAliasServiceConfiguration.put(HashicorpVaultAliasService.VAULT_PATH_PREFIX_KEY,
+        vaultPathPrefix);
+    remoteAliasServiceConfiguration.put(HashicorpVaultClientAuthenticationProvider.AUTHENTICATION_TYPE_KEY,
+        TokenHashicorpVaultClientAuthenticationProvider.TYPE);
+    remoteAliasServiceConfiguration.put(TokenHashicorpVaultClientAuthenticationProvider.TOKEN_KEY,
+        getKnoxToken(true));
+
+    EasyMock.expect(gatewayConfig.getRemoteAliasServiceConfiguration())
+        .andReturn(remoteAliasServiceConfiguration).anyTimes();
+    EasyMock.replay(gatewayConfig);
+
+    AliasService localAliasService = EasyMock.createNiceMock(AliasService.class);
+
+    AliasService aliasService = new HashicorpVaultAliasService(localAliasService);
+    aliasService.init(gatewayConfig, Collections.emptyMap());
+    aliasService.start();
+
+    String clusterName = "test-" + RANDOM.nextInt(100);
+    String alias = "abc-" + RANDOM.nextInt(100);
+    String aliasPassword = "def-" + RANDOM.nextInt(100);
+
+    try {
+      aliasService.getAliasesForCluster(clusterName);
+      fail("Should have gotten a 403");
+    } catch (AliasServiceException e) {
+      assertTrue(e.getMessage().contains("Status 403 Forbidden"));
+    }
+
+    try {
+      aliasService.addAliasForCluster(clusterName, alias, aliasPassword);
+      fail("Should have gotten a 403");
+    } catch (AliasServiceException e) {
+      assertTrue(e.getMessage().contains("Status 403 Forbidden"));
+    }
+
+    try {
+      aliasService.getPasswordFromAliasForCluster(clusterName, alias);
+      fail("Should have gotten a 403");
+    } catch (AliasServiceException e) {
+      assertTrue(e.getMessage().contains("Status 403 Forbidden"));
+    }
+
+    try {
+      aliasService.removeAliasForCluster(clusterName, alias);
+      fail("Should have gotten a 403");
+    } catch (AliasServiceException e) {
+      assertTrue(e.getMessage().contains("Status 403 Forbidden"));
+    }
+
+    aliasService.stop();
+  }
+
+  private String generatePathPrefix() {
+    StringBuilder pathPrefix = new StringBuilder();
+    int numParts = RANDOM.nextInt(10);
+    for(int i = 0; i < numParts; i++) {
+      pathPrefix.append(VAULT_SEPARATOR).append(RANDOM.nextInt(10));
+    }
+    pathPrefix.append(VAULT_SEPARATOR);
+    String result = pathPrefix.toString();
+    LOG.info("Using path prefix: '{}'", result);
+    return result;
+  }
+
+  private void setupVaultPolicy(String pathPrefix) throws Exception {
+    String policy = "path \"" + vaultSecretsEngine + pathPrefix + "*\" {\n" +
+                        "  capabilities = [\"create\", \"read\", \"update\", \"delete\", \"list\"]\n" +
+                        "}";
+    LOG.info("policy: {}", policy);
+    String policyFilePath = "/tmp/" + getVaultPolicy() + ".hcl";
+    vaultContainer.copyFileToContainer(Transferable.of(policy.getBytes(StandardCharsets.UTF_8)),
+        policyFilePath);
+    vaultContainer.execInContainer("vault", "policy", "write", getVaultPolicy(), policyFilePath);
+    LOG.debug("created policy %s", getVaultPolicy());
+    vaultContainer.execInContainer("rm", "-f", policyFilePath);
+  }
+
+  private void cleanupVaultPolicy() {
+    try {
+      vaultContainer.execInContainer("vault", "policy", "delete", getVaultPolicy());
+      LOG.debug("deleted policy %s", getVaultPolicy());
+    } catch (Exception ignore) {
+      // ignore
+    }
+  }
+}
diff --git a/gateway-service-hashicorp-vault/src/test/resources/log4j.properties b/gateway-service-hashicorp-vault/src/test/resources/log4j.properties
new file mode 100644
index 0000000..e8643cd
--- /dev/null
+++ b/gateway-service-hashicorp-vault/src/test/resources/log4j.properties
@@ -0,0 +1,24 @@
+# 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.
+
+# Root logger option
+log4j.rootLogger=INFO, stdout
+
+# Direct log messages to stdout
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/AliasService.java b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/AliasService.java
index 7a7f76d..79d69d1 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/AliasService.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/AliasService.java
@@ -23,6 +23,7 @@ import java.util.List;
 import org.apache.knox.gateway.services.Service;
 
 public interface AliasService extends Service {
+  String NO_CLUSTER_NAME = "__gateway";
 
   List<String> getAliasesForCluster(String clusterName)
       throws AliasServiceException;
@@ -58,4 +59,4 @@ public interface AliasService extends Service {
 
   Certificate getCertificateForGateway(String alias)
       throws AliasServiceException;
-}
\ No newline at end of file
+}
diff --git a/pom.xml b/pom.xml
index c847983..ba4326c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -129,6 +129,7 @@
         <module>gateway-test-release-utils</module>
         <module>gateway-service-nifi</module>
         <module>gateway-docker</module>
+        <module>gateway-service-hashicorp-vault</module>
     </modules>
 
     <properties>
@@ -220,7 +221,9 @@
         <spotbugs.version>3.1.11</spotbugs.version>
         <spotbugs-maven-plugin.version>3.1.11</spotbugs-maven-plugin.version>
         <spring-core.version>5.1.5.RELEASE</spring-core.version>
+        <spring-vault.version>2.1.2.RELEASE</spring-vault.version>
         <taglibs-standard.version>1.2.5</taglibs-standard.version>
+        <testcontainers.version>1.10.6</testcontainers.version>
         <velocity.version>1.7</velocity.version>
         <xmltool.version>3.3</xmltool.version>
         <xml-matchers.version>0.10</xml-matchers.version>
@@ -1064,6 +1067,11 @@
                 <artifactId>gateway-shell-samples</artifactId>
                 <version>${project.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.apache.knox</groupId>
+                <artifactId>gateway-service-hashicorp-vault</artifactId>
+                <version>${project.version}</version>
+            </dependency>
 
             <dependency>
                 <groupId>org.glassfish.jersey.core</groupId>
@@ -1918,6 +1926,30 @@
             </dependency>
 
             <dependency>
+                <groupId>org.springframework.vault</groupId>
+                <artifactId>spring-vault-dependencies</artifactId>
+                <version>${spring-vault.version}</version>
+                <scope>import</scope>
+                <type>pom</type>
+            </dependency>
+
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-context</artifactId>
+                <version>${spring-core.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-beans</artifactId>
+                <version>${spring-core.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework</groupId>
+                <artifactId>spring-web</artifactId>
+                <version>${spring-core.version}</version>
+            </dependency>
+
+            <dependency>
                 <groupId>de.thetaphi</groupId>
                 <artifactId>forbiddenapis</artifactId>
                 <version>${forbiddenapis.version}</version>
@@ -2025,6 +2057,19 @@
                 <version>${curator-test.version}</version>
                 <scope>test</scope>
             </dependency>
+
+            <dependency>
+                <groupId>org.testcontainers</groupId>
+                <artifactId>testcontainers</artifactId>
+                <version>${testcontainers.version}</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.testcontainers</groupId>
+                <artifactId>vault</artifactId>
+                <version>${testcontainers.version}</version>
+                <scope>test</scope>
+            </dependency>
         </dependencies>
     </dependencyManagement>