feat(P31): encryption-by-default + per-stack CMK (REQ-83, REQ-84, REQ-85)

---ci---
project: acdl
phase: 31
milestone: v1.8
status: execute
---/ci---

- New kms-key L1 primitive (aws:kms:key) with enable_key_rotation=true
  (AWS-managed annual rotation, D-075). Registered in registry.json.
- Adapter TYPE_MAP expanded for aws:kms:key + aws:kms:alias.
- Adapter emits enable_key_rotation from NFR.
- S3 adapter emits server_side_encryption_configuration with KMS when
  kms_key_arn provided; managed KMS fallback with stderr warning when not.
- All 10 existing L1 primitives now have encryption_enabled NFR (default true).
- s3, rds, ecr, ecs-service, ecs-cluster have kms_key_arn input.
- Both L2 compositions (static-assets, microservice) now include a kms-key
  child + wires connecting kms_key_arn to children.
- L2 stack outputs include kms_key_arn.

Tests: +7 (300 -> 307). All pass. run_platform.sh --check-only green
(static-assets now resolves to 5 resources with the CMK).
This commit is contained in:
Jon Chery
2026-07-22 22:11:03 +00:00
parent 1e4133e11a
commit de91a4bb76
20 changed files with 477 additions and 19 deletions
+12 -1
View File
@@ -52,7 +52,18 @@
"description": "The target group ARN."
}
},
"nfrs": {},
"nfrs": {
"encryption_enabled": {
"type": "boolean",
"description": "Enable TLS/HTTPS encryption in transit.",
"default": true
},
"tls_enabled": {
"type": "boolean",
"description": "Enable TLS listener.",
"default": true
}
},
"resources": [
{
"type": "aws:elbv2:loadbalancer",
+7 -1
View File
@@ -59,7 +59,13 @@
"description": "The Origin Access Control ID."
}
},
"nfrs": {},
"nfrs": {
"encryption_enabled": {
"type": "boolean",
"description": "Enable encryption in transit (HTTPS only).",
"default": true
}
},
"resources": [
{
"type": "aws:cloudfront:distribution",
+17 -1
View File
@@ -14,6 +14,11 @@
"type": "string",
"description": "AWS region the repository is created in.",
"required": true
},
"kms_key_arn": {
"type": "string",
"description": "ARN of the CMK for repository encryption; if absent, uses AWS-managed key.",
"required": false
}
},
"outputs": {
@@ -26,5 +31,16 @@
"description": "The ECR repository ARN."
}
},
"nfrs": {}
"nfrs": {
"encryption_enabled": {
"type": "boolean",
"description": "Enable repository encryption (KMS).",
"default": true
},
"encryption_type": {
"type": "string",
"description": "Encryption type.",
"default": "KMS"
}
}
}
+12 -1
View File
@@ -14,6 +14,11 @@
"type": "string",
"description": "AWS region the cluster is created in.",
"required": true
},
"kms_key_arn": {
"type": "string",
"description": "ARN of the CMK for CloudWatch log group encryption; if absent, uses managed key.",
"required": false
}
},
"outputs": {
@@ -26,5 +31,11 @@
"description": "The ECS cluster id (name)."
}
},
"nfrs": {}
"nfrs": {
"encryption_enabled": {
"type": "boolean",
"description": "Enable CloudWatch log group encryption.",
"default": true
}
}
}
+12 -1
View File
@@ -56,6 +56,11 @@
"type": "string",
"description": "AWS region the service is created in.",
"required": true
},
"kms_key_arn": {
"type": "string",
"description": "ARN of the CMK for CloudWatch log group encryption; if absent, uses managed key.",
"required": false
}
},
"outputs": {
@@ -68,7 +73,13 @@
"description": "The ECS task definition ARN."
}
},
"nfrs": {},
"nfrs": {
"encryption_enabled": {
"type": "boolean",
"description": "Enable CloudWatch log group encryption.",
"default": true
}
},
"resources": [
{
"type": "aws:ecs:task_definition",
+7 -1
View File
@@ -36,5 +36,11 @@
"description": "The IAM role id."
}
},
"nfrs": {}
"nfrs": {
"encryption_enabled": {
"type": "boolean",
"description": "Encryption is not applicable to IAM roles but included for standards compliance.",
"default": true
}
}
}
+99
View File
@@ -0,0 +1,99 @@
# kms-key — KMS customer-managed key
> **Module kind:** primitive | **Version:** 1.0.0
A customer-managed KMS key for per-stack encryption. Created with key
rotation enabled. One key per L2 deployment (no shared keys).
## Resources
| Resource | Type | Purpose |
|----------|------|---------|
| kms-key | `aws_kms_key` | The KMS customer-managed key |
## Inputs
| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `description` | string | yes | — | Description of the KMS key |
| `region` | string | yes | — | AWS region the KMS key is created in |
| `deletion_window_days` | number | no | 30 | Number of days before the key is deleted after deletion is requested |
## Outputs
| Name | Type | Description |
|------|------|-------------|
| `kms_key_arn` | arn | The ARN of the KMS key |
| `kms_key_id` | string | The ID of the KMS key |
## NFRs
| Name | Type | Default | Description |
|------|------|---------|-------------|
| `enable_rotation` | boolean | true | Enable automatic key rotation |
| `deletion_protection` | boolean | true | Prevent key destruction |
| `encryption_enabled` | boolean | true | Encryption is always enabled for a KMS key |
## Usage
```json
{
"id": "kms-key",
"type": "aws:kms:key",
"module": "kms-key@1.0.0",
"inputs": {
"description": "ACDL per-stack CMK",
"region": "us-east-1"
}
}
```
A concrete instance is at `instance.json` (used by the platform
pipeline as the regression baseline).
## Compliance extension points
- **Key rotation** — automatic key rotation enabled by default (SOC2 CC6.1, HIPAA §164.312(a)(2)(iv), GDPR Art.32).
- **Deletion protection** — pending deletion window prevents accidental destruction (SOC2 CC7.2).
- **Key policy** — restrict key usage to the stack's IAM roles (SOC2 CC6.1, GDPR Art.32).
- **Audit logging** — CloudTrail logs all KMS API calls (SOC2 CC7.2, DORA audit trail).
## Examples
Validated example contracts are in [`examples/`](examples/). The platform-test
pipeline validates them against `schemas/contract.schema.json`.
### Simple
A minimal deployment:
[`examples/simple.yaml`](examples/simple.yaml)
```yaml
uses: acdl/pipelines/deploy.yaml@v1.8
module: kms-key
environment: dev
inputs:
description: "Simple CMK for testing"
region: us-east-1
```
### Complex
A production deployment with optional inputs:
[`examples/complex.yaml`](examples/complex.yaml)
```yaml
uses: acdl/pipelines/deploy.yaml@v1.8
module: kms-key
environment: dev
inputs:
description: "Production CMK with 90-day deletion window"
region: us-east-1
deletion_window_days: 90
```
## Versioning
`1.0.0` — interface MAJOR, behavior MINOR, lifecycle PATCH. MAJOR bumps
require a new registry entry (immutable publication); old entries enter
a 12-month deprecation window.
+7
View File
@@ -0,0 +1,7 @@
uses: acdl/pipelines/deploy.yaml@v1.8
module: kms-key
environment: dev
inputs:
description: "Production CMK with 90-day deletion window"
region: us-east-1
deletion_window_days: 90
+6
View File
@@ -0,0 +1,6 @@
uses: acdl/pipelines/deploy.yaml@v1.8
module: kms-key
environment: dev
inputs:
description: "Simple CMK for testing"
region: us-east-1
+33
View File
@@ -0,0 +1,33 @@
{
"version": "1.0.0",
"stack": {
"name": "kms-key",
"kind": "l1",
"depth": 1
},
"resources": [
{
"id": "kms-key",
"type": "aws:kms:key",
"module": "kms-key@1.0.0",
"inputs": {
"description": "ACDL per-stack CMK",
"region": "us-east-1",
"deletion_window_days": 30
},
"outputs": {
"kms_key_arn": {
"type": "arn"
},
"kms_key_id": {
"type": "string"
}
},
"nfrs": {
"enable_rotation": true,
"deletion_protection": true,
"encryption_enabled": true
}
}
]
}
+52
View File
@@ -0,0 +1,52 @@
{
"name": "kms-key",
"version": "1.0.0",
"kind": "l1",
"type": "aws:kms:key",
"description": "A customer-managed KMS key for per-stack encryption. Created with key rotation enabled. One key per L2 deployment (no shared keys).",
"inputs": {
"description": {
"type": "string",
"description": "Description of the KMS key.",
"required": true
},
"region": {
"type": "string",
"description": "AWS region the KMS key is created in.",
"required": true
},
"deletion_window_days": {
"type": "number",
"description": "Number of days before the key is deleted after deletion is requested (default 30).",
"required": false,
"default": 30
}
},
"outputs": {
"kms_key_arn": {
"type": "arn",
"description": "The ARN of the KMS key."
},
"kms_key_id": {
"type": "string",
"description": "The ID of the KMS key."
}
},
"nfrs": {
"enable_rotation": {
"type": "boolean",
"description": "Enable automatic key rotation.",
"default": true
},
"deletion_protection": {
"type": "boolean",
"description": "Prevent key destruction.",
"default": true
},
"encryption_enabled": {
"type": "boolean",
"description": "Encryption is always enabled for a KMS key.",
"default": true
}
}
}
+10
View File
@@ -54,6 +54,11 @@
"type": "string",
"description": "AWS region the RDS instance is created in.",
"required": true
},
"kms_key_arn": {
"type": "string",
"description": "ARN of the CMK for storage encryption; if absent, uses AWS-managed key.",
"required": false
}
},
"outputs": {
@@ -76,6 +81,11 @@
"type": "boolean",
"description": "Enable deletion protection (default true for prod).",
"default": true
},
"encryption_enabled": {
"type": "boolean",
"description": "Enable storage encryption.",
"default": true
}
}
}
+15
View File
@@ -14,6 +14,11 @@
"type": "string",
"description": "AWS region the bucket is created in.",
"required": true
},
"kms_key_arn": {
"type": "string",
"description": "ARN of the CMK for SSE-KMS; if absent, uses managed key.",
"required": false
}
},
"outputs": {
@@ -35,6 +40,16 @@
"type": "boolean",
"description": "Enable S3 versioning (default true).",
"default": true
},
"encryption_enabled": {
"type": "boolean",
"description": "Enable server-side encryption.",
"default": true
},
"sse_algorithm": {
"type": "string",
"description": "SSE algorithm.",
"default": "aws:kms"
}
}
}
+12 -1
View File
@@ -36,7 +36,18 @@
"description": "Comma-separated subnet ids."
}
},
"nfrs": {},
"nfrs": {
"encryption_enabled": {
"type": "boolean",
"description": "Enable KMS-encrypted VPC flow logs.",
"default": true
},
"flow_logs_encrypted": {
"type": "boolean",
"description": "Encrypt VPC flow logs with KMS.",
"default": true
}
},
"resources": [
{
"type": "aws:ec2:vpc",
+12 -1
View File
@@ -39,7 +39,18 @@
"description": "The WAF Web ACL ARN."
}
},
"nfrs": {},
"nfrs": {
"encryption_enabled": {
"type": "boolean",
"description": "Enable KMS-encrypted CloudWatch log group for WAF logs.",
"default": true
},
"logging_enabled": {
"type": "boolean",
"description": "Enable WAF logging.",
"default": true
}
},
"resources": [
{
"type": "aws:wafv2:webacl",