Frontend Labs
⋯

Chapter 14 — Hands-on: a supply-chain-hardened pipeline

Brief

Build one end-to-end pipeline for a SecureShop service that is hardened at every stage from Part 3: IaC scanned and policy-gated, CI locked down with OIDC and least privilege, artefacts signed with provenance and an SBOM, and a cluster that refuses anything not built the approved way. Then attack it and confirm each gate holds.

Setup

   * A repo for one service (e.g. `orders-api`) with a Dockerfile and
     Terraform for its infra (an ECR repo, an IAM role, an S3 bucket).
   * A `kind` cluster with Kyverno (or policy-controller) installed.
   * GitHub Actions (or GitLab CI) with OIDC to your sandbox cloud account.
   * Tools: checkov/trivy, conftest, syft, grype, cosign, slsa-github-
     generator (or equivalent), Argo CD (or manual `kubectl apply` of
     digest-pinned manifests).

Tasks

   1. IaC GATE
      [ ] `terraform plan -out` -> `show -json` -> checkov + trivy config
          + 3 custom conftest policies (no public admin ports, mandatory
          customer-CMK encryption, no `iam:PassRole` Resource "*").
      [ ] State in S3 (SSE-KMS) + DynamoDB lock; state bucket policy
          restricts to the CI role.
      [ ] Provider lockfile committed; modules pinned to versions/SHAs.
      [ ] A scheduled drift-check workflow.

   2. CI HARDENING
      [ ] `permissions:` least-privilege in every workflow; `id-token:
          write` only on the release job.
      [ ] All actions pinned to commit SHAs; `harden-runner` with an
          egress allowlist.
      [ ] OIDC to a cloud role whose trust pins
          `sub: repo:<org>/<repo>:environment:production`. Prove a PR
          branch cannot assume it.
      [ ] Branch protection + CODEOWNERS on workflow files, Dockerfile,
          and build scripts; a gated `production` environment.
      [ ] No static cloud keys anywhere; secret scanning + push
          protection on.

   3. BUILD INTEGRITY
      [ ] Build the image; generate SBOM (syft, SPDX); scan (grype/trivy,
          fail on CRITICAL with an expiring exception file).
      [ ] `cosign sign` (keyless) + `cosign attest` the SBOM + SLSA
          provenance (slsa-github-generator).
      [ ] Push BY DIGEST to ECR; never deploy a tag.

   4. DEPLOY VERIFICATION
      [ ] Kyverno `verifyImages` policy in `Enforce`: signature from the
          exact release-workflow identity + SLSA provenance whose source
          repo == this repo + image from the approved registry + by
          digest. Applies to all containers incl. init/sidecar.
      [ ] GitOps (Argo CD) or a deploy job updates the manifest to the
          new digest; cluster admits it.

   5. ATTACK IT (document each result)
      [ ] `docker push` an unsigned `orders-api:evil` and try to run it
          -> DENIED (no signature).
      [ ] Sign an image with a DIFFERENT identity (your personal cosign
          keyless login) -> DENIED (wrong `--certificate-identity`).
      [ ] Open a PR editing the `Makefile`/build script to exfil the OIDC
          token -> CI requires maintainer approval to run; the push role
          isn't assumable from a PR ref anyway.
      [ ] Add `cidr_blocks=["0.0.0.0/0"]` on port 22 in Terraform -> PR
          blocked by conftest.
      [ ] Bump a dependency to a known-CVE version -> grype fails the
          build.

Deliverable

~/sec-lab/adv/reports/p3-pipeline.md: the pipeline diagram with every gate labelled, the config (workflows, policies, Terraform) in the repo, and the five attack attempts each with the command and the "DENIED/BLOCKED" evidence. Plus: "which single gate would you keep if you could only keep one, and why".

Definition of done

  • A bad Terraform plan is blocked pre-merge by policy-as-code.
  • CI uses OIDC (no static cloud keys), least-privilege tokens, SHA-pinned actions, and a gated prod environment; a PR branch cannot assume the deploy role.
  • Every released image has a keyless signature, an SBOM attestation, and SLSA provenance, and is deployed by digest.
  • The cluster refuses an unsigned image and an image signed by the wrong identity, in Enforce mode, for all container types.
  • All five attack attempts are blocked, with evidence.

Rubric (/100)

   IaC scanning + policy-as-code gate ............. 15
   CI hardening (OIDC, perms, pinning, gates) ..... 25
   Signing + SBOM + provenance in the pipeline .... 20
   Admission verification in Enforce (all containers) 25
   Attack attempts documented and blocked ........ 10
   Report ....................................... 5

Further reading

  • Reference pipeline: slsa-framework/slsa-github-generator examples; Chainguard's "secure-by-default" pipeline posts; the CNCF tag-security "Secure Software Factory" reference architecture.
  • Practice: the sigstore/cosign tutorials; Kyverno "verify images" playground; Kubernetes Goat's supply-chain scenarios.
  • Standard: NIST SP 800-204D; SLSA v1.0; CIS Software Supply Chain Benchmark.

End of Part 3 — Milestone check

  • I scan the IaC plan, protect state as a secrets store, pin modules, and detect drift
  • I have written policy-as-code that blocks a real misconfiguration in review
  • I can explain direct vs indirect Poisoned Pipeline Execution and defend against both
  • I have set up OIDC from CI to the cloud with a tightly-pinned trust policy and proven a PR branch cannot use it
  • I can produce and verify keyless signatures, SBOM attestations, and SLSA provenance
  • I have a cluster that refuses images not built the approved way, in Enforce mode
  • I completed the supply-chain-hardened pipeline lab and defeated every attack attempt against it

Generated from real-life-security-guide-v1.md · 8 Oct 2026 16:36