Jenkins CI 管道脚本不允许使用方法 groovy.lang.GroovyObject

Posted

技术标签:

【中文标题】Jenkins CI 管道脚本不允许使用方法 groovy.lang.GroovyObject【英文标题】:Jenkins CI Pipeline Scripts not permitted to use method groovy.lang.GroovyObject 【发布时间】:2016-11-11 14:15:47 【问题描述】:

我正在使用 Jenkins 2 编译 Java 项目,我想从 pom.xml 中读取版本,我正在关注这个示例:

https://github.com/jenkinsci/pipeline-plugin/blob/master/TUTORIAL.md

例子建议:

访问文件系统似乎存在一些安全问题,但我无法弄清楚它给出了什么(或为什么)这个问题:

我只是做了一些与示例不同的操作:

def version() 
    String path = pwd();
    def matcher = readFile("$path/pom.xml") =~ '<version>(.+)</version>'
    return matcher ? matcher[0][1] : null

运行“版本”方法时遇到的错误:

org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: Scripts not permitted to use method groovy.lang.GroovyObject invokeMethod java.lang.String java.lang.Object (org.codehaus.groovy.runtime.GStringImpl call org.codehaus.groovy.runtime.GStringImpl)
    at org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.StaticWhitelist.rejectMethod(StaticWhitelist.java:165)
    at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onMethodCall(SandboxInterceptor.java:117)
    at org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SandboxInterceptor.onMethodCall(SandboxInterceptor.java:103)
    at org.kohsuke.groovy.sandbox.impl.Checker$1.call(Checker.java:149)
    at org.kohsuke.groovy.sandbox.impl.Checker.checkedCall(Checker.java:146)
    at com.cloudbees.groovy.cps.sandbox.SandboxInvoker.methodCall(SandboxInvoker.java:15)
    at WorkflowScript.run(WorkflowScript:71)
    at ___cps.transform___(Native Method)
    at com.cloudbees.groovy.cps.impl.ContinuationGroup.methodCall(ContinuationGroup.java:55)
    at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:106)
    at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixArg(FunctionCallBlock.java:79)
    at sun.reflect.GeneratedMethodAccessor408.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72)
    at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:100)
    at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixArg(FunctionCallBlock.java:79)
    at sun.reflect.GeneratedMethodAccessor408.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at com.cloudbees.groovy.cps.impl.ContinuationPtr$ContinuationImpl.receive(ContinuationPtr.java:72)
    at com.cloudbees.groovy.cps.impl.ContinuationGroup.methodCall(ContinuationGroup.java:57)
    at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.dispatchOrArg(FunctionCallBlock.java:106)
    at com.cloudbees.groovy.cps.impl.FunctionCallBlock$ContinuationImpl.fixArg(FunctionCallBlock.java:79)
    at sun.reflect.GeneratedMethodAccessor408.invoke(Unknown Source)

我正在使用这些版本: 插件管道 2.1 詹金斯 2.2

【问题讨论】:

我有一个关于Scripts not permitted to use method 的类似错误,但它发生是因为我写了scm 'checkout' 而不是checkou scm。以防万一有人遇到这种情况,请注意语法错误:)。正如 Maarten Kieft 所说的那样,我可以看到关于错误命令的更清晰的错误消息:) 我强烈建议不要使用 Regex 来解析 XML。不仅是我,还有 4413+ You can't parse [X]html with regex. 加上 47+ Why is it such a bad idea to parse XML with regex? 其他人。现在有better 解决方案herein,无需管理员批准甚至完全禁用安全功能。 【参考方案1】:

快速修复解决方案:

我遇到了类似的问题,我通过以下方式解决了它

    导航到 jenkins > 管理 jenkins > 进程内脚本批准 有一个待处理的命令,我必须批准。



备选方案 1:禁用沙盒

正如article 深入解释的那样,groovy 脚本默认在沙盒模式下运行。这意味着允许运行 groovy 方法的子集而无需管理员批准。也可以在非沙盒模式下运行脚本,这意味着整个脚本需要立即得到管理员的批准。这会阻止用户当时批准每一行。

可以通过在脚本下方的项目配置中取消选中此复选框来在没有沙箱的情况下运行脚本:

备选方案 2:禁用脚本安全

正如article 所解释的,也可以完全禁用脚本安全性。首先安装permissive script security plugin,然后更改您的 jenkins.xml 文件添加此参数:

-Dpermissive-script-security.enabled=true

所以你的 jenkins.xml 看起来像这样:

<executable>..bin\java</executable>
<arguments>-Dpermissive-script-security.enabled=true -Xrs -Xmx4096m -Dhudson.lifecycle=hudson.lifecycle.WindowsServiceLifecycle -jar "%BASE%\jenkins.war" --httpPort=80 --webroot="%BASE%\war"</arguments>

如果你实现了这个,请确保你知道你在做什么!

【讨论】:

如果批准整个脚本更好,取决于团队结构。对于一些具有完全访问权限的开发人员来说,这非常好。但是,多个团队的设置将迫使管理员批准所有管道脚本中的每一项更改。 备选方案 3(应该是第一个建议)是更改有问题的未列入白名单的代码。在这种情况下,将@NonCPS 简单地用于Matcher 用法就足够了。在这种情况下,无需禁用整个管道的安全性,尤其是整个 Jenkins 安装。单独评估每个被阻止的呼叫,并决定您是否真的需要批准它们。 @mkobit 对我不起作用。 @NonCPS 没有帮助。 @mkobit 我用NonCPS 装饰了一个使用currentBuild.rawBuild.getCause(Cause.UserIdCause).getUserId() 的函数。根据我的阅读,NonCPS 根本无法解决安全问题。 @warvariuc 感谢您的回复。你是对的,@NonCPS 不会像that method is intentionally not whitelisted 那样帮助你。在某些情况下,您可能只想批准特定运行、单个作业、单个文件夹、单个方法等。还有一些其他方法可以解决有限的安全问题(例如使用全局共享库)。我试图指出禁用整个实例的安全性几乎肯定不是一个好主意。【参考方案2】:

您必须在作业配置中为 Groovy 禁用沙箱。

目前这对于 groovy 脚本来自 scm 的多分支项目是不可能的。欲了解更多信息,请参阅https://issues.jenkins-ci.org/browse/JENKINS-28178

【讨论】:

禁用沙箱是个糟糕的主意。这样做会让一个糟糕的演员,或者一个笨拙的人利用詹金斯做任何想做的事情,包括绕过安全性,窃取凭据......只是不要这样做!【参考方案3】:

当我将 userInput 中的用户输入参数数量从 3 个减少到 1 个时,我遇到了这个问题。这将 userInput 的可变输出类型从数组更改为原始类型。

例子:

myvar1 = userInput['param1']
myvar2 = userInput['param2']

到:

myvar = userInput

【讨论】:

这正是我遇到的症状的修复方法。错误消息是org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: Scripts not permitted to use method groovy.lang.GroovyObject invokeMethod java.lang.String java.lang.Object。该方法需要 2 个参数并且正在接收 3 个。【参考方案4】:

要绕过 SCM 存储的 Groovy 脚本的沙盒,我建议将脚本作为 Groovy 命令(而不是 Groovy 脚本文件)运行:

import hudson.FilePath
final GROOVY_SCRIPT = "workspace/relative/path/to/the/checked/out/groovy/script.groovy"

evaluate(new FilePath(build.workspace, GROOVY_SCRIPT).read().text)

在这种情况下,groovy 脚本会从工作区传输到 Jenkins Master,在那里它可以作为 system Groovy Script 执行。只要 Use Groovy Sandbox 未被选中,沙箱就会被抑制。

【讨论】:

这看起来很笨重,有风险,而且一定会回来咬你。 嗯,安全性很重要,尤其是在保护用户敏感数据时,但它也有代价,例如开发过程中的复杂性。当安全工具只实施了一半时,情况会变得更糟。 Jenkins 脚本沙盒是半实施安全工具的一个很好的例子,因此您可能需要完全禁用该功能,否则您将无法使用。 在我的情况下,从较旧的 Jenkins 升级后,我的 Groovy 脚本停止工作,使其工作的唯一方法是运行脚本 300 次(只是一个估计),每次运行单击 Jenkins UI 以允许在 200 行脚本中调用所有方法。此外,UI 不允许您粘贴所有允许的方法调用的完整列表,以防您能够以某种方式生成它们。此外,UI 停止显示一些方法调用,一段时间后我无法继续。【参考方案5】:

为了获取一个maven项目的版本,我通常在sh块中使用mvn二进制,如下所示。无需管理员权限。

stage("Compile") 
    steps 
       sh """
         mvn help:evaluate -Dexpression=project.version -q -DforceStdout > version.txt
       """
    

【讨论】:

+1 好主意!但是之后如何处理version.txt?无论如何,有no need for redirection and later text file handling。【参考方案6】:

按照 @JavaTechnical 的回答,可以将 Maven 项目的版本分配给变量:

stage("getPomProjectVersion") 
    steps 
       ...
       def pomProjectVersion = sh script: 'mvn help:evaluate -Dexpression=project.version -q -DforceStdout', returnStdout: true
       ...
    

【讨论】:

我同意这是阅读 pom 的最佳方式,至少在此时 jenkins.io/doc/pipeline/steps/pipeline-utility-steps/… 但他的帖子需要的是当我们需要使用自定义时如何跳过或绕过该安全约束或奇怪的代码

以上是关于Jenkins CI 管道脚本不允许使用方法 groovy.lang.GroovyObject的主要内容,如果未能解决你的问题,请参考以下文章

DEVOPS技术实践_08:Jenkins多分支管道

Gitlab CI中的实用工作独立于主要管道

@NonCPS在Jenkins管道脚本中的作用是什么?

如何在运行Jenkins CI管道时屏蔽作为用户输入传递的密码?

通过覆盖“检测到 CI,跳过 Git 挂钩安装”在 Jenkins 管道作业中安装 husky git 挂钩

如何在Kubernetes CI管道中使用Gradle守护程序?