Использование шаблонов для безопасности

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

В этой статье описывается, как шаблоны могут упростить безопасность для Azure Pipelines. Шаблоны могут определять внешнюю структуру конвейера и предотвращать проникновение вредоносного кода. Шаблоны также могут автоматически включать шаги для выполнения таких задач, как сканирование учетных данных. Если несколько конвейеров в команде или организации используют одну и ту же структуру, рекомендуется использовать шаблоны.

Проверка защищенных ресурсов формирует базовую платформу безопасности для Azure Pipelines. Эти проверки применяются независимо от структуры конвейера, этапов и заданий. Вы можете использовать шаблоны для применения этих проверок.

Включает и расширяет шаблоны

Azure Pipelines предоставляет шаблоны и расширяет шаблоны.

  • Включает шаблоны включают код шаблона непосредственно в внешний файл, который ссылается на него, как #include и в C++. В следующем примере конвейер вставляет шаблон include-npm-steps.yml в steps раздел.

      steps:
      - template: templates/include-npm-steps.yml 
    
  • Расширяет шаблоны определяют внешнюю структуру конвейера и предлагают определенные точки для целевых настроек. В контексте C++ extends шаблоны похожи на наследование.

При использовании extends шаблонов можно также использовать includes как в шаблоне, так и в последнем конвейере для выполнения общих частей конфигурации. Полный справочник см. в справочнике по использованию шаблона.

Расширение шаблонов

Для наиболее безопасных конвейеров начните с расширения шаблонов. Эти шаблоны определяют внешнюю структуру конвейера и препятствуют инфильтрации конвейера вредоносным кодом.

Например, следующий файл шаблона называется 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

Синтаксис конвейера YAML включает несколько встроенных защит. Расширение шаблона может применять их использование. Чтобы повысить безопасность конвейера, можно реализовать любое из следующих ограничений.

Целевые объекты шага

Вы можете ограничить выполнение определенных шагов в контейнере, а не на узле. Действия в контейнерах не имеют доступа к узлу агента, предотвращая изменение конфигурации агента или выход вредоносного кода для последующего выполнения.

Например, рекомендуется ограничить доступ к сети. Без открытого сетевого доступа действия пользователя не могут извлекать пакеты из несанкционированных источников или отправлять код и секреты во внешние сетевые расположения.

В следующем примере конвейер выполняет шаги на узле агента перед выполнением шагов внутри контейнера.

resources:
  containers:
  - container: builder
    image: mysecurebuildcontainer:latest
steps:
- script: echo This step runs on the agent host, and it could use Docker commands to tear down or limit the container's network
- script: echo This step runs inside the builder container
  target: builder

Ограничения команд ведения журнала агента

Вы можете ограничить службы, предоставляемые агентом Azure Pipelines, для выполнения действий пользователя. Шаги пользователя запрашивают службы с помощью команд ведения журнала, которые специально отформатированы строки, напечатанные в стандартные выходные данные. В ограниченном режиме большинство служб агента, таких как отправка артефактов и присоединение результатов теста, недоступны.

В следующем примере задача завершается сбоем, так как его target свойство указывает агенту не разрешать публикацию артефактов.

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

В restricted режиме команда остается допустимой setvariable , поэтому осторожность необходима, так как переменные конвейера экспортируются в виде переменных среды в последующие задачи. Если задачи выводят предоставленные пользователем данные, такие как открытые проблемы, полученные через REST API, они могут быть уязвимы для атак на внедрение. Содержимое вредоносного пользователя может задать переменные среды, которые могут быть использованы для компрометации узла агента.

Чтобы устранить этот риск, авторы конвейера могут явно объявить, какие переменные задаются с помощью setvariable команды ведения журнала. При указании пустого списка все параметры переменной запрещены.

Следующий пример задачи завершается сбоем, так как задача разрешена только для задания expectedVar переменной или префикса okпеременной.

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

Условный этап или выполнение задания

Этапы и задания можно ограничить только в определенных условиях. В следующем примере условие гарантирует, что ограниченный код выполняет сборку только для основной ветви.

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.

Шаблон также может переписать пользовательские шаги, позволяя выполнять только утвержденные задачи. Например, можно предотвратить встроенное выполнение скрипта.

Следующий пример шаблона запрещает выполнение типов bashшагов, powershellpwshа также script выполнение. Для полной блокировки нерегламентированных сценариев можно также блокировать BatchScript и ShellScript.

# 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 will be stripped out and not run!
    - bash: echo This step will be stripped out and not run!
    - powershell: echo "This step will be stripped out and not run!"
    - pwsh: echo "This step will be stripped out and not run!"
    - script: echo This step will be stripped out and not run!
    - task: CmdLine@2
      displayName: Test - Will be stripped out
      inputs:
        script: echo This step will be stripped out and not run!
    - task: MyOtherTask@2

Типобезопасные параметры

Перед запуском конвейера шаблоны и их параметры преобразуются в константы. Параметры шаблона могут повысить безопасность типов для входных параметров.

В следующем примере шаблона параметры ограничивают доступные параметры пула конвейеров, предоставляя перечисление определенных вариантов, а не разрешать строки свободной формы.

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

pool: ${{ parameters.userpool }}
steps:
- script: # ... removed for clarity

Когда конвейер расширяет шаблон, он должен указать один из доступных вариантов пула.

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

Шаги шаблона

Шаблон может автоматически включать шаги в конвейер. Эти действия могут выполнять такие задачи, как сканирование учетных данных или статические проверки кода. Следующий шаблон вставляет шаги до и после действий пользователя в каждом задании.

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 и требует его утверждения. Чтобы продемонстрировать сбой конвейера, закомментируйте ссылку на params.yml.

# azure-pipeline.yml

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

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