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:8200
You may also need to configure the CA cert for Vault:
CONCOURSE_VAULT_CA_CERT=path/to/ca.crt
You'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
/concourse
to 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 kv
To enable kv_v2 and versioned secrets:
$ vault secrets enable -version=2 -path=concourse kv
Next, 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.hcl
This 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.mSNnbhGAqxK2ZbMasOQ91rIA
Periodic 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=names
This 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=duration
This 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=duration
This 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
web
node so that it can re-auth before this duration is reached:CONCOURSE_VAULT_AUTH_BACKEND_MAX_TTL=1h
period=duration
If 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
period
andtoken_max_ttl
as the max TTL will be ignored.token_num_uses=count
This 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
web
node does not currently know to re-acquire a token when this limit is reached.secret_id_ttl=duration
andsecret_id_num_uses=count
These 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
web
nodes 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/concourse
Now 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-3b8f9b5654ee
These 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=names
This 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=duration
This 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=duration
This 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
web
node so that it can re-auth before this duration is reached:CONCOURSE_VAULT_AUTH_BACKEND_MAX_TTL=1h
period=duration
If 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
period
andmax_ttl
as 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/concourse
Once 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.key
In 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.