在代码中绕过脚本安全插件

时间:2018-01-09 14:59:36

标签: jenkins groovy jenkins-pipeline

我已经编写了一些用于Jenkins管道的常规实用程序方法。一个简单的例子是:

// src/org/package/utils.groovy
def remove_file(String file) {
  new File(file).delete()
}

但是,正如预期的那样,这会在Jenkins管道中抛出以下异常:

  

org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException:脚本不允许使用新的java.io.File java.lang.String

根据Script Security Plugin documentation,我可以使用@Whitelisted方法注释绕过这个:

@Whitelisted
def remove_file(String file) {
  new File(file).delete()
}

根据这个question,我可以使用@NonCPS注释绕过这个(当然,此方法中的代码无需进行序列化):

@NonCPS
def remove_file(String file) {
  new File(file).delete()
}

共享库正在全局变量中使用,如下所示:

// vars/var.groovy
import org.package.utils

def method(body) {
  ...
  new utils().remove_file('file')
  ...
}

正在导入库/ var并在Jenkins文件中使用,如下所示:

library identifier: 'lib@master', retriever: modernSCM(
  [$class: 'GitSCMSource',
   remote: 'https://github.com/org/repo.git'])

pipeline {
  ...
  script {
    var.method {
      // body
    }
  }
  ...
}

在列出的基于代码的导入Pipeline共享库的方法中,我上面使用的方法似乎是唯一一个实际使用标准Jenkins Pipeline插件集的方法,所以这就是我采用的方法。

这两个都未能绕过Jenkins Pipeline抛出的安全异常。什么是使用代码绕过脚本安全插件并避免手动/人为错误解决方案(如修改GUI中的白名单)的最佳解决方案?

2 个答案:

答案 0 :(得分:4)

根据您上次发表的评论以及您对原始问题的更新,我想我了解情况。我将尝试澄清全球共享图书馆和"常规图书馆之间的差异。共享库。

注册图书馆

在撰写本文时,有一些不同的方法可以向Jenkins实例注册共享库

  1. Globally to the Jenkins installation - 可用于所有管道执行。
  2. For a Folder - 文件夹中的所有项目都有可用的管道库
  3. Automatic libraries - 文档中提供的示例是GitHub Branch Source Plugin
  4. 图书馆注册需要一些设置:

    • 名称 - 与library步骤和@Library注释一起使用的库名称
    • 默认版本 - 提交,分支,标记或其他标识符
    • 隐式加载 - 如果应隐式加载库(即消费者不需要使用@Librarylibrary
    • 检索方法 - 如何检索SCM(与Git或Mercurial一样)
      • 允许覆盖默认版本 - 如果使用者可以使用与默认版本不同的版本

    上面引用的Global Shared Libraries会绕过安全沙箱,这意味着他们可以执行任何。以下是一些相关的documentation

      

    由于这些库可以全局使用,因此系统中的任何管道都可以利用这些库中实现的功能。

         

    这些图书馆被视为"信任:"他们可以在Java,Groovy,Jenkins内部API,Jenkins插件或第三方库中运行任何方法。这允许您定义库,这些库将单独的不安全API封装在更高级别的包装器中,以便从任何管道中使用。请注意任何能够推送到此SCM存储库的人都可以无限制地访问Jenkins。您需要Overall/RunScripts权限才能配置这些库(通常这将授予Jenkins管理员)。 / p>

    这些是唯一绕过安全沙箱检查的管道库。其他类型(如文件夹库)已进行安全检查,这意味着您无法执行new File(file).delete()之类的操作。

    消耗

    在管道中,用户可以访问所有可用的共享库。安装或文件夹中隐式加载的库(以上称为 Load implicitly 选项)在脚本类路径中自动可用。必须使用@Library注释或library步骤引入其他库。

    例如,假设您有一个包含以下选项的库:

    • 姓名 - my-shared-lib
    • 默认版本 - master
    • 隐式加载 - false
    • 检索方法 - 将现代Git SCM设置到某个存储库
      • 允许覆盖默认版本 - true

    您可以从管道中获取此特定库的几种方法是:

    • @Library('my-shared-lib') - 使用默认版本
    • @Library('my-shared-lib@develop') - 使用已覆盖的develop
    • 版本
    • `def myLib = library(' my-shared-lib') - 使用默认版本
    • def myLib = library('my-shared-lib@develop') - 使用已覆盖的develop
    • 版本

    这些都将使用先前配置的库。

    在您的示例中,您使用的是library步骤,但是从远程存储库动态加载而不是使用注册版本。这也是您指定retriever参数的原因。请记住,任何非全局共享库 的库都无法绕过安全沙箱

    如果您想让您的库绕过沙箱,您需要将其作为全局共享库以Jenkins安装注册。

    关于new File(file).delete()的附注 - 小心写作"正常" Jenkins管道内部的Groovy(有关详细原因,请参阅此answer。)

答案 1 :(得分:0)

如果您使用Jenkins Job-DSL以编程方式创建作业,则可以使用以下代码自动批准管道脚本。

 def pipelineScript = 'your pipeline script'

 def scriptApproval = Jenkins.instance.getExtensionList('org.jenkinsci.plugins.scriptsecurity.scripts.ScriptApproval')[0]

 scriptApproval.approveScript(scriptApproval.hash(pipelineScript, 'groovy'))

 script pipelineScript

此代码可能需要一些调整,具体取决于您使用的插件的版本,否则这应该会使事情变得更容易。

和平!