Zabezpieczenia za pośrednictwem szablonów

Azure DevOps Services | Azure DevOps Server 2022 r. | Azure DevOps Server 2020 r.

Sprawdzanie chronionych zasobów to podstawowy blok konstrukcyjny zabezpieczeń usługi Azure Pipelines. Sprawdza działanie niezależnie od struktury — etapów i zadań — potoku. Jeśli kilka potoków w zespole lub organizacji ma taką samą strukturę, możesz dodatkowo uprościć zabezpieczenia przy użyciu szablonów.

Usługa Azure Pipelines oferuje dwa rodzaje szablonów: obejmuje i rozszerza. Dołączone szablony zachowują się tak jak #include w języku C++: wklejasz kod szablonu bezpośrednio do pliku zewnętrznego, który się do niego odwołuje. Na przykład w tym miejscu wstawiono szablon dołączania (include-npm-steps.yml) do stepselementu .

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

Aby kontynuować metaforę języka C++, extends szablony są bardziej podobne do dziedziczenia: szablon zapewnia zewnętrzną strukturę potoku i zestaw miejsc, w których odbiorca szablonu może wprowadzać ukierunkowane zmiany.

Korzystanie z szablonów rozszerzeń

W przypadku najbardziej bezpiecznych potoków zalecamy rozpoczęcie od extends szablonów. Udostępniając strukturę zewnętrzną, szablon może uniemożliwić złośliwemu kodowi przejście do potoku. Nadal można użyć metody includes, zarówno w szablonie, jak i w ostatnim potoku, aby uwzględnić typowe elementy konfiguracji. Aby użyć szablonu rozszerzenia, potok może wyglądać podobnie do poniższego przykładu.

# 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

Podczas konfigurowania extends szablonów rozważ zakotwiczenie ich w określonej gałęzi lub tagu usługi Git. W ten sposób, jeśli konieczne będzie wprowadzenie zmian powodujących niezgodność, istniejące potoki nie będą miały wpływu. W powyższych przykładach użyto tej funkcji.

Funkcje zabezpieczeń wymuszane za pomocą języka YAML

Istnieje szereg zabezpieczeń wbudowanych w składnię języka YAML, a szablon rozszerzeń może wymuszać użycie dowolnych lub wszystkich z nich.

Cele kroków

Ogranicz kilka kroków do uruchomienia w kontenerze zamiast hosta. Bez dostępu do hosta agenta kroki użytkownika nie mogą modyfikować konfiguracji agenta ani pozostawiać złośliwego kodu do późniejszego wykonania. Najpierw uruchom kod na hoście, aby zwiększyć bezpieczeństwo kontenera. Na przykład zalecamy ograniczenie dostępu do sieci. Bez otwartego dostępu do sieci kroki użytkownika nie będą mogły uzyskać dostępu do pakietów z nieautoryzowanych źródeł lub przekazać kod i wpisy tajne do lokalizacji sieciowej.

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

Ograniczenia poleceń rejestrowania agentów

Ogranicz usługi, które agent usługi Azure Pipelines zapewni użytkownikowi. Kroki żądań usług przy użyciu "poleceń rejestrowania" (specjalnie sformatowane ciągi drukowane do stdout). W trybie ograniczonym większość usług agenta, takich jak przekazywanie artefaktów i dołączanie wyników testów, jest niedostępna.

# 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

Jednym z poleceń nadal dozwolonych w trybie ograniczonym jest setvariable polecenie . Ponieważ zmienne potoku są eksportowane jako zmienne środowiskowe do kolejnych zadań, zadania, które generują dane dostarczone przez użytkownika (na przykład zawartość otwartych problemów pobranych z interfejsu API REST) może być podatna na ataki iniekcyjne. Taka zawartość użytkownika może ustawiać zmienne środowiskowe, które z kolei mogą służyć do wykorzystania hosta agenta. Aby to uniemożliwić, autorzy potoków mogą jawnie zadeklarować, które zmienne są ustawiane za pomocą polecenia rejestrowania setvariable . Określanie pustej listy nie zezwala na ustawienie wszystkich zmiennych.

# 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"

Warunkowe wstawianie etapów lub zadań

Ogranicz etapy i zadania do uruchamiania w określonych warunkach. Warunki mogą pomóc, na przykład, aby upewnić się, że tworzysz tylko niektóre gałęzie.

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

Wymagaj określonej składni z szablonami rozszerzeń

Szablony mogą iterować i modyfikować/nie zezwalać na dowolną składnię YAML. Iteracja może wymusić użycie określonej składni YAML, w tym powyższych funkcji.

Szablon może ponownie napisać kroki użytkownika i zezwalać tylko na uruchamianie niektórych zatwierdzonych zadań. Można na przykład uniemożliwić wykonywanie skryptu wbudowanego.

Ostrzeżenie

W poniższym przykładzie kroki typu "bash", "powershell", "pwsh" i "script" nie są wykonywane. Aby zapewnić pełną blokadę skryptów ad hoc, należy również zablokować "BatchScript" i "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

Parametry bezpieczne dla typów

Szablony i ich parametry są przekształcane w stałe przed uruchomieniem potoku. Parametry szablonu zapewniają bezpieczeństwo typu parametrom wejściowym. Na przykład może ograniczyć, które pule mogą być używane w potoku, oferując wyliczenie możliwych opcji, a nie ciąg wolny.

# 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

Ustawianie wymaganych szablonów

Aby wymagać użycia określonego szablonu, możesz ustawić wymagany szablon sprawdzania zasobu lub środowiska. Wymagane sprawdzenie szablonu można użyć podczas rozszerzania szablonu.

Stan sprawdzania można sprawdzić podczas wyświetlania zadania potoku. Jeśli potok nie wykracza poza szablon wymagania, sprawdzanie zakończy się niepowodzeniem, a uruchomienie zostanie zatrzymane. Zobaczysz, że sprawdzanie nie powiodło się.

Sprawdzanie zatwierdzenia kończy się niepowodzeniem

Po użyciu wymaganego szablonu zobaczysz, że sprawdzanie zostało przekazane.

Sprawdzanie zatwierdzenia przechodzi

W tym miejscu szablon params.yml jest wymagany z zatwierdzeniem zasobu. Aby wyzwolić potok, aby zakończyć się niepowodzeniem, oznacz odwołanie do 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'

Dodatkowe kroki

Szablon może dodawać kroki bez konieczności uwzględnienia ich przez autora potoku. Te kroki mogą służyć do uruchamiania skanowania poświadczeń lub sprawdzania kodu statycznego.

# 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()

Wymuszanie szablonu

Szablon jest tylko mechanizmem zabezpieczeń, jeśli można go wymusić. Punkt kontrolny do wymuszania używania szablonów jest zasobem chronionym. Zatwierdzenia i kontrole puli agentów lub innych chronionych zasobów, takich jak repozytoria. Aby zapoznać się z przykładem, zobacz Dodawanie sprawdzania zasobów repozytorium.

Następne kroki

Następnie dowiesz się, jak bezpiecznie pobierać dane wejściowe za pomocą zmiennych i parametrów.