1.15.4.1 The Vault credential manager
Concourse can be configured to pull credentials from a Vault instance.
To configure this, first configure the URL of your Vault server by setting the following env on the web node:
CONCOURSE_VAULT_URL=https://vault.example.com:8200You may also need to configure the CA cert for Vault:
CONCOURSE_VAULT_CA_CERT=path/to/ca.crtYou'll also need to configure how the web node authenticates with Vault - see Authenticating with Vault for more details as that step is quite involved.
Credential lookup rules
Vault lets you organize secrets into hierarchies, which is useful for when they should be accessible for particular pipelines or teams. When you have a parameter like ((foo)) in a pipeline definition, Concourse will (by default) look for it in the following paths, in order:
/concourse/TEAM_NAME/PIPELINE_NAME/foo/concourse/TEAM_NAME/foo
Vault credentials are actually key-value, so for ((foo)) Concourse will default to the field name value. You can specify the field to grab via . syntax, e.g. ((foo.bar)).
If you have multiple, intermediate levels in your path, you can use the / separator to reach your intended field, e.g. ((foo/bar/baz.qux)).
When executing a one-off task, there is no pipeline: so in this case, only the team path /concourse/TEAM_NAME/foo is searched.
There are several ways to customize the lookup logic:
Add a "shared path", for secrets common to all teams.
Change the team- and pipeline-dependent path templates.
Change the path prefix from
/concourseto something else.Set a Vault namespace for isolation within a Vault Enterprise installation.
Each of these can be controlled by Concourse command line flags, or environment variables.
Changing the path templates
You can choose your own list of templates, which will expand to team- or pipeline-specific paths. These are subject to the path prefix. By default, the templates used are:
CONCOURSE_VAULT_LOOKUP_TEMPLATES=/{{.Team}}/{{.Pipeline}}/{{.Secret}},/{{.Team}}/{{.Secret}}When secrets are to be looked up, these are evaluated subject to the configured path prefix, where {{.Team}} expands to the current team, {{.Pipeline}} to the current pipeline (if any), and {{.Secret}} to the name of the secret. So if the settings are:
CONCOURSE_VAULT_PATH_PREFIX=/secrets
CONCOURSE_VAULT_LOOKUP_TEMPLATES=/{{.Team}}/concourse/{{.Pipeline}}/{{.Secret}},/{{.Team}}/concourse/{{.Secret}},/common/{{.Secret}}and ((password)) is used in team myteam and pipeline mypipeline, Concourse will look for the following, in order:
/secrets/myteam/concourse/mypipeline/password/secrets/myteam/concourse/password/secrets/common/password
Configuring the secrets engine
Concourse is currently limited to looking under a single path, meaning enabling only one secrets engine is supported: kv, or kv_v2. This may change in the future - we're still collecting ideas in RFC #21.
Using kv version 2 enables versioned secrets and the ability to restore previous versions or deleted secrets. Concourse will read the latest version of a secret at all times and if it is deleted it will appear as if the secret does not exist. More information regarding the Vault KV backend and the differences in versions can be found here.
So, let's configure the kv secrets engine and mount it at /concourse:
$ vault secrets enable -version=1 -path=concourse kvTo enable kv_v2 and versioned secrets:
$ vault secrets enable -version=2 -path=concourse kvNext, you'll want to create a policy to allow Concourse to read from this path.
path "concourse/*" {
capabilities = ["read"]
}Save this to concourse-policy.hcl, and then run:
vault policy write concourse ./concourse-policy.hclThis configuration will allow Concourse to read all credentials under /concourse. This should match your configured path prefix.
Authenticating with Vault
There are many ways to authenticate with a Vault server. The web-node can be configured with either a token or an arbitrary auth backend and arbitrary auth params, so just about all of them should be configurable.
When the web node acquires a token, either by logging in with an auth backend or by being given one directly, it will continuously renew the token to ensure it doesn't expire. The renewal interval is half of the token's lease duration.
Using a periodic token
The simplest way to authenticate is by generating a periodic token:
$ vault token create --policy concourse --period 1h
Key Value
--- -----
token s.mSNnbhGAqxK2ZbMasOQ91rIA
token_accessor 0qsib5YcYvROm86cT08IFxIT
token_duration 1h
token_renewable true
token_policies [concourse default]Choose your --period wisely, as the timer starts counting down as soon as the token is created. You should also use a duration long enough to account for any planned web node downtime.
Once you have the token, just set the following env on the web node:
CONCOURSE_VAULT_CLIENT_TOKEN=s.mSNnbhGAqxK2ZbMasOQ91rIAPeriodic tokens are the quickest way to get started, but they have one fatal flaw: if the web node is down for longer than the token's configured period, the token will expire and a new one will have to be created and configured. This can be avoided by using the approle auth backend.
Using the approle auth backend
The approle backend allows for an app (in this case, Concourse) to authenticate with a role pre-configured in Vault.
With this backend, the web node is configured with a role_id corresponding to a pre-configured role, and a secret_id which is used to authenticate and acquire a token.
The approle backend must first be configured in Vault. Vault's approle backend allows for a few parameters which you may want to set to determine the permissions and lifecycle of its issued tokens:
policies=namesThis determines the policies (comma-separated) to set on each token. Be sure to set one that has access to the secrets path - see Configuring the secrets engine for more information.
token_ttl=durationThis determines the TTL for each token granted. The token can be continuously renewed, as long as it is renewed before the TTL elapses.
token_max_ttl=durationThis sets a maximum lifetime for each token, after which the token can no longer be renewed.
If configured, be sure to set the same value on the
webnode so that it can re-auth before this duration is reached:CONCOURSE_VAULT_AUTH_BACKEND_MAX_TTL=1hperiod=durationIf configured, tokens issued will be periodic. Periodic tokens are not bound by any configured max TTL, and can be renewed continuously. It does not make sense to configure both
periodandtoken_max_ttlas the max TTL will be ignored.token_num_uses=countThis sets a limit on how often a token can be used. We do not recommend setting this value, as it will effectively hamstring Concourse after a few credential acquisitions. The
webnode does not currently know to re-acquire a token when this limit is reached.secret_id_ttl=durationandsecret_id_num_uses=countThese two configurations will result in the secret ID expiring after the configured time or configured number of log-ins, respectively.
You should only set these if you have something periodically re-generating secret IDs and re-configuring your
webnodes accordingly.
Given all that, a typical configuration may look something like this:
$ vault auth enable approle
Success! Enabled approle auth method at: approle/
$ vault write auth/approle/role/concourse policies=concourse period=1h
Success! Data written to: auth/approle/role/concourseNow that the backend is configured, we'll need to obtain the role_id and generate a secret_id:
$ vault read auth/approle/role/concourse/role-id
Key Value
--- -----
role_id 5f3420cd-3c66-2eff-8bcc-0e8e258a7d18
$ vault write -f auth/approle/role/concourse/secret-id
Key Value
--- -----
secret_id f7ec2ac8-ad07-026a-3e1c-4c9781423155
secret_id_accessor 1bd17fc6-dae1-0c82-d325-3b8f9b5654eeThese should then be set on the web node like so:
CONCOURSE_VAULT_AUTH_BACKEND="approle"
CONCOURSE_VAULT_AUTH_PARAM="role_id:5f3420cd-3c66-2eff-8bcc-0e8e258a7d18,secret_id:f7ec2ac8-ad07-026a-3e1c-4c9781423155"
Using the cert auth backend
The cert auth method allows authentication using SSL/TLS client certificates.
With this backend, the web node is configured with a client cert and a client key. Vault must be configured with TLS, which you should be almost certainly be doing anyway.
The cert backend must first be configured in Vault. The backend is associated to a policy and a CA cert used to verify the client certificate. It may also be given the client certificate itself.
The cert backend must first be configured in Vault. Vault's cert backend allows for a few parameters which you may want to set to determine the lifecycle of its issued tokens:
policies=namesThis determines the policies (comma-separated) to set on each token. Be sure to set one that has access to the secrets path - see Configuring the secrets engine for more information.
ttl=durationThis determines the TTL for each token granted. The token can be continuously renewed, as long as it is renewed before the TTL elapses.
max_ttl=durationThis sets a maximum lifetime for each token, after which the token can no longer be renewed.
If configured, be sure to set the same value on the
webnode so that it can re-auth before this duration is reached:CONCOURSE_VAULT_AUTH_BACKEND_MAX_TTL=1hperiod=durationIf configured, tokens issued will be periodic. Periodic tokens are not bound by any configured max TTL, and can be renewed continuously. It does not make sense to configure both
periodandmax_ttlas the max TTL will be ignored.
$ vault auth enable cert
Success! Enabled cert auth method at: cert/
$ vault write auth/cert/certs/concourse policies=concourse certificate=@out/vault-ca.crt ttl=1h
Success! Data written to: auth/cert/certs/concourseOnce that's all set up, you'll just need to configure the client cert and key on the web node like so:
CONCOURSE_VAULT_AUTH_BACKEND="cert"
CONCOURSE_VAULT_CLIENT_CERT=vault-certs/concourse.crt
CONCOURSE_VAULT_CLIENT_KEY=vault-certs/concourse.keyIn this case no additional auth params are necessary, as the Vault's TLS auth backend will check the certificate against all roles if no name is specified.