Skip to content

Commit

Permalink
Support any dynamic credentials in VaultCredentialsProvider
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kdubb committed Nov 8, 2021
1 parent f2cdc82 commit 09b5dd4
Show file tree
Hide file tree
Showing 15 changed files with 241 additions and 205 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand All @@ -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();
}
Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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<VaultDynamicCredentialsData, Object> {

}
Original file line number Diff line number Diff line change
@@ -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;

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class VaultCredentialsProvider implements CredentialsProvider {
@Inject
private VaultKVSecretEngine vaultKVSecretEngine;
@Inject
private VaultDbManager vaultDbManager;
private VaultDynamicCredentialsManager vaultDynamicCredentialsManager;
@Inject
private VaultConfigHolder vaultConfigHolder;

Expand All @@ -34,7 +34,8 @@ public Map<String, String> 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()) {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, VaultDynamicCredentials> 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<String, String> 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<String, String> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading

0 comments on commit 09b5dd4

Please sign in to comment.