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

Job DSL support for ScriptApproval (was: Groovy postbuild ignores RUN_SCRIPTS permission)

    • Icon: New Feature New Feature
    • Resolution: Unresolved
    • Icon: Major Major
    • Ubuntu 14.04 LTS
      OpenJDK 1.7 IcedTea
      Jenkins ver. 1.635
      Script Security Plugin 1.15
      Groovy Postbuild 2.2.2

      The Groovy Postbuild plugin doesn't appear to adhere to the Jenkins.RUN_SCRIPTS permission at all. I'm generating jobs using the Job DSL Plugin and have given the RUN_SCRIPTS permission to the anonymous user.

      When I generate scripts this plugin still requires the script approval.

      Authentication is enabled but anonymous is given RunScripts permission. I would assume anybody would be able to create scripts (including the Job DSL plugin generated jobs).

          [JENKINS-31201] Job DSL support for ScriptApproval (was: Groovy postbuild ignores RUN_SCRIPTS permission)

          Sam Gleske added a comment -

          I reverse engineered how script approvals work and automatically approve scripts via hashing. I didn't have time for an upstream fix for this and I don't know what it would be.

          Sam Gleske added a comment - I reverse engineered how script approvals work and automatically approve scripts via hashing. I didn't have time for an upstream fix for this and I don't know what it would be.

          Sam Gleske added a comment -

          The workaround in Job DSL is essentially:

          job {
              publishers {
                  def groovyscript = "some groovy script"
                  groovyPostBuild(groovyscript, Behavior.DoNothing)
                  //pre-approve post-build groovy scripts
                  def scriptApproval = Jenkins.instance.getExtensionList('org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval')[0]
                  scriptApproval.approveScript(scriptApproval.hash(groovyscript, 'groovy'))
              }
          }
          

          Sam Gleske added a comment - The workaround in Job DSL is essentially: job { publishers { def groovyscript = "some groovy script" groovyPostBuild(groovyscript, Behavior.DoNothing) //pre-approve post-build groovy scripts def scriptApproval = Jenkins.instance.getExtensionList( 'org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval' )[0] scriptApproval.approveScript(scriptApproval.hash(groovyscript, 'groovy' )) } }

          Jesse Glick added a comment -

          Nothing specific to Groovy Postbuild.

          The originally reported situation is nonsensical. If you granted anonymous RUN_SCRIPTS, it is game over—you may as well disable security altogether, in which case script approval is also skipped.

          The legitimate case is that you have a secured instance in which there is an administrator who is the only one able to create jobs (otherwise anyone could create a Job DSL project), and you want projects created via DSL to have their scripts preapproved. This probably requires a new API in script-security and a call to that API from job-dsl in createNewItem/updateExistingItem to associate a user with the item. (Really that user ought to have been defined by the authorize-project plugin, and job-dsl itself should use script-security to guard scripts. As written, it can only be used in an installation in which no one other than superusers have any write permissions, and all SCM commits are trusted.)

          Jesse Glick added a comment - Nothing specific to Groovy Postbuild. The originally reported situation is nonsensical. If you granted anonymous RUN_SCRIPTS , it is game over—you may as well disable security altogether, in which case script approval is also skipped. The legitimate case is that you have a secured instance in which there is an administrator who is the only one able to create jobs (otherwise anyone could create a Job DSL project), and you want projects created via DSL to have their scripts preapproved. This probably requires a new API in script-security and a call to that API from job-dsl in createNewItem / updateExistingItem to associate a user with the item. (Really that user ought to have been defined by the authorize-project plugin, and job-dsl itself should use script-security to guard scripts. As written, it can only be used in an installation in which no one other than superusers have any write permissions, and all SCM commits are trusted.)

          Sam Gleske added a comment -

          That's precisely our setup. Everything is programmatically generated and there are no human super users.

          Sam Gleske added a comment - That's precisely our setup. Everything is programmatically generated and there are no human super users.

          Sam Gleske added a comment - - edited

          I essentially wanted to disable the script security plugin and giving Anonymous RUN_SCRIPTS permission is how I could accomplish that. However, the Groovy postbuild plugin doesn't even adhere to the runscripts permission. I still think that's a bug in the groovy postbuild plugin. Though, the Job DSL plugin could support it.

          In any case, I already pre-approve the scripts so this no longer is blocking me.

          Sam Gleske added a comment - - edited I essentially wanted to disable the script security plugin and giving Anonymous RUN_SCRIPTS permission is how I could accomplish that. However, the Groovy postbuild plugin doesn't even adhere to the runscripts permission. I still think that's a bug in the groovy postbuild plugin. Though, the Job DSL plugin could support it. In any case, I already pre-approve the scripts so this no longer is blocking me.

          Jesse Glick added a comment -

          the Groovy postbuild plugin doesn't even adhere to the runscripts permission

          For whole-script approval mode (as opposed to the Groovy sandbox), RUN_SCRIPTS is checked only for actual users who are saving a configuration—or anonymous if somehow the configuration is being saved from a web request made by a non-logged-in user. If a job definition comes out of the blue, as it seems to when Job DSL creates it without any further API calls, there is no information available to decide whether or not it should be trusted. Granting RUN_SCRIPTS to anonymous, besides disabling all security on your instance, would not accomplish that because there is not even a recognized event during which to check the security context.

          As I say, the only way to make this work is to offer a new API which Job DSL could call to signify that it is intentionally creating or overwriting job configuration on behalf of an unidentified but fully trusted agent.

          Jesse Glick added a comment - the Groovy postbuild plugin doesn't even adhere to the runscripts permission For whole-script approval mode (as opposed to the Groovy sandbox), RUN_SCRIPTS is checked only for actual users who are saving a configuration—or anonymous if somehow the configuration is being saved from a web request made by a non-logged-in user. If a job definition comes out of the blue, as it seems to when Job DSL creates it without any further API calls, there is no information available to decide whether or not it should be trusted. Granting RUN_SCRIPTS to anonymous , besides disabling all security on your instance, would not accomplish that because there is not even a recognized event during which to check the security context. As I say, the only way to make this work is to offer a new API which Job DSL could call to signify that it is intentionally creating or overwriting job configuration on behalf of an unidentified but fully trusted agent.

          Any ideas when a solution for this issue could be worked on?

          My project is now building many pipelines where the starting job uses the Active Choices Param plugin with complex groovy scripts. Without some solution to this problem, we are stuck manually approving all (60+) scripts that are all near identical.

          Jeffrey Nelson added a comment - Any ideas when a solution for this issue could be worked on? My project is now building many pipelines where the starting job uses the Active Choices Param plugin with complex groovy scripts. Without some solution to this problem, we are stuck manually approving all (60+) scripts that are all near identical.

          Figured out a workaround.

          1. Write a scriptler script that approves pending scripts with a given identifier (we used something simple like '// SCRIPTLER AUTO APPROVE'). Thanks to sag47 for pointing me to the code to approve a script.

          – Select 'Permission' option (Allow execution by user with RunScripts permission)
          – Select 'Restriction' option (Script is always executed on Master)
          – Script parameter: "approvalPrefix"

          – Script:

          import jenkins.model.Jenkins
          
          def scriptApproval = Jenkins.instance.getExtensionList('org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval')[0]
          def hashesToApprove = scriptApproval.pendingScripts.findAll{ it.script.startsWith(approvalPrefix) }.collect{ it.getHash() }
          hashesToApprove.each { 
            scriptApproval.approveScript(it)
          }

          2. use the configure UI to add another step to your DSL seed job to run this scriptler script created in step 1
          3. add '// SCRIPTLER AUTO APPROVE' at the top of any groovy script you want approved

          If your DSL seed job is itself configured by DSL, complete steps 4-5
          4. look at the config.xml generated by manually adding this step to your DSL seed job. You need to grab the value in the 'builderId' field
          5. add the additional step to your DSL seed job to run this scriptler script created above in step 1.
          – dsl for adding the step:

                def approvePrefixParam = {
                  name 'approvalPrefix'
                  value '// SCRIPTLER AUTO APPROVE'
                }
                
                configure { project ->
                  project / builders << 'org.jenkinsci.plugins.scriptler.builder.ScriptlerBuilder' {
                    builderId(<builderIdGrabbedFromXml>)
                    scriptId('approveGroovyScripts.groovy')
                    propagateParams(false)
                    parameters {
                      'org.jenkinsci.plugins.scriptler.config.Parameter' approvePrefixParam
                    }
                  }
                }

          Jeffrey Nelson added a comment - Figured out a workaround. 1. Write a scriptler script that approves pending scripts with a given identifier (we used something simple like '// SCRIPTLER AUTO APPROVE'). Thanks to sag47 for pointing me to the code to approve a script. – Select 'Permission' option (Allow execution by user with RunScripts permission) – Select 'Restriction' option (Script is always executed on Master) – Script parameter: "approvalPrefix" – Script: import jenkins.model.Jenkins def scriptApproval = Jenkins.instance.getExtensionList( 'org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval' )[0] def hashesToApprove = scriptApproval.pendingScripts.findAll{ it.script.startsWith(approvalPrefix) }.collect{ it.getHash() } hashesToApprove.each { scriptApproval.approveScript(it) } 2. use the configure UI to add another step to your DSL seed job to run this scriptler script created in step 1 3. add '// SCRIPTLER AUTO APPROVE' at the top of any groovy script you want approved If your DSL seed job is itself configured by DSL, complete steps 4-5 4. look at the config.xml generated by manually adding this step to your DSL seed job. You need to grab the value in the 'builderId' field 5. add the additional step to your DSL seed job to run this scriptler script created above in step 1. – dsl for adding the step: def approvePrefixParam = { name 'approvalPrefix' value ' // SCRIPTLER AUTO APPROVE' } configure { project -> project / builders << 'org.jenkinsci.plugins.scriptler.builder.ScriptlerBuilder' { builderId(<builderIdGrabbedFromXml>) scriptId( 'approveGroovyScripts.groovy' ) propagateParams( false ) parameters { 'org.jenkinsci.plugins.scriptler.config.Parameter' approvePrefixParam } } }

          Jesse Glick added a comment -

          Without some solution to this problem

          Best to switch to using the Groovy sandbox, which bypasses all these issues.

          Jesse Glick added a comment - Without some solution to this problem Best to switch to using the Groovy sandbox, which bypasses all these issues.

          Jesse Glick added a comment -

          Probably the way this should work is that you should use the Authorize Project plugin to associate an (administrative) authentication with the DSL job. Then whatever in Job DSL actually writes out the job definition and loads it—this seems to be JenkinsJobManagement.createOrUpdateConfig—just needs to be run as part of that authentication, which I think is already the case. That would suffice for ScriptApproval.configuring to associate the script with the administrator. Needs to be tested, but it is possible this already works without code changes, if your system is properly configured.

          Jesse Glick added a comment - Probably the way this should work is that you should use the Authorize Project plugin to associate an (administrative) authentication with the DSL job. Then whatever in Job DSL actually writes out the job definition and loads it—this seems to be JenkinsJobManagement.createOrUpdateConfig —just needs to be run as part of that authentication, which I think is already the case. That would suffice for ScriptApproval.configuring to associate the script with the administrator. Needs to be tested, but it is possible this already works without code changes, if your system is properly configured.

            Unassigned Unassigned
            sag47 Sam Gleske
            Votes:
            7 Vote for this issue
            Watchers:
            12 Start watching this issue

              Created:
              Updated: