Utiliser des modèles pour la sécurité

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

Cet article décrit comment les modèles peuvent simplifier la sécurité pour Azure Pipelines. Les modèles peuvent définir la structure externe de votre pipeline et empêcher l’infiltration de code malveillante. Les modèles peuvent également inclure automatiquement des étapes pour effectuer des tâches telles que l’analyse des informations d’identification. Si plusieurs pipelines au sein de votre équipe ou organisation partagent la même structure, envisagez d’utiliser des modèles.

Les vérifications sur les ressources protégées forment l’infrastructure de sécurité fondamentale pour Azure Pipelines. Ces vérifications s’appliquent indépendamment de la structure, des étapes et des travaux du pipeline. Vous pouvez utiliser des modèles pour vous aider à appliquer ces vérifications.

Inclut et étend les modèles

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

  • Inclut les modèles incluent le code du modèle directement dans le fichier externe qui le référence, 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 
    
  • Étend les modèles définissent la structure externe du pipeline et offrent 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 dans le modèle et le pipeline final pour effectuer des éléments de configuration courants. Pour obtenir une référence complète, consultez la référence d’utilisation du modèle.

Étend les modèles

Pour les pipelines les plus sécurisés, commencez par utiliser des modèles étendus. Ces modèles définissent la structure externe du pipeline et empêchent le code malveillant d’infiltrer le pipeline.

Par exemple, le fichier de modèle suivant est nommé template.yml.

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

Le 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 extends des modèles, envisagez de les ancrer dans une branche ou une balise Git particulière. Par conséquent, s’il existe des modifications cassantes, les pipelines existants ne sont pas affectés. L’exemple précédent utilise cette fonctionnalité.

Fonctionnalités de sécurité du pipeline YAML

La syntaxe du pipeline YAML comprend plusieurs protections intégrées. Étend le modèle peut appliquer son utilisation. Pour améliorer la sécurité du pipeline, vous pouvez implémenter l’une des restrictions suivantes.

Cibles d’étape

Vous pouvez restreindre certaines étapes à exécuter dans un conteneur plutôt que sur l’hôte. Les étapes des conteneurs n’ont pas accès à l’hôte de l’agent, ce qui empêche ces étapes de modifier la configuration de l’agent ou de laisser du code malveillant pour une exécution ultérieure.

Par exemple, envisagez de limiter l’accès réseau. Sans accès réseau ouvert, les étapes utilisateur ne peuvent pas récupérer des packages à partir de sources non autorisées ou charger du code et des secrets vers des emplacements réseau externes.

L’exemple de pipeline suivant exécute les étapes sur l’hôte de l’agent avant d’exécuter des étapes à l’intérieur d’un conteneur.

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

Restrictions de la commande de journalisation de l’agent

Vous pouvez restreindre les services fournis par l’agent Azure Pipelines aux étapes utilisateur. 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. En mode restreint, la plupart des services de l’agent, tels que le chargement d’artefacts et l’attachement des résultats des tests, ne sont pas disponibles.

L’exemple de tâche suivant échoue, car sa target propriété indique à l’agent de ne pas autoriser la publication d’artefacts.

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

En restricted mode, la setvariable commande reste autorisée, de sorte que la prudence est nécessaire, car les variables de pipeline sont exportées en tant que variables d’environnement vers les tâches suivantes. Si les tâches génèrent des données fournies par l’utilisateur, telles que des problèmes ouverts récupérés via une API REST, elles peuvent être vulnérables aux attaques par injection. Le contenu utilisateur malveillant peut définir des variables d’environnement qui peuvent être exploitées pour compromettre l’hôte de l’agent.

Pour atténuer ce risque, les auteurs de pipelines peuvent déclarer explicitement les variables définies à l’aide de la setvariable commande de journalisation. Lorsque vous spécifiez une liste vide, tous les paramètres de variable ne sont pas autorisés.

L’exemple de tâche suivant échoue, car la tâche est uniquement autorisée à définir la expectedVar variable ou une variable préfixée avec ok.

- 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. Dans l’exemple suivant, la condition garantit que le code restreint est généré uniquement pour la branche principale.

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, vous pouvez empêcher l’exécution du script inline.

L’exemple de modèle suivant empêche l’exécution des types bashd’étapes, powershellpwshet script de l’exécution. Pour un verrouillage complet des scripts ad hoc, 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 le pipeline suivant qui étend ce modèle, les étapes de script sont supprimées et non exécutées.

# 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

Paramètres de type sécurisé

Avant l’exécution d’un pipeline, les modèles et leurs paramètres sont transformés 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 fournissant une énumération de choix spécifiques au lieu d’autoriser les chaînes de forme libre.

# 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

Lorsque le pipeline étend le modèle, il doit spécifier l’un des choix de pool disponibles.

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

Étapes du modèle

Un modèle peut inclure automatiquement des étapes dans un pipeline. Ces étapes peuvent effectuer des tâches telles que l’analyse des informations d’identification ou les vérifications de code statique. 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

Les modèles sont un mécanisme de sécurité précieux, mais leur efficacité dépend de 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 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. L’exécution s’arrête et vous avertit de l’échec de la vérification.

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.

Le modèle 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 une défaillance du pipeline, commentez la référence à params.yml.

# azure-pipeline.yml

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

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