Skip to content
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

Feature: Add vault sync connector #48

Merged
merged 3 commits into from
Jan 18, 2024
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
49 changes: 46 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ Create the connector:
AWSSecretManagerConnectorAsync connector = new AWSSecretManagerConnectorAsync(config);
```

Get the secret in String:
Get the secret as String:
```java
connector.getSecret("secretName")
.doOnNext(System.out::println);
Expand Down Expand Up @@ -184,7 +184,7 @@ Create the connector:
GenericManagerAsync connector = configurator.getVaultClient();
```

Get the secret in String:
Get the secret as String:
```java
connector.getSecret("my/secret/path")
.doOnNext(System.out::println);
Expand All @@ -198,6 +198,49 @@ connector.getSecret("my/database/credentials", DBCredentials.class)
})
```

## Vault Sync

```java
dependencies {
// vault-sync dependency
implementation 'com.github.bancolombia:vault-sync:<version-here>'
}
```

Define your configuration:
```java
// Example Config
VaultSecretManagerConfigurator configurator = VaultSecretManagerConfigurator.builder()
.withProperties(VaultSecretsManagerProperties.builder()
.host("localhost")
.port(8200)
.ssl(false)
.roleId("65903d42-6dd4-2aa3-6a61-xxxxxxxxxx") // for authentication with vault
.secretId("0cce6d0b-e756-c12e-9729-xxxxxxxxx") // for authentication with vault
.build())
.build();
```

##### Configurations

_Same as defined for Vault Async._

Create the connector:
```java
GenericManagerAsync connector = configurator.getVaultClient();
```

Get the secret as String:
```java
String secret = connector.getSecret("my/secret/path");
// ... continue your sync flow
```
Get the secret deserialized:
```java
DBCredentials creds = connector.getSecret("my/database/credentials",
DBCredentials.class);
// ... continue your sync flow
```

## Parameter Store Sync
```java
Expand Down Expand Up @@ -263,7 +306,7 @@ Create the connector:
AWSParameterStoreConnectorAsync connector = new AWSParameterStoreConnectorAsync(config);
```

Get the secret in String:
Get the secret as String:
```java
connector.getSecret("parameterName")
.doOnNext(System.out::println);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@

import co.com.bancolombia.secretsmanager.api.exceptions.SecretException;
import co.com.bancolombia.secretsmanager.commons.utils.GsonUtils;
import co.com.bancolombia.secretsmanager.config.VaultSecretsManagerProperties;
import co.com.bancolombia.secretsmanager.connector.auth.AuthResponse;
import co.com.bancolombia.secretsmanager.connector.auth.K8sAuth;
import co.com.bancolombia.secretsmanager.connector.auth.K8sTokenReader;
import co.com.bancolombia.secretsmanager.connector.auth.RoleAuth;
import co.com.bancolombia.secretsmanager.vault.K8sTokenReader;
import co.com.bancolombia.secretsmanager.vault.auth.AuthResponse;
import co.com.bancolombia.secretsmanager.vault.auth.K8sAuth;
import co.com.bancolombia.secretsmanager.vault.auth.RoleAuth;
import co.com.bancolombia.secretsmanager.vault.config.VaultSecretsManagerProperties;
import com.github.benmanes.caffeine.cache.AsyncCache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import java.lang.reflect.Type;
import java.net.URI;
Expand Down Expand Up @@ -103,24 +104,25 @@ private Mono<AuthResponse> performLoginByRoleId() {
}

private Mono<AuthResponse> performLoginWithK8s() {
return k8sTokenReader.getKubernetesServiceAccountToken()
.flatMap(token -> Mono.fromSupplier(() ->
HttpRequest.newBuilder()
.uri(URI.create(this.properties.buildUrl() + properties.getK8sAuthPath()))
.timeout(Duration.ofSeconds(5))
.header(CONTENT_TYPE_HEADER, "application/json")
.POST(HttpRequest.BodyPublishers.ofString(
gson.toJson(K8sAuth.builder()
.jwt(token)
.role(properties.getVaultRoleForK8sAuth())
.build())
))
.build()
))
.flatMap(this::doCallAuthApi)
.doOnSuccess(authResponse ->
logger.info("Successfully authenticated via k8s with vault")
);
return Mono.fromCallable(k8sTokenReader::getKubernetesServiceAccountToken)
.flatMap(token -> Mono.fromSupplier(() ->
HttpRequest.newBuilder()
.uri(URI.create(this.properties.buildUrl() + properties.getK8sAuthPath()))
.timeout(Duration.ofSeconds(5))
.header(CONTENT_TYPE_HEADER, "application/json")
.POST(HttpRequest.BodyPublishers.ofString(
gson.toJson(K8sAuth.builder()
.jwt(token)
.role(properties.getVaultRoleForK8sAuth())
.build())
))
.build()
))
.flatMap(this::doCallAuthApi)
.doOnSuccess(authResponse ->
logger.info("Successfully authenticated via k8s with vault")
)
.subscribeOn(Schedulers.boundedElastic());
}

private Mono<AuthResponse> doCallAuthApi(HttpRequest request) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,49 +1,33 @@
package co.com.bancolombia.secretsmanager.connector;

import co.com.bancolombia.secretsmanager.api.exceptions.SecretException;
import co.com.bancolombia.secretsmanager.config.VaultKeyStoreProperties;
import co.com.bancolombia.secretsmanager.config.VaultSecretsManagerProperties;
import co.com.bancolombia.secretsmanager.config.VaultTrustStoreProperties;
import co.com.bancolombia.secretsmanager.connector.auth.K8sTokenReader;
import co.com.bancolombia.secretsmanager.connector.ssl.SslConfig;
import co.com.bancolombia.secretsmanager.vault.K8sTokenReader;
import co.com.bancolombia.secretsmanager.vault.VaultSecretManagerConfiguratorBase;
import co.com.bancolombia.secretsmanager.vault.config.VaultSecretsManagerProperties;
import lombok.Builder;

import java.net.http.HttpClient;
import java.time.Duration;
import java.util.logging.Logger;

/**
* This class is in charge of configuring the VaultSecretsManagerConnector
* This class is in charge of configuring the Async VaultSecretsManagerConnector
*/
@Builder(setterPrefix = "with", toBuilder = true)
public class VaultSecretManagerConfigurator {
public class VaultSecretManagerConfigurator extends VaultSecretManagerConfiguratorBase {

private static final Logger logger = Logger.getLogger("config.VaultSecretManagerConfigurator");
private final VaultSecretsManagerProperties properties;
private final K8sTokenReader k8sTokenReader;

/**
* This method is in charge of configuring the HttpClient
* @return HttpClient configured.
* @throws SecretException
*/
public HttpClient getHttpClient() throws SecretException {
HttpClient.Builder clientBuilder = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NORMAL)
.connectTimeout(Duration.ofSeconds(properties.getHttpProperties().getConnectionTimeout()));
if (properties.isSsl() || properties.getTrustStoreProperties() != null || properties.getKeyStoreProperties() != null) {
clientBuilder = clientBuilder.sslContext(buildSslConfig().getSslContext());
}
return clientBuilder.build();
@Builder(setterPrefix = "with", toBuilder = true)
public VaultSecretManagerConfigurator(VaultSecretsManagerProperties properties, K8sTokenReader k8sTokenReader) {
super(properties);
this.k8sTokenReader = k8sTokenReader;
}

/**
* This method is in charge of configuring the VaultAuthenticator
* @return the VaultAuthenticator configured.
* @throws SecretException
* @throws SecretException when an error occurs while reading the kubernetes service account token
*/
public VaultAuthenticator getVaultAuthenticator() throws SecretException {
return new VaultAuthenticator(getHttpClient(), properties,
return new VaultAuthenticator(getHttpClient(), getProperties(),
k8sTokenReader != null? k8sTokenReader : new K8sTokenReader());
}

Expand All @@ -56,43 +40,7 @@ public VaultSecretsManagerConnectorAsync getVaultClient() throws SecretException
HttpClient httpClient = getHttpClient();
return new VaultSecretsManagerConnectorAsync(httpClient,
getVaultAuthenticator(),
properties);
getProperties());
}

private SslConfig buildSslConfig() throws SecretException {
SslConfig sslConfig = new SslConfig();
if (properties.getTrustStoreProperties() != null) {
setTrustConfiguration(sslConfig, properties.getTrustStoreProperties());
}
if (properties.getKeyStoreProperties() != null) {
setKeystoreConfiguration(sslConfig, properties.getKeyStoreProperties());
}
return sslConfig.build();
}

private SslConfig setTrustConfiguration(SslConfig sslConfig,
VaultTrustStoreProperties trustStoreProperties) throws SecretException {
if (trustStoreProperties.getTrustStoreJksFile() != null) {
sslConfig.trustStoreFile(trustStoreProperties.getTrustStoreJksFile());
} else if (trustStoreProperties.getPemFile() != null) {
sslConfig.pemFile(trustStoreProperties.getPemFile());
} else {
throw new SecretException("VaultTrustStoreProperties was set, but no trust store file or pem resource provided");
}
return sslConfig;
}

private SslConfig setKeystoreConfiguration(SslConfig sslConfig,
VaultKeyStoreProperties keyStoreProperties) throws SecretException {
if (keyStoreProperties.getKeyStoreFile() != null) {
sslConfig.keyStoreFile(keyStoreProperties.getKeyStoreFile(),
keyStoreProperties.getKeyStorePassword());
} else if (keyStoreProperties.getClientPem() != null && keyStoreProperties.getClientKeyPem() != null) {
sslConfig.clientPemFile(keyStoreProperties.getClientPem());
sslConfig.clientKeyPemFile(keyStoreProperties.getClientKeyPem());
} else {
throw new SecretException("VaultKeyStoreProperties was set, but no key store file or pem resources provided");
}
return sslConfig;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import co.com.bancolombia.secretsmanager.api.GenericManagerAsync;
import co.com.bancolombia.secretsmanager.api.exceptions.SecretException;
import co.com.bancolombia.secretsmanager.commons.utils.GsonUtils;
import co.com.bancolombia.secretsmanager.config.VaultSecretsManagerProperties;
import co.com.bancolombia.secretsmanager.connector.auth.AuthResponse;
import co.com.bancolombia.secretsmanager.connector.secret.SecretResponse;
import co.com.bancolombia.secretsmanager.vault.config.VaultSecretsManagerProperties;
import co.com.bancolombia.secretsmanager.vault.secret.SecretResponse;
import co.com.bancolombia.secretsmanager.vault.auth.AuthResponse;
import com.github.benmanes.caffeine.cache.AsyncCache;
import com.github.benmanes.caffeine.cache.Caffeine;
import reactor.core.publisher.Mono;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package co.com.bancolombia.secretsmanager.connector;

import co.com.bancolombia.secretsmanager.config.VaultSecretsManagerProperties;
import co.com.bancolombia.secretsmanager.connector.auth.K8sTokenReader;
import co.com.bancolombia.secretsmanager.vault.K8sTokenReader;
import co.com.bancolombia.secretsmanager.vault.config.VaultSecretsManagerProperties;
import lombok.SneakyThrows;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
Expand All @@ -10,7 +10,6 @@
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

import static org.junit.Assert.assertEquals;
Expand Down Expand Up @@ -76,7 +75,7 @@ public void testAuthenticateWithK8s() {
.build();

K8sTokenReader k8sTokenReaderMock = Mockito.mock(K8sTokenReader.class);
when(k8sTokenReaderMock.getKubernetesServiceAccountToken()).thenReturn(Mono.just("ey..."));
when(k8sTokenReaderMock.getKubernetesServiceAccountToken()).thenReturn("ey...");

VaultSecretManagerConfigurator configurator = VaultSecretManagerConfigurator.builder()
.withProperties(properties)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package co.com.bancolombia.secretsmanager.connector;

import co.com.bancolombia.secretsmanager.api.exceptions.SecretException;
import co.com.bancolombia.secretsmanager.config.VaultKeyStoreProperties;
import co.com.bancolombia.secretsmanager.config.VaultSecretsManagerProperties;
import co.com.bancolombia.secretsmanager.config.VaultTrustStoreProperties;
import co.com.bancolombia.secretsmanager.vault.config.VaultKeyStoreProperties;
import co.com.bancolombia.secretsmanager.vault.config.VaultSecretsManagerProperties;
import co.com.bancolombia.secretsmanager.vault.config.VaultTrustStoreProperties;
import lombok.SneakyThrows;
import org.junit.Assert;
import org.junit.Test;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package co.com.bancolombia.secretsmanager.connector;

import co.com.bancolombia.secretsmanager.config.VaultSecretsManagerProperties;
import co.com.bancolombia.secretsmanager.connector.auth.AuthResponse;
import co.com.bancolombia.secretsmanager.vault.auth.AuthResponse;
import co.com.bancolombia.secretsmanager.vault.config.VaultSecretsManagerProperties;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand Down

This file was deleted.

4 changes: 2 additions & 2 deletions async/vault-async/vault-async.gradle
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
dependencies {
api project(":secrets-manager-api")
api project(":vault-commons")
implementation "io.projectreactor.addons:reactor-extra:${reactorExtraVersion}"
implementation "io.projectreactor:reactor-core:${reactorCoreVersion}"
implementation "com.google.code.gson:gson:${gsonVersion}"
implementation "com.github.ben-manes.caffeine:caffeine:${cafeineVersion}"
testImplementation "io.projectreactor:reactor-test:${reactorCoreVersion}"
testImplementation("com.squareup.okhttp3:mockwebserver:4.9.3")

}

ext {
artifactId = 'vault-async'
artifactDescription = 'Secrets Manager'
artifactDescription = 'Secrets Manager async connector for Vault'
}
Loading
Loading