338 lines
12 KiB
Plaintext
338 lines
12 KiB
Plaintext
= Handling Secrets
|
|
:toc:
|
|
:toc-placement: preamble
|
|
:toclevels: 3
|
|
|
|
Almost every Jenkins instance defines credentials and other sensitive information, and JCasC offers ways to manage credentials and other sensitive information in the YAML configuration files.
|
|
This page describes the available options.
|
|
|
|
There are 3 ways to securely pass credentials in JCasC:
|
|
|
|
* Using credential provider plugins
|
|
* Passing secrets through variables
|
|
* Passing secrets through encrypted strings
|
|
|
|
== Using credential provider plugins
|
|
|
|
link:https://plugins.jenkins.io/credentials[Credentials Plugin] is a standard way to manage credentials in Jenkins.
|
|
This plugin offers the link:https://jenkins.io/doc/developer/extensions/credentials/#credentialsprovider[CredentialsProvider extension point] which might be used to use credentials from external sources.
|
|
Examples of available plugins:
|
|
|
|
* link:https://plugins.jenkins.io/kubernetes-credentials-provider[Kubernetes Credentials Provider]
|
|
* link:https://plugins.jenkins.io/aws-secrets-manager-credentials-provider[AWS Secrets Manager Credentials Provider]
|
|
* link:https://plugins.jenkins.io/azure-keyvault[Azure KeyVault]
|
|
* link:https://go.cloudbees.com/docs/cloudbees-core/cloud-secure-guide/cyberark/#cyberark-credentials-provider[CyberARK Credential Provider] (proprietary plugin offered by CloudBees)
|
|
|
|
When an external Credentials provider is configured in Jenkins through JCasC, there is no need to define credentials inside.
|
|
Jenkins will be able to take the credentials from external storage by ID:
|
|
|
|
```yaml
|
|
- kubernetes:
|
|
connectTimeout: 5
|
|
containerCapStr: "10"
|
|
credentialsId: "k8s-user-password"
|
|
jenkinsUrl: "http://localhost:8080"
|
|
|
|
```
|
|
|
|
Using an external credentials source does **NOT** address all use-cases.
|
|
There are configurations inside Jenkins (e.g. proxy password) which use a low-level link:https://javadoc.jenkins-ci.org/hudson/util/Secret.html[hudson.util.Secret] engine.
|
|
For this type of credentials other engines should be used, see below.
|
|
|
|
== Passing secrets through variables
|
|
|
|
Currently, you can provide initial secrets to JCasC that all rely on <key,value> substitution of strings in the configuration.
|
|
For example, `Jenkins: "${some_var}"`.
|
|
Default variable substitution using the `:-` operator from `bash` is also available.
|
|
For example, `key: "${VALUE:-defaultvalue}"` will evaluate to `defaultvalue` if `$VALUE` is unset.
|
|
To escape a string from secret interpolation, put `^` in front of the value.
|
|
For example, `Jenkins: "^${some_var}"` will produce the literal `Jenkins: "${some_var}"`.
|
|
|
|
=== Additional variable substitution
|
|
|
|
If you need to read a secret from a file, or perform additional en/decoding of the secret prior to use, the following helpers are available:
|
|
|
|
- `base64`
|
|
- `readFile`
|
|
- `decodeBase64`
|
|
- `readFileBase64`
|
|
- `json`
|
|
|
|
Helpers can be combined to do multiple transformations. Examples:
|
|
|
|
- `${base64:${readFile:/secret/file.txt}}`: read the file -> base64-encode it
|
|
- `${base64:${readFile:${SECRET_FILE_PATH}}}`: expand the SECRET_FILE_PATH variable -> read the file -> base64-encode it
|
|
|
|
==== base64
|
|
|
|
Encodes the provided secret to base64.
|
|
|
|
Example:
|
|
|
|
`${base64:HELLO WORLD}` -> `SEVMTE8gV09STEQ=`
|
|
|
|
==== decodeBase64
|
|
|
|
Decodes the provided base64-encoded secret into a plain text string.
|
|
|
|
Example:
|
|
|
|
```
|
|
${decodeBase64:${ENV_VAR}}
|
|
```
|
|
|
|
`decodeBase64` (and its related `readFileBase64`) may be useful for the credential plugin or any other plugin that needs binary base64 encoding of a file. This might help when handling link:https://tools.ietf.org/html/rfc7292[a PKCS#12 certificate files] or passing an SSH key as an environment variable.
|
|
|
|
==== file
|
|
|
|
Alias for `readFile`.
|
|
|
|
Example:
|
|
|
|
```
|
|
${file:/secret/file.txt}
|
|
```
|
|
|
|
==== fileBase64
|
|
|
|
Alias for `readFileBase64`.
|
|
|
|
Example:
|
|
|
|
```
|
|
${fileBase64:/secret/certificate.p12}
|
|
```
|
|
|
|
==== readFile
|
|
|
|
Reads the secret from the specified file.
|
|
|
|
Example:
|
|
|
|
```
|
|
${readFile:/secret/file.txt}
|
|
```
|
|
|
|
==== readFileBase64
|
|
|
|
Read the specified binary file into a base64 representation.
|
|
|
|
```
|
|
${readFileBase64:/secret/certificate.p12}
|
|
```
|
|
|
|
==== json
|
|
|
|
Parse the string secret as JSON, then extract the value for the specified `<key>`.
|
|
|
|
(An empty string is returned if the JSON secret does not contain `<key>`).
|
|
|
|
```
|
|
${json:<key>:${ENV_VAR}}
|
|
```
|
|
|
|
This can help in situations where the backend has the option to store JSON secrets, e.g. AWS Secrets Manager.
|
|
|
|
Example:
|
|
|
|
```
|
|
${json:username:${ENV_VAR}}
|
|
```
|
|
|
|
=== Security and compatibility considerations
|
|
|
|
// TODO(oleg_nenashev): Add a link to the advisory once ready
|
|
|
|
Jenkins configurations might include property definitions,
|
|
e.g. for Token Macro resolution in Mail Ext Plugin.
|
|
Such properties are not supposed to be resolved when importing configurations,
|
|
but the JCasC plugin has no way to determine which variables should be resolved when reading the configurations.
|
|
|
|
In some cases non-admin users can contribute to JCasC exports if they have some permissions
|
|
(e.g. agent/view configuration or credentials management),
|
|
and they could potentially inject variable expressions in plain text fields like descriptions
|
|
and then see the resolved secrets in Jenkins Web UI if the Jenkins admin exports and imports the configuration without checking contents.
|
|
It led to a security vulnerability which was addressed in JCasC `1.25` (SECURITY-1446).
|
|
|
|
- When reading configuration YAMLs, JCasC plugin will try to resolve
|
|
**all** variables having the `${VARNAME}` format.
|
|
- Starting from JCasC `1.25`, JCasC export escapes the internal variable expressions,
|
|
e.g. as `^${VARNAME}`, so newly exported and then imported configurations are
|
|
are not subject for this risk
|
|
- For previously exported configurations, Jenkins admins are expected to manually
|
|
resolve the issues by putting the escape symbol `^` in front of variables which should not be resolved
|
|
|
|
=== Secret sources
|
|
|
|
In JCasC there is a link:https://jenkins.io/doc/developer/extensions/configuration-as-code/#secretsource[SecretSource extension point] which allows resolving variables passed to JCasC.
|
|
We can provide these initial secrets in the following ways:
|
|
|
|
- link:https://github.com/jenkinsci/aws-secrets-manager-secret-source-plugin[AWS Secrets Manager]
|
|
- link:https://github.com/jenkinsci/configuration-as-code-secret-ssm-plugin[AWS Systems Manager Parameter Store]
|
|
- link:https://github.com/jenkinsci/azure-keyvault-plugin#secretsource[Azure KeyVault]
|
|
- Docker Secrets
|
|
- Environment variables
|
|
- link:https://github.com/jenkinsci/hashicorp-vault-plugin[HashiCorp Vault]
|
|
- Kubernetes secrets
|
|
|
|
==== Docker secrets
|
|
|
|
Files on path `/run/secrets/${KEY}` will be replaced by `${KEY}` in the configuration.
|
|
The base folder `/run/secrets` can be overridden by setting the environment variable `SECRETS`.
|
|
So this can be used as a file based secret, and not just docker secrets.
|
|
|
|
==== Kubernetes secrets
|
|
|
|
Logic is the same as for docker-secrets.
|
|
The secret needs to be mounted as a file to `/run/secrets/`, and then the filename can be used as the KEY.
|
|
For example:
|
|
|
|
```yaml
|
|
apiVersion: v1
|
|
kind: Secret
|
|
metadata:
|
|
name: secret-name
|
|
data:
|
|
filename: {{ "encoded string" | b64enc }}
|
|
```
|
|
|
|
can be used from a pod containing
|
|
|
|
```yaml
|
|
containers:
|
|
- name: jenkins
|
|
volumeMounts:
|
|
- name: filename-secret
|
|
mountPath: /run/secrets/filename
|
|
subPath: filename
|
|
readOnly: true
|
|
volumes:
|
|
- name: filename-secret
|
|
secret:
|
|
secretName: secret-name
|
|
```
|
|
|
|
as:
|
|
|
|
```yaml
|
|
- credentials:
|
|
- string:
|
|
id: "cred-id"
|
|
secret: ${filename}
|
|
```
|
|
|
|
==== HashiCorp Vault Secret Source
|
|
|
|
Prerequisites: link:https://plugins.jenkins.io/hashicorp-vault-plugin[HashiCorp Vault plugin] v2.4.0+.
|
|
|
|
- The environment variable `CASC_VAULT_PW` must be present, if token is not used and appRole/Secret is not used. (Vault password.)
|
|
- The environment variable `CASC_VAULT_USER` must be present, if token is not used and appRole/Secret is not used. (Vault username.)
|
|
- The environment variable `CASC_VAULT_APPROLE` must be present, if token is not used and U/P not used. (Vault AppRole ID.)
|
|
- The environment variable `CASC_VAULT_APPROLE_SECRET` must be present, it token is not used and U/P not used. (Vault AppRole Secret ID.)
|
|
- The environment variable `CASC_VAULT_TOKEN` must be present, if U/P is not used. (Vault token.)
|
|
- The environment variable `CASC_VAULT_PATHS` must be present. (Comma separated vault key paths. For example, `secret/jenkins,secret/admin`.)
|
|
- The environment variable `CASC_VAULT_URL` must be present. (Vault url, including port number.)
|
|
- The environment variable `CASC_VAULT_MOUNT` is optional. (Vault auth mount. For example, `ldap` or another username & password authentication type, defaults to `userpass`.)
|
|
- The environment variable `CASC_VAULT_NAMESPACE` is optional. If used, sets the Vault namespace for Enterprise Vaults.
|
|
- The environment variable `CASC_VAULT_FILE` is optional, provides a way for the other variables to be read from a file instead of environment variables.
|
|
- The environment variable `CASC_VAULT_ENGINE_VERSION` is optional.
|
|
If unset, your vault path is assumed to be using kv version 2.
|
|
If your vault path uses engine version 1, set this variable to `1`.
|
|
- The issued token should have read access to vault path `auth/token/lookup-self` in order to determine its expiration time.
|
|
JCasC will re-issue a token if its expiration is reached (except for `CASC_VAULT_TOKEN`).
|
|
|
|
If the environment variables `CASC_VAULT_URL` and `CASC_VAULT_PATHS` are present, JCasC will try to gather initial secrets from Vault.
|
|
However for it to work properly there is a need for authentication by either the combination of `CASC_VAULT_USER` and `CASC_VAULT_PW`, a `CASC_VAULT_TOKEN`, or the combination of `CASC_VAULT_APPROLE` and `CASC_VAULT_APPROLE_SECRET`.
|
|
The authenticated user must have at least read access.
|
|
|
|
You can also provide a `CASC_VAULT_FILE` environment variable where you load the secrets from a file.
|
|
|
|
File should be in a Java Properties format
|
|
|
|
```properties
|
|
CASC_VAULT_PW=PASSWORD
|
|
CASC_VAULT_USER=USER
|
|
CASC_VAULT_TOKEN=TOKEN
|
|
CASC_VAULT_PATHS=secret/jenkins/master,secret/admin
|
|
CASC_VAULT_URL=https://vault.dot.com
|
|
CASC_VAULT_MOUNT=ldap
|
|
```
|
|
|
|
A good use for `CASC_VAULT_FILE` would be together with docker secrets.
|
|
|
|
```yaml
|
|
version: "3.6"
|
|
|
|
services:
|
|
jenkins:
|
|
environment:
|
|
CASC_VAULT_FILE: /run/secrets/jcasc_vault
|
|
restart: always
|
|
build: .
|
|
image: jenkins.master:v1.0
|
|
ports:
|
|
- 8080:8080
|
|
- 50000:50000
|
|
volumes:
|
|
- jenkins-home:/var/jenkins_home
|
|
secrets:
|
|
- jcasc_vault
|
|
|
|
volumes:
|
|
jenkins-home:
|
|
|
|
secrets:
|
|
jcasc_vault:
|
|
file: ./secrets/jcasc_vault
|
|
```
|
|
|
|
==== Using environment variables (not recommended for secrets)
|
|
|
|
Although it is supported, it is not recommended to inject secrets using environment variables.
|
|
Doing so is *unsafe* because the environment variables can be read by
|
|
Jenkins admins and jobs running on the Jenkins controller.
|
|
|
|
You can use this to abstract away non secret configuration values across environments.
|
|
|
|
==== Using properties file
|
|
|
|
JCasC will try to resolve secrets via
|
|
link:https://en.wikipedia.org/wiki/.properties[.properties] file if
|
|
`/run/secrets/secrets.properties` exists. To change this
|
|
default file path you can use the environment variable `SECRETS_FILE`.
|
|
This file must be secured through machine ownership and permissions.
|
|
|
|
== Passing credentials as encrypted text
|
|
|
|
This is an additional engine which uses the link:https://javadoc.jenkins-ci.org/hudson/util/Secret.html[hudson.util.Secret] engine to define encrypted credentials in JCasC configuration files.
|
|
|
|
* Encrypted credentials can be stored in plain text
|
|
* Encryption is done using the Jenkins-internal secret key
|
|
which is unique for every Jenkins instance.
|
|
It means that the credentials are not portable between instances.
|
|
* Encrypted credential values can be exported using the link:./configExport.md[configuration export] feature.
|
|
|
|
NOTE: There is an open feature request for supporting portable credentials.
|
|
See link:https://github.com/jenkinsci/configuration-as-code-plugin/issues/1141[JCasC #1141].
|
|
|
|
Configuration example:
|
|
|
|
```yaml
|
|
credentials:
|
|
system:
|
|
domainCredentials:
|
|
- credentials:
|
|
- usernamePassword:
|
|
id: "exampleuser-creds-id"
|
|
username: "exampleuser"
|
|
password: "{AQAAABAAAAAQ1/JHKggxIlBcuVqegoa2AdyVaNvjWIFk430/vI4jEBM=}"
|
|
description: "Sample credentials of exampleuser"
|
|
scope: GLOBAL
|
|
|
|
```
|
|
|
|
== Useful links
|
|
|
|
* link:https://jenkins.io/doc/developer/security/secrets/[Jenkins Developer Guide: Storing Secrets in Jenkins]
|
|
|