Secrets Handling
Using a Secrets Store to Store Credentials
Secrets stores, such as Credhub, can be used to store secure properties that you don't want committed into a config file. Within your pipeline, the config file can then reference that secrets store value for runtime evaluation.
Platform Automation Toolkit Tasks contains two tasks to help with retrieving these credentials in the tasks that use them:
- If you're using Concourse version 5 or newer
the
prepare-tasks-with-secretstask can be used with any Concourse supported secrets store. - The
credhub-interpolatetask can only be used with Credhub.
Using prepare-tasks-with-secrets
The prepare-tasks-with-secrets task takes a set of tasks
and modifies them to include environment variables referencing the variables found in the provided config files.
This allows use of the native Concourse secrets handling
and provides support for any secret store Concourse supports.
The prepare-tasks-with-secrets task
replaces the credhub-interpolate task on Concourse versions 5.x+
and provides the following benefits:
- Support for all native Concourse secrets stores including Credhub and Vault.
- Credhub credentials are no longer required by the task so they can be completely handled by concourse.
- Secrets are no longer written to disk which alleviates some security concerns.
The prepare-tasks-with-secrets task can be used two ways:
- Adding to a pipeline without an already implemented credhub-interpolate task
- Replacing an already implemented credhub-interpolate task
All Variables Must Exist
If using prepare-tasks-with-secrets, all secrets must exist in either a secrets store
or a vars file found under VARS_PATHS.
If a vars from a config file can't be found in credhub,
it must be available in a yaml file found under VARS_PATHS in prepare-tasks-with-secrets.
This will prevent those credentials from being added as environment variables to the task
resulting in Concourse being unable to find them in the secrets store.
To understand how prepare-tasks-with-secrets modifies the Platform Automation Toolkit tasks,
below is an example of how a task will be changed:
- Authenticate with your credhub instance.
- Generate a username and password:
1credhub generate --name="/concourse/:team_name/:pipeline_name/vcenter_login" --type=user --username=some-user -
Create a director configuration file that references the properties using the om interpolation syntax:
1 2 3 4 5
properties-configuration: iaas_configuration: vcenter_host: ((vcenter_host)) vcenter_username: ((vcenter_login.username)) vcenter_password: ((vcenter_login.password)) -
(Optional) Create vars files with additional variables not stored in the secrets store.
We recommend this only for non-secret variables. It's more secure to store secrets in the secrets store. If using multiple foundations, there are some cases where a foundation-specific key might not be sensitive, but should be extracted to allow reuse of the config file between foundations. If using a single config file for multiple foundations, vars files may be used instead of storing those variables in a secrets store.
For example:
1vcenter_host: vcenter.example.com -
Configure your pipeline to use the
prepare-tasks-with-secretstask.- The
configinput is required and is a directory that contains your configuration file from (3). - The
tasksinput is required and is the set of tasks that will be modified. - The
varsinput andVARS_PATHSparam are only required if vars files are being used in subsequent tasks - The
output_mappingsection is required and is where the modified tasks will be.
The declaration within a pipeline might look like:
1 2 3 4 5 6 7 8 9 10 11
- task: prepare-tasks-with-secrets file: platform-automation-tasks/tasks/prepare-tasks-with-secrets.yml input_mapping: tasks: platform-automation-tasks config: deployments vars: deployments # required only if using vars output_mapping: tasks: platform-automation-tasks params: CONFIG_PATHS: ((foundation))/config VARS_PATHS: ((foundation))/vars # required only if using varsInfo
Unlike with
credhub-interpolate, there is no concept ofSKIP_MISSING. As such, if there are credentials that will be filled in future jobs by vars files, those vars files must be provided in thevarsinput and theVARS_PATHSparam.This task will replace all of the tasks provided in the
tasksinput with the modified tasks. The modified tasks include an extendedparamssection with the secret references detected from the config files. - The
-
Use the modified tasks in future jobs.
Here's an example of what prepare-tasks-with-secrets is doing internally.
Given an original task and the previously provided config/vars files:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | |
The prepare-tasks-with-secrets task will modify the original task
to have the variables found in director.yml embedded in the params section.
Any variables found in the vars.yml file will not be included in the modified task.
The params added will have a prefix of OM_VAR, so there are no collisions.
The task is a programmatically modified YAML file, so the output loses the comments and keys are sorted.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
Replacing credhub-interpolate with prepare-tasks-with-secrets
If you already have implemented the credhub-interpolate task within your pipeline,
this solution should be a drop in replacement if you are not using vars files.
Note this is only a replacement if using Concourse 5.x or greater.
If you are using vars files, the vars input and the VARS_PATHS param will also need to be set on the prepare-tasks-with-secrets task.
For example, if the existing credhub-interpolate task looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
In the task definition (above), you've had to define the prefix and Credhub authorization credentials.
The new prepare-tasks-with-secrets task uses concourse's native integration with Credhub (and other credential managers).
The above definition can be replaced with the following:
1 2 3 4 5 6 7 8 9 10 11 | |
If Using Vars Files
If using vars files in subsequent tasks, the vars input and the VARS_PATHS param must be used to prevent
interpolation errors in those subsequent tasks.
Notice in the above:
- The
output_mapping, which is required. This will replace allplatform-automation-taskswith the tasks that we have modified. The modification now includes an extendedparamsthat now includes the secret references detected from the config files. - The
INTERPOLATION_PATHSis nowCONFIG_PATHS. The concept of reading the references from the config files is still here, but no interpolation actually happens. - The
PREFIXis no longer defined or provided. Since the tasks are using concourse's native credential management, the lookup path is predetermined. For example,/concourse/:team_name/:cred_nameor/concourse/:team_name/:pipeline_name/:cred_name.
Using credhub-interpolate
The credhub-interpolate task can only be used with Credhub.
If using Concourse 5.x+, It is recommended to use the prepare-tasks-with-secrets task instead.
An example workflow would be storing an SSH key.
- Authenticate with your credhub instance.
- Generate an ssh key:
credhub generate --name="/concourse/:team_name/:pipeline_name/opsman_ssh_key" --type=ssh - Create an Ops Manager configuration file that references the name of the property.
1 2 3 | |
- Configure your pipeline to use the credhub-interpolate task.
It takes an input called
files, which should contain your configuration file from (3).
The declaration within a pipeline might look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
Notice the PREFIX has been set to /concourse/:team_name/:pipeline_name, the path prefix defined for your cred in (2).
This allows the config file to have values scoped, for example, per foundation.
params should be filled in by the credhub created with your Concourse instance.
Info
You can set the param SKIP_MISSING:false to enforce strict checking of
your vars files during intrpolation. This is true by default to support
credential management from multiple sources. For more information, see the
Multiple Sources section.
This task will reach out to the deployed credhub and fill in your entry references and return an output
named interpolated-files that can then be read as an input to any following tasks.
Our configuration will now look like
1 2 3 | |
Info
If using this you need to ensure the concourse worker can talk to credhub.
Depending on how you deployed credhub and/or the worker,
this may not be possible.
Using credhub-interpolate inverts control;
now workers need to access Credhub.
With prepare-tasks-with-secrets and other uses of Concourse's native integration,
the ATC retrieves secrets from Credhub and passes them to the worker.
Defining Multiline Certificates and Keys in Config Files
There are three ways to include certificates in the yaml files that are used by Platform Automation Toolkit tasks.
-
Direct inclusion in yaml file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
# An incomplete base.yml response from om staged-config product-name: cf product-properties: .uaa.service_provider_key_credentials: value: cert_pem: | -----BEGIN CERTIFICATE----- ...<Some Cert>... -----END CERTIFICATE----- private_key_pem: | -----BEGIN RSA PRIVATE KEY----- ...<Some Private Key>... -----END RSA PRIVATE KEY----- .properties.networking_poe_ssl_certs: value: - certificate: cert_pem: | -----BEGIN CERTIFICATE----- ...<Some Cert>... -----END CERTIFICATE----- private_key_pem: | -----BEGIN RSA PRIVATE KEY----- ...<Some Private Key>... -----END RSA PRIVATE KEY----- -
Secrets Manager reference in yaml file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# An incomplete base.yml product-name: cf product-properties: .uaa.service_provider_key_credentials: value: cert_pem: ((uaa_service_provider_key_credentials.certificate)) private_key_pem: ((uaa_service_provider_key_credentials.private_key)) .properties.networking_poe_ssl_certs: value: - certificate: cert_pem: ((networking_poe_ssl_certs.certificate)) private_key_pem: ((networking_poe_ssl_certs.private_key))This example assumes the use of Credhub.
Credhub supports a
--type=certificatecredential type which allows you to store a certificate and private key pair under a single name. The cert and key can be stored temporarily in local files or can be passed directly on the command line.An example of the file storage method:
1 2 3 4
credhub set --type=certificate \ --name=uaa_service_provider_key_credentials \ --certificate=./cert.pem \ --private=./private.key -
Using vars files
Vars files are a mix of the two previous methods. The cert/key is defined inline in the vars file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
#vars.yml uaa_service_provider_key_credentials_cert_pem: | -----BEGIN CERTIFICATE----- ...<Some Cert>... -----END CERTIFICATE----- uaa_service_provider_key_credentials_private_key: | -----BEGIN RSA PRIVATE KEY----- ...<Some Private Key>... -----END RSA PRIVATE KEY----- networking_poe_ssl_certs_cert_pem: | -----BEGIN CERTIFICATE----- ...<Some Cert>... -----END CERTIFICATE----- networking_poe_ssl_certs_private_key: | -----BEGIN RSA PRIVATE KEY----- ...<Some Private Key>... -----END RSA PRIVATE KEY-----and referenced as a
((parameter))in thebase.yml1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# An incomplete base.yml product-name: cf product-properties: .uaa.service_provider_key_credentials: value: cert_pem: ((uaa_service_provider_key_credentials_cert_pem)) private_key_pem: ((uaa_service_provider_key_credentials_private_key)) .properties.networking_poe_ssl_certs: value: - certificate: cert_pem: ((networking_poe_ssl_certs_cert_pem)) private_key_pem: ((networking_poe_ssl_certs_private_key))
Storing values for Multi-foundation
Concourse Supported Secrets Store
If you have multiple foundations, store relevant keys to that foundation in a different pipeline path,
and Concourse will read those values in appropriately.
If sharing the same base.yml across foundations, it is recommended to have a different pipeline per foundation.
Vars Files
Vars files can be used for your secrets handling. They are not recommended, but are sometimes required based on your foundation setup.
Take the example below (which only uses vars files and does not use a secrets store):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | |
In our first foundation, we have the following vars.yml, optional for the configure-product task.
1 2 3 | |
The vars.yml can then be passed to configure-product with base.yml as the config file.
The configure-product task will then sub the ((cloud_controller_encrypt_key.secret)) and ((cloud_controller_apps_domain))
specified in vars.yml and configure the product as normal.
An example of how this might look in a pipeline(resources not listed):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
If deploying more than one foundation, a unique vars.yml should be used for each foundation.
prepare-tasks-with-secrets and Vars Files
Both Credhub and vars files may be used together to interpolate variables into base.yml.
This use case is described in the Using prepare-tasks-with-secrets section.
credub-interpolate and Vars Files
Both Credhub and vars files may be used together to interpolate variables into base.yml.
Using the same example from above:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | |
We have one parametrized variable that is secret and might not want to have stored in
a plain text vars file, ((cloud_controller_encrypt_key.secret)), but ((cloud_controller_apps_domain))
is fine in a vars file. In order to support a base.yml with credentials from multiple sources (i.e.
credhub and vars files), you will need to SKIP_MISSING: true in the credhub-interpolate task.
This is enabled by default by the credhub-interpolate task.
The workflow would be the same as Credhub, but when passing the interpolated base.yml as a config into the
next task, you would add in a Vars File to fill in the missing variables.
An example of how this might look in a pipeline (resources not listed), assuming:
- The
((base.yml))above ((cloud_controller_encrypt_key.secret))is stored in credhub((cloud_controller_apps_domain))is stored indirector-vars.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | |
credhub-interpolate and Multiple Key Lookups
When using the credhub-interpolate task with a Credhub in a single foundation or multi-foundation manner,
we want to avoid duplicating identical credentials
(duplication makes credential rotation harder).
In order to have Credhub read in credentials from multiple paths
(not relative to your PREFIX),
you must provide the absolute path to any credentials
not in your relative path.
For example, using an alternative base.yml:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Let's say in our job, we define the prefix as "foundation1".
The parameterized values in the example above will be interpolated as follows:
((cloud_controller_apps_domain)) uses a relative path for Credhub.
When running credhub-interpolate, the task will prepend the PREFIX.
This value is stored in Credhub as /foundation1/cloud_controller_apps_domain.
((/alternate_prefix/cloud_controller_encrypt_key.secret)) (note the leading slash)
uses an absolute path for Credhub.
When running credhub-interpolate, the task will not prepend the prefix.
This value is stored in Credhub at it's absolute path /alternate_prefix/cloud_controller_encrypt_key.secret.
Any value with a leading / slash will never use the PREFIX
to look up values in Credhub.
Therefore, you can have multiple key lookups in a single interpolate task.