HashiCorp Vault
Since Camel 3.18
Only producer is supported
The hashicorp-vault component that integrates HashiCorp Vault.
URI Format
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-hashicorp-vault</artifactId>
<version>x.x.x</version>
<!-- use the same version as your Camel core version -->
</dependency> Configuring Options
Camel components are configured on two separate levels:
-
component level
-
endpoint level
Configuring Component Options
At the component level, you set general and shared configurations that are, then, inherited by the endpoints. It is the highest configuration level.
For example, a component may have security settings, credentials for authentication, urls for network connection and so forth.
Some components only have a few options, and others may have many. Because components typically have pre-configured defaults that are commonly used, then you may often only need to configure a few options on a component; or none at all.
You can configure components using:
-
the Component DSL.
-
in a configuration file (
application.properties,*.yamlfiles, etc). -
directly in the Java code.
Configuring Endpoint Options
You usually spend more time setting up endpoints because they have many options. These options help you customize what you want the endpoint to do. The options are also categorized into whether the endpoint is used as a consumer (from), as a producer (to), or both.
Configuring endpoints is most often done directly in the endpoint URI as path and query parameters. You can also use the Endpoint DSL and DataFormat DSL as a type safe way of configuring endpoints and data formats in Java.
A good practice when configuring options is to use Property Placeholders.
Property placeholders provide a few benefits:
-
They help prevent using hardcoded urls, port numbers, sensitive information, and other settings.
-
They allow externalizing the configuration from the code.
-
They help the code to become more flexible and reusable.
The following two sections list all the options, firstly for the component followed by the endpoint.
Component Options
The HashiCorp Vault component supports the following options which are listed below.
| Name | Description | Default | Type |
|---|---|---|---|
Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel’s routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing. | false | boolean | |
Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching type, which then gets configured on the component. This can be used for automatic configuring JDBC data sources, JMS connection factories, AWS Clients, etc. | true | boolean |
Endpoint Options
The HashiCorp Vault endpoint is configured using URI syntax:
hashicorp-vault:secretsEngine
With the following path and query parameters:
Query Parameters
| Name | Description | Default | Type |
|---|---|---|---|
Determine if the Hashicorp Vault is deployed on Hashicorp Cloud or not. | false | boolean | |
Hashicorp Vault instance host to be used. | String | ||
If the Hashicorp Vault instance is deployed on Hashicorp Cloud, this field will determine the namespace. | String | ||
Operation to be performed. Enum values:
| HashicorpVaultOperation | ||
Hashicorp Vault instance port to be used. | 8200 | String | |
Hashicorp Vault instance scheme to be used. | https | String | |
Hashicorp Vault instance secret Path to be used. | String | ||
Autowired Instance of Vault template. | VaultTemplate | ||
Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail during starting and cause the route to fail being started. By deferring this startup to be lazy then the startup failure can be handled during routing messages via Camel’s routing error handlers. Beware that when the first message is processed then creating and starting the producer may take a little time and prolong the total processing time of the processing. | false | boolean | |
How to log in to HashiCorp Vault: token (a fixed token), approle (a role ID and a secret ID), kubernetes (the service account token of the pod), jwt (a JWT, such as a workload identity token), cert (the TLS client certificate of the keyStore), aws-iam (the AWS credentials of the application), azure-msi (the Azure managed identity) or gcp-gce (the service account of the Google Compute Engine instance). The token is renewed before it expires, and the endpoint logs in again when the token can no longer be renewed. Enum values:
| token | String | |
The path the login method is mounted at in HashiCorp Vault (without auth/), when it is not the default path of the login method: approle, kubernetes, jwt, cert, aws (for aws-iam), azure (for azure-msi) or gcp (for gcp-gce). | String | ||
The value of the X-Vault-AWS-IAM-Server-ID header of the aws-iam login, when the AWS login of HashiCorp Vault requires one (iam_server_id_header_value). | String | ||
The AWS region the aws-iam login signs its request for, which must be the region of awsStsEndpoint. When not set: us-east-1 with the global endpoint, else the region of the AWS SDK default region chain (such as the AWS_REGION environment variable). | String | ||
The AWS STS endpoint of the aws-iam login, which the AWS login of HashiCorp Vault must accept (its sts_endpoint). When not set, the global endpoint https://sts.amazonaws.com/. | String | ||
The client ID of a user-assigned managed identity, for the azure-msi login. When not set, the system-assigned managed identity. | String | ||
The resource (audience) of the managed identity token of the azure-msi login, which must be the resource of the Azure login of HashiCorp Vault. | String | ||
The JWT to log in with, for the jwt and kubernetes logins. For a JWT that is replaced over time, such as a projected service account token, use jwtFile instead. | String | ||
The file to read the JWT from at every login, for the jwt and kubernetes logins. The kubernetes login reads /var/run/secrets/kubernetes.io/serviceaccount/token when neither jwt nor jwtFile is set. | String | ||
The file with the TLS client certificate and its private key, which the endpoint presents to HashiCorp Vault: for the cert login, or for a Vault that requires client certificates. A PKCS12 or JKS key store, or PEM with the certificate and its unencrypted private key, in PKCS#8 or, for an RSA key, in PKCS#1. | String | ||
The password of the key store and of its private key, when it is a PKCS12 or JKS key store. | String | ||
The HashiCorp Vault role to log in as, for the kubernetes, jwt, cert, aws-iam, azure-msi and gcp-gce logins. The jwt login can leave it out when the login method has a default role, the aws-iam login to log in as the role named as the IAM principal, and the cert login to let Vault pick the certificate role that trusts the certificate. | String | ||
The role ID of the AppRole, for the approle login. | String | ||
The secret ID of the AppRole, for the approle login. | String | ||
Token to be used, for the token login. | String | ||
The file with the certificates to trust for the TLS connection to HashiCorp Vault, such as the CA certificate of a private CA: a PKCS12 or JKS trust store, or PEM certificates. When not set, the JVM trusts the certificate as for any other TLS connection. | String | ||
The password of the trust store, when it is a PKCS12 or JKS trust store. | String |
Message Headers
The HashiCorp Vault component supports the following message header(s), which is/are listed below:
| Name | Description | Default | Type |
|---|---|---|---|
CamelHashicorpVaultProducerOperation (producer) Constant: | Overrides the desired operation to be used in the producer. | String | |
CamelHashicorpVaultSecretPath (producer) Constant: | Set the desired secret path as header. | String | |
CamelHashicorpVaultSecretVersion (producer) Constant: | Set the desired secret version as header. | String |
Message Body
The message body at each side of the HashiCorp Vault component: Consumer is the body of the messages the consumer creates, Consumer reply the bodies a consumer that answers its caller can reply with, Producer input the bodies the producer can send or write, and Producer output the body after the producer.
| Side | Body | Description |
|---|---|---|
Producer input |
| The fields of the secret for createSecret, the default operation: a Map of the keys and values, a POJO written as JSON, or text, bytes, a stream or a file with a JSON object. A List or no body fails. The other operations take the secret from the secretPath option or the CamelHashicorpVaultSecretPath header.
|
Producer output |
| getSecret gives a Map with the fields of the secret under data and its version under metadata, or no body when the secret does not exist or is deleted. listSecrets gives a List of the names of the secrets in the engine; createSecret and deleteSecret leave the body unchanged.
|
The bodies are the ones with the default options. A side that is not listed is not known, and tooling does not guess it. The carriers, the sides and the tools that use them are described in Message Body Types.
Authentication and HashiCorp Vault on-premise vs HashiCorp Cloud
The component supports operations at the producer level. Specifically, it provides the following functionalities:
-
createSecret -
getSecret -
deleteSecret -
listSecrets
The component can interact with HashiCorp Vault, which may be deployed either as an on-premise/local instance or as a HashiCorp Vault Cloud instance (Enterprise version).
Configuration for HashiCorp Vault Cloud
When using a HashiCorp Vault Cloud instance, in addition to the standard parameters such as host, port, scheme, and token, you must configure the following additional parameters:
-
cloud:: A boolean flag that must be explicitly set totrueto indicate that the HashiCorp Vault instance is hosted in the cloud. -
namespace:: The namespace of your secrets engine. The operations work on<namespace>/<secretsEngine>; without a namespace they work on the secrets engine itself, as on-premise.
Logging in to HashiCorp Vault
The authentication option chooses how the component logs in to HashiCorp Vault:
| Login | Settings |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
When the login method is not mounted at its default path in Vault (approle, kubernetes, jwt, cert, aws for aws-iam, azure for azure-msi or gcp for gcp-gce), set the path with authMountPath, without auth/.
Camel renews the token before it expires, and logs in again when the token can no longer be renewed, such as when it reached its maximum time to live. A JWT read from a file is read again at every login, so a token that the platform replaces in the file, such as a projected service account token, is picked up. When the endpoint stops, Camel revokes the token of its login. A token given for the token login is renewed when it is renewable, but never revoked.
On HashiCorp Cloud (cloud=true and a namespace), the login is made in the namespace, as are the renewal and the revocation of the token.
For example, an endpoint that logs in with an AppRole, whose role ID and secret ID are in environment variables:
from("direct:start")
.to("hashicorp-vault:secret?operation=getSecret&host=vault.example.com&authentication=approle"
+ "&roleId={{env:VAULT_ROLE_ID}}&secretId=RAW({{env:VAULT_SECRET_ID}})")
.log("${body}"); TLS
With the https scheme, the component trusts the certificate of Vault as any other TLS connection of the JVM does. For a Vault whose certificate is signed by a private CA, set trustStore to the certificates to trust: a PKCS12 or JKS trust store with its trustStorePassword, or a PEM file with the CA certificates, such as the file that the VAULT_CACERT environment variable of the Vault CLI points to.
To present a client certificate to Vault, for the cert login or for a Vault that requires client certificates, set keyStore: a PKCS12 or JKS key store with its keyStorePassword, or a PEM file with the certificate and its private key. The key of a PEM file is not encrypted, in PKCS#8 (-----BEGIN PRIVATE KEY-----) or, for an RSA key, in PKCS#1 (-----BEGIN RSA PRIVATE KEY-----).
The trust store and the key store are read when the client is created, when the endpoint, the properties function or the secret refresh task starts. A certificate replaced in its file, such as a client certificate renewed before it expires, is used after a restart.
For example, the properties function logs in with a client certificate:
camel.vault.hashicorp.host = vault.example.com
camel.vault.hashicorp.port = 8200
camel.vault.hashicorp.scheme = https
camel.vault.hashicorp.trustStore = /etc/vault/ca.pem
camel.vault.hashicorp.authentication = cert
camel.vault.hashicorp.role = my-application
camel.vault.hashicorp.keyStore = /etc/vault/client.pem Cloud logins
The aws-iam login signs a request to AWS STS with the credentials of the AWS SDK default chain: the environment variables, the system properties, a web identity token (such as IAM roles for service accounts on Amazon EKS), the profile, the container credentials (Amazon ECS) or the instance profile (Amazon EC2). Vault checks the request with STS. The login needs the AWS SDK: add the software.amazon.awssdk:auth dependency, in the version of the AWS SDK of the Camel AWS components. A web identity token also needs the software.amazon.awssdk:sts dependency, and an SSO profile the software.amazon.awssdk:sso and software.amazon.awssdk:ssooidc dependencies. Without the sts module, the AWS SDK skips the web identity token and takes the next credentials of the chain, such as those of the instance: so Camel refuses to start the login when a web identity token is set (AWS_WEB_IDENTITY_TOKEN_FILE) and the sts module is missing.
The request is sent to awsStsEndpoint, by default the global endpoint https://sts.amazonaws.com/, which takes requests signed for us-east-1. Without awsStsEndpoint and awsRegion, the request is signed for us-east-1, whatever the region of the application, as vault login -method=aws does. To use a regional endpoint, set awsStsEndpoint and its region in awsRegion, as the AWS login of Vault is configured (sts_endpoint and sts_region); without awsRegion, the request is then signed for the region of the AWS SDK default region chain, such as the AWS_REGION environment variable. awsIamServerId sets the X-Vault-AWS-IAM-Server-ID header, when the AWS login of Vault requires one.
For example, the properties function logs in with the AWS credentials of the application, through the STS endpoint of its region:
camel.vault.hashicorp.host = vault.example.com
camel.vault.hashicorp.port = 8200
camel.vault.hashicorp.scheme = https
camel.vault.hashicorp.authentication = aws-iam
camel.vault.hashicorp.role = my-application
camel.vault.hashicorp.awsStsEndpoint = https://sts.eu-west-1.amazonaws.com
camel.vault.hashicorp.awsRegion = eu-west-1 The azure-msi login asks the Azure instance metadata service (169.254.169.254) for a token of the managed identity of the virtual machine, for the resource azureResource, by default https://management.azure.com/, which must be the resource of the Azure login of Vault (auth/azure/config). azureClientId chooses a user-assigned managed identity, instead of the system-assigned one. The instance metadata service is on Azure virtual machines and scale sets. A pod of Azure Kubernetes Service with workload identity has a token in the file named by the AZURE_FEDERATED_TOKEN_FILE environment variable instead, for the jwt login with jwtFile.
The gcp-gce login asks the metadata server of a Google Compute Engine instance for an identity token of the default service account of the instance, for a role of type gce of the GCP login of Vault.
The metadata services are reached directly, never through an HTTP proxy.
OpenBao
The component, the properties function and the secret refresh task also work with OpenBao, the open source fork of HashiCorp Vault, which keeps the API of Vault. OpenBao has the token, AppRole, Kubernetes, JWT and TLS certificate logins built in.
Examples
Using the producer operations
The producer works on a secrets engine, given as the path of the endpoint (secret is the default key/value engine), and connects with the host, port, scheme and token endpoint options. The operation option selects what to do, and secretPath names the secret to work on; the CamelHashicorpVaultProducerOperation and CamelHashicorpVaultSecretPath headers override them per message.
Creating a secret
The message body is the map of key/value pairs to store as the secret. A POJO is written as JSON, and text, bytes, a stream or a file must hold a JSON object of the fields. A message without a body fails, instead of writing a new, empty version of the secret:
-
Java
-
XML
-
YAML
from("direct:createSecret")
.to("hashicorp-vault:secret?operation=createSecret&secretPath=database&host=localhost&port=8200&scheme=http&token={{vault.token}}"); <camelContext>
<route>
<from uri="direct:createSecret"/>
<to uri="hashicorp-vault:secret?operation=createSecret&secretPath=database&host=localhost&port=8200&scheme=http&token={{vault.token}}"/>
</route>
</camelContext> - route:
from:
uri: direct:createSecret
steps:
- to:
uri: hashicorp-vault:secret?operation=createSecret&secretPath=database&host=localhost&port=8200&scheme=http&token={{vault.token}} Getting a secret
The message body becomes a Map with the fields of the secret under data and its version details under metadata, or no body when the secret does not exist; the CamelHashicorpVaultSecretVersion header reads a given version (a number) instead of the latest:
-
Java
-
XML
-
YAML
from("direct:getSecret")
.to("hashicorp-vault:secret?operation=getSecret&secretPath=database&host=localhost&port=8200&scheme=http&token={{vault.token}}")
.log("The database secret is ${body}"); <camelContext>
<route>
<from uri="direct:getSecret"/>
<to uri="hashicorp-vault:secret?operation=getSecret&secretPath=database&host=localhost&port=8200&scheme=http&token={{vault.token}}"/>
<log message="The database secret is ${body}"/>
</route>
</camelContext> - route:
from:
uri: direct:getSecret
steps:
- to:
uri: hashicorp-vault:secret?operation=getSecret&secretPath=database&host=localhost&port=8200&scheme=http&token={{vault.token}}
- log:
message: "The database secret is ${body}" The secret can also be chosen per message with the CamelHashicorpVaultSecretPath header:
-
Java
-
XML
-
YAML
from("direct:getSecret")
.setHeader(HashicorpVaultConstants.SECRET_PATH, constant("database"))
.to("hashicorp-vault:secret?operation=getSecret&host=localhost&port=8200&scheme=http&token={{vault.token}}"); <camelContext>
<route>
<from uri="direct:getSecret"/>
<setHeader name="CamelHashicorpVaultSecretPath">
<constant>database</constant>
</setHeader>
<to uri="hashicorp-vault:secret?operation=getSecret&host=localhost&port=8200&scheme=http&token={{vault.token}}"/>
</route>
</camelContext> - route:
from:
uri: direct:getSecret
steps:
- setHeader:
name: CamelHashicorpVaultSecretPath
expression:
constant:
expression: database
- to:
uri: hashicorp-vault:secret?operation=getSecret&host=localhost&port=8200&scheme=http&token={{vault.token}} Deleting a secret
-
Java
-
XML
-
YAML
from("direct:deleteSecret")
.to("hashicorp-vault:secret?operation=deleteSecret&secretPath=database&host=localhost&port=8200&scheme=http&token={{vault.token}}"); <camelContext>
<route>
<from uri="direct:deleteSecret"/>
<to uri="hashicorp-vault:secret?operation=deleteSecret&secretPath=database&host=localhost&port=8200&scheme=http&token={{vault.token}}"/>
</route>
</camelContext> - route:
from:
uri: direct:deleteSecret
steps:
- to:
uri: hashicorp-vault:secret?operation=deleteSecret&secretPath=database&host=localhost&port=8200&scheme=http&token={{vault.token}} Listing the secrets
The message body becomes the list of the names of the secrets in the engine:
-
Java
-
XML
-
YAML
from("direct:listSecrets")
.to("hashicorp-vault:secret?operation=listSecrets&host=localhost&port=8200&scheme=http&token={{vault.token}}")
.log("The secrets are ${body}"); <camelContext>
<route>
<from uri="direct:listSecrets"/>
<to uri="hashicorp-vault:secret?operation=listSecrets&host=localhost&port=8200&scheme=http&token={{vault.token}}"/>
<log message="The secrets are ${body}"/>
</route>
</camelContext> - route:
from:
uri: direct:listSecrets
steps:
- to:
uri: hashicorp-vault:secret?operation=listSecrets&host=localhost&port=8200&scheme=http&token={{vault.token}}
- log:
message: "The secrets are ${body}" Using HashiCorp Vault Property Function
To use this function, you’ll need to provide credentials for HashiCorp Vault as environment variables:
export CAMEL_VAULT_HASHICORP_TOKEN=token
export CAMEL_VAULT_HASHICORP_HOST=host
export CAMEL_VAULT_HASHICORP_PORT=port
export CAMEL_VAULT_HASHICORP_SCHEME=http/https You can also configure the credentials in the application.properties file such as:
camel.vault.hashicorp.token = token
camel.vault.hashicorp.host = host
camel.vault.hashicorp.port = port
camel.vault.hashicorp.scheme = scheme In case the running HashiCorp Vault instance you’re pointing is running on HashiCorp Cloud, the configuration will require two additional parameters:
export CAMEL_VAULT_HASHICORP_TOKEN=token
export CAMEL_VAULT_HASHICORP_HOST=host
export CAMEL_VAULT_HASHICORP_PORT=port
export CAMEL_VAULT_HASHICORP_SCHEME=http/https
export CAMEL_HASHICORP_VAULT_CLOUD=true
export CAMEL_HASHICORP_VAULT_NAMESPACE=namespace You can also set the same in the application.properties file such as:
camel.vault.hashicorp.token = token
camel.vault.hashicorp.host = host
camel.vault.hashicorp.port = port
camel.vault.hashicorp.scheme = scheme
camel.vault.hashicorp.cloud = true
camel.vault.hashicorp.namespace = namespace This will make the Properties function works even in the HashiCorp Cloud deployment option.
The properties function logs in with a token by default. To log in another way, set the options of Logging in to HashiCorp Vault with the camel.vault.hashicorp prefix. For example, an application in a Kubernetes pod logs in with the service account of the pod:
camel.vault.hashicorp.host = vault.example.com
camel.vault.hashicorp.port = 8200
camel.vault.hashicorp.scheme = https
camel.vault.hashicorp.authentication = kubernetes
camel.vault.hashicorp.role = my-application The properties function also reads these options from the environment variables CAMEL_HASHICORP_VAULT_AUTHENTICATION, CAMEL_HASHICORP_VAULT_ROLE_ID, CAMEL_HASHICORP_VAULT_SECRET_ID, CAMEL_HASHICORP_VAULT_ROLE, CAMEL_HASHICORP_VAULT_JWT, CAMEL_HASHICORP_VAULT_JWT_FILE and CAMEL_HASHICORP_VAULT_AUTH_MOUNT_PATH, and the settings of TLS from CAMEL_HASHICORP_VAULT_TRUST_STORE, CAMEL_HASHICORP_VAULT_TRUST_STORE_PASSWORD, CAMEL_HASHICORP_VAULT_KEY_STORE and CAMEL_HASHICORP_VAULT_KEY_STORE_PASSWORD, and the settings of the Cloud logins from CAMEL_HASHICORP_VAULT_AWS_IAM_SERVER_ID, CAMEL_HASHICORP_VAULT_AWS_REGION, CAMEL_HASHICORP_VAULT_AWS_STS_ENDPOINT, CAMEL_HASHICORP_VAULT_AZURE_RESOURCE and CAMEL_HASHICORP_VAULT_AZURE_CLIENT_ID. When any of the CAMEL_HASHICORP_VAULT_* environment variables is set, the properties function takes all its settings from the environment variables, and none from the camel.vault.hashicorp configuration. The secret refresh task logs in the same way as the properties function.
| if you’re running the application on a Kubernetes based cloud platform, you can initialize the environment variables from a Secret or Configmap to enhance security. You can also enhance security by setting a Secret property placeholder which will be initialized at application runtime only. |
camel.vault.hashicorp configuration only applies to the HashiCorp Vault properties function (E.g when resolving properties). When using the operation option to create, get, list secrets etc., you should provide the host, port, scheme (if required) and the options of the login, such as token. |
At this point, you’ll be able to reference a property in the following way:
-
Java
-
XML
-
YAML
from("direct:start")
.to("{{hashicorp:secret:route}}"); <camelContext>
<route>
<from uri="direct:start"/>
<to uri="{{hashicorp:secret:route}}"/>
</route>
</camelContext> - route:
from:
uri: direct:start
steps:
- to:
uri: "{{hashicorp:secret:route}}" Where route will be the name of the secret stored in the HashiCorp Vault instance, in the 'secret' engine.
The engine is the part before the first :; for a property with a field, only the part before the # counts. When the property has no engine, such as {{hashicorp:route}} or {{hashicorp:database#username}}, the secret is read from the 'secret' engine. Without a field, a default value needs the engine, as in {{hashicorp:secret:route:default}}: in {{hashicorp:route:default}} the engine would be route. With a field it does not, as in {{hashicorp:database#username:admin}}. |
You could specify a default value in case the secret is not present on HashiCorp Vault instance:
-
Java
-
XML
-
YAML
from("direct:start")
.to("{{hashicorp:secret:route:default}}"); <camelContext>
<route>
<from uri="direct:start"/>
<to uri="{{hashicorp:secret:route:default}}"/>
</route>
</camelContext> - route:
from:
uri: direct:start
steps:
- to:
uri: "{{hashicorp:secret:route:default}}" In this case, if the secret doesn’t exist in the 'secret' engine, the property will fall back to "default" as value.
Also, you are able to get a particular field of the secret, if you have, for example, a secret named database of this form:
{
"username": "admin",
"password": "password123",
"engine": "postgres",
"host": "127.0.0.1",
"port": "3128",
"dbname": "db"
} You’re able to do get single secret value in your route, in the 'secret' engine, like for example:
-
Java
-
XML
-
YAML
from("direct:start")
.log("Username is {{hashicorp:secret:database#username}}"); <camelContext>
<route>
<from uri="direct:start"/>
<log message="Username is {{hashicorp:secret:database#username}}"/>
</route>
</camelContext> - route:
from:
uri: direct:start
steps:
- log:
message: "Username is {{hashicorp:secret:database#username}}" Or re-use the property as part of an endpoint.
You could specify a default value in case the particular field of secret is not present on HashiCorp Vault instance, in the 'secret' engine:
-
Java
-
XML
-
YAML
from("direct:start")
.log("Username is {{hashicorp:secret:database#username:admin}}"); <camelContext>
<route>
<from uri="direct:start"/>
<log message="Username is {{hashicorp:secret:database#username:admin}}"/>
</route>
</camelContext> - route:
from:
uri: direct:start
steps:
- log:
message: "Username is {{hashicorp:secret:database#username:admin}}" In this case, if the secret doesn’t exist or the secret exists (in the 'secret' engine) but the username field is not part of the secret, the property will fall back to "admin" as value.
There is also the syntax to get a particular version of the secret for both the approach, with field/default value specified or only with secret:
-
Java
-
XML
-
YAML
from("direct:start")
.to("{{hashicorp:secret:route@2}}"); <camelContext>
<route>
<from uri="direct:start"/>
<to uri="{{hashicorp:secret:route@2}}"/>
</route>
</camelContext> - route:
from:
uri: direct:start
steps:
- to:
uri: "{{hashicorp:secret:route@2}}" This approach will return the RAW route secret with version '2', in the 'secret' engine.
-
Java
-
XML
-
YAML
from("direct:start")
.to("{{hashicorp:secret:route:default@2}}"); <camelContext>
<route>
<from uri="direct:start"/>
<to uri="{{hashicorp:secret:route:default@2}}"/>
</route>
</camelContext> - route:
from:
uri: direct:start
steps:
- to:
uri: "{{hashicorp:secret:route:default@2}}" This approach will return the route secret value with version '2' or default value in case the secret doesn’t exist or the version doesn’t exist (in the 'secret' engine).
-
Java
-
XML
-
YAML
from("direct:start")
.log("Username is {{hashicorp:secret:database#username:admin@2}}"); <camelContext>
<route>
<from uri="direct:start"/>
<log message="Username is {{hashicorp:secret:database#username:admin@2}}"/>
</route>
</camelContext> - route:
from:
uri: direct:start
steps:
- log:
message: "Username is {{hashicorp:secret:database#username:admin@2}}" This approach will return the username field of the database secret with version '2' or admin in case the secret doesn’t exist or the version doesn’t exist (in the 'secret' engine).
The only requirement is adding the camel-hashicorp-vault jar to your Camel application.
Automatic Camel context reloading on Secret Refresh
Being able to reload Camel context on a Secret Refresh could be done by specifying the usual credentials (the same used for HashiCorp Vault Property Function).
With Environment variables:
export CAMEL_HASHICORP_VAULT_TOKEN=token
export CAMEL_HASHICORP_VAULT_HOST=host
export CAMEL_HASHICORP_VAULT_PORT=port
export CAMEL_HASHICORP_VAULT_SCHEME=http/https or as plain Camel main properties:
camel.vault.hashicorp.token = token
camel.vault.hashicorp.host = host
camel.vault.hashicorp.port = port
camel.vault.hashicorp.scheme = scheme To enable the automatic refresh, you’ll need additional properties to set:
camel.vault.hashicorp.refreshEnabled=true
camel.vault.hashicorp.refreshPeriod=60000
camel.vault.hashicorp.secrets=database,api-keys
camel.main.context-reload-enabled = true where camel.vault.hashicorp.refreshEnabled will enable the automatic context reload, camel.vault.hashicorp.refreshPeriod is the interval of time between two different checks for update events (default 60000ms), and camel.vault.hashicorp.secrets is a comma-separated list of secret names (or patterns) to check for updates.
The secret names should use the following format: - mysecret or path/to/secret - tracks a secret in the default secret engine - myengine:mysecret or myengine:path/to/secret - tracks a secret in the myengine engine (use : to separate engine from path) - You can use patterns like database* to match multiple secrets
Note that camel.vault.hashicorp.secrets is not mandatory: if not specified the task responsible for checking updates events will take into account all the properties with a hashicorp: prefix. Such a secret is tracked in the engine of the property, in the format above (hashicorp:myengine:mysecret is tracked as myengine:mysecret, hashicorp:secret:mysecret as mysecret), and its first check compares the version in Vault with the version that was resolved, so an update made before the first check also triggers a reload.
How the Refresh Mechanism Works
Unlike cloud-based secret managers (AWS, GCP, Azure) that provide event-driven notifications, HashiCorp Vault does not have a native event notification system for secret changes. Therefore, this implementation uses a polling-based approach:
-
Metadata Polling: The refresh task periodically queries the HashiCorp Vault metadata endpoint (
/v1/<engine>/metadata/<secret>) for each tracked secret. -
Version Tracking: It compares the
current_versionfield to detect changes. -
Change Detection: When a version change is detected, it triggers a Camel context reload.
-
Efficiency: Only metadata is queried (not the full secret content), making this approach lightweight.
For example, if a secret named database is updated in Vault: - The metadata endpoint returns "current_version": 5 - On the next check, if current_version changes to 6, a reload is triggered - The refresh period (default 60 seconds) determines how quickly changes are detected
This polling approach works with all HashiCorp Vault deployments (on-premise, cloud, enterprise, and open-source) without requiring additional infrastructure.
Using HashiCorp Vault Property Function in Spring Boot for Early resolving properties
HashiCorp Vault Spring Boot component starter offers the ability to early resolve properties, so the end user could resolve properties directly in the application.properties before both Spring Boot runtime and Camel context will start.
This could be accomplished in the following way. You should specified this property in your application.properties file:
camel.component.hashicorp-vault.early-resolve-properties=true This will enable the feature so you’ll be able to resolved properties, in your application.properties file, like:
foo = hashicorp:secret:database/password#string