Jenkins logo

How to manage secrets in Jenkins

When using Jenkins, secrets are usually handled through the Jenkins credential system. While it provides a good way of storing and injecting secrets, it also leaves some problems unsolved.

For starters, to run tests locally, secrets need to be manually synchronized between different environments and developers.

Any environment variables defined in the pipeline also need to be configured on the local workstation.

Moreover, all secrets used by the pipeline need to be read and mapped to environment variables in the Jenkinsfile, which can result in a cluttered script.

pipeline {
    agent {
    	dockerfile true
    }
    environment {
        DATABASE_PASSWORD = credentials('db-password')
        GITHUB_TOKEN = credentials('github-token')
        STRIPE_TOKEN = credentials('stripe-token')
        FIREBASE_API_KEY = credentials('firebase-key')
        AUTH0_TOKEN = credentials('auth0-token')

        /* ... */

    }
    stages {
        stage('build') {
            steps {
                sh 'npm install'
            }
        }
        stage('test') {
            steps {
                sh 'npm test'
                /* ... */
            }
        }
    }
} 

Secret management with SecretHub

SecretHub simplifies secret management by storing secrets securely in a central database and providing them automatically to authenticated parties, whenever and wherever they are needed.

When using SecretHub, the secret to environment variable mapping will be moved to a secrethub.env file which can also be used for running the code in production or locally, from your IDE or the command line.

DATABASE_PASSWORD = {{ your-username/repo/db-password }}
GITHUB_TOKEN = {{ your-username/repo/github-token }}
STRIPE_TOKEN = {{ your-username/repo/stripe-token}}
FIREBASE_API_KEY = {{ your-username/repo/firebase-key }}
AUTH0_TOKEN = {{ your-username/repo/auth0-token }}

Your Jenkinsfile will also be simpler and contain no secret-related information:

pipeline {
    agent {
    	dockerfile true
    }
    environment {
        SECRETHUB_CREDENTIAL = credentials('secrethub_credential')
    }
    stages {
        stage('build') {
            steps {
                sh 'npm install'
            }
        }
        stage('test') {
            steps {
                sh 'secrethub run -- node test.js'
            }
        }
    }
}

This guide will walk you through the steps necessary to integrate SecretHub with your Jenkins pipeline.

If you’re running your Jenkins server on AWS, check out the AWS integration that covers how to authenticate to SecretHub without having to deal with service credentials.

Before you begin

To follow along, you will need to:

  • Have the SecretHub CLI installed and sign up for an account.
  • Have Jenkins installed and have working knowledge of it
  • Have Docker installed on your Jenkins server

This guide uses Jenkins version 2.190.1

Step 1: Set up the Jenkinsfile

First, create a Jenkinsfile similar to the one showed before and add it to your project’s root directory. This guide uses a Node.js app as an example.

pipeline {
    agent {
    	dockerfile true
    }
    environment {
        SECRETHUB_CREDENTIAL = credentials('secrethub_credential')
    }
    stages {
        stage('build') {
            steps {
                sh 'npm install'
            }
        }
        stage('test') {
            steps {
                sh 'secrethub run -- node test.js'
            }
        }
    }
} 

In the agent block, it is specified that the Docker container should be based on a Docker image that will be set up in the next step.

In the environment block, the credential used for authenticating to the SecretHub API is read and assigned to the environment variable named SECRETHUB_CREDENTIAL. This environment variable will be used by SecretHub to access the secrets that the previously created service account has access to.

In the stage block, the command for running tests is wrapped in the secrethub run command. This makes sure that the environment variables required by the testing script are set to the correct secret values, according to the secrethub.env file which will be introduced in the third step.

Step 2: Add SecretHub to the Dockerfile

The previous Jenkinsfile requires the SecretHub CLI to be installed. To make sure that this is the case, you can configure the Jenkins agent to run the pipeline in a Docker container with SecretHub installed on it. To achieve this, place a Dockerfile similar to this one in your project’s root directory:

FROM node:alpine

RUN apk add --repository https://alpine.secrethub.io/alpine/edge/main --allow-untrusted secrethub-cli

This file ensures that the SecretHub CLI is installed in the container in which the job is executed. Note that Jenkins will use this container image for the pipeline since this has been previously specified in the Jenkinsfile’s agent block.

Step 3: Add the secrethub.env file

To provide a mapping between environment variables and secrets, create a file named secrethub.env similar to this one in your project’s root directory:

DB_HOST = localhost
DB_USER = test-user
DB_PASS = {{ your-username/repo-name/password }}

The path {{ your-username/repo-name/password }} references a secret that will be defined later in this guide.

If you want to use different secrets for each environment (e.g. env=dev or env=prd) check out the environment file syntax.

Step 4: Write your secrets to SecretHub

You can write your secrets to SecretHub from any workstation with the SecretHub CLI installed on it.

Secrets are organized in repositories. To create a repository for your secrets use the repo init command:

secrethub repo init your-username/repo-name

Now you can write the secrets that you want your Jenkins server to have access to with this command:

secrethub write your-username/repo-name/password

Step 5: Create a SecretHub service account

The account you previously created is meant to be used by humans. To give the Jenkins server access to your secrets you can create a service account for it.

To create a service account, run the secrethub service init command:

secrethub service init --permission read your-username/repo-name

This command will output the generated account credential. Make sure to copy it as it will be needed in the next step.

The --permission read flag will automatically grant the service read access to the specified repo.

Note that you can use the --clip flag to automatically write the output of the command to your clipboard:

secrethub service init --clip --permission read your-username/repo-name

Step 6: Provide the service credential to the server

The next step is to provide the generated credential to the Jenkins server in order to authenticate the pipeline to SecretHub.

  1. Log in to the Jenkins server’s web interface
  2. Go to Credentials > System > Global Credentials
    Credentials > System > Global Credentials
  3. Click on Add Credentials
    Click Add Credential
  4. Fill in the form, then click OK:
    • Set the Kind field to Secret text
    • Input the previously generated account credential in the Secret field
    • Set the Id field to secrethub_credential
    • Add a suitable description in the Description field
    Fill form and click Ok

For a detailed explanation of how credentials are handled in Jenkins, check out Using credentials.

You have successfully configured your Jenkins pipeline to automatically fetch secrets from the SecretHub API, and thus also simpified secret provisioning when running code locally or in production!

See also

Happy coding!