Sunday, November 14, 2021

[SOLVED] Jenkinsfile: Is there a more secure alternative to cleartext username:password/PAT for Bitbucket REST API?

Issue

I work with a Jenkinsfile that makes Bitbucket REST API calls.

The original version of the Jenkinsfile used a super-user's username:password as the -u argument to curl. E.g.

pipeline {
  agent any
  stages {
    stage( "1" ) {
      steps {
        script {
          def CRED = "super_user:super_password"
          def url = "https://bitbucket.company.com/rest/build-status/1.0/commits"
          def commit = 0000000000000000000000000000000000000001
          def dict = [:]
          dict.state = "INPROGRESS"
          dict.key = "foo_002"
          dict.url = "http://server:8080/blue/organizations/jenkins/job/detail/job/002/pipeline"
          def cmd = "curl " +
                    "-f " +
                    "-L " +
                    "-u ${CRED} " +
                    "-H \\\"Content-Type: application/json\\\" " +
                    "-X POST ${url}/${commit} " +
                    "-d \\\''${JsonOutput.toJson(dict)}'\\\'"
          sh(script: cmd)
        }
      }
    }
  }
}

I don't think that def CRED = "super_user:super_password" is secure -- i.e. the presence of the username/password in plaintext. So I went around trying to find alternatives.

It was recommended to me to use a personal access token (PAT) instead of a username/password.

I recently learned that the PAT is effectively "another password" for an existing user. I.e. if the noted super_user creates a PAT in the Bitbucket web UI -- "00000000000000000000000000000000000000000000" as an example -- then the only change to the above Jenkinsfile is:

def CRED = "super_user:00000000000000000000000000000000000000000000"

Why is this considered any more secure? Isn't the presence of the cleartext super_user:00000000000000000000000000000000000000000000 as much of a security vulnerability as the presence of the cleartext super_user:super_password?

This Bitbucket REST API documentation offers the example of the curl command to invoke the REST API that updates a commit's build status, which is what the above Jenkinsfile implements.

Since invoking the REST API ultimately comes down to a curl command -- i.e. something invoked in a shell prompt, whether by human or Jenkinsfile script -- what are prevailing conventions to secure that username:password/PAT so that it's not cleartext in the Jenkinsfile (or a file read by calling readFile(), etc.)?


Solution

You need to use Jenkins credentials store and do not use double quotes for the credentials variable in your curl command to avoid the string interpolation.

pipeline {
agent any
stages {
stage( "1" ) {
  steps {
    script {
     withCredentials([usernamePassword(credentialsId: 'bitBucketCreds', passwordVariable: 'password', usernameVariable: 'username')]) {
      String url = "https://bitbucket.company.com/rest/build-status/1.0/commits"
      String commit = '0000000000000000000000000000000000000001'
      Map dict = [:]
      dict.state = "INPROGRESS"
      dict.key = "foo_002"
      dict.url = "http://server:8080/blue/organizations/jenkins/job/detail/job/002/pipeline"
      List command = []
      command.add("curl -f -L")
      command.add('-u ${username}:${password}')
      command.add("-H \\\"Content-Type: application/json\\\"")
      command.add("-X POST ${url}/${commit}")
      command.add("-d \\\''${JsonOutput.toJson(dict)}'\\\'")
                         
      sh(script: command.join(' '))
     }
    }
   }
  }
 }
}


Answered By - Ram