Add job deploy #72
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: devsecops-pipeline | |
| on: | |
| pull_request: | |
| push: | |
| branches: ["main"] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| security-events: write | |
| packages: write # útil si luego publicas en GHCR | |
| discussions: write | |
| pull-requests: write | |
| concurrency: | |
| group: devsecops-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| # 🔁 Cambia esto para probar cada escenario (apps/10-secrets-leak, 20-sast-bugs, etc.) | |
| APP_DIR: apps/10-secrets-leak | |
| # Parámetros de despliegue local | |
| IMAGE_NAME: demo-app | |
| IMAGE_TAG: local | |
| KIND_CLUSTER: devsecops | |
| SERVICE_RELEASE_NAME: demo | |
| jobs: | |
| # ────────────────────────────────────────────────────────────────────────────── | |
| # Secrets + SAST | |
| # ────────────────────────────────────────────────────────────────────────────── | |
| secrets: | |
| name: Secrets scanning (Gitleaks) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Clean gitleaks | |
| run: rm -f /tmp/gitleaks.tmp | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # para análisis que miran historial | |
| - name: Gitleaks | |
| uses: gitleaks/gitleaks-action@v2 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Si usas un gitleaks.toml propio, añade inputs/vars según la acción | |
| sast: | |
| name: SAST (Semgrep) | |
| runs-on: ubuntu-latest | |
| needs: [secrets] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: self-hosted (no bloqueante) | |
| run: | | |
| docker run --rm -v "$PWD:/src" returntocorp/semgrep:latest \ | |
| semgrep scan --config p/ci --config .semgrep | |
| - name: Export SARIF (para pestaña Security) | |
| run: | | |
| docker run --rm -v "$PWD:/src" returntocorp/semgrep:latest \ | |
| semgrep scan --config p/ci --config .semgrep --sarif -o semgrep.sarif || true | |
| - uses: github/codeql-action/upload-sarif@v3 | |
| with: { sarif_file: semgrep.sarif } | |
| # ────────────────────────────────────────────────────────────────────────────── | |
| # IaC + Build + SBOM | |
| # ────────────────────────────────────────────────────────────────────────────── | |
| # iac: | |
| # name: IaC scan (Checkov) | |
| # runs-on: ubuntu-latest | |
| # needs: [sast] | |
| # steps: | |
| # - uses: actions/checkout@v4 | |
| # - name: Checkov (K8s/Terraform) | |
| # uses: bridgecrewio/checkov-action@master | |
| # with: | |
| # directory: . | |
| # quiet: true | |
| # soft_fail: false # pon true si no quieres bloquear (no recomendado) | |
| build: | |
| name: Build + SBOM | |
| runs-on: ubuntu-latest | |
| needs: [sast] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Build imagen de la app objetivo | |
| run: | | |
| cd "${APP_DIR}" | |
| docker build -t ${IMAGE_NAME}:${IMAGE_TAG} . | |
| - name: SBOM (Syft) | |
| uses: anchore/sbom-action@v0 | |
| with: | |
| image: demo-app:local | |
| artifact-name: sbom.spdx.json # queda como artefacto del job | |
| # ────────────────────────────────────────────────────────────────────────────── | |
| # Trivy + Firma/Verify (cosign) | |
| # ────────────────────────────────────────────────────────────────────────────── | |
| container_scan: | |
| name: Container & deps scan (Trivy) | |
| runs-on: self-hosted | |
| needs: [build] | |
| steps: | |
| # - name: Trivy image (CRITICAL,HIGH) | |
| # uses: aquasecurity/trivy-action@0.28.0 | |
| # with: | |
| # scan-type: fs | |
| # image-ref: demo-app:local | |
| # format: sarif | |
| # output: trivy-image.sarif | |
| # ignore-unfixed: true | |
| # severity: CRITICAL,HIGH | |
| # - uses: github/codeql-action/upload-sarif@v3 | |
| # with: { sarif_file: trivy-image.sarif } | |
| - name: Trivy fs (SCA sobre el repo) | |
| uses: aquasecurity/trivy-action@0.28.0 | |
| with: | |
| scan-type: fs | |
| scan-ref: . | |
| format: sarif | |
| output: trivy-fs.sarif | |
| ignore-unfixed: true | |
| severity: CRITICAL,HIGH | |
| - uses: github/codeql-action/upload-sarif@v3 | |
| with: { sarif_file: trivy-fs.sarif } | |
| # sign: | |
| # name: Supply chain gate (cosign sobre SBOM) | |
| # runs-on: self-hosted | |
| # needs: [container_scan] | |
| # env: | |
| # COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }} | |
| # steps: | |
| # - uses: actions/checkout@v4 | |
| # - name: Instalar cosign | |
| # run: | | |
| # COSIGN_URL="https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64" | |
| # curl -sSLf "$COSIGN_URL" -o /usr/local/bin/cosign | |
| # chmod +x /usr/local/bin/cosign | |
| # - name: Generar claves (si no existen) | |
| # run: | | |
| # test -f cosign.key || cosign generate-key-pair | |
| # - name: Descargar SBOM del job anterior | |
| # uses: actions/download-artifact@v4 | |
| # with: | |
| # name: sbom.spdx.json | |
| # path: . | |
| # - name: Firmar SBOM (sign-blob) | |
| # run: cosign sign-blob --yes --key cosign.key sbom.spdx.json <<< "$COSIGN_PASSWORD" | |
| # - name: Verificar firma del SBOM (gate) | |
| # run: cosign verify-blob --key cosign.pub --signature sbom.spdx.json.sig sbom.spdx.json | |
| # ── Alternativa (comentada) si publicas la imagen en GHCR y quieres firmar la imagen: | |
| # - name: Login GHCR | |
| # run: echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin | |
| # - name: Push a GHCR | |
| # run: | | |
| # export IMG="ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ github.sha }}" | |
| # docker tag ${IMAGE_NAME}:${IMAGE_TAG} "$IMG" | |
| # docker push "$IMG" | |
| # - name: Sign imagen en GHCR | |
| # run: cosign sign --yes --key cosign.key "ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ github.sha }}" <<< "$COSIGN_PASSWORD" | |
| # - name: Verify imagen (gate) | |
| # run: cosign verify --key cosign.pub "ghcr.io/${{ github.repository_owner }}/${{ github.event.repository.name }}:${{ github.sha }}" | |
| # ────────────────────────────────────────────────────────────────────────────── | |
| # Deploy a K8s local + DAST (ZAP) | |
| # ────────────────────────────────────────────────────────────────────────────── | |
| deploy: | |
| name: Deploy a Kubernetes local (kind/minikube) | |
| runs-on: self-hosted | |
| needs: [container_scan] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Cargar imagen local al clúster kind | |
| run: kind load docker-image demo-app:local --name devsecops | |
| - name: Helm upgrade/install | |
| run: | | |
| helm upgrade --install demo-app charts/demo-app \ | |
| --set image.repository=${IMAGE_NAME} \ | |
| --set image.tag=${IMAGE_TAG} \ | |
| --set service.type=NodePort | |
| - name: Esperar readiness | |
| run: kubectl rollout status deploy/demo-app --timeout=180s | |
| dast: | |
| name: DAST (OWASP ZAP baseline) | |
| runs-on: self-hosted | |
| needs: [deploy] | |
| steps: | |
| - name: Port-forward al Service y ejecutar ZAP | |
| run: | | |
| kubectl port-forward svc/demo 8080:80 & echo $! > pf.pid | |
| sleep 3 | |
| docker run --rm -t owasp/zap2docker-stable \ | |
| zap-baseline.py -t http://localhost:8080 -x zap.xml | |
| kill $(cat pf.pid) || true |