Skip to main content

Configurar la acción de GitHub para analizar imágenes de Docker

feature docker secrets

19 de mayo de 2023

0 minutos de lectura

Hoy en día, el producto final de la mayoría de los repositorios de Git es una imagen de Docker que luego se usa en una implementación de Kubernetes. Como la seguridad es un tema de gran interés (y con razón), es vital analizar las imágenes de Docker que creas en CI.

En este artículo, usaré GitHub Actions para crear imágenes de Docker y analizarlas en busca de vulnerabilidades de seguridad. La imagen de Docker creada en CI también se publica en el registro de Docker de GitHub.

Taza de café con arte latte debajo del texto «Análisis de imágenes Docker en CI con GITHUB»

Crear un flujo de trabajo de GitHub Actions

El flujo de trabajo de CI que vamos a crear tiene la siguiente estructura:

  • Pruebas

  • Crear imagen

  • Analizar imagen

La imagen creada en el segundo paso se publica en el registro de Docker de GitHub y luego se vuelve a descargar desde allí en la tercera etapa.

Crear el trabajo de pruebas

Si tu repositorio de GitHub no tiene una configuración de CI, crearemos un archivo en la ruta `.github/workflows/ci.yaml` con el siguiente contenido:

    name: ci
    on:
      push:
        branches:
          - master
      pull_request:

    jobs:
      test:
        runs-on: ubuntu-latest

        strategy:
          matrix:
            node-version: [ 18.x ]

        steps:
          - name: Checkout 🛎️
            uses: actions/checkout@v3

          - name: Setup Node environment 🧱: Node.js ${{ matrix.node-version }}
            uses: actions/setup-node@v3
            with:
              node-version: ${{ matrix.node-version }}

          - name: Install and test 🪲
            run: |
              npm ci
              npm test

El atributo `on` especifica los eventos que activan este flujo de trabajo. En este caso, cualquier push a la rama `master` y cada pull request activan el flujo de trabajo de CI.

Los pasos del trabajo incluyen lo siguiente:

  • Checkout 🔔: Obtiene el código del repositorio

  • Configurar el entorno de Node 🧱: Instala el entorno de Node.js 18

  • Instalar y probar 🪲: Instala las dependencias y ejecuta las pruebas

El trabajo de pruebas es solo un ejemplo de cómo ejecutar las pruebas unitarias de un proyecto de Node.js. Puedes modificarlo para adaptarlo al lenguaje y la estructura de tu proyecto.

Aunque no es necesario tener un trabajo de pruebas para crear la imagen de Docker, es una buena práctica, así que lo incluí.

Crear el trabajo de compilación de Docker

Para crear el trabajo de compilación de Docker, primero crearía dos variables de entorno al principio del archivo, debajo de `on`:

  on:
      ...

    env:
      DOCKER_IMAGE_TAG: ${{ github.ref == 'refs/heads/master' && 'prod-' || 'dev-' }}${{ github.sha }}
      GITHUB_REGISTRY: ghcr.io
      GITHUB_REPOSITORY: ${{ github.repository }}

Ahora, creemos el `build_docker job`:

    jobs:
      test:
        ...

      build_image:
        permissions:
          id-token: write
          contents: read
          packages: write
        runs-on: ubuntu-latest
        needs: [ test ]

        steps:
          - name: Checkout 🛎️
            uses: actions/checkout@v2

          - name: Log in to the Container registry 📦
            uses: docker/login-action@v2
            with:
              registry: ${{ env.GITHUB_REGISTRY }}
              username: ${{ github.actor }}
              password: ${{ secrets.GITHUB_TOKEN }}

          - name: Build and push Docker image 🐳
            uses: docker/build-push-action@v3
            with:
              push: true
              tags: |
                ${{ env.GITHUB_REGISTRY }}/${{ env.GITHUB_REPOSITORY }}:${{ env.DOCKER_IMAGE_TAG }}

Veamos el código. La sección de permisos es necesaria porque queremos publicar imágenes de Docker en el registro de GitHub.

El trabajo se ejecuta en la versión más reciente de Ubuntu y requiere que el trabajo de pruebas termine. Esto garantiza que solo creemos imágenes de Docker para código funcional. Si omitiste el trabajo de pruebas, elimina la línea needs.

Ahora, veamos los pasos:

  • Checkout 🔔: Obtiene el código del repositorio. Esto es necesario para obtener el Dockerfile y su contexto.

  • Iniciar sesión en el registro de contenedores 📦

  • Crear y publicar la imagen de Docker 🐳: En este paso, usamos las variables de entorno que agregamos al principio del archivo para especificar la etiqueta de la imagen de Docker.

Crear el trabajo de análisis

Ahora, agreguemos el trabajo para analizar la imagen de Docker:

    jobs:
      test:
        ...

      build_image:
        ...

      scan_docker_image:
        permissions:
          id-token: read
          contents: read
          packages: read
        runs-on: ubuntu-latest
        needs: [ build_image ]
        steps:
          - name: Checkout 🛎️
            uses: actions/checkout@v2

          - name: Log in to the Container registry 📦
            uses: docker/login-action@v2
            with:
              registry: ${{ env.GITHUB_REGISTRY }}
              username: ${{ github.actor }}
              password: ${{ secrets.GITHUB_TOKEN }}

          - name: Scan Docker image 🐳
            uses: snyk/actions/docker@master
            continue-on-error: true
            with:
              image: ${{ env.GITHUB_REGISTRY }}/${{ env.GITHUB_REPOSITORY }}:${{ env.DOCKER_IMAGE_TAG }}
              args: --file=Dockerfile --severity-threshold=high --sarif-file-output=snyk.sarif
            env:
              SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}

          - name: Upload Snyk report as sarif 📦
            uses: github/codeql-action/upload-sarif@v2
            with:
              sarif_file: snyk.sarif

Este paso usa Snyk, el motor de análisis de seguridad que impulsa `docker scan`. Para usarlo, debes crear una cuenta gratuita y guardar su token como secreto:

  • Regístrate aquí

  • Obtén el token como se describe aquí

  • Agrégalo a los secretos de CI de tu repositorio de GitHub con el nombre `SNYK_TOKEN`, como se explica en el apéndice

Ahora, continuemos con los pasos:

  • Checkout 🛎️: El analizador funciona mejor si también tiene acceso al Dockerfile.

  • Iniciar sesión en el registro de contenedores 📦: Para obtener las imágenes de Docker que publicamos allí anteriormente.

  • Analizar la imagen de Docker 🐳: Este trabajo analiza la imagen de Docker e informa las vulnerabilidades en un archivo llamado `snyk.sarif`. GitHub reconoce este formato de archivo y puede mostrarlo en el PR, por eso tenemos el siguiente paso.

  • Subir el informe de Snyk como sarif 📦: Aquí subimos a GitHub el archivo `sarif` que generamos en el paso anterior.

Las vulnerabilidades que se suben a GitHub aparecen en tu PR de esta manera:

Anotación del análisis de código en un Dockerfile que muestra una vulnerabilidad de curl de gravedad alta y una verificación fallida.

Conclusión


En este artículo, creamos un flujo de trabajo de GitHub Actions con 3 trabajos para ejecutar las pruebas, crear la imagen de Docker, publicarla en el registro de GitHub, buscar problemas de seguridad y subir el informe de vulnerabilidades para que GitHub pueda interpretarlo y mostrarlo en los PR.

Apéndice: Agregar un secreto de CI en GitHub

En tu repositorio:

  1. Haz clic en la pestaña de configuración.

  2. En el panel de menú de la izquierda, en la sección Security, haz clic en Secrets and variables.

  3. Luego, en las opciones de menú que aparecen, haz clic en Actions.

  4. En la parte superior derecha, haz clic en el botón verde que dice New repository secret.

  5. En la sección Name, escribe el nombre que quieras, por ejemplo, `SNYK_TOKEN`.

  6. En la sección Secret, pega el secreto, por ejemplo, el token de Snyk que copiaste del sitio web de Snyk.

  7. Luego, haz clic en el botón verde que dice Add secret.

Página de configuración del repositorio de GitHub que muestra los secretos y las variables de Actions, incluidos los secretos del repositorio ACCESS_TOKEN y SNYK_TOKEN.