diff --git a/doc/licenses/bouncycastle-fips-1.0.2.1/LICENSE b/doc/licenses/bouncycastle-fips-1.0.2.1/LICENSE
new file mode 100644
index 000000000..a02bc176b
--- /dev/null
+++ b/doc/licenses/bouncycastle-fips-1.0.2.1/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2000 - 2021 The Legion of the Bouncy Castle Inc.
+(https://www.bouncycastle.org)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/doc/licenses/bouncycastle-fips-1.0.2.1/README b/doc/licenses/bouncycastle-fips-1.0.2.1/README
new file mode 100644
index 000000000..4d92a89ee
--- /dev/null
+++ b/doc/licenses/bouncycastle-fips-1.0.2.1/README
@@ -0,0 +1,8 @@
+BouncyCastle FIPS Distribution (https://www.bouncycastle.org/fips-java)
+-----------------------------------------------------------------------
+
+ Version: 1.0.2.1
+ From: 'The Legion of Bouncy Castle' (https://www.bouncycastle.org)
+ License(s):
+ MIT (bundled/bouncycastle-fips-1.0.2.1/LICENSE)
+
diff --git a/doc/licenses/bouncycastle-fips-1.0.2.1/dep-coordinates.txt b/doc/licenses/bouncycastle-fips-1.0.2.1/dep-coordinates.txt
new file mode 100644
index 000000000..854f49a58
--- /dev/null
+++ b/doc/licenses/bouncycastle-fips-1.0.2.1/dep-coordinates.txt
@@ -0,0 +1 @@
+org.bouncycastle:bc-fips:jar:1.0.2.1
diff --git a/doc/licenses/jetbrains-annotations-13.0/README b/doc/licenses/jetbrains-annotations-13.0/README
new file mode 100644
index 000000000..e09618cab
--- /dev/null
+++ b/doc/licenses/jetbrains-annotations-13.0/README
@@ -0,0 +1,9 @@
+Annotations for JVM-based languages
+(https://github.com/JetBrains/java-annotations)
+-----------------------------------------------
+
+ Version: 13.0
+ From: 'JetBrains s.r.o.' (http://www.jetbrains.com)
+ License(s):
+ Apache v2.0
+
diff --git a/doc/licenses/jetbrains-annotations-13.0/dep-coordinates.txt b/doc/licenses/jetbrains-annotations-13.0/dep-coordinates.txt
new file mode 100644
index 000000000..bb558cd6c
--- /dev/null
+++ b/doc/licenses/jetbrains-annotations-13.0/dep-coordinates.txt
@@ -0,0 +1 @@
+org.jetbrains:annotations:jar:13.0
diff --git a/doc/licenses/kotlin-1.5.30/NOTICE.txt b/doc/licenses/kotlin-1.5.30/NOTICE.txt
new file mode 100644
index 000000000..3efb9a198
--- /dev/null
+++ b/doc/licenses/kotlin-1.5.30/NOTICE.txt
@@ -0,0 +1,2 @@
+Kotlin Compiler
+Copyright 2010-2020 JetBrains s.r.o and respective authors and developers
diff --git a/doc/licenses/kotlin-1.5.30/README b/doc/licenses/kotlin-1.5.30/README
new file mode 100644
index 000000000..f2de926e9
--- /dev/null
+++ b/doc/licenses/kotlin-1.5.30/README
@@ -0,0 +1,8 @@
+Kotlin (https://kotlinlang.org/)
+--------------------------------
+
+ Version: 1.5.30
+ From: 'JetBrains s.r.o and respective authors and developers'
+ License(s):
+ Apache v2.0
+
diff --git a/doc/licenses/kotlin-1.5.30/dep-coordinates.txt b/doc/licenses/kotlin-1.5.30/dep-coordinates.txt
new file mode 100644
index 000000000..474a373f0
--- /dev/null
+++ b/doc/licenses/kotlin-1.5.30/dep-coordinates.txt
@@ -0,0 +1,5 @@
+org.jetbrains.kotlin:kotlin-reflect:jar:1.5.30
+org.jetbrains.kotlin:kotlin-stdlib:jar:1.5.30
+org.jetbrains.kotlin:kotlin-stdlib-common:jar:1.5.30
+org.jetbrains.kotlin:kotlin-stdlib-jdk8:jar:1.5.30
+org.jetbrains.kotlin:kotlin-stdlib-jdk7:jar:1.5.30
diff --git a/doc/licenses/kotlinx-serialization-1.2.1/NOTICE.txt b/doc/licenses/kotlinx-serialization-1.2.1/NOTICE.txt
new file mode 100644
index 000000000..16f7e9bee
--- /dev/null
+++ b/doc/licenses/kotlinx-serialization-1.2.1/NOTICE.txt
@@ -0,0 +1,2 @@
+kotlinx.serialization library.
+Copyright 2017-2019 JetBrains s.r.o and respective authors and developers
diff --git a/doc/licenses/kotlinx-serialization-1.2.1/README b/doc/licenses/kotlinx-serialization-1.2.1/README
new file mode 100644
index 000000000..5ded4539e
--- /dev/null
+++ b/doc/licenses/kotlinx-serialization-1.2.1/README
@@ -0,0 +1,8 @@
+Kotlin Serialization (https://github.com/Kotlin/kotlinx.serialization)
+----------------------------------------------------------------------
+
+ Version: 1.2.1
+ From: 'JetBrains s.r.o and respective authors and developers'
+ License(s):
+ Apache v2.0
+
diff --git a/doc/licenses/kotlinx-serialization-1.2.1/dep-coordinates.txt b/doc/licenses/kotlinx-serialization-1.2.1/dep-coordinates.txt
new file mode 100644
index 000000000..e6fe5522d
--- /dev/null
+++ b/doc/licenses/kotlinx-serialization-1.2.1/dep-coordinates.txt
@@ -0,0 +1,2 @@
+org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:jar:1.2.1
+org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:jar:1.2.1
diff --git a/doc/licenses/ksm-sdk-16.2.1/LICENSE b/doc/licenses/ksm-sdk-16.2.1/LICENSE
new file mode 100644
index 000000000..b63322a17
--- /dev/null
+++ b/doc/licenses/ksm-sdk-16.2.1/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Keeper Security
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/doc/licenses/ksm-sdk-16.2.1/README b/doc/licenses/ksm-sdk-16.2.1/README
new file mode 100644
index 000000000..4bf0527cf
--- /dev/null
+++ b/doc/licenses/ksm-sdk-16.2.1/README
@@ -0,0 +1,9 @@
+Keeper Secrets Manager Java SDK
+(https://github.com/Keeper-Security/secrets-manager)
+----------------------------------------------------
+
+ Version: 16.2.1
+ From: 'Keeper Security' (https://www.keepersecurity.com/)
+ License(s):
+ MIT (bundled/ksm-sdk-16.2.1/LICENSE)
+
diff --git a/doc/licenses/ksm-sdk-16.2.1/dep-coordinates.txt b/doc/licenses/ksm-sdk-16.2.1/dep-coordinates.txt
new file mode 100644
index 000000000..5b9330316
--- /dev/null
+++ b/doc/licenses/ksm-sdk-16.2.1/dep-coordinates.txt
@@ -0,0 +1 @@
+com.keepersecurity.secrets-manager:core:jar:16.2.1
diff --git a/doc/licenses/snakeyaml-1.27/README b/doc/licenses/snakeyaml-1.27/README
new file mode 100644
index 000000000..3fcd837d6
--- /dev/null
+++ b/doc/licenses/snakeyaml-1.27/README
@@ -0,0 +1,8 @@
+SnakeYAML (https://bitbucket.org/asomov/snakeyaml/)
+---------------------------------------------------
+
+ Version: 1.27
+ From: 'Andrey Somov' (https://bitbucket.org/asomov/)
+ License(s):
+ Apache v2.0
+
diff --git a/doc/licenses/snakeyaml-1.27/dep-coordinates.txt b/doc/licenses/snakeyaml-1.27/dep-coordinates.txt
new file mode 100644
index 000000000..d7cbad91a
--- /dev/null
+++ b/doc/licenses/snakeyaml-1.27/dep-coordinates.txt
@@ -0,0 +1 @@
+org.yaml:snakeyaml:jar:1.27
diff --git a/extensions/guacamole-vault/.ratignore b/extensions/guacamole-vault/.ratignore
new file mode 100644
index 000000000..e69de29bb
diff --git a/extensions/guacamole-vault/modules/guacamole-vault-base/.ratignore b/extensions/guacamole-vault/modules/guacamole-vault-base/.ratignore
new file mode 100644
index 000000000..e69de29bb
diff --git a/extensions/guacamole-vault/modules/guacamole-vault-base/pom.xml b/extensions/guacamole-vault/modules/guacamole-vault-base/pom.xml
new file mode 100644
index 000000000..96fe1180a
--- /dev/null
+++ b/extensions/guacamole-vault/modules/guacamole-vault-base/pom.xml
@@ -0,0 +1,74 @@
+
+
+
+
+ 4.0.0
+ org.apache.guacamole
+ guacamole-vault-base
+ jar
+ guacamole-vault-base
+ http://guacamole.apache.org/
+
+
+ UTF-8
+
+
+
+ org.apache.guacamole
+ guacamole-vault
+ 1.4.0
+ ../../
+
+
+
+
+
+
+ org.apache.guacamole
+ guacamole-ext
+ provided
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-yaml
+
+
+
+
+ com.google.inject
+ guice
+
+
+ com.google.inject.extensions
+ guice-assistedinject
+
+
+
+
+
diff --git a/extensions/guacamole-vault/modules/guacamole-vault-base/src/main/java/org/apache/guacamole/vault/VaultAuthenticationProvider.java b/extensions/guacamole-vault/modules/guacamole-vault-base/src/main/java/org/apache/guacamole/vault/VaultAuthenticationProvider.java
new file mode 100644
index 000000000..440ef95d1
--- /dev/null
+++ b/extensions/guacamole-vault/modules/guacamole-vault-base/src/main/java/org/apache/guacamole/vault/VaultAuthenticationProvider.java
@@ -0,0 +1,77 @@
+/*
+ * 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.guacamole.vault;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.environment.Environment;
+import org.apache.guacamole.net.auth.AbstractAuthenticationProvider;
+import org.apache.guacamole.net.auth.AuthenticatedUser;
+import org.apache.guacamole.net.auth.Credentials;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.vault.conf.VaultConfigurationService;
+import org.apache.guacamole.vault.user.VaultUserContextFactory;
+
+/**
+ * AuthenticationProvider implementation which automatically injects tokens
+ * containing the values of secrets retrieved from a vault.
+ */
+public abstract class VaultAuthenticationProvider
+ extends AbstractAuthenticationProvider {
+
+ /**
+ * Factory for creating instances of the relevant vault-specific
+ * UserContext implementation.
+ */
+ private final VaultUserContextFactory userContextFactory;
+
+ /**
+ * Creates a new VaultAuthenticationProvider which uses the given module to
+ * configure dependency injection.
+ *
+ * @param module
+ * The module to use to configure dependency injection.
+ *
+ * @throws GuacamoleException
+ * If the properties file containing vault-mapped Guacamole
+ * configuration properties exists but cannot be read.
+ */
+ protected VaultAuthenticationProvider(VaultAuthenticationProviderModule module)
+ throws GuacamoleException {
+
+ Injector injector = Guice.createInjector(module);
+ this.userContextFactory = injector.getInstance(VaultUserContextFactory.class);
+
+ // Automatically pull properties from vault
+ Environment environment = injector.getInstance(Environment.class);
+ VaultConfigurationService confService = injector.getInstance(VaultConfigurationService.class);
+ environment.addGuacamoleProperties(confService.getProperties());
+
+ }
+
+ @Override
+ public UserContext decorate(UserContext context,
+ AuthenticatedUser authenticatedUser, Credentials credentials)
+ throws GuacamoleException {
+ return userContextFactory.create(context);
+ }
+
+}
diff --git a/extensions/guacamole-vault/modules/guacamole-vault-base/src/main/java/org/apache/guacamole/vault/VaultAuthenticationProviderModule.java b/extensions/guacamole-vault/modules/guacamole-vault-base/src/main/java/org/apache/guacamole/vault/VaultAuthenticationProviderModule.java
new file mode 100644
index 000000000..a790d0119
--- /dev/null
+++ b/extensions/guacamole-vault/modules/guacamole-vault-base/src/main/java/org/apache/guacamole/vault/VaultAuthenticationProviderModule.java
@@ -0,0 +1,99 @@
+/*
+ * 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.guacamole.vault;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.environment.Environment;
+import org.apache.guacamole.environment.LocalEnvironment;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.vault.user.VaultUserContext;
+import org.apache.guacamole.vault.user.VaultUserContextFactory;
+
+/**
+ * Guice module which configures injections specific to the base support for
+ * key vaults. When adding support for a key vault provider, a subclass
+ * specific to that vault implementation will need to be created.
+ *
+ * @see KsmAuthenticationProviderModule
+ */
+public abstract class VaultAuthenticationProviderModule extends AbstractModule {
+
+ /**
+ * Guacamole server environment.
+ */
+ private final Environment environment;
+
+ /**
+ * Creates a new VaultAuthenticationProviderModule which configures
+ * dependency injection for the authentication provider of a vault
+ * implementation.
+ *
+ * @throws GuacamoleException
+ * If an error occurs while retrieving the Guacamole server
+ * environment.
+ */
+ public VaultAuthenticationProviderModule() throws GuacamoleException {
+ this.environment = LocalEnvironment.getInstance();
+ }
+
+ /**
+ * Configures injections for interfaces which are implementation-specific
+ * to the vault service in use. Subclasses MUST provide a version of this
+ * function which binds concrete implementations to the following
+ * interfaces:
+ *
+ * - VaultConfigurationService
+ * - VaultSecretService
+ *
+ * @see KsmAuthenticationProviderModule
+ */
+ protected abstract void configureVault();
+
+ /**
+ * Returns the instance of the Guacamole server environment which will be
+ * exposed to other classes via dependency injection.
+ *
+ * @return
+ * The instance of the Guacamole server environment which will be
+ * exposed via dependency injection.
+ */
+ protected Environment getEnvironment() {
+ return environment;
+ }
+
+ @Override
+ protected void configure() {
+
+ // Bind Guacamole server environment
+ bind(Environment.class).toInstance(environment);
+
+ // Bind factory for creating UserContexts
+ install(new FactoryModuleBuilder()
+ .implement(UserContext.class, VaultUserContext.class)
+ .build(VaultUserContextFactory.class));
+
+ // Bind all other implementation-specific interfaces
+ configureVault();
+
+ }
+
+}
diff --git a/extensions/guacamole-vault/modules/guacamole-vault-base/src/main/java/org/apache/guacamole/vault/conf/VaultConfigurationService.java b/extensions/guacamole-vault/modules/guacamole-vault-base/src/main/java/org/apache/guacamole/vault/conf/VaultConfigurationService.java
new file mode 100644
index 000000000..a666a7b97
--- /dev/null
+++ b/extensions/guacamole-vault/modules/guacamole-vault-base/src/main/java/org/apache/guacamole/vault/conf/VaultConfigurationService.java
@@ -0,0 +1,190 @@
+/*
+ * 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.guacamole.vault.conf;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import com.google.inject.Inject;
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ExecutionException;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
+import org.apache.guacamole.environment.Environment;
+import org.apache.guacamole.properties.FileGuacamoleProperties;
+import org.apache.guacamole.properties.GuacamoleProperties;
+import org.apache.guacamole.properties.PropertiesGuacamoleProperties;
+import org.apache.guacamole.vault.VaultAuthenticationProviderModule;
+import org.apache.guacamole.vault.secret.VaultSecretService;
+
+/**
+ * Base class for services which retrieve key vault configuration information.
+ * A concrete implementation of this class must be defined and bound for key
+ * vault support to work.
+ *
+ * @see VaultAuthenticationProviderModule
+ */
+public abstract class VaultConfigurationService {
+
+ /**
+ * The Guacamole server environment.
+ */
+ @Inject
+ private Environment environment;
+
+ @Inject
+ private VaultSecretService secretService;
+
+ /**
+ * ObjectMapper for deserializing YAML.
+ */
+ private final ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
+
+ /**
+ * The name of the file containing a YAML mapping of Guacamole parameter
+ * token to vault secret name.
+ */
+ private final String tokenMappingFilename;
+
+ /**
+ * The name of the properties file containing Guacamole configuration
+ * properties. Unlike guacamole.properties, the values of these properties
+ * are read from the vault. Each property is expected to contain a secret
+ * name instead of a property value.
+ */
+ private final String propertiesFilename;
+
+ /**
+ * Creates a new VaultConfigurationService which retrieves the token/secret
+ * mappings and Guacamole configuration properties from the files with the
+ * given names.
+ *
+ * @param tokenMappingFilename
+ * The name of the YAML file containing the token/secret mapping.
+ *
+ * @param propertiesFilename
+ * The name of the properties file containing Guacamole configuration
+ * properties whose values are the names of corresponding secrets.
+ */
+ protected VaultConfigurationService(String tokenMappingFilename,
+ String propertiesFilename) {
+ this.tokenMappingFilename = tokenMappingFilename;
+ this.propertiesFilename = propertiesFilename;
+ }
+
+ /**
+ * Returns a mapping dictating the name of the secret which maps to each
+ * parameter token. In the returned mapping, the value of each entry is the
+ * name of the secret to use to populate the value of the parameter token,
+ * and the key of each entry is the name of the parameter token which
+ * should receive the value of the secret.
+ *
+ * The name of the secret may contain its own tokens, which will be
+ * substituted using values from the given filter. See the definition of
+ * VaultUserContext for the names of these tokens and the contexts in which
+ * they can be applied to secret names.
+ *
+ * @return
+ * A mapping dictating the name of the secret which maps to each
+ * parameter token.
+ *
+ * @throws GuacamoleException
+ * If the YAML file defining the token/secret mapping cannot be read.
+ */
+ public Map getTokenMapping() throws GuacamoleException {
+
+ // Get configuration file from GUACAMOLE_HOME
+ File confFile = new File(environment.getGuacamoleHome(), tokenMappingFilename);
+ if (!confFile.exists())
+ return Collections.emptyMap();
+
+ // Deserialize token mapping from YAML
+ try {
+
+ Map mapping = mapper.readValue(confFile, new TypeReference