GUACAMOLE-641: Consider existing tokens when injecting tokens from vault.

This commit is contained in:
Michael Jumper
2022-01-21 15:23:41 -08:00
parent 7641fa9222
commit b655866057
4 changed files with 34 additions and 22 deletions

View File

@@ -34,6 +34,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.protocol.GuacamoleConfiguration; import org.apache.guacamole.protocol.GuacamoleConfiguration;
import org.apache.guacamole.token.TokenFilter;
import org.apache.guacamole.vault.azure.conf.AzureKeyVaultAuthenticationException; import org.apache.guacamole.vault.azure.conf.AzureKeyVaultAuthenticationException;
import org.apache.guacamole.vault.azure.conf.AzureKeyVaultConfigurationService; import org.apache.guacamole.vault.azure.conf.AzureKeyVaultConfigurationService;
import org.apache.guacamole.vault.secret.CachedVaultSecretService; import org.apache.guacamole.vault.secret.CachedVaultSecretService;
@@ -123,8 +124,8 @@ public class AzureKeyVaultSecretService extends CachedVaultSecretService {
} }
@Override @Override
public Map<String, Future<String>> getTokens(GuacamoleConfiguration config) public Map<String, Future<String>> getTokens(GuacamoleConfiguration config,
throws GuacamoleException { TokenFilter filter) throws GuacamoleException {
return Collections.emptyMap(); return Collections.emptyMap();
} }

View File

@@ -23,6 +23,7 @@ import java.util.Map;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.protocol.GuacamoleConfiguration; import org.apache.guacamole.protocol.GuacamoleConfiguration;
import org.apache.guacamole.token.TokenFilter;
/** /**
* Generic service for retrieving the value of a secret stored in a vault. * Generic service for retrieving the value of a secret stored in a vault.
@@ -84,6 +85,11 @@ public interface VaultSecretService {
* being generated. This configuration may be empty or partial, * being generated. This configuration may be empty or partial,
* depending on the underlying implementation. * depending on the underlying implementation.
* *
* @param filter
* A TokenFilter instance that applies any tokens already available to
* be applied to the configuration of the Guacamole connection. These
* tokens will consist of tokens already supplied to connect().
*
* @return * @return
* A map of token names to their corresponding future values, where * A map of token names to their corresponding future values, where
* each token and value may be dynamically determined based on the * each token and value may be dynamically determined based on the
@@ -93,7 +99,7 @@ public interface VaultSecretService {
* If an error occurs producing the tokens and values required for the * If an error occurs producing the tokens and values required for the
* given configuration. * given configuration.
*/ */
Map<String, Future<String>> getTokens(GuacamoleConfiguration config) Map<String, Future<String>> getTokens(GuacamoleConfiguration config,
throws GuacamoleException; TokenFilter filter) throws GuacamoleException;
} }

View File

@@ -189,13 +189,18 @@ public class VaultUserContext extends TokenInjectingUserContext {
* may contain its own tokens, which will be substituted using values * may contain its own tokens, which will be substituted using values
* from the given filter. * from the given filter.
* *
* @param secretNameFilter
* The filter to use to substitute values for tokens in the names of
* secrets to be retrieved from the vault.
*
* @param config * @param config
* The GuacamoleConfiguration of the connection for which tokens are * The GuacamoleConfiguration of the connection for which tokens are
* being retrieved, if available. This may be null. * being retrieved, if available. This may be null.
* *
* @param filter * @param configFilter
* The filter to use to substitute values for tokens in the names of * A TokenFilter instance that applies any tokens already available to
* secrets to be retrieved from the vault. * be applied to the configuration of the Guacamole connection. These
* tokens will consist of tokens already supplied to connect().
* *
* @return * @return
* A Map of token name to Future, where each Future represents the * A Map of token name to Future, where each Future represents the
@@ -207,8 +212,8 @@ public class VaultUserContext extends TokenInjectingUserContext {
* vault due to an error. * vault due to an error.
*/ */
private Map<String, Future<String>> getTokens(Map<String, String> tokenMapping, private Map<String, Future<String>> getTokens(Map<String, String> tokenMapping,
GuacamoleConfiguration config, TokenFilter filter) TokenFilter secretNameFilter, GuacamoleConfiguration config,
throws GuacamoleException { TokenFilter configFilter) throws GuacamoleException {
// Populate map with pending secret retrieval operations corresponding // Populate map with pending secret retrieval operations corresponding
// to each mapped token // to each mapped token
@@ -219,7 +224,7 @@ public class VaultUserContext extends TokenInjectingUserContext {
// secrets which cannot be translated // secrets which cannot be translated
String secretName; String secretName;
try { try {
secretName = filter.filterStrict(entry.getValue()); secretName = secretNameFilter.filterStrict(entry.getValue());
} }
catch (GuacamoleTokenUndefinedException e) { catch (GuacamoleTokenUndefinedException e) {
logger.debug("Secret for token \"{}\" will not be retrieved. " logger.debug("Secret for token \"{}\" will not be retrieved. "
@@ -237,7 +242,7 @@ public class VaultUserContext extends TokenInjectingUserContext {
} }
// Additionally include any dynamic, parameter-based tokens // Additionally include any dynamic, parameter-based tokens
pendingTokens.putAll(secretService.getTokens(config)); pendingTokens.putAll(secretService.getTokens(config, configFilter));
return pendingTokens; return pendingTokens;
@@ -298,8 +303,8 @@ public class VaultUserContext extends TokenInjectingUserContext {
} }
@Override @Override
protected Map<String, String> getTokens(ConnectionGroup connectionGroup) protected void addTokens(ConnectionGroup connectionGroup,
throws GuacamoleException { Map<String, String> tokens) throws GuacamoleException {
String name = connectionGroup.getName(); String name = connectionGroup.getName();
String identifier = connectionGroup.getIdentifier(); String identifier = connectionGroup.getIdentifier();
@@ -313,7 +318,8 @@ public class VaultUserContext extends TokenInjectingUserContext {
// Substitute tokens producing secret names, retrieving and storing // Substitute tokens producing secret names, retrieving and storing
// those secrets as parameter tokens // those secrets as parameter tokens
return resolve(getTokens(confService.getTokenMapping(), null, filter)); tokens.putAll(resolve(getTokens(confService.getTokenMapping(), filter,
null, new TokenFilter(tokens))));
} }
@@ -353,7 +359,7 @@ public class VaultUserContext extends TokenInjectingUserContext {
} }
@Override @Override
protected Map<String, String> getTokens(Connection connection) protected void addTokens(Connection connection, Map<String, String> tokens)
throws GuacamoleException { throws GuacamoleException {
String name = connection.getName(); String name = connection.getName();
@@ -392,7 +398,8 @@ public class VaultUserContext extends TokenInjectingUserContext {
// Substitute tokens producing secret names, retrieving and storing // Substitute tokens producing secret names, retrieving and storing
// those secrets as parameter tokens // those secrets as parameter tokens
return resolve(getTokens(confService.getTokenMapping(), config, filter)); tokens.putAll(resolve(getTokens(confService.getTokenMapping(), filter,
config, new TokenFilter(tokens))));
} }

View File

@@ -31,6 +31,7 @@ import java.util.concurrent.Future;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.protocol.GuacamoleConfiguration; import org.apache.guacamole.protocol.GuacamoleConfiguration;
import org.apache.guacamole.token.TokenFilter;
import org.apache.guacamole.vault.secret.VaultSecretService; import org.apache.guacamole.vault.secret.VaultSecretService;
/** /**
@@ -71,14 +72,11 @@ public class KsmSecretService implements VaultSecretService {
} }
@Override @Override
public Map<String, Future<String>> getTokens(GuacamoleConfiguration config) public Map<String, Future<String>> getTokens(GuacamoleConfiguration config,
throws GuacamoleException { TokenFilter filter) throws GuacamoleException {
Map<String, Future<String>> tokens = new HashMap<>(); Map<String, Future<String>> tokens = new HashMap<>();
// TODO: Ensure tokens within parameters are evaluated when considering
// whether a KSM record matches (ie: "username" might be ${GUAC_USERNAME})
// TODO: Verify protocol before assuming meaning of "hostname" // TODO: Verify protocol before assuming meaning of "hostname"
// parameter // parameter
@@ -87,7 +85,7 @@ public class KsmSecretService implements VaultSecretService {
// Retrieve and define server-specific tokens, if any // Retrieve and define server-specific tokens, if any
String hostname = parameters.get("hostname"); String hostname = parameters.get("hostname");
if (hostname != null && !hostname.isEmpty()) { if (hostname != null && !hostname.isEmpty()) {
KeeperRecord record = ksm.getRecordByHost(hostname); KeeperRecord record = ksm.getRecordByHost(filter.filter(hostname));
if (record != null) { if (record != null) {
// Username of server-related record // Username of server-related record