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