From 5137706403159eb5e26e91fedd3ead6d89fc5ea0 Mon Sep 17 00:00:00 2001 From: mck21 Date: Thu, 18 Jun 2026 16:58:09 +0200 Subject: [PATCH] docs: add phase 9 documentation, ADRs, diagrams and cost - docs: architecture, deployment-guide, security, monitoring, sonarqube, cost - docs/adr: 7 architecture decision records + index - docs/diagrams: mermaid sources (aws infra, ci/cd flow, k8s architecture) - README: status, inline architecture diagram, documentation index - STATUS.md / AGENTS.md: phases 6-9 implemented (code/manifests/docs) Co-Authored-By: Claude Opus 4.8 --- AGENTS.md | 31 +++-- README.md | 50 +++++-- STATUS.md | 53 +++++--- docs/adr/001-argocd-over-fluxcd.md | 23 ++++ docs/adr/002-eks-over-ecs.md | 23 ++++ .../003-kustomize-for-app-helm-for-infra.md | 25 ++++ docs/adr/004-github-actions-over-jenkins.md | 20 +++ docs/adr/005-istio-tradeoffs.md | 24 ++++ docs/adr/006-feature-flag-service-domain.md | 23 ++++ docs/adr/007-staging-first-cd.md | 25 ++++ docs/adr/README.md | 30 +++++ docs/architecture.md | 118 +++++++++++++++++ docs/cost.md | 40 ++++++ docs/deployment-guide.md | 122 ++++++++++++++++++ docs/diagrams/README.md | 22 ++++ docs/diagrams/aws-infrastructure.mmd | 26 ++++ docs/diagrams/ci-cd-flow.mmd | 23 ++++ docs/diagrams/kubernetes-architecture.mmd | 41 ++++++ docs/monitoring.md | 41 ++++++ docs/security.md | 87 +++++++++++++ docs/sonarqube.md | 49 +++++++ 21 files changed, 861 insertions(+), 35 deletions(-) create mode 100644 docs/adr/001-argocd-over-fluxcd.md create mode 100644 docs/adr/002-eks-over-ecs.md create mode 100644 docs/adr/003-kustomize-for-app-helm-for-infra.md create mode 100644 docs/adr/004-github-actions-over-jenkins.md create mode 100644 docs/adr/005-istio-tradeoffs.md create mode 100644 docs/adr/006-feature-flag-service-domain.md create mode 100644 docs/adr/007-staging-first-cd.md create mode 100644 docs/adr/README.md create mode 100644 docs/architecture.md create mode 100644 docs/cost.md create mode 100644 docs/deployment-guide.md create mode 100644 docs/diagrams/README.md create mode 100644 docs/diagrams/aws-infrastructure.mmd create mode 100644 docs/diagrams/ci-cd-flow.mmd create mode 100644 docs/diagrams/kubernetes-architecture.mmd create mode 100644 docs/monitoring.md create mode 100644 docs/security.md create mode 100644 docs/sonarqube.md diff --git a/AGENTS.md b/AGENTS.md index 1d521d3..c495b9a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,7 +21,7 @@ Entry point for AI agents working on this repository. Read this file first, then | Dev cluster | Minikube (local) | | Staging / Production | EKS on AWS | -**Current phase:** Phase 5 implemented (staging-first CD). **Next:** Validate CD on EKS staging, then Phase 6 — Security Hardening. See [STATUS.md](STATUS.md). +**Current phase:** Phases 1–9 built as code/manifests/docs (staging-first; production off). **Next:** live EKS validation + screenshots (`terraform apply`, deploy, capture evidence). See [STATUS.md](STATUS.md). --- @@ -33,7 +33,7 @@ devsecops-platform/ ├── app/frontend/ # Empty — reserved for future UI ├── infrastructure/ # Terraform modules + staging/production envs ├── k8s/ # Kustomize manifests (base + overlays + blue-green + argocd) -├── .github/workflows/ # ci.yaml, cd.yaml, security.yaml, terraform.yaml +├── .github/workflows/ # ci.yaml, cd.yaml (main) ├── scripts/ # blue/green CD automation (Phase 5) ├── docs/ # Placeholder — full docs in Phase 9 ├── images/phase-N/ # Screenshot evidence per phase @@ -114,16 +114,26 @@ Summary: --- -## Phase Boundaries — What NOT to Do Yet +## Phase Boundaries — what's built vs deferred -| Deferred to | Do not implement yet | -|-------------|---------------------| -| Phase 6 | Kyverno policies, NetworkPolicy, External Secrets, manifest `securityContext` | -| Phase 7 | Grafana dashboards, Loki, custom Prometheus ServiceMonitors | -| Phase 9 | Full `docs/`, ADRs, architecture diagrams | -| End of PLAN | Production CD auto-sync — see [docs/cd-production-promotion.md](docs/cd-production-promotion.md) | +Phases 1–9 are now **implemented as code/manifests/docs**. What remains is +**manual/live** work, intentionally deferred: -**Phase 5 (staging-first):** CD auto-deploys to **staging EKS only**. Dev is manual. Run EKS bootstrap once: [k8s/argocd/install-notes.md](k8s/argocd/install-notes.md). Set GitHub vars `EKS_CLUSTER_NAME`, `STAGING_HEALTH_URL`. +| Status | Item | +|--------|------| +| Built | Phase 6 security (`k8s/security`, `k8s/kyverno`, `k8s/external-secrets`, securityContext, Cosign in `ci.yaml`) | +| Built | Phase 7 monitoring (`monitoring/`, SLO rules, dashboards, `docs/sre.md`) | +| Built | Phase 8 DR/resilience (`k8s/velero`, `tests/k6`, DR + resilience docs) | +| Built | Phase 9 docs (`docs/` guides, `docs/adr/`, `docs/diagrams/*.mmd`) | +| Deferred (manual) | Live EKS apply of the above, screenshots, diagram PNG exports, `terraform apply` | +| Undecided | Production **runtime** (EKS apply, CD promote, ECR push) — **off**; reference [docs/cd-production-promotion.md](docs/cd-production-promotion.md) | + +> When changing app manifests, keep Kyverno policies and securityContext intact +> (Phase 6 enforces them on staging/prod). + +**Showcase mode:** Staging is the only environment with a working pipeline. Production is **off** — it stays in Git as a **mirror** of staging overlays/Terraform, with no build, push, or deploy. Turning production runtime on is an open decision, not a committed phase. Runbook: [docs/showcase-staging-only.md](docs/showcase-staging-only.md). When staging overlay changes, align `k8s/overlays/production/` in the same PR. + +**Phase 5 CD:** auto-deploys to **staging EKS only**. Set GitHub vars `EKS_CLUSTER_NAME`, `STAGING_HEALTH_URL` (optional until Ingress ready). --- @@ -146,6 +156,7 @@ Summary: | CI / CD workflows | `.github/workflows/ci.yaml`, `.github/workflows/cd.yaml` | | CD scripts | `scripts/gitops-bump.sh`, `scripts/blue-green-switch.sh`, `scripts/rollback.sh` | | SonarCloud config | `sonar-project.properties` | +| Showcase runbook (staging-only) | [docs/showcase-staging-only.md](docs/showcase-staging-only.md) | | GitHub OIDC (Terraform) | `infrastructure/modules/github-oidc/` | --- diff --git a/README.md b/README.md index d422b7c..f6707b8 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,14 @@ The application is a **Feature Flag Service** — manage feature toggles per env | 3 | Feature Flag Service + K8s manifests | **Done** | | 4 | CI Pipeline (GitHub Actions, SonarCloud, ECR) | **Done** | | 5 | CD Pipeline & GitOps (ArgoCD, staging-first) | **Implemented** — validate on EKS | -| 6–9 | Security, Monitoring, DR, Docs | Planned | +| 6 | Security hardening (Kyverno, Cosign, NetworkPolicy, External Secrets) | **Implemented** | +| 7 | Monitoring & SRE (Prometheus, Grafana, Loki, SLOs) | **Implemented** | +| 8 | DR & resilience (Velero, k6) | **Implemented** | +| 9 | Docs, ADRs, cost | **Implemented** | -Full tracker: [STATUS.md](STATUS.md) · Full roadmap: [PLAN.md](PLAN.md) +All manifests/config/docs are committed. Live validation (EKS deploy + screenshots) +is the remaining step — staging is the only counted runtime; production is a Git +mirror that stays **off**. Full tracker: [STATUS.md](STATUS.md) · Roadmap: [PLAN.md](PLAN.md) --- @@ -105,7 +110,27 @@ See [app/backend/README.md](app/backend/README.md) for `.env` and Prisma setup. ## Architecture -> _Diagram coming in Phase 9_ +```mermaid +flowchart LR + dev[Developer] -->|git push| GH[GitHub] + GH -->|CI: lint, test, Sonar, Trivy, build, sign| ECR[(ECR)] + GH -->|CD: GitOps bump| GH + subgraph AWS[AWS - staging EKS] + ArgoCD -->|sync| ns[staging namespace] + ns --> bg[Blue/Green Deployments] + bg --> istio[Istio VirtualService] + bg --> pg[(PostgreSQL)] + bg --> redis[(Redis cache)] + ECR -.image.-> bg + kyverno[Kyverno admission] -. verify .-> bg + prom[Prometheus] --> graf[Grafana] + bg -.metrics.-> prom + end + ArgoCD -. watches .-> GH +``` + +Detailed diagrams (AWS infra, CI/CD flow, Kubernetes layout) and rationale: +[docs/architecture.md](docs/architecture.md). Decision records: [docs/adr/](docs/adr/). --- @@ -140,11 +165,20 @@ docs/ Full documentation (Phase 9) --- -## Deployment Guide - -> _Full Minikube + EKS guide coming in Phase 9 (`docs/deployment-guide.md`)_ - -Minikube dev deploy: [k8s/README.md](k8s/README.md) +## Documentation + +| Topic | Doc | +|-------|-----| +| Architecture & diagrams | [docs/architecture.md](docs/architecture.md), [docs/diagrams/](docs/diagrams/) | +| Deployment (Minikube + EKS) | [docs/deployment-guide.md](docs/deployment-guide.md), [k8s/README.md](k8s/README.md) | +| Security hardening | [docs/security.md](docs/security.md) | +| Monitoring & observability | [docs/monitoring.md](docs/monitoring.md), [monitoring/README.md](monitoring/README.md) | +| SRE — SLI/SLO/error budget | [docs/sre.md](docs/sre.md) | +| Disaster recovery | [docs/disaster-recovery.md](docs/disaster-recovery.md) | +| Resilience testing | [docs/resilience-testing.md](docs/resilience-testing.md) | +| SonarCloud quality gate | [docs/sonarqube.md](docs/sonarqube.md) | +| Cost estimates | [docs/cost.md](docs/cost.md) | +| Architecture Decision Records | [docs/adr/](docs/adr/) | --- diff --git a/STATUS.md b/STATUS.md index bb0273a..4bf3568 100644 --- a/STATUS.md +++ b/STATUS.md @@ -2,7 +2,7 @@ Phase tracker for the DevSecOps platform. Updated after Phase 5 implementation. -**Active work:** Phase 5 validation on EKS staging (screenshots) +**Active work:** Phases 6–9 built as code/manifests/docs; pending live EKS validation + screenshots **Agent entry point:** [AGENTS.md](AGENTS.md) **Deploy runbook:** [TROUBLESHOOTING.md](TROUBLESHOOTING.md) **Full roadmap:** [PLAN.md](PLAN.md) @@ -19,34 +19,53 @@ Phase tracker for the DevSecOps platform. Updated after Phase 5 implementation. | 3 | Feature Flag Service + K8s manifests | **Done** | `app/backend/`, `k8s/`, `images/phase-3/` | | 4 | CI Pipeline (GitHub Actions, SonarCloud, ECR) | **Done** | `.github/workflows/`, `sonar-project.properties`, `images/phase-4/` | | 5 | CD & GitOps (ArgoCD sync, blue/green automation) | **Implemented** (staging-first) | `.github/workflows/cd.yaml`, `scripts/`, `images/phase-5/` | -| 6 | Security hardening (Kyverno, Cosign, NetworkPolicy) | Not started | — | -| 7 | Monitoring & SRE (Prometheus, Grafana, Loki) | Not started | `monitoring/` placeholder | -| 8 | DR & resilience (Velero, k6) | Not started | — | -| 9 | Documentation, ADRs, diagrams, cost docs | Not started | `docs/` placeholder | +| 6 | Security hardening (Kyverno, Cosign, NetworkPolicy, External Secrets) | **Implemented** | `k8s/security/`, `k8s/kyverno/`, `k8s/external-secrets/`, securityContext in deployments, Cosign in `ci.yaml` | +| 7 | Monitoring & SRE (Prometheus, Grafana, Loki) | **Implemented** | `monitoring/`, `docs/sre.md` | +| 8 | DR & resilience (Velero, k6) | **Implemented** | `k8s/velero/`, `tests/k6/`, `scripts/resilience-test.sh`, `docs/disaster-recovery.md`, `docs/resilience-testing.md` | +| 9 | Documentation, ADRs, diagrams, cost docs | **Implemented** | `docs/` (architecture, deployment-guide, security, monitoring, sonarqube, cost), `docs/adr/`, `docs/diagrams/` | + +> **Phases 6–9 are built as code/manifests/docs.** Live validation (EKS deploy, +> screenshots, `terraform apply`) is the remaining manual step and is intentionally +> deferred. All new manifests pass `kubectl kustomize` + `yamllint`. + +--- + +## Showcase mode — Git vs runtime + +**Scope:** Staging is the only environment with a working pipeline right now. Production is **off** — kept in Git as a mirror of staging only. Whether/when to lift production runtime is an open decision, not a committed phase. + +| Layer | Staging | Production | +|-------|---------|------------| +| **Git** | Active source | **Mirror** of staging — kept aligned | +| **Runtime** | EKS + CD on every `main` push | **Off** — not lifted; next steps TBD | +| **Pipeline (CI/CD)** | Counted as the working pipeline | Excluded — no build, push, or deploy | + +**Before screenshots:** follow [docs/showcase-staging-only.md](docs/showcase-staging-only.md) (terraform apply **staging only**, bootstrap EKS staging, push to `main`). --- ## Phase 5 — Implementation Details (staging-first) -Automatic CD targets **staging EKS only**. Dev (Minikube) remains manual. Production CD deferred — see [docs/cd-production-promotion.md](docs/cd-production-promotion.md). +Automatic CD targets **staging EKS only** — staging is the only counted pipeline for now. Dev (Minikube) remains manual. Production is **off**: no CI build/push, no CD deploy. Production **manifests** stay in Git as a mirror of staging; turning production runtime on later is an undecided next step — reference: [docs/cd-production-promotion.md](docs/cd-production-promotion.md). ### Delivered - [`.github/workflows/cd.yaml`](.github/workflows/cd.yaml) — post-CI GitOps bump, rollout health check, traffic switch, rollback - [`scripts/`](scripts/) — `gitops-bump.sh`, `blue-green-switch.sh`, `blue-green-health.sh`, `rollback.sh` - [`k8s/overlays/staging/`](k8s/overlays/staging/) — blue/green overlay, sync waves, `cd-active-color` ConfigMap +- [`k8s/overlays/production/`](k8s/overlays/production/) — Git mirror of staging (blue/green, production ECR, HA patches) - [`k8s/argocd/install-notes.md`](k8s/argocd/install-notes.md) — EKS staging bootstrap runbook - Terraform EKS access entry for staging `cicd` role ([`infrastructure/modules/eks/`](infrastructure/modules/eks/)) -- CI: `[skip ci]` guard; production ECR gated by `ENABLE_PROD_ECR` (default off) +- CI: `[skip ci]` guard; **no production ECR push** until final promotion phase ### GitHub variables required | Variable | Example | |----------|---------| | `EKS_CLUSTER_NAME` | `mck21-devsecops-staging-eks` | -| `STAGING_HEALTH_URL` | `https:///health` | +| `STAGING_HEALTH_URL` | `https:///health` (optional — leave empty until Ingress ready) | -Optional: `ENABLE_PROD_ECR=true`, secret `CD_BOT_TOKEN` if branch protection blocks `GITHUB_TOKEN`. +Optional: secret `CD_BOT_TOKEN` if branch protection blocks `GITHUB_TOKEN`. ### Pending validation @@ -61,17 +80,17 @@ Optional: `ENABLE_PROD_ECR=true`, secret `CD_BOT_TOKEN` if branch protection blo | Item | Reason | Target | |------|--------|--------| -| Production CD | Staging-first cost/risk strategy | [docs/cd-production-promotion.md](docs/cd-production-promotion.md) | +| Production runtime (EKS, CD) | Off for now — Git mirror only; staging is the working pipeline | TBD — decide next steps; reference [docs/cd-production-promotion.md](docs/cd-production-promotion.md) | | Dev automated CD | Minikube not reachable from GitHub-hosted runners | Manual dev deploy | -| Architecture diagram, full deployment guide | Portfolio polish | Phase 9 | -| Grafana dashboards | Monitoring stack not installed | Phase 7 | +| Diagram PNG exports | Mermaid sources committed (`docs/diagrams/*.mmd`); render deferred with screenshots | Manual | +| Live EKS validation of Phases 6–9 | Manifests/Helm values committed; not yet applied to a cluster | Manual | --- ## Environment Strategy -| Environment | Kubernetes | CD | -|-------------|------------|-----| -| `dev` | Minikube (local) | Manual | -| `staging` | EKS (AWS) | **Automatic** (merge to `main`) | -| `production` | EKS (AWS) | Deferred | +| Environment | Kubernetes (runtime) | Git manifests | CD | +|-------------|----------------------|---------------|-----| +| `dev` | Minikube (local) | `overlays/dev/` | Manual | +| `staging` | EKS (AWS) | `overlays/staging/` | **Automatic** | +| `production` | Off — not lifted | `overlays/production/` mirror | None (excluded; next steps TBD) | diff --git a/docs/adr/001-argocd-over-fluxcd.md b/docs/adr/001-argocd-over-fluxcd.md new file mode 100644 index 0000000..ecb0fd4 --- /dev/null +++ b/docs/adr/001-argocd-over-fluxcd.md @@ -0,0 +1,23 @@ +# ADR-001: ArgoCD over Flux CD + +## Status +Accepted + +## Context +GitOps needs a controller that reconciles cluster state from Git. The two mature +CNCF options are ArgoCD and Flux CD. We want clear visibility of sync/health for a +portfolio project, multi-environment app management, and a gentle learning curve. + +## Decision +Use **ArgoCD**. It ships a first-class UI showing per-Application sync and health +status, the `Application` CRD maps cleanly to our per-overlay environments, and it +integrates well with the blue/green traffic-switch flow and sync waves we use. + +## Consequences +- **Easier:** observing drift/sync state visually (great for screenshots and + demos), modelling dev/staging/production as separate Applications, manual + sync/rollback from the UI or CLI. +- **Harder / cost:** ArgoCD is a heavier install than Flux and is less "pure + GitOps" for image automation (we drive image bumps from CI rather than an + in-cluster image automation controller). Acceptable: the CD pipeline already + owns tag bumping and blue/green switching. diff --git a/docs/adr/002-eks-over-ecs.md b/docs/adr/002-eks-over-ecs.md new file mode 100644 index 0000000..4888025 --- /dev/null +++ b/docs/adr/002-eks-over-ecs.md @@ -0,0 +1,23 @@ +# ADR-002: EKS over ECS + +## Status +Accepted + +## Context +The platform needs a container orchestrator on AWS. ECS/Fargate is simpler and +cheaper to operate; EKS is managed Kubernetes. The project's goal is to +demonstrate portable, industry-standard Kubernetes + ecosystem skills (Istio, +ArgoCD, Kyverno, Prometheus), and to run the *same* manifests locally on Minikube. + +## Decision +Use **EKS**. Kubernetes is the portable target: identical manifests run on +Minikube (dev) and EKS (staging/production) via Kustomize overlays, and the whole +CNCF tooling stack (Istio, ArgoCD, Kyverno, External Secrets, kube-prometheus) +assumes Kubernetes. + +## Consequences +- **Easier:** one manifest set across local and cloud; access to the CNCF + ecosystem; transferable skills. +- **Harder / cost:** EKS control plane (~$73/mo) and operational complexity vs + ECS. Mitigated with Spot nodes, single-AZ NAT on staging, off-hours shutdown, + and running **staging-only** during showcase. diff --git a/docs/adr/003-kustomize-for-app-helm-for-infra.md b/docs/adr/003-kustomize-for-app-helm-for-infra.md new file mode 100644 index 0000000..cb1337e --- /dev/null +++ b/docs/adr/003-kustomize-for-app-helm-for-infra.md @@ -0,0 +1,25 @@ +# ADR-003: Kustomize for app manifests, Helm for platform components + +## Status +Accepted + +## Context +We need to template per-environment app config and also install large third-party +platform components (Prometheus, Loki, Kyverno, ArgoCD, Velero, External Secrets). +Helm and Kustomize solve overlapping but different problems. + +## Decision +Use **Kustomize for our own application manifests** and **Helm for upstream +platform components**. + +- App: a single `base/` with `overlays/{dev,staging,production}` patches. No + templating language, transparent diffs, native `kubectl -k` / ArgoCD support. +- Platform: install via upstream Helm charts with environment-specific + `values-*.yaml` (kube-prometheus-stack, loki, promtail, kyverno, velero, + external-secrets). + +## Consequences +- **Easier:** readable, reviewable app overlays; we don't reinvent complex charts + that vendors maintain; values files keep env differences explicit. +- **Harder:** two mental models in one repo. Acceptable and conventional — the + boundary (our code = Kustomize, their code = Helm) is easy to reason about. diff --git a/docs/adr/004-github-actions-over-jenkins.md b/docs/adr/004-github-actions-over-jenkins.md new file mode 100644 index 0000000..e98bcc1 --- /dev/null +++ b/docs/adr/004-github-actions-over-jenkins.md @@ -0,0 +1,20 @@ +# ADR-004: GitHub Actions over Jenkins + +## Status +Accepted + +## Context +CI/CD needs an automation engine. Jenkins is powerful but self-hosted (a server to +patch, secure, and scale). The repo already lives on GitHub. + +## Decision +Use **GitHub Actions**. It is fully managed, co-located with the code, has a large +marketplace, supports **OIDC federation to AWS** (no long-lived keys), and pins +actions by commit SHA for supply-chain safety. + +## Consequences +- **Easier:** no CI server to operate; OIDC removes static AWS credentials; + native PR checks and Sonar/Cosign integrations; `workflow_run` chaining for + CI→CD. +- **Harder:** vendor lock-in to GitHub; less flexible than Jenkins for exotic + agents. Acceptable for this project's scope and the security benefit of OIDC. diff --git a/docs/adr/005-istio-tradeoffs.md b/docs/adr/005-istio-tradeoffs.md new file mode 100644 index 0000000..8231993 --- /dev/null +++ b/docs/adr/005-istio-tradeoffs.md @@ -0,0 +1,24 @@ +# ADR-005: Keep Istio despite its operational cost + +## Status +Accepted + +## Context +Istio adds real complexity: sidecar injection, TCP services needing `appProtocol` ++ DestinationRules, LimitRange tuning for the proxy, and higher resource use +(documented in [../../TROUBLESHOOTING.md](../../TROUBLESHOOTING.md)). A simpler +ingress-only setup would avoid these. But the platform relies on mesh features. + +## Decision +**Keep Istio.** It provides the blue/green traffic switching via `VirtualService` +weights (core to the Phase 5 CD flow), consistent mesh metrics +(`istio_requests_total`) that power the SLOs independently of app code, and +distributed tracing via Jaeger. + +## Consequences +- **Easier:** zero-downtime blue/green by shifting weights; uniform telemetry for + SLOs/dashboards; mTLS-capable mesh. +- **Harder:** operational gotchas (TCP routing for postgres/redis, sidecar + resource overhead, NetworkPolicy must allow Istiod/DNS). These are documented + and templated, so the cost is paid once. For a tiny single-service app this is + arguably over-engineered — accepted deliberately to demonstrate mesh skills. diff --git a/docs/adr/006-feature-flag-service-domain.md b/docs/adr/006-feature-flag-service-domain.md new file mode 100644 index 0000000..f620a64 --- /dev/null +++ b/docs/adr/006-feature-flag-service-domain.md @@ -0,0 +1,23 @@ +# ADR-006: Feature Flag Service as the demo domain + +## Status +Accepted + +## Context +A DevSecOps platform needs a demo workload. It should make each infrastructure +decision feel *earned* rather than arbitrary (why a cache? why an HPA? why +overlays?), while staying small enough to not distract from the platform. + +## Decision +Build a **Feature Flag Service** (manage toggles per environment; Redis-cached +reads; PostgreSQL audit log). Real-world equivalents: LaunchDarkly, Unleash, +Flipt. + +## Consequences +- **Earned decisions:** flag reads happen on every client request → justifies the + **Redis cache** and the **HPA**; per-environment toggles → justify **Kustomize + overlays**; the audit log → justifies **PostgreSQL** persistence; rare writes / + frequent reads → a clean cache-invalidation story. +- **Bounded scope:** five small NestJS modules (`flags`, `audit`, `health`, + `cache`, `prisma`) keep app code from overshadowing the platform. +- **Harder:** none significant; the domain is intentionally simple. diff --git a/docs/adr/007-staging-first-cd.md b/docs/adr/007-staging-first-cd.md new file mode 100644 index 0000000..57c7822 --- /dev/null +++ b/docs/adr/007-staging-first-cd.md @@ -0,0 +1,25 @@ +# ADR-007: Staging-first CD; production kept off as a Git mirror + +## Status +Accepted + +## Context +This is a portfolio project with a real AWS bill. Running staging **and** +production EKS clusters doubles cost and the failure surface while building the +pipeline, without adding much demonstrative value — the CD mechanics are identical +per environment. + +## Decision +Make **staging** the only environment with a live runtime and automatic CD. +**Production** is kept fully in Git as a mirror of staging (Kustomize overlays, +Terraform, ArgoCD Application) but its runtime stays **off**. Enabling it later is +a documented, undecided step ([../cd-production-promotion.md](../cd-production-promotion.md)), +not a committed phase. Dev stays manual on Minikube. + +## Consequences +- **Easier:** lower cost (~one EKS instead of two), smaller blast radius, + faster iteration; the multi-environment IaC/manifest design is still fully + demonstrated in Git. +- **Harder:** production manifests must be kept aligned with staging in every PR + (a review checklist item); "production" is not provably running. Accepted — the + promotion path is documented and the mirror is validated by `kubectl kustomize`. diff --git a/docs/adr/README.md b/docs/adr/README.md new file mode 100644 index 0000000..d0b0467 --- /dev/null +++ b/docs/adr/README.md @@ -0,0 +1,30 @@ +# Architecture Decision Records + +ADRs capture significant, hard-to-reverse decisions: the context, the choice, and +its trade-offs. Format follows [MADR](https://adr.github.io/)-lite. + +## Format + +```markdown +# ADR-00X: Title +## Status +Accepted / Superseded / Deprecated +## Context +What problem were we solving? +## Decision +What did we decide? +## Consequences +What becomes easier or harder? +``` + +## Index + +| ADR | Title | Status | +|-----|-------|--------| +| [001](001-argocd-over-fluxcd.md) | ArgoCD over Flux CD | Accepted | +| [002](002-eks-over-ecs.md) | EKS over ECS | Accepted | +| [003](003-kustomize-for-app-helm-for-infra.md) | Kustomize for app, Helm for platform | Accepted | +| [004](004-github-actions-over-jenkins.md) | GitHub Actions over Jenkins | Accepted | +| [005](005-istio-tradeoffs.md) | Keep Istio despite operational cost | Accepted | +| [006](006-feature-flag-service-domain.md) | Feature Flag Service as demo domain | Accepted | +| [007](007-staging-first-cd.md) | Staging-first CD; production off | Accepted | diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..adeb2ff --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,118 @@ +# Architecture + +End-to-end design of the DevSecOps platform and its demo workload, the Feature +Flag Service. Diagram sources live in [diagrams/](diagrams/). + +## System overview + +```mermaid +flowchart TB + subgraph Dev[Local dev] + compose[Docker Compose] + mk[Minikube + Istio + ArgoCD] + end + subgraph CICD[GitHub] + ci[CI: lint, test, Sonar, Trivy, build, Cosign sign, ECR push] + cd[CD: GitOps tag bump, blue/green switch, rollback] + end + subgraph AWS + ecr[(ECR)] + subgraph EKSstaging[EKS staging] + argocd[ArgoCD] + app[Feature Flag API blue/green] + data[(PostgreSQL + Redis)] + mesh[Istio] + policy[Kyverno] + obs[Prometheus/Grafana/Loki] + eso[External Secrets -> Secrets Manager] + end + mirror[[EKS production - Git mirror, OFF]] + end + compose -. alt .- mk + ci --> ecr + ci --> cd + cd --> argocd + argocd --> app + ecr -.image.-> app + app --> data + app -.metrics.-> obs + policy -. admission .-> app +``` + +## Environments + +| Env | Cluster | Manifests | CD | Runtime | +|-----|---------|-----------|----|---------| +| dev | Minikube | `k8s/overlays/dev` | manual | local | +| staging | EKS | `k8s/overlays/staging` | automatic (ArgoCD) | **on** | +| production | EKS | `k8s/overlays/production` | deferred | **off** (Git mirror) | + +One set of base manifests; per-environment differences are Kustomize overlays. + +## Application (Feature Flag Service) + +```mermaid +flowchart LR + client[Client] -->|GET /api/flags/key| api[NestJS API] + api -->|hit| redis[(Redis)] + api -->|miss| pg[(PostgreSQL)] + pg --> redis + api -->|PATCH /toggle| pg + api -->|invalidate| redis + api -->|audit write| pg +``` + +- **NestJS modules:** `flags`, `audit`, `health`, `cache`, `prisma`. +- **Redis** is the architectural core: flag reads happen on every client request, + which is what justifies the HPA. Writes (toggles) are rare and invalidate cache. +- **PostgreSQL** is the source of truth for flag definitions + the audit log. +- Observability: `/metrics` (prom-client), structured JSON logs (pino) with + request IDs, Istio mesh metrics/traces. + +## CI/CD flow + +```mermaid +sequenceDiagram + participant Dev + participant GH as GitHub Actions + participant ECR + participant Git + participant Argo as ArgoCD (EKS) + Dev->>GH: push / merge to main + GH->>GH: lint, test, SonarCloud gate + GH->>GH: Trivy scan, docker build + GH->>ECR: push sha- + sign (Cosign keyless) + GH->>Git: CD bumps idle color image tag [skip ci] + Argo->>Git: detect drift + Argo->>Argo: sync idle color, wait healthy + GH->>Argo: blue/green traffic switch (VirtualService) + GH-->>Git: on failure, rollback traffic + revert tag +``` + +## Security layers (Phase 6) + +- **Build:** Trivy image scan, Cosign keyless signing. +- **Admission:** Kyverno (no privileged, resource limits, non-root, no `:latest`, + hardened securityContext, verify Cosign signature). +- **Runtime:** Pod Security Standards (baseline enforce / restricted audit), + hardened `securityContext`, NetworkPolicy default-deny, RBAC least-privilege. +- **Secrets:** External Secrets Operator + AWS Secrets Manager on EKS; local + template on dev. + +## Observability (Phase 7) + +Prometheus scrapes app `/metrics` + Istio mesh + cluster; Grafana dashboards; +Loki/Promtail logs; SLOs with multi-window burn-rate alerts. See +[../docs/sre.md](sre.md) and [../monitoring/README.md](../monitoring/README.md). + +## Resilience (Phase 8) + +Velero backups (S3 + EBS snapshots), k6 load/HPA tests, documented failure- +injection drills. See [disaster-recovery.md](disaster-recovery.md) and +[resilience-testing.md](resilience-testing.md). + +## Key decisions + +See the ADRs in [adr/](adr/): ArgoCD over Flux, EKS over ECS, Kustomize for app / +Helm for infra, GitHub Actions over Jenkins, Istio trade-offs, and the +Feature-Flag-Service domain choice. diff --git a/docs/cost.md b/docs/cost.md new file mode 100644 index 0000000..bacda4b --- /dev/null +++ b/docs/cost.md @@ -0,0 +1,40 @@ +# Cost Estimates + +Approximate monthly AWS cost per environment (us-east-1, on-demand list prices; +Spot reduces EC2 ~70%). Dev runs on Minikube (local) so it is effectively free +apart from shared S3/ECR. + +## Per-environment + +| Resource | Dev (Minikube) | Staging (EKS) | Production (EKS) | +|----------|----------------|---------------|------------------| +| EKS control plane | $0 | ~$73 | ~$73 | +| EC2 nodes (Spot) | $0 | ~$30 | ~$60 | +| NAT Gateway | $0 | ~$32 (single-AZ) | ~$64 (multi-AZ) | +| S3 + state/locking | ~$1 | ~$2 | ~$2 | +| ECR storage | ~$1 | ~$1 | ~$1 | +| EBS volumes (Prometheus/Loki/PV) | $0 | ~$10 | ~$20 | +| Secrets Manager | $0 | ~$1 | ~$1 | +| **Estimated total** | **~$2/mo** | **~$149/mo** | **~$221/mo** | + +> Production is **off** during showcase, so the production column is the projected +> cost if its runtime were enabled — it currently incurs **$0**. + +## Cost controls in place + +- **Spot** node groups on staging (and burst on production). +- **Single-AZ NAT** on staging; multi-AZ only on production. +- Staging can be **shut down outside working hours** (~60% saving): scale node + group to 0 or `terraform destroy` the cluster (state + ECR persist). +- ECR lifecycle policy: 10 images staging / 20 production. +- Prometheus/Loki retention tuned per env (2d dev, 15d/7d EKS). + +## Showcase footprint + +Running **staging only** keeps the live bill near **~$149/mo**, or lower with +off-hours shutdown. Dev/local validation is free. + +## Cost observability + +A Grafana CI/CD + cost panel and AWS Cost Explorer tags (`environment`, +`project=mck21-devsecops`) make spend attributable per environment. diff --git a/docs/deployment-guide.md b/docs/deployment-guide.md new file mode 100644 index 0000000..5b63ebe --- /dev/null +++ b/docs/deployment-guide.md @@ -0,0 +1,122 @@ +# Deployment Guide + +Covers local **Minikube** (dev) and cloud **EKS** (staging). Production runtime is +off during showcase — see [showcase-staging-only.md](showcase-staging-only.md). + +## Prerequisites + +- `kubectl`, `kustomize` (or `kubectl kustomize`), `helm`, `aws` CLI, `docker`, + `bun`, `istioctl`, `minikube` (local), `argocd` CLI (optional). +- AWS account `125156866917`, region `us-east-1`. + +--- + +## A. Local — Minikube (dev) + +```bash +minikube start --cpus=4 --memory=8192 --driver=docker +minikube addons enable ingress +minikube addons enable metrics-server + +# Istio (demo profile) + injection on dev +istioctl install --set profile=demo -y +kubectl label namespace dev istio-injection=enabled --overwrite + +kubectl apply -f k8s/namespaces/namespaces.yaml + +# Build + load app image +docker build -t backend:latest ./app/backend +minikube image load backend:latest + +# Create the local secret from the template (do not commit real values) +kubectl apply -f k8s/base/secret.yaml -n dev + +kubectl apply -k k8s/overlays/dev +kubectl get pods -n dev +kubectl port-forward svc/backend -n dev 3000:80 +curl http://localhost:3000/health +``` + +Gotchas (Prisma v7, Istio TCP, image caching, LimitRange): [../TROUBLESHOOTING.md](../TROUBLESHOOTING.md). + +Optional add-ons on dev: +```bash +# Monitoring (Phase 7) +helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \ + -n monitoring -f monitoring/prometheus/values-dev.yaml +kubectl apply -k monitoring +# Policies (Phase 6) — audit mode on dev +helm install kyverno kyverno/kyverno -n policy-system +kubectl apply -k k8s/kyverno/policies +kubectl apply -k k8s/security +``` + +--- + +## B. Cloud — EKS (staging) + +### 1. Infrastructure (Terraform) + +```bash +cd infrastructure/environments/staging +terraform init && terraform apply +aws eks update-kubeconfig --name mck21-devsecops-staging-eks --region us-east-1 +kubectl get nodes +``` + +### 2. Platform bootstrap + +Follow [../k8s/argocd/install-notes.md](../k8s/argocd/install-notes.md): +Istio (production profile), Ingress NGINX, cert-manager + Let's Encrypt, ArgoCD, +`backend-secrets` (or External Secrets), then register the staging Application. + +### 3. Security, monitoring, DR add-ons + +```bash +# Phase 6 +helm install kyverno kyverno/kyverno -n policy-system +helm install external-secrets external-secrets/external-secrets -n external-secrets --create-namespace --set installCRDs=true +kubectl apply -f k8s/external-secrets/staging.yaml +# Phase 7 +helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack -n monitoring -f monitoring/prometheus/values-eks.yaml +helm install loki grafana/loki -n monitoring -f monitoring/loki/values-eks.yaml +helm install promtail grafana/promtail -n monitoring -f monitoring/promtail/values.yaml +# Phase 8 +helm install velero vmware-tanzu/velero -n velero --create-namespace # see k8s/velero/install-notes.md +kubectl apply -f k8s/velero/schedules.yaml +``` + +Or let ArgoCD manage them via the Applications in [../k8s/argocd/](../k8s/argocd/) +(`application-platform-security.yaml`, `application-kyverno-policies.yaml`, +`application-monitoring.yaml`). + +### 4. Trigger the pipeline + +```bash +git checkout main && git pull +git commit --allow-empty -m "chore: trigger staging deploy" +git push origin main +gh run list --workflow=CI --limit 3 +gh run list --workflow=CD --limit 3 +``` + +CI builds/scans/signs/pushes to ECR; CD bumps the idle blue/green color, ArgoCD +syncs, health check runs, traffic switches. Rollback is automatic on failure. + +--- + +## C. Production + +Off during showcase. To enable later, follow +[cd-production-promotion.md](cd-production-promotion.md). Manifests are a Git +mirror of staging and stay aligned in every PR. + +## Verify a deploy + +```bash +kubectl get pods,virtualservice -n staging +kubectl get application -n argocd +kubectl get clusterpolicy # Kyverno +kubectl get servicemonitor -n monitoring +curl -f "$STAGING_HEALTH_URL" +``` diff --git a/docs/diagrams/README.md b/docs/diagrams/README.md new file mode 100644 index 0000000..aff3676 --- /dev/null +++ b/docs/diagrams/README.md @@ -0,0 +1,22 @@ +# Diagrams + +Source-controlled Mermaid sources. PNG exports are deferred (same as screenshots) +— render them when preparing the portfolio: + +```bash +# Mermaid CLI +npx -y @mermaid-js/mermaid-cli -i aws-infrastructure.mmd -o aws-infrastructure.png +npx -y @mermaid-js/mermaid-cli -i ci-cd-flow.mmd -o ci-cd-flow.png +npx -y @mermaid-js/mermaid-cli -i kubernetes-architecture.mmd -o kubernetes-architecture.png +``` + +Or paste into and export. + +| Source | Renders | Referenced by | +|--------|---------|---------------| +| `aws-infrastructure.mmd` | `aws-infrastructure.png` | README, architecture.md | +| `ci-cd-flow.mmd` | `ci-cd-flow.png` | architecture.md | +| `kubernetes-architecture.mmd` | `kubernetes-architecture.png` | architecture.md | + +The README and architecture.md also embed inline Mermaid that GitHub renders +natively, so the `.png` exports are only needed for slide decks / PDFs. diff --git a/docs/diagrams/aws-infrastructure.mmd b/docs/diagrams/aws-infrastructure.mmd new file mode 100644 index 0000000..23a4db0 --- /dev/null +++ b/docs/diagrams/aws-infrastructure.mmd @@ -0,0 +1,26 @@ +flowchart TB + subgraph AWS["AWS account 125156866917 (us-east-1)"] + s3[(S3 - tfstate, versioned + lockfile)] + ecr[(ECR - backend images)] + sm[(Secrets Manager)] + subgraph VPC["VPC (staging 10.0.0.0/16 / prod 10.1.0.0/16)"] + igw[Internet Gateway] + subgraph public[Public subnets - multi-AZ] + nat[NAT Gateway] + alb[ALB / Ingress NGINX] + end + subgraph private[Private subnets - multi-AZ] + subgraph eks[EKS cluster] + ng[Managed node groups - Spot + On-Demand] + irsa[IRSA OIDC provider] + end + end + end + end + igw --- public + public --- private + nat --- private + alb --> eks + eks -. pull .-> ecr + eks -. IRSA .-> sm + eks -. IRSA .-> s3 diff --git a/docs/diagrams/ci-cd-flow.mmd b/docs/diagrams/ci-cd-flow.mmd new file mode 100644 index 0000000..ff6ec34 --- /dev/null +++ b/docs/diagrams/ci-cd-flow.mmd @@ -0,0 +1,23 @@ +sequenceDiagram + participant Dev + participant GH as GitHub Actions (CI) + participant Sonar as SonarCloud + participant ECR + participant CD as GitHub Actions (CD) + participant Git + participant Argo as ArgoCD (EKS staging) + Dev->>GH: push / merge to main + GH->>GH: lint (ESLint, yamllint, tf fmt) + GH->>GH: unit tests + coverage + GH->>Sonar: quality gate (PR check) + GH->>GH: Trivy image scan, docker build + GH->>ECR: push sha- + latest + GH->>ECR: cosign sign (keyless, OIDC) + GH->>CD: workflow_run on CI success + CD->>Git: bump idle color image tag [skip ci] + Argo->>Git: detect drift -> sync idle color + CD->>Argo: wait rollout healthy + CD->>Git: blue/green switch (VirtualService) [skip ci] + alt health check fails + CD->>Git: rollback traffic + revert tag + end diff --git a/docs/diagrams/kubernetes-architecture.mmd b/docs/diagrams/kubernetes-architecture.mmd new file mode 100644 index 0000000..b8f80f5 --- /dev/null +++ b/docs/diagrams/kubernetes-architecture.mmd @@ -0,0 +1,41 @@ +flowchart TB + subgraph cluster["EKS staging cluster"] + subgraph istio["istio-system"] + gw[Istio ingress gateway] + istiod[istiod] + end + subgraph staging["namespace: staging (istio-injection)"] + vs[VirtualService backend] + blue[Deployment backend-blue] + green[Deployment backend-green] + svc[Service backend :80->3000] + hpa[HPA 2-10 @70% CPU] + pg[(postgres)] + rd[(redis)] + es[ExternalSecret -> backend-secrets] + end + subgraph policy["policy-system"] + kyv[Kyverno ClusterPolicies] + end + subgraph mon["monitoring"] + prom[Prometheus] + graf[Grafana] + loki[Loki] + end + subgraph argons["argocd"] + argo[ArgoCD] + end + end + gw --> vs + vs -->|weight 100/0| blue + vs --> green + blue --> svc + green --> svc + svc --> pg + svc --> rd + hpa -. scales .-> blue + kyv -. admission .-> staging + blue -. /metrics .-> prom + prom --> graf + loki --> graf + argo -. syncs .-> staging diff --git a/docs/monitoring.md b/docs/monitoring.md new file mode 100644 index 0000000..2e152a5 --- /dev/null +++ b/docs/monitoring.md @@ -0,0 +1,41 @@ +# Monitoring & Observability + +The full operational guide (install order, values, dashboards, access) lives in +[../monitoring/README.md](../monitoring/README.md). This page is the conceptual +overview. + +## Three pillars + +| Pillar | Tool | Notes | +|--------|------|-------| +| Metrics | Prometheus (kube-prometheus-stack) | app `/metrics`, Istio mesh, node + kube-state | +| Logs | Loki + Promtail | pino JSON logs; `req_id` / `trace_id` labels for correlation | +| Traces | Jaeger (via Istio) | distributed traces across the mesh | +| Dashboards/alerts | Grafana + Alertmanager | custom + community dashboards | + +## Metric sources + +- **Application:** `prom-client` / `@willsoto/nestjs-prometheus` exposes default + process metrics plus app counters (e.g. cache hit/miss) at `GET /metrics`, + scraped via `ServiceMonitor/backend`. +- **Mesh:** Istio emits `istio_requests_total` and + `istio_request_duration_milliseconds_bucket` — the basis for SLOs (consistent + across dev and EKS, independent of app code). +- **Cluster:** node-exporter + kube-state-metrics (bundled). + +## Dashboards + +Custom: **Feature Flag Service** (`feature-flags-app`) and **SLO — Error Budget** +(`slo-error-budget`), provisioned as `grafana_dashboard` ConfigMaps. Community: +Kubernetes (7249), Istio Mesh (7639), Node Exporter (1860). + +## SLOs & alerting + +SLIs/SLOs, error-budget policy and multi-window burn-rate alerts: +[sre.md](sre.md). Rules: [../monitoring/prometheus/prometheusrules.yaml](../monitoring/prometheus/prometheusrules.yaml). + +## Logging correlation + +pino emits structured JSON with a per-request `req.id`. Promtail promotes +`level` / `req_id` / `trace_id` so a request can be followed across logs (Loki) +and traces (Jaeger) from a single Grafana view. diff --git a/docs/security.md b/docs/security.md new file mode 100644 index 0000000..bb1a380 --- /dev/null +++ b/docs/security.md @@ -0,0 +1,87 @@ +# Security + +Defense-in-depth across build, supply chain, admission, runtime, and secrets. + +## Layers at a glance + +| Stage | Control | Where | +|-------|---------|-------| +| Source | Secret scanning (Gitleaks), SAST + Quality Gate (SonarCloud) | CI | +| Build | Image vuln scan (Trivy), IaC scan (Checkov) | CI | +| Supply chain | Cosign keyless signing; Kyverno signature verification | CI + admission | +| Admission | Kyverno ClusterPolicies | `policy-system` | +| Runtime | Pod Security Standards, securityContext, NetworkPolicy, RBAC | app namespaces | +| Secrets | External Secrets Operator + AWS Secrets Manager | EKS | + +## Container hardening (Phase 3 + 6) + +- Dockerfile: multi-stage, non-root `appuser` (uid 1001), minimal `oven/bun:1-slim`, no secrets baked in. +- Manifest `securityContext` (backend deployments — base + blue + green): + - pod: `runAsNonRoot`, `runAsUser/Group: 1001`, `fsGroup: 1001`, `seccompProfile: RuntimeDefault` + - container: `allowPrivilegeEscalation: false`, `readOnlyRootFilesystem: true`, `capabilities.drop: [ALL]` + - writable `/tmp` via emptyDir (compatible with read-only root fs) + - `automountServiceAccountToken: false` + +## Pod Security Standards + +Namespace labels (`k8s/namespaces/namespaces.yaml`): **enforce baseline**, +**audit/warn restricted** on `dev`, `staging`, `production`. Baseline is enforced +(not restricted) because in-namespace postgres/redis need writable filesystems; +the backend app independently meets the restricted bar via its securityContext. +For a production runtime, prefer managed RDS/ElastiCache and enforce restricted. + +## Kyverno policies (`k8s/kyverno/policies/`) + +| Policy | Mode | +|--------|------| +| disallow-privileged-containers | Enforce (all app ns) | +| require-resource-limits | Enforce | +| require-run-as-non-root | Enforce (staging/prod), Audit (dev) | +| disallow-latest-tag | Enforce (staging/prod) | +| require-securitycontext | Enforce (prod), Audit (dev/staging) | +| verify-image-signatures | Enforce (staging/prod) | + +Install + demo: [../k8s/kyverno/install-notes.md](../k8s/kyverno/install-notes.md). + +## Supply chain — Cosign + +CI signs every pushed image **keylessly** via GitHub Actions OIDC +(Sigstore/Fulcio), recording the signature in the Rekor transparency log. Kyverno +`verify-image-signatures` admits only images whose signature matches the expected +workflow identity (`.github/workflows/ci.yaml@refs/heads/main`). This blocks +unsigned or tampered images from running. + +```bash +cosign verify \ + --certificate-identity-regexp 'https://github.com/mck21/devsecops-platform/.*' \ + --certificate-oidc-issuer https://token.actions.githubusercontent.com \ + 125156866917.dkr.ecr.us-east-1.amazonaws.com/mck21-devsecops-staging-backend: +``` + +## Network policies + +Default-deny ingress per app namespace, plus explicit allows: ingress-nginx → +backend:3000, monitoring → backend:3000 (scrape), backend → postgres:5432 / +redis:6379, and DNS + Istiod egress. See `k8s/security/network-policies.yaml`. + +## RBAC + +Least-privilege: cluster-wide read-only `platform-readonly` for auditors; +namespaced `app-developer` (read/write on dev, read-only on staging/production — +prod changes go through GitOps only). Subjects are groups mapped from AWS IAM / +OIDC. See `k8s/security/rbac.yaml`. + +## Secrets management + +| Env | Backend | +|-----|---------| +| dev (Minikube) | local `secret.yaml` template (never real values) | +| staging/production (EKS) | AWS Secrets Manager via External Secrets Operator (IRSA) | + +See [../k8s/external-secrets/install-notes.md](../k8s/external-secrets/install-notes.md). + +## CI security scans + +`ci.yaml` (Trivy, build) plus the documented Phase 4 scanners (Gitleaks, Checkov, +OWASP Dependency Check) and the SonarCloud Quality Gate with security hotspots. +See [sonarqube.md](sonarqube.md). diff --git a/docs/sonarqube.md b/docs/sonarqube.md new file mode 100644 index 0000000..41f92a8 --- /dev/null +++ b/docs/sonarqube.md @@ -0,0 +1,49 @@ +# SonarCloud — Code Quality & Security + +Static analysis, coverage, and a Quality Gate enforced on every PR via the +**SonarCloud GitHub App** (free for public repos). + +## Configuration + +`sonar-project.properties`: + +```properties +sonar.projectKey=mck21_devsecops-platform +sonar.organization=mck21 +sonar.sources=app/backend/src +sonar.tests=app/backend/test +sonar.javascript.lcov.reportPaths=app/backend/coverage/lcov.info +sonar.exclusions=**/.github/**,**/infrastructure/** +sonar.qualitygate.wait=false +``` + +## How analysis runs + +On the **free plan**, SonarCloud's **Automatic Analysis** runs via the GitHub App +and posts the `SonarCloud Code Analysis` check on PRs. A manual scanner job is +intentionally **not** used — it conflicts with Automatic Analysis. CI publishes +the Jest LCOV coverage report (`app/backend/coverage/lcov.info`) which SonarCloud +ingests for coverage metrics. + +## Quality Gate + +The gate blocks merges on: new bugs, new vulnerabilities, unreviewed **security +hotspots**, coverage on new code below threshold, and excessive duplication. +Security hotspot review is part of the Phase 6 security posture. + +## SonarCloud's role across phases + +| Phase | Role | +|-------|------| +| 4 — CI | SAST scan, coverage, Quality Gate on PRs | +| 6 — Security | Security Hotspots review, OWASP rules in the gate | +| 7 — Monitoring | (optional) Grafana panel pulling SonarCloud API for quality trends | + +## Local + +```bash +cd app/backend +bun run test:ci # produces coverage/lcov.info +``` + +The badge in the README pulls live status from the SonarCloud API.