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

New API Token system should allow tokens to be created for service accounts

    XMLWordPrintable

Details

    • Improvement
    • Status: Closed (View Workflow)
    • Major
    • Resolution: Not A Defect
    • core
    • Jenkins 2.129

    Description

      Jenkins 2.129 introduced a new API token system (see Security Hardening: New API token system).

      The recommendation is for users to delete their existing (legacy) tokens, and replace them (if they are required) with a newly generated non-legacy token.

      However, I cannot do that for a service account that cannot log in. 

      • Previously, administrators could generate tokens on behalf of such users.
      • In 2.129+, an administrator can generate a new value for an existing legacy token, but cannot generate a new non-legacy token for a service user.

      Administrators should be able to generate a token for a service account.

       

       

      Attachments

        Activity

          oleg_nenashev Oleg Nenashev added a comment -

          We would need to introduce a new "Service account" type of users to the Jenkins core.

          I think it would be a nice improvement, but it may require  some design to implement it.

           

          oleg_nenashev Oleg Nenashev added a comment - We would need to introduce a new "Service account" type of users to the Jenkins core. I think it would be a nice improvement, but it may require  some design to implement it.  

          The alternative would be to allow Administrators to create a new-style token for any user - they can already do this for legacy tokens.

          Although of course there may be some security concerns about this, since an Administrator would then be able to pretend to be any use.

          mwebber Matthew Webber added a comment - The alternative would be to allow Administrators to create a new-style token for any user - they can already do this for legacy tokens. Although of course there may be some security concerns about this, since an Administrator would then be able to pretend to be any use.
          wfollonier Wadeck Follonier added a comment - - edited

          mwebber you can use the new system property that enable such behavior (not enabled by default because of security concerns you mentioned)

          jenkins.security.ApiTokenProperty.adminCanGenerateNewTokens="true"

          Also accessible by Groovy script, with jenkins.security.ApiTokenProperty.ADMIN_CAN_GENERATE_NEW_TOKENS = true.

          Information added in the wiki page about system properties.

          If you're ok with that, you can close this ticket.

          wfollonier Wadeck Follonier added a comment - - edited mwebber you can use the new system property that enable such behavior (not enabled by default because of security concerns you mentioned) jenkins.security.ApiTokenProperty.adminCanGenerateNewTokens="true" Also accessible by Groovy script, with jenkins.security.ApiTokenProperty.ADMIN_CAN_GENERATE_NEW_TOKENS = true . Information added in the wiki page about system properties . If you're ok with that, you can close this ticket.

          Thanks wfollonier, that works perfectly.

          mwebber Matthew Webber added a comment - Thanks wfollonier , that works perfectly.

          A system property already supports this.

          mwebber Matthew Webber added a comment - A system property already supports this.
          mthaddon Tom Haddon added a comment - - edited

          Is there any documentation on how to create a new API token using groovy? I'm trying to following (having run jenkins.security.ApiTokenProperty.ADMIN_CAN_GENERATE_NEW_TOKENS = true per above and got 'Result: true\n' for that):

          user = hudson.model.User.get('admin')
          prop = user.getProperty(jenkins.security.ApiTokenProperty.class)
          println(prop.getApiToken())

          But I get 'This user currently does not have a legacy token\n'. Obviously I don't want a legacy token, I'm just not sure how to get a new token.

          Hope it's okay to comment on a closed issue, thought it was better than opening a new one, but let me know if you'd prefer that.

           

          mthaddon Tom Haddon added a comment - - edited Is there any documentation on how to create a new API token using groovy? I'm trying to following (having run jenkins.security.ApiTokenProperty.ADMIN_CAN_GENERATE_NEW_TOKENS = true per above and got 'Result: true\n' for that): user = hudson.model.User.get('admin') prop = user.getProperty(jenkins.security.ApiTokenProperty.class) println(prop.getApiToken()) But I get 'This user currently does not have a legacy token\n'. Obviously I don't want a legacy token, I'm just not sure how to get a new token. Hope it's okay to comment on a closed issue, thought it was better than opening a new one, but let me know if you'd prefer that.  

          Hello mthaddon

          Commenting on a closed ticket exposes the risk to be completely ignored The best way to ask your question is on IRC I think.

          Anyway, here is a way to generate API Token for a user, using Groovy script console:

          import hudson.model.*
          import jenkins.model.*
          import jenkins.security.*
          import jenkins.security.apitoken.*
          
          // you can change the "admin" name
          // the false is to explicitely ask to not create a user who does not exist yet
          def user = User.get("admin", false)
          def prop = user.getProperty(ApiTokenProperty.class)
          // the name is up to you
          def result = prop.tokenStore.generateNewToken("token-created-by-script")
          user.save()
          
          return result.plainValue
          
          wfollonier Wadeck Follonier added a comment - Hello mthaddon Commenting on a closed ticket exposes the risk to be completely ignored The best way to ask your question is on IRC I think. Anyway, here is a way to generate API Token for a user, using Groovy script console: import hudson.model.* import jenkins.model.* import jenkins.security.* import jenkins.security.apitoken.* // you can change the "admin" name // the false is to explicitely ask to not create a user who does not exist yet def user = User.get( "admin" , false ) def prop = user.getProperty(ApiTokenProperty.class) // the name is up to you def result = prop.tokenStore.generateNewToken( "token-created-by-script" ) user.save() return result.plainValue
          mthaddon Tom Haddon added a comment -

          Great, thanks!

           

          Will use IRC next time

          mthaddon Tom Haddon added a comment - Great, thanks!   Will use IRC next time
          timblaktu Tim Black added a comment -

          FWIW, here's an alternative solution that uses a pre-determined token, which is useful for automated jenkins deployments when you really want to "bake in" a token a priori. Note this is from an ansible/jinja2 template, which explains the {{{{ var }}}} syntax. 
           
           

          # NOTE: Don't use > yaml multiline formatting as this will squash newlines into spaces. Use | instead.
          groovy:
            # Generate API Token for Jenkins Admin user. This is used by automated configuration/validation tasks
            # Solution from here: https://issues.jenkins.io/browse/JENKINS-52339
            # ..and here: https://support.cloudbees.com/hc/en-us/articles/115003090592-How-to-re-generate-my-Jenkins-user-token#programmaticallycreatingatoken
            - script: |
            println("Inline Groovy Script: Enter..");
            import hudson.model.*;
            import jenkins.model.*;
            import jenkins.security.*;
            import jenkins.security.apitoken.*;
            
            def userName = "{{ jenkins_admin_active_directory_username }}";
            def tokenName = "{{ jenkins_admin_active_directory_jenkins_api_token_name }}";
            def tokenUuid = "{{ jenkins_admin_active_directory_jenkins_api_token }}";
            def user = User.get("$userName", false);
            def prop = user.getProperty(ApiTokenProperty.class);
            if (prop.tokenStore.findMatchingToken("$tokenUuid") != null) {
              println("Inline Groovy Script: Token with name $tokenName already exists.");
            } 
            else {
              println("Inline Groovy Script: Token with name $tokenName does NOT exist. Creating it...");
              def newTokenString = prop.tokenStore.addFixedNewToken(tokenName, tokenUuid);
              println("Inline Groovy Script: Added New Fixed Token '$newTokenString' in Property Token Store");
              user.save();
              println("Inline Groovy Script: Created API token for $userName.");
            }
            println("Inline Groovy Script: Exiting..")            

           
           

          timblaktu Tim Black added a comment - FWIW, here's an alternative solution that uses a pre-determined token, which is useful for automated jenkins deployments when you really want to "bake in" a token a priori. Note this is from an ansible/jinja2 template, which explains the {{{{ var }}}} syntax.      # NOTE: Don't use > yaml multiline formatting as this will squash newlines into spaces. Use | instead. groovy: # Generate API Token for Jenkins Admin user. This is used by automated configuration/validation tasks # Solution from here: https: //issues.jenkins.io/browse/JENKINS-52339 # ..and here: https: //support.cloudbees.com/hc/en-us/articles/115003090592-How-to-re-generate-my-Jenkins-user-token#programmaticallycreatingatoken - script: | println( "Inline Groovy Script: Enter.." ); import hudson.model.*; import jenkins.model.*; import jenkins.security.*; import jenkins.security.apitoken.*; def userName = "{{ jenkins_admin_active_directory_username }}" ; def tokenName = "{{ jenkins_admin_active_directory_jenkins_api_token_name }}" ; def tokenUuid = "{{ jenkins_admin_active_directory_jenkins_api_token }}" ; def user = User.get( "$userName" , false ); def prop = user.getProperty(ApiTokenProperty.class); if (prop.tokenStore.findMatchingToken( "$tokenUuid" ) != null ) { println( "Inline Groovy Script: Token with name $tokenName already exists." ); } else { println( "Inline Groovy Script: Token with name $tokenName does NOT exist. Creating it..." ); def newTokenString = prop.tokenStore.addFixedNewToken(tokenName, tokenUuid); println( "Inline Groovy Script: Added New Fixed Token '$newTokenString' in Property Token Store" ); user.save(); println( "Inline Groovy Script: Created API token for $userName." ); } println( "Inline Groovy Script: Exiting.." )        

          People

            wfollonier Wadeck Follonier
            mwebber Matthew Webber
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: