Skip to content

Commit fa60f6c

Browse files
authored
Merge pull request #533 from slashdevops/fix/kms-scoping-conditions
fix(template): scope Lambda role S3+KMS perms to the state object
2 parents bf36ac5 + 13587b4 commit fa60f6c

2 files changed

Lines changed: 39 additions & 3 deletions

File tree

docs/Whats-New.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,26 @@ This document tracks notable changes, new features, and bug fixes across release
44

55
## Unreleased
66

7+
### IAM least-privilege hardening for the state-file Lambda role
8+
9+
Tightens the Lambda execution role in `template.yaml` so it can only touch the single state object via the single intended path. No behavior change for normal operation; the role is now strictly scoped.
10+
11+
**S3 statement (split in two):**
12+
13+
* `S3ObjectPolicy``s3:GetObject*` / `s3:PutObject*` are now scoped to the exact state object (`arn:${Partition}:s3:::<bucket>/<BucketKey>`) instead of `<bucket>/*`.
14+
* `S3ListBucketPolicy``s3:ListBucket` stays on the bucket ARN (required by S3) but is now gated by a `s3:prefix` condition matching `BucketKey`.
15+
16+
**KMS statements (`KMSGetDataPolicy` and `KMSDecryptPolicy`):**
17+
18+
Both now carry the AWS-recommended SSE-KMS scoping conditions:
19+
20+
* `kms:ViaService = s3.<region>.amazonaws.com` — the CMK can only be used through requests S3 forwards on the role's behalf, not via direct `kms:Decrypt` / `kms:Encrypt` calls.
21+
* `kms:EncryptionContext:aws:s3:arn = arn:${Partition}:s3:::<bucket>/<BucketKey>` — the KMS grant only applies when S3 passes that exact object ARN as encryption context. S3 always populates this context for SSE-KMS objects, so legitimate reads/writes of the state file continue to work; any other object path is rejected by KMS.
22+
23+
These are belt-and-braces additions on top of the existing bucket policy, public-access block, `aws:SecureTransport` deny, and SSE-KMS-with-bucket-key configuration.
24+
25+
References: [AWS docs — kms:ViaService](https://docs.aws.amazon.com/kms/latest/developerguide/policy-conditions.html#conditions-kms-via-service), [AWS docs — SSE-KMS encryption context](https://docs.aws.amazon.com/AmazonS3/latest/userguide/specifying-kms-encryption.html).
26+
727
### OpenSSF Scorecard Hardening (Phase 4) — Fuzzing
828

929
Closes the **Fuzzing** Scorecard check by adding native Go fuzz targets and a CI workflow that exercises them.

template.yaml

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -342,24 +342,36 @@ Resources:
342342
- !Ref AWSGWSUserEmailSecret
343343
- !Ref AWSSCIMEndpointSecret
344344
- !Ref AWSSCIMAccessTokenSecret
345-
- Sid: S3Policy
345+
- Sid: S3ObjectPolicy
346346
Effect: Allow
347347
Action:
348348
- s3:GetObject
349349
- s3:GetObjectAcl
350350
- s3:GetObjectVersion
351351
- s3:PutObject
352352
- s3:PutObjectAcl
353+
Resource:
354+
- !Sub "arn:${AWS::Partition}:s3:::${BucketNamePrefix}-${AWS::AccountId}-${AWS::Region}/${BucketKey}"
355+
- Sid: S3ListBucketPolicy
356+
Effect: Allow
357+
Action:
353358
- s3:ListBucket
354359
Resource:
355-
- !Sub "arn:aws:s3:::${BucketNamePrefix}-${AWS::AccountId}-${AWS::Region}"
356-
- !Sub "arn:aws:s3:::${BucketNamePrefix}-${AWS::AccountId}-${AWS::Region}/*"
360+
- !Sub "arn:${AWS::Partition}:s3:::${BucketNamePrefix}-${AWS::AccountId}-${AWS::Region}"
361+
Condition:
362+
StringLike:
363+
s3:prefix:
364+
- !Ref BucketKey
357365
- Sid: KMSGetDataPolicy
358366
Effect: Allow
359367
Action:
360368
- kms:GenerateDataKeyPair
361369
Resource:
362370
- !GetAtt KMSKey.Arn
371+
Condition:
372+
StringEquals:
373+
kms:ViaService: !Sub "s3.${AWS::Region}.amazonaws.com"
374+
kms:EncryptionContext:aws:s3:arn: !Sub "arn:${AWS::Partition}:s3:::${BucketNamePrefix}-${AWS::AccountId}-${AWS::Region}/${BucketKey}"
363375
- Sid: KMSDecryptPolicy
364376
Effect: Allow
365377
Action:
@@ -368,6 +380,10 @@ Resources:
368380
- kms:GenerateDataKey
369381
Resource:
370382
- !GetAtt KMSKey.Arn
383+
Condition:
384+
StringEquals:
385+
kms:ViaService: !Sub "s3.${AWS::Region}.amazonaws.com"
386+
kms:EncryptionContext:aws:s3:arn: !Sub "arn:${AWS::Partition}:s3:::${BucketNamePrefix}-${AWS::AccountId}-${AWS::Region}/${BucketKey}"
371387

372388
AWSGWSServiceAccountFileSecret:
373389
Type: AWS::SecretsManager::Secret

0 commit comments

Comments
 (0)