Sicurezza tramite modelli

I controlli sulle risorse protette sono il blocco predefinito di base della sicurezza per Azure Pipelines. I controlli funzionano indipendentemente dalla struttura, ad esempio le fasi e i processi, della pipeline. Se più pipeline nel team o nell'organizzazione hanno la stessa struttura, è possibile semplificare ulteriormente la sicurezza usando i modelli.

Azure Pipelines offre due tipi di modelli: include ed estende. I modelli inclusi si comportano come in C++: è come incollare il codice del modello direttamente nel #include file esterno, che vi fa riferimento. Per continuare la metafora C++, i modelli sono più simili all'ereditarietà: il modello fornisce la struttura esterna della pipeline e un set di posizioni in cui il consumer del modello può apportare extends modifiche mirate.

Usare modelli di estensione

Per le pipeline più sicure, è consigliabile iniziare con extends i modelli. Fornendo la struttura esterna, un modello può impedire a codice dannoso di entrare nella pipeline. È comunque possibile usare , sia nel modello che nella pipeline finale, per fattori includes di configurazione comuni. Per usare un modello extends, la pipeline potrebbe essere simile all'esempio seguente.

# 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

Quando si configurano extends i modelli, è consigliabile ancorarli a un particolare ramo o tag Git. In questo modo, se è necessario apportare modifiche di rilievo, le pipeline esistenti non saranno interessate. Gli esempi precedenti usano questa funzionalità.

Funzionalità di sicurezza applicate tramite YAML

Esistono diverse protezioni integrate nella sintassi YAML e un modello extends può imporre l'utilizzo di una o tutte.

Destinazioni dei passaggi

Limitare alcuni passaggi da eseguire in un contenitore anziché nell'host. Senza l'accesso all'host dell'agente, i passaggi dell'utente non possono modificare la configurazione dell'agente o lasciare codice dannoso per l'esecuzione successiva. Eseguire prima il codice nell'host per rendere il contenitore più sicuro. Ad esempio, è consigliabile limitare l'accesso alla rete. Senza l'accesso aperto alla rete, i passaggi dell'utente non potranno accedere ai pacchetti da origini non autorizzate o caricare codice e segreti in un percorso di rete.

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

Restrizioni dei comandi di registrazione dell'agente

Limitare i servizi che Azure Pipelines'agente fornirà ai passaggi dell'utente. I passaggi richiedono i servizi usando "comandi di registrazione" (stringhe formattate in modo speciale stampate in stdout). In modalità con restrizioni, la maggior parte dei servizi dell'agente, ad esempio il caricamento di elementi e il collegamento dei risultati dei test, non è disponibile.

# 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

Uno dei comandi ancora consentiti in modalità con restrizioni è il setvariable comando . Poiché le variabili della pipeline vengono esportate come variabili di ambiente in attività successive, le attività che generano dati forniti dall'utente (ad esempio, il contenuto dei problemi aperti recuperati da un'API REST) possono essere vulnerabili agli attacchi injection. Tale contenuto utente può impostare variabili di ambiente che a loro volta possono essere usate per sfruttare l'host dell'agente. Per non consentire questa operazione, gli autori della pipeline possono dichiarare in modo esplicito quali variabili sono impostabili tramite il setvariable comando di registrazione. La specifica di un elenco vuoto non consente l'impostazione di tutte le variabili.

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

Inserimento condizionale di fasi o processi

Limitare l'esecuzione di fasi e processi in condizioni specifiche. Le condizioni possono essere utili, ad esempio, per assicurarsi di creare solo determinati rami.

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

Richiedere una sintassi specifica con i modelli extends

I modelli possono scorrere e modificare/non consentire qualsiasi sintassi YAML. L'iterazione può forzare l'uso di una particolare sintassi YAML, incluse le funzionalità precedenti.

Un modello può riscrivere i passaggi utente e consentire l'esecuzione solo di determinate attività approvate. È ad esempio possibile impedire l'esecuzione di script inline.

Avviso

Nell'esempio seguente viene impedita l'esecuzione dei passaggi "bash", "powershell", "pwsh" e "script". Per il blocco completo degli script ad hoc, è anche necessario bloccare "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

Parametri type-safe

I modelli e i relativi parametri vengono trasformati in costanti prima dell'esecuzione della pipeline. I parametri di modello forniscono l'sicurezza dei tipi ai parametri di input. Ad esempio, può limitare i pool che possono essere usati in una pipeline offrendo un'enumerazione di possibili opzioni anziché una stringa in formato libero.

# 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

Impostare i modelli necessari

Per richiedere l'uso di un modello specifico, è possibile impostare il controllo del modello richiesto per una risorsa o un ambiente. Il controllo del modello richiesto può essere usato per l'estensione da un modello.

È possibile controllare lo stato di un controllo quando si visualizza un processo della pipeline. Quando una pipeline non si estende dal modello di richiesta, il controllo avrà esito negativo e l'esecuzione verrà interrotta. Si noti che il controllo non è riuscito.

approval check fails

Quando viene usato il modello richiesto, si può vedere che il controllo è stato superato.

approval check passes

In questo caso params.yml il modello è necessario con un'approvazione per la risorsa. Per attivare l'esito negativo della pipeline, impostare come commento il riferimento 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'

Passaggi aggiuntivi

Un modello può aggiungere passaggi senza che l'autore della pipeline li includa. Questi passaggi possono essere usati per eseguire l'analisi delle credenziali o i controlli statici del codice.

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

Passaggi successivi

Informazioni su come gestire gli input in modo sicuro tramite variabili e parametri.