spring-cloud / spring-cloud/spring-cloud-vault

Vault Namespace Login for Secret Sharing in non Hierarchical Relationship

Open
#694 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

type: enhancement
Dominant language
Java
Stars
291
Forks
152
Avg merge
11h 24m
Merged PRs (30d)
3

Description

Problem Description
With the release of Vault 1.13.0 it's possible to share secrets between namespaces without an hierachical relationship.
See documentation.
For Auth Methods (other than Token, e.g. Kubernetes) the Namespace is required for the login.
For importing secrets without the namespace a full path is needed.
If the namespace is set in the vault configuration the 'X-Vault-Namespace' header is set for every request.
Therefore its not possible to read secrets with an full qualified path.

Desired Solution
It would be nice to set the namespace for the login only.

  spring: 
     cloud:
       vault: 
          login-namespace: 'MY-DOMAIN-NAMESPACE'

So we could reference the secrets like:

 spring: 
     config:
       import: 
        - "optional:vault://'MY-DOMAIN-NAMESPACE/kv2/secret1"
        - "optional:vault://'OTHER-DOMAIN-NAMESPACE/kv2/other-secret"

Workaround
In order to make this possible we found following workaround:

Register a custome WebClientFactory in the Application Class:

public static void main(String[] args) throws GeneralSecurityException, IOException {
		SpringApplication application = new SpringApplication(Application.class);
		application.addBootstrapRegistryInitializer(registry -> registry.register(WebClientFactory.class, CustomVaultSupplier .getWebClientFactorySupplier("MY-DOMAIN-NAMESPACE")));
		application.run(args);
	}

Creating an CustomVaultWebClientFactory:


public class CustomVaultWebClientFactory implements WebClientFactory {

	private final ClientHttpConnector connector;

	private final Function<ClientHttpConnector, WebClientBuilder> builderFunction;

	CustomVaultWebClientFactory(ClientHttpConnector connector, Function<ClientHttpConnector, WebClientBuilder> builderFunction) {
		this.connector = connector;
		this.builderFunction = builderFunction;
	}

	@Override
	public WebClient create(@Nullable Consumer<WebClientBuilder> customizer) {

		WebClientBuilder builder = builderFunction.apply(connector);

		if (customizer != null) {
			customizer.accept(builder);
		}

		return builder.build();
	}
	
}

Adding an ExchangeFilterFunction (Interceptor) to the (Kubernetes) auth method:


public class CustomVaultSupplier {

	public static BootstrapRegistry.InstanceSupplier<WebClientFactory> getWebClientFactorySupplier(String loginNamespace) {
		return context -> new CustomVaultWebClientFactory (context.get(VaultReactiveAutoConfiguration.ClientHttpConnectorWrapper.class).getConnector(), (connector) -> {
			VaultEndpoint endpoint = VaultEndpoint.create(context.get(VaultProperties.class).getHost(), context.get(VaultProperties.class).getPort());
			endpoint.setScheme(context.get(VaultProperties.class).getScheme());
			return WebClientBuilder.builder().filter(getKubernetesLoginNamespaceFilter(context.get(VaultProperties.class).getKubernetes().getKubernetesPath(), loginNamespace))
					.endpointProvider(SimpleVaultEndpointProvider.of(endpoint));
		});
	}

	private static ExchangeFilterFunction getKubernetesLoginNamespaceFilter(String kubernetesPath, String loginNamespace) {
		return (clientRequest, nextFilter) -> {
			if (clientRequest.url().toString().contains(String.format("auth/%s/login", kubernetesPath))) {
				ClientRequest newRequest = ClientRequest.from(clientRequest).header("X-Vault-Namespace", loginNamespace).build();
				return nextFilter.exchange(newRequest);
			}
			return nextFilter.exchange(clientRequest);
		};
	}

}

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by tracing the WebClientFactory setup, VaultProperties, VaultReactiveAutoConfiguration, and the Kubernetes auth method shown in the workaround. Verify how the login request and config import requests set X-Vault-Namespace; done means a login-only namespace can be configured while fully qualified secret paths continue to address other namespaces.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
authentication, backend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.