Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .github/workflows/03-build-containers.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: "Nível 3: Containers e Segurança"

on:
pull_request:
types: [closed]
branches: [ desafio-nivel-3 ]

permissions:
contents: read
packages: write

env:
CHALLENGE_LEVEL: 3
CHALLENGE_NAME: "containers-e-seguranca"
REGISTRY: ghcr.io

jobs:
build-scan-and-push:
name: "Build, Lint, Trivy Scan e Push no GHCR"
if: #????
runs-on: ubuntu-latest

steps:
# AQUI VAI O CÓDIGO DO DESAFIO :)

generate-certificate: # DAQUI PARA BAIXO, NÃO ALTERAR
name: "Desafio Nível 3 - Certificado"
needs: build-scan-and-push
if: success()
runs-on: ubuntu-latest
steps:
- name: "Gerar certificado"
run: |
mkdir -p certificates
cat > certificates/level-3-certificate.md << EOF
# Certificado de Conclusão - Nível 3

**Descomplicando Github Actions - GitHub Actions Edition**
---

Este certificado atesta que **${{ github.actor }}** concluiu com sucesso:
## Nível 3: Containers e Segurança

**Competências desenvolvidas:**
- Build de imagem Docker
- Lint de Dockerfile com Hadolint
- Scan de vulnerabilidades com Trivy (CRITICAL = 0)
- Relatório de vulnerabilidades como artefato
- Smoke test de execução do container
- Publicação no GitHub Container Registry (GHCR) condicionada ao scan
- Boas práticas de supply chain

**Data de conclusão:** $(date)
**Repositório:** ${{ github.repository }}
**Workflow:** ${{ github.run_id }}

---
**Badge conquistado:** Containers e Segurança

---
*Certificado gerado automaticamente pelo GitHub Actions*
*LINUXtips*
EOF

- name: "Upload do certificado"
uses: actions/upload-artifact@v4
with:
name: level-3-certificate
path: certificates/
retention-days: 30
60 changes: 60 additions & 0 deletions DESAFIO-03.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Desafio 03 – Containers e Segurança (GHCR)

## Requisitos do pipeline

- Deve existir um novo workflow do GitHub Actions para o Nível 3 (containers e segurança).
- O disparo deve acontecer quando um Pull Request para a branch `desafio-nivel-3` for fechado com merge (evento `pull_request`, `types: [closed]` + condição `github.event.pull_request.merged == true`).
- O workflow deve:
- Fazer login no GitHub Container Registry (GHCR) utilizando `GITHUB_TOKEN`.
- Montar a tag da imagem sempre com o SHA do commit, e com `owner`, `IMAGE_NAME` e `registry` em minúsculas.
- Buildar a imagem Docker para permitir o scan de vulnerabilidades.
- Executar lint do `Dockerfile` com Hadolint, salvando o resultado em `lint-report.txt` e falhando se forem encontrados os problemas DL3006 ou DL3008.
- Executar o scan de vulnerabilidades com Trivy na imagem construída.
- O resultado deve ser salvo em `trivy-report.txt` e publicado como artefato.
- O workflow deve falhar caso o Trivy encontre vulnerabilidades CRITICAL.
- Publicar no GHCR somente se todas as validações passarem.
- Gerar o artefato `level-3-certificate.md` (não alterar a seção do certificado, assim como nos desafios anteriores).

### Importante: Proteção de branch

Antes de executar, configure a proteção da branch `desafio-nivel-3` como mostramos na live de quarta. Isso garante a qualidade do ciclo de revisão e evita merges diretos na `desafio-nivel-3` sem validações. Veja a demonstração aqui: [AO VIVO - Descomplicando Github Actions - Resolvendo Desafio](https://www.youtube.com/watch?v=VihvfGx58IY).

## Variável obrigatória

- Crie uma variável de repositório chamada `IMAGE_NAME` (em: Settings > Secrets and variables > Actions > Variables) com o nome da aplicação a ser usada no nome da imagem (ex.: `desafio3-linuxtips-gha`).
- Essa variável é obrigatória e será utilizada para compor o nome final da imagem no GHCR.

## Actions obrigatórias

- `docker/login-action@v3`
- `docker/build-push-action@v6`
- `aquasecurity/trivy-action@0.28.0`

### Política de lint (Hadolint)

Para nós, os checks do Hadolint devem focar nos itens que mais impactam segurança e reprodutibilidade. Portanto, este pipeline deve falhar apenas quando forem detectadas as regras abaixo:

- DL3006: uso consistente e fixação do gerenciador de pacotes/base da imagem (garante builds reprodutíveis);
- DL3008: instalação de pacotes sem pin de versões (evita deriva de dependências e janelas de vulnerabilidade).

Por política da empresa e conformidade de supply chain, consideramos DL3006 e DL3008 bloqueadores.

## Regras de publicação da imagem

- Registry: `ghcr.io`
- Tag obrigatória: o SHA do commit (`${{ github.sha }}`)
- Nome completo (exemplo): `ghcr.io/<owner>/<IMAGE_NAME>:<sha>`
- O nome completo deve ser convertido para minúsculas para evitar erros no push.

## Esqueleto mínimo do workflow

No arquivo `.github/workflows/03-build-containers.yml`, deixe apenas o esqueleto mínimo dentro do job `build-scan-and-push`. Defina os nomes dos steps como dicas, mas sem implementação.

## Critérios de aceite

- [ ] Workflow Nível 3 dispara somente após PR mergeado na `desafio-nivel-3`.
- [ ] `Dockerfile` analisado com Hadolint; gerar artefato `lint-report.txt` e falhar em DL3006/DL3008.
- [ ] Imagem construída e escaneada com Trivy (CRITICAL = 0) com artefato `trivy-report.txt`.
- [ ] Push realizado no GHCR apenas se todas as verificações passarem.
- [ ] Tag da imagem é exatamente o SHA do commit e nome em minúsculas.
- [ ] Uso das actions nas versões exigidas.
20 changes: 20 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM node:18-alpine AS base

WORKDIR /app

RUN apk add --no-cache gcompat=1.1.0-r4

COPY package*.json ./

RUN npm ci --omit=dev

COPY . .

ENV NODE_ENV=production
ENV PORT=3000

EXPOSE 3000

CMD ["node", "server.js"]


2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ <h3 id="testsCount">0</h3>
</section>

<section class="challenges-section">
<h2><i class="fas fa-trophy"></i> Desafios 01 e 02</h2>
<h2><i class="fas fa-trophy"></i> Desafios 01 a 03</h2>
<div class="challenges-grid" id="challengesGrid">
<!-- Desafios serão carregados dinamicamente -->
</div>
Expand Down
11 changes: 9 additions & 2 deletions public/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ const challenges = [
description: 'Implemente testes com Jest e atinja cobertura mínima de 80% no pipeline. Gere e faça upload do certificado do nível 2.',
badge: 'testes-automatizados',
reward: 'Badge: Desafio 02 Concluído'
},
{
id: 'containers-seguros',
title: 'Desafio 03 - Containers e Segurança',
description: 'Construa a imagem Docker, escaneie com Trivy e publique no ECR apenas se não houver vulnerabilidades CRÍTICAS. Gere e faça upload do certificado do nível 3.',
badge: 'containers-seguros',
reward: 'Badge: Desafio 03 Concluído'
}
];

Expand Down Expand Up @@ -229,7 +236,7 @@ function updateBadgesDisplay() {
// Atualizar seção do certificado
function updateCertificateSection() {
const certificateSection = document.getElementById('certificateSection');
const hasAnyCertificate = appState.badges.includes('first-steps') || appState.badges.includes('testes-automatizados');
const hasAnyCertificate = appState.badges.includes('first-steps') || appState.badges.includes('testes-automatizados') || appState.badges.includes('containers-seguros');

if (certificateSection) {
certificateSection.style.display = hasAnyCertificate ? 'block' : 'none';
Expand Down Expand Up @@ -553,7 +560,7 @@ Badges disponíveis: first-steps, testes-automatizados
window.onBadgeClick = function(badgeId) {
const username = document.getElementById('certificateUsername').value.trim();
if (!username) return;
const level = badgeId === 'testes-automatizados' ? 2 : 1;
const level = badgeId === 'containers-seguros' ? 3 : (badgeId === 'testes-automatizados' ? 2 : 1);
selectedCertificateLevel = level;
generateCertificate();
}
Expand Down
Loading