Skip to content

Upgrade vault java driver and add kubernetes support #131

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Oct 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions config-vault/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@

<artifactId>config-vault</artifactId>

<properties>
<log4j.version>2.5</log4j.version>
<vault-java-driver.version>5.0.0</vault-java-driver.version>
<org.testcontainers.vault.version>1.12.1</org.testcontainers.vault.version>
</properties>

<dependencies>
<dependency>
<groupId>io.scalecube</groupId>
Expand All @@ -19,12 +25,24 @@
<dependency>
<groupId>com.bettercloud</groupId>
<artifactId>vault-java-driver</artifactId>
<version>3.1.0</version>
<version>${vault-java-driver.version}</version>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>vault</artifactId>
<version>1.6.0</version>
<version>${org.testcontainers.vault.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.scalecube.config.vault;

import com.bettercloud.vault.EnvironmentLoader;
import com.bettercloud.vault.Vault;
import com.bettercloud.vault.VaultConfig;
import io.scalecube.config.utils.ThrowableUtil;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Objects;
import java.util.stream.Collectors;

public class KubernetesVaultTokenSupplier implements VaultTokenSupplier {

private static final String VAULT_ROLE = "VAULT_ROLE";
private static final String SERVICE_ACCOUNT_TOKEN_PATH =
"/var/run/secrets/kubernetes.io/serviceaccount/token";

@Override
public String getToken(EnvironmentLoader environmentLoader, VaultConfig config) {
String role = Objects.requireNonNull(environmentLoader.loadVariable(VAULT_ROLE), "vault role");
try {
String jwt = Files.lines(Paths.get(SERVICE_ACCOUNT_TOKEN_PATH)).collect(Collectors.joining());
return Objects.requireNonNull(
new Vault(config).auth().loginByKubernetes(role, jwt).getAuthClientToken(),
"vault token");
} catch (Exception e) {
throw ThrowableUtil.propagate(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package io.scalecube.config.vault;

import static java.util.Objects.requireNonNull;

import com.bettercloud.vault.EnvironmentLoader;
import com.bettercloud.vault.SslConfig;
import com.bettercloud.vault.Vault;
import com.bettercloud.vault.VaultConfig;
import com.bettercloud.vault.VaultException;
Expand All @@ -15,10 +12,12 @@
import io.scalecube.config.utils.ThrowableUtil;
import java.time.Duration;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -32,47 +31,69 @@ public class VaultConfigSource implements ConfigSource {

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

private static final ThreadFactory THREAD_FACTORY =
r -> {
Thread thread = new Thread(r);
thread.setDaemon(true);
thread.setName(VaultConfigSource.class.getSimpleName().toLowerCase() + "-token-renewer");
return thread;
};

private static final String VAULT_SECRETS_PATH = "VAULT_SECRETS_PATH";
private static final String VAULT_RENEW_PERIOD = "VAULT_RENEW_PERIOD";

private final Vault vault;
private final String secretsPath;
private final Duration renewEvery;

/**
* Create a new {@link VaultConfigSource} with the given {@link Builder}.
*
* @param builder configuration to create vault access with.
*/
private VaultConfigSource(Builder builder) {
this.secretsPath = builder.secretsPath();
this.renewEvery = builder.renewEvery;
vault = new Vault(builder.config);
private VaultConfigSource(Builder builder) throws VaultException {
EnvironmentLoader environmentLoader =
builder.environmentLoader != null ? builder.environmentLoader : new EnvironmentLoader();

secretsPath =
Objects.requireNonNull(
builder.secretsPath != null
? builder.secretsPath
: environmentLoader.loadVariable(VAULT_SECRETS_PATH),
"Missing secretsPath");

VaultConfig vaultConfig =
builder.config.apply(new VaultConfig()).environmentLoader(environmentLoader).build();
String token = builder.tokenSupplier.getToken(environmentLoader, vaultConfig);
vault = new Vault(vaultConfig.token(token));

Duration renewEvery =
builder.renewEvery != null
? builder.renewEvery
: duration(environmentLoader.loadVariable(VAULT_RENEW_PERIOD));

if (renewEvery != null) {
long initialDelay = renewEvery.toMillis();
long period = renewEvery.toMillis();
TimeUnit unit = TimeUnit.MILLISECONDS;
ThreadFactory factory =
r -> {
Thread thread = new Thread(r);
thread.setDaemon(true);
thread.setName(VaultConfigSource.class.getSimpleName() + " token renewer");
return thread;
};
Executors.newScheduledThreadPool(1, factory)
.scheduleAtFixedRate(
() -> {
try {
vault.auth().renewSelf();
LOGGER.info("renew token success");
} catch (VaultException vaultException) {
LOGGER.error("failed to renew token", vaultException);
}
},
initialDelay,
period,
unit);
scheduleVaultTokenRenew(renewEvery);
}
}

private void scheduleVaultTokenRenew(Duration renewEvery) {
long initialDelay = renewEvery.toMillis();
long period = renewEvery.toMillis();
Executors.newSingleThreadScheduledExecutor(THREAD_FACTORY)
.scheduleAtFixedRate(
() -> {
try {
this.vault.auth().renewSelf();
LOGGER.info("renew token success");
} catch (VaultException vaultException) {
LOGGER.error("failed to renew token", vaultException);
}
},
initialDelay,
period,
TimeUnit.MILLISECONDS);
}

private void checkVaultStatus() throws VaultException {
if (vault.seal().sealStatus().getSealed()) {
throw new VaultException("Vault is sealed");
Expand All @@ -83,6 +104,10 @@ private void checkVaultStatus() throws VaultException {
}
}

private Duration duration(String duration) {
return duration != null ? Duration.parse(duration) : null;
}

@Override
public Map<String, ConfigProperty> loadConfig() {
try {
Expand Down Expand Up @@ -119,42 +144,38 @@ public static Builder builder() {
* @param environmentLoader an {@link EnvironmentLoader}
*/
static Builder builder(EnvironmentLoader environmentLoader) {
return builder(
environmentLoader.loadVariable("VAULT_ADDR"),
environmentLoader.loadVariable("VAULT_TOKEN"),
environmentLoader.loadVariable("VAULT_SECRETS_PATH"));
}

public static Builder builder(String address, String token, String secretsPath) {
return new Builder(address, token, secretsPath);
return new Builder(environmentLoader);
}

public static final class Builder {

final VaultConfig config = new VaultConfig();
private final String secretsPath;
private Duration renewEvery = null;
private Function<VaultConfig, VaultConfig> config = Function.identity();
private VaultTokenSupplier tokenSupplier = new VaultTokenSupplier() {};
private EnvironmentLoader environmentLoader;
private String secretsPath;
private Duration renewEvery;

Builder(String address, String token, String secretsPath) {
config
.address(requireNonNull(address, "Missing address"))
.token(requireNonNull(token, "Missing token"))
.sslConfig(new SslConfig());
this.secretsPath = requireNonNull(secretsPath, "Missing secretsPath");
private Builder(EnvironmentLoader environmentLoader) {
this.environmentLoader = environmentLoader;
}

public Builder connectTimeout(int connectTimeout) {
config.openTimeout(connectTimeout);
public Builder renewEvery(Duration duration) {
renewEvery = duration;
return this;
}

public Builder readTimeout(int readTimeout) {
config.readTimeout(readTimeout);
public Builder secretsPath(String secretsPath) {
this.secretsPath = secretsPath;
return this;
}

public Builder renewEvery(Duration duration) {
renewEvery = duration;
public Builder config(UnaryOperator<VaultConfig> config) {
this.config = this.config.andThen(config);
return this;
}

public Builder tokenSupplier(VaultTokenSupplier supplier) {
this.tokenSupplier = supplier;
return this;
}

Expand All @@ -165,17 +186,11 @@ public Builder renewEvery(Duration duration) {
*/
public VaultConfigSource build() {
try {
this.config.build();
return new VaultConfigSource(this);
} catch (VaultException propogateException) {
LOGGER.error(
"Unable to build " + VaultConfigSource.class.getSimpleName(), propogateException);
throw ThrowableUtil.propagate(propogateException);
} catch (VaultException e) {
LOGGER.error("Unable to build " + VaultConfigSource.class.getSimpleName(), e);
throw ThrowableUtil.propagate(e);
}
}

public String secretsPath() {
return secretsPath;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.scalecube.config.vault;

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

public interface VaultTokenSupplier {

default String getToken(EnvironmentLoader environmentLoader, VaultConfig config) {
return Objects.requireNonNull(config.getToken(), "vault token");
}
}
Loading