安全模板

Azure DevOps Services |Azure DevOps Server |Azure DevOps Server 2022 |Azure DevOps Server 2020

Azure Pipelines 模板 允许在 YAML 管道中定义可重用的内容、逻辑和参数。 本文介绍模板如何增强管道安全性:

  • 定义管道的外部结构,以帮助防止恶意代码渗透。
  • 自动包括执行凭据扫描等任务的步骤。
  • 帮助对 受保护的资源执行检查,这些资源构成了 Azure Pipelines 的基本安全框架,并应用于所有管道结构和组件。

本文是一系列教程的一部分,可帮助实现 Azure Pipelines 的安全措施。 有关更多信息,请参阅 Secure Azure Pipelines 指南

先决条件

类别 要求
Azure DevOps - 在使 Azure DevOps 安全和保护 Azure Pipelines 中实施建议。
- 对 YAML 和 Azure Pipelines 的基本知识。 有关详细信息,请参阅 创建第一个管道
权限 - 修改管道权限: 项目管理员组的成员
- 要修改组织权限,必须是 项目集合管理员组 的成员。

包括和扩展模板

Azure Pipelines 提供包含扩展模板。

  • 引用includes模板时,会直接在外部文件中包含模板的代码,类似于C++中的#include。 以下示例管道会将 include-npm-steps.yml 模板插入到 steps 部分。

      steps:
      - template: templates/include-npm-steps.yml 
    
  • 模板 extends 定义管道的外部结构,并为目标自定义提供特定点。 在C++上下文中, extends 模板类似于继承。

使用 extends 模板时,还可以在 includes 模板和最终管道中执行常见配置部分。 有关详细信息,请参阅 在管道中使用 YAML 模板实现可重用和安全的进程

扩展模板

对于最安全的管道,首先使用 extends 模板。 这些模板定义管道的外部结构,并帮助防止恶意代码渗透。

以下示例显示了名为 template.yml的模板文件。

parameters:
- name: usersteps
  type: stepList
  default: []
steps:
- ${{ each step in parameters.usersteps }}:
  - ${{ step }}

以下示例管道扩展 template.yml 模板。

# azure-pipelines.yml
resources:
  repositories:
  - repository: templates
    type: git
    name: MyProject/MyTemplates
    ref: refs/tags/v1

extends:
  template: template.yml@templates
  parameters:
    usersteps:
    - script: echo This is my first step
    - script: echo This is my second step

提示

设置 extends 模板时,请考虑将它们定位到特定的 Git 分支或标记,以便任何破坏性变更都不会影响现有管道。 前面的示例使用此功能。

管道安全功能

YAML 管道语法包括多个内置保护。 Extends 模板可以强制使用,从而增强管道的安全性。 可以实施以下任何限制。

步骤目标

可以限制在容器而不是主机上运行的指定步骤。 容器中的步骤无法访问代理主机,因此无法修改代理配置或留下恶意代码供以后执行。

例如,可以在容器中运行用户步骤,以防止他们访问网络,因此他们无法从未经授权的源检索包或将代码和机密上传到外部位置。

以下示例管道在代理主机上运行一个可能更改主机网络的步骤,然后在一个限制网络访问的容器中运行另一个步骤。

resources:
  containers:
  - container: builder
    image: mysecurebuildcontainer:latest
steps:
- script: echo This step runs on the agent host
- script: echo This step runs inside the builder container
  target: builder

类型安全参数

在管道运行之前,模板及其参数将转换为常量。 模板参数 可以增强输入参数的类型安全性。

在以下示例模板中,参数通过枚举特定选项来限制可用的管道池选项,而不是允许任何字符串。

# template.yml
parameters:
- name: userpool
  type: string
  default: Azure Pipelines
  values:
  - Azure Pipelines
  - private-pool-1
  - private-pool-2

pool: ${{ parameters.userpool }}
steps:
- script: echo Hello world

若要扩展模板,管道必须指定可用池选项之一。

# azure-pipelines.yml
extends:
  template: template.yml
  parameters:
    userpool: private-pool-1

代理日志记录命令限制

用户通过使用 日志记录命令来请求服务,这些命令是打印到标准输出的特制字符串。 可以限制日志记录命令为用户步骤提供的服务。 在 restricted 模式下,大多数代理服务(例如上传项目和附加测试结果)都不适用于日志记录命令。

在下面的示例中,该 target 属性指示代理限制发布项目,因此项目发布任务失败。

- task: PublishBuildArtifacts@1
  inputs:
    artifactName: myartifacts
  target:
    commands: restricted

日志记录命令中的变量

setvariable 命令在 restricted 模式下仍然允许,因此输出用户提供的数据的任务(如通过 REST API 检索的开放问题)可能容易受到注入攻击。 恶意用户内容可以将导出到后续任务的变量设置为环境变量,并可能危及代理主机。

若要缓解此风险,可以使用日志记录命令显式声明可设置的 setvariable 变量。 如果指定了空列表 settableVariables,则不允许任何变量设置。

以下示例将settableVariables限制为expectedVar以及任何以ok为前缀的变量。 任务失败,因为它尝试设置名为 BadVar 的其他变量。

- task: PowerShell@2
  target:
    commands: restricted
    settableVariables:
    - expectedVar
    - ok*
  inputs:
    targetType: 'inline'
    script: |
      Write-Host "##vso[task.setvariable variable=BadVar]myValue"

条件阶段或作业执行

可以限制阶段和作业仅在特定条件下运行。 以下示例确保仅针对 main 分支生成受限代码。

jobs:
- job: buildNormal
  steps:
  - script: echo Building the normal, unsensitive part
- ${{ if eq(variables['Build.SourceBranchName'], 'refs/heads/main') }}:
  - job: buildMainOnly
    steps:
    - script: echo Building the restricted part that only builds for main branch

语法修改

Azure Pipelines 模板可以灵活地循环访问和修改 YAML 语法。 通过使用迭代,可以强制实施特定的 YAML 安全功能。

模板还可以重写用户步骤,仅允许已批准的任务运行。 例如,模板可以阻止内联脚本执行。

以下示例模板阻止脚本步骤类型bashpowershellpwshscript运行。 若要完全锁定脚本,还可以阻止 BatchScriptShellScript

# template.yml
parameters:
- name: usersteps
  type: stepList
  default: []
steps:
- ${{ each step in parameters.usersteps }}:
  - ${{ if not(or(startsWith(step.task, 'Bash'),startsWith(step.task, 'CmdLine'),startsWith(step.task, 'PowerShell'))) }}:  
    - ${{ step }}
  # The following lines replace tasks like Bash@3, CmdLine@2, PowerShell@2
  - ${{ else }}:  
    - ${{ each pair in step }}:
        ${{ if eq(pair.key, 'inputs') }}:
          inputs:
            ${{ each attribute in pair.value }}:
              ${{ if eq(attribute.key, 'script') }}:
                script: echo "Script removed by template"
              ${{ else }}:
                ${{ attribute.key }}: ${{ attribute.value }}
        ${{ elseif ne(pair.key, 'displayName') }}:
          ${{ pair.key }}: ${{ pair.value }}

          displayName: 'Disabled by template: ${{ step.displayName }}'

在扩展上述模板的以下示例管道中,脚本步骤会剥离出来,并且不会运行。

# azure-pipelines.yml
extends:
  template: template.yml
  parameters:
    usersteps:
    - task: MyTask@1
    - script: echo This step is stripped out and not run
    - bash: echo This step is stripped out and not run
    - powershell: echo "This step is stripped out and not run"
    - pwsh: echo "This step is stripped out and not run"
    - script: echo This step is stripped out and not run
    - task: CmdLine@2
      displayName: Test - stripped out
      inputs:
        script: echo This step is stripped out and not run
    - task: MyOtherTask@2

模板步骤

模板可以自动在管道中包含步骤,例如执行凭据扫描或静态代码检查。 以下模板在每个作业中的用户步骤之前和之后插入步骤。

parameters:
  jobs: []

jobs:
- ${{ each job in parameters.jobs }}: 
  - ${{ each pair in job }}:  
      ${{ if ne(pair.key, 'steps') }}:
        ${{ pair.key }}: ${{ pair.value }}
    steps:                            
    - task: CredScan@1 
    - ${{ job.steps }} 
    - task: PublishMyTelemetry@1 
      condition: always()

模板强制实施

模板作为安全机制的有效性依赖于强制实施。 强制实施模板使用的关键控制点是 受保护的资源

可以为代理池或其他受保护资源(例如存储库)配置审批和检查。 有关示例,请参阅 “添加存储库资源检查”。

所需的模板

若要实施特定模板,请在资源的服务连接上配置 强制模板检查。 此检查仅适用于管道从模板扩展时。

查看管道作业时,可以监视检查的状态。 如果管道未从所需的模板进行扩展,则检查将失败。

显示审批检查失败的屏幕截图。

使用所需的模板时,检查将通过。

显示已通过审批检查的屏幕截图。

例如,任何扩展此模板的管道中都必须引用以下 params.yml 模板。

# params.yml
parameters:
- name: yesNo 
  type: boolean
  default: false
- name: image
  displayName: Pool Image
  type: string
  default: ubuntu-latest
  values:
  - windows-latest
  - ubuntu-latest
  - macOS-latest

steps:
- script: echo ${{ parameters.yesNo }}
- script: echo ${{ parameters.image }}

以下示例管道扩展 params.yml 模板,并要求其批准。 若要演示管道故障,请注释掉对extends引用。

# azure-pipeline.yml

resources:
 containers:
     - container: my-container
       endpoint: my-service-connection
       image: mycontainerimages

extends:
    template: params.yml
    parameters:
        yesNo: true
        image: 'windows-latest'