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

Hashicorp Vault Namespace support. #1525

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.quorum.tessera.key.vault.hashicorp;

import static com.quorum.tessera.config.util.EnvironmentVariables.*;
import static com.quorum.tessera.key.vault.hashicorp.HashicorpKeyVaultServiceFactoryUtil.NAMESPACE_KEY;

import com.quorum.tessera.config.*;
import com.quorum.tessera.config.util.EnvironmentVariableProvider;
Expand All @@ -11,10 +12,13 @@
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.authentication.SessionManager;
import org.springframework.vault.authentication.SimpleSessionManager;
import org.springframework.vault.client.RestTemplateBuilder;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.core.VaultOperations;
import org.springframework.vault.core.VaultTemplate;
Expand All @@ -23,6 +27,9 @@

public class HashicorpKeyVaultServiceFactory implements KeyVaultServiceFactory {

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

@Override
public KeyVaultService create(Config config, EnvironmentVariableProvider envProvider) {
Objects.requireNonNull(config);
Expand Down Expand Up @@ -79,6 +86,7 @@ KeyVaultService create(

try {
URI uri = new URI(keyVaultConfig.getProperty("url").get());
LOGGER.info("URL for Hashicorp key vault is {}", keyVaultConfig.getProperty("url").get());
vaultEndpoint = VaultEndpoint.from(uri);
} catch (URISyntaxException | NoSuchElementException | IllegalArgumentException e) {
throw new ConfigException(
Expand All @@ -97,13 +105,43 @@ KeyVaultService create(
keyVaultConfig, envProvider, clientHttpRequestFactory, vaultEndpoint);

SessionManager sessionManager = new SimpleSessionManager(clientAuthentication);

VaultOperations vaultOperations =
new VaultTemplate(vaultEndpoint, clientHttpRequestFactory, sessionManager);
getVaultOperations(
keyVaultConfig, vaultEndpoint, clientHttpRequestFactory, sessionManager, util);

return new HashicorpKeyVaultService(
vaultOperations, () -> new VaultVersionedKeyValueTemplateFactory() {});
}

private VaultOperations getVaultOperations(
KeyVaultConfig keyVaultConfig,
VaultEndpoint vaultEndpoint,
ClientHttpRequestFactory clientHttpRequestFactory,
SessionManager sessionManager,
HashicorpKeyVaultServiceFactoryUtil util) {

VaultOperations vaultOperations;

if (keyVaultConfig.hasProperty(NAMESPACE_KEY)
&& keyVaultConfig.getProperty(NAMESPACE_KEY).isPresent()) {
LOGGER.info(
"Namespace for Hashicorp key vault is {}",
keyVaultConfig.getProperty(NAMESPACE_KEY).get());

String namespace = keyVaultConfig.getProperty(NAMESPACE_KEY).get();
RestTemplateBuilder restTemplateBuilder =
util.getRestTemplateWithVaultNamespace(
namespace, clientHttpRequestFactory, vaultEndpoint);

vaultOperations = new VaultTemplate(restTemplateBuilder, sessionManager);
} else {
vaultOperations = new VaultTemplate(vaultEndpoint, clientHttpRequestFactory, sessionManager);
}

return vaultOperations;
}

@Override
public KeyVaultType getType() {
return KeyVaultType.HASHICORP;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.vault.authentication.AppRoleAuthentication;
import org.springframework.vault.authentication.AppRoleAuthenticationOptions;
import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.authentication.TokenAuthentication;
import org.springframework.vault.client.RestTemplateBuilder;
import org.springframework.vault.client.SimpleVaultEndpointProvider;
import org.springframework.vault.client.VaultClients;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.config.ClientHttpRequestFactoryFactory;
Expand All @@ -23,6 +27,11 @@

class HashicorpKeyVaultServiceFactoryUtil {

public static final String NAMESPACE_KEY = "namespace";

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

SslConfiguration configureSsl(
KeyVaultConfig keyVaultConfig, EnvironmentVariableProvider envProvider) {
if (keyVaultConfig.hasProperty("tlsKeyStorePath", "tlsTrustStorePath")) {
Expand Down Expand Up @@ -82,8 +91,18 @@ ClientAuthentication configureClientAuthentication(
.secretId(AppRoleAuthenticationOptions.SecretId.provided(secretId))
.build();

RestOperations restOperations =
VaultClients.createRestTemplate(vaultEndpoint, clientHttpRequestFactory);
RestOperations restOperations;
if (keyVaultConfig.hasProperty(NAMESPACE_KEY)
&& keyVaultConfig.getProperty(NAMESPACE_KEY).isPresent()) {
String namespace = keyVaultConfig.getProperty(NAMESPACE_KEY).get();
LOGGER.info("Using namespace {} for login", namespace);
var restTemplateBuilder =
getRestTemplateWithVaultNamespace(namespace, clientHttpRequestFactory, vaultEndpoint);
restOperations = restTemplateBuilder.build();
} else {
LOGGER.info("No namespace");
restOperations = VaultClients.createRestTemplate(vaultEndpoint, clientHttpRequestFactory);
}

return new AppRoleAuthentication(appRoleAuthenticationOptions, restOperations);

Expand All @@ -110,4 +129,18 @@ ClientAuthentication configureClientAuthentication(

return new TokenAuthentication(authToken);
}

RestTemplateBuilder getRestTemplateWithVaultNamespace(
String namespace,
ClientHttpRequestFactory clientHttpRequestFactory,
VaultEndpoint vaultEndpoint) {
return RestTemplateBuilder.builder()
.endpointProvider(SimpleVaultEndpointProvider.of(vaultEndpoint))
.requestFactory(clientHttpRequestFactory)
.customizers(
restTemplate ->
restTemplate
.getInterceptors()
.add(VaultClients.createNamespaceInterceptor(namespace)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@
import org.junit.Test;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.client.RestTemplateBuilder;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.SslConfiguration;
import org.springframework.web.client.RestTemplate;

public class HashicorpKeyVaultServiceFactoryTest {

Expand Down Expand Up @@ -259,12 +261,17 @@ private void setUpUtilMocks(KeyVaultConfig keyVaultConfig) {
.thenReturn(clientHttpRequestFactory);

ClientAuthentication clientAuthentication = mock(ClientAuthentication.class);
RestTemplateBuilder restTemplateBuilder = mock(RestTemplateBuilder.class);
when(keyVaultServiceFactoryUtil.configureClientAuthentication(
eq(keyVaultConfig),
eq(envProvider),
eq(clientHttpRequestFactory),
any(VaultEndpoint.class)))
.thenReturn(clientAuthentication);
when(keyVaultServiceFactoryUtil.getRestTemplateWithVaultNamespace(
anyString(), eq(clientHttpRequestFactory), any(VaultEndpoint.class)))
.thenReturn(restTemplateBuilder);
when(restTemplateBuilder.build()).thenReturn(new RestTemplate());
}

@Test
Expand Down Expand Up @@ -313,4 +320,55 @@ public void returnedValueIsCorrectTypeUsing2ArgConstructor() {

assertThat(result).isInstanceOf(HashicorpKeyVaultService.class);
}

@Test
public void returnedValueIsCorrectTypeUsingNamespaceVaultTemplate() {
when(envProvider.getEnv(HASHICORP_ROLE_ID)).thenReturn("role-id");
when(envProvider.getEnv(HASHICORP_SECRET_ID)).thenReturn("secret-id");
when(envProvider.getEnv(HASHICORP_TOKEN)).thenReturn("token");

KeyConfiguration keyConfiguration = mock(KeyConfiguration.class);
when(config.getKeys()).thenReturn(keyConfiguration);

DefaultKeyVaultConfig keyVaultConfig = mock(DefaultKeyVaultConfig.class);
when(keyConfiguration.getKeyVaultConfig(KeyVaultType.HASHICORP))
.thenReturn(Optional.of(keyVaultConfig));

when(keyVaultConfig.getProperty("url")).thenReturn(Optional.of("http://someurl"));
when(keyVaultConfig.getProperty("approlePath")).thenReturn(Optional.of("approle"));
when(keyVaultConfig.getProperty("namespace")).thenReturn(Optional.of("sample_namespace"));
when(keyVaultConfig.hasProperty("namespace")).thenReturn(true);

setUpUtilMocks(keyVaultConfig);

KeyVaultService result =
keyVaultServiceFactory.create(config, envProvider, keyVaultServiceFactoryUtil);

assertThat(result).isInstanceOf(HashicorpKeyVaultService.class);
}

@Test
public void returnedValueIsCorrectTypeUsingNamespaceVaultTemplate2ArgConstructor() {
when(envProvider.getEnv(HASHICORP_ROLE_ID)).thenReturn("role-id");
when(envProvider.getEnv(HASHICORP_SECRET_ID)).thenReturn("secret-id");
when(envProvider.getEnv(HASHICORP_TOKEN)).thenReturn("token");

KeyConfiguration keyConfiguration = mock(KeyConfiguration.class);
when(config.getKeys()).thenReturn(keyConfiguration);

DefaultKeyVaultConfig keyVaultConfig = mock(DefaultKeyVaultConfig.class);
when(keyConfiguration.getKeyVaultConfig(KeyVaultType.HASHICORP))
.thenReturn(Optional.of(keyVaultConfig));

when(keyVaultConfig.getProperty("url")).thenReturn(Optional.of("http://someurl"));
when(keyVaultConfig.getProperty("approlePath")).thenReturn(Optional.of("approle"));
when(keyVaultConfig.getProperty("namespace")).thenReturn(Optional.of("sample_namespace"));
when(keyVaultConfig.hasProperty("namespace")).thenReturn(true);

setUpUtilMocks(keyVaultConfig);

KeyVaultService result = keyVaultServiceFactory.create(config, envProvider);

assertThat(result).isInstanceOf(HashicorpKeyVaultService.class);
}
}