Segurança por meio de modelos

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

As verificações sobre recursos protegidos são a peça básica da criação de segurança para o Azure Pipelines. As verificações funcionam independentemente da estrutura (estágios e trabalhos) do pipeline. Se vários pipelines em sua equipe ou organização tiverem a mesma estrutura, você poderá simplificar ainda mais a segurança usando modelos.

O Azure Pipelines oferece dois tipos de modelos: inclui e estende. Os modelos incluídos se comportam como #include no C++: é como se você colasse o código do modelo diretamente no arquivo externo, que faz referência a ele. Por exemplo, aqui um modelo de inclusão (include-npm-steps.yml) é inserido em steps.

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

Para continuar a metáfora do C++, os modelos extends são mais como 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.

Usar modelos de extensão

Para os pipelines mais seguros, recomendamos começar com modelos extends. Ao fornecer a estrutura externa, um modelo pode impedir que código mal-intencionado entre em seu pipeline. Você ainda pode usar includes, tanto no modelo quanto no pipeline final, para fatorar partes comuns da configuração. Para usar um modelo de extensão, seu pipeline pode ser semelhante ao 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

Ao configurar modelos extends, considere ancorá-los em um branch ou marca git específico. Dessa forma, se forem necessárias alterações significativas, os pipelines existentes não serão afetados. Os exemplos acima usam esse recurso.

Recursos de segurança impostos por meio do YAML

Existem várias proteções incorporadas à sintaxe do YAML, e um modelo de ampliações pode implementar o uso de algumas ou todas elas.

Destinos de etapa

Restrinja algumas etapas para execução em um contêiner em vez do host. Sem acesso ao host do agente, as etapas do usuário não podem modificar a configuração do agente ou deixar código mal-intencionado para execução posterior. Execute o código no host primeiro para tornar o contêiner mais seguro. Por exemplo, recomendamos limitar o acesso à rede. Sem acesso aberto à rede, as etapas do usuário não poderão acessar pacotes de fontes não autorizadas ou carregar código e segredos em um local 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 comando de registro em log do agente

Restrinja quais serviços o agente do Azure Pipelines fornecerá às etapas do usuário. Etapas solicitam serviços usando "comandos de log" (cadeias de caracteres especialmente formatadas impressas em stdout). No modo restrito, a maioria dos serviços do agente, como carregar artefatos e anexar resultados de teste, não está disponível.

# 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 comando setvariable. Como as variáveis ​​de pipeline são exportadas como variáveis ​​de ambiente para tarefas subsequentes, as tarefas que geram dados fornecidos pelo usuário (por exemplo, o conteúdo de problemas em aberto recuperados de uma API REST) ​​podem ser vulneráveis ​​a ataques de injeção. Esse conteúdo do usuário pode definir variáveis de ambiente que, por sua vez, podem ser usadas para explorar o host do agente. Para não permitir isso, os autores de pipeline podem declarar explicitamente quais variáveis são configuráveis por meio do comando de registro em log setvariable. 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 estágios ou trabalhos

Restrinja estágios e trabalhos a serem executados em condições específicas. As condições podem ajudar, por exemplo, a garantir que você esteja criando apenas determinadas ramificações.

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 certa sintaxe com modelos estendidos

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

Um modelo pode reescrever as etapas do usuário e permitir apenas a execução de determinadas tarefas aprovadas. Você pode, por exemplo, impedir a execução de script embutido.

Aviso

No exemplo a seguir, as etapas digitam "bash", "powershell", "pwsh" e "script" são impedidas de serem executadas. Para o bloqueio total de scripts ad hoc, você também precisaria 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 de segurança de tipo

Os modelos e seus 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, ele pode restringir quais pools podem ser usados em um pipeline oferecendo uma enumeração de opções possíveis em vez de uma cadeia de caracteres 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 que um modelo específico seja usado, você pode definir a verificação de modelo necessária para um recurso ou ambiente. A verificação de modelo necessária pode ser usada ao estender de um modelo.

Você pode verificar o status de uma verificação ao exibir um trabalho de pipeline. Quando um pipeline não se estende do modelo necessário, a verificação falhará e a execução será interrompida. Você verá que a verificação falhou.

falha na verificação de aprovação

Quando o modelo necessário for usado, você verá que a verificação foi aprovada.

aprovação de verificação

Aqui, o modelo params.yml é necessário com uma aprovação no recurso. Para disparar o pipeline para falhar, 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'

Etapas adicionais

Um modelo pode adicionar etapas sem que o autor do pipeline precise incluí-las. Essas etapas podem ser usadas para executar verificações de credenciais ou 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 modelo

Um modelo será apenas um mecanismo de segurança se você puder aplicá-lo. O ponto de controle para impor o uso de modelos é um recurso protegido. Você pode configurar aprovações e verificações no pool de agentes ou em outros recursos protegidos, como repositórios. Para obter um exemplo, confira Adicionar uma verificação de recursos do repositório.

Próximas etapas

Em seguida, saiba mais sobre como fazer entradas com segurança por meio de variáveis e parâmetros.