Skip to content

Commit

Permalink
Merge pull request #168 from scalecube/cleanup-configuration-in-vault
Browse files Browse the repository at this point in the history
Get rid of dependency on environment variables in public configuration
  • Loading branch information
artem-v authored May 30, 2020
2 parents 30e36c3 + 968b177 commit 441bd5e
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 188 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package io.scalecube.config.vault;

import com.bettercloud.vault.EnvironmentLoader;
import com.bettercloud.vault.VaultConfig;
import java.util.Objects;

public class EnvironmentVaultTokenSupplier implements VaultTokenSupplier {

public String getToken(EnvironmentLoader environmentLoader, VaultConfig config) {
public String getToken(VaultConfig config) {
return Objects.requireNonNull(config.getToken(), "vault token");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,45 @@

public class KubernetesVaultTokenSupplier implements VaultTokenSupplier {

private static final String VAULT_ROLE = "VAULT_ROLE";
private static final String VAULT_JWT_PROVIDER = "VAULT_JWT_PROVIDER";
private static final String DEFAULT_JWT_PROVIDER = "kubernetes";
private static final String SERVICE_ACCOUNT_TOKEN_PATH =
"/var/run/secrets/kubernetes.io/serviceaccount/token";
private static final EnvironmentLoader ENVIRONMENT_LOADER = new EnvironmentLoader();

private String vaultRole = ENVIRONMENT_LOADER.loadVariable("VAULT_ROLE");

private String vaultJwtProvider =
Optional.ofNullable(ENVIRONMENT_LOADER.loadVariable("VAULT_JWT_PROVIDER"))
.orElse("kubernetes");

private String serviceAccountTokenPath =
Optional.ofNullable(ENVIRONMENT_LOADER.loadVariable("SERVICE_ACCOUNT_TOKEN_PATH"))
.orElse("/var/run/secrets/kubernetes.io/serviceaccount/token");

public KubernetesVaultTokenSupplier vaultRole(String vaultRole) {
this.vaultRole = vaultRole;
return this;
}

public KubernetesVaultTokenSupplier vaultJwtProvider(String vaultJwtProvider) {
this.vaultJwtProvider = vaultJwtProvider;
return this;
}

public KubernetesVaultTokenSupplier serviceAccountTokenPath(String serviceAccountTokenPath) {
this.serviceAccountTokenPath = serviceAccountTokenPath;
return this;
}

@Override
public String getToken(EnvironmentLoader environmentLoader, VaultConfig config) {
String role = Objects.requireNonNull(environmentLoader.loadVariable(VAULT_ROLE), "vault role");
public String getToken(VaultConfig config) {
Objects.requireNonNull(vaultRole, "vault role");
Objects.requireNonNull(vaultJwtProvider, "jwt provider");
Objects.requireNonNull(serviceAccountTokenPath, "k8s service account token path");
try {
String jwt = Files.lines(Paths.get(SERVICE_ACCOUNT_TOKEN_PATH)).collect(Collectors.joining());
String provider =
Optional.ofNullable(environmentLoader.loadVariable(VAULT_JWT_PROVIDER))
.orElse(DEFAULT_JWT_PROVIDER);
String jwt = Files.lines(Paths.get(serviceAccountTokenPath)).collect(Collectors.joining());
return Objects.requireNonNull(
new Vault(config).auth().loginByJwt(provider, role, jwt).getAuthClientToken(),
new Vault(config)
.auth()
.loginByJwt(vaultJwtProvider, vaultRole, jwt)
.getAuthClientToken(),
"vault token");
} catch (Exception e) {
throw ThrowableUtil.propagate(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
import io.scalecube.config.source.LoadedConfigProperty;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
Expand All @@ -28,99 +30,119 @@ public class VaultConfigSource implements ConfigSource {

private static final Logger LOGGER = LoggerFactory.getLogger(VaultConfigSource.class);

private static final String VAULT_SECRETS_PATH = "VAULT_SECRETS_PATH";
private static final EnvironmentLoader ENVIRONMENT_LOADER = new EnvironmentLoader();

private static final String PATHS_SEPARATOR = ":";

private final VaultInvoker vault;
private final List<String> secretsPath;
private final List<String> secretsPaths;

/**
* Create a new {@link VaultConfigSource}.
* @param vault vault invoker.
* @param secretsPaths secret path-s.
*/
private VaultConfigSource(VaultInvoker vault, List<String> secretsPaths) {
this.vault = vault;
this.secretsPath = secretsPaths;
this.secretsPaths = new ArrayList<>(secretsPaths);
}

@Override
public Map<String, ConfigProperty> loadConfig() {
Map<String, ConfigProperty> result = new HashMap<>();
for (String path : secretsPath) {
for (String path : secretsPaths) {
try {
LogicalResponse response = vault.invoke(vault -> vault.logical().read(path));
final Map<String, LoadedConfigProperty> pathProps = response.getData().entrySet().stream()
.map(LoadedConfigProperty::withNameAndValue)
.map(LoadedConfigProperty.Builder::build)
.collect(Collectors.toMap(LoadedConfigProperty::name, Function.identity()));
final Map<String, LoadedConfigProperty> pathProps =
response.getData().entrySet().stream()
.map(LoadedConfigProperty::withNameAndValue)
.map(LoadedConfigProperty.Builder::build)
.collect(Collectors.toMap(LoadedConfigProperty::name, Function.identity()));
result.putAll(pathProps);
} catch (Exception ex) {
LOGGER.warn("unable to load config properties from {}",path, ex);
LOGGER.warn("Unable to load config properties from {}", path, ex);
throw new ConfigSourceNotAvailableException(ex);
}
}
return result;
}

/**
* This builder method is used internally for test purposes. please use it only for tests. Please
* note the following required environment variables are required.
*
* <ul>
* <li><code>VAULT_SECRETS_PATH</code> is the path to use (defaults to <code>secret</code>)
* <li><code>VAULT_TOKEN</code> is the {@link VaultConfig#token(String) token} to use
* <li><code>VAULT_ADDR</code> is the {@link VaultConfig#address(String) address} of the vault
* (API)
* </ul>
*/
public static Builder builder() {
return new Builder();
}

/**
* This builder method is used internally for test purposes. please use it only for tests
*
* @param environmentLoader an {@link EnvironmentLoader}
*/
static Builder builder(EnvironmentLoader environmentLoader) {
final Builder builder = new Builder();
if (environmentLoader != null) {
builder.environmentLoader = environmentLoader;
}
return builder;
}

public static final class Builder {

private Function<VaultInvoker.Builder, VaultInvoker.Builder> vault = Function.identity();
private Function<VaultInvoker.Builder, VaultInvoker.Builder> builderFunction = b -> b;

private VaultInvoker invoker;
private EnvironmentLoader environmentLoader = VaultInvoker.Builder.ENVIRONMENT_LOADER;
private List<String> secretsPaths = new ArrayList<>();

private List<String> secretsPaths =
Optional.ofNullable(ENVIRONMENT_LOADER.loadVariable("VAULT_SECRETS_PATH"))
.or(() -> Optional.ofNullable(ENVIRONMENT_LOADER.loadVariable("VAULT_SECRETS_PATHS")))
.map(s -> s.split(PATHS_SEPARATOR))
.map(Arrays::asList)
.orElseGet(ArrayList::new);

private Builder() {}

/**
* Appends {@code secretsPath} to {@code secretsPaths}.
*
* @param secretsPath secretsPath (may contain value with paths separated by {@code :})
* @return this builder
* @deprecated will be removed in future releases without notice, use {@link
* #addSecretsPath(String...)} or {@link #secretsPaths(Collection)}.
*/
@Deprecated
public Builder secretsPath(String secretsPath) {
this.secretsPaths.add(secretsPath);
this.secretsPaths.addAll(toSecretsPaths(Collections.singletonList(secretsPath)));
return this;
}

/**
* Appends one or several secretsPath\es to {@code secretsPaths}.
*
* @param secretsPath one or several secretsPath\es (each value may contain paths separated by
* {@code :})
* @return this builder
*/
public Builder addSecretsPath(String... secretsPath) {
this.secretsPaths.addAll(toSecretsPaths(Arrays.asList(secretsPath)));
return this;
}

/**
* Setter for {@code secretsPaths}.
*
* @param secretsPaths collection of secretsPath\es (each value may contain paths separated by
* {@code :})
* @return this builder
*/
public Builder secretsPaths(Collection<String> secretsPaths) {
this.secretsPaths = toSecretsPaths(secretsPaths);
return this;
}

private static List<String> toSecretsPaths(Collection<String> secretsPaths) {
return secretsPaths.stream()
.flatMap(s -> Arrays.stream(s.split(PATHS_SEPARATOR)))
.distinct()
.collect(Collectors.toList());
}

public Builder invoker(VaultInvoker invoker) {
this.invoker = invoker;
return this;
}

public Builder vault(UnaryOperator<VaultInvoker.Builder> config) {
this.vault = this.vault.andThen(config);
this.builderFunction = this.builderFunction.andThen(config);
return this;
}

public Builder config(UnaryOperator<VaultConfig> vaultConfig) {
this.vault = this.vault.andThen(c -> c.options(vaultConfig));
this.builderFunction = this.builderFunction.andThen(c -> c.options(vaultConfig));
return this;
}

public Builder tokenSupplier(VaultTokenSupplier supplier) {
this.vault = this.vault.andThen(c -> c.tokenSupplier(supplier));
this.builderFunction = this.builderFunction.andThen(c -> c.tokenSupplier(supplier));
return this;
}

Expand All @@ -130,17 +152,9 @@ public Builder tokenSupplier(VaultTokenSupplier supplier) {
* @return instance of {@link VaultConfigSource}
*/
public VaultConfigSource build() {
VaultInvoker vaultInvoker =
invoker != null
? invoker
: vault.apply(new VaultInvoker.Builder(environmentLoader)).build();
if (secretsPaths.isEmpty()) {
String envSecretPath = Objects
.requireNonNull(environmentLoader.loadVariable(VAULT_SECRETS_PATH),
"Missing secretsPath");
secretsPaths = Arrays.asList(envSecretPath.split(":"));
}
return new VaultConfigSource(vaultInvoker, secretsPaths);
return new VaultConfigSource(
invoker != null ? invoker : builderFunction.apply(new VaultInvoker.Builder()).build(),
secretsPaths);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ public class VaultInvoker {

private static final long MIN_REFRESH_MARGIN = TimeUnit.MINUTES.toSeconds(10);

private final Builder config;
private final Builder builder;

private Vault vault;
private Timer timer;

public static Builder builder() {
return new Builder(Builder.ENVIRONMENT_LOADER);
return new Builder();
}

private VaultInvoker(Builder config) {
this.config = config;
private VaultInvoker(Builder builder) {
this.builder = builder;
}

/**
Expand Down Expand Up @@ -81,12 +81,11 @@ private synchronized Vault recreateVault(Vault prev) throws VaultException {
vault = null;

VaultConfig vaultConfig =
config
builder
.options
.apply(new VaultConfig())
.environmentLoader(config.environmentLoader)
.apply(new VaultConfig().environmentLoader(new EnvironmentLoader()))
.build();
String token = config.tokenSupplier.getToken(config.environmentLoader, vaultConfig);
String token = builder.tokenSupplier.getToken(vaultConfig);
Vault vault = new Vault(vaultConfig.token(token));
checkVault(vault);
LookupResponse lookupSelf = vault.auth().lookupSelf();
Expand Down Expand Up @@ -211,23 +210,9 @@ public void run() {
}

public static class Builder {
public static final EnvironmentLoader ENVIRONMENT_LOADER = new EnvironmentLoader();
public static final VaultTokenSupplier TOKEN_SUPPLIER = new EnvironmentVaultTokenSupplier();

private Function<VaultConfig, VaultConfig> options = Function.identity();
private VaultTokenSupplier tokenSupplier = TOKEN_SUPPLIER;
private EnvironmentLoader environmentLoader = ENVIRONMENT_LOADER;

/**
* This builder method is used internally for test purposes. please use it only for tests
*
* @param environmentLoader an {@link EnvironmentLoader}
*/
Builder(EnvironmentLoader environmentLoader) {
if (environmentLoader != null) {
this.environmentLoader = environmentLoader;
}
}
private VaultTokenSupplier tokenSupplier = new EnvironmentVaultTokenSupplier();

public Builder options(UnaryOperator<VaultConfig> config) {
this.options = this.options.andThen(config);
Expand All @@ -245,7 +230,7 @@ public Builder tokenSupplier(VaultTokenSupplier supplier) {
* @return instance of {@link VaultInvoker}
*/
public VaultInvoker build() {
Builder builder = new Builder(environmentLoader);
Builder builder = new Builder();
builder.options = options;
builder.tokenSupplier = tokenSupplier;
return new VaultInvoker(builder);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package io.scalecube.config.vault;

import com.bettercloud.vault.EnvironmentLoader;
import com.bettercloud.vault.VaultConfig;

@FunctionalInterface
public interface VaultTokenSupplier {

String getToken(EnvironmentLoader environmentLoader, VaultConfig config);
String getToken(VaultConfig config);
}
Loading

0 comments on commit 441bd5e

Please sign in to comment.