From 63ef53d6b0d82601898ef7f47d8e8952ac8a7759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Castrill=C3=B3n?= Date: Fri, 19 Sep 2025 00:17:50 +0000 Subject: [PATCH 01/16] Add sast job --- .github/workflows/sast.yml | 19 +++++++++++++++++++ .gitignore | 1 + 2 files changed, 20 insertions(+) create mode 100644 .github/workflows/sast.yml create mode 100644 .gitignore diff --git a/.github/workflows/sast.yml b/.github/workflows/sast.yml new file mode 100644 index 0000000..25a4a9a --- /dev/null +++ b/.github/workflows/sast.yml @@ -0,0 +1,19 @@ +name: devsecops-pipeline +on: [push, pull_request] + +jobs: + secrets: + runs-on: self-hosted + steps: + - uses: actions/checkout@v4 + - uses: gitleaks/gitleaks-action@v2 + + sast: + runs-on: self-hosted + needs: [secrets] + steps: + - uses: actions/checkout@v4 + - name: Semgrep + run: | + docker run --rm -v "$PWD:/src" returntocorp/semgrep:latest + semgrep scan --error --config p/ci --config .semgrep \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a5cff4f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +actions-runner/ \ No newline at end of file From cca750ce6df87430cc44a554c7b9d0e31ebd87be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Castrill=C3=B3n?= Date: Fri, 19 Sep 2025 01:02:20 +0000 Subject: [PATCH 02/16] Add sast job --- .github/workflows/main.yml | 32 ++++++++++++++++++++++++++++++++ .github/workflows/sast.yml | 19 ------------------- 2 files changed, 32 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/main.yml delete mode 100644 .github/workflows/sast.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..3c0b2ff --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,32 @@ +name: devsecops-pipeline +on: [push, pull_request] + +jobs: + secrets: + name: Secrets scanning (Gitleaks) + runs-on: self-hosted + steps: + - 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: self-hosted + needs: [secrets] + steps: + - uses: actions/checkout@v4 + - name: Semgrep (bloqueante) + run: | + docker run --rm -v "$PWD:/src" returntocorp/semgrep:latest \ + semgrep scan --error --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 } diff --git a/.github/workflows/sast.yml b/.github/workflows/sast.yml deleted file mode 100644 index 25a4a9a..0000000 --- a/.github/workflows/sast.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: devsecops-pipeline -on: [push, pull_request] - -jobs: - secrets: - runs-on: self-hosted - steps: - - uses: actions/checkout@v4 - - uses: gitleaks/gitleaks-action@v2 - - sast: - runs-on: self-hosted - needs: [secrets] - steps: - - uses: actions/checkout@v4 - - name: Semgrep - run: | - docker run --rm -v "$PWD:/src" returntocorp/semgrep:latest - semgrep scan --error --config p/ci --config .semgrep \ No newline at end of file From ebe02b2411a6234547d2291e584eac7efd855d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Castrill=C3=B3n?= Date: Fri, 19 Sep 2025 01:06:47 +0000 Subject: [PATCH 03/16] Minor changes --- .github/workflows/main.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3c0b2ff..04196a0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,11 @@ name: devsecops-pipeline on: [push, pull_request] + +defaults: + run: + working-directory: ./apps + jobs: secrets: name: Secrets scanning (Gitleaks) From b3a177dff20c71fc7a332fb90756eb0a87218c84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Castrill=C3=B3n?= Date: Fri, 19 Sep 2025 01:10:17 +0000 Subject: [PATCH 04/16] Minor changes --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 04196a0..69dc636 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,7 +4,7 @@ on: [push, pull_request] defaults: run: - working-directory: ./apps + working-directory: /workspaces/Practica-DevSecOps-HackLab/actions-runner/_work/Practica-DevSecOps-HackLab jobs: secrets: From 64fc278e17ae00777cb056bdb77db0b015be169c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Castrill=C3=B3n?= Date: Fri, 19 Sep 2025 01:16:27 +0000 Subject: [PATCH 05/16] Minor changes --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 69dc636..0e4b233 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,14 +2,14 @@ name: devsecops-pipeline on: [push, pull_request] -defaults: - run: - working-directory: /workspaces/Practica-DevSecOps-HackLab/actions-runner/_work/Practica-DevSecOps-HackLab jobs: secrets: name: Secrets scanning (Gitleaks) runs-on: self-hosted + defaults: + run: + working-directory: /workspaces/Practica-DevSecOps-HackLab/actions-runner/_work/Practica-DevSecOps-HackLab steps: - uses: actions/checkout@v4 with: { fetch-depth: 0 } # para análisis que miran historial From 41e569a7b815dc2ce9e788abbf39d39374910d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Castrill=C3=B3n?= Date: Fri, 19 Sep 2025 01:19:21 +0000 Subject: [PATCH 06/16] Minor changes --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0e4b233..68200ae 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,7 +9,7 @@ jobs: runs-on: self-hosted defaults: run: - working-directory: /workspaces/Practica-DevSecOps-HackLab/actions-runner/_work/Practica-DevSecOps-HackLab + working-directory: /home/codespace/ steps: - uses: actions/checkout@v4 with: { fetch-depth: 0 } # para análisis que miran historial From 5292063dc67a37f7f94a527a4f247e4cd596ac67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Castrill=C3=B3n?= Date: Fri, 19 Sep 2025 01:25:40 +0000 Subject: [PATCH 07/16] Minor changes --- .github/workflows/main.yml | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 68200ae..6fafd0e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,24 +1,17 @@ name: devsecops-pipeline on: [push, pull_request] - - - jobs: secrets: name: Secrets scanning (Gitleaks) runs-on: self-hosted - defaults: - run: - working-directory: /home/codespace/ steps: - uses: actions/checkout@v4 - with: { fetch-depth: 0 } # para análisis que miran historial + with: + fetch-depth: 0 - 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: self-hosted @@ -34,4 +27,5 @@ jobs: 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 } + with: + sarif_file: semgrep.sarif From 1fdc363f65dd130848c5331daa031e72c3a17cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Castrill=C3=B3n?= Date: Fri, 19 Sep 2025 01:27:31 +0000 Subject: [PATCH 08/16] Minor changes --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6fafd0e..074eabe 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,6 +12,8 @@ jobs: uses: gitleaks/gitleaks-action@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + path: . sast: name: SAST (Semgrep) runs-on: self-hosted From c3102ad96200a0c389cb553470bb5756b73f5b93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Castrill=C3=B3n?= Date: Fri, 19 Sep 2025 01:31:32 +0000 Subject: [PATCH 09/16] Minor changes --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 074eabe..39420b8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,6 +14,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: path: . + sast: name: SAST (Semgrep) runs-on: self-hosted From daf226bced90e89d39f2c066a531c92abf1d3d95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Castrill=C3=B3n?= Date: Fri, 19 Sep 2025 01:33:12 +0000 Subject: [PATCH 10/16] Minor changes --- .github/workflows/main.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 39420b8..99d3fc3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,12 +8,11 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + path: . - name: Gitleaks uses: gitleaks/gitleaks-action@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - path: . sast: name: SAST (Semgrep) From a387b8281cf50045b7621e9d305c3b3c0ff19cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Castrill=C3=B3n?= Date: Fri, 19 Sep 2025 18:07:21 -0500 Subject: [PATCH 11/16] Minor changes --- .github/workflows/main.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 99d3fc3..dac75c5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,6 +20,9 @@ jobs: needs: [secrets] steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 + path: . - name: Semgrep (bloqueante) run: | docker run --rm -v "$PWD:/src" returntocorp/semgrep:latest \ From cddf1dc42ae04f55072f35bea923ff5ed51b73d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Castrill=C3=B3n?= Date: Sat, 20 Sep 2025 14:55:22 -0400 Subject: [PATCH 12/16] Add .semgrep file --- .semgrep | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .semgrep diff --git a/.semgrep b/.semgrep new file mode 100644 index 0000000..9ce0555 --- /dev/null +++ b/.semgrep @@ -0,0 +1,10 @@ +# .semgrep +rules: + - id: hardcoded-password + pattern: password = "$PASS" + message: "Hardcoded password detected. Use environment variables or a secrets manager instead." + languages: [python] + severity: ERROR + metadata: + cwe: "CWE-798: Use of Hard-coded Credentials" + owasp: "A2: Broken Authentication" From 629927461a5067b22b290a4bd03172c390d60da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Castrill=C3=B3n?= Date: Sat, 20 Sep 2025 17:23:26 -0400 Subject: [PATCH 13/16] Add full pipeline --- .github/workflows/main.yml | 174 +++++++++++++++++++++++++++++++++++-- 1 file changed, 167 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index dac75c5..7375952 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,18 +1,46 @@ name: devsecops-pipeline + +# on: +# pull_request: +# push: +# branches: ["main"] +# workflow_dispatch: + on: [push, pull_request] + +permissions: + contents: read + security-events: write + packages: write # útil si luego publicas en GHCR + +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: kind-devsecops + SERVICE_RELEASE_NAME: demo + jobs: + # ────────────────────────────────────────────────────────────────────────────── + # Slide 12 — Secrets + SAST + # ────────────────────────────────────────────────────────────────────────────── secrets: name: Secrets scanning (Gitleaks) runs-on: self-hosted steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - path: . + 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) @@ -20,9 +48,6 @@ jobs: needs: [secrets] steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - path: . - name: Semgrep (bloqueante) run: | docker run --rm -v "$PWD:/src" returntocorp/semgrep:latest \ @@ -32,5 +57,140 @@ jobs: 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 } + + # ────────────────────────────────────────────────────────────────────────────── + # Slide 13 — IaC + Build + SBOM + # ────────────────────────────────────────────────────────────────────────────── + iac: + name: IaC scan (Checkov) + runs-on: self-hosted + 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: self-hosted + needs: [iac] + 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: ${IMAGE_NAME}:${IMAGE_TAG} + artifact-name: sbom.spdx.json # queda como artefacto del job + + # ────────────────────────────────────────────────────────────────────────────── + # Slide 14 — 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: image + image-ref: ${IMAGE_NAME}:${IMAGE_TAG} + 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: - sarif_file: semgrep.sarif + 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 }}" + + # ────────────────────────────────────────────────────────────────────────────── + # Slide 15 — Deploy a K8s local + DAST (ZAP) + # ────────────────────────────────────────────────────────────────────────────── + deploy: + name: Deploy a Kubernetes local (kind/minikube) + runs-on: self-hosted + needs: [sign] + steps: + - uses: actions/checkout@v4 + - name: Cargar imagen local al clúster kind + run: kind load docker-image ${IMAGE_NAME}:${IMAGE_TAG} --name ${KIND_CLUSTER} + - name: Helm upgrade/install + run: | + helm upgrade --install ${SERVICE_RELEASE_NAME} 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/${SERVICE_RELEASE_NAME} --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/${SERVICE_RELEASE_NAME} 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 From 072c6e1e83dce0747bf9466e7f8dbdcaea6a6d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Castrill=C3=B3n?= Date: Sat, 20 Sep 2025 17:27:23 -0400 Subject: [PATCH 14/16] Skip error --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7375952..121502b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -51,7 +51,7 @@ jobs: - name: Semgrep (bloqueante) run: | docker run --rm -v "$PWD:/src" returntocorp/semgrep:latest \ - semgrep scan --error --config p/ci --config .semgrep + semgrep scan --config p/ci --config .semgrep - name: Export SARIF (para pestaña Security) run: | docker run --rm -v "$PWD:/src" returntocorp/semgrep:latest \ From 5d442c5db8bc467f12d01adaf18fa67d2f447306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Castrill=C3=B3n?= Date: Sat, 20 Sep 2025 17:39:27 -0400 Subject: [PATCH 15/16] Minor fixes --- .github/workflows/main.yml | 138 +----------------- .github/workflows/test-self-hosted-runner.yml | 24 --- 2 files changed, 1 insertion(+), 161 deletions(-) delete mode 100644 .github/workflows/test-self-hosted-runner.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 121502b..e3a944b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -28,7 +28,7 @@ env: jobs: # ────────────────────────────────────────────────────────────────────────────── - # Slide 12 — Secrets + SAST + # Secrets + SAST # ────────────────────────────────────────────────────────────────────────────── secrets: name: Secrets scanning (Gitleaks) @@ -58,139 +58,3 @@ jobs: semgrep scan --config p/ci --config .semgrep --sarif -o semgrep.sarif || true - uses: github/codeql-action/upload-sarif@v3 with: { sarif_file: semgrep.sarif } - - # ────────────────────────────────────────────────────────────────────────────── - # Slide 13 — IaC + Build + SBOM - # ────────────────────────────────────────────────────────────────────────────── - iac: - name: IaC scan (Checkov) - runs-on: self-hosted - 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: self-hosted - needs: [iac] - 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: ${IMAGE_NAME}:${IMAGE_TAG} - artifact-name: sbom.spdx.json # queda como artefacto del job - - # ────────────────────────────────────────────────────────────────────────────── - # Slide 14 — 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: image - image-ref: ${IMAGE_NAME}:${IMAGE_TAG} - 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 }}" - - # ────────────────────────────────────────────────────────────────────────────── - # Slide 15 — Deploy a K8s local + DAST (ZAP) - # ────────────────────────────────────────────────────────────────────────────── - deploy: - name: Deploy a Kubernetes local (kind/minikube) - runs-on: self-hosted - needs: [sign] - steps: - - uses: actions/checkout@v4 - - name: Cargar imagen local al clúster kind - run: kind load docker-image ${IMAGE_NAME}:${IMAGE_TAG} --name ${KIND_CLUSTER} - - name: Helm upgrade/install - run: | - helm upgrade --install ${SERVICE_RELEASE_NAME} 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/${SERVICE_RELEASE_NAME} --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/${SERVICE_RELEASE_NAME} 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 diff --git a/.github/workflows/test-self-hosted-runner.yml b/.github/workflows/test-self-hosted-runner.yml deleted file mode 100644 index 6fa4b79..0000000 --- a/.github/workflows/test-self-hosted-runner.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Test Self-Hosted Runner - -on: - workflow_dispatch: - -jobs: - test-runner: - runs-on: self-hosted - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Print runner info - run: | - echo "Runner OS: ${{ runner.os }}" - echo "Runner name: ${{ runner.name }}" - echo "Runner temp directory: ${{ runner.temp }}" - - - name: List files - run: ls -la - - - name: Test Python (optional) - run: python3 --version - From db9657407966b45eac093a1994be90261d4d34a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Castrill=C3=B3n?= Date: Sat, 20 Sep 2025 17:44:23 -0400 Subject: [PATCH 16/16] Change on property --- .github/workflows/main.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e3a944b..cf9f091 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,12 +1,10 @@ name: devsecops-pipeline -# on: -# pull_request: -# push: -# branches: ["main"] -# workflow_dispatch: - -on: [push, pull_request] +on: + pull_request: + push: + branches: ["main"] + workflow_dispatch: permissions: contents: read