Compartir a través de


Plantillas para la seguridad

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

Las plantillas de Azure Pipelines permiten definir contenido, lógica y parámetros reutilizables en canalizaciones YAML. En este artículo se describe cómo las plantillas pueden ayudar a mejorar la seguridad de la canalización mediante:

  • Definir la estructura externa de una canalización para ayudar a evitar la infiltración de código malintencionado.
  • Inclusión automática de pasos para realizar tareas como el examen de credenciales.
  • Contribuye a aplicar comprobaciones en los recursos protegidos, que forman el marco de seguridad fundamental para Azure Pipelines y se aplican a todas las estructuras y componentes de las canalizaciones.

Este artículo forma parte de una serie que le ayuda a implementar medidas de seguridad para Azure Pipelines. Para más información, consulte Protección de Azure Pipelines.

Prerrequisitos

Categoría Requisitos
Azure DevOps - Implementar recomendaciones en Hacer seguro Azure DevOps y Secure Azure Pipelines.
- Conocimientos básicos de YAML y Azure Pipelines. Para más información, consulte Creación de la primera canalización.
Permisos - Para modificar los permisos de canalizaciones: miembro del grupo Administradores de proyectos.
- Para modificar permisos de la organización: Ser miembro del grupo Administradores de la colección de proyectos.

Incluye y amplía plantillas

Azure Pipelines proporciona plantillas de inclusión y plantillas de extensión.

  • Una includes plantilla incluye el código de la plantilla directamente en el archivo externo que hace referencia a la plantilla, similar a #include en C++. La siguiente canalización de ejemplo inserta la plantilla include-npm-steps.yml en la sección steps.

      steps:
      - template: templates/include-npm-steps.yml 
    
  • Una extends plantilla define la estructura externa de la canalización y ofrece puntos específicos para personalizaciones específicas. En el contexto de C++, las extends plantillas se asemejan a la herencia.

Al usar extends plantillas, también puede usar includes para realizar partes de configuración comunes tanto en la plantilla como en la canalización final. Para obtener más información, consulte Uso de plantillas YAML en canalizaciones para procesos reutilizables y seguros.

Extiende las plantillas

Para las canalizaciones más seguras, empiece por usar extends plantillas. Estas plantillas definen la estructura externa de la canalización y ayudan a evitar la infiltración de código malintencionado.

En el ejemplo siguiente se muestra un archivo de plantilla denominado template.yml.

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

La siguiente tubería de ejemplo extiende la plantilla 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

Sugerencia

Al configurar extends plantillas, considere anclarlas a una rama o etiqueta determinada de Git, de modo que los cambios disruptivos no afecten a las canalizaciones existentes. En el ejemplo anterior se usa esta característica.

Características de seguridad de canalización

La sintaxis de canalización de YAML incluye varias protecciones integradas. Extends las "templates" pueden imponer su uso para mejorar la seguridad del pipeline. Puede implementar cualquiera de las restricciones siguientes.

Destinos de paso

Puede restringir los pasos especificados para ejecutarse en un contenedor en lugar de en el host. Los pasos de los contenedores no pueden acceder al host del agente, por lo que no pueden modificar la configuración del agente ni dejar código malintencionado para su ejecución posterior.

Por ejemplo, puede ejecutar pasos de usuario en un contenedor para evitar que accedan a la red, por lo que no pueden recuperar paquetes de orígenes no autorizados ni cargar código y secretos en ubicaciones externas.

La canalización de ejemplo siguiente ejecuta un paso en el host del agente que podría modificar la red host, seguido de un paso dentro de un contenedor que limita el acceso a la red.

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

Parámetros con seguridad de tipos

Antes de que se ejecute una canalización, las plantillas y sus parámetros se transforman en constantes. Los parámetros de plantilla pueden mejorar la seguridad de tipos para los parámetros de entrada.

En la plantilla de ejemplo siguiente, los parámetros restringen las opciones del grupo de canalizaciones disponibles mediante la enumeración de opciones específicas en lugar de permitir cualquier cadena.

# 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

Para ampliar la plantilla, la canalización debe especificar una de las opciones de grupo disponibles.

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

Restricciones de comandos de registro de agentes

Los usuarios solicitan servicios mediante comandos de registro, que son cadenas especialmente formateadas impresas en la salida estándar. Puede restringir los servicios que proporcionan los comandos de registro para los pasos del usuario. En modo restricted, la mayoría de los servicios de agente, como la carga de artefactos y la vinculación de resultados de pruebas, no están disponibles para el registro de comandos.

En el ejemplo siguiente, la target propiedad indica al agente que restrinja la publicación de artefactos. Como resultado, falla la tarea de publicación de artefactos.

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

Variables en comandos de registro

El setvariable comando permanece permitido en restricted modo, por lo que las tareas que generan datos proporcionados por el usuario, como problemas abiertos recuperados a través de una API REST, podrían ser vulnerables a ataques por inyección. El contenido malintencionado del usuario podría establecer variables que exportan a tareas posteriores como variables de entorno y podrían poner en peligro el host del agente.

Para mitigar este riesgo, puede declarar explícitamente las variables que se pueden establecer mediante el setvariable comando de registro. Si especifica una lista vacía en settableVariables, se prohíbe la configuración de variables.

En el siguiente ejemplo, se restringe settableVariables a expectedVar y cualquier variable con el prefijo ok. Se produce un error en la tarea porque intenta establecer una variable diferente denominada BadVar.

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

Fase condicional o ejecución del trabajo

Puede restringir las fases y los trabajos para que solo se ejecuten en condiciones específicas. En el ejemplo siguiente se garantiza que el código restringido solo se compila para la main rama.

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

Modificación de sintaxis

Las plantillas de Azure Pipelines tienen la flexibilidad de iterar y modificar la sintaxis de YAML. Mediante la iteración, puede aplicar características de seguridad yaML específicas.

Una plantilla también puede reescribir los pasos de usuario, lo que permite que solo se ejecuten las tareas aprobadas. Por ejemplo, la plantilla puede impedir la ejecución de scripts en línea.

La siguiente plantilla de ejemplo impide que se ejecuten los tipos de paso de script bash, powershell, pwsh y script. Para un bloqueo completo de scripts, también puede bloquear BatchScript y 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 }}'

En el pipeline de ejemplo siguiente que extiende la plantilla anterior, los pasos del script se eliminan y no se ejecutan.

# 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

Pasos de plantilla

Una plantilla puede incluir automáticamente pasos en una canalización, por ejemplo, para realizar el examen de credenciales o comprobaciones de código estático. La plantilla siguiente inserta los pasos antes y después de los pasos del usuario en cada trabajo.

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

Aplicación de plantillas

La eficacia de las plantillas como mecanismo de seguridad se basa en la aplicación. Los puntos de control clave para aplicar el uso de plantillas son recursos protegidos.

Puede configurar aprobaciones y comprobaciones para el grupo de agentes u otros recursos protegidos, como repositorios. Para obtener un ejemplo, consulte Incorporación de una comprobación de recursos de repositorio.

Plantillas necesarias

Para aplicar el uso de una plantilla específica, configure la comprobación de plantilla necesaria en la conexión de servicio de un recurso. Esta comprobación solo se aplica cuando la canalización se extiende desde una plantilla.

Al ver el trabajo de canalización, puede supervisar el estado de la comprobación. Si la canalización no se extiende desde la plantilla necesaria, se produce un error en la comprobación.

Captura de pantalla que muestra una comprobación de aprobación con errores.

Cuando se usa la plantilla necesaria, la comprobación pasa.

Captura de pantalla que muestra una comprobación de aprobación superada.

Por ejemplo, se debe hacer referencia a la siguiente plantilla params.yml en cualquier canalización que la extienda.

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

La canalización de ejemplo siguiente amplía la plantilla de params.yml y la requiere para su aprobación. Para demostrar un error de canalización, comente la extends referencia a 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'