Sicherheit durch Vorlagen

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

Überprüfungen geschützter Ressourcen sind der grundlegende Baustein der Sicherheit für Azure Pipelines. Sie funktionieren unabhängig von der Struktur (Stages und Aufträge) Ihrer Pipeline. Wenn mehrere Pipelines in Ihrem Team oder Ihrer Organisation dieselbe Struktur aufweisen, können Sie die Sicherheit mithilfe von Vorlagen weiter vereinfachen.

Azure Pipelines bietet zwei Arten von Vorlagen: includes und extends. includes-Vorlagen verhalten sich wie #include in C++: Es ist so, als würden Sie den Code der Vorlage direkt in die externe Datei einfügen, die darauf verweist. Hier wird beispielsweise eine includes-Vorlage (include-npm-steps.yml) in steps eingefügt.

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

Um bei der C++-Metapher zu bleiben: extends-Vorlagen folgen dem Prinzip der Vererbung. Die Vorlage stellt die äußere Struktur der Pipeline und eine Reihe von Stellen bereit, an denen die Benutzer*innen der Vorlage gezielte Änderungen vornehmen können.

Verwenden von extends-Vorlagen

Für besonders sichere Pipelines wird empfohlen, mit extends-Vorlagen zu beginnen. Durch die Bereitstellung der äußeren Struktur kann eine Vorlage verhindern, dass bösartiger Code in Ihre Pipeline gelangt. Sie können includes weiterhin sowohl in der Vorlage als auch in der finalen Pipeline verwenden, um allgemeine Konfigurationselemente auszuklammern. Wenn Sie eine extends-Vorlage verwenden, sieht Ihre Pipeline möglicherweise wie im folgenden Beispiel aus.

# 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

Wenn Sie extends-Vorlagen einrichten, sollten Sie sie in einem bestimmten Git-Branch oder -Tag verankern. Auf diese Weise werden vorhandene Pipelines nicht beeinträchtigt, wenn Breaking Changes erforderlich sind. In den obigen Beispielen wird dieses Feature verwendet.

Durch YAML erzwungene Sicherheitsfeatures

In die YAML-Syntax sind mehrere Schutzmechanismen integriert, und eine extends-Vorlage kann die Verwendung dieser Mechanismen erzwingen.

Schrittziele

Beschränken Sie einige Schritte, um sie in einem Container anstatt auf dem Host auszuführen. Ohne Zugriff auf den Host des Agents kann die Agent-Konfiguration durch Benutzerschritte nicht geändert werden, und schädlicher Code kann nicht für die spätere Ausführung beibehalten werden. Führen Sie Code zuerst auf dem Host aus, um den Container sicherer zu machen. Es wird beispielsweise empfohlen, den Zugriff auf das Netzwerk einzuschränken. Ohne offenen Zugriff auf das Netzwerk kann über Benutzerschritte nicht auf Pakete aus nicht autorisierten Quellen zugegriffen werden, und Code und Geheimnisse können nicht in einen Netzwerkspeicherort hochgeladen werden.

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

Einschränkungen für Agent-Protokollierungsbefehle

Schränken Sie ein, welche Dienste der Azure Pipelines-Agent für Benutzerschritte bereitstellt. Die Schritte erfordern, dass Dienste Protokollierungsbefehle verwenden (speziell formatierte Zeichenfolgen, die in StdOut ausgegeben werden). Im eingeschränkten Modus sind die meisten Dienste des Agents (z. B. das Hochladen von Artefakten und das Anfügen von Testergebnissen) nicht verfügbar.

# 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

Einer der Befehle, die im eingeschränkten Modus noch zulässig sind, ist der setvariable-Befehl. Da Pipelinevariablen als Umgebungsvariablen in nachfolgende Aufgaben exportiert werden, können Aufgaben, die von den Benutzer*innen bereitgestellte Daten ausgeben (z. B. der Inhalt offener Probleme, die über eine REST-API abgerufen wurden), anfällig für Einschleusungsangriffe sein. Solche Benutzerinhalte können Umgebungsvariablen festlegen, die wiederum verwendet werden können, um den Agent-Host auszunutzen. Um dies zu verhindern, können Pipelineersteller*innen explizit deklarieren, welche Variablen über den setvariable-Protokollierungsbefehl festgelegt werden können. Die Angabe einer leeren Liste verhindert das Festlegen aller Variablen.

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

Bedingte Einfügung von Stages oder Aufträgen

Beschränken Sie Stages und Aufträge, die unter bestimmten Bedingungen ausgeführt werden sollen. Bedingungen können beispielsweise dazu beitragen, dass Sie nur bestimmte Branches erstellen.

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

Erfordern einer bestimmten Syntax mit extends-Vorlagen

Vorlagen können jede YAML-Syntax durchlaufen und daraufhin ändern bzw. nicht zulassen. Die Iteration kann die Verwendung einer bestimmten YAML-Syntax einschließlich der oben genannten Features erzwingen.

Eine Vorlage kann Benutzerschritte neu schreiben und nur bestimmte genehmigte Aufgaben ausführen lassen. Sie können beispielsweise die Inlineskriptausführung verhindern.

Warnung

Im folgenden Beispiel wird die Ausführung der Schritte „bash“, „powershell“, „pwsh“ und „script“ verhindert. Für die vollständige Sperrung von Ad-hoc-Skripts müssen Sie auch „BatchScript“ und „ShellScript“ blockieren.

# 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

Typsichere Parameter

Vorlagen und deren Parameter werden vor der Ausführung der Pipeline in Konstanten umgewandelt. Vorlagenparameter bieten Typsicherheit für Eingabeparameter. So kann beispielsweise eingeschränkt werden, welche Pools in einer Pipeline verwendet werden können, indem eine Enumeration möglicher Optionen anstelle einer Freiformzeichenfolge bereitgestellt wird.

# 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

Festlegen erforderlicher Vorlagen

Damit eine bestimmte Vorlage verwendet wird, können Sie die erforderliche Vorlagenüberprüfung für eine Ressource oder Umgebung festlegen. Die erforderliche Vorlagenüberprüfung kann beim Erweitern über eine Vorlage verwendet werden.

Sie können den Status einer Überprüfung überprüfen, wenn Sie einen Pipelineauftrag anzeigen. Wenn eine Pipeline nicht über die Anforderungsvorlage erweitert wird, tritt bei der Überprüfung ein Fehler auf, und die Ausführung wird beendet. Sie erhalten eine Nachricht, dass die Überprüfung fehlgeschlagen ist.

Fehler bei der Genehmigungsüberprüfung

Wenn die erforderliche Vorlage verwendet wird, erhalten Sie die Nachricht, dass die Überprüfung erfolgreich war.

Genehmigungsüberprüfung bestanden

Hier ist die Vorlage params.yml mit einer Genehmigung für die Ressource erforderlich. Kommentieren Sie den Verweis auf params.yml aus, damit die ausgelöste Pipeline fehlschlägt.

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

Zusätzliche Schritte

Eine Vorlage kann Schritte hinzufügen, ohne dass die Pipelineersteller*innen sie einbeziehen müssen. Diese Schritte können für Überprüfungen von Anmeldeinformationen oder statischem Code verwendet werden.

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

Erzwingung von Vorlagen

Eine Vorlage stellt nur dann einen Sicherheitsmechanismus dar, wenn Sie sie erzwingen können. Der Kontrollpunkt zum Erzwingen der Verwendung von Vorlagen ist eine geschützte Ressource. Sie können Genehmigungen und Überprüfungen für Ihren Agentpool oder andere geschützte Ressourcen wie Repositorys konfigurieren. Ein Beispiel finden Sie unter Hinzufügen einer Repositoryressourcenüberprüfung.

Nächste Schritte

Als Nächstes erfahren Sie, wie Sie Variablen und Parameter sicher für Eingaben verwenden.