From 5369022061288eecad97ffe199ed2404aee0ed9b Mon Sep 17 00:00:00 2001 From: Sascha Isele Date: Wed, 9 Oct 2024 10:15:14 +0200 Subject: [PATCH 1/7] docs: adds decision record for HashiCorp Vault auth refactor --- .../README.md | 243 ++++++++++++++++++ 1 file changed, 243 insertions(+) create mode 100644 docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md diff --git a/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md b/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md new file mode 100644 index 0000000000..e9e516123f --- /dev/null +++ b/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md @@ -0,0 +1,243 @@ +# Hashicorp Vault authentication refactor + +## Decision + +Refactor the HashiCorp Vault extension and make it extensible with different methods of authentication. + +## Rationale + +The current implementation of the authentication in the HashiCorp Vault extension has no way to add new authentication methods. +The full list of possible authentication methods can be found in the [HashiCorp Vault Documentation](https://developer.hashicorp.com/vault/docs/auth) +Relevant examples are: + +* [Token auth](https://developer.hashicorp.com/vault/docs/auth/token) +* [Kubernetes auth](https://developer.hashicorp.com/vault/docs/auth/kubernetes) +* [AppRole auth](https://developer.hashicorp.com/vault/docs/auth/approle) + +It goes against the EDC extensibility model and needs to be remedied. +Additionally, extracting the current implementation of the token authentication into its own extension will improve readability and maintainability of the code. + +## Approach + +The refactor will affect only the `vault-hashicorp` extension. +It will create one additional extension called `vault-hashicorp-auth-tokenbased` that contains the token authentication and token renewal functionality. + +To allow an extensible authentication for HashiCorp Vault, the implementation will follow the registry pattern. + +### Hashicorp Vault Auth SPI + +For the proper organisation of the interfaces needed for the refactoring, a new module named `hashicorp-vault-auth-spi` is introduced in the `spi` directory. + +It will contain the following interfaces: + +* [Hashicorp Vault Auth Interface](#hashicorp-vault-auth-interface) +* [Hashicorp Vault Auth Registry Interface](#hashicorp-vault-auth-registry) + +### Hashicorp Vault Auth Interface + +To implement a multitude of different authentication methods, an interface for the Hashicorp Vault authentication is needed. +The sole common point between the authentication methods is the ability to provide a `client_token` that can be used to authenticate with HashiCorp Vault. + +```java +public interface HashicorpVaultAuth { + + // The stored token is returned. + String vaultToken(); + +} +``` + +`HashicorpVaultAuth` implementations will be registered in `HashicorpVaultAuthRegistry` and used by the `HashicorpVaultClient` to receive the `client_token` for the request authentication. +More on that in the sections [Hashicorp Vault Auth Implementation Registration](#Hashicorp-Vault-Auth-Registration) and [HashiCorp Vault Client](#HashiCorp-Vault-Client) + +### Haschicorp Vault Auth Service Extension + +For every authentication method, an implementation of the [Hashicorp Vault Auth interface](#hashicorp-vault-auth-interface) is needed. +Each `HashicorpVaultAuth` implementation represents a different authentication method and is packaged inside its own service extension. +In this way, it can easily be added/removed from the runtime and maintained in one place. +Due to the possible differences in the needed configuration of different authentication methods, each Service Extension will need its own configuration values specific to the authentication method. + +### Simple Token Auth + +To keep the HashiCorp Vault Extension functional on its own for demo and testing purposes, `SimpleTokenAuth` is implemented. +`SimpleTokenAuth` is a very rudimentary implementation of the [Hashicorp Vault Auth interface](#hashicorp-vault-auth-interface), that only stores a token and does not offer any refresh functionality. +It is always added to the registry and will be used when `fallbackToken` is selected as authentication method, more on that in the [Configuration](#configuration) section. + +```java +public class SimpleTokenAuth implements HashicorpVaultAuth { + private String vaultToken; + + SimpleTokenAuth(String vaultToken) { + this.vaultToken = vaultToken; + } + + @Override + public String vaultToken() { + return vaultToken; + } +} +``` + +### Hashicorp Vault Auth Registry + +In line with the registry pattern, `HashicorpVaultAuthRegistry` and `HashicorpVaultAuthRegistryImpl` are created. +The `HashicorpVaultAuthRegistry` will be used to store one or more implementations of `HashicorpVaultAuth`, each representing a different authentication method. +More on the usage of the `HashicorpVaultAuthRegistry` for registration in the [HashiCorp Vault Auth Registration](#hashicorp-vault-auth-registration) section. +The `HashicorpVaultAuthRegistryImpl` is added to the constructor of `HashicorpVaultClient` and can then be used to retrieve the `vaultToken` for the Header creation, more in that in the [HashiCorp Vault Client](#hashicorp-vault-client) section. + +```java +public interface HashicorpVaultAuthRegistry { + + void register(String method, HashicorpVaultAuth authImplementation); + + @NotNull + HashicorpVaultAuth resolve(String method); + + boolean hasMethod(String method); +} +``` + +```java +public class HashicorpVaultAuthRegistryImpl implements HashicorpVaultAuthRegistry { + + private final Map services = new HashMap<>(); + + public HashicorpVaultAuthRegistryImpl() { + } + + @Override + public void register(String method, HashicorpVaultAuth service) { + services.put(method, service); + } + + @Override + public @NotNull HashicorpVaultAuth resolve(String method) { + if (services.get(method) == null){ + throw new IllegalArgumentException(String.format("No authentication method registered under %s", method)); + } + return services.get(method); + } + + @Override + public boolean hasMethod(String method) { + return services.containsKey(method); + } +} +``` + +### Hashicorp Vault Auth Registration + +During the `initialize()` call, service extensions providing an auth method will register an instance of their `HashicorpVaultAuth` implementation in the `HashicorpVaultAuthRegistry`. +The `HashicorpVaultAuthRegistry` is provided to the service extension through use of the provider pattern by the `HashicorpVaultExtension`. + +```java +@Provider +public HashicorpVaultAuthRegistry hashicorpVaultAuthRegistry() { + return new HashicorpVaultAuthRegistryImpl(); +} +``` + +Inside the service extension providing an `HashicorpVaultAuth` implementation, the `HashicorpVaultAuthRegistry` is injected. + +```java +@Inject +private HashicorpVaultAuthRegistry hashicorpVaultAuthRegistry; +``` + +The injected `HashicorpVaultAuthRegistry` is used to register the `HashicorpVaultAuth` implementation. + +```java +@Override +public void initialize(ServiceExtensionContext context) { + var token = context.getSetting(VAULT_TOKEN, null); + + if (hashicorpVaultAuthRegistry.hasMethod("token-based")) { + throw new EdcException("Authentication method token-based is already registered"); + } + + hashicorpVaultAuthRegistry.register("token-based", HashicorpVaultTokenAuth(token)); + +} +``` + +### Configuration + +A new config value is introduced to the HashiCorp Vault Extension named `edc.vault.hashicorp.auth.method`. +`edc.vault.hashicorp.auth.method` governs which `HashicorpVaultAuth` implementation is used from `HashicorpVaultAuthRegistry` and is persisted in `HashicorpVaultSettings`. + +For testing and demo purposes, another setting called `edc.vault.hashicorp.auth.token.fallback` is introduced. +In case, the HashiCorp Vault Extension is used on its own, this setting can be used to store a token for authentication without any refresh mechanism. + +### HashiCorp Vault Client + +Since the `HashicorpVaultClient` contains a lot of authentication logic, it will also go through a refactoring. +The goal of the refactoring, is the removal of the token authentication logic from `HashicorpVaultClient` and to make the authentication logic interchangeable. +`VaultAuthenticationRegistry` is passed to `HashicorpVaultClient` during creation by `HashicorpVaultServiceExtension`. +`HashicorpVaultClient` will use `VaultAuthenticationRegistry` based on `edc.vault.hashicorp.auth.method` setting to fetch the `client_token` that is provided by the chosen `HashicorpVaultAuth` implementation. +`client_token` will then be used to generate the Headers for the HTTP requests and to authenticate with HashiCorp Vault. + +Old `getHeaders()`: + +```java +@NotNull +private Headers getHeaders() { + var headersBuilder = new Headers.Builder().add(VAULT_REQUEST_HEADER, Boolean.toString(true)); + headersBuilder.add(VAULT_TOKEN_HEADER, settings.token()); + return headersBuilder.build(); +} +``` + +New `getHeaders()`: + +```java +@NotNull +private Headers getHeaders() { + var headersBuilder = new Headers.Builder().add(VAULT_REQUEST_HEADER, Boolean.toString(true)); + headersBuilder.add(VAULT_TOKEN_HEADER, vaultAuthenticationRegistry.resolve(settings.getAuthMethod()).vaultToken()); + return headersBuilder.build(); +} +``` + +An important change that was made, is the point at which the headers are generated. +Previously, the headers were generated only once since the token was not liable to change. +With the addition of numerous potential authentication mechanisms, it can no longer be guaranteed, that the token never changes during refresh. +An example for this would be the kubernetes authentication method, where only short term tokens are produced each time. +As such, `headers` are no longer saved as a variable inside the `HashicorpVaultClient` and `getHeaders()` is called instead to always fetch the newest token. + +### HashiCorp Vault Extension refactor + +The token based authentication logic is refactored and moved into its own extension named `HashiCorp Vault Token Auth`. +This includes the token refresh functionality and will lead to a refactoring of the following classes: + +`HashicorpVaultExtension.java`: `initialize()` no longer will start the token refresh and `stop()` will be removed since the extension does not govern token refresh anymore. + +`HashicorpVaultClient.java`: `isTokenRenewable()` and `renewToken()` and their private methods will be moved to a new dedicated client in the Token Auth Extension. + +`HashicorpVaultSettings.java`: some settings are moved and two new ones are added as described in the sections [HashiCorp Vault Token Auth Extension Settings](#hashicorp-vault-token-auth-extension-settings) and [Configuration](#configuration). + +`HashicorpVaultTokenRenewTask.java`: is moved to the HashiCorp Vault Token Auth Extension in its entirety. + +The respective tests covering the moved functionality are also moved accordingly. + +### HashiCorp Vault Token Auth Extension + +The HashiCorp Vault Token Auth Extension will contain the token storage functionality and the token refresh functionality that has been removed from the HashiCorp Vault Extension. + +#### HashiCorp Vault Token Auth Extension Settings + +The following settings will be moved from the HashiCorp Vault Extension to th HashiCorp Vault Token Auth Extension. + +Duplicate setting from the HashiCorp Vault Extension, since the HashiCorp Vault Token Auth Extension also needs access to the vault. +`edc.vault.hashicorp.url` + +Settings moved from the HashiCorp Vault Extension, that only concern the token storage and token refresh. +`edc.vault.hashicorp.token` +`edc.vault.hashicorp.token.scheduled-renew-enabled` +`edc.vault.hashicorp.token.ttl` +`edc.vault.hashicorp.token.renew-buffer` + +### HashiCorp Vault Health Extension + +The `get()` method of `HashicorpVaultHealthCheck` is currently using a merge of the response from the Hashicorp Vault Health API and the ability to renew the given token. +After the refactor, the token renewal is no longer a part of the base `HashicorpVaultExtension`. +As such the `get()` method is adjusted and will only return the response from the Hashicorp Vault Health API. From 2c2a490e97818bd1ddf8ce348434bae64420a316 Mon Sep 17 00:00:00 2001 From: Sascha Isele Date: Thu, 9 Jan 2025 09:49:08 +0100 Subject: [PATCH 2/7] docs: changes registry pattern to provider with default --- .../README.md | 170 +++--------------- 1 file changed, 26 insertions(+), 144 deletions(-) diff --git a/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md b/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md index e9e516123f..40cde6cf36 100644 --- a/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md +++ b/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md @@ -14,24 +14,11 @@ Relevant examples are: * [Kubernetes auth](https://developer.hashicorp.com/vault/docs/auth/kubernetes) * [AppRole auth](https://developer.hashicorp.com/vault/docs/auth/approle) -It goes against the EDC extensibility model and needs to be remedied. -Additionally, extracting the current implementation of the token authentication into its own extension will improve readability and maintainability of the code. - ## Approach The refactor will affect only the `vault-hashicorp` extension. -It will create one additional extension called `vault-hashicorp-auth-tokenbased` that contains the token authentication and token renewal functionality. - -To allow an extensible authentication for HashiCorp Vault, the implementation will follow the registry pattern. - -### Hashicorp Vault Auth SPI -For the proper organisation of the interfaces needed for the refactoring, a new module named `hashicorp-vault-auth-spi` is introduced in the `spi` directory. - -It will contain the following interfaces: - -* [Hashicorp Vault Auth Interface](#hashicorp-vault-auth-interface) -* [Hashicorp Vault Auth Registry Interface](#hashicorp-vault-auth-registry) +To allow an extensible authentication for HashiCorp Vault, the implementation will follow the provider pattern with a default implementation. ### Hashicorp Vault Auth Interface @@ -47,133 +34,43 @@ public interface HashicorpVaultAuth { } ``` -`HashicorpVaultAuth` implementations will be registered in `HashicorpVaultAuthRegistry` and used by the `HashicorpVaultClient` to receive the `client_token` for the request authentication. -More on that in the sections [Hashicorp Vault Auth Implementation Registration](#Hashicorp-Vault-Auth-Registration) and [HashiCorp Vault Client](#HashiCorp-Vault-Client) +`HashicorpVaultAuth` implementations will be used by the `HashicorpVaultClient` to receive the `client_token` for the request authentication. +More on that in the sections [Hashicorp Vault Auth Provision](#Hashicorp-Vault-Auth-Provision) and [HashiCorp Vault Client](#HashiCorp-Vault-Client) -### Haschicorp Vault Auth Service Extension +### Haschicorp Vault Auth Extensions For every authentication method, an implementation of the [Hashicorp Vault Auth interface](#hashicorp-vault-auth-interface) is needed. Each `HashicorpVaultAuth` implementation represents a different authentication method and is packaged inside its own service extension. In this way, it can easily be added/removed from the runtime and maintained in one place. Due to the possible differences in the needed configuration of different authentication methods, each Service Extension will need its own configuration values specific to the authentication method. +The exception will be the token authentication, which is already packaged inside the `HashicorpVaultExtension` and used as the default authentication method. -### Simple Token Auth +### Hashicorp Vault Auth Provision -To keep the HashiCorp Vault Extension functional on its own for demo and testing purposes, `SimpleTokenAuth` is implemented. -`SimpleTokenAuth` is a very rudimentary implementation of the [Hashicorp Vault Auth interface](#hashicorp-vault-auth-interface), that only stores a token and does not offer any refresh functionality. -It is always added to the registry and will be used when `fallbackToken` is selected as authentication method, more on that in the [Configuration](#configuration) section. +The `HashicorpVaultTokenAuth` implementation will serve as default authentication mechanism and will be provided by the `HashicorpVaultExtension`. ```java -public class SimpleTokenAuth implements HashicorpVaultAuth { - private String vaultToken; - - SimpleTokenAuth(String vaultToken) { - this.vaultToken = vaultToken; - } - - @Override - public String vaultToken() { - return vaultToken; - } +@Provider(isDefault = true) +public HashicorpVaultAuth hashicorpVaultAuth() { + return new HashicorpVaultTokenAuth(); } ``` -### Hashicorp Vault Auth Registry - -In line with the registry pattern, `HashicorpVaultAuthRegistry` and `HashicorpVaultAuthRegistryImpl` are created. -The `HashicorpVaultAuthRegistry` will be used to store one or more implementations of `HashicorpVaultAuth`, each representing a different authentication method. -More on the usage of the `HashicorpVaultAuthRegistry` for registration in the [HashiCorp Vault Auth Registration](#hashicorp-vault-auth-registration) section. -The `HashicorpVaultAuthRegistryImpl` is added to the constructor of `HashicorpVaultClient` and can then be used to retrieve the `vaultToken` for the Header creation, more in that in the [HashiCorp Vault Client](#hashicorp-vault-client) section. - -```java -public interface HashicorpVaultAuthRegistry { - - void register(String method, HashicorpVaultAuth authImplementation); - - @NotNull - HashicorpVaultAuth resolve(String method); - - boolean hasMethod(String method); -} -``` - -```java -public class HashicorpVaultAuthRegistryImpl implements HashicorpVaultAuthRegistry { - - private final Map services = new HashMap<>(); - - public HashicorpVaultAuthRegistryImpl() { - } - - @Override - public void register(String method, HashicorpVaultAuth service) { - services.put(method, service); - } - - @Override - public @NotNull HashicorpVaultAuth resolve(String method) { - if (services.get(method) == null){ - throw new IllegalArgumentException(String.format("No authentication method registered under %s", method)); - } - return services.get(method); - } - - @Override - public boolean hasMethod(String method) { - return services.containsKey(method); - } -} -``` - -### Hashicorp Vault Auth Registration - -During the `initialize()` call, service extensions providing an auth method will register an instance of their `HashicorpVaultAuth` implementation in the `HashicorpVaultAuthRegistry`. -The `HashicorpVaultAuthRegistry` is provided to the service extension through use of the provider pattern by the `HashicorpVaultExtension`. - -```java -@Provider -public HashicorpVaultAuthRegistry hashicorpVaultAuthRegistry() { - return new HashicorpVaultAuthRegistryImpl(); -} -``` +`isDefault = true` signifies, that `HashicorpVaultTokenAuth` will be used unless another extension overwrites it by providing its own implementation without the parameter. -Inside the service extension providing an `HashicorpVaultAuth` implementation, the `HashicorpVaultAuthRegistry` is injected. +The provided implementation of `HashicorpVaultAuth` is then injected into the `HashicorpVaultExtension` to be used in the `HashicorpVaultClient` later on. ```java @Inject -private HashicorpVaultAuthRegistry hashicorpVaultAuthRegistry; -``` - -The injected `HashicorpVaultAuthRegistry` is used to register the `HashicorpVaultAuth` implementation. - -```java -@Override -public void initialize(ServiceExtensionContext context) { - var token = context.getSetting(VAULT_TOKEN, null); - - if (hashicorpVaultAuthRegistry.hasMethod("token-based")) { - throw new EdcException("Authentication method token-based is already registered"); - } - - hashicorpVaultAuthRegistry.register("token-based", HashicorpVaultTokenAuth(token)); - -} +private HashicorpVaultAuth hashicorpVaultAuth; ``` -### Configuration - -A new config value is introduced to the HashiCorp Vault Extension named `edc.vault.hashicorp.auth.method`. -`edc.vault.hashicorp.auth.method` governs which `HashicorpVaultAuth` implementation is used from `HashicorpVaultAuthRegistry` and is persisted in `HashicorpVaultSettings`. - -For testing and demo purposes, another setting called `edc.vault.hashicorp.auth.token.fallback` is introduced. -In case, the HashiCorp Vault Extension is used on its own, this setting can be used to store a token for authentication without any refresh mechanism. - ### HashiCorp Vault Client Since the `HashicorpVaultClient` contains a lot of authentication logic, it will also go through a refactoring. The goal of the refactoring, is the removal of the token authentication logic from `HashicorpVaultClient` and to make the authentication logic interchangeable. -`VaultAuthenticationRegistry` is passed to `HashicorpVaultClient` during creation by `HashicorpVaultServiceExtension`. -`HashicorpVaultClient` will use `VaultAuthenticationRegistry` based on `edc.vault.hashicorp.auth.method` setting to fetch the `client_token` that is provided by the chosen `HashicorpVaultAuth` implementation. +An implementation of `HashicorpVaultAuth` is passed to `HashicorpVaultClient` during creation by `HashicorpVaultExtension`. +`HashicorpVaultClient` will use the `HashicorpVaultAuth` implementation to fetch the `client_token`. `client_token` will then be used to generate the Headers for the HTTP requests and to authenticate with HashiCorp Vault. Old `getHeaders()`: @@ -193,7 +90,7 @@ New `getHeaders()`: @NotNull private Headers getHeaders() { var headersBuilder = new Headers.Builder().add(VAULT_REQUEST_HEADER, Boolean.toString(true)); - headersBuilder.add(VAULT_TOKEN_HEADER, vaultAuthenticationRegistry.resolve(settings.getAuthMethod()).vaultToken()); + headersBuilder.add(VAULT_TOKEN_HEADER, vaultAuth.vaultToken()); return headersBuilder.build(); } ``` @@ -201,43 +98,28 @@ private Headers getHeaders() { An important change that was made, is the point at which the headers are generated. Previously, the headers were generated only once since the token was not liable to change. With the addition of numerous potential authentication mechanisms, it can no longer be guaranteed, that the token never changes during refresh. -An example for this would be the kubernetes authentication method, where only short term tokens are produced each time. +An example for this would be the kubernetes authentication method, where short term tokens can be produced depending on the settings. As such, `headers` are no longer saved as a variable inside the `HashicorpVaultClient` and `getHeaders()` is called instead to always fetch the newest token. -### HashiCorp Vault Extension refactor +### HashiCorp Vault Extension Refactor -The token based authentication logic is refactored and moved into its own extension named `HashiCorp Vault Token Auth`. +The token based authentication logic is refactored and moved into dedicated classes in the newly created `auth` package. This includes the token refresh functionality and will lead to a refactoring of the following classes: -`HashicorpVaultExtension.java`: `initialize()` no longer will start the token refresh and `stop()` will be removed since the extension does not govern token refresh anymore. - -`HashicorpVaultClient.java`: `isTokenRenewable()` and `renewToken()` and their private methods will be moved to a new dedicated client in the Token Auth Extension. +`HashicorpVaultClient.java`: `isTokenRenewable()` and `renewToken()` and their private methods will be moved to a new dedicated client class. -`HashicorpVaultSettings.java`: some settings are moved and two new ones are added as described in the sections [HashiCorp Vault Token Auth Extension Settings](#hashicorp-vault-token-auth-extension-settings) and [Configuration](#configuration). - -`HashicorpVaultTokenRenewTask.java`: is moved to the HashiCorp Vault Token Auth Extension in its entirety. +`HashicorpVaultTokenRenewTask.java`: is moved to the `auth` package in its entirety. The respective tests covering the moved functionality are also moved accordingly. -### HashiCorp Vault Token Auth Extension - -The HashiCorp Vault Token Auth Extension will contain the token storage functionality and the token refresh functionality that has been removed from the HashiCorp Vault Extension. - -#### HashiCorp Vault Token Auth Extension Settings - -The following settings will be moved from the HashiCorp Vault Extension to th HashiCorp Vault Token Auth Extension. - -Duplicate setting from the HashiCorp Vault Extension, since the HashiCorp Vault Token Auth Extension also needs access to the vault. -`edc.vault.hashicorp.url` - -Settings moved from the HashiCorp Vault Extension, that only concern the token storage and token refresh. -`edc.vault.hashicorp.token` -`edc.vault.hashicorp.token.scheduled-renew-enabled` -`edc.vault.hashicorp.token.ttl` -`edc.vault.hashicorp.token.renew-buffer` ### HashiCorp Vault Health Extension The `get()` method of `HashicorpVaultHealthCheck` is currently using a merge of the response from the Hashicorp Vault Health API and the ability to renew the given token. After the refactor, the token renewal is no longer a part of the base `HashicorpVaultExtension`. As such the `get()` method is adjusted and will only return the response from the Hashicorp Vault Health API. + +## Further Considerations + +This Decision Record only paves the way to add additional authentication methods later on. +As such, possible authentication methods that can be added in the future are not discussed in more detail here, but will require their own Decision Records containing the outline of the implementation and testing. From f48fedbd9682cf547266b3b1c79fdd49eae41ec2 Mon Sep 17 00:00:00 2001 From: Sascha Isele Date: Mon, 13 Jan 2025 09:17:39 +0100 Subject: [PATCH 3/7] docs: minor wording changes --- .../2024-12-13-vault-authentication-refactor/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md b/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md index 40cde6cf36..18fe5d46f4 100644 --- a/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md +++ b/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md @@ -116,7 +116,7 @@ The respective tests covering the moved functionality are also moved accordingly ### HashiCorp Vault Health Extension The `get()` method of `HashicorpVaultHealthCheck` is currently using a merge of the response from the Hashicorp Vault Health API and the ability to renew the given token. -After the refactor, the token renewal is no longer a part of the base `HashicorpVaultExtension`. +After the refactor, the token renewal lookup functionality is no longer guaranteed to exist inside the `HashicorpVaultExtension`. As such the `get()` method is adjusted and will only return the response from the Hashicorp Vault Health API. ## Further Considerations From 57eac2b8deb501f023699cbcf7ac5c1dfd30f526 Mon Sep 17 00:00:00 2001 From: Sascha Isele Date: Fri, 17 Jan 2025 04:02:21 +0100 Subject: [PATCH 4/7] docs: incorporated review suggestions --- .../README.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md b/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md index 18fe5d46f4..5db4bef2c9 100644 --- a/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md +++ b/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md @@ -26,7 +26,7 @@ To implement a multitude of different authentication methods, an interface for t The sole common point between the authentication methods is the ability to provide a `client_token` that can be used to authenticate with HashiCorp Vault. ```java -public interface HashicorpVaultAuth { +public interface HashicorpVaultAuthTokenProvider { // The stored token is returned. String vaultToken(); @@ -34,13 +34,13 @@ public interface HashicorpVaultAuth { } ``` -`HashicorpVaultAuth` implementations will be used by the `HashicorpVaultClient` to receive the `client_token` for the request authentication. +`HashicorpVaultAuthTokenProvider` implementations will be used by the `HashicorpVaultClient` to receive the `client_token` for the request authentication. More on that in the sections [Hashicorp Vault Auth Provision](#Hashicorp-Vault-Auth-Provision) and [HashiCorp Vault Client](#HashiCorp-Vault-Client) ### Haschicorp Vault Auth Extensions For every authentication method, an implementation of the [Hashicorp Vault Auth interface](#hashicorp-vault-auth-interface) is needed. -Each `HashicorpVaultAuth` implementation represents a different authentication method and is packaged inside its own service extension. +Each `HashicorpVaultAuthTokenProvider` implementation represents a different authentication method and is packaged inside its own service extension. In this way, it can easily be added/removed from the runtime and maintained in one place. Due to the possible differences in the needed configuration of different authentication methods, each Service Extension will need its own configuration values specific to the authentication method. The exception will be the token authentication, which is already packaged inside the `HashicorpVaultExtension` and used as the default authentication method. @@ -51,26 +51,26 @@ The `HashicorpVaultTokenAuth` implementation will serve as default authenticatio ```java @Provider(isDefault = true) -public HashicorpVaultAuth hashicorpVaultAuth() { +public HashicorpVaultAuthTokenProvider hashicorpVaultAuthTokenProvider() { return new HashicorpVaultTokenAuth(); } ``` `isDefault = true` signifies, that `HashicorpVaultTokenAuth` will be used unless another extension overwrites it by providing its own implementation without the parameter. -The provided implementation of `HashicorpVaultAuth` is then injected into the `HashicorpVaultExtension` to be used in the `HashicorpVaultClient` later on. +The provided implementation of `HashicorpVaultAuthTokenProvider` is then injected into the `HashicorpVaultExtension` to be used in the `HashicorpVaultClient` later on. ```java @Inject -private HashicorpVaultAuth hashicorpVaultAuth; +private HashicorpVaultAuthTokenProvider hashicorpVaultAuthTokenProvider; ``` ### HashiCorp Vault Client Since the `HashicorpVaultClient` contains a lot of authentication logic, it will also go through a refactoring. The goal of the refactoring, is the removal of the token authentication logic from `HashicorpVaultClient` and to make the authentication logic interchangeable. -An implementation of `HashicorpVaultAuth` is passed to `HashicorpVaultClient` during creation by `HashicorpVaultExtension`. -`HashicorpVaultClient` will use the `HashicorpVaultAuth` implementation to fetch the `client_token`. +An implementation of `HashicorpVaultAuthTokenProvider` is passed to `HashicorpVaultClient` during creation by `HashicorpVaultExtension`. +`HashicorpVaultClient` will use the `HashicorpVaultAuthTokenProvider` implementation to fetch the `client_token`. `client_token` will then be used to generate the Headers for the HTTP requests and to authenticate with HashiCorp Vault. Old `getHeaders()`: @@ -88,9 +88,9 @@ New `getHeaders()`: ```java @NotNull -private Headers getHeaders() { +private Headers getHeaders(String token) { var headersBuilder = new Headers.Builder().add(VAULT_REQUEST_HEADER, Boolean.toString(true)); - headersBuilder.add(VAULT_TOKEN_HEADER, vaultAuth.vaultToken()); + headersBuilder.add(VAULT_TOKEN_HEADER, token); return headersBuilder.build(); } ``` From b4c47287bf48e965365ccefb877c59c29160b978 Mon Sep 17 00:00:00 2001 From: Sascha Isele Date: Wed, 22 Jan 2025 02:14:06 +0100 Subject: [PATCH 5/7] docs: makes naming more distinct --- .../README.md | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md b/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md index 5db4bef2c9..0746dd1a9c 100644 --- a/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md +++ b/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md @@ -26,7 +26,7 @@ To implement a multitude of different authentication methods, an interface for t The sole common point between the authentication methods is the ability to provide a `client_token` that can be used to authenticate with HashiCorp Vault. ```java -public interface HashicorpVaultAuthTokenProvider { +public interface HashicorpVaultAuthClientTokenProvider { // The stored token is returned. String vaultToken(); @@ -34,43 +34,43 @@ public interface HashicorpVaultAuthTokenProvider { } ``` -`HashicorpVaultAuthTokenProvider` implementations will be used by the `HashicorpVaultClient` to receive the `client_token` for the request authentication. +`HashicorpVaultAuthClientTokenProvider` implementations will be used by the `HashicorpVaultClient` to receive the `client_token` for the request authentication. More on that in the sections [Hashicorp Vault Auth Provision](#Hashicorp-Vault-Auth-Provision) and [HashiCorp Vault Client](#HashiCorp-Vault-Client) ### Haschicorp Vault Auth Extensions For every authentication method, an implementation of the [Hashicorp Vault Auth interface](#hashicorp-vault-auth-interface) is needed. -Each `HashicorpVaultAuthTokenProvider` implementation represents a different authentication method and is packaged inside its own service extension. +Each `HashicorpVaultAuthClientTokenProvider` implementation represents a different authentication method and is packaged inside its own service extension. In this way, it can easily be added/removed from the runtime and maintained in one place. Due to the possible differences in the needed configuration of different authentication methods, each Service Extension will need its own configuration values specific to the authentication method. The exception will be the token authentication, which is already packaged inside the `HashicorpVaultExtension` and used as the default authentication method. ### Hashicorp Vault Auth Provision -The `HashicorpVaultTokenAuth` implementation will serve as default authentication mechanism and will be provided by the `HashicorpVaultExtension`. +The `HashicorpVaultAuthTokenImpl` implementation will serve as default authentication mechanism and will be provided by the `HashicorpVaultExtension`. ```java @Provider(isDefault = true) -public HashicorpVaultAuthTokenProvider hashicorpVaultAuthTokenProvider() { - return new HashicorpVaultTokenAuth(); +public HashicorpVaultAuthClientTokenProvider HashicorpVaultAuthClientTokenProvider() { + return new HashicorpVaultAuthTokenImpl(); } ``` -`isDefault = true` signifies, that `HashicorpVaultTokenAuth` will be used unless another extension overwrites it by providing its own implementation without the parameter. +`isDefault = true` signifies, that `HashicorpVaultAuthTokenImpl` will be used unless another extension overwrites it by providing its own implementation without the parameter. -The provided implementation of `HashicorpVaultAuthTokenProvider` is then injected into the `HashicorpVaultExtension` to be used in the `HashicorpVaultClient` later on. +The provided implementation of `HashicorpVaultAuthClientTokenProvider` is then injected into the `HashicorpVaultExtension` to be used in the `HashicorpVaultClient` later on. ```java @Inject -private HashicorpVaultAuthTokenProvider hashicorpVaultAuthTokenProvider; +private HashicorpVaultAuthClientTokenProvider hashicorpVaultAuthClientTokenProvider; ``` ### HashiCorp Vault Client Since the `HashicorpVaultClient` contains a lot of authentication logic, it will also go through a refactoring. The goal of the refactoring, is the removal of the token authentication logic from `HashicorpVaultClient` and to make the authentication logic interchangeable. -An implementation of `HashicorpVaultAuthTokenProvider` is passed to `HashicorpVaultClient` during creation by `HashicorpVaultExtension`. -`HashicorpVaultClient` will use the `HashicorpVaultAuthTokenProvider` implementation to fetch the `client_token`. +An implementation of `HashicorpVaultAuthClientTokenProvider` is passed to `HashicorpVaultClient` during creation by `HashicorpVaultExtension`. +`HashicorpVaultClient` will use the `HashicorpVaultAuthClientTokenProvider` implementation to fetch the `client_token`. `client_token` will then be used to generate the Headers for the HTTP requests and to authenticate with HashiCorp Vault. Old `getHeaders()`: From 0d2c23ca1c9ca1403d4a1c5b8b991d45916ea493 Mon Sep 17 00:00:00 2001 From: Sascha Isele Date: Fri, 24 Jan 2025 07:59:46 +0100 Subject: [PATCH 6/7] docs: adjusts for the changes coming with the signing service --- .../README.md | 70 +++++-------------- 1 file changed, 16 insertions(+), 54 deletions(-) diff --git a/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md b/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md index 0746dd1a9c..8c9d43ae73 100644 --- a/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md +++ b/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md @@ -2,11 +2,11 @@ ## Decision -Refactor the HashiCorp Vault extension and make it extensible with different methods of authentication. +We will refactor the HashiCorp Vault extension and make it extensible with different methods of authentication. ## Rationale -The current implementation of the authentication in the HashiCorp Vault extension has no way to add new authentication methods. +The current implementation of the authentication in the HashiCorp Vault extension has no way to customize/exchange authentication methods. The full list of possible authentication methods can be found in the [HashiCorp Vault Documentation](https://developer.hashicorp.com/vault/docs/auth) Relevant examples are: @@ -16,7 +16,7 @@ Relevant examples are: ## Approach -The refactor will affect only the `vault-hashicorp` extension. +The refactor will affect only the `vault-hashicorp` module. To allow an extensible authentication for HashiCorp Vault, the implementation will follow the provider pattern with a default implementation. @@ -34,58 +34,31 @@ public interface HashicorpVaultAuthClientTokenProvider { } ``` -`HashicorpVaultAuthClientTokenProvider` implementations will be used by the `HashicorpVaultClient` to receive the `client_token` for the request authentication. -More on that in the sections [Hashicorp Vault Auth Provision](#Hashicorp-Vault-Auth-Provision) and [HashiCorp Vault Client](#HashiCorp-Vault-Client) +### Hashicorp Vault Token Auth Method Implementation -### Haschicorp Vault Auth Extensions - -For every authentication method, an implementation of the [Hashicorp Vault Auth interface](#hashicorp-vault-auth-interface) is needed. -Each `HashicorpVaultAuthClientTokenProvider` implementation represents a different authentication method and is packaged inside its own service extension. -In this way, it can easily be added/removed from the runtime and maintained in one place. -Due to the possible differences in the needed configuration of different authentication methods, each Service Extension will need its own configuration values specific to the authentication method. -The exception will be the token authentication, which is already packaged inside the `HashicorpVaultExtension` and used as the default authentication method. - -### Hashicorp Vault Auth Provision - -The `HashicorpVaultAuthTokenImpl` implementation will serve as default authentication mechanism and will be provided by the `HashicorpVaultExtension`. +For every authentication method, an implementation of `HashicorpVaultAuthClientTokenProvider` is needed. +`HashicorpVaultTokenAuthMethodImpl` implements the [Token auth method](https://developer.hashicorp.com/vault/docs/auth/token) and serves as default authentication mechanism provided by the `HashicorpVaultExtension`. ```java @Provider(isDefault = true) -public HashicorpVaultAuthClientTokenProvider HashicorpVaultAuthClientTokenProvider() { - return new HashicorpVaultAuthTokenImpl(); +public HashicorpVaultAuthClientTokenProvider hashicorpVaultAuthClientTokenProvider() { + return new HashicorpVaultTokenAuthMethodImpl(); } ``` -`isDefault = true` signifies, that `HashicorpVaultAuthTokenImpl` will be used unless another extension overwrites it by providing its own implementation without the parameter. +`HashicorpVaultTokenAuthMethodImpl` is then used by services in the `vault-hashicorp` module for authentication with the HashiCorp Vault instance. +These services are: -The provided implementation of `HashicorpVaultAuthClientTokenProvider` is then injected into the `HashicorpVaultExtension` to be used in the `HashicorpVaultClient` later on. - -```java -@Inject -private HashicorpVaultAuthClientTokenProvider hashicorpVaultAuthClientTokenProvider; -``` +* secure key-value store `HashicorpVault` +* signing service `HashicorpVaultSignatureService` +* health check service `HashicorpVaultHealthService` -### HashiCorp Vault Client +### HashiCorp Vault Health Service -Since the `HashicorpVaultClient` contains a lot of authentication logic, it will also go through a refactoring. -The goal of the refactoring, is the removal of the token authentication logic from `HashicorpVaultClient` and to make the authentication logic interchangeable. -An implementation of `HashicorpVaultAuthClientTokenProvider` is passed to `HashicorpVaultClient` during creation by `HashicorpVaultExtension`. -`HashicorpVaultClient` will use the `HashicorpVaultAuthClientTokenProvider` implementation to fetch the `client_token`. +An implementation of `HashicorpVaultAuthClientTokenProvider` is passed to `HashicorpVaultHealthService` during creation by `HashicorpVaultExtension`. +`HashicorpVaultHealthService` will use the `HashicorpVaultAuthClientTokenProvider` implementation to fetch the `client_token`. `client_token` will then be used to generate the Headers for the HTTP requests and to authenticate with HashiCorp Vault. -Old `getHeaders()`: - -```java -@NotNull -private Headers getHeaders() { - var headersBuilder = new Headers.Builder().add(VAULT_REQUEST_HEADER, Boolean.toString(true)); - headersBuilder.add(VAULT_TOKEN_HEADER, settings.token()); - return headersBuilder.build(); -} -``` - -New `getHeaders()`: - ```java @NotNull private Headers getHeaders(String token) { @@ -101,17 +74,6 @@ With the addition of numerous potential authentication mechanisms, it can no lon An example for this would be the kubernetes authentication method, where short term tokens can be produced depending on the settings. As such, `headers` are no longer saved as a variable inside the `HashicorpVaultClient` and `getHeaders()` is called instead to always fetch the newest token. -### HashiCorp Vault Extension Refactor - -The token based authentication logic is refactored and moved into dedicated classes in the newly created `auth` package. -This includes the token refresh functionality and will lead to a refactoring of the following classes: - -`HashicorpVaultClient.java`: `isTokenRenewable()` and `renewToken()` and their private methods will be moved to a new dedicated client class. - -`HashicorpVaultTokenRenewTask.java`: is moved to the `auth` package in its entirety. - -The respective tests covering the moved functionality are also moved accordingly. - ### HashiCorp Vault Health Extension From 89353e11742176f969941b46dc8d605dff43ffca Mon Sep 17 00:00:00 2001 From: Sascha Isele Date: Tue, 28 Jan 2025 05:27:13 +0100 Subject: [PATCH 7/7] docs: more concise wording and some additional details --- .../README.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md b/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md index 8c9d43ae73..b142e6bcbb 100644 --- a/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md +++ b/docs/developer/decision-records/2024-12-13-vault-authentication-refactor/README.md @@ -17,13 +17,12 @@ Relevant examples are: ## Approach The refactor will affect only the `vault-hashicorp` module. - To allow an extensible authentication for HashiCorp Vault, the implementation will follow the provider pattern with a default implementation. ### Hashicorp Vault Auth Interface -To implement a multitude of different authentication methods, an interface for the Hashicorp Vault authentication is needed. -The sole common point between the authentication methods is the ability to provide a `client_token` that can be used to authenticate with HashiCorp Vault. +To support multiple authentication methods, an interface is defined for the HashiCorp Vault authentication. +The common point among all methods is the ability to provide a `client_token` used for authentication. ```java public interface HashicorpVaultAuthClientTokenProvider { @@ -36,8 +35,7 @@ public interface HashicorpVaultAuthClientTokenProvider { ### Hashicorp Vault Token Auth Method Implementation -For every authentication method, an implementation of `HashicorpVaultAuthClientTokenProvider` is needed. -`HashicorpVaultTokenAuthMethodImpl` implements the [Token auth method](https://developer.hashicorp.com/vault/docs/auth/token) and serves as default authentication mechanism provided by the `HashicorpVaultExtension`. +The default implementation of `HashicorpVaultAuthClientTokenProvider` provided by the `HashicorpVaultExtension` will use the [Token auth method](https://developer.hashicorp.com/vault/docs/auth/token). ```java @Provider(isDefault = true) @@ -49,9 +47,12 @@ public HashicorpVaultAuthClientTokenProvider hashicorpVaultAuthClientTokenProvid `HashicorpVaultTokenAuthMethodImpl` is then used by services in the `vault-hashicorp` module for authentication with the HashiCorp Vault instance. These services are: -* secure key-value store `HashicorpVault` -* signing service `HashicorpVaultSignatureService` -* health check service `HashicorpVaultHealthService` +* Secure key-value store `HashicorpVault` +* Signing service `HashicorpVaultSignatureService` +* Health check service `HashicorpVaultHealthService` + +`HashicorpVault` and `HashicorpVaultSignatureService` will only need small changes for authentication. +They will use the `client_token` from the `HashicorpVaultTokenAuthMethodImpl` instead of directly fetching the token from the settings. ### HashiCorp Vault Health Service @@ -68,13 +69,11 @@ private Headers getHeaders(String token) { } ``` -An important change that was made, is the point at which the headers are generated. Previously, the headers were generated only once since the token was not liable to change. With the addition of numerous potential authentication mechanisms, it can no longer be guaranteed, that the token never changes during refresh. An example for this would be the kubernetes authentication method, where short term tokens can be produced depending on the settings. As such, `headers` are no longer saved as a variable inside the `HashicorpVaultClient` and `getHeaders()` is called instead to always fetch the newest token. - ### HashiCorp Vault Health Extension The `get()` method of `HashicorpVaultHealthCheck` is currently using a merge of the response from the Hashicorp Vault Health API and the ability to renew the given token.