From 28eb037e8795a813b7b5a951ad326ff56b018e8e Mon Sep 17 00:00:00 2001 From: erotonin <181610827+erotonin@users.noreply.github.com> Date: Thu, 28 May 2026 05:31:29 +0000 Subject: [PATCH 1/8] chore: promote images for 10f66a8bf891b313f5d6cb3b7791dc9c154a83db --- k8s/values-azure.yaml | 18 +++++++++--------- k8s/values-prod.yaml | 12 ++++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/k8s/values-azure.yaml b/k8s/values-azure.yaml index ebb9af5..bde1c2b 100644 --- a/k8s/values-azure.yaml +++ b/k8s/values-azure.yaml @@ -21,17 +21,17 @@ ingress: images: vote: - repository: devsecopsvotingacrfi1hac.azurecr.io/voting-app-vote - tag: 0a08ef4bd5ed7bf9ad71fc89293d53326f9f9ba0 - digest: sha256:0c94c8e5fd6fcb5495aeaaff7c2e11adf5e2b7227b212e92e3955a9d469965a9 + repository: devsecopsvotingacreyvpxg.azurecr.io/voting-app-vote + tag: 10f66a8bf891b313f5d6cb3b7791dc9c154a83db + digest: sha256:782f3d0dcb99b67e2c1fe2e603f33263400d90549a1330d9569db422120e7fc1 result: - repository: devsecopsvotingacrfi1hac.azurecr.io/voting-app-result - tag: 0a08ef4bd5ed7bf9ad71fc89293d53326f9f9ba0 - digest: sha256:d09993ce2c74c7de88075a40d0c30aa56c4d617babe075db945664c665bd0256 + repository: devsecopsvotingacreyvpxg.azurecr.io/voting-app-result + tag: 10f66a8bf891b313f5d6cb3b7791dc9c154a83db + digest: sha256:9e366eb13fcfc3940b3e2f4100c124bd32b68900c46344d94d14538473854aec worker: - repository: devsecopsvotingacrfi1hac.azurecr.io/voting-app-worker - tag: 0a08ef4bd5ed7bf9ad71fc89293d53326f9f9ba0 - digest: sha256:0410576a67d0fc8eb57e6a68b99b19bba4f43ec43f0554914eac95376d8b7bfe + repository: devsecopsvotingacreyvpxg.azurecr.io/voting-app-worker + tag: 10f66a8bf891b313f5d6cb3b7791dc9c154a83db + digest: sha256:2d9986931ba0a0be18ebe2a9b244e4373410cc71bb12897905b1879a4640fd57 redis: repository: redis tag: 7-alpine diff --git a/k8s/values-prod.yaml b/k8s/values-prod.yaml index 03eab6b..c0800eb 100644 --- a/k8s/values-prod.yaml +++ b/k8s/values-prod.yaml @@ -13,16 +13,16 @@ ingress: images: vote: repository: 800557027783.dkr.ecr.us-east-1.amazonaws.com/voting-app-vote - tag: 0a08ef4bd5ed7bf9ad71fc89293d53326f9f9ba0 - digest: sha256:0c94c8e5fd6fcb5495aeaaff7c2e11adf5e2b7227b212e92e3955a9d469965a9 + tag: 10f66a8bf891b313f5d6cb3b7791dc9c154a83db + digest: sha256:782f3d0dcb99b67e2c1fe2e603f33263400d90549a1330d9569db422120e7fc1 result: repository: 800557027783.dkr.ecr.us-east-1.amazonaws.com/voting-app-result - tag: 0a08ef4bd5ed7bf9ad71fc89293d53326f9f9ba0 - digest: sha256:d09993ce2c74c7de88075a40d0c30aa56c4d617babe075db945664c665bd0256 + tag: 10f66a8bf891b313f5d6cb3b7791dc9c154a83db + digest: sha256:9e366eb13fcfc3940b3e2f4100c124bd32b68900c46344d94d14538473854aec worker: repository: 800557027783.dkr.ecr.us-east-1.amazonaws.com/voting-app-worker - tag: 0a08ef4bd5ed7bf9ad71fc89293d53326f9f9ba0 - digest: sha256:0410576a67d0fc8eb57e6a68b99b19bba4f43ec43f0554914eac95376d8b7bfe + tag: 10f66a8bf891b313f5d6cb3b7791dc9c154a83db + digest: sha256:2d9986931ba0a0be18ebe2a9b244e4373410cc71bb12897905b1879a4640fd57 redis: repository: redis tag: 7-alpine From e164025b90fccd645e2aa2853d485d0023019103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=AA=20=C4=90=C3=ACnh=20Hi=E1=BA=BFu?= Date: Thu, 28 May 2026 16:14:14 +0700 Subject: [PATCH 2/8] docs: add teacher-aligned architecture overview (#73) --- README.md | 3 +- docs/devsecops-phase-model.md | 2 +- docs/final-readiness.md | 6 +- docs/implementation-map.md | 5 +- docs/teacher-aligned-architecture.md | 258 +++++++++++++++++++++++++++ docs/teacher-aligned-demo-runbook.md | 2 +- 6 files changed, 271 insertions(+), 5 deletions(-) create mode 100644 docs/teacher-aligned-architecture.md diff --git a/README.md b/README.md index 88088be..96b8f3a 100644 --- a/README.md +++ b/README.md @@ -168,7 +168,7 @@ kubectl --context arn:aws:eks:us-east-1:800557027783:cluster/voting-app-cluster -n argocd get application voting-staging voting-production kubectl --context devsecops-voting-aks ` - -n argocd get application voting-azure-production + -n argocd get application voting-azure ``` Expected result: AWS staging and production should be `Synced Healthy`; Azure production should be healthy after the warm-standby sync succeeds. @@ -180,6 +180,7 @@ Use the verified demo runbook: ```text docs/teacher-aligned-demo-runbook.md docs/devsecops-phase-model.md +docs/teacher-aligned-architecture.md ``` Useful scripts: diff --git a/docs/devsecops-phase-model.md b/docs/devsecops-phase-model.md index 1874ccf..8a4a97e 100644 --- a/docs/devsecops-phase-model.md +++ b/docs/devsecops-phase-model.md @@ -40,7 +40,7 @@ flowchart LR | Staging deploy | Desired state staging độc lập production | Branch `staging`, ArgoCD app `voting-staging`, namespace `voting-staging` | ArgoCD OutOfSync/Degraded, không DAST | | Dynamic test | Ứng dụng thật đã chạy và reachable | Smoke test `/healthz`, OWASP ZAP baseline | Không mở promotion PR | | Production promotion | Thay đổi production desired state có review boundary | GitOps PR cập nhật `values-prod.yaml`, `values-azure.yaml` | Không merge production | -| Production deploy | Production sync từ Git, không deploy tay | ArgoCD app `voting-production`, Azure `voting-azure-production` | ArgoCD history/events thể hiện lỗi | +| Production deploy | Production sync từ Git, không deploy tay | ArgoCD app `voting-production`, Azure `voting-azure` | ArgoCD history/events thể hiện lỗi | | Operate | Functional + security telemetry | Prometheus/Grafana, Loki/Promtail, Falco, Gatekeeper/Kyverno/Sigstore events | GitHub incident issue, quarantine workflow, rollback/DR runbook | ## Vì Sao Không Vẽ Tool Thành Chuỗi Cứng diff --git a/docs/final-readiness.md b/docs/final-readiness.md index e29d150..7252820 100644 --- a/docs/final-readiness.md +++ b/docs/final-readiness.md @@ -24,12 +24,16 @@ This file summarizes the final project state for the capstone handoff. ## Verified Evidence -- AWS ArgoCD application `voting-aws` is `Synced Healthy`. +- AWS ArgoCD application `voting-staging` is `Synced Healthy`. +- AWS ArgoCD application `voting-production` is `Synced Healthy`. - Azure ArgoCD application `voting-azure` is `Synced Healthy`. - ArgoCD SSO OIDC config is present on both clusters. - ArgoCD RBAC maps Entra group object IDs to admin and readonly roles. - PR security gate demo passed on pull request `#28`. - Production approval workflow_dispatch run passed. +- Full main release pipeline `26556068376` passed build, SBOM, Cosign sign/verify, Trivy image scan, staging GitOps, smoke test, OWASP ZAP DAST, and promotion PR creation. +- GitOps promotion run `26556732195` passed after promotion PR `#72` was merged. +- AWS staging, AWS production, and Azure warm standby `/healthz` endpoints returned HTTP 200. - Redis CloudWatch log group is encrypted with KMS and retains logs for 365 days. - Live AWS EKS was observed at Kubernetes `1.30`; AKS was observed at `1.34.7`. diff --git a/docs/implementation-map.md b/docs/implementation-map.md index a932e7e..6630341 100644 --- a/docs/implementation-map.md +++ b/docs/implementation-map.md @@ -35,7 +35,10 @@ This map links the project scope to the implementation files. | Azure standby ArgoCD | `terraform/environments/azure/helm.tf` | | AWS secret sync | External Secrets + AWS Secrets Manager in `terraform/environments/aws/helm.tf` | | Azure secret sync | External Secrets + Azure Key Vault in `terraform/environments/azure/helm.tf`, `secrets.tf` | -| App Helm chart | `k8s/templates`, `k8s/values-prod.yaml`, `k8s/values-azure.yaml` | +| AWS staging app | ArgoCD app `voting-staging`, namespace `voting-staging`, Git branch `staging`, `k8s/values-staging.yaml` | +| AWS production app | ArgoCD app `voting-production`, namespace `voting-production`, Git branch `main`, `k8s/values-prod.yaml` | +| Azure warm standby app | ArgoCD app `voting-azure`, namespace `voting`, Git branch `main`, `k8s/values-azure.yaml` | +| App Helm chart | `k8s/templates`, `k8s/values-staging.yaml`, `k8s/values-prod.yaml`, `k8s/values-azure.yaml` | ## Policy And Runtime Security diff --git a/docs/teacher-aligned-architecture.md b/docs/teacher-aligned-architecture.md new file mode 100644 index 0000000..68761d8 --- /dev/null +++ b/docs/teacher-aligned-architecture.md @@ -0,0 +1,258 @@ +# Kien Truc Theo Huong Thay Yeu Cau + +Tai lieu nay trinh bay kien truc theo logic: muc tieu va phase truoc, framework/tool sau. Khi thuyet trinh, khong noi "em dung tool A, B, C" ngay tu dau. Hay noi he thong can kiem soat rui ro gi, o phase nao, va tool nao hien thuc hoa control do. + +## 1. Kien Truc Tong The + +He thong la mot DevSecOps platform cho ung dung voting gom `vote`, `result`, `worker`, Redis va PostgreSQL. AWS la primary site, Azure la warm standby site. + +```mermaid +flowchart LR + User["User / Demo browser"] --> DNS["Route53 failover layer\n(optional hosted zone)"] + DNS --> AWSVote["AWS vote endpoint\nactive primary"] + DNS -. failover .-> AzureVote["Azure vote endpoint\nwarm standby"] + + Dev["Developer"] --> Git["GitHub repository\nsource + IaC + Helm values"] + Git --> CI["GitHub Actions\nDevSecOps pipeline"] + + CI --> ECR["AWS ECR\nsigned image digests"] + CI --> ACR["Azure ACR\nsigned image digests"] + CI --> SBOM["SBOM artifacts\nSyft SPDX"] + CI --> Promotion["GitOps promotion PR\nprod desired state"] + + Promotion --> ArgoAWS["ArgoCD AWS\nvoting-staging + voting-production"] + Promotion --> ArgoAzure["ArgoCD Azure\nvoting-azure"] + + subgraph AWS["AWS primary site"] + VPC["VPC"] + EKS["EKS"] + Staging["Namespace voting-staging"] + Prod["Namespace voting-production"] + RDS["RDS PostgreSQL\nprimary publisher"] + RedisAWS["ElastiCache Redis"] + SM["AWS Secrets Manager"] + ESOAWS["External Secrets Operator"] + PolicyAWS["Gatekeeper + Sigstore policy-controller"] + ObsAWS["Prometheus/Grafana\nLoki/Promtail\nFalco"] + VGW["AWS VPN Gateway"] + + VPC --> EKS + EKS --> Staging + EKS --> Prod + Prod --> RDS + Prod --> RedisAWS + SM --> ESOAWS --> Prod + PolicyAWS --> EKS + ObsAWS --> EKS + VPC --> VGW + end + + subgraph Azure["Azure warm standby site"] + VNet["VNet"] + AKS["AKS"] + AzureApp["Namespace voting\nwarm standby workload"] + PG["Azure PostgreSQL\nlogical subscriber"] + RedisAzure["In-cluster Redis\ncost-aware standby"] + KV["Azure Key Vault"] + ESOAzure["External Secrets Operator"] + VNG["Azure Virtual Network Gateway"] + + VNet --> AKS + AKS --> AzureApp + AzureApp --> PG + AzureApp --> RedisAzure + KV --> ESOAzure --> AzureApp + VNet --> VNG + end + + ECR --> ArgoAWS + ACR --> ArgoAzure + VGW <-->|"IPsec VPN + BGP"| VNG + RDS -->|"PostgreSQL logical replication"| PG +``` + +### Cach noi ngan gon voi thay + +```text +Kien truc cua em khong chi la CI/CD. No la vong DevSecOps day du: +source control -> security gate -> signed artifact -> GitOps deploy -> runtime policy -> observability -> incident response -> DR. +AWS chay primary, Azure la warm standby. Production khong deploy truc tiep tu CI, ma di qua GitOps PR va ArgoCD sync tu Git. +``` + +## 2. Kien Truc Phase Theo DevSecOps Lifecycle + +| Phase | Muc tieu kiem soat | Framework / tool hien thuc | Ket qua dung | +| --- | --- | --- | --- | +| Plan | Xac dinh scope, rui ro, boundary dev/main/prod | Threat model, branch protection, GitHub PR rules | Co approval boundary ro rang | +| Code | Developer thay doi ung dung/IaC/Helm | Git branch, PR, pre-commit | Loi co ban bi bat som | +| Feature gate | Fast feedback truoc khi vao `dev` | Gitleaks, Semgrep, Trivy FS, SonarCloud | PR feature khong dua secret/bug ro rang vao dev | +| Integration gate | Kiem tra tong the sau khi merge vao `dev` | Gitleaks, Semgrep, Checkov, tfsec, Trivy FS, Helm lint/template, Conftest | `dev` thanh release candidate sach | +| Release gate | Chan release sai truoc `main` | Full security gate tren PR `dev -> main` | Chi source/config da qua gate moi vao main | +| Build | Tao artifact bat bien | Docker, GitHub Actions matrix | Co image cho vote/result/worker | +| Supply chain | Chung minh image dung nguon va khong bi thay the | Syft SBOM, Cosign keyless, Fulcio/Rekor, GitHub OIDC | Image digest co SBOM va chu ky | +| Image security | Scan artifact sau khi build | Trivy image scan by digest | CVE HIGH/CRITICAL bi chan | +| Staging deploy | Chay ban build that tren moi truong truoc production | GitOps branch `staging`, ArgoCD app `voting-staging` | Staging `Synced Healthy` | +| Dynamic test | Kiem tra app dang song nhu attacker/user that | Smoke `/healthz`, OWASP ZAP baseline | DAST pass truoc promotion | +| Production promotion | Doi desired state production co review | GitOps promotion PR, Helm values digest | Production khong bi auto-push truc tiep | +| Production deploy | Cluster tu sync theo Git | ArgoCD `voting-production`, Azure `voting-azure` | AWS/Azure `Synced Healthy` | +| Operate | Giam sat, phat hien, phan ung | Prometheus, Grafana, Loki, Promtail, Falco, incident workflow | Co evidence runtime va response path | +| Recover / DR | Khoi phuc khi AWS fail | Azure AKS warm standby, PostgreSQL logical replication, Route53 failover script | Co RTO/RPO demo path | + +## 3. Kien Truc Trien Khai Cu The + +### 3.1 Source, Branch va Approval Boundary + +```mermaid +flowchart LR + Feature["feature/*"] --> PRDev["PR to dev\nFeature PR light gate"] + PRDev --> Dev["dev\nintegration branch"] + Dev --> DevGate["Dev integration security gates"] + DevGate --> PRMain["PR dev -> main\nRelease PR full gate"] + PRMain --> Main["main\nrelease branch"] + Main --> Build["Build/sign/scan/deploy staging"] + Build --> PromotePR["GitOps promotion PR\nvalues-prod + values-azure"] + PromotePR --> MainProd["main updated\nproduction desired state"] +``` + +Branch protection hien tai: + +- `dev`: required check `Feature PR light security gates`, 1 approving review. +- `main`: required check `Release PR full security gates`, 1 approving review. +- Admin bypass chi dung cho demo/test khi da co ket qua check pass, sau do protection duoc khoi phuc. + +### 3.2 CI/CD va Supply Chain + +Pipeline chinh nam o `.github/workflows/ci-pipeline.yml`. + +```mermaid +flowchart LR + Source["main push"] --> Classify["Classify changed files"] + Classify --> Build["Docker build\nvote/result/worker"] + Build --> SBOM["Syft SBOM"] + Build --> Push["Push ECR + ACR"] + Push --> Sign["Cosign keyless sign digest"] + Sign --> Verify["Verify OIDC identity\nand crypto policy"] + Verify --> Trivy["Trivy image scan"] + Trivy --> Staging["Update staging GitOps branch"] + Staging --> DAST["Smoke + OWASP ZAP"] + DAST --> PR["Open promotion PR"] +``` + +Framework/tool cu the: + +- GitHub Actions: pipeline orchestration. +- GitHub OIDC: cloud auth khong dung static cloud key. +- Docker: build container artifact. +- Syft: SBOM. +- Cosign keyless: ky image digest bang identity cua workflow. +- Fulcio/Rekor: certificate va transparency log cua Sigstore. +- Trivy: image vulnerability scan. +- Script `scripts/verify-cosign-signature-policy.sh`: verify identity, issuer, va crypto policy cua certificate. + +Dieu can nhan manh: + +```text +Trivy khong thay the Cosign. Cosign tra loi "artifact co dung nguon va co chu ky hop le khong". +Trivy tra loi "artifact do co CVE nghiem trong khong". +Hai control nay khac nhau va deu can co. +``` + +### 3.3 GitOps Deployment + +| Environment | Git source | ArgoCD app | Namespace | Values file | +| --- | --- | --- | --- | --- | +| AWS staging | branch `staging` | `voting-staging` | `voting-staging` | `values-staging.yaml` | +| AWS production | branch `main` | `voting-production` | `voting-production` | `values-prod.yaml` | +| Azure warm standby | branch `main` | `voting-azure` | `voting` | `values-azure.yaml` | + +Deployment framework: + +- Kubernetes: runtime orchestration. +- Helm: package/render Kubernetes manifests. +- ArgoCD: GitOps reconciliation. +- Terraform: provision AWS/Azure infra and platform controllers. + +Why GitOps: + +```text +CI khong kubectl apply vao production. CI chi tao artifact va thay doi desired state trong Git. +ArgoCD la deployment controller, nen audit deploy gom Git commit, PR, ArgoCD revision va Kubernetes event. +``` + +### 3.4 Runtime Security va Policy + +| Control | AWS primary | Azure standby | +| --- | --- | --- | +| Kubernetes hardening | PSS labels, restricted securityContext, read-only root FS | Same Helm baseline | +| Admission policy | Gatekeeper, Sigstore policy-controller | Gatekeeper/Kyverno baseline, ACR verify disabled by cost/private-registry constraint | +| Signed image enforcement | Sigstore ClusterImagePolicy for ECR images | Images are signed and deployed from ACR, strict admission can be enabled later with registry credential support | +| Secrets | AWS Secrets Manager + External Secrets Operator | Azure Key Vault + External Secrets Operator | +| Detection | Falco/Falcosidekick | Standby baseline | +| Monitoring/logging | Prometheus, Grafana, Loki, Promtail | Cost-aware standby verification | + +### 3.5 Multi-Cloud va DR + +```mermaid +flowchart LR + AWSApp["AWS production app"] --> RDS["AWS RDS PostgreSQL"] + RDS -->|"logical replication"| AzurePG["Azure PostgreSQL"] + AWSNet["AWS VPC"] <-->|"IPsec VPN + BGP"| AzureNet["Azure VNet"] + MainGit["main GitOps state"] --> AWSArgo["AWS ArgoCD"] + MainGit --> AzureArgo["Azure ArgoCD"] + AWSArgo --> EKSProd["EKS production"] + AzureArgo --> AKSStandby["AKS warm standby"] +``` + +DR story: + +- AWS la active primary. +- Azure AKS sync cung desired state production nhu standby. +- Data path dung PostgreSQL logical replication tu AWS RDS sang Azure PostgreSQL. +- Route53 failover script da co, nhung can hosted zone/domain that de kich hoat public DNS failover. +- Azure student subscription co cost/quota constraint: result service de `ClusterIP`, Redis dung in-cluster Redis, ACR dung Basic SKU. + +## 4. Evidence Hien Tai De Dua Vao Slide + +Trang thai da verify ngay sau khi hoan thanh: + +```text +AWS ArgoCD: +- voting-staging: Synced Healthy +- voting-production: Synced Healthy + +Azure ArgoCD: +- voting-azure: Synced Healthy + +Health: +- AWS staging /healthz: HTTP 200 +- AWS production /healthz: HTTP 200 +- Azure warm standby /healthz: HTTP 200 + +CI: +- Main release pipeline: success +- GitOps promotion pipeline: success +``` + +Run IDs: + +- Full main release: `26556068376` +- GitOps promotion: `26556732195` + +## 5. Cach Tra Loi Khi Thay Hoi "Kien Truc Nay Khac Gi CI/CD Thuong?" + +```text +CI/CD thuong chi build va deploy. Kien truc nay them cac control DevSecOps: +1. PR gate de chan loi truoc khi merge. +2. Supply-chain gate de ky va verify image digest. +3. GitOps de production deploy tu Git, khong tu lenh thu cong. +4. Admission policy de cluster tu choi workload sai policy. +5. Runtime detection va observability de phat hien sau deploy. +6. DR sang Azure de khong phu thuoc mot cloud. +``` + +## 6. Dieu Can Noi That Khi Bi Hoi Gioi Han + +- Azure ACR Basic khong co mot so tinh nang Premium nhu private endpoint, geo-replication, Defender integration. +- Route53 failover can hosted zone/domain that; script da san sang nhung hien tai khong co hosted zone trong account. +- Azure signed-image admission chua enforce nhu AWS vi private ACR verification can cau hinh registry credential bo sung; AWS EKS la noi enforce Sigstore policy-controller chinh. +- Canary rollout chua dung Argo Rollouts; hien tai dung rolling update, readiness/liveness probe va rollback bang Git revert. diff --git a/docs/teacher-aligned-demo-runbook.md b/docs/teacher-aligned-demo-runbook.md index f50547f..ab56d81 100644 --- a/docs/teacher-aligned-demo-runbook.md +++ b/docs/teacher-aligned-demo-runbook.md @@ -305,7 +305,7 @@ RTO: minutes Nếu Azure public IP quota gây hạn chế, demo bằng port-forward: ```powershell -kubectl --context devsecops-voting-aks -n voting-production port-forward svc/vote 8080:80 +kubectl --context devsecops-voting-aks -n voting port-forward svc/vote 8080:80 ``` Open: From f44de2a835e64f6598b4df2401c4679fbcac6f6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=AA=20=C4=90=C3=ACnh=20Hi=E1=BA=BFu?= Date: Thu, 28 May 2026 16:20:33 +0700 Subject: [PATCH 3/8] chore: promote images for e164025b90fccd645e2aa2853d485d0023019103 (#74) Co-authored-by: erotonin <181610827+erotonin@users.noreply.github.com> --- k8s/values-azure.yaml | 12 ++++++------ k8s/values-prod.yaml | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/k8s/values-azure.yaml b/k8s/values-azure.yaml index bde1c2b..c9843f3 100644 --- a/k8s/values-azure.yaml +++ b/k8s/values-azure.yaml @@ -22,16 +22,16 @@ ingress: images: vote: repository: devsecopsvotingacreyvpxg.azurecr.io/voting-app-vote - tag: 10f66a8bf891b313f5d6cb3b7791dc9c154a83db - digest: sha256:782f3d0dcb99b67e2c1fe2e603f33263400d90549a1330d9569db422120e7fc1 + tag: e164025b90fccd645e2aa2853d485d0023019103 + digest: sha256:5713af8d425b51ed0e1c044fa2b8db5f981581428c75fe4c0912fae904c71f0b result: repository: devsecopsvotingacreyvpxg.azurecr.io/voting-app-result - tag: 10f66a8bf891b313f5d6cb3b7791dc9c154a83db - digest: sha256:9e366eb13fcfc3940b3e2f4100c124bd32b68900c46344d94d14538473854aec + tag: e164025b90fccd645e2aa2853d485d0023019103 + digest: sha256:acbb6f181ca97025edb9a1daacd3d6c18a8a5993fc2783e5df8319097487c253 worker: repository: devsecopsvotingacreyvpxg.azurecr.io/voting-app-worker - tag: 10f66a8bf891b313f5d6cb3b7791dc9c154a83db - digest: sha256:2d9986931ba0a0be18ebe2a9b244e4373410cc71bb12897905b1879a4640fd57 + tag: e164025b90fccd645e2aa2853d485d0023019103 + digest: sha256:45206dcd2a8fb3500b641448fa200c1d2ccd5771d6dd252b9953506cc17a1851 redis: repository: redis tag: 7-alpine diff --git a/k8s/values-prod.yaml b/k8s/values-prod.yaml index c0800eb..3cc4d39 100644 --- a/k8s/values-prod.yaml +++ b/k8s/values-prod.yaml @@ -13,16 +13,16 @@ ingress: images: vote: repository: 800557027783.dkr.ecr.us-east-1.amazonaws.com/voting-app-vote - tag: 10f66a8bf891b313f5d6cb3b7791dc9c154a83db - digest: sha256:782f3d0dcb99b67e2c1fe2e603f33263400d90549a1330d9569db422120e7fc1 + tag: e164025b90fccd645e2aa2853d485d0023019103 + digest: sha256:5713af8d425b51ed0e1c044fa2b8db5f981581428c75fe4c0912fae904c71f0b result: repository: 800557027783.dkr.ecr.us-east-1.amazonaws.com/voting-app-result - tag: 10f66a8bf891b313f5d6cb3b7791dc9c154a83db - digest: sha256:9e366eb13fcfc3940b3e2f4100c124bd32b68900c46344d94d14538473854aec + tag: e164025b90fccd645e2aa2853d485d0023019103 + digest: sha256:acbb6f181ca97025edb9a1daacd3d6c18a8a5993fc2783e5df8319097487c253 worker: repository: 800557027783.dkr.ecr.us-east-1.amazonaws.com/voting-app-worker - tag: 10f66a8bf891b313f5d6cb3b7791dc9c154a83db - digest: sha256:2d9986931ba0a0be18ebe2a9b244e4373410cc71bb12897905b1879a4640fd57 + tag: e164025b90fccd645e2aa2853d485d0023019103 + digest: sha256:45206dcd2a8fb3500b641448fa200c1d2ccd5771d6dd252b9953506cc17a1851 redis: repository: redis tag: 7-alpine From 169a59f0dc4f5da4d79cbfa79d7b294ee186b982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=AA=20=C4=90=C3=ACnh=20Hi=E1=BA=BFu?= Date: Sun, 31 May 2026 21:32:53 +0700 Subject: [PATCH 4/8] docs: split conceptual architecture and demo guidance (#75) * docs: split conceptual architecture and demo guidance * docs: add devsecops pipeline diagram * docs: correct feature gate retry flow * docs: clarify source dependency scanning * docs: verify demo checklist commands * fix: harden result db runtime configuration --- README.md | 14 +- docs/architecture-conceptual.md | 173 ++++++++++++++++ docs/demo-checklist.md | 226 +++++++++++++++++++++ docs/demo-recording-guide.md | 2 +- docs/devsecops-pipeline-diagram.md | 167 +++++++++++++++ docs/final-readiness.md | 4 +- docs/implementation-map.md | 2 +- docs/pipeline-frameworks.md | 176 ++++++++++++++++ docs/teacher-aligned-architecture.md | 4 +- k8s/templates/externalsecret.yaml | 1 + result/server.js | 32 ++- scripts/dr-failover.ps1 | 6 +- scripts/dr-update-azure-runtime-secret.ps1 | 5 +- scripts/test-policy-rejects.ps1 | 22 +- terraform/environments/aws/main.tf | 26 ++- terraform/environments/azure/secrets.tf | 26 ++- 16 files changed, 844 insertions(+), 42 deletions(-) create mode 100644 docs/architecture-conceptual.md create mode 100644 docs/demo-checklist.md create mode 100644 docs/devsecops-pipeline-diagram.md create mode 100644 docs/pipeline-frameworks.md diff --git a/README.md b/README.md index 96b8f3a..3916cf4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This repository is a production-inspired DevSecOps capstone for a small voting application. It combines application code, Terraform infrastructure, Kubernetes GitOps deployment, CI/CD security gates, image signing, admission policy, observability, runtime response, and disaster recovery. -The current implementation uses AWS as the active primary site and Azure as the warm standby site. AWS and Azure are connected by a route-based IPsec VPN with BGP. PostgreSQL data is replicated from AWS RDS to Azure PostgreSQL Flexible Server with native PostgreSQL logical replication. +The current implementation uses AWS as the active primary site and Azure as the warm standby site. AWS and Azure are connected by a route-based IPsec VPN with BGP. The codebase supports PostgreSQL logical replication from AWS RDS to Azure PostgreSQL Flexible Server; in the cost-capped demo, the Azure database standby can be enabled for a DR drill or left as a restore-required placeholder. ## Current Architecture @@ -31,13 +31,13 @@ AWS primary site Prometheus/Grafana, Loki/Promtail, Falco/Falcosidekick Azure warm standby site - VNet, AKS, ACR, Azure Key Vault, Azure PostgreSQL Flexible Server + VNet, AKS, ACR, Azure Key Vault, optional Azure PostgreSQL Flexible Server ArgoCD standby controller, External Secrets Operator Cross-cloud path AWS VPN Gateway <-> Azure Virtual Network Gateway BGP routes carry private traffic between the two networks - PostgreSQL logical replication sends WAL deltas from AWS to Azure + Optional PostgreSQL logical replication sends WAL deltas from AWS to Azure during a DB standby drill ``` Route53 DNS failover support is implemented in `scripts/configure-route53-failover.ps1`, but it requires a real Route53 hosted zone/domain. The current AWS account has no hosted zone, so public DNS failover is ready as code but not live-configured. @@ -59,7 +59,7 @@ On Azure, only the `vote` service is exposed with a public LoadBalancer by defau | Secrets | External Secrets Operator syncs AWS Secrets Manager and Azure Key Vault into Kubernetes. Secrets are not committed in Helm values. | | Runtime security | Falco detects suspicious runtime behavior and can open a GitHub incident workflow. Quarantine is a manual approval workflow. | | Observability | kube-prometheus-stack, Grafana SLI/SLO dashboard, Loki, Promtail, and PrometheusRule resources. | -| DR | Azure warm standby, ArgoCD sync, PostgreSQL logical replication, and Route53 failover script when a hosted zone exists. | +| DR | Azure warm standby, ArgoCD sync, optional PostgreSQL logical replication or restore-required DB placeholder, and Route53 failover script when a hosted zone exists. | ## Repository Structure @@ -181,6 +181,10 @@ Use the verified demo runbook: docs/teacher-aligned-demo-runbook.md docs/devsecops-phase-model.md docs/teacher-aligned-architecture.md +docs/architecture-conceptual.md +docs/devsecops-pipeline-diagram.md +docs/pipeline-frameworks.md +docs/demo-checklist.md ``` Useful scripts: @@ -215,6 +219,6 @@ az network vnet-gateway list -o table - Azure ACR uses Basic SKU. Premium-only controls such as private endpoints, geo-replication, image quarantine, and Defender integration are documented as production hardening items. - Azure image verification with Kyverno is disabled by default because Kyverno cannot verify private ACR manifests without extra registry credentials. AWS EKS remains the enforced signed-image admission path through Sigstore policy-controller. -- Azure warm standby uses an in-cluster Redis service to avoid extra managed Redis cost. PostgreSQL state is still replicated with native logical replication. +- Azure warm standby uses an in-cluster Redis service to avoid extra managed Redis cost. PostgreSQL standby is optional in the cost-capped demo; when disabled, Azure runtime secrets intentionally point at a restore-required database host. - Route53 failover requires a real hosted zone. The script exists, but no hosted zone is currently present in the AWS account. - GitHub branch protection is configured, but repository owners/admins can still bypass unless admin bypass is explicitly disabled in GitHub rulesets. diff --git a/docs/architecture-conceptual.md b/docs/architecture-conceptual.md new file mode 100644 index 0000000..05e6fd1 --- /dev/null +++ b/docs/architecture-conceptual.md @@ -0,0 +1,173 @@ +# Kien Truc Tong The Theo Ban Chat + +Tai lieu nay dung de trinh bay voi thay theo muc "em hieu he thong dang giai quyet van de gi", khong di vao ten tool truoc. Tool/framework nam o tai lieu pipeline rieng. + +## 1. Developer Dua Code Vao Repository + +Developer khong dua thang code len production. Developer tao branch rieng cho tung feature hoac bug fix, sau do tao pull request vao nhanh tich hop. + +Repository la nguon su that cua he thong: no luu source code, cau hinh, ha tang, lich su thay doi va la noi kich hoat cac buoc kiem soat. + +Y nghia: + +- Tach code dang lam voi code on dinh. +- Ghi lai ai sua, sua gi, vi sao sua. +- Tao noi de review truoc khi merge. +- Tao diem kich hoat cho kiem tra tu dong. + +Ghi chu ve tu "pool": trong boi canh nay cach goi dung thuong la `pull request`, `integration branch`, hoac `merge queue`. Neu y la noi gom nhieu feature de kiem tra chung thi do la nhanh tich hop, vi du `dev`. + +## 2. Quet So Bo Khi Co Pull Request + +Khi pull request duoc tao, he thong can quet so bo code truoc khi cho code vao nhanh tich hop. Muc tieu la bat loi som, khong doi den luc deploy moi phat hien. + +Vi du quan trong nhat la secret scanning. Neu developer hardcode token, password, private key hoac access key vao code, secret do co the nam trong lich su Git ke ca sau khi da xoa. Vi vay secret phai bi chan ngay tai pull request. + +Ngoai secret, buoc so bo co the kiem tra: + +- Mau code co dau hieu gay bug hoac lo hong. +- Thu vien phu thuoc co rui ro. +- Container build file, deployment manifest, infrastructure code co cau hinh nguy hiem. +- Format va loi cau hinh co ban. + +Y nghia: + +- Feedback nhanh cho developer. +- Giam chi phi sua loi. +- Ngan loi ro rang di vao nhanh tich hop. +- Bao ve repository khoi secret va cau hinh nhay cam. + +## 3. Nhanh Tich Hop Gom Nhieu Feature + +Sau khi pull request pass kiem tra so bo va duoc review, code duoc merge vao nhanh tich hop, vi du `dev`. + +Nhanh tich hop khong phai production. No la noi gom nhieu feature lai de kiem tra xem chung co hoat dong dung khi di cung nhau hay khong. + +Ly do can nhanh tich hop: + +- Tung feature rieng co the dung, nhung khi ghep voi feature khac co the xung dot. +- Cau hinh ung dung, ha tang va deployment can duoc kiem tra nhu mot bo. +- Can co mot noi tao release candidate truoc khi vao nhanh release. + +Y nghia: + +- Kiem tra chat luong o muc tong the. +- Phat hien loi do nhieu thay doi ket hop. +- Chuan bi mot ban release candidate sach hon. + +## 4. Ranh Gioi Release + +Khi nhanh tich hop da on dinh, he thong tao pull request tu `dev` sang `main`. Day la ranh gioi release. + +O buoc nay cau hoi khong con la "feature nay co dung khong", ma la "toan bo phien ban nay co du dieu kien de release khong". + +Can kiem tra nghiem hon: + +- Code co lo hong nghiem trong khong. +- Cau hinh ha tang co mo quyen qua rong khong. +- Manifest Kubernetes co vi pham policy khong. +- Dependency co rui ro cao khong. +- Ban release co the build thanh artifact nhat quan khong. + +Y nghia: + +- Tach moi truong tich hop voi moi truong release. +- Chi cho ban da qua gate vao `main`. +- Tao diem kiem soat truoc khi sinh artifact that. + +## 5. Build Artifact Bat Bien + +Sau khi code vao `main`, pipeline build source code thanh container image. Tu luc nay he thong deploy artifact, khong deploy source code truc tiep. + +Artifact can bat bien. Nghia la cung mot image digest thi noi dung ben trong khong thay doi. Neu chi dung tag nhu `latest`, ta khong biet production dang chay dung ban nao. Neu dung digest, ta truy vet duoc production dang chay image nao va image do sinh tu commit nao. + +Y nghia: + +- Bien source code thanh don vi trien khai. +- Dam bao staging va production dung cung artifact da kiem tra. +- Truy vet duoc tu production ve commit nguon. + +## 6. Kiem Soat Chuoi Cung Ung Phan Mem + +Sau khi build image, he thong can chung minh image do dung la image do pipeline tao ra va chua bi thay the. + +Can tra loi: + +- Image nay sinh tu commit nao? +- Image nay gom nhung package/dependency nao? +- Image nay co bi thay the tren registry khong? + +Vi vay artifact can co danh sach thanh phan va chu ky. Danh sach thanh phan giup biet trong image co gi. Chu ky giup cluster xac minh image den tu pipeline hop le. + +Y nghia: + +- Chong thay the image. +- Truy vet khi co CVE moi. +- Tao bang chung bao mat chuoi cung ung. + +## 7. Staging Truoc Production + +Truoc khi vao production, image phai duoc deploy len staging. Staging la moi truong gan production, dung de kiem tra app khi da chay that voi service, config, secret, network va database. + +Can kiem tra: + +- Pod co san sang khong. +- Endpoint co tra ve thanh cong khong. +- App co ket noi duoc thanh phan phu thuoc khong. +- Runtime co loi khong. +- Quet dong co phat hien loi web co ban khong. + +Y nghia: + +- Bat loi chi xuat hien khi app chay that. +- Kiem tra duong di tu user den service. +- Giam rui ro truoc khi cap nhat production. + +## 8. Promotion Bang GitOps + +Sau khi staging pass, pipeline khong day thang len production. Pipeline tao mot thay doi ve desired state cua production trong Git, goi la promotion PR. + +Production chi thay doi khi promotion PR duoc chap nhan. Cluster doc Git va tu dong dong bo trang thai production theo noi dung trong Git. + +Y nghia: + +- Git la nguon su that cua production. +- Moi thay doi production co review va lich su. +- De rollback vi co the quay lai desired state cu. +- Tach quyen build artifact voi quyen thay doi production. + +## 9. Kiem Soat Runtime + +Sau khi ung dung da chay, van can kiem soat runtime. Pipeline khong du de bao ve toan bo he thong, vi co the co nguoi apply tay, dung image sai, gan secret sai hoac workload co hanh vi bat thuong. + +Cluster can co cac lop: + +- Chi chap nhan image co nguon goc hop le. +- Khong de secret nam truc tiep trong manifest. +- Gioi han quyen cua workload. +- Kiem soat network giua service. +- Ghi log, metric va canh bao khi co bat thuong. + +Y nghia: + +- Bao ve he thong sau khi deploy. +- Chan cau hinh sai tai runtime. +- Tao bang chung van hanh khi co incident. + +## 10. Multi-Cloud Va Disaster Recovery + +He thong co AWS la site chinh va Azure la warm standby. Warm standby co nghia la moi truong du phong da ton tai va san sang nhan workload/traffic khi can, nhung traffic chinh van di vao AWS. + +DR khong chi la co them mot cluster. DR can co network path, data replication, cach chuyen traffic va quy trinh khoi phuc. + +Y nghia: + +- Giam phu thuoc vao mot cloud. +- Co duong failover khi primary site loi. +- Chung minh kha nang recover thay vi chi deploy thanh cong. + +## 11. Doan Noi 30 Giay + +```text +Kien truc cua em di theo dong chay DevSecOps. Developer khong day thang code len production ma tao pull request vao repository. PR duoc quet so bo de chan secret, loi code va cau hinh nguy hiem. Sau khi qua review, feature vao nhanh tich hop de kiem tra nhieu thay doi chung voi nhau. Khi san sang release, dev tao PR sang main va chay gate nghiem hon. Main moi build ra container image bat bien, co danh sach thanh phan va chu ky. Image duoc deploy len staging de test runtime va DAST. Neu staging pass, pipeline tao promotion PR de cap nhat desired state production. Production va Azure khong nhan lenh deploy truc tiep tu CI, ma duoc dong bo tu Git. Runtime tiep tuc co policy, secret management, monitoring va DR. +``` diff --git a/docs/demo-checklist.md b/docs/demo-checklist.md new file mode 100644 index 0000000..1ad97c4 --- /dev/null +++ b/docs/demo-checklist.md @@ -0,0 +1,226 @@ +# Demo Can Trinh Bay Nhung Gi + +Tai lieu nay la checklist de quay hoac trinh bay demo. Muc tieu la chung minh ban chat he thong, khong chi mo tung tool len cho thay xem. + +## 1. Mo Dau: Noi Kien Truc Trong 1 Phut + +Noi ngan gon: + +```text +Do an cua em la mot DevSecOps platform cho voting app. Developer khong deploy truc tiep len production. Moi thay doi di qua repository, pull request, security gate, build artifact, signing, staging test, promotion PR va GitOps deployment. AWS la primary site, Azure la warm standby site. Sau khi deploy, cluster van co runtime policy, secret management, monitoring va DR. +``` + +Can mo: + +- `docs/architecture-conceptual.md` +- `docs/devsecops-pipeline-diagram.md` +- So do tong the trong file do neu can. + +## 2. Demo Repository Va Branch Boundary + +Muc tieu: chung minh code di qua pull request va branch protection. + +Can demo: + +- Repository co source code, IaC, Helm, workflow, docs. +- Branch `dev` la integration branch. +- Branch `main` la release/production desired state. +- `dev` va `main` co required checks va review. + +Lenh: + +```powershell +gh api /repos/erotonin/devsecops-voting/branches/dev/protection --jq ".required_status_checks.contexts" +gh api /repos/erotonin/devsecops-voting/branches/main/protection --jq ".required_status_checks.contexts" +``` + +## 3. Demo PR Security Gate + +Muc tieu: chung minh code moi khong duoc merge neu chua qua kiem tra. + +Can demo: + +- Mo mot PR da pass gate gan day. +- Chi vao required check. +- Giai thich gate so bo gom secret scanning, SAST, dependency/config scan. + +Noi voi thay: + +```text +O buoc nay em muon bat loi som. Vi du secret hardcode phai bi chan ngay khi tao PR, khong doi den luc deploy. +``` + +## 4. Demo Release Pipeline + +Muc tieu: chung minh full pipeline tu `main` build ra artifact that. + +Can demo GitHub Actions run: + +- Main release run `26565822002`. +- Cac job build `vote`, `result`, `worker`. +- SBOM artifact. +- Cosign signing va verification. +- Trivy image scan. +- Deploy staging. +- DAST staging. +- Open promotion PR. + +Noi voi thay: + +```text +Sau khi vao main, pipeline moi tao artifact. Image duoc build, sinh SBOM, ky so, verify chu ky va scan CVE. Artifact nao khong qua cac buoc nay thi khong duoc dua vao staging. +``` + +## 5. Demo Staging Va DAST + +Muc tieu: chung minh ung dung da chay that truoc khi promotion. + +Can demo: + +- ArgoCD app `voting-staging` la `Synced Healthy`. +- Endpoint `/healthz` staging HTTP 200. +- OWASP ZAP baseline pass. + +Lenh: + +```powershell +kubectl --context arn:aws:eks:us-east-1:800557027783:cluster/voting-app-cluster ` + -n argocd get applications.argoproj.io voting-staging + +$h=(kubectl --context arn:aws:eks:us-east-1:800557027783:cluster/voting-app-cluster ` + -n voting-staging get svc vote -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') +Invoke-WebRequest -UseBasicParsing -Uri "http://$h/healthz" +``` + +## 6. Demo Promotion PR + +Muc tieu: chung minh production khong deploy truc tiep. + +Can demo: + +- Promotion PR `#74`. +- File thay doi: `k8s/values-prod.yaml`, `k8s/values-azure.yaml`. +- Noi dung thay doi la image digest production/Azure. + +Lenh: + +```powershell +gh pr view 74 --json url,state,mergedAt,mergeCommit +gh pr diff 74 --name-only +``` + +Noi voi thay: + +```text +CI khong day thang len production. CI chi tao PR thay doi desired state. Khi PR nay merge, ArgoCD moi dong bo production tu Git. +``` + +## 7. Demo Production Va Azure Warm Standby + +Muc tieu: chung minh release da vao hai moi truong. + +Can demo: + +- AWS production `Synced Healthy`. +- Azure warm standby `Synced Healthy`. +- Pod production/Azure running. +- `/healthz` cua production va Azure HTTP 200. + +Lenh: + +```powershell +kubectl --context arn:aws:eks:us-east-1:800557027783:cluster/voting-app-cluster ` + -n argocd get applications.argoproj.io voting-production + +kubectl --context devsecops-voting-aks ` + -n argocd get applications.argoproj.io voting-azure + +$prod=(kubectl --context arn:aws:eks:us-east-1:800557027783:cluster/voting-app-cluster ` + -n voting-production get svc vote -o jsonpath='{.status.loadBalancer.ingress[0].hostname}') +Invoke-WebRequest -UseBasicParsing -Uri "http://$prod/healthz" + +$az=(kubectl --context devsecops-voting-aks ` + -n voting get svc vote -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +Invoke-WebRequest -UseBasicParsing -Uri "http://$az/healthz" +``` + +## 8. Demo Runtime Security + +Muc tieu: chung minh bao mat khong dung lai o CI/CD. + +Can demo: + +- Policy admission trong cluster. +- External Secrets Operator. +- Monitoring/logging/runtime detection neu can. + +Lenh goi y: + +```powershell +kubectl --context arn:aws:eks:us-east-1:800557027783:cluster/voting-app-cluster get clusterimagepolicy +kubectl --context arn:aws:eks:us-east-1:800557027783:cluster/voting-app-cluster get constrainttemplates +kubectl --context arn:aws:eks:us-east-1:800557027783:cluster/voting-app-cluster get externalsecrets -A +kubectl --context arn:aws:eks:us-east-1:800557027783:cluster/voting-app-cluster -n monitoring get pods +kubectl --context arn:aws:eks:us-east-1:800557027783:cluster/voting-app-cluster -n logging get pods +kubectl --context arn:aws:eks:us-east-1:800557027783:cluster/voting-app-cluster -n falco get pods +.\scripts\test-policy-rejects.ps1 -Context arn:aws:eks:us-east-1:800557027783:cluster/voting-app-cluster -Namespace voting-staging +``` + +Ghi chu: trong namespace co Sigstore policy, mot so manifest xau co the bi policy-controller chan truoc Gatekeeper. Diem can noi la admission layer da reject workload khong hop policy. + +## 9. Demo DR Neu Co Thoi Gian + +Muc tieu: chung minh co ke hoach recover. + +Can demo: + +- So do AWS primary va Azure standby. +- VPN/BGP giua hai cloud. +- PostgreSQL logical replication. +- Script failover/Route53 neu co hosted zone. + +Lenh an toan de demo warm standby ma khong scale lai node pool: + +```powershell +.\scripts\dr-failover.ps1 -SkipScale +``` + +Noi voi thay: + +```text +DR khong chi la co them mot cluster. No can network path, data replication va cach chuyen traffic. Trong do an nay AWS la primary, Azure la warm standby. +``` + +## 10. Thu Tu Demo De Khong Bi Roi + +1. Mo `docs/architecture-conceptual.md` va noi ban chat kien truc. +2. Mo `docs/devsecops-pipeline-diagram.md` de nguoi xem nhin duoc toan bo pipeline. +3. Mo GitHub branch protection de chung minh boundary. +4. Mo PR/check de chung minh gate. +5. Mo Actions run `26565822002` de chung minh build/sign/scan/staging/DAST. +6. Mo promotion PR `#74` de chung minh production qua GitOps. +7. Mo terminal kiem tra ArgoCD AWS/Azure `Synced Healthy`. +8. Goi `/healthz` staging, production, Azure. +9. Neu con thoi gian, noi runtime security va DR. + +## 11. Cau Tra Loi Nhanh Khi Thay Hoi + +**Tai sao khong deploy thang tu CI vao production?** + +Vi production can audit va rollback ro rang. Neu deploy bang GitOps, moi thay doi production la mot commit/PR trong Git. + +**Tai sao can staging?** + +Vi nhieu loi chi xuat hien khi app chay that voi service, secret, network va database. + +**Tai sao can SBOM va signing?** + +SBOM cho biet image gom thanh phan nao. Signing chung minh image do do pipeline hop le tao ra va chua bi thay the. + +**Tai sao can Azure neu da co AWS?** + +AWS la primary. Azure la warm standby de co duong recover khi primary site gap su co. + +**Tool nao quan trong nhat?** + +Khong co mot tool quan trong nhat. Quan trong la cac lop kiem soat: source control, security gate, artifact integrity, GitOps deployment, runtime policy va recovery. diff --git a/docs/demo-recording-guide.md b/docs/demo-recording-guide.md index fb6e9ad..d7efd0f 100644 --- a/docs/demo-recording-guide.md +++ b/docs/demo-recording-guide.md @@ -470,7 +470,7 @@ Say: - "AWS is primary." - "Azure is warm standby." - "DR flow: scale/sync AKS, verify PostgreSQL logical replication, switch endpoint/DNS when a hosted zone exists, measure RTO/RPO." -- "The current build uses native PostgreSQL logical replication from AWS RDS to Azure PostgreSQL. Backup/seed remains a fallback path for cost-capped rebuilds." +- "The codebase supports native PostgreSQL logical replication from AWS RDS to Azure PostgreSQL. In the cost-capped live demo, Azure DB can stay as a restore-required placeholder unless the DR database drill is enabled." ## Part 15 - EKS upgrade readiness diff --git a/docs/devsecops-pipeline-diagram.md b/docs/devsecops-pipeline-diagram.md new file mode 100644 index 0000000..b323905 --- /dev/null +++ b/docs/devsecops-pipeline-diagram.md @@ -0,0 +1,167 @@ +# So Do Pipeline DevSecOps + +File nay dung de mo khi can giai thich pipeline bang hinh. Cach doc: di tu trai sang phai, moi khoi la mot diem kiem soat rui ro. Tool cu the nam trong ngoac de nguoi xem biet implementation, nhung y nghia chinh la muc tieu kiem soat. + +## 1. Pipeline Tong The + +```mermaid +flowchart LR + Dev["Developer
viet feature/fix"] --> Branch["Feature branch
tach thay doi rieng"] + Branch --> PRDev["Pull request vao dev
review + fast feedback"] + + PRDev --> Secret["Secret scan
chan token/password/key hardcode"] + PRDev --> SAST["Static code scan
bat bug va mau lo hong"] + PRDev --> SCA["Source dependency scan / SCA
bat CVE trong lockfile va third-party libraries"] + PRDev --> ConfigScan["Config/IaC scan
bat cau hinh nguy hiem"] + + Secret --> FeatureGate{"Feature gate pass?"} + SAST --> FeatureGate + SCA --> FeatureGate + ConfigScan --> FeatureGate + FeatureGate -- "Fail" --> FixFeature["Developer sua loi
push commit moi"] + FixFeature --> Branch + FeatureGate -- "Pass + review" --> DevBranch["dev
integration branch"] + + DevBranch --> Integration["Integration gate
kiem tra nhieu feature khi ghep chung"] + Integration --> ReleasePR["PR dev -> main
release boundary"] + ReleasePR --> ReleaseGate{"Release gate pass?"} + ReleaseGate -- "Fail" --> FixRelease["Sua release candidate"] + FixRelease --> DevBranch + ReleaseGate -- "Pass + review" --> Main["main
release source"] + + Main --> Build["Build container images
vote/result/worker"] + Build --> SBOM["Generate SBOM
biet image gom thanh phan nao"] + Build --> Sign["Sign image digest
chung minh image dung nguon"] + Build --> ImageScan["Image vulnerability scan
chan CVE nghiem trong"] + + SBOM --> ArtifactGate{"Artifact gate pass?"} + Sign --> ArtifactGate + ImageScan --> ArtifactGate + ArtifactGate -- "Fail" --> StopRelease["Dung release
khong deploy"] + ArtifactGate -- "Pass" --> StagingGit["Cap nhat desired state staging
GitOps branch staging"] + + StagingGit --> Staging["ArgoCD deploy staging
app chay that tren Kubernetes"] + Staging --> Smoke["Smoke test /healthz
kiem tra app song"] + Staging --> DAST["DAST baseline
quet web app dang chay"] + + Smoke --> StagingGate{"Staging gate pass?"} + DAST --> StagingGate + StagingGate -- "Fail" --> NoPromote["Khong promote
sua loi va build lai"] + StagingGate -- "Pass" --> PromotePR["Promotion PR
cap nhat values prod/Azure"] + + PromotePR --> ProdReview{"Review production change?"} + ProdReview -- "Reject" --> NoPromote + ProdReview -- "Approve + merge" --> ProdGit["main
desired state production"] + + ProdGit --> AWSProd["AWS production
ArgoCD sync"] + ProdGit --> AzureStandby["Azure warm standby
ArgoCD sync"] + + AWSProd --> Runtime["Runtime controls
policy, secret, monitor, log, alert"] + AzureStandby --> Runtime + Runtime --> DR["Recovery / DR
failover path khi primary loi"] + + classDef human fill:#f8fafc,stroke:#334155,stroke-width:1px,color:#0f172a; + classDef gate fill:#fef3c7,stroke:#b45309,stroke-width:2px,color:#78350f; + classDef security fill:#fee2e2,stroke:#b91c1c,stroke-width:1px,color:#7f1d1d; + classDef artifact fill:#dbeafe,stroke:#1d4ed8,stroke-width:1px,color:#1e3a8a; + classDef deploy fill:#dcfce7,stroke:#15803d,stroke-width:1px,color:#14532d; + classDef fail fill:#f3f4f6,stroke:#6b7280,stroke-dasharray: 4 3,color:#111827; + + class Dev,Branch human; + class FeatureGate,ReleaseGate,ArtifactGate,StagingGate,ProdReview gate; + class Secret,SAST,SCA,ConfigScan,Integration,ReleasePR security; + class Build,SBOM,Sign,ImageScan artifact; + class StagingGit,Staging,Smoke,DAST,PromotePR,ProdGit,AWSProd,AzureStandby,Runtime,DR deploy; + class FixFeature,FixRelease,StopRelease,NoPromote fail; +``` + +## 2. Pipeline Theo Lane + +So do nay dung khi muon giai thich ai lam gi va ranh gioi nam o dau. + +```mermaid +flowchart TB + subgraph L1["Developer / Git"] + A1["Feature branch"] + A2["Pull request vao dev"] + A3["dev integration branch"] + A4["Pull request dev -> main"] + A5["main release branch"] + end + + subgraph L2["Security Gates"] + B1["Fast PR gate
secret, SAST, SCA/dependency, config risk"] + B2["Integration gate
scan lai khi nhieu feature ghep chung"] + B3["Release gate
kiem tra release candidate truoc main"] + end + + subgraph L3["Artifact Supply Chain"] + C1["Build image
vote/result/worker"] + C2["SBOM
danh sach thanh phan"] + C3["Signature
ky image digest"] + C4["Image scan
CVE tren artifact that"] + end + + subgraph L4["Staging Validation"] + D1["GitOps staging desired state"] + D2["Deploy staging"] + D3["Smoke test"] + D4["DAST"] + end + + subgraph L5["Production GitOps"] + E1["Promotion PR"] + E2["Review production desired state"] + E3["AWS production sync"] + E4["Azure warm standby sync"] + end + + subgraph L6["Operate / Recover"] + F1["Admission policy"] + F2["Secret management"] + F3["Metrics/logs/runtime detection"] + F4["DR failover path"] + end + + A1 --> A2 --> B1 + B1 --> A3 + A3 --> B2 --> A4 --> B3 --> A5 + A5 --> C1 --> C2 --> C3 --> C4 + C4 --> D1 --> D2 --> D3 --> D4 + D4 --> E1 --> E2 + E2 --> E3 + E2 --> E4 + E3 --> F1 --> F2 --> F3 --> F4 + E4 --> F1 + + classDef lane fill:#f8fafc,stroke:#64748b,stroke-width:1px,color:#0f172a; + classDef security fill:#fee2e2,stroke:#b91c1c,color:#7f1d1d; + classDef artifact fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a; + classDef deploy fill:#dcfce7,stroke:#15803d,color:#14532d; + classDef git fill:#ede9fe,stroke:#6d28d9,color:#3b0764; + + class A1,A2,A3,A4,A5 git; + class B1,B2,B3 security; + class C1,C2,C3,C4 artifact; + class D1,D2,D3,D4,E1,E2,E3,E4,F1,F2,F3,F4 deploy; +``` + +## 3. Loi Thuyet Minh Ngan + +```text +Pipeline nay khong phai chi la build va deploy. No la chuoi kiem soat rui ro. Developer tao PR thi he thong quet loi som nhu secret, SAST, SCA/dependency va cau hinh nguy hiem. SCA o tang source giup bat CVE trong dependency manifest/lockfile som de developer sua ngay. Khi code vao dev, pipeline kiem tra lai vi nhieu feature ghep chung co the sinh loi moi. Truoc khi vao main co release gate de dam bao release candidate sach. Main moi build artifact that, tao SBOM, ky image va scan CVE tren image da build. Artifact da pass moi len staging. Staging phai smoke test va DAST pass thi moi mo promotion PR. Production khong deploy truc tiep tu CI, ma ArgoCD dong bo tu desired state trong Git. Sau deploy van co runtime policy, secret management, monitoring va DR. +``` + +## 4. Diem Can Nhan Manh Khi Giai Thich + +- `Pull request` la diem bat loi som va review thay doi. +- Neu PR fail gate, developer sua tren feature branch va push commit moi; PR hien tai tu cap nhat va gate chay lai. +- `SCA/dependency scan` nam o PR gate de bat CVE trong third-party libraries som; `image scan` nam o artifact gate de quet image that sau build. +- `dev` la noi gom feature de kiem tra tong hop, khong phai production. +- `main` la ranh gioi release, khong phai noi push code tuy tien. +- `Build artifact` tao image bat bien, co the truy vet bang digest. +- `SBOM + signing + image scan` bao ve chuoi cung ung phan mem. +- `Staging + smoke + DAST` kiem tra app dang chay that. +- `Promotion PR` la ranh gioi production. +- `GitOps` giup production co audit, rollback va desired state ro rang. +- `Runtime controls` bao ve sau khi deploy, vi pipeline khong phai lop bao ve duy nhat. diff --git a/docs/final-readiness.md b/docs/final-readiness.md index 7252820..4338434 100644 --- a/docs/final-readiness.md +++ b/docs/final-readiness.md @@ -43,9 +43,9 @@ This file summarizes the final project state for the capstone handoff. - ACR public access is kept for GitHub-hosted runners and AKS pulls; production should use private endpoints. - ACR Premium-only controls such as geo-replication, zone redundancy, retention policy, dedicated data endpoints, and Defender-integrated scanning are documented as production hardening items. - Checkov still runs in CI, but enterprise/cost-heavy controls are listed as accepted lab exceptions in `.github/workflows/ci-pipeline.yml`. The project continues to block high-signal failures through Gitleaks, Semgrep, tfsec, Trivy, Helm validation, Conftest, Gatekeeper, Kyverno, and Cosign verification. -- Cross-cloud database replication is enabled with native PostgreSQL logical replication from AWS RDS to Azure PostgreSQL Flexible Server. It creates extra paid database resources, so destroy the stack after recording. +- Cross-cloud database replication is implemented as an optional DR drill with native PostgreSQL logical replication from AWS RDS to Azure PostgreSQL Flexible Server. In the cost-capped live demo, Azure PostgreSQL may be disabled and represented by a restore-required runtime placeholder. - Canary/automated rollback is not enabled; current deployment uses rolling updates, readiness/liveness probes, and GitOps rollback by reverting Git. Production should add Argo Rollouts with Prometheus analysis. -- AWS EKS should be upgraded from `1.30` to `1.31` before the demo handoff window if time allows, then advanced one minor version at a time. See `docs/eks-upgrade-runbook.md`. +- AWS EKS has been brought to `1.31` in the current live environment. Future upgrades should continue one minor version at a time. See `docs/eks-upgrade-runbook.md`. ## Demo Commands diff --git a/docs/implementation-map.md b/docs/implementation-map.md index 6630341..7c3c4ec 100644 --- a/docs/implementation-map.md +++ b/docs/implementation-map.md @@ -78,6 +78,6 @@ This map links the project scope to the implementation files. ## Known Limits -- Cross-cloud live database replication is implemented with native PostgreSQL logical replication. The cost-capped fallback remains restore or seed data. +- Cross-cloud database replication is implemented as an optional native PostgreSQL logical replication drill. The cost-capped live demo can use a restore-required placeholder or seed data fallback. - Auto-quarantine is optional demo mode; default response is alert and human triage. - Full enterprise SSO is not implemented yet; ArgoCD RBAC groups are prepared as a baseline. diff --git a/docs/pipeline-frameworks.md b/docs/pipeline-frameworks.md new file mode 100644 index 0000000..32cd082 --- /dev/null +++ b/docs/pipeline-frameworks.md @@ -0,0 +1,176 @@ +# Full Pipeline Voi Framework Va Tool Cu The + +File nay trinh bay implementation cu the cua kien truc DevSecOps. Neu `docs/architecture-conceptual.md` noi ve ban chat, file nay noi ro framework, tool va moi truong dang dung. + +## 1. Source Control Va Branch Model + +| Thanh phan | Framework / tool | Vai tro | +| --- | --- | --- | +| Repository | GitHub | Luu source code, IaC, Helm chart, workflow, scripts va docs | +| Branch feature | Git branch | Noi developer lam tung thay doi rieng | +| Pull request | GitHub Pull Request | Review code va kich hoat security gate | +| Integration branch | `dev` | Gom feature da pass gate de kiem tra tong hop | +| Release branch | `main` | Nguon release va desired state production | +| Branch protection | GitHub branch protection | Bat required checks va approving review | + +Hien tai: + +- `dev` yeu cau check `Feature PR light security gates` va 1 review. +- `main` yeu cau check `Release PR full security gates` va 1 review. + +## 2. Feature PR Gate + +Khi developer tao PR vao `dev`, GitHub Actions chay fast gate. + +| Muc kiem tra | Tool / framework | Muc dich | +| --- | --- | --- | +| Secret scanning | Gitleaks | Chan token, password, private key bi hardcode | +| Static application security | Semgrep | Bat bug va mau code co lo hong | +| SCA / dependency scan | Trivy FS, npm audit | Quet CVE trong dependency manifest/lockfile va third-party libraries | +| Code quality | SonarCloud | Phat hien bug, smell, duplication | +| Local hygiene | pre-commit | Chuan hoa format, newline, whitespace, secret check | + +Ket qua dung: PR feature chi duoc merge vao `dev` khi gate nhanh pass va co review. + +## 3. Integration Gate Tren `dev` + +Sau khi merge vao `dev`, pipeline chay bo kiem tra rong hon. + +| Muc kiem tra | Tool / framework | Muc dich | +| --- | --- | --- | +| Secret scanning | Gitleaks | Dam bao `dev` khong chua secret | +| SAST | Semgrep | Quet loi code theo rule bao mat | +| IaC security | Checkov, tfsec | Kiem tra Terraform AWS/Azure | +| SCA / filesystem scan | Trivy FS | Kiem tra dependency manifest, lockfile, filesystem va config | +| Kubernetes manifest | Helm lint, Helm template | Dam bao chart render hop le | +| Policy as code | Conftest/OPA | Chan manifest vi pham policy | + +Ket qua dung: `dev` tro thanh release candidate sach hon, co the tao PR sang `main`. + +## 4. Release PR Gate Vao `main` + +Khi tao PR `dev -> main`, GitHub Actions chay `Release PR full security gates`. + +| Muc kiem tra | Tool / framework | Muc dich | +| --- | --- | --- | +| Secret | Gitleaks | Khong cho secret vao release branch | +| SAST | Semgrep | Chan loi bao mat trong code | +| IaC | Checkov, tfsec | Chan cau hinh cloud nguy hiem | +| K8s/Helm | Helm lint/template | Dam bao manifest render duoc cho staging/prod/Azure | +| Policy | Conftest | Dam bao manifest hop policy | + +Ket qua dung: PR vao `main` chi merge khi full gate pass va co review. + +Ghi chu: Trivy FS va `npm audit` la source-level SCA. Chung bat CVE trong dependency manifest/lockfile som o PR/integration/release gate. Trivy image scan o buoc artifact la lop khac, dung de quet image that sau khi build, bao gom base image va package da cai trong container. + +## 5. Build, SBOM, Signing Va Scan Image + +Khi code vao `main`, GitHub Actions build ba service: + +- `vote`: Python/Flask. +- `result`: Node.js/Express. +- `worker`: .NET/C#. + +Pipeline build container image bang Docker va day len: + +- AWS ECR cho AWS EKS. +- Azure ACR cho Azure AKS. + +| Buoc | Tool / framework | Ket qua | +| --- | --- | --- | +| Build image | Docker | Tao image cho `vote`, `result`, `worker` | +| Generate SBOM | Syft | Tao SBOM SPDX cho image | +| Sign image | Cosign keyless | Ky image digest bang GitHub OIDC/Fulcio/Rekor | +| Verify signature | Cosign + policy script | Xac minh identity, issuer, Rekor bundle/certificate | +| Image CVE scan | Trivy image | Chan image co CVE nghiem trong | + +Ket qua dung: chi image da co SBOM, da ky, da verify va scan pass moi duoc dung cho staging. + +## 6. Staging Deployment Va DAST + +| Thanh phan | Framework / tool | Gia tri hien tai | +| --- | --- | --- | +| GitOps branch | Git branch `staging` | Luu desired state staging | +| Deployment engine | ArgoCD | Sync tu Git vao EKS | +| Cluster | AWS EKS | Primary Kubernetes cluster | +| Namespace | `voting-staging` | Moi truong staging | +| Helm values | `k8s/values-staging.yaml` | Cau hinh staging | +| Smoke test | HTTP `/healthz` | Xac nhan app song | +| DAST | OWASP ZAP baseline | Quet web app khi dang chay | + +Ket qua dung: + +- ArgoCD app `voting-staging` `Synced Healthy`. +- `/healthz` staging HTTP 200. +- OWASP ZAP baseline pass. + +## 7. Production Promotion + +Neu staging va DAST pass, pipeline tao promotion PR cap nhat: + +- `k8s/values-prod.yaml` +- `k8s/values-azure.yaml` + +Promotion PR thay doi image digest cua production/Azure sang digest da duoc build, sign, verify va test o staging. + +| Thanh phan | Framework / tool | Vai tro | +| --- | --- | --- | +| Promotion PR | GitHub Pull Request | Review thay doi desired state production | +| GitOps desired state | Helm values | Ghi image digest production/Azure | +| Required gate | Release PR full security gates | Kiem tra promotion PR truoc merge | + +## 8. Production Va Azure Deployment + +| Moi truong | Cloud | Cluster | ArgoCD app | Namespace | Helm values | +| --- | --- | --- | --- | --- | --- | +| AWS staging | AWS | EKS | `voting-staging` | `voting-staging` | `k8s/values-staging.yaml` | +| AWS production | AWS | EKS | `voting-production` | `voting-production` | `k8s/values-prod.yaml` | +| Azure warm standby | Azure | AKS | `voting-azure` | `voting` | `k8s/values-azure.yaml` | + +Ket qua dung: + +- `voting-production` `Synced Healthy`. +- `voting-azure` `Synced Healthy`. +- `/healthz` production va Azure HTTP 200. + +## 9. Runtime Security + +| Lop bao ve | Framework / tool | Muc dich | +| --- | --- | --- | +| Image admission | Sigstore policy-controller | Chi chap nhan image co chu ky hop le | +| Policy as code | Gatekeeper/OPA | Chan workload/cau hinh vi pham policy | +| Secret sync | External Secrets Operator | Lay secret tu secret manager, khong hardcode trong manifest | +| AWS secrets | AWS Secrets Manager | Luu secret phia AWS | +| Azure secrets | Azure Key Vault | Luu secret phia Azure | +| Monitoring | Prometheus, Grafana | Metric va dashboard | +| Logging | Loki, Promtail | Tap trung log | +| Runtime detection | Falco | Phat hien hanh vi bat thuong trong cluster | + +## 10. Infrastructure Va Multi-Cloud + +| Thanh phan | Framework / tool | Vai tro | +| --- | --- | --- | +| IaC | Terraform | Tao va quan ly AWS/Azure infrastructure | +| AWS compute | EKS | Kubernetes primary | +| Azure compute | AKS | Kubernetes warm standby | +| AWS database | RDS PostgreSQL | Primary database | +| Azure database | Azure PostgreSQL | Logical subscriber | +| AWS cache | ElastiCache Redis | Cache/queue primary | +| Azure standby cache | In-cluster Redis | Standby cost-aware | +| Network DR | AWS VPN Gateway, Azure VNet Gateway, BGP | Ket noi hai cloud | +| Failover | Route53 failover script | Chuyen traffic khi primary loi | + +## 11. Evidence Gan Nhat + +| Evidence | Trang thai | +| --- | --- | +| Main release run `26565822002` | Success | +| Promotion GitOps run `26566117530` | Success | +| PR tai lieu `#73` | Merged | +| Promotion PR `#74` | Merged | +| AWS `voting-staging` | Synced Healthy | +| AWS `voting-production` | Synced Healthy | +| Azure `voting-azure` | Synced Healthy | +| Staging `/healthz` | HTTP 200 | +| Production `/healthz` | HTTP 200 | +| Azure `/healthz` | HTTP 200 | diff --git a/docs/teacher-aligned-architecture.md b/docs/teacher-aligned-architecture.md index 68761d8..aa022f4 100644 --- a/docs/teacher-aligned-architecture.md +++ b/docs/teacher-aligned-architecture.md @@ -195,7 +195,7 @@ ArgoCD la deployment controller, nen audit deploy gom Git commit, PR, ArgoCD rev ```mermaid flowchart LR AWSApp["AWS production app"] --> RDS["AWS RDS PostgreSQL"] - RDS -->|"logical replication"| AzurePG["Azure PostgreSQL"] + RDS -->|"optional logical replication / restore drill"| AzurePG["Azure PostgreSQL"] AWSNet["AWS VPC"] <-->|"IPsec VPN + BGP"| AzureNet["Azure VNet"] MainGit["main GitOps state"] --> AWSArgo["AWS ArgoCD"] MainGit --> AzureArgo["Azure ArgoCD"] @@ -207,7 +207,7 @@ DR story: - AWS la active primary. - Azure AKS sync cung desired state production nhu standby. -- Data path dung PostgreSQL logical replication tu AWS RDS sang Azure PostgreSQL. +- Data path co san runbook PostgreSQL logical replication tu AWS RDS sang Azure PostgreSQL. Trong live demo cost-capped, Azure DB co the de placeholder `restore-required` va trinh bay nhu buoc DR restore/replication drill. - Route53 failover script da co, nhung can hosted zone/domain that de kich hoat public DNS failover. - Azure student subscription co cost/quota constraint: result service de `ClusterIP`, Redis dung in-cluster Redis, ACR dung Basic SKU. diff --git a/k8s/templates/externalsecret.yaml b/k8s/templates/externalsecret.yaml index b5b65d5..c75bbca 100644 --- a/k8s/templates/externalsecret.yaml +++ b/k8s/templates/externalsecret.yaml @@ -29,6 +29,7 @@ stringData: DB_USER: postgres DB_PASSWORD: postgres DB_NAME: postgres + DB_SSL: "false" DB_SSL_MODE: Disable DATABASE_URL: postgres://postgres:postgres@db:5432/postgres COOKIE_SECURE: "false" diff --git a/result/server.js b/result/server.js index 7882b3d..d25ba0a 100644 --- a/result/server.js +++ b/result/server.js @@ -10,6 +10,26 @@ var express = require('express'), io = require('socket.io')(server); var port = process.env.PORT || 4000; +var dbConnected = false; + +function dbSslConfig() { + var explicitSsl = (process.env.DB_SSL || '').toLowerCase(); + var sslMode = (process.env.DB_SSL_MODE || '').toLowerCase(); + var databaseUrl = process.env.DATABASE_URL || ''; + var urlRequiresSsl = /[?&]sslmode=(require|verify-ca|verify-full)/i.test(databaseUrl); + + if (explicitSsl === 'false' || sslMode === 'disable') { + return false; + } + + if (explicitSsl === 'true' || sslMode === 'require' || sslMode === 'verify-ca' || sslMode === 'verify-full' || urlRequiresSsl) { + return { + rejectUnauthorized: process.env.DB_SSL_REJECT_UNAUTHORIZED === 'true' || sslMode === 'verify-full' + }; + } + + return false; +} client.collectDefaultMetrics({ prefix: 'result_' }); const httpRequestDuration = new client.Histogram({ @@ -35,7 +55,7 @@ io.on('connection', function (socket) { var pool = new Pool({ connectionString: process.env.DATABASE_URL || `postgres://${process.env.DB_USER || 'postgres'}:${process.env.DB_PASSWORD || 'postgres'}@${process.env.DB_HOST || 'db'}:${process.env.DB_PORT || '5432'}/${process.env.DB_NAME || 'postgres'}`, - ssl: process.env.DB_SSL === 'true' ? { rejectUnauthorized: process.env.DB_SSL_REJECT_UNAUTHORIZED !== 'false' } : false + ssl: dbSslConfig() }); async.retry( @@ -43,7 +63,8 @@ async.retry( function(callback) { pool.connect(function(err, client, done) { if (err) { - console.error("Waiting for db"); + dbConnected = false; + console.error("Waiting for db: " + err.message); } callback(err, client); }); @@ -53,6 +74,7 @@ async.retry( return console.error("Giving up"); } console.log("Connected to db"); + dbConnected = true; getVotes(client); } ); @@ -60,8 +82,10 @@ async.retry( function getVotes(client) { client.query('SELECT vote, COUNT(id) AS count FROM votes GROUP BY vote', [], function(err, result) { if (err) { + dbConnected = false; console.error("Error performing query: " + err); } else { + dbConnected = true; var votes = collectVotesFromResult(result); io.sockets.emit("scores", JSON.stringify(votes)); } @@ -126,6 +150,10 @@ app.use(function (err, _req, res, next) { }); app.get('/healthz', function (req, res) { + if (!dbConnected) { + return res.status(503).json({ status: 'error', service: 'result', dependency: 'db' }); + } + res.status(200).json({ status: 'ok', service: 'result' }); }); diff --git a/scripts/dr-failover.ps1 b/scripts/dr-failover.ps1 index ef0c114..9b191fa 100644 --- a/scripts/dr-failover.ps1 +++ b/scripts/dr-failover.ps1 @@ -1,7 +1,7 @@ param( [string]$AzureEnv = "terraform/environments/azure", [int]$UserNodeCount = 1, - [string]$Namespace = "voting-production", + [string]$Namespace = "voting", [switch]$SkipScale, [switch]$OpenPortForward ) @@ -67,7 +67,7 @@ kubectl -n argocd rollout status deploy/argocd-server --timeout=5m | Out-Host kubectl -n argocd rollout status statefulset/argocd-application-controller --timeout=5m | Out-Host Write-Step "Refreshing Azure standby application" -kubectl -n argocd annotate application voting-azure-production argocd.argoproj.io/refresh=hard --overwrite | Out-Host +kubectl -n argocd annotate application voting-azure argocd.argoproj.io/refresh=hard --overwrite | Out-Host Write-Step "Waiting for voting workloads" kubectl -n $Namespace rollout status deploy/vote --timeout=10m | Out-Host @@ -84,7 +84,7 @@ Write-Host "RTO: $Rto minutes" Write-Step "Useful verification commands" Write-Host "kubectl -n $Namespace get pods,svc" -Write-Host "kubectl -n argocd get application voting-azure-production" +Write-Host "kubectl -n argocd get application voting-azure" if ($OpenPortForward) { Write-Step "Opening local port-forward to Azure vote service" diff --git a/scripts/dr-update-azure-runtime-secret.ps1 b/scripts/dr-update-azure-runtime-secret.ps1 index 1809e55..658a88e 100644 --- a/scripts/dr-update-azure-runtime-secret.ps1 +++ b/scripts/dr-update-azure-runtime-secret.ps1 @@ -44,9 +44,12 @@ $RuntimeConfig = [ordered]@{ DB_USER = $DbUser DB_PASSWORD = $DbPassword DB_NAME = $DbName + DB_SSL = "true" DB_SSL_MODE = "Require" + DB_SSL_REJECT_UNAUTHORIZED = "false" DATABASE_URL = "postgres://${DbUser}:${DbPassword}@${DbHost}:5432/${DbName}?sslmode=require" - COOKIE_SECURE = "true" + COOKIE_SECURE = "false" + COOKIE_SAMESITE = "Lax" } $SecretValue = $RuntimeConfig | ConvertTo-Json -Compress diff --git a/scripts/test-policy-rejects.ps1 b/scripts/test-policy-rejects.ps1 index b78659a..65f6959 100644 --- a/scripts/test-policy-rejects.ps1 +++ b/scripts/test-policy-rejects.ps1 @@ -26,16 +26,32 @@ $files = @( foreach ($file in $files) { $path = Join-Path $PolicyDir $file + $tempPath = Join-Path ([System.IO.Path]::GetTempPath()) "voting-$file" + (Get-Content -LiteralPath $path -Raw).Replace("namespace: voting", "namespace: $Namespace") | + Set-Content -LiteralPath $tempPath -NoNewline + Write-Host "" Write-Host "Applying $file. This should be denied." -ForegroundColor Yellow - $kubectlArgs = @("apply", "-f", $path, "-n", $Namespace, "--dry-run=server") + $kubectlArgs = @("apply", "-f", $tempPath, "--dry-run=server") if ($Context) { $kubectlArgs += @("--context", $Context) } - & kubectl @kubectlArgs - if ($LASTEXITCODE -eq 0) { + $previousErrorActionPreference = $ErrorActionPreference + $ErrorActionPreference = "Continue" + $output = (& kubectl @kubectlArgs 2>&1) -join "`n" + $kubectlExitCode = $LASTEXITCODE + $ErrorActionPreference = $previousErrorActionPreference + if ($kubectlExitCode -eq 0) { + Remove-Item -LiteralPath $tempPath -Force -ErrorAction SilentlyContinue throw "$file was accepted, but it should have been denied by admission policy." } + + Remove-Item -LiteralPath $tempPath -Force -ErrorAction SilentlyContinue + if ($output -notmatch "denied|admission webhook|violat|Image signature verification failed") { + throw "$file failed before admission policy could evaluate it:`n$output" + } + + Write-Host $output Write-Host "$file denied as expected." -ForegroundColor Green } diff --git a/terraform/environments/aws/main.tf b/terraform/environments/aws/main.tf index b1b2a15..f9820ff 100644 --- a/terraform/environments/aws/main.tf +++ b/terraform/environments/aws/main.tf @@ -152,17 +152,21 @@ module "app_runtime_secret" { name = "${var.name_prefix}/app-runtime" generate_password = false secret_data = { - REDIS_HOST = module.elasticache.primary_endpoint_address - REDIS_PORT = tostring(module.elasticache.port) - REDIS_PASSWORD = module.redis_secret.password - REDIS_SSL = "true" - DB_HOST = module.rds.address - DB_PORT = tostring(module.rds.port) - DB_USER = "postgres" - DB_PASSWORD = module.db_secret.password - DB_NAME = module.rds.db_name - DB_SSL_MODE = "Require" - DATABASE_URL = "postgres://postgres:${urlencode(module.db_secret.password)}@${module.rds.address}:${module.rds.port}/${module.rds.db_name}?sslmode=require" + REDIS_HOST = module.elasticache.primary_endpoint_address + REDIS_PORT = tostring(module.elasticache.port) + REDIS_PASSWORD = module.redis_secret.password + REDIS_SSL = "true" + DB_HOST = module.rds.address + DB_PORT = tostring(module.rds.port) + DB_USER = "postgres" + DB_PASSWORD = module.db_secret.password + DB_NAME = module.rds.db_name + DB_SSL = "true" + DB_SSL_MODE = "Require" + DB_SSL_REJECT_UNAUTHORIZED = "false" + DATABASE_URL = "postgres://postgres:${urlencode(module.db_secret.password)}@${module.rds.address}:${module.rds.port}/${module.rds.db_name}?sslmode=require" + COOKIE_SECURE = "false" + COOKIE_SAMESITE = "Lax" } tags = local.common_tags } diff --git a/terraform/environments/azure/secrets.tf b/terraform/environments/azure/secrets.tf index 9b7e7c8..d199bfa 100644 --- a/terraform/environments/azure/secrets.tf +++ b/terraform/environments/azure/secrets.tf @@ -35,17 +35,21 @@ resource "azurerm_key_vault_secret" "app_runtime" { content_type = "application/json" expiration_date = "2099-12-31T23:59:59Z" value = jsonencode({ - REDIS_HOST = var.azure_redis_host - REDIS_PORT = "6379" - REDIS_PASSWORD = var.azure_redis_password - REDIS_SSL = tostring(var.azure_redis_ssl) - DB_HOST = local.azure_db_host_effective - DB_PORT = "5432" - DB_USER = local.azure_db_user_effective - DB_PASSWORD = local.azure_db_password_effective - DB_NAME = local.azure_db_database_effective - DB_SSL_MODE = local.azure_db_ssl_mode_effective - DATABASE_URL = "postgres://${local.azure_db_user_effective}:${urlencode(local.azure_db_password_effective)}@${local.azure_db_host_effective}:5432/${local.azure_db_database_effective}?sslmode=require" + REDIS_HOST = var.azure_redis_host + REDIS_PORT = "6379" + REDIS_PASSWORD = var.azure_redis_password + REDIS_SSL = tostring(var.azure_redis_ssl) + DB_HOST = local.azure_db_host_effective + DB_PORT = "5432" + DB_USER = local.azure_db_user_effective + DB_PASSWORD = local.azure_db_password_effective + DB_NAME = local.azure_db_database_effective + DB_SSL = "true" + DB_SSL_MODE = local.azure_db_ssl_mode_effective + DB_SSL_REJECT_UNAUTHORIZED = "false" + DATABASE_URL = "postgres://${local.azure_db_user_effective}:${urlencode(local.azure_db_password_effective)}@${local.azure_db_host_effective}:5432/${local.azure_db_database_effective}?sslmode=require" + COOKIE_SECURE = "false" + COOKIE_SAMESITE = "Lax" }) depends_on = [azurerm_role_assignment.current_key_vault_admin] From c2cb5959c3abe6e6ba16584f3f3bd5889ba6e1e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=AA=20=C4=90=C3=ACnh=20Hi=E1=BA=BFu?= Date: Sun, 31 May 2026 21:37:49 +0700 Subject: [PATCH 5/8] chore: promote images for 169a59f0dc4f5da4d79cbfa79d7b294ee186b982 (#78) Co-authored-by: erotonin <181610827+erotonin@users.noreply.github.com> --- k8s/values-azure.yaml | 18 +++++++++--------- k8s/values-prod.yaml | 12 ++++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/k8s/values-azure.yaml b/k8s/values-azure.yaml index c9843f3..c31c4c1 100644 --- a/k8s/values-azure.yaml +++ b/k8s/values-azure.yaml @@ -21,17 +21,17 @@ ingress: images: vote: - repository: devsecopsvotingacreyvpxg.azurecr.io/voting-app-vote - tag: e164025b90fccd645e2aa2853d485d0023019103 - digest: sha256:5713af8d425b51ed0e1c044fa2b8db5f981581428c75fe4c0912fae904c71f0b + repository: devsecopsvotingacrpgx92e.azurecr.io/voting-app-vote + tag: 169a59f0dc4f5da4d79cbfa79d7b294ee186b982 + digest: sha256:2f629ed11d6083ef71383d27ea2816a917a18481a5383ea5841847532a269045 result: - repository: devsecopsvotingacreyvpxg.azurecr.io/voting-app-result - tag: e164025b90fccd645e2aa2853d485d0023019103 - digest: sha256:acbb6f181ca97025edb9a1daacd3d6c18a8a5993fc2783e5df8319097487c253 + repository: devsecopsvotingacrpgx92e.azurecr.io/voting-app-result + tag: 169a59f0dc4f5da4d79cbfa79d7b294ee186b982 + digest: sha256:bf5ae9a299209f67d6d07fe556cea22866ebed6672e479fd1b8a7d5328012024 worker: - repository: devsecopsvotingacreyvpxg.azurecr.io/voting-app-worker - tag: e164025b90fccd645e2aa2853d485d0023019103 - digest: sha256:45206dcd2a8fb3500b641448fa200c1d2ccd5771d6dd252b9953506cc17a1851 + repository: devsecopsvotingacrpgx92e.azurecr.io/voting-app-worker + tag: 169a59f0dc4f5da4d79cbfa79d7b294ee186b982 + digest: sha256:13b80e3c9d921cf039459d31be4d55913560fdb776515d6cce7da4a360c5f163 redis: repository: redis tag: 7-alpine diff --git a/k8s/values-prod.yaml b/k8s/values-prod.yaml index 3cc4d39..cbcb702 100644 --- a/k8s/values-prod.yaml +++ b/k8s/values-prod.yaml @@ -13,16 +13,16 @@ ingress: images: vote: repository: 800557027783.dkr.ecr.us-east-1.amazonaws.com/voting-app-vote - tag: e164025b90fccd645e2aa2853d485d0023019103 - digest: sha256:5713af8d425b51ed0e1c044fa2b8db5f981581428c75fe4c0912fae904c71f0b + tag: 169a59f0dc4f5da4d79cbfa79d7b294ee186b982 + digest: sha256:2f629ed11d6083ef71383d27ea2816a917a18481a5383ea5841847532a269045 result: repository: 800557027783.dkr.ecr.us-east-1.amazonaws.com/voting-app-result - tag: e164025b90fccd645e2aa2853d485d0023019103 - digest: sha256:acbb6f181ca97025edb9a1daacd3d6c18a8a5993fc2783e5df8319097487c253 + tag: 169a59f0dc4f5da4d79cbfa79d7b294ee186b982 + digest: sha256:bf5ae9a299209f67d6d07fe556cea22866ebed6672e479fd1b8a7d5328012024 worker: repository: 800557027783.dkr.ecr.us-east-1.amazonaws.com/voting-app-worker - tag: e164025b90fccd645e2aa2853d485d0023019103 - digest: sha256:45206dcd2a8fb3500b641448fa200c1d2ccd5771d6dd252b9953506cc17a1851 + tag: 169a59f0dc4f5da4d79cbfa79d7b294ee186b982 + digest: sha256:13b80e3c9d921cf039459d31be4d55913560fdb776515d6cce7da4a360c5f163 redis: repository: redis tag: 7-alpine From b6808d95a232dd5f7558c0dac219d9c8ad2657ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=AA=20=C4=90=C3=ACnh=20Hi=E1=BA=BFu?= Date: Sun, 31 May 2026 21:45:21 +0700 Subject: [PATCH 6/8] fix: normalize result database ssl mode (#79) --- result/server.js | 59 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/result/server.js b/result/server.js index d25ba0a..1b7e887 100644 --- a/result/server.js +++ b/result/server.js @@ -12,25 +12,67 @@ var express = require('express'), var port = process.env.PORT || 4000; var dbConnected = false; -function dbSslConfig() { +function dbSslModeFromUrl(databaseUrl) { + var match = (databaseUrl || '').match(/[?&]sslmode=(disable|allow|prefer|require|verify-ca|verify-full)/i); + return match ? match[1].toLowerCase() : ''; +} + +function stripDatabaseUrlSslMode(databaseUrl) { + return (databaseUrl || '').replace(/([?&])sslmode=(disable|allow|prefer|require|verify-ca|verify-full)(&?)/ig, function(match, prefix, mode, suffix) { + if (prefix === '?' && suffix === '&') { + return '?'; + } + + if (suffix === '&') { + return prefix; + } + + return ''; + }).replace(/[?&]$/, ''); +} + +function envFlag(name) { + return (process.env[name] || '').toLowerCase(); +} + +function dbSslConfig(databaseUrl) { var explicitSsl = (process.env.DB_SSL || '').toLowerCase(); - var sslMode = (process.env.DB_SSL_MODE || '').toLowerCase(); - var databaseUrl = process.env.DATABASE_URL || ''; - var urlRequiresSsl = /[?&]sslmode=(require|verify-ca|verify-full)/i.test(databaseUrl); + var sslMode = (process.env.DB_SSL_MODE || dbSslModeFromUrl(databaseUrl)).toLowerCase(); + var rejectUnauthorized = envFlag('DB_SSL_REJECT_UNAUTHORIZED'); if (explicitSsl === 'false' || sslMode === 'disable') { return false; } - if (explicitSsl === 'true' || sslMode === 'require' || sslMode === 'verify-ca' || sslMode === 'verify-full' || urlRequiresSsl) { + if (explicitSsl === 'true' || sslMode === 'require' || sslMode === 'verify-ca' || sslMode === 'verify-full') { return { - rejectUnauthorized: process.env.DB_SSL_REJECT_UNAUTHORIZED === 'true' || sslMode === 'verify-full' + rejectUnauthorized: rejectUnauthorized ? rejectUnauthorized === 'true' : (sslMode === 'verify-ca' || sslMode === 'verify-full') }; } return false; } +function dbPoolConfig() { + var databaseUrl = process.env.DATABASE_URL || ''; + + if (process.env.DATABASE_URL) { + return { + connectionString: stripDatabaseUrlSslMode(databaseUrl), + ssl: dbSslConfig(databaseUrl) + }; + } + + return { + host: process.env.DB_HOST || 'db', + port: parseInt(process.env.DB_PORT || '5432', 10), + user: process.env.DB_USER || 'postgres', + password: process.env.DB_PASSWORD || '', + database: process.env.DB_NAME || 'postgres', + ssl: dbSslConfig(databaseUrl) + }; +} + client.collectDefaultMetrics({ prefix: 'result_' }); const httpRequestDuration = new client.Histogram({ name: 'result_http_request_duration_seconds', @@ -53,10 +95,7 @@ io.on('connection', function (socket) { }); }); -var pool = new Pool({ - connectionString: process.env.DATABASE_URL || `postgres://${process.env.DB_USER || 'postgres'}:${process.env.DB_PASSWORD || 'postgres'}@${process.env.DB_HOST || 'db'}:${process.env.DB_PORT || '5432'}/${process.env.DB_NAME || 'postgres'}`, - ssl: dbSslConfig() -}); +var pool = new Pool(dbPoolConfig()); async.retry( {times: 1000, interval: 1000}, From bf4c25109cd18c3683b8589d1f626f8f08c73b67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=AA=20=C4=90=C3=ACnh=20Hi=E1=BA=BFu?= Date: Sun, 31 May 2026 21:50:27 +0700 Subject: [PATCH 7/8] chore: promote images for b6808d95a232dd5f7558c0dac219d9c8ad2657ed (#80) Co-authored-by: erotonin <181610827+erotonin@users.noreply.github.com> --- k8s/values-azure.yaml | 12 ++++++------ k8s/values-prod.yaml | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/k8s/values-azure.yaml b/k8s/values-azure.yaml index c31c4c1..c0a758d 100644 --- a/k8s/values-azure.yaml +++ b/k8s/values-azure.yaml @@ -22,16 +22,16 @@ ingress: images: vote: repository: devsecopsvotingacrpgx92e.azurecr.io/voting-app-vote - tag: 169a59f0dc4f5da4d79cbfa79d7b294ee186b982 - digest: sha256:2f629ed11d6083ef71383d27ea2816a917a18481a5383ea5841847532a269045 + tag: b6808d95a232dd5f7558c0dac219d9c8ad2657ed + digest: sha256:9e5178ee12a6589521e0ab312ab1a39b052a01e889d8f3404a7bb5e0d7981111 result: repository: devsecopsvotingacrpgx92e.azurecr.io/voting-app-result - tag: 169a59f0dc4f5da4d79cbfa79d7b294ee186b982 - digest: sha256:bf5ae9a299209f67d6d07fe556cea22866ebed6672e479fd1b8a7d5328012024 + tag: b6808d95a232dd5f7558c0dac219d9c8ad2657ed + digest: sha256:633a19846e7503fd74ed52919b55eed1e45f33d1d8b4c807ecfd59f9d98864ff worker: repository: devsecopsvotingacrpgx92e.azurecr.io/voting-app-worker - tag: 169a59f0dc4f5da4d79cbfa79d7b294ee186b982 - digest: sha256:13b80e3c9d921cf039459d31be4d55913560fdb776515d6cce7da4a360c5f163 + tag: b6808d95a232dd5f7558c0dac219d9c8ad2657ed + digest: sha256:5eeb8c6ce16184688ae5672c8c4e67f9d352c7ae0301aa60c81ac9f8262fa979 redis: repository: redis tag: 7-alpine diff --git a/k8s/values-prod.yaml b/k8s/values-prod.yaml index cbcb702..0910ef4 100644 --- a/k8s/values-prod.yaml +++ b/k8s/values-prod.yaml @@ -13,16 +13,16 @@ ingress: images: vote: repository: 800557027783.dkr.ecr.us-east-1.amazonaws.com/voting-app-vote - tag: 169a59f0dc4f5da4d79cbfa79d7b294ee186b982 - digest: sha256:2f629ed11d6083ef71383d27ea2816a917a18481a5383ea5841847532a269045 + tag: b6808d95a232dd5f7558c0dac219d9c8ad2657ed + digest: sha256:9e5178ee12a6589521e0ab312ab1a39b052a01e889d8f3404a7bb5e0d7981111 result: repository: 800557027783.dkr.ecr.us-east-1.amazonaws.com/voting-app-result - tag: 169a59f0dc4f5da4d79cbfa79d7b294ee186b982 - digest: sha256:bf5ae9a299209f67d6d07fe556cea22866ebed6672e479fd1b8a7d5328012024 + tag: b6808d95a232dd5f7558c0dac219d9c8ad2657ed + digest: sha256:633a19846e7503fd74ed52919b55eed1e45f33d1d8b4c807ecfd59f9d98864ff worker: repository: 800557027783.dkr.ecr.us-east-1.amazonaws.com/voting-app-worker - tag: 169a59f0dc4f5da4d79cbfa79d7b294ee186b982 - digest: sha256:13b80e3c9d921cf039459d31be4d55913560fdb776515d6cce7da4a360c5f163 + tag: b6808d95a232dd5f7558c0dac219d9c8ad2657ed + digest: sha256:5eeb8c6ce16184688ae5672c8c4e67f9d352c7ae0301aa60c81ac9f8262fa979 redis: repository: redis tag: 7-alpine From 4368462ee0c303b781eb488788dd0ed1ffc58c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hi=E1=BA=BFu?= Date: Sun, 31 May 2026 23:51:26 +0700 Subject: [PATCH 8/8] ci: build and deploy staging from dev --- .github/workflows/ci-pipeline.yml | 54 ++++++++++--------- README.md | 12 ++--- docs/architecture-conceptual.md | 2 +- docs/demo-checklist.md | 2 +- docs/demo-recording-guide.md | 2 +- docs/devsecops-pipeline-diagram.md | 2 +- docs/final-readiness.md | 2 +- docs/pipeline-frameworks.md | 46 ++++++---------- docs/teacher-aligned-architecture.md | 20 +++---- docs/teacher-aligned-demo-runbook.md | 2 +- policies/sigstore/README.md | 1 + .../clusterimagepolicy-ecr-keyless.yaml | 2 +- terraform/environments/aws/oidc.tf | 5 +- terraform/environments/azure/acr.tf | 9 ++++ 14 files changed, 82 insertions(+), 79 deletions(-) diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index 3ae509e..9a36ac1 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -12,7 +12,7 @@ on: workflow_dispatch: inputs: promote_production: - description: "Promote the already-built main commit to production" + description: "Record a human production approval for the already-staged release" required: true default: "false" type: choice @@ -254,16 +254,13 @@ jobs: name: Classify main push runs-on: ubuntu-latest if: github.event_name == 'push' && github.ref == 'refs/heads/main' - outputs: - build_required: ${{ steps.classify.outputs.build_required }} steps: - name: Checkout release source uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: fetch-depth: 2 - - name: Decide whether this is a release build or GitOps-only promotion - id: classify + - name: Classify production desired-state update shell: bash run: | set -euo pipefail @@ -271,19 +268,18 @@ jobs: non_gitops="$(printf '%s\n' "$changed" | grep -Ev '^(k8s/values-staging.yaml|k8s/values-prod.yaml|k8s/values-azure.yaml)$' || true)" if [[ -z "$non_gitops" ]]; then - echo "build_required=false" >> "$GITHUB_OUTPUT" - echo "Main push only changed GitOps values; production ArgoCD will sync without rebuilding." + echo "Main push only changed GitOps values; production ArgoCD will sync the promoted image without rebuilding." exit 0 fi - echo "build_required=true" >> "$GITHUB_OUTPUT" - echo "Main push changed release source/config; build, sign, verify, scan, staging, DAST, and promotion are required." + echo "Main source/config changed after a reviewed release PR." + echo "Image build, signing, staging deployment, and DAST are performed from dev before promotion." build-sign-verify-scan: name: Build, sign, verify, and scan image runs-on: ubuntu-latest - needs: classify-main-push - if: needs.classify-main-push.outputs.build_required == 'true' + needs: dev-integration-security-scan + if: github.event_name == 'push' && github.ref == 'refs/heads/dev' strategy: fail-fast: false matrix: @@ -396,17 +392,23 @@ jobs: run: cosign sign --yes "${{ steps.push_acr.outputs.image_digest }}" - name: Verify ECR signature identity and crypto policy + env: + IMAGE_DIGEST: ${{ steps.push_ecr.outputs.image_digest }} + SIGNATURE_IDENTITY: https://github.com/${{ github.repository }}/.github/workflows/ci-pipeline.yml@${{ github.ref }} run: | bash scripts/verify-cosign-signature-policy.sh \ - "${{ steps.push_ecr.outputs.image_digest }}" \ - "https://github.com/${{ github.repository }}/.github/workflows/ci-pipeline.yml@refs/heads/main" + "$IMAGE_DIGEST" \ + "$SIGNATURE_IDENTITY" - name: Verify ACR signature identity and crypto policy if: env.ACR_LOGIN_SERVER != '' + env: + IMAGE_DIGEST: ${{ steps.push_acr.outputs.image_digest }} + SIGNATURE_IDENTITY: https://github.com/${{ github.repository }}/.github/workflows/ci-pipeline.yml@${{ github.ref }} run: | bash scripts/verify-cosign-signature-policy.sh \ - "${{ steps.push_acr.outputs.image_digest }}" \ - "https://github.com/${{ github.repository }}/.github/workflows/ci-pipeline.yml@refs/heads/main" + "$IMAGE_DIGEST" \ + "$SIGNATURE_IDENTITY" - name: Trivy image scan after signature verification uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 @@ -420,7 +422,7 @@ jobs: name: Deploy staging through ArgoCD GitOps runs-on: ubuntu-latest needs: build-sign-verify-scan - if: github.event_name == 'push' && github.ref == 'refs/heads/main' + if: github.event_name == 'push' && github.ref == 'refs/heads/dev' steps: - name: Checkout release source uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 @@ -475,7 +477,7 @@ jobs: name: DAST staging smoke and baseline scan runs-on: ubuntu-latest needs: deploy-staging-gitops - if: github.event_name == 'push' && github.ref == 'refs/heads/main' + if: github.event_name == 'push' && github.ref == 'refs/heads/dev' steps: - name: Require staging URL if: vars.STAGING_URL == '' @@ -516,12 +518,12 @@ jobs: needs: - build-sign-verify-scan - dast-staging - if: github.event_name == 'push' && github.ref == 'refs/heads/main' + if: github.event_name == 'push' && github.ref == 'refs/heads/dev' steps: - name: Checkout uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: - fetch-depth: 2 + fetch-depth: 0 - name: Check whether promotion is needed id: promote @@ -530,7 +532,7 @@ jobs: message="$(git log -1 --pretty=%s)" changed="$(git diff --name-only HEAD^1 HEAD || true)" - if [[ "$message" == chore:\ promote\ images* ]]; then + if [[ "$message" == chore:\ promote\ staged\ images* ]]; then echo "should_promote=false" >> "$GITHUB_OUTPUT" exit 0 fi @@ -578,13 +580,15 @@ jobs: uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c with: token: ${{ secrets.ACTIONS_PR_TOKEN || github.token }} + base: main branch: promote/images-${{ github.sha }} - title: "chore: promote images for ${{ github.sha }}" - commit-message: "chore: promote images for ${{ github.sha }}" + title: "chore: promote staged images for ${{ github.sha }}" + commit-message: "chore: promote staged images for ${{ github.sha }}" body: | - Promote signed images built from `${{ github.sha }}`. + Promote signed images built from dev commit `${{ github.sha }}`. - This PR updates the GitOps values files only. Merge it after reviewing the successful build, signature verification, image scan, SBOM, and DAST jobs. + This release PR carries the tested source changes plus the production GitOps values that pin the exact image digests verified in staging. + Merge it after reviewing the successful dev build, signature verification, image scan, SBOM, staging deploy, and DAST jobs. labels: gitops,promotion production-approval: @@ -598,5 +602,5 @@ jobs: run: | echo "Production promotion approved for commit ${GITHUB_SHA}." echo "This job is the human approval boundary." - echo "Production should promote a previously signed image digest, not rebuild or deploy from a direct main push." + echo "Production should promote a previously signed dev image digest, not rebuild or deploy from a direct main push." echo "ArgoCD production sync is intentionally performed by GitOps, not by committing from CI." diff --git a/README.md b/README.md index 3916cf4..4672078 100644 --- a/README.md +++ b/README.md @@ -13,14 +13,14 @@ Developers -> light feature security gate -> merge to dev -> integration security gate on combined work - -> release pull request from dev into main - -> full release gate on final code and deploy config -> build images, generate SBOM, push immutable digests -> Cosign keyless sign and verify identity plus crypto policy -> Trivy image scan by digest -> ArgoCD syncs staging from the staging GitOps branch -> smoke test and OWASP ZAP DAST against staging - -> GitOps promotion PR updates production Helm values and image digests + -> promotion PR from tested dev commit into main + -> full release gate on final code and production deploy config + -> promotion PR updates production Helm values and image digests -> ArgoCD syncs production from main after review ``` @@ -49,12 +49,12 @@ On Azure, only the `vote` service is exposed with a public LoadBalancer by defau | Area | Implementation | | --- | --- | | Local pre-commit | `.pre-commit-config.yaml` runs YAML hygiene, Gitleaks, Terraform format/validate, and optional local wrappers for Checkov, Hadolint, yamllint, and Semgrep when those CLIs are installed. CI remains the authoritative security gate. | -| PR security gates | Feature PRs into `dev` run fast feedback checks. Release PRs into `main` run full SAST, IaC, dependency, Helm render, and policy checks before release build is allowed. | +| PR security gates | Feature PRs into `dev` run fast feedback checks. Release/promotion PRs into `main` run full SAST, IaC, dependency, Helm render, and policy checks after staging has validated the built artifact. | | Build security | Docker build for `vote`, `result`, and `worker`; Syft SPDX SBOM generation; Trivy image scan after signature verification. | | Signing | Cosign keyless signing through GitHub Actions OIDC/Fulcio. Images are signed by digest, not mutable tag. CI verifies the certificate identity and rejects weak certificate/signature algorithms before scanning and promotion. | | Registry | Images are pushed to AWS ECR and Azure ACR. ECR tags are immutable and scan-on-push is enabled. | -| GitOps staging | After main build/sign/verify/scan, CI updates the `staging` branch with `k8s/values-staging.yaml`. The AWS ArgoCD staging app tracks that branch and deploys to `voting-staging`. | -| GitOps production | After staging smoke test and ZAP DAST, CI opens a promotion PR that updates `k8s/values-prod.yaml` and `k8s/values-azure.yaml` on `main`. The AWS and Azure production ArgoCD apps deploy only after that PR is reviewed and merged. | +| GitOps staging | After `dev` build/sign/verify/scan, CI updates the `staging` branch with `k8s/values-staging.yaml`. The AWS ArgoCD staging app tracks that branch and deploys to `voting-staging`. | +| GitOps production | After staging smoke test and ZAP DAST, CI opens a promotion PR from the tested `dev` commit into `main`, updating `k8s/values-prod.yaml` and `k8s/values-azure.yaml`. The AWS and Azure production ArgoCD apps deploy only after that PR is reviewed and merged. | | Admission policy | Gatekeeper rejects unsafe Kubernetes manifests. AWS EKS enforces signed ECR images with Sigstore policy-controller. | | Secrets | External Secrets Operator syncs AWS Secrets Manager and Azure Key Vault into Kubernetes. Secrets are not committed in Helm values. | | Runtime security | Falco detects suspicious runtime behavior and can open a GitHub incident workflow. Quarantine is a manual approval workflow. | diff --git a/docs/architecture-conceptual.md b/docs/architecture-conceptual.md index 05e6fd1..d61c669 100644 --- a/docs/architecture-conceptual.md +++ b/docs/architecture-conceptual.md @@ -169,5 +169,5 @@ Y nghia: ## 11. Doan Noi 30 Giay ```text -Kien truc cua em di theo dong chay DevSecOps. Developer khong day thang code len production ma tao pull request vao repository. PR duoc quet so bo de chan secret, loi code va cau hinh nguy hiem. Sau khi qua review, feature vao nhanh tich hop de kiem tra nhieu thay doi chung voi nhau. Khi san sang release, dev tao PR sang main va chay gate nghiem hon. Main moi build ra container image bat bien, co danh sach thanh phan va chu ky. Image duoc deploy len staging de test runtime va DAST. Neu staging pass, pipeline tao promotion PR de cap nhat desired state production. Production va Azure khong nhan lenh deploy truc tiep tu CI, ma duoc dong bo tu Git. Runtime tiep tuc co policy, secret management, monitoring va DR. +Kien truc cua em di theo dong chay DevSecOps. Developer khong day thang code len production ma tao pull request vao repository. PR duoc quet so bo de chan secret, loi code va cau hinh nguy hiem. Sau khi qua review, feature vao nhanh tich hop `dev` de kiem tra nhieu thay doi chung voi nhau. Khi code vao `dev`, pipeline build container image bat bien, tao SBOM, ky image va scan CVE tren artifact. Image do duoc deploy len staging de test runtime va DAST. Neu staging pass, pipeline tao promotion PR tu tested `dev` commit vao `main`, kem production Helm values pin dung image digest da test. Production va Azure khong nhan lenh deploy truc tiep tu CI, ma duoc dong bo tu Git sau khi PR duoc review. Runtime tiep tuc co policy, secret management, monitoring va DR. ``` diff --git a/docs/demo-checklist.md b/docs/demo-checklist.md index 1ad97c4..c614bfc 100644 --- a/docs/demo-checklist.md +++ b/docs/demo-checklist.md @@ -68,7 +68,7 @@ Can demo GitHub Actions run: Noi voi thay: ```text -Sau khi vao main, pipeline moi tao artifact. Image duoc build, sinh SBOM, ky so, verify chu ky va scan CVE. Artifact nao khong qua cac buoc nay thi khong duoc dua vao staging. +Sau khi vao `dev`, pipeline tao artifact. Image duoc build, sinh SBOM, ky so, verify chu ky va scan CVE. Artifact nao khong qua cac buoc nay thi khong duoc dua vao staging. ``` ## 5. Demo Staging Va DAST diff --git a/docs/demo-recording-guide.md b/docs/demo-recording-guide.md index d7efd0f..29e624f 100644 --- a/docs/demo-recording-guide.md +++ b/docs/demo-recording-guide.md @@ -166,7 +166,7 @@ Open: Explain the workflow: - Pull request event runs `PR security gates`. -- Push to `main` builds signed staging artifacts for `vote`, `result`, and `worker`. +- Push to `dev` builds signed staging artifacts for `vote`, `result`, and `worker`. - Build job performs dependency/image scanning, SBOM, Cosign signing, and registry push. - DAST runs against the staging URL. - Production approval is a separate manual gate. diff --git a/docs/devsecops-pipeline-diagram.md b/docs/devsecops-pipeline-diagram.md index b323905..00ae4c9 100644 --- a/docs/devsecops-pipeline-diagram.md +++ b/docs/devsecops-pipeline-diagram.md @@ -149,7 +149,7 @@ flowchart TB ## 3. Loi Thuyet Minh Ngan ```text -Pipeline nay khong phai chi la build va deploy. No la chuoi kiem soat rui ro. Developer tao PR thi he thong quet loi som nhu secret, SAST, SCA/dependency va cau hinh nguy hiem. SCA o tang source giup bat CVE trong dependency manifest/lockfile som de developer sua ngay. Khi code vao dev, pipeline kiem tra lai vi nhieu feature ghep chung co the sinh loi moi. Truoc khi vao main co release gate de dam bao release candidate sach. Main moi build artifact that, tao SBOM, ky image va scan CVE tren image da build. Artifact da pass moi len staging. Staging phai smoke test va DAST pass thi moi mo promotion PR. Production khong deploy truc tiep tu CI, ma ArgoCD dong bo tu desired state trong Git. Sau deploy van co runtime policy, secret management, monitoring va DR. +Pipeline nay khong phai chi la build va deploy. No la chuoi kiem soat rui ro. Developer tao PR thi he thong quet loi som nhu secret, SAST, SCA/dependency va cau hinh nguy hiem. SCA o tang source giup bat CVE trong dependency manifest/lockfile som de developer sua ngay. Khi code vao dev, pipeline kiem tra lai vi nhieu feature ghep chung co the sinh loi moi, sau do build artifact that, tao SBOM, ky image va scan CVE tren image da build. Artifact da pass moi len staging. Staging phai smoke test va DAST pass thi moi mo promotion PR vao main. PR nay chay release gate, gom source da test va production values pin dung image digest da test. Production khong deploy truc tiep tu CI, ma ArgoCD dong bo tu desired state trong Git. Sau deploy van co runtime policy, secret management, monitoring va DR. ``` ## 4. Diem Can Nhan Manh Khi Giai Thich diff --git a/docs/final-readiness.md b/docs/final-readiness.md index 4338434..2433bb5 100644 --- a/docs/final-readiness.md +++ b/docs/final-readiness.md @@ -31,7 +31,7 @@ This file summarizes the final project state for the capstone handoff. - ArgoCD RBAC maps Entra group object IDs to admin and readonly roles. - PR security gate demo passed on pull request `#28`. - Production approval workflow_dispatch run passed. -- Full main release pipeline `26556068376` passed build, SBOM, Cosign sign/verify, Trivy image scan, staging GitOps, smoke test, OWASP ZAP DAST, and promotion PR creation. +- Historical full release pipeline `26556068376` passed build, SBOM, Cosign sign/verify, Trivy image scan, staging GitOps, smoke test, OWASP ZAP DAST, and promotion PR creation. - GitOps promotion run `26556732195` passed after promotion PR `#72` was merged. - AWS staging, AWS production, and Azure warm standby `/healthz` endpoints returned HTTP 200. - Redis CloudWatch log group is encrypted with KMS and retains logs for 365 days. diff --git a/docs/pipeline-frameworks.md b/docs/pipeline-frameworks.md index 32cd082..a976ef9 100644 --- a/docs/pipeline-frameworks.md +++ b/docs/pipeline-frameworks.md @@ -10,7 +10,7 @@ File nay trinh bay implementation cu the cua kien truc DevSecOps. Neu `docs/arch | Branch feature | Git branch | Noi developer lam tung thay doi rieng | | Pull request | GitHub Pull Request | Review code va kich hoat security gate | | Integration branch | `dev` | Gom feature da pass gate de kiem tra tong hop | -| Release branch | `main` | Nguon release va desired state production | +| Release branch | `main` | Nguon production desired state sau khi staging pass | | Branch protection | GitHub branch protection | Bat required checks va approving review | Hien tai: @@ -45,27 +45,11 @@ Sau khi merge vao `dev`, pipeline chay bo kiem tra rong hon. | Kubernetes manifest | Helm lint, Helm template | Dam bao chart render hop le | | Policy as code | Conftest/OPA | Chan manifest vi pham policy | -Ket qua dung: `dev` tro thanh release candidate sach hon, co the tao PR sang `main`. +Ket qua dung: `dev` tro thanh release candidate sach hon va du dieu kien build/deploy staging. -## 4. Release PR Gate Vao `main` +## 4. Release Artifact Build Tren `dev` -Khi tao PR `dev -> main`, GitHub Actions chay `Release PR full security gates`. - -| Muc kiem tra | Tool / framework | Muc dich | -| --- | --- | --- | -| Secret | Gitleaks | Khong cho secret vao release branch | -| SAST | Semgrep | Chan loi bao mat trong code | -| IaC | Checkov, tfsec | Chan cau hinh cloud nguy hiem | -| K8s/Helm | Helm lint/template | Dam bao manifest render duoc cho staging/prod/Azure | -| Policy | Conftest | Dam bao manifest hop policy | - -Ket qua dung: PR vao `main` chi merge khi full gate pass va co review. - -Ghi chu: Trivy FS va `npm audit` la source-level SCA. Chung bat CVE trong dependency manifest/lockfile som o PR/integration/release gate. Trivy image scan o buoc artifact la lop khac, dung de quet image that sau khi build, bao gom base image va package da cai trong container. - -## 5. Build, SBOM, Signing Va Scan Image - -Khi code vao `main`, GitHub Actions build ba service: +Khi code vao `dev`, GitHub Actions build ba service: - `vote`: Python/Flask. - `result`: Node.js/Express. @@ -80,13 +64,15 @@ Pipeline build container image bang Docker va day len: | --- | --- | --- | | Build image | Docker | Tao image cho `vote`, `result`, `worker` | | Generate SBOM | Syft | Tao SBOM SPDX cho image | -| Sign image | Cosign keyless | Ky image digest bang GitHub OIDC/Fulcio/Rekor | +| Sign image | Cosign keyless | Ky image digest bang GitHub OIDC/Fulcio/Rekor tu branch `dev` | | Verify signature | Cosign + policy script | Xac minh identity, issuer, Rekor bundle/certificate | | Image CVE scan | Trivy image | Chan image co CVE nghiem trong | Ket qua dung: chi image da co SBOM, da ky, da verify va scan pass moi duoc dung cho staging. -## 6. Staging Deployment Va DAST +Ghi chu: Trivy FS va `npm audit` la source-level SCA. Chung bat CVE trong dependency manifest/lockfile som o PR/integration/release gate. Trivy image scan o buoc artifact la lop khac, dung de quet image that sau khi build, bao gom base image va package da cai trong container. + +## 5. Staging Deployment Va DAST | Thanh phan | Framework / tool | Gia tri hien tai | | --- | --- | --- | @@ -104,22 +90,22 @@ Ket qua dung: - `/healthz` staging HTTP 200. - OWASP ZAP baseline pass. -## 7. Production Promotion +## 6. Release PR Gate Va Production Promotion -Neu staging va DAST pass, pipeline tao promotion PR cap nhat: +Neu staging va DAST pass, pipeline tao promotion PR tu tested `dev` commit vao `main`. PR nay gom source changes da test va cap nhat: - `k8s/values-prod.yaml` - `k8s/values-azure.yaml` -Promotion PR thay doi image digest cua production/Azure sang digest da duoc build, sign, verify va test o staging. +Promotion PR thay doi image digest cua production/Azure sang digest da duoc build, sign, verify va test o staging. GitHub Actions chay `Release PR full security gates` tren PR nay truoc khi merge vao `main`. | Thanh phan | Framework / tool | Vai tro | | --- | --- | --- | -| Promotion PR | GitHub Pull Request | Review thay doi desired state production | +| Promotion PR | GitHub Pull Request | Review source release va desired state production | | GitOps desired state | Helm values | Ghi image digest production/Azure | | Required gate | Release PR full security gates | Kiem tra promotion PR truoc merge | -## 8. Production Va Azure Deployment +## 7. Production Va Azure Deployment | Moi truong | Cloud | Cluster | ArgoCD app | Namespace | Helm values | | --- | --- | --- | --- | --- | --- | @@ -133,7 +119,7 @@ Ket qua dung: - `voting-azure` `Synced Healthy`. - `/healthz` production va Azure HTTP 200. -## 9. Runtime Security +## 8. Runtime Security | Lop bao ve | Framework / tool | Muc dich | | --- | --- | --- | @@ -146,7 +132,7 @@ Ket qua dung: | Logging | Loki, Promtail | Tap trung log | | Runtime detection | Falco | Phat hien hanh vi bat thuong trong cluster | -## 10. Infrastructure Va Multi-Cloud +## 9. Infrastructure Va Multi-Cloud | Thanh phan | Framework / tool | Vai tro | | --- | --- | --- | @@ -160,7 +146,7 @@ Ket qua dung: | Network DR | AWS VPN Gateway, Azure VNet Gateway, BGP | Ket noi hai cloud | | Failover | Route53 failover script | Chuyen traffic khi primary loi | -## 11. Evidence Gan Nhat +## 10. Evidence Gan Nhat | Evidence | Trang thai | | --- | --- | diff --git a/docs/teacher-aligned-architecture.md b/docs/teacher-aligned-architecture.md index aa022f4..d607c83 100644 --- a/docs/teacher-aligned-architecture.md +++ b/docs/teacher-aligned-architecture.md @@ -87,12 +87,12 @@ AWS chay primary, Azure la warm standby. Production khong deploy truc tiep tu CI | Code | Developer thay doi ung dung/IaC/Helm | Git branch, PR, pre-commit | Loi co ban bi bat som | | Feature gate | Fast feedback truoc khi vao `dev` | Gitleaks, Semgrep, Trivy FS, SonarCloud | PR feature khong dua secret/bug ro rang vao dev | | Integration gate | Kiem tra tong the sau khi merge vao `dev` | Gitleaks, Semgrep, Checkov, tfsec, Trivy FS, Helm lint/template, Conftest | `dev` thanh release candidate sach | -| Release gate | Chan release sai truoc `main` | Full security gate tren PR `dev -> main` | Chi source/config da qua gate moi vao main | -| Build | Tao artifact bat bien | Docker, GitHub Actions matrix | Co image cho vote/result/worker | +| Build | Tao artifact bat bien sau khi merge vao `dev` | Docker, GitHub Actions matrix | Co image cho vote/result/worker | | Supply chain | Chung minh image dung nguon va khong bi thay the | Syft SBOM, Cosign keyless, Fulcio/Rekor, GitHub OIDC | Image digest co SBOM va chu ky | | Image security | Scan artifact sau khi build | Trivy image scan by digest | CVE HIGH/CRITICAL bi chan | | Staging deploy | Chay ban build that tren moi truong truoc production | GitOps branch `staging`, ArgoCD app `voting-staging` | Staging `Synced Healthy` | | Dynamic test | Kiem tra app dang song nhu attacker/user that | Smoke `/healthz`, OWASP ZAP baseline | DAST pass truoc promotion | +| Release gate | Chan release sai truoc `main` | Full security gate tren promotion PR tu tested `dev` commit | Chi source/config da qua staging moi vao main | | Production promotion | Doi desired state production co review | GitOps promotion PR, Helm values digest | Production khong bi auto-push truc tiep | | Production deploy | Cluster tu sync theo Git | ArgoCD `voting-production`, Azure `voting-azure` | AWS/Azure `Synced Healthy` | | Operate | Giam sat, phat hien, phan ung | Prometheus, Grafana, Loki, Promtail, Falco, incident workflow | Co evidence runtime va response path | @@ -107,11 +107,11 @@ flowchart LR Feature["feature/*"] --> PRDev["PR to dev\nFeature PR light gate"] PRDev --> Dev["dev\nintegration branch"] Dev --> DevGate["Dev integration security gates"] - DevGate --> PRMain["PR dev -> main\nRelease PR full gate"] - PRMain --> Main["main\nrelease branch"] - Main --> Build["Build/sign/scan/deploy staging"] - Build --> PromotePR["GitOps promotion PR\nvalues-prod + values-azure"] - PromotePR --> MainProd["main updated\nproduction desired state"] + DevGate --> Build["Build/sign/scan"] + Build --> Staging["Deploy staging\nSmoke + DAST"] + Staging --> PromotePR["Promotion PR to main\nsource + values-prod + values-azure"] + PromotePR --> PRMain["Release PR full gate"] + PRMain --> Main["main\nproduction desired state"] ``` Branch protection hien tai: @@ -126,8 +126,8 @@ Pipeline chinh nam o `.github/workflows/ci-pipeline.yml`. ```mermaid flowchart LR - Source["main push"] --> Classify["Classify changed files"] - Classify --> Build["Docker build\nvote/result/worker"] + Source["dev push"] --> DevGate["Dev integration gates"] + DevGate --> Build["Docker build\nvote/result/worker"] Build --> SBOM["Syft SBOM"] Build --> Push["Push ECR + ACR"] Push --> Sign["Cosign keyless sign digest"] @@ -235,7 +235,7 @@ CI: Run IDs: -- Full main release: `26556068376` +- Historical full release: `26556068376` - GitOps promotion: `26556732195` ## 5. Cach Tra Loi Khi Thay Hoi "Kien Truc Nay Khac Gi CI/CD Thuong?" diff --git a/docs/teacher-aligned-demo-runbook.md b/docs/teacher-aligned-demo-runbook.md index ab56d81..771ce37 100644 --- a/docs/teacher-aligned-demo-runbook.md +++ b/docs/teacher-aligned-demo-runbook.md @@ -131,7 +131,7 @@ Trivy image scan after signature verification ## 5. Demo Staging Deploy Và DAST -Sau khi CI main pass và staging branch được cập nhật: +Sau khi CI trên `dev` pass và staging branch được cập nhật: ```powershell $ctx = "arn:aws:eks:us-east-1:800557027783:cluster/voting-app-cluster" diff --git a/policies/sigstore/README.md b/policies/sigstore/README.md index 530e6e0..a83395f 100644 --- a/policies/sigstore/README.md +++ b/policies/sigstore/README.md @@ -3,6 +3,7 @@ This directory contains the cluster-side image signature policy for the AWS primary site. The policy verifies the voting app images in Amazon ECR using Sigstore keyless signatures issued by GitHub Actions through Fulcio. +Release images are built and signed from the protected `dev` branch after staging validation, then promoted to production by a GitOps PR into `main`. ## Policy diff --git a/policies/sigstore/clusterimagepolicy-ecr-keyless.yaml b/policies/sigstore/clusterimagepolicy-ecr-keyless.yaml index 5fdeb19..25b5e93 100644 --- a/policies/sigstore/clusterimagepolicy-ecr-keyless.yaml +++ b/policies/sigstore/clusterimagepolicy-ecr-keyless.yaml @@ -13,4 +13,4 @@ spec: url: https://fulcio.sigstore.dev identities: - issuer: https://token.actions.githubusercontent.com - subjectRegExp: "^https://github.com/erotonin/devsecops-voting/.github/workflows/ci-pipeline.yml@refs/heads/main$" + subjectRegExp: "^https://github.com/erotonin/devsecops-voting/.github/workflows/ci-pipeline.yml@refs/heads/(dev|main)$" diff --git a/terraform/environments/aws/oidc.tf b/terraform/environments/aws/oidc.tf index 979dcef..6c7efcf 100644 --- a/terraform/environments/aws/oidc.tf +++ b/terraform/environments/aws/oidc.tf @@ -18,7 +18,10 @@ resource "aws_iam_role" "github_actions" { Condition = { StringEquals = { "token.actions.githubusercontent.com:aud" : "sts.amazonaws.com" - "token.actions.githubusercontent.com:sub" : "repo:${var.github_repo}:ref:refs/heads/main" + "token.actions.githubusercontent.com:sub" : [ + "repo:${var.github_repo}:ref:refs/heads/main", + "repo:${var.github_repo}:ref:refs/heads/dev" + ] } } } diff --git a/terraform/environments/azure/acr.tf b/terraform/environments/azure/acr.tf index fb41553..1251dde 100644 --- a/terraform/environments/azure/acr.tf +++ b/terraform/environments/azure/acr.tf @@ -48,6 +48,15 @@ resource "azurerm_federated_identity_credential" "github_actions_main" { subject = "repo:${var.github_repo}:ref:refs/heads/main" } +resource "azurerm_federated_identity_credential" "github_actions_dev" { + name = "${var.name_prefix}-github-dev" + resource_group_name = module.azure_networking.resource_group_name + parent_id = azurerm_user_assigned_identity.github_actions.id + audience = ["api://AzureADTokenExchange"] + issuer = "https://token.actions.githubusercontent.com" + subject = "repo:${var.github_repo}:ref:refs/heads/dev" +} + resource "azurerm_role_assignment" "github_actions_acr_push" { principal_id = azurerm_user_assigned_identity.github_actions.principal_id role_definition_name = "AcrPush"