Skip to main content

Como configurar a GitHub Action para analisar imagens Docker

feature docker secrets

19 de maio de 2023

0 minutos de leitura

Hoje em dia, o produto final da maioria dos repositórios Git é uma imagem Docker, que depois é usada em uma implantação no Kubernetes. Com a segurança em evidência (e por bons motivos), analisar as imagens Docker criadas no CI é essencial.

Neste artigo, vou usar o GitHub Actions para criar imagens Docker e analisá-las em busca de vulnerabilidades de segurança. A imagem Docker criada no CI também será enviada ao registro Docker do GitHub.

Xícara de café com latte art abaixo do texto “Verificação de imagem Docker no CI com GITHUB”

Criando um workflow do GitHub Actions

O workflow de CI que vamos criar tem a seguinte estrutura:

  • Testar

  • Criar a imagem

  • Analisar a imagem

A imagem criada na segunda etapa é enviada ao registro Docker do GitHub e, na terceira etapa, baixada novamente de lá.

Criar o job de teste

Supondo que seu repositório no GitHub não tenha uma configuração de CI, vamos criar um arquivo no caminho `.github/workflows/ci.yaml` com o seguinte conteúdo:

    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

O atributo `on` especifica os eventos que disparam a execução deste workflow. Neste caso, qualquer push para a branch `master` e cada pull request disparam o workflow de CI.

As etapas do job incluem:

  • Checkout 🔔: obtém o código do repositório

  • Configurar o ambiente Node 🧱: instala o ambiente Node.js 18

  • Instalar e testar 🪲: instala as dependências e executa os testes

O job de teste aqui é apenas um exemplo de execução dos testes unitários de um projeto Node.js. Fique à vontade para adaptá-lo à linguagem de programação e à estrutura do seu projeto.

Embora não seja necessário ter um job de teste para criar a imagem Docker, essa é uma boa prática, então mantive essa etapa.

Criar o job de build do Docker

Para criar o job de build do Docker, primeiro vou definir duas variáveis de ambiente no início do arquivo, abaixo de `on`:

  on:
      ...

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

Agora, vamos criar o `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 }}

Vamos analisar o código. A seção de permissões é necessária porque queremos enviar imagens Docker para o registro do GitHub.

O job é executado na versão mais recente do Ubuntu e depende da conclusão do job de teste. Assim, garantimos que só criaremos imagens Docker para código funcional. Se você não criou o job de teste, remova a linha `needs`.

Agora, vamos às etapas:

  • Checkout 🔔: obtém o código do repositório. Isso é necessário para acessar o Dockerfile e seu contexto.

  • Fazer login no registro de contêineres 📦

  • Criar e enviar a imagem Docker 🐳: nesta etapa, usamos as variáveis de ambiente definidas no início do arquivo para especificar a tag da imagem Docker.

Criar o job de análise

Agora, vamos adicionar o job de análise da imagem 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

Esta etapa usa o Snyk, o mecanismo de análise de segurança por trás do `docker scan`. Para usá-lo, crie uma conta gratuita e armazene o token como um secret:

  • Cadastre-se aqui

  • Obtenha o token conforme explicado aqui

  • Adicione-o aos secrets de CI do seu repositório no GitHub com o nome `SNYK_TOKEN`, conforme explicado no apêndice

Agora, vamos às etapas:

  • Checkout 🛎️: o scanner tem um desempenho melhor quando também pode acessar o Dockerfile.

  • Fazer login no registro de contêineres 📦: para obter as imagens Docker que enviamos para lá anteriormente.

  • Analisar a imagem Docker 🐳: este job analisa a imagem Docker e registra as vulnerabilidades em um arquivo chamado `snyk.sarif`. O GitHub reconhece esse formato e pode exibi-lo no PR — por isso, temos a próxima etapa.

  • Enviar o relatório do Snyk como SARIF 📦: aqui, enviamos ao GitHub o arquivo `sarif` gerado na etapa anterior.

As vulnerabilidades enviadas ao GitHub aparecem no seu PR assim:

Anotação de análise de código em um Dockerfile mostrando uma vulnerabilidade de alta gravidade no curl e uma verificação com falha.

Conclusão


Neste artigo, criamos um workflow do GitHub Actions com três jobs: executar os testes, criar a imagem Docker, enviá-la ao registro do GitHub, verificar problemas de segurança e enviar o relatório de vulnerabilidades para que o GitHub possa interpretá-las e exibi-las nos PRs.

Apêndice: como adicionar um secret de CI no GitHub

No seu repositório:

  1. Clique na aba de configurações.

  2. No menu à esquerda, na seção Security, clique em Secrets and variables.

  3. Em seguida, entre as opções de menu que aparecerem, clique em Actions.

  4. No canto superior direito, clique no botão verde New repository secret.

  5. Na seção Name, digite o nome desejado, por exemplo, `SNYK_TOKEN`.

  6. Na seção Secret, cole o secret, por exemplo, o token do Snyk que você copiou do site do Snyk.

  7. Em seguida, clique no botão verde Add secret.

Página de configurações do repositório GitHub exibindo segredos e variáveis do Actions, incluindo os segredos do repositório ACCESS_TOKEN e SNYK_TOKEN.