Skip to content

Latest commit

 

History

History
245 lines (189 loc) · 11.2 KB

File metadata and controls

245 lines (189 loc) · 11.2 KB

Handling Secrets

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

Credentials Plugin is a standard way to manage credentials in Jenkins. This plugin offers the CredentialsProvider extension point which might be used to use credentials from external sources. Examples of available plugins:

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:

 - 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 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

Currently we have base64, readFile, decodeBase64 and readFileBase64 all serving a different purpose. decodeBase64 and readFileBase64 are useful for credential plugin or any other plugin that needs binary base64 encoding of a file, in this specific case that would be useful for a PKCS#12 certificate file.

  • ${base64:HELLO WORLD} into SEVMTE8gV09STEQ=

  • ${readFile:/secret/file.txt} into the content of a file.

  • ${base64:${readFile:/secret/file.txt} into a base64 representation of the content in a file

  • ${base64:${readFile:${SECRET_FILE_PATH}} nest it all together with regular secret expansion.

  • ${decodeBase64:${ENV_VAR}} decodes environment variables encoded as base64. It might be used for passing multi-line variables like SSH keys through the environment.

  • ${readFileBase64:/secret/certificate.p12} into a base64 representation of the binary file.

  • ${readFile:/secret/file.txt} an alias for readFile

  • ${fileBase64:/secret/certificate.p12} an alias for readFileBase64

Security and compatibility considerations

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 SecretSource extension point which allows resolving variables passed to JCasC. We can provide these initial secrets in the following ways:

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:

apiVersion: v1
kind: Secret
metadata:
  name: secret-name
data:
  filename: {{ "encoded string" | b64enc }}

can be used as:

- credentials:
    - string:
      id: "cred-id"
      secret: ${filename}

HashiCorp Vault Secret Source

Prerequisites: 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

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.

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

Environment variables can be directly read by JCasC when loading configurations. Secrets can be also injected using an environment variables. Note that such approach implies security risks, because the environment variables can be read by Jenkins admins and jobs running on the Jenkins controller.

Using properties file

JCasC will try to resolve secrets via .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 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 configuration export feature.

Note
There is an open feature request for supporting portable credentials. See JCasC #1141.

Configuration example:

credentials:
  system:
    domainCredentials:
    - credentials:
      - usernamePassword:
          id: "exampleuser-creds-id"
          username: "exampleuser"
          password: "{AQAAABAAAAAQ1/JHKggxIlBcuVqegoa2AdyVaNvjWIFk430/vI4jEBM=}"
          description: "Sample credentials of exampleuser"
          scope: GLOBAL