diff --git a/extensions/guacamole-vault/modules/guacamole-vault-azure/src/main/java/org/apache/guacamole/vault/azure/conf/AzureKeyVaultConfigurationService.java b/extensions/guacamole-vault/modules/guacamole-vault-azure/src/main/java/org/apache/guacamole/vault/azure/conf/AzureKeyVaultConfigurationService.java index bed7b264f..16620c2f6 100644 --- a/extensions/guacamole-vault/modules/guacamole-vault-azure/src/main/java/org/apache/guacamole/vault/azure/conf/AzureKeyVaultConfigurationService.java +++ b/extensions/guacamole-vault/modules/guacamole-vault-azure/src/main/java/org/apache/guacamole/vault/azure/conf/AzureKeyVaultConfigurationService.java @@ -47,6 +47,13 @@ public class AzureKeyVaultConfigurationService extends VaultConfigurationService */ private static final String TOKEN_MAPPING_FILENAME = "azure-keyvault-token-mapping.yml"; + /** + * The name of the properties file containing Guacamole configuration + * properties whose values are the names of corresponding secrets within + * Azure Key Vault. + */ + private static final String PROPERTIES_FILENAME = "guacamole.properties.azure"; + /** * The number of milliseconds that each retrieved secret should be cached * for. @@ -101,12 +108,15 @@ public class AzureKeyVaultConfigurationService extends VaultConfigurationService /** * Creates a new AzureKeyVaultConfigurationService which reads the token - * mapping from "azure-keyvault-token-mapping.yml". The token mapping is a - * YAML file which lists each connection parameter token and the name of - * the secret from which the value for that token should be read. + * mapping from "azure-keyvault-token-mapping.yml" and properties from + * "guacamole.properties.azure". The token mapping is a YAML file which + * lists each connection parameter token and the name of the secret from + * which the value for that token should be read, while the properties + * file is an alternative to guacamole.properties where each property + * value is the name of a secret containing the actual value. */ public AzureKeyVaultConfigurationService() { - super(TOKEN_MAPPING_FILENAME); + super(TOKEN_MAPPING_FILENAME, PROPERTIES_FILENAME); } /** 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 index a18b9c05a..440ef95d1 100644 --- 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 @@ -22,10 +22,12 @@ 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; /** @@ -47,10 +49,22 @@ public abstract class VaultAuthenticationProvider * * @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) { + 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 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 index 36a74ea4c..a666a7b97 100644 --- 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 @@ -27,10 +27,16 @@ 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. @@ -47,6 +53,9 @@ public abstract class VaultConfigurationService { @Inject private Environment environment; + @Inject + private VaultSecretService secretService; + /** * ObjectMapper for deserializing YAML. */ @@ -58,15 +67,30 @@ public abstract class VaultConfigurationService { */ 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 - * mapping from a YAML file having the given name. + * 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) { + protected VaultConfigurationService(String tokenMappingFilename, + String propertiesFilename) { this.tokenMappingFilename = tokenMappingFilename; + this.propertiesFilename = propertiesFilename; } /** @@ -114,4 +138,53 @@ public abstract class VaultConfigurationService { } + /** + * Returns a GuacamoleProperties instance which automatically reads the + * values of requested properties from the vault. The name of the secret + * corresponding to a property stored in the vault is defined via the + * properties filename supplied at construction time. + * + * @return + * A GuacamoleProperties instance which automatically reads property + * values from the vault. + * + * @throws GuacamoleException + * If the properties file containing the property/secret mappings + * exists but cannot be read. + */ + public GuacamoleProperties getProperties() throws GuacamoleException { + + // Use empty properties if file cannot be found + File propFile = new File(environment.getGuacamoleHome(), propertiesFilename); + if (!propFile.exists()) + return new PropertiesGuacamoleProperties(new Properties()); + + // Automatically pull properties from vault + return new FileGuacamoleProperties(propFile) { + + @Override + public String getProperty(String name) throws GuacamoleException { + try { + + String secretName = super.getProperty(name); + if (secretName == null) + return null; + + return secretService.getValue(secretName).get(); + + } + catch (InterruptedException | ExecutionException e) { + + if (e.getCause() instanceof GuacamoleException) + throw (GuacamoleException) e; + + throw new GuacamoleServerException(String.format("Property " + + "\"%s\" could not be retrieved from the vault.", name), e); + } + } + + }; + + } + } diff --git a/extensions/guacamole-vault/modules/guacamole-vault-ksm/src/main/java/org/apache/guacamole/vault/ksm/conf/KsmConfigurationService.java b/extensions/guacamole-vault/modules/guacamole-vault-ksm/src/main/java/org/apache/guacamole/vault/ksm/conf/KsmConfigurationService.java index 398b9bb3a..38bcaaef1 100644 --- a/extensions/guacamole-vault/modules/guacamole-vault-ksm/src/main/java/org/apache/guacamole/vault/ksm/conf/KsmConfigurationService.java +++ b/extensions/guacamole-vault/modules/guacamole-vault-ksm/src/main/java/org/apache/guacamole/vault/ksm/conf/KsmConfigurationService.java @@ -46,6 +46,13 @@ public class KsmConfigurationService extends VaultConfigurationService { */ private static final String TOKEN_MAPPING_FILENAME = "ksm-token-mapping.yml"; + /** + * The name of the properties file containing Guacamole configuration + * properties whose values are the names of corresponding secrets within + * Keeper Secrets Manager. + */ + private static final String PROPERTIES_FILENAME = "guacamole.properties.ksm"; + /** * The base64-encoded configuration information generated by the Keeper * Commander CLI tool. @@ -71,12 +78,15 @@ public class KsmConfigurationService extends VaultConfigurationService { /** * Creates a new KsmConfigurationService which reads the configuration - * from "ksm-token-mapping.yml". The token mapping is a YAML file which - * lists each connection parameter token and the title of the secret from - * which the value for that token should be read. + * from "ksm-token-mapping.yml" and properties from + * "guacamole.properties.ksm". The token mapping is a YAML file which lists + * each connection parameter token and the name of the secret from which + * the value for that token should be read, while the properties file is an + * alternative to guacamole.properties where each property value is the + * name of a secret containing the actual value. */ public KsmConfigurationService() { - super(TOKEN_MAPPING_FILENAME); + super(TOKEN_MAPPING_FILENAME, PROPERTIES_FILENAME); } /**