Segurança através de modelos

Serviços do Azure DevOps | Azure DevOps Server 2022 | Azure DevOps Server 2020

As verificações de recursos protegidos são o bloco modular básico de segurança para os Pipelines do Azure. As verificações funcionam independentemente da estrutura ( as fases e tarefas) do pipeline. Se vários pipelines na sua equipa ou organização tiverem a mesma estrutura, pode simplificar ainda mais a segurança através de modelos.

O Azure Pipelines oferece dois tipos de modelos: inclui e expande. Os modelos incluídos comportam-se como #include em C++: é como se colasse o código do modelo diretamente no ficheiro externo, que o referencia. Por exemplo, aqui um modelo inclui (include-npm-steps.yml) é inserido em steps.

  steps:
  - template: templates/include-npm-steps.yml 

Para continuar a metáfora C++, extends os modelos são mais semelhantes à herança: o modelo fornece a estrutura externa do pipeline e um conjunto de locais onde o consumidor do modelo pode fazer alterações direcionadas.

Utilizar modelos de extensões

Para os pipelines mais seguros, recomendamos que comece com extends modelos. Ao fornecer a estrutura externa, um modelo pode impedir que código malicioso entre no pipeline. Ainda pode utilizar includes, tanto no modelo como no pipeline final, para considerar partes comuns da configuração. Para utilizar um modelo de extensões, o pipeline poderá ter o aspeto do exemplo abaixo.

# template.yml
parameters:
- name: usersteps
  type: stepList
  default: []
steps:
- ${{ each step in parameters.usersteps }}:
  - ${{ step }}
# 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

Quando configurar extends modelos, considere ancorá-los a uma determinada ramificação ou etiqueta git. Desta forma, se forem necessárias alterações interruptivas, os pipelines existentes não serão afetados. Os exemplos acima utilizam esta funcionalidade.

Funcionalidades de segurança impostas através do YAML

Existem várias proteções incorporadas na sintaxe do YAML e um modelo de extensões pode impor a utilização de qualquer uma ou de todas.

Destinos de passos

Restringir alguns passos a executar num contentor em vez do anfitrião. Sem acesso ao anfitrião do agente, os passos do utilizador não podem modificar a configuração do agente ou deixar código malicioso para execução posterior. Execute o código no anfitrião primeiro para tornar o contentor mais seguro. Por exemplo, recomendamos limitar o acesso à rede. Sem acesso aberto à rede, os passos do utilizador não conseguirão aceder a pacotes de origens não autorizadas ou carregar código e segredos para uma localização de rede.

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

Restrições de comandos de registo de agentes

Restrinja os serviços que o agente dos Pipelines do Azure fornecerá aos passos do utilizador. Os passos pedem serviços com "comandos de registo" (cadeias de carateres especialmente formatadas impressas para stdout). No modo restrito, a maioria dos serviços do agente, como carregar artefactos e anexar resultados de teste, não estão disponíveis.

# this task will fail because its `target` property instructs the agent not to allow publishing artifacts
- task: PublishBuildArtifacts@1
  inputs:
    artifactName: myartifacts
  target:
    commands: restricted

Um dos comandos ainda permitidos no modo restrito é o setvariable comando . Uma vez que as variáveis de pipeline são exportadas como variáveis de ambiente para tarefas subsequentes, as tarefas que produzem dados fornecidos pelo utilizador (por exemplo, os conteúdos de problemas abertos obtidos a partir de uma API REST) podem ser vulneráveis a ataques de injeção. Esses conteúdos de utilizador podem definir variáveis de ambiente que, por sua vez, podem ser utilizadas para explorar o anfitrião do agente. Para não permitir isto, os autores do pipeline podem declarar explicitamente quais as variáveis que são definidas através do setvariable comando de registo. Especificar uma lista vazia não permite definir todas as variáveis.

# this task will fail because the task is only allowed to set the 'expectedVar' variable, or a variable prefixed with "ok"
- task: PowerShell@2
  target:
    commands: restricted
    settableVariables:
    - expectedVar
    - ok*
  inputs:
    targetType: 'inline'
    script: |
      Write-Host "##vso[task.setvariable variable=BadVar]myValue"

Inserção condicional de fases ou tarefas

Restrinja as fases e as tarefas a serem executadas em condições específicas. As condições podem ajudar, por exemplo, a garantir que está apenas a criar determinados ramos.

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

Exigir determinada sintaxe com modelos expandidos

Os modelos podem iterar e alterar/não permitir qualquer sintaxe YAML. A iteração pode forçar a utilização de sintaxe YAML específica, incluindo as funcionalidades acima.

Um modelo pode reescrever os passos do utilizador e permitir apenas a execução de determinadas tarefas aprovadas. Pode, por exemplo, impedir a execução de scripts inline.

Aviso

No exemplo abaixo, os passos escrevem "bash", "powershell", "pwsh" e "script" são impedidos de ser executados. Para o bloqueio total de scripts ad-hoc, também teria de bloquear "BatchScript" e "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 lines below will 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

Parâmetros seguros para tipos

Os modelos e os respetivos parâmetros são transformados em constantes antes da execução do pipeline. Os parâmetros de modelo fornecem segurança de tipo aos parâmetros de entrada. Por exemplo, pode restringir os conjuntos que podem ser utilizados num pipeline ao oferecer uma enumeração de opções possíveis em vez de uma cadeia de forma livre.

# 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

Definir modelos necessários

Para exigir a utilização de um modelo específico, pode definir a verificação de modelo necessária para um recurso ou ambiente. A verificação de modelos necessária pode ser utilizada ao expandir a partir de um modelo.

Pode verificar o estado de uma verificação ao ver uma tarefa de pipeline. Quando um pipeline não se estende a partir do modelo necessário, a verificação falhará e a execução será interrompida. Verá que a verificação falhou.

falha na verificação de aprovação

Quando o modelo necessário for utilizado, verá que a verificação passou.

aprovação de aprovação passa

Aqui, o modelo params.yml é necessário com uma aprovação no recurso. Para acionar a falha do pipeline, comente a referência a 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 }}
# azure-pipeline.yml

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

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

Passos adicionais

Um modelo pode adicionar passos sem que o autor do pipeline tenha de os incluir. Estes passos podem ser utilizados para executar a análise de credenciais ou verificações de código estático.

# template to insert a step before and after user steps in every job
parameters:
  jobs: []

jobs:
- ${{ each job in parameters.jobs }}: # Each job
  - ${{ each pair in job }}:  # Insert all properties other than "steps"
      ${{ if ne(pair.key, 'steps') }}:
        ${{ pair.key }}: ${{ pair.value }}
    steps:                            # Wrap the steps
    - task: CredScan@1                # Pre steps
    - ${{ job.steps }}                # Users steps
    - task: PublishMyTelemetry@1      # Post steps
      condition: always()

Imposição de modelos

Um modelo é apenas um mecanismo de segurança se o conseguir impor. O ponto de controlo para impor a utilização de modelos é um recurso protegido. Pode configurar aprovações e verificações no conjunto de agentes ou noutros recursos protegidos, como repositórios. Por exemplo, veja Adicionar uma verificação de recursos do repositório.

Passos seguintes

Em seguida, saiba mais sobre como tirar entradas de forma segura através de variáveis e parâmetros.