From 09b5dd42369bae0a44b4b6fbc6778226daf7cec0 Mon Sep 17 00:00:00 2001 From: Kevin Wooten Date: Mon, 8 Nov 2021 07:59:12 -0700 Subject: [PATCH] Support any dynamic credentials in VaultCredentialsProvider This change is in preparation of support for dynamic Vault credentials for RabbitMQ. It renames the "database" dynamic credentials supports to use "dynamic" in the names in an effort to be clear about its use. Vault dynamic credentials all have basicallly identical responses so the credentials provider should support any of them easily. This does essentially 3 things. 1. Renames `VaultDatabaseCredentials`, `VaultDBManager` and supporting classes to use the naming "Dynamic" 2. Adds a `VaultDynamicCredentialsType` enum (to determine plugin mount) and adds this as a parameter to `VaultDynamicCredentialsManager.getDynamicCredentials` 3. Adds an `expires-at` property to the returned credentials which is calculated from the Vault lease duration. --- .../credentials/CredentialsProvider.java | 1 + .../java/io/quarkus/vault/VaultProcessor.java | 8 +- .../database/VaultDatabaseCredentials.java | 7 - .../VaultDatabaseCredentialsData.java | 10 -- .../dto/dynamic/VaultDynamicCredentials.java | 7 + .../dynamic/VaultDynamicCredentialsData.java | 10 ++ .../runtime/VaultCredentialsProvider.java | 5 +- .../quarkus/vault/runtime/VaultDbManager.java | 124 ---------------- ...ials.java => VaultDynamicCredentials.java} | 4 +- .../VaultDynamicCredentialsManager.java | 134 ++++++++++++++++++ .../runtime/VaultDynamicCredentialsType.java | 17 +++ .../VaultInternalDatabaseSecretEngine.java | 14 -- ...nternalDynamicCredentialsSecretEngine.java | 14 ++ ...> VaultDynamicCredentialsManagerTest.java} | 70 ++++----- .../java/io/quarkus/vault/VaultITCase.java | 21 +-- 15 files changed, 241 insertions(+), 205 deletions(-) delete mode 100644 extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/database/VaultDatabaseCredentials.java delete mode 100644 extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/database/VaultDatabaseCredentialsData.java create mode 100644 extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/dynamic/VaultDynamicCredentials.java create mode 100644 extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/dynamic/VaultDynamicCredentialsData.java delete mode 100644 extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultDbManager.java rename extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/{VaultDynamicDatabaseCredentials.java => VaultDynamicCredentials.java} (79%) create mode 100644 extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultDynamicCredentialsManager.java create mode 100644 extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultDynamicCredentialsType.java delete mode 100644 extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/client/secretengine/VaultInternalDatabaseSecretEngine.java create mode 100644 extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/client/secretengine/VaultInternalDynamicCredentialsSecretEngine.java rename extensions/vault/runtime/src/test/java/io/quarkus/vault/runtime/{VaultDbManagerTest.java => VaultDynamicCredentialsManagerTest.java} (69%) diff --git a/extensions/credentials/runtime/src/main/java/io/quarkus/credentials/CredentialsProvider.java b/extensions/credentials/runtime/src/main/java/io/quarkus/credentials/CredentialsProvider.java index f80c72131da8e..3332de5b32bb1 100644 --- a/extensions/credentials/runtime/src/main/java/io/quarkus/credentials/CredentialsProvider.java +++ b/extensions/credentials/runtime/src/main/java/io/quarkus/credentials/CredentialsProvider.java @@ -9,6 +9,7 @@ public interface CredentialsProvider { String USER_PROPERTY_NAME = "user"; String PASSWORD_PROPERTY_NAME = "password"; + String EXPIRATION_TIMESTAMP_PROPERTY_NAME = "expires-at"; /** * Returns the credentials for a given credentials provider diff --git a/extensions/vault/deployment/src/main/java/io/quarkus/vault/VaultProcessor.java b/extensions/vault/deployment/src/main/java/io/quarkus/vault/VaultProcessor.java index bef92cf0ee02f..ac280e469bde6 100644 --- a/extensions/vault/deployment/src/main/java/io/quarkus/vault/VaultProcessor.java +++ b/extensions/vault/deployment/src/main/java/io/quarkus/vault/VaultProcessor.java @@ -21,7 +21,7 @@ import io.quarkus.vault.runtime.VaultAuthManager; import io.quarkus.vault.runtime.VaultConfigHolder; import io.quarkus.vault.runtime.VaultCredentialsProvider; -import io.quarkus.vault.runtime.VaultDbManager; +import io.quarkus.vault.runtime.VaultDynamicCredentialsManager; import io.quarkus.vault.runtime.VaultKubernetesAuthManager; import io.quarkus.vault.runtime.VaultKvManager; import io.quarkus.vault.runtime.VaultPKIManager; @@ -37,7 +37,7 @@ import io.quarkus.vault.runtime.client.authmethod.VaultInternalUserpassAuthMethod; import io.quarkus.vault.runtime.client.backend.VaultInternalSystemBackend; import io.quarkus.vault.runtime.client.dto.VaultModel; -import io.quarkus.vault.runtime.client.secretengine.VaultInternalDatabaseSecretEngine; +import io.quarkus.vault.runtime.client.secretengine.VaultInternalDynamicCredentialsSecretEngine; import io.quarkus.vault.runtime.client.secretengine.VaultInternalKvV1SecretEngine; import io.quarkus.vault.runtime.client.secretengine.VaultInternalKvV2SecretEngine; import io.quarkus.vault.runtime.client.secretengine.VaultInternalPKISecretEngine; @@ -87,7 +87,7 @@ AdditionalBeanBuildItem registerAdditionalBeans() { .addBeanClass(VaultSystemBackendManager.class) .addBeanClass(VaultKubernetesAuthManager.class) .addBeanClass(VaultAuthManager.class) - .addBeanClass(VaultDbManager.class) + .addBeanClass(VaultDynamicCredentialsManager.class) .addBeanClass(VertxVaultClient.class) .addBeanClass(VaultConfigHolder.class) .addBeanClass(VaultPKIManager.class) @@ -101,7 +101,7 @@ AdditionalBeanBuildItem registerAdditionalBeans() { .addBeanClass(VaultInternalKubernetesAuthMethod.class) .addBeanClass(VaultInternalTokenAuthMethod.class) .addBeanClass(VaultInternalUserpassAuthMethod.class) - .addBeanClass(VaultInternalDatabaseSecretEngine.class) + .addBeanClass(VaultInternalDynamicCredentialsSecretEngine.class) .addBeanClass(VaultInternalPKISecretEngine.class) .build(); } diff --git a/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/database/VaultDatabaseCredentials.java b/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/database/VaultDatabaseCredentials.java deleted file mode 100644 index 6b196fc47f774..0000000000000 --- a/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/database/VaultDatabaseCredentials.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.quarkus.vault.runtime.client.dto.database; - -import io.quarkus.vault.runtime.client.dto.AbstractVaultDTO; - -public class VaultDatabaseCredentials extends AbstractVaultDTO { - -} diff --git a/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/database/VaultDatabaseCredentialsData.java b/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/database/VaultDatabaseCredentialsData.java deleted file mode 100644 index 6de7de1e73f34..0000000000000 --- a/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/database/VaultDatabaseCredentialsData.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.quarkus.vault.runtime.client.dto.database; - -import io.quarkus.vault.runtime.client.dto.VaultModel; - -public class VaultDatabaseCredentialsData implements VaultModel { - - public String username; - public String password; - -} diff --git a/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/dynamic/VaultDynamicCredentials.java b/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/dynamic/VaultDynamicCredentials.java new file mode 100644 index 0000000000000..5b801683030dd --- /dev/null +++ b/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/dynamic/VaultDynamicCredentials.java @@ -0,0 +1,7 @@ +package io.quarkus.vault.runtime.client.dto.dynamic; + +import io.quarkus.vault.runtime.client.dto.AbstractVaultDTO; + +public class VaultDynamicCredentials extends AbstractVaultDTO { + +} diff --git a/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/dynamic/VaultDynamicCredentialsData.java b/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/dynamic/VaultDynamicCredentialsData.java new file mode 100644 index 0000000000000..7df80a9f6add8 --- /dev/null +++ b/extensions/vault/model/src/main/java/io/quarkus/vault/runtime/client/dto/dynamic/VaultDynamicCredentialsData.java @@ -0,0 +1,10 @@ +package io.quarkus.vault.runtime.client.dto.dynamic; + +import io.quarkus.vault.runtime.client.dto.VaultModel; + +public class VaultDynamicCredentialsData implements VaultModel { + + public String username; + public String password; + +} diff --git a/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultCredentialsProvider.java b/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultCredentialsProvider.java index cf6d6212b7ad1..b884218a7756b 100644 --- a/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultCredentialsProvider.java +++ b/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultCredentialsProvider.java @@ -20,7 +20,7 @@ public class VaultCredentialsProvider implements CredentialsProvider { @Inject private VaultKVSecretEngine vaultKVSecretEngine; @Inject - private VaultDbManager vaultDbManager; + private VaultDynamicCredentialsManager vaultDynamicCredentialsManager; @Inject private VaultConfigHolder vaultConfigHolder; @@ -34,7 +34,8 @@ public Map getCredentials(String credentialsProviderName) { } if (config.databaseCredentialsRole.isPresent()) { - return vaultDbManager.getDynamicDbCredentials(config.databaseCredentialsRole.get()); + return vaultDynamicCredentialsManager.getDynamicCredentials(VaultDynamicCredentialsType.DATABASE, + config.databaseCredentialsRole.get()); } if (config.kvPath.isPresent()) { diff --git a/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultDbManager.java b/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultDbManager.java deleted file mode 100644 index adf3e6b274c14..0000000000000 --- a/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultDbManager.java +++ /dev/null @@ -1,124 +0,0 @@ -package io.quarkus.vault.runtime; - -import static io.quarkus.credentials.CredentialsProvider.PASSWORD_PROPERTY_NAME; -import static io.quarkus.credentials.CredentialsProvider.USER_PROPERTY_NAME; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import javax.inject.Singleton; - -import org.jboss.logging.Logger; - -import io.quarkus.vault.runtime.client.VaultClientException; -import io.quarkus.vault.runtime.client.backend.VaultInternalSystemBackend; -import io.quarkus.vault.runtime.client.dto.database.VaultDatabaseCredentials; -import io.quarkus.vault.runtime.client.dto.sys.VaultRenewLease; -import io.quarkus.vault.runtime.client.secretengine.VaultInternalDatabaseSecretEngine; -import io.quarkus.vault.runtime.config.VaultBootstrapConfig; - -@Singleton -public class VaultDbManager { - - private static final Logger log = Logger.getLogger(VaultDbManager.class.getName()); - - ConcurrentHashMap credentialsCache = new ConcurrentHashMap<>(); - private VaultAuthManager vaultAuthManager; - private VaultConfigHolder vaultConfigHolder; - private VaultInternalSystemBackend vaultInternalSystemBackend; - private VaultInternalDatabaseSecretEngine vaultInternalDatabaseSecretEngine; - - public VaultDbManager(VaultConfigHolder vaultConfigHolder, VaultAuthManager vaultAuthManager, - VaultInternalSystemBackend vaultInternalSystemBackend, - VaultInternalDatabaseSecretEngine vaultInternalDatabaseSecretEngine) { - this.vaultConfigHolder = vaultConfigHolder; - this.vaultAuthManager = vaultAuthManager; - this.vaultInternalSystemBackend = vaultInternalSystemBackend; - this.vaultInternalDatabaseSecretEngine = vaultInternalDatabaseSecretEngine; - } - - private VaultBootstrapConfig getConfig() { - return vaultConfigHolder.getVaultBootstrapConfig(); - } - - public Map getDynamicDbCredentials(String databaseCredentialsRole) { - String clientToken = vaultAuthManager.getClientToken(); - VaultDynamicDatabaseCredentials currentCredentials = credentialsCache.get(databaseCredentialsRole); - VaultDynamicDatabaseCredentials credentials = getCredentials(currentCredentials, clientToken, databaseCredentialsRole); - credentialsCache.put(databaseCredentialsRole, credentials); - Map properties = new HashMap<>(); - properties.put(USER_PROPERTY_NAME, credentials.username); - properties.put(PASSWORD_PROPERTY_NAME, credentials.password); - return properties; - } - - public VaultDynamicDatabaseCredentials getCredentials(VaultDynamicDatabaseCredentials currentCredentials, - String clientToken, - String databaseCredentialsRole) { - - VaultDynamicDatabaseCredentials credentials = currentCredentials; - - // check lease is still valid - if (credentials != null) { - credentials = validate(credentials, clientToken); - } - - // extend lease if necessary - if (credentials != null && credentials.shouldExtend(getConfig().renewGracePeriod)) { - credentials = extend(credentials, clientToken, databaseCredentialsRole); - } - - // create lease if necessary - if (credentials == null || credentials.isExpired() - || credentials.expiresSoon(getConfig().renewGracePeriod)) { - credentials = create(clientToken, databaseCredentialsRole); - } - - return credentials; - } - - private VaultDynamicDatabaseCredentials validate(VaultDynamicDatabaseCredentials credentials, String clientToken) { - try { - vaultInternalSystemBackend.lookupLease(clientToken, credentials.leaseId); - return credentials; - } catch (VaultClientException e) { - if (e.getStatus() == 400) { // bad request - log.debug("lease " + credentials.leaseId + " has become invalid"); - return null; - } else { - throw e; - } - } - } - - private VaultDynamicDatabaseCredentials extend(VaultDynamicDatabaseCredentials currentCredentials, String clientToken, - String databaseCredentialsRole) { - VaultRenewLease vaultRenewLease = vaultInternalSystemBackend.renewLease(clientToken, currentCredentials.leaseId); - LeaseBase lease = new LeaseBase(vaultRenewLease.leaseId, vaultRenewLease.renewable, vaultRenewLease.leaseDurationSecs); - VaultDynamicDatabaseCredentials credentials = new VaultDynamicDatabaseCredentials(lease, currentCredentials.username, - currentCredentials.password); - sanityCheck(credentials, databaseCredentialsRole); - log.debug("extended " + databaseCredentialsRole + " credentials with: " - + credentials.getConfidentialInfo(getConfig().logConfidentialityLevel)); - return credentials; - } - - private VaultDynamicDatabaseCredentials create(String clientToken, String databaseCredentialsRole) { - VaultDatabaseCredentials vaultDatabaseCredentials = vaultInternalDatabaseSecretEngine.generateCredentials(clientToken, - databaseCredentialsRole); - LeaseBase lease = new LeaseBase(vaultDatabaseCredentials.leaseId, vaultDatabaseCredentials.renewable, - vaultDatabaseCredentials.leaseDurationSecs); - VaultDynamicDatabaseCredentials credentials = new VaultDynamicDatabaseCredentials(lease, - vaultDatabaseCredentials.data.username, - vaultDatabaseCredentials.data.password); - log.debug("generated " + databaseCredentialsRole + " credentials: " - + credentials.getConfidentialInfo(getConfig().logConfidentialityLevel)); - sanityCheck(credentials, databaseCredentialsRole); - return credentials; - } - - private void sanityCheck(VaultDynamicDatabaseCredentials credentials, String databaseCredentialsRole) { - credentials.leaseDurationSanityCheck(databaseCredentialsRole, getConfig().renewGracePeriod); - } -} diff --git a/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultDynamicDatabaseCredentials.java b/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultDynamicCredentials.java similarity index 79% rename from extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultDynamicDatabaseCredentials.java rename to extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultDynamicCredentials.java index 897d9a0377fc5..9bb793b8a5c8e 100644 --- a/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultDynamicDatabaseCredentials.java +++ b/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultDynamicCredentials.java @@ -3,12 +3,12 @@ import static io.quarkus.vault.runtime.LogConfidentialityLevel.LOW; import static io.quarkus.vault.runtime.LogConfidentialityLevel.MEDIUM; -public class VaultDynamicDatabaseCredentials extends LeaseBase { +public class VaultDynamicCredentials extends LeaseBase { public String username; public String password; - public VaultDynamicDatabaseCredentials(LeaseBase lease, String username, String password) { + public VaultDynamicCredentials(LeaseBase lease, String username, String password) { super(lease); this.username = username; this.password = password; diff --git a/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultDynamicCredentialsManager.java b/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultDynamicCredentialsManager.java new file mode 100644 index 0000000000000..262f0df3f2082 --- /dev/null +++ b/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultDynamicCredentialsManager.java @@ -0,0 +1,134 @@ +package io.quarkus.vault.runtime; + +import static io.quarkus.credentials.CredentialsProvider.EXPIRATION_TIMESTAMP_PROPERTY_NAME; +import static io.quarkus.credentials.CredentialsProvider.PASSWORD_PROPERTY_NAME; +import static io.quarkus.credentials.CredentialsProvider.USER_PROPERTY_NAME; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.inject.Singleton; + +import org.jboss.logging.Logger; + +import io.quarkus.vault.runtime.client.VaultClientException; +import io.quarkus.vault.runtime.client.backend.VaultInternalSystemBackend; +import io.quarkus.vault.runtime.client.dto.sys.VaultRenewLease; +import io.quarkus.vault.runtime.client.secretengine.VaultInternalDynamicCredentialsSecretEngine; +import io.quarkus.vault.runtime.config.VaultBootstrapConfig; + +@Singleton +public class VaultDynamicCredentialsManager { + + private static final Logger log = Logger.getLogger(VaultDynamicCredentialsManager.class.getName()); + + private ConcurrentHashMap credentialsCache = new ConcurrentHashMap<>(); + private VaultAuthManager vaultAuthManager; + private VaultConfigHolder vaultConfigHolder; + private VaultInternalSystemBackend vaultInternalSystemBackend; + private VaultInternalDynamicCredentialsSecretEngine vaultInternalDynamicCredentialsSecretEngine; + + public VaultDynamicCredentialsManager(VaultConfigHolder vaultConfigHolder, VaultAuthManager vaultAuthManager, + VaultInternalSystemBackend vaultInternalSystemBackend, + VaultInternalDynamicCredentialsSecretEngine vaultInternalDynamicCredentialsSecretEngine) { + this.vaultConfigHolder = vaultConfigHolder; + this.vaultAuthManager = vaultAuthManager; + this.vaultInternalSystemBackend = vaultInternalSystemBackend; + this.vaultInternalDynamicCredentialsSecretEngine = vaultInternalDynamicCredentialsSecretEngine; + } + + VaultDynamicCredentials getCachedCredentials(VaultDynamicCredentialsType type, String role) { + return credentialsCache.get(type + "@" + role); + } + + void putCachedCredentials(VaultDynamicCredentialsType type, String role, VaultDynamicCredentials credentials) { + credentialsCache.put(type + "@" + role, credentials); + } + + private VaultBootstrapConfig getConfig() { + return vaultConfigHolder.getVaultBootstrapConfig(); + } + + public Map getDynamicCredentials(VaultDynamicCredentialsType type, String role) { + String clientToken = vaultAuthManager.getClientToken(); + VaultDynamicCredentials currentCredentials = getCachedCredentials(type, role); + VaultDynamicCredentials credentials = getCredentials(currentCredentials, clientToken, type, role); + putCachedCredentials(type, role, credentials); + Map properties = new HashMap<>(); + properties.put(USER_PROPERTY_NAME, credentials.username); + properties.put(PASSWORD_PROPERTY_NAME, credentials.password); + properties.put(EXPIRATION_TIMESTAMP_PROPERTY_NAME, credentials.getExpireInstant().toString()); + return properties; + } + + public VaultDynamicCredentials getCredentials(VaultDynamicCredentials currentCredentials, + String clientToken, + VaultDynamicCredentialsType type, + String role) { + + VaultDynamicCredentials credentials = currentCredentials; + + // check lease is still valid + if (credentials != null) { + credentials = validate(credentials, clientToken); + } + + // extend lease if necessary + if (credentials != null && credentials.shouldExtend(getConfig().renewGracePeriod)) { + credentials = extend(credentials, clientToken, type, role); + } + + // create lease if necessary + if (credentials == null || credentials.isExpired() + || credentials.expiresSoon(getConfig().renewGracePeriod)) { + credentials = create(clientToken, type, role); + } + + return credentials; + } + + private VaultDynamicCredentials validate(VaultDynamicCredentials credentials, String clientToken) { + try { + vaultInternalSystemBackend.lookupLease(clientToken, credentials.leaseId); + return credentials; + } catch (VaultClientException e) { + if (e.getStatus() == 400) { // bad request + log.debug("lease " + credentials.leaseId + " has become invalid"); + return null; + } else { + throw e; + } + } + } + + private VaultDynamicCredentials extend(VaultDynamicCredentials currentCredentials, String clientToken, + VaultDynamicCredentialsType type, String role) { + VaultRenewLease vaultRenewLease = vaultInternalSystemBackend.renewLease(clientToken, currentCredentials.leaseId); + LeaseBase lease = new LeaseBase(vaultRenewLease.leaseId, vaultRenewLease.renewable, vaultRenewLease.leaseDurationSecs); + VaultDynamicCredentials credentials = new VaultDynamicCredentials(lease, currentCredentials.username, + currentCredentials.password); + sanityCheck(credentials, type, role); + log.debug("extended " + role + "(" + type.getDefaultName() + ") credentials:" + + credentials.getConfidentialInfo(getConfig().logConfidentialityLevel)); + return credentials; + } + + private VaultDynamicCredentials create(String clientToken, VaultDynamicCredentialsType type, String role) { + io.quarkus.vault.runtime.client.dto.dynamic.VaultDynamicCredentials vaultDynamicCredentials = vaultInternalDynamicCredentialsSecretEngine + .generateCredentials(clientToken, type.getDefaultName(), role); + LeaseBase lease = new LeaseBase(vaultDynamicCredentials.leaseId, vaultDynamicCredentials.renewable, + vaultDynamicCredentials.leaseDurationSecs); + VaultDynamicCredentials credentials = new VaultDynamicCredentials(lease, + vaultDynamicCredentials.data.username, + vaultDynamicCredentials.data.password); + log.debug("generated " + role + "(" + type.getDefaultName() + ") credentials:" + + credentials.getConfidentialInfo(getConfig().logConfidentialityLevel)); + sanityCheck(credentials, type, role); + return credentials; + } + + private void sanityCheck(VaultDynamicCredentials credentials, VaultDynamicCredentialsType type, String role) { + credentials.leaseDurationSanityCheck(role + " (" + type.getDefaultName() + ")", getConfig().renewGracePeriod); + } +} diff --git a/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultDynamicCredentialsType.java b/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultDynamicCredentialsType.java new file mode 100644 index 0000000000000..641c99f001d0d --- /dev/null +++ b/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/VaultDynamicCredentialsType.java @@ -0,0 +1,17 @@ +package io.quarkus.vault.runtime; + +public enum VaultDynamicCredentialsType { + DATABASE("database"), + + ; + + private String mount; + + VaultDynamicCredentialsType(String mount) { + this.mount = mount; + } + + public String getDefaultName() { + return mount; + } +} diff --git a/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/client/secretengine/VaultInternalDatabaseSecretEngine.java b/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/client/secretengine/VaultInternalDatabaseSecretEngine.java deleted file mode 100644 index 19d6faa7bd9ee..0000000000000 --- a/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/client/secretengine/VaultInternalDatabaseSecretEngine.java +++ /dev/null @@ -1,14 +0,0 @@ -package io.quarkus.vault.runtime.client.secretengine; - -import javax.inject.Singleton; - -import io.quarkus.vault.runtime.client.VaultInternalBase; -import io.quarkus.vault.runtime.client.dto.database.VaultDatabaseCredentials; - -@Singleton -public class VaultInternalDatabaseSecretEngine extends VaultInternalBase { - - public VaultDatabaseCredentials generateCredentials(String token, String databaseCredentialsRole) { - return vaultClient.get("database/creds/" + databaseCredentialsRole, token, VaultDatabaseCredentials.class); - } -} diff --git a/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/client/secretengine/VaultInternalDynamicCredentialsSecretEngine.java b/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/client/secretengine/VaultInternalDynamicCredentialsSecretEngine.java new file mode 100644 index 0000000000000..3ff12103ddf22 --- /dev/null +++ b/extensions/vault/runtime/src/main/java/io/quarkus/vault/runtime/client/secretengine/VaultInternalDynamicCredentialsSecretEngine.java @@ -0,0 +1,14 @@ +package io.quarkus.vault.runtime.client.secretengine; + +import javax.inject.Singleton; + +import io.quarkus.vault.runtime.client.VaultInternalBase; +import io.quarkus.vault.runtime.client.dto.dynamic.VaultDynamicCredentials; + +@Singleton +public class VaultInternalDynamicCredentialsSecretEngine extends VaultInternalBase { + + public VaultDynamicCredentials generateCredentials(String token, String type, String role) { + return vaultClient.get(type + "/creds/" + role, token, VaultDynamicCredentials.class); + } +} diff --git a/extensions/vault/runtime/src/test/java/io/quarkus/vault/runtime/VaultDbManagerTest.java b/extensions/vault/runtime/src/test/java/io/quarkus/vault/runtime/VaultDynamicCredentialsManagerTest.java similarity index 69% rename from extensions/vault/runtime/src/test/java/io/quarkus/vault/runtime/VaultDbManagerTest.java rename to extensions/vault/runtime/src/test/java/io/quarkus/vault/runtime/VaultDynamicCredentialsManagerTest.java index 76fde7c66d141..4dd0266a8d175 100644 --- a/extensions/vault/runtime/src/test/java/io/quarkus/vault/runtime/VaultDbManagerTest.java +++ b/extensions/vault/runtime/src/test/java/io/quarkus/vault/runtime/VaultDynamicCredentialsManagerTest.java @@ -2,6 +2,7 @@ import static io.quarkus.credentials.CredentialsProvider.PASSWORD_PROPERTY_NAME; import static io.quarkus.credentials.CredentialsProvider.USER_PROPERTY_NAME; +import static io.quarkus.vault.runtime.VaultDynamicCredentialsType.DATABASE; import static java.time.Instant.EPOCH; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -24,11 +25,11 @@ import io.quarkus.vault.runtime.client.authmethod.VaultInternalTokenAuthMethod; import io.quarkus.vault.runtime.client.authmethod.VaultInternalUserpassAuthMethod; import io.quarkus.vault.runtime.client.backend.VaultInternalSystemBackend; -import io.quarkus.vault.runtime.client.dto.database.VaultDatabaseCredentials; -import io.quarkus.vault.runtime.client.dto.database.VaultDatabaseCredentialsData; +import io.quarkus.vault.runtime.client.dto.dynamic.VaultDynamicCredentials; +import io.quarkus.vault.runtime.client.dto.dynamic.VaultDynamicCredentialsData; import io.quarkus.vault.runtime.client.dto.sys.VaultLeasesLookup; import io.quarkus.vault.runtime.client.dto.sys.VaultRenewLease; -import io.quarkus.vault.runtime.client.secretengine.VaultInternalDatabaseSecretEngine; +import io.quarkus.vault.runtime.client.secretengine.VaultInternalDynamicCredentialsSecretEngine; import io.quarkus.vault.runtime.config.VaultAppRoleAuthenticationConfig; import io.quarkus.vault.runtime.config.VaultAuthenticationConfig; import io.quarkus.vault.runtime.config.VaultBootstrapConfig; @@ -36,24 +37,26 @@ import io.quarkus.vault.runtime.config.VaultTlsConfig; import io.quarkus.vault.runtime.config.VaultUserpassAuthenticationConfig; -public class VaultDbManagerTest { +public class VaultDynamicCredentialsManagerTest { VaultBootstrapConfig config = createConfig(); TlsConfig tlsConfig = new TlsConfig(); - VaultDatabaseCredentials credentials = new VaultDatabaseCredentials(); + VaultDynamicCredentials credentials = new VaultDynamicCredentials(); VaultLeasesLookup vaultLeasesLookup = new VaultLeasesLookup(); AtomicBoolean lookupLeaseShouldReturn400 = new AtomicBoolean(false); VaultRenewLease vaultRenewLease = new VaultRenewLease(); VaultConfigHolder vaultConfigHolder = new VaultConfigHolder().setVaultBootstrapConfig(config); VaultClient vaultClient = createVaultClient(); VaultInternalSystemBackend vaultInternalSystemBackend = createSystemBackend(); - VaultInternalDatabaseSecretEngine vaultInternalDatabaseSecretEngine = createVaultInternalDatabaseSecretEngine(); + VaultInternalDynamicCredentialsSecretEngine vaultInternalDynamicCredentialsSecretEngine = createVaultInternalDynamicCredentialsSecretEngine(); VaultAuthManager vaultAuthManager = new VaultAuthManager(vaultConfigHolder, vaultInternalSystemBackend, new VaultInternalAppRoleAuthMethod(), new VaultInternalKubernetesAuthMethod(), new VaultInternalUserpassAuthMethod(), new VaultInternalTokenAuthMethod()); - VaultDbManager vaultDbManager = new VaultDbManager(vaultConfigHolder, vaultAuthManager, - vaultInternalSystemBackend, vaultInternalDatabaseSecretEngine); + VaultDynamicCredentialsManager vaultDynamicCredentialsManager = new VaultDynamicCredentialsManager(vaultConfigHolder, + vaultAuthManager, + vaultInternalSystemBackend, + vaultInternalDynamicCredentialsSecretEngine); String mydbrole = "mydbrole"; String mylease = "mylease"; @@ -63,9 +66,9 @@ public void after() { } @Test - public void getDynamicDbCredentials() { + public void getDynamicCredentials() { - credentials.data = new VaultDatabaseCredentialsData(); + credentials.data = new VaultDynamicCredentialsData(); credentials.data.username = "bob"; credentials.data.password = "sinclair1"; credentials.leaseId = mylease; @@ -76,42 +79,43 @@ public void getDynamicDbCredentials() { vaultRenewLease.leaseDurationSecs = 10; vaultRenewLease.renewable = true; - Map properties = vaultDbManager.getDynamicDbCredentials(mydbrole); + Map properties = vaultDynamicCredentialsManager.getDynamicCredentials( + DATABASE, mydbrole); assertEquals("bob", properties.get(USER_PROPERTY_NAME)); assertEquals("sinclair1", properties.get(PASSWORD_PROPERTY_NAME)); - VaultDynamicDatabaseCredentials vaultDynamicDatabaseCredentials = vaultDbManager.credentialsCache.get(mydbrole); - vaultDynamicDatabaseCredentials.created = EPOCH; - vaultDynamicDatabaseCredentials.nowSupplier = () -> EPOCH.plusSeconds(1); + var vaultDynamicCredentials = vaultDynamicCredentialsManager.getCachedCredentials(DATABASE, mydbrole); + vaultDynamicCredentials.created = EPOCH; + vaultDynamicCredentials.nowSupplier = () -> EPOCH.plusSeconds(1); credentials.data.password = "sinclair2"; - properties = vaultDbManager.getDynamicDbCredentials(mydbrole); + properties = vaultDynamicCredentialsManager.getDynamicCredentials(DATABASE, mydbrole); assertEquals("sinclair1", properties.get(PASSWORD_PROPERTY_NAME), "returned from cache"); lookupLeaseShouldReturn400.set(true); - properties = vaultDbManager.getDynamicDbCredentials(mydbrole); + properties = vaultDynamicCredentialsManager.getDynamicCredentials(DATABASE, mydbrole); assertEquals("sinclair2", properties.get(PASSWORD_PROPERTY_NAME), "fetched again because of 400"); lookupLeaseShouldReturn400.set(false); - vaultDynamicDatabaseCredentials = vaultDbManager.credentialsCache.get(mydbrole); - vaultDynamicDatabaseCredentials.created = EPOCH; - vaultDynamicDatabaseCredentials.password = "sinclair3"; // this will be extended with the current value in cache - vaultDynamicDatabaseCredentials.nowSupplier = () -> EPOCH.plusSeconds(8); // within renew grace period - properties = vaultDbManager.getDynamicDbCredentials(mydbrole); + vaultDynamicCredentials = vaultDynamicCredentialsManager.getCachedCredentials(DATABASE, mydbrole); + vaultDynamicCredentials.created = EPOCH; + vaultDynamicCredentials.password = "sinclair3"; // this will be extended with the current value in cache + vaultDynamicCredentials.nowSupplier = () -> EPOCH.plusSeconds(8); // within renew grace period + properties = vaultDynamicCredentialsManager.getDynamicCredentials(DATABASE, mydbrole); assertEquals("sinclair3", properties.get(PASSWORD_PROPERTY_NAME), "extended"); - vaultDynamicDatabaseCredentials = vaultDbManager.credentialsCache.get(mydbrole); - vaultDynamicDatabaseCredentials.created = EPOCH; - vaultDynamicDatabaseCredentials.nowSupplier = () -> EPOCH.plusSeconds(12); // expired + vaultDynamicCredentials = vaultDynamicCredentialsManager.getCachedCredentials(DATABASE, mydbrole); + vaultDynamicCredentials.created = EPOCH; + vaultDynamicCredentials.nowSupplier = () -> EPOCH.plusSeconds(12); // expired credentials.data.password = "sinclair4"; - properties = vaultDbManager.getDynamicDbCredentials(mydbrole); + properties = vaultDynamicCredentialsManager.getDynamicCredentials(DATABASE, mydbrole); assertEquals("sinclair4", properties.get(PASSWORD_PROPERTY_NAME), "recreated"); - vaultDynamicDatabaseCredentials = vaultDbManager.credentialsCache.get(mydbrole); - vaultDynamicDatabaseCredentials.created = EPOCH; - vaultDynamicDatabaseCredentials.nowSupplier = () -> EPOCH.plusSeconds(8); // within renew grace period - vaultDynamicDatabaseCredentials.leaseDurationSecs = 2; + vaultDynamicCredentials = vaultDynamicCredentialsManager.getCachedCredentials(DATABASE, mydbrole); + vaultDynamicCredentials.created = EPOCH; + vaultDynamicCredentials.nowSupplier = () -> EPOCH.plusSeconds(8); // within renew grace period + vaultDynamicCredentials.leaseDurationSecs = 2; credentials.data.password = "sinclair5"; - properties = vaultDbManager.getDynamicDbCredentials(mydbrole); + properties = vaultDynamicCredentialsManager.getDynamicCredentials(DATABASE, mydbrole); assertEquals("sinclair5", properties.get(PASSWORD_PROPERTY_NAME), "reaching max-ttl"); } @@ -151,10 +155,10 @@ private VaultClient createVaultClient() { return vaultClient; } - private VaultInternalDatabaseSecretEngine createVaultInternalDatabaseSecretEngine() { - return new VaultInternalDatabaseSecretEngine() { + private VaultInternalDynamicCredentialsSecretEngine createVaultInternalDynamicCredentialsSecretEngine() { + return new VaultInternalDynamicCredentialsSecretEngine() { @Override - public VaultDatabaseCredentials generateCredentials(String token, String databaseCredentialsRole) { + public VaultDynamicCredentials generateCredentials(String token, String type, String role) { return credentials; } }; diff --git a/integration-tests/vault/src/test/java/io/quarkus/vault/VaultITCase.java b/integration-tests/vault/src/test/java/io/quarkus/vault/VaultITCase.java index f31b7e04ceb61..b4b56b2362430 100644 --- a/integration-tests/vault/src/test/java/io/quarkus/vault/VaultITCase.java +++ b/integration-tests/vault/src/test/java/io/quarkus/vault/VaultITCase.java @@ -44,6 +44,7 @@ import io.quarkus.test.QuarkusUnitTest; import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.vault.runtime.Base64String; +import io.quarkus.vault.runtime.VaultDynamicCredentialsType; import io.quarkus.vault.runtime.client.VaultClientException; import io.quarkus.vault.runtime.client.authmethod.VaultInternalAppRoleAuthMethod; import io.quarkus.vault.runtime.client.authmethod.VaultInternalTokenAuthMethod; @@ -54,7 +55,7 @@ import io.quarkus.vault.runtime.client.dto.auth.VaultLookupSelf; import io.quarkus.vault.runtime.client.dto.auth.VaultRenewSelf; import io.quarkus.vault.runtime.client.dto.auth.VaultUserPassAuth; -import io.quarkus.vault.runtime.client.dto.database.VaultDatabaseCredentials; +import io.quarkus.vault.runtime.client.dto.dynamic.VaultDynamicCredentials; import io.quarkus.vault.runtime.client.dto.kv.VaultKvListSecrets; import io.quarkus.vault.runtime.client.dto.kv.VaultKvSecretV1; import io.quarkus.vault.runtime.client.dto.kv.VaultKvSecretV2; @@ -75,7 +76,7 @@ import io.quarkus.vault.runtime.client.dto.transit.VaultTransitVerify; import io.quarkus.vault.runtime.client.dto.transit.VaultTransitVerifyBatchInput; import io.quarkus.vault.runtime.client.dto.transit.VaultTransitVerifyBody; -import io.quarkus.vault.runtime.client.secretengine.VaultInternalDatabaseSecretEngine; +import io.quarkus.vault.runtime.client.secretengine.VaultInternalDynamicCredentialsSecretEngine; import io.quarkus.vault.runtime.client.secretengine.VaultInternalKvV1SecretEngine; import io.quarkus.vault.runtime.client.secretengine.VaultInternalKvV2SecretEngine; import io.quarkus.vault.runtime.client.secretengine.VaultInternalTransitSecretEngine; @@ -127,7 +128,7 @@ public class VaultITCase { @Inject VaultInternalTokenAuthMethod vaultInternalTokenAuthMethod; @Inject - VaultInternalDatabaseSecretEngine vaultInternalDatabaseSecretEngine; + VaultInternalDynamicCredentialsSecretEngine vaultInternalDynamicCredentialsSecretEngine; @Test public void credentialsProvider() { @@ -308,17 +309,19 @@ private String decrypt(String token, String keyName, String ciphertext, Base64St } private void assertDynamicCredentials(String clientToken, VaultAuthenticationType authType) { - VaultDatabaseCredentials vaultDatabaseCredentials = vaultInternalDatabaseSecretEngine.generateCredentials(clientToken, + VaultDynamicCredentials vaultDynamicCredentials = vaultInternalDynamicCredentialsSecretEngine.generateCredentials( + VaultDynamicCredentialsType.DATABASE.getDefaultName(), + clientToken, VAULT_DBROLE); - String username = vaultDatabaseCredentials.data.username; + String username = vaultDynamicCredentials.data.username; assertTrue(username.startsWith("v-" + authType.name().toLowerCase() + "-" + VAULT_DBROLE + "-")); VaultLeasesLookup vaultLeasesLookup = vaultInternalSystemBackend.lookupLease(clientToken, - vaultDatabaseCredentials.leaseId); - assertEquals(vaultDatabaseCredentials.leaseId, vaultLeasesLookup.data.id); + vaultDynamicCredentials.leaseId); + assertEquals(vaultDynamicCredentials.leaseId, vaultLeasesLookup.data.id); - VaultRenewLease vaultRenewLease = vaultInternalSystemBackend.renewLease(clientToken, vaultDatabaseCredentials.leaseId); - assertEquals(vaultDatabaseCredentials.leaseId, vaultRenewLease.leaseId); + VaultRenewLease vaultRenewLease = vaultInternalSystemBackend.renewLease(clientToken, vaultDynamicCredentials.leaseId); + assertEquals(vaultDynamicCredentials.leaseId, vaultRenewLease.leaseId); } private void assertKvSecrets(String clientToken) {