Cómo trabajar con workflows anidados en GitHub Actions

Para uno de los vídeos de mi serie sobre DevSecOps he estado jugando con unos flujos que lanzan diferentes herramientas para el escaneo de vulnerabilidades en mi código, sea del tipo que sea 😙. Para poder reutilizarlos en ese u otros repos he configurado los mismos para poder llamarlos desde dentro de otros flujos. En este artículo te cuento cómo lo he hecho.

Este ejemplo está basado en mi vídeo sobre la búsqueda de vulnerabilidades en tu código en Terraform, el cuál puedes ver directamente desde aquí 👇🏻

Flujo reutilizable

Para que puedas verlo con un ejemplo he creado un flujo con la siguiente pinta:

name: Snyk Scan
on:
  workflow_call:
    secrets:
      SNYK_TOKEN:
        required: true
    inputs:
      working-directory:
        description: 'The directory to run Snyk in'
        type: string
        required: false
        default: '.'
jobs:
  snyk:
    env:
      SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up Snyk CLI to check for security issues
        uses: snyk/actions/setup@806182742461562b67788a64410098c9d9b96adb
              
      # Runs Snyk Infrastructure as Code (IaC) analysis and uploads result to Snyk.
      - name: Snyk IaC test and report
        continue-on-error: true
        working-directory: ${{ inputs.working-directory }}
        run: |
          snyk iac test --sarif || true
          snyk iac test --sarif > snyk-iac.sarif || true       
      # Push the Snyk Code results into GitHub Code Scanning tab
      - name: Upload result to GitHub Code Scanning
        uses: github/codeql-action/upload-sarif@v2
       
        with:
          sarif_file: ${{ inputs.working-directory }}/snyk-iac.sarif

Este, como ves, parece un flujo normal pero le diferencia de los que estás acostumbrados por el apartado on, donde en lugar de utilizar el típico push, pull_request o incluso workflow_dispatch estoy usando otro evento llamado workflow_call, el cuál es el que me va a permitir que este pueda ser llamado desde otros flujos, aunque estén incluso en otro repo. Además, he querido ponerte este ejemplo porque en el caso de este flujo necesita tener un secreto para funcionar, que en este caso es el token para la API de Snyk, por lo que debo indicarlo como parte de este evento para que pueda recibirlo del flujo «padre». También he especificado que necesito como input para este workflow el directorio de trabajo (working-directory) porque no siempre los terraform van a estar en el raíz, pero como este input no es obligatorio puedo ponerle por defecto cuál es el valor del mismo.

El flujo padre

Por otro lado, el flujo padre, hará las llamadas de la siguiente forma:

name: IaC scans
on:
  push:
    branches: ["main" ]
  pull_request:
    branches: ["main"]
  workflow_dispatch:
permissions:
  contents: read
  
jobs:
  checkov:
    permissions:
      contents: read
      security-events: write
    uses: 0GiS0/scan-tf-vulnerabilities/.github/workflows/checkov.yaml@main
    with:
      working-directory: terraform
  trivy:
    permissions:
      contents: read
      security-events: write
    uses: 0GiS0/scan-tf-vulnerabilities/.github/workflows/trivy.yaml@main
    with:
      working-directory: terraform
  terrascan:
    permissions:
      contents: read
      security-events: write
    uses: 0GiS0/scan-tf-vulnerabilities/.github/workflows/terrascan.yaml@main
    with:
      working-directory: terraform
  snyk:
    permissions:
      contents: read
      security-events: write
    uses: 0GiS0/scan-tf-vulnerabilities/.github/workflows/snyk.yaml@main
    with:
      working-directory: terraform
    secrets:
      SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

Como puedes ver, este puede llamar a diferentes workflows (hasta 20 y con cuatro niveles a día de hoy), cada uno con unos permisos en concreto, pasarle a cada uno los secretos que corresponda (puedes usar también la palabra clave inherit para pasar todos si quieres) y, a través del array with, todos los inputs que necesite.

¡Saludos!