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

Vault Namespace Login for Secret Sharing in non Hierarchical Relationship #694

Open
jschell96 opened this issue Jul 13, 2023 · 2 comments
Open

Comments

@jschell96
Copy link

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);
		};
	}

}
@mp911de
Copy link
Member

mp911de commented Jul 14, 2023

In Spring Cloud vault, we keep a single RestTemplateFactory/WebClientFactory instance that holds all configuration. If we update the config to set default headers, then headers are applied to all RestTemplate/WebClient instances produced from our factories.

We need to come up with a proper design approach without introducing too much complexity on our end and I expect this can take a while.

@milansanjeev
Copy link

milansanjeev commented Sep 4, 2023

@jschell96 @mp911de I also need to use different namespaces in both login and vault-get in Spring Vault. Can you please share some ref on how did you achieve this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants