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!