Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-58170

Allow credential parameters to shadow credential ids in lookup

    XMLWordPrintable

    Details

    • Similar Issues:
    • Released As:
      credentials-2.3.0

      Description

      As a pipeline author, suppose I have a pipeline that performs a deployment action that requires credentials from Jenkins. A naive approach might be to add my credentials to the global Jenkins credential store (call it deployKey as its id); then I can reference it in my pipeline later like so:

       

      node {
        // ...
        stage('Deploy') {
          withCredentials([usernameColonPassword(credentialsId: 'deployKey', variable: 'DEPLOY_CREDENTIALS')]) {
            sh 'curl -u "${DEPLOY_CREDENTIALS}" ...'
          }
        }
      }
      

      But then I want to make my pipeline more secure. One option I might have is to have a dedicated Jenkins instance for running deployments. This is a large maintenance burden (e.g., trusted.ci.j.io versus ci.j.io), so digging deeper into Jenkins' existing features, I find user-scoped credentials. I can simply use credentials attached to my user account instead of Jenkins itself, though in order for my pipeline to recognize and use these credentials, I find that I must make them parameterized to the build itself so that I can select my credentials upon starting a build (there's an alternative option using the Authorize Project plugin, but this plugin is not widely used). A neat side-effect of this functionality is that other users can also deploy using the same pipeline but with their own user-scoped credentials. This can also allow me the flexibility of using a default global credential and allowing users to override that specifically for a pipeline run.

      Thus, I modify my pipeline to add a build parameter for the deploy credentials as well as using a modified syntax in withCredentials to indicate that the credentialId parameter being provided is in fact a build parameter name and not the actual credential id. Combined, we get the following updated pipeline:

      properties([parameters([credentials(credentialType: 'com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials', name: 'deployKey', required: true)])])
      node {
        // ...
        stage('Deploy') {
          withCredentials([usernameColonPassword(credentialsId: '${deployKey}', variable: 'DEPLOY_CREDENTIALS')]) {
            sh 'curl -u "${DEPLOY_CREDENTIALS}" ...'
          }
        }
      }
      

      Notice how we need to use a special syntax: credentialsId: '${deployKey}'. This syntax is misleading because it can be easily confused with the similar but not equivalent GString syntax: credentialsId: "${deployKey}". In fact, this latter syntax would never work for a user-scoped credential because user-scoped credentials are currently only looked up if the credential id is provided using the former template syntax.

      What I am proposing we change here is to make credential parameters referenced by name take precedent over a globally-scoped credential with an id of the same name as the parameter name itself. Thus, we could update the above pipeline using this feature to be a little simpler:

       

      properties([parameters([credentials(credentialType: 'com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials', name: 'deployKey', required: true)])])
      node {
        // ...
        stage('Deploy') {
        withCredentials([usernameColonPassword(credentialsId: 'deployKey', variable: 'DEPLOY_CREDENTIALS')]) {
          sh 'curl -u "${DEPLOY_CREDENTIALS}" ...'
        }
       }
      }
      

      The beauty of this approach is that it allows me to go from the first pipeline to the third pipeline simply by updating my build to add a credential parameter with the same name as the credential id I was using before that I want to migrate to a user account. I don't even have to touch the rest of the pipeline!

      This issue relates to JENKINS-47699 in that it's a useful point to help support that feature.

       

       

        Attachments

          Issue Links

            Activity

            jvz Matt Sicker created issue -
            jvz Matt Sicker made changes -
            Field Original Value New Value
            Status Open [ 1 ] In Progress [ 3 ]
            jvz Matt Sicker made changes -
            Status In Progress [ 3 ] In Review [ 10005 ]
            jvz Matt Sicker made changes -
            Description As a pipeline author, suppose I have a pipeline that performs a deployment action that requires credentials from Jenkins. A naive approach might be to add my credentials to the global Jenkins credential store (call it {{deployKey}} as its id); then I can reference it in my pipeline later like so:

             
            {code:java}
            node {
              // ...
              stage('Deploy') {
                withCredentials([usernameColonPassword(credentialsId: 'deployKey', variable: 'DEPLOY_CREDENTIALS')]) {
                  sh 'curl -u "${DEPLOY_CREDENTIALS}" ...'
                }
              }
            }
            {code}
            But then I want to make my pipeline more secure. One option I might have is to have a dedicated Jenkins instance for running deployments. This is a _large_ maintenance burden (e.g., trusted.ci.j.io versus ci.j.io), so digging deeper into Jenkins' existing features, I find user-scoped credentials. I can simply use credentials attached to my user account instead of Jenkins itself, though in order for my pipeline to recognize and use these credentials, I find that I must make them parameterized to the build itself so that I can select my credentials upon starting a build (there's an alternative option using the Authorize Project plugin, but this plugin is not widely used). A neat side-effect of this functionality is that other users can also deploy using the same pipeline but with their own user-scoped credentials. This can also allow me the flexibility of using a default global credential and allowing users to override that specifically for a pipeline run.

            Thus, I modify my pipeline to add a build parameter for the deploy credentials as well as using a modified syntax in {{withCredentials}} to indicate that the {{credentialId}} parameter being provided is in fact a build parameter name and not the actual credential id. Combined, we get the following updated pipeline:
            {code:java}
            properties([parameters([credentials(credentialType: 'com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials', name: 'deployKey', required: true)])])
            node {
              // ...
              stage('Deploy') {
                withCredentials([usernameColonPassword(credentialsId: '${deployKey}', variable: 'DEPLOY_CREDENTIALS')]) {
                  sh 'curl -u "${DEPLOY_CREDENTIALS}" ...'
                }
              }
            }
            {code}
            Notice how we need to use a special syntax: {{credentialsId: '${deployKey}'}}. This syntax is misleading because it can be easily confused with the similar but not equivalent GString syntax: {{credentialsId: "${deployKey}"}}. In fact, this latter syntax would never work for a user-scoped credential because user-scoped credentials are currently only looked up if the credential id is provided using that template syntax.

            What I am proposing we change here is to make credential parameters referenced by name take precedent over a globally-scoped credential with an id of the same name as the parameter name itself. Thus, we could update the above pipeline using this feature to be a little simpler:

             
            {code:java}
            properties([parameters([credentials(credentialType: 'com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials', name: 'deployKey', required: true)])])
            node {
              // ...
              stage('Deploy') {
              withCredentials([usernameColonPassword(credentialsId: 'deployKey', variable: 'DEPLOY_CREDENTIALS')]) {
                sh 'curl -u "${DEPLOY_CREDENTIALS}" ...'
              }
             }
            }
            {code}
            The beauty of this approach is that it allows me to go from the first pipeline to the third pipeline simply by updating my build to add a credential parameter with the same name as the credential id I was using before that I want to migrate to a user account. I don't even have to touch the rest of the pipeline!

            This issue relates to JENKINS-47699 in that it's a useful point to help support that feature.

             

             
            As a pipeline author, suppose I have a pipeline that performs a deployment action that requires credentials from Jenkins. A naive approach might be to add my credentials to the global Jenkins credential store (call it {{deployKey}} as its id); then I can reference it in my pipeline later like so:

             
            {code:java}
            node {
              // ...
              stage('Deploy') {
                withCredentials([usernameColonPassword(credentialsId: 'deployKey', variable: 'DEPLOY_CREDENTIALS')]) {
                  sh 'curl -u "${DEPLOY_CREDENTIALS}" ...'
                }
              }
            }
            {code}
            But then I want to make my pipeline more secure. One option I might have is to have a dedicated Jenkins instance for running deployments. This is a _large_ maintenance burden (e.g., trusted.ci.j.io versus ci.j.io), so digging deeper into Jenkins' existing features, I find user-scoped credentials. I can simply use credentials attached to my user account instead of Jenkins itself, though in order for my pipeline to recognize and use these credentials, I find that I must make them parameterized to the build itself so that I can select my credentials upon starting a build (there's an alternative option using the Authorize Project plugin, but this plugin is not widely used). A neat side-effect of this functionality is that other users can also deploy using the same pipeline but with their own user-scoped credentials. This can also allow me the flexibility of using a default global credential and allowing users to override that specifically for a pipeline run.

            Thus, I modify my pipeline to add a build parameter for the deploy credentials as well as using a modified syntax in {{withCredentials}} to indicate that the {{credentialId}} parameter being provided is in fact a build parameter name and not the actual credential id. Combined, we get the following updated pipeline:
            {code:java}
            properties([parameters([credentials(credentialType: 'com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials', name: 'deployKey', required: true)])])
            node {
              // ...
              stage('Deploy') {
                withCredentials([usernameColonPassword(credentialsId: '${deployKey}', variable: 'DEPLOY_CREDENTIALS')]) {
                  sh 'curl -u "${DEPLOY_CREDENTIALS}" ...'
                }
              }
            }
            {code}
            Notice how we need to use a special syntax: {{credentialsId: '${deployKey}'}}. This syntax is misleading because it can be easily confused with the similar but not equivalent GString syntax: {{credentialsId: "${deployKey}"}}. In fact, this latter syntax would never work for a user-scoped credential because user-scoped credentials are currently only looked up if the credential id is provided using the former template syntax.

            What I am proposing we change here is to make credential parameters referenced by name take precedent over a globally-scoped credential with an id of the same name as the parameter name itself. Thus, we could update the above pipeline using this feature to be a little simpler:

             
            {code:java}
            properties([parameters([credentials(credentialType: 'com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials', name: 'deployKey', required: true)])])
            node {
              // ...
              stage('Deploy') {
              withCredentials([usernameColonPassword(credentialsId: 'deployKey', variable: 'DEPLOY_CREDENTIALS')]) {
                sh 'curl -u "${DEPLOY_CREDENTIALS}" ...'
              }
             }
            }
            {code}
            The beauty of this approach is that it allows me to go from the first pipeline to the third pipeline simply by updating my build to add a credential parameter with the same name as the credential id I was using before that I want to migrate to a user account. I don't even have to touch the rest of the pipeline!

            This issue relates to JENKINS-47699 in that it's a useful point to help support that feature.

             

             
            jvz Matt Sicker made changes -
            Link This issue relates to JENKINS-44772 [ JENKINS-44772 ]
            jvz Matt Sicker made changes -
            Link This issue relates to JENKINS-47699 [ JENKINS-47699 ]
            wfollonier Wadeck Follonier made changes -
            Link This issue relates to JENKINS-38963 [ JENKINS-38963 ]
            wfollonier Wadeck Follonier made changes -
            Link This issue relates to JENKINS-44774 [ JENKINS-44774 ]
            wfollonier Wadeck Follonier made changes -
            Link This issue relates to JENKINS-44773 [ JENKINS-44773 ]
            wfollonier Wadeck Follonier made changes -
            Link This issue relates to JENKINS-47864 [ JENKINS-47864 ]
            jvz Matt Sicker made changes -
            Resolution Fixed [ 1 ]
            Status In Review [ 10005 ] Fixed but Unreleased [ 10203 ]
            jvz Matt Sicker made changes -
            Released As credentials-2.3.0
            Status Fixed but Unreleased [ 10203 ] Resolved [ 5 ]

              People

              Assignee:
              jvz Matt Sicker
              Reporter:
              jvz Matt Sicker
              Votes:
              0 Vote for this issue
              Watchers:
              7 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved: