Search code examples
jenkinsjenkins-pipelinejenkins-pluginsjenkins-groovyhashicorp-vault

Injecting HashiCorp Vault KV secrets into Jenkins Pipeline


I was trying to build a pipeline that build and deploy nodejs application. In this pipeline NodeJS code is getting deployed. All the steps are getting passwed as required. But Looks like env file is missing while building the image. Now I want to create a .env file in root folder of the application and use that for building of the project.

I'm storing the environment variables in HashiCorp Vault which is deployed on different server. I want to pull all secrets in the path secrets/creds/my-app and inser it into .env file.

I'm new to both Vault and Jenkins, kindly help me with this.

Pipeline will be triggered on push to a BitBucket repo. I'm using HashiCorp Vault

Below is my Pipeline (Jenkinsfile)

pipeline{
agent any
tools { nodejs "nodejs18" }
stages{
    stage ('Checkout'){
    steps{
        checkout scm
    }
    }
    stage ('Cleanup') {
    steps{
        sh 'rm -rf node_modules'
    }
    }
    stage('Setup Environment') {
        steps {
            script{
                def secrets = [
                    [
                        path: 'secrets/creds/my-app', 
                        engineVersion: 1, 
                        secretValues: [[vaultKey: 'PORT'], [vaultKey: 'MONGO_DB_URL']]
                    ]
                ]

                def configuration = [
                    vaultUrl: 'http:/my-vault.com',
                    vaultCredentialId: 'vault-jenkins',
                    engineVersion: 1
                ]
                withVault([configuration: configuration, vaultSecrets: secrets]) {
                    def secretData = vaultRead(path: 'secrets/creds/my-app')
                    sh 'echo "Vault KV Values"'
                    echo "All secrets: ${secretData.data}"
                }
            }
        }
    }
    stage ('Resolve Dependency'){
    steps{
        sh '''
        npm install
        '''
    }
    }
    stage ('Build Project') {
    steps{
        sh 'npm run build'
    }
    }
    stage('Build & Push Docker') {
        steps {
            script {
                def dockerImage = docker.build("my-username/my-app-api:master", '--network host .')
                docker.withRegistry('', 'my-username') {
                    dockerImage.push('master')
                }
            }
        }
    }
    stage('Pull') {
        steps {
            script {
                docker.withRegistry('', 'my-username') {
                    def dockerImage = docker.image("my-username/my-app-api:master")
                    dockerImage.pull()
                }
            }
        }
    }
    stage('Deploy') {
        steps {
            script {
                sh 'docker stop my-appapi || true'
                sh 'docker rm my-appapi || true'
                sh 'docker run -d --name my-appapi -p 3000:3000 my-username/my-app-api:master'
            }
        }
    }
}
}

This configurarion will throw the below error

java.lang.NoSuchMethodError: No such DSL method 'vaultRead' found among steps

also, I found an answer where someone suggested to import library as below at the beginning of pipeline.

@Library('hashicorp-vault') _
import com.datapipe.jenkins.vault.*

This will throw different error tahn the one above.

ERROR: Could not find any definition of libraries [hashicorp-vault]

I've tried a few more things and found an answer taht said go to Manage Jenkins > Configure System > Global Pipeline Libraries and then add hashicorp-vault as library.

I could not find the library github link, so tried to add https://github.com/jenkinsci/hashicorp-vault-plugin.git as Library URL. and below is the error log.

Loading library hashicorp-vault@360.v0a_1c04cf807d
11:17:41 Jenkins-Imposed API Limiter: Current quota for Github API usage has 51 remaining (2 over budget). Next quota of 60 in 59 min. Sleeping for 6 min 51 sec.
11:17:41 Jenkins is attempting to evenly distribute GitHub API requests. To configure a different rate limiting strategy, such as having Jenkins restrict GitHub API requests only when near or above the GitHub rate limit, go to "GitHub API usage" under "Configure System" in the Jenkins settings.
11:20:42 Jenkins-Imposed API Limiter: Still sleeping, now only 3 min 48 sec remaining.
11:23:43 Jenkins-Imposed API Limiter: Still sleeping, now only 47 sec remaining.
Examining jenkinsci/hashicorp-vault-plugin
Attempting to resolve 360.v0a_1c04cf807d as a branch
Attempting to resolve 360.v0a_1c04cf807d as a tag
Resolved 360.v0a_1c04cf807d as tag 360.v0a_1c04cf807d at revision 0a1c04cf807da08a74dcf499865fa96ee8dbae39
The recommended git tool is: NONE
No credentials specified
Cloning the remote Git repository
Cloning with configured refspecs honoured and with tags
Cloning repository https://github.com/jenkinsci/hashicorp-vault-plugin.git
> git init /var/jenkins_home/workspace/curiovy-api-v2@libs/cbf0f7307134c7e67151812899f055d0a075a06bbb887daaec9cd68facf7b289 # timeout=10
Fetching upstream changes from https://github.com/jenkinsci/hashicorp-vault-plugin.git
> git --version # timeout=10
> git --version # 'git version 2.30.2'
> git fetch --tags --force --progress -- https://github.com/jenkinsci/hashicorp-vault-plugin.git +refs/tags/360.v0a_1c04cf807d:refs/tags/360.v0a_1c04cf807d # timeout=10
> git config remote.origin.url https://github.com/jenkinsci/hashicorp-vault-plugin.git # timeout=10
> git config --add remote.origin.fetch +refs/tags/360.v0a_1c04cf807d:refs/tags/360.v0a_1c04cf807d # timeout=10
> git config remote.origin.url https://github.com/jenkinsci/hashicorp-vault-plugin.git # timeout=10
Fetching with tags
Fetching upstream changes from https://github.com/jenkinsci/hashicorp-vault-plugin.git
> git fetch --tags --force --progress -- https://github.com/jenkinsci/hashicorp-vault-plugin.git +refs/tags/360.v0a_1c04cf807d:refs/tags/360.v0a_1c04cf807d # timeout=10
Checking out Revision 0a1c04cf807da08a74dcf499865fa96ee8dbae39 (360.v0a_1c04cf807d)
> git config core.sparsecheckout # timeout=10
> git checkout -f 0a1c04cf807da08a74dcf499865fa96ee8dbae39 # timeout=10
Commit message: "Mark logger as transient (#285)"
First time build. Skipping changelog.
Excluding src/test/ from checkout of git https://github.com/jenkinsci/hashicorp-vault-plugin.git so that library test code cannot be accessed by Pipelines.
To remove this log message, move the test code outside of src/. To restore the previous behavior that allowed access to files in src/test/, pass -Dorg.jenkinsci.plugins.workflow.libs.SCMSourceRetriever.INCLUDE_SRC_TEST_IN_LIBRARIES=true to the java command used to start Jenkins.
ERROR: Library hashicorp-vault expected to contain at least one of src or vars directories
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
WorkflowScript: Loading libraries failed

1 error

    at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:309)
    at org.codehaus.groovy.control.CompilationUnit.applyToPrimaryClassNodes(CompilationUnit.java:1107)
    at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:624)
    at org.codehaus.groovy.control.CompilationUnit.processPhaseOperations(CompilationUnit.java:602)
    at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:579)
    at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:323)
    at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:293)
    at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.GroovySandbox$Scope.parse(GroovySandbox.java:163)
    at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.doParse(CpsGroovyShell.java:190)
    at org.jenkinsci.plugins.workflow.cps.CpsGroovyShell.reparse(CpsGroovyShell.java:175)
    at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.parseScript(CpsFlowExecution.java:568)
    at org.jenkinsci.plugins.workflow.cps.CpsFlowExecution.start(CpsFlowExecution.java:518)
    at org.jenkinsci.plugins.workflow.job.WorkflowRun.run(WorkflowRun.java:336)
    at hudson.model.ResourceController.execute(ResourceController.java:101)
    at hudson.model.Executor.run(Executor.java:442)
Finished: FAILURE

Kindly give me the solution or Point me in right direction if I'm doing something wrong. Is my way of adding .env to the Project build is correct? or do I need to follow some other step?


Solution

  • to load secrets into pipeline from vault you have 2 ways

    1. configuration as code plugin + vault plugin CASC vault

    you need to pass envs doc to jenkins master with vault auth

    CASC_VAULT_URL 
    CASC_VAULT_TOKEN
    CASC_VAULT_PATHS
    

    where paths will be secrets/creds/my-app and jenkins will load secrets into credentials via CASC plugin like so

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

    where my-secret is vault secret

    vault path: secrets/creds/my-app
                              my-secret=secretvalue
                              my-secret2=secretvalue2
    

    and use it in pipeline with credentials-binding plugin like so:

    https://www.jenkins.io/doc/pipeline/steps/credentials-binding/

    withCredentials([string(credentialsId: 'cred-id', variable: 'TOKEN')]) {
        sh '''
          $TOKEN >> .env
        '''
    }
    

    2.Use hashicorp-vault-plugin without everything

    // how vault secret structure looks like:
    // secrets/creds/my-app
    //               secret_one="my-super-secret-ssh-key"
    //               secret_two="my-super-secret-ssh-key"
    
    def secrets = [
      [
        path: 'secrets/creds/my-app', engineVersion: 2, secretValues: [
          [envVar: 'SSH_SECRET_ENV_VAR', vaultKey: 'secret_one'],
          [envVar: 'ANOTHER_SECRET_ENV_VAR', vaultKey: 'secret_two']
        ]
      ]
    ]
    
    // optional configuration, if you do not provide this the next higher configuration
    // (e.g. folder or global) will be used
    def configuration = [
      vaultUrl: 'http:/my-vault.com',
      // 'vault-jenkins' -> key from global jenkins credentials or folder jenkins credentials
      // see https://plugins.jenkins.io/hashicorp-vault-plugin/#plugin-content-vault-token-credential
      vaultCredentialId: 'vault-jenkins',
      engineVersion: 2
    ]
    // inside this block your credentials will be available as env variables
    withVault([configuration: configuration, vaultSecrets: secrets]) {
        sh 'echo "sshkey=$SSH_SECRET" >> .env'
        sh 'echo "password=$ANOTHER_SECRET_ENV_VAR" >> .env'
    }
    

    it will load value of key from vault path into env variables see "secrets" ^