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

Option to disable sandbox in CpsScmFlowDefinition

      Currently CpsScmFlowDefinition enforces sandbox mode on the grounds that whole-script approval is unrealistic (an administrator would need to approve every SCM revision, and Jenkins cannot automatically approve revisions like it could from GUI changes to a CpsFlowDefinition by an administrator).

      There should however be an option to simply trust the script as it comes from the SCM. This could be checked by default if Jenkins were unsecured; for a secured Jenkins, the default should remain to use the sandbox, though you could switch to trusted mode with a stern warning in form validation explaining that you are responsible for auditing all changes to that SCM repository, and noting that attackers with SCM access could take over control of Jenkins in ways that might make auditing difficult. (For example, someone with push access to a Git repository could push a script which obtains the API token of a legitimate Jenkins administrator, mails it to the attacker, then deletes the current build record; and finally force-push the attacking script out of existence except via the reflog.)

      Pending such an option, the workaround is given by the tutorial here: define a CpsFlowDefinition with an approved script that checks out the SCM and uses load to run it. This has the same effect at the price of more awkward configuration.

          [JENKINS-28178] Option to disable sandbox in CpsScmFlowDefinition

          Jesse Glick created issue -

          Lars Bilke added a comment - - edited

          Can you please explain the workaround in more detail? I tried to use the method you mention in the tutorial:

          def flow
          node('slave') {
              git '…'
              flow = load 'flow.groovy'
              flow.devQAStaging()
          }
          flow.production()
          

          But flow.groovy is still run in the sandbox.

          Lars Bilke added a comment - - edited Can you please explain the workaround in more detail? I tried to use the method you mention in the tutorial: def flow node( 'slave' ) { git '…' flow = load 'flow.groovy' flow.devQAStaging() } flow.production() But flow.groovy is still run in the sandbox.

          Jesse Glick added a comment -

          bilke well of course you would need to uncheck the sandbox option in the job configuration too.

          Jesse Glick added a comment - bilke well of course you would need to uncheck the sandbox option in the job configuration too.

          Lars Bilke added a comment -

          Is this also possible on a Workflow Multibranch job?

          Lars Bilke added a comment - Is this also possible on a Workflow Multibranch job?

          Jesse Glick added a comment -

          No, currently multibranch does reuse CpsScmFlowDefinition including the forced sandbox=true.

          Probably this needs to be handled in that case using a BranchProperty: while you may be comfortable permitting unsandboxed execution from Jenkinsfile contents in origin/master, you would not want to allow that in a pull request on a publicly visible repository. TBD what the policy should be: allowing a customized Jenkinsfile if run in the sandbox, or just pinning a Jenkinsfile from the base branch you control.

          In other words, I consider the multibranch variant of this issue to be distinct.

          Jesse Glick added a comment - No, currently multibranch does reuse CpsScmFlowDefinition including the forced sandbox=true . Probably this needs to be handled in that case using a BranchProperty : while you may be comfortable permitting unsandboxed execution from Jenkinsfile contents in origin/master , you would not want to allow that in a pull request on a publicly visible repository. TBD what the policy should be: allowing a customized Jenkinsfile if run in the sandbox, or just pinning a Jenkinsfile from the base branch you control. In other words, I consider the multibranch variant of this issue to be distinct.

          Ed Holzwarth added a comment - - edited

          jglick Is there a separate issue created for the multibranch variant of this issue?

          We are using the global workflow library. The setup was working with a standard workflow, but getting a RejectedAccessException after switching to the multibranch workflow which I understand from your comment forces groovy sandbox mode.

          org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: unclassified field java.util.ArrayList name
          	at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.rejectField(SandboxInterceptor.java:182)
          	at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onGetProperty(SandboxInterceptor.java:174)
          
          

          How can I authorize the properties our workflow expects so that we can use the multibranch workflow?

          Ed Holzwarth added a comment - - edited jglick Is there a separate issue created for the multibranch variant of this issue? We are using the global workflow library. The setup was working with a standard workflow, but getting a RejectedAccessException after switching to the multibranch workflow which I understand from your comment forces groovy sandbox mode. org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: unclassified field java.util.ArrayList name at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.rejectField(SandboxInterceptor.java:182) at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onGetProperty(SandboxInterceptor.java:174) How can I authorize the properties our workflow expects so that we can use the multibranch workflow?

          Ed Holzwarth added a comment -

          PS - the lines that trigger RejectedAccessExceptions with our multibranch workflow are as follows:

          	// triggers RejectedAccessException: unclassified field java.util.ArrayList name
          	def choices = skipDeploy + "\n" config.deployTargets*.name.join('\n') 
          

          and

          	// triggers RejectedAccessException: Scripts not permitted to use staticMethod 
          	def selectedTarget = config.deployTargets.findResult({ it.name == deployTarget ? it : null })
          

          Ed Holzwarth added a comment - PS - the lines that trigger RejectedAccessExceptions with our multibranch workflow are as follows: // triggers RejectedAccessException: unclassified field java.util.ArrayList name def choices = skipDeploy + "\n" config.deployTargets*.name.join( '\n' ) and // triggers RejectedAccessException: Scripts not permitted to use staticMethod def selectedTarget = config.deployTargets.findResult({ it.name == deployTarget ? it : null })
          Ed Holzwarth made changes -
          Priority Original: Minor [ 4 ] New: Major [ 3 ]

          Jesse Glick added a comment -

          The ArrayList.name issue sounds like a bug in script-security which was already fixed; make sure you are running the latest version. It is possible you found a new corner case, of course. Best to report it in a separate issue with a standalone reproducible test case if you can come up with one.

          findResult could be whitelisted but it is probably not going to work anyway (with or without the sandbox) due to JENKINS-26481. At least I am guessing that is the method you are talking about; the exception message was apparently truncated.

          Jesse Glick added a comment - The ArrayList.name issue sounds like a bug in script-security which was already fixed; make sure you are running the latest version. It is possible you found a new corner case, of course. Best to report it in a separate issue with a standalone reproducible test case if you can come up with one. findResult could be whitelisted but it is probably not going to work anyway (with or without the sandbox) due to JENKINS-26481 . At least I am guessing that is the method you are talking about; the exception message was apparently truncated.
          Jesse Glick made changes -
          Description Original: Currently {{CpsScmFlowDefinition}} enforces sandbox mode on the grounds that whole-script approval is unrealistic (an administrator would need to approve every SCM revision, and Jenkins cannot automatically approve revisions like it could from GUI changes to a {{CpsFlowDefinition}} by an administrator).

          There should however be an option to simply trust the script as it comes from the SCM. This could be checked by default if Jenkins were unsecured; for a secured Jenkins, the default should remain to use the sandbox, though you could switch to trusted mode with a stern warning in form validation explaining that you are responsible for auditing all changes to that SCM repository, and noting that attackers with SCM access could take over control of Jenkins in ways that might make auditing difficult. (For example, someone with push access to a Git repository could push a script which obtains the API token of a legitimate Jenkins administrator, mails it to the attacker, then deletes the current build record; and finally force-push the attacking script out of existence except via the reflog.)

          Pending such an option, the workaround is given by the tutorial [here|https://github.com/jenkinsci/workflow-plugin/blob/master/TUTORIAL.md#manual-loading]: define a {{CpsFlowDefinition}} with an approved script that checks out the SCM and uses {{load}} to run it. This has the same effect at the price of more awkward configuration.
          New: Currently {{CpsScmFlowDefinition}} enforces sandbox mode on the grounds that whole-script approval is unrealistic (an administrator would need to approve every SCM revision, and Jenkins cannot automatically approve revisions like it could from GUI changes to a {{CpsFlowDefinition}} by an administrator).

          There should however be an option to simply trust the script as it comes from the SCM. This could be checked by default if Jenkins were unsecured; for a secured Jenkins, the default should remain to use the sandbox, though you could switch to trusted mode with a stern warning in form validation explaining that you are responsible for auditing all changes to that SCM repository, and noting that attackers with SCM access could take over control of Jenkins in ways that might make auditing difficult. (For example, someone with push access to a Git repository could push a script which obtains the API token of a legitimate Jenkins administrator, mails it to the attacker, then deletes the current build record; and finally force-push the attacking script out of existence except via the reflog.)

          Pending such an option, the workaround is given by the tutorial [here|https://github.com/jenkinsci/workflow-plugin/blob/master/TUTORIAL.md#triggering-manual-loading]: define a {{CpsFlowDefinition}} with an approved script that checks out the SCM and uses {{load}} to run it. This has the same effect at the price of more awkward configuration.

            Unassigned Unassigned
            jglick Jesse Glick
            Votes:
            122 Vote for this issue
            Watchers:
            118 Start watching this issue

              Created:
              Updated: