Partager via


Modèles de sécurité

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

Les modèles Azure Pipelines vous permettent de définir du contenu, une logique et des paramètres réutilisables dans des pipelines YAML. Cet article décrit comment les modèles peuvent aider à améliorer la sécurité des pipelines en procédant comme suit :

  • Définition de la structure externe d’un pipeline pour empêcher l’infiltration de code malveillante.
  • Ajout automatique des étapes pour effectuer des tâches telles que l’analyse des informations d’identification.
  • Aider à appliquer des vérifications sur les ressources protégées, qui forment l’infrastructure de sécurité fondamentale pour Azure Pipelines et s’appliquent à toutes les structures et composants de pipeline.

Cet article fait partie d’une série qui vous aide à implémenter des mesures de sécurité pour Azure Pipelines. Pour plus d’informations, consultez Sécuriser Azure Pipelines.

Conditions préalables

Catégorie Exigences
Azure DevOps - Implémentez des recommandations dans Rendre vos pipelines Azure DevOps sécurisés et sécurisés.
- Connaissance de base de YAML et d’Azure Pipelines. Pour plus d’informations, consultez Créer votre premier pipeline.
Autorisations - Pour modifier les autorisations des pipelines : membre du groupe Administrateurs de projet.
- Pour modifier les autorisations de l'Organisation : Membre du groupe Project Collection Administrators.

Inclut et étend les modèles

Azure Pipelines fournit des modèles inclus et étendus .

  • Un includes modèle inclut le code du modèle directement dans le fichier externe qui fait référence au modèle, comme #include dans C++. L’exemple de pipeline suivant insère le modèle include-npm-steps.yml dans la steps section.

      steps:
      - template: templates/include-npm-steps.yml 
    
  • Un extends modèle définit la structure externe du pipeline et offre des points spécifiques pour les personnalisations ciblées. Dans le contexte de C++, extends les modèles ressemblent à l’héritage.

Lorsque vous utilisez extends des modèles, vous pouvez également utiliser includes pour effectuer des éléments de configuration courants dans le modèle et le pipeline final. Pour plus d’informations, consultez Utiliser des modèles YAML dans des pipelines pour des processus réutilisables et sécurisés.

Étend les modèles

Pour les pipelines les plus sécurisés, commencez par utiliser extends modèles. Ces modèles définissent la structure externe du pipeline et aident à empêcher l’infiltration de code malveillante.

L’exemple suivant montre un fichier de modèle nommé template.yml.

parameters:
- name: usersteps
  type: stepList
  default: []
steps:
- ${{ each step in parameters.usersteps }}:
  - ${{ step }}

L’exemple de pipeline suivant étend le modèle 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

Conseil

Lorsque vous configurez des modèles de extends, envisagez de les ancrer à une branche ou une balise Git particulière afin que les modifications disruptives n’affectent pas les pipelines existants. L’exemple précédent utilise cette fonctionnalité.

Fonctionnalités de sécurité du pipeline

La syntaxe du pipeline YAML comprend plusieurs protections intégrées. Extends les modèles peuvent imposer leur utilisation pour renforcer la sécurité du pipeline. Vous pouvez implémenter l’une des restrictions suivantes.

Cibles d’étape

Vous pouvez restreindre les étapes spécifiées à exécuter dans un conteneur plutôt que sur l’hôte. Les étapes des conteneurs ne peuvent pas accéder à l’hôte de l’agent, de sorte qu’elles ne peuvent pas modifier la configuration de l’agent ou laisser du code malveillant pour une exécution ultérieure.

Par exemple, vous pouvez exécuter des étapes utilisateur dans un conteneur pour les empêcher d’accéder au réseau, afin qu’ils ne puissent pas récupérer des packages à partir de sources non autorisées ou charger du code et des secrets vers des emplacements externes.

L’exemple de pipeline suivant exécute une étape sur l’hôte de l’agent susceptible de modifier le réseau hôte, suivie d’une étape à l’intérieur d’un conteneur qui limite l’accès réseau.

resources:
  containers:
  - container: builder
    image: mysecurebuildcontainer:latest
steps:
- script: echo This step runs on the agent host
- script: echo This step runs inside the builder container
  target: builder

Paramètres de type sécurisé

Avant l’exécution d’un pipeline, les modèles et leurs paramètres se transforment en constantes. Les paramètres de modèle peuvent améliorer la sécurité des types pour les paramètres d’entrée.

Dans l’exemple de modèle suivant, les paramètres limitent les options de pool de pipelines disponibles en énumérant des choix spécifiques au lieu d’autoriser n’importe quelle chaîne.

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

pool: ${{ parameters.userpool }}
steps:
- script: echo Hello world

Pour étendre le modèle, le pipeline doit spécifier l’un des choix de pool disponibles.

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

Restrictions de la commande de journalisation de l’agent

Les étapes utilisateur demandent des services à l’aide de commandes de journalisation, qui sont des chaînes spécialement mises en forme imprimées en sortie standard. Vous pouvez restreindre les services fournis par les commandes de journalisation pour les étapes utilisateur. En restricted mode, la plupart des services d’agent, tels que le chargement d’artefacts et l’attachement des résultats des tests, ne sont pas disponibles pour la journalisation des commandes.

Dans l’exemple suivant, la propriété target instruit l’agent de restreindre la publication des artefacts, de sorte que la tâche de publication d’artefact échoue.

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

Variables dans les commandes de journalisation

La setvariable commande reste autorisée en restricted mode, de sorte que les tâches qui génèrent des données fournies par l’utilisateur, telles que les problèmes d’ouverture récupérés via une API REST, peuvent être vulnérables aux attaques par injection. Le contenu utilisateur malveillant peut définir des variables qui exportent vers des tâches ultérieures en tant que variables d’environnement et peuvent compromettre l’hôte de l’agent.

Pour atténuer ce risque, vous pouvez explicitement déclarer les variables modifiables à l’aide de la commande de "logging" setvariable. Si vous spécifiez une liste vide dans settableVariables, tous les paramètres de variable ne sont pas autorisés.

L’exemple suivant restreint settableVariables à expectedVar et à toute variable préfixée de ok. La tâche échoue, car elle tente de définir une variable différente appelée BadVar.

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

Étape conditionnelle ou exécution du travail

Vous pouvez restreindre les étapes et les travaux à exécuter uniquement dans des conditions spécifiques. L’exemple suivant garantit que le code restreint est généré uniquement pour la main branche.

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

Modification de la syntaxe

Les modèles Azure Pipelines ont la possibilité d’itérer et de modifier la syntaxe YAML. En utilisant l’itération, vous pouvez appliquer des fonctionnalités de sécurité YAML spécifiques.

Un modèle peut également réécrire les étapes utilisateur, ce qui autorise uniquement les tâches approuvées à s’exécuter. Par exemple, le modèle peut empêcher l’exécution du script inline.

L’exemple de modèle suivant empêche l'exécution des types d’étapes de script bash, powershell, pwsh, et script. Pour un verrouillage complet des scripts, vous pouvez également bloquer BatchScript et 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 }}'

Dans l’exemple de pipeline suivant qui étend le modèle précédent, les étapes de script sont supprimées et ne sont pas exécutées.

# azure-pipelines.yml
extends:
  template: template.yml
  parameters:
    usersteps:
    - task: MyTask@1
    - script: echo This step is stripped out and not run
    - bash: echo This step is stripped out and not run
    - powershell: echo "This step is stripped out and not run"
    - pwsh: echo "This step is stripped out and not run"
    - script: echo This step is stripped out and not run
    - task: CmdLine@2
      displayName: Test - stripped out
      inputs:
        script: echo This step is stripped out and not run
    - task: MyOtherTask@2

Étapes du modèle

Un modèle peut inclure automatiquement des étapes dans un pipeline, par exemple pour effectuer une analyse des informations d’identification ou des vérifications de code statiques. Le modèle suivant insère des étapes avant et après les étapes de l’utilisateur dans chaque travail.

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

Application des modèles

L’efficacité des modèles en tant que mécanisme de sécurité repose sur l’application. Les points de contrôle clés pour appliquer l’utilisation du modèle sont des ressources protégées.

Vous pouvez configurer des approbations et des vérifications pour votre pool d’agents ou d’autres ressources protégées telles que des dépôts. Pour obtenir un exemple, consultez Ajouter une vérification de ressource de référentiel.

Modèles obligatoires

Pour appliquer l’utilisation d’un modèle spécifique, configurez la vérification de modèle requise sur la connexion de service pour une ressource. Cette vérification s’applique uniquement lorsque le pipeline s’étend à partir d’un modèle.

Lorsque vous affichez la tâche de pipeline, vous pouvez surveiller l’état de la vérification. Si le pipeline ne s’étend pas à partir du modèle requis, la vérification échoue.

Capture d’écran montrant une vérification d’approbation ayant échoué.

Lorsque vous utilisez le modèle requis, la vérification passe.

Capture d’écran montrant une vérification d’approbation passée.

Par exemple, le modèle de params.yml suivant doit être référencé dans n’importe quel pipeline qui l’étend.

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

L’exemple de pipeline suivant étend le modèle params.yml et nécessite son approbation. Pour illustrer un échec de pipeline, mettez en commentaire la référence au fichier extends.

# azure-pipeline.yml

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

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