Skip to content

Commit

Permalink
fix(bootstrap): remove Security Hub finding KMS.2 (aws#24588)
Browse files Browse the repository at this point in the history
**NOTE:** This PR bumps the version of the bootstrap stack to `16`, but there is no need to update your bootstrap stacks, unless it is to get rid of the Security Hub finding; this change has no effect on the functionality of any CDK app deployed to the environment.

[Security Hub finding KMS.2](https://docs.aws.amazon.com/securityhub/latest/userguide/kms-controls.html#kms-2) says:

> The control fails if the policy is open enough to allow kms:Decrypt or kms:ReEncryptFrom actions on any arbitrary KMS key.
>
> [...]
>
> The control only checks KMS keys in the Resource element and doesn't take into account any conditionals in the Condition element of a policy.

This control is a "defense in depth" control. It does not mitigate any attack by itself, and there is no actual security impact from the current configuration of our policies. However, customers are anxious about the Security Hub findings reported on resources we create for them.

Therefore, we turn the `Resources: *` into a list of wildcard ARNs, one for each trusted account. This should satisify Security Hub without breaking the functionality of the bootstrap resources (as this statement is only used for cross-account CodePipeline deployments using CDK Pipelines).

The CloudFormation expression we use to turn a list of account IDs into a list of ARNs is quite crazy. To turn `['1111', '2222', '3333']` into `['arn:aws:kms:*:1111:*', 'arn:aws:kms:*:2222:*', 'arn:aws:kms:*:3333:*']` we do the following:

* Skip the entire statement if the list is empty
* Use the following equivalence if the list has at least one element (E1 cannot be expressed in CloudFormation but E2 can):

```
(E1)  xs.map(x => PREFIX + x + SUFFIX).join(SEP)

         <==> { assuming xs.length >= 1 }

(E2)  PREFIX + xs.join(SUFFIX + SEP + PREFIX) + SUFFIX
```

* Finally split the string on the separator to come up with an array of elements.

I would have used `${AWS::Region}` instead of allowing all regions, but `{ Fn::Join }` doesn't allow using intrinsics in its separator.

I tested the new template using a CDK Pipeline that deploys in-region, cross-region, cross-account and cross-account-cross-region.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
rix0rrr authored and homakk committed Mar 28, 2023
1 parent d8be2df commit b6fc6a5
Showing 1 changed file with 30 additions and 15 deletions.
45 changes: 30 additions & 15 deletions packages/aws-cdk/lib/api/bootstrap/bootstrap-template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -455,20 +455,35 @@ Resources:
StringNotEquals:
s3:ResourceAccount:
Ref: 'AWS::AccountId'
- Sid: PipelineCrossAccountArtifactsKey
# Use keys only for the purposes of reading encrypted files from S3.
Effect: Allow
Action:
- kms:Decrypt
- kms:DescribeKey
- kms:Encrypt
- kms:ReEncrypt*
- kms:GenerateDataKey*
Resource: "*"
Condition:
StringEquals:
kms:ViaService:
Fn::Sub: s3.${AWS::Region}.amazonaws.com
- Fn::If:
- HasTrustedAccounts
- Sid: PipelineCrossAccountArtifactsKey
# Use keys only for the purposes of reading encrypted files from S3.
Effect: Allow
Action:
- kms:Decrypt
- kms:DescribeKey
- kms:Encrypt
- kms:ReEncrypt*
- kms:GenerateDataKey*

# SecurityHub's rule KMS.2 complains if we put a '*' here, so instead we'll
# turn the list of trusted accountIds ['111', '222', ...] into a list of
# wildcard ARNS: ['arn:aws:kms:*:1111:*', 'arn:aws:kms:*:2222:*', ...].
Resource:
Fn::Split:
- "|"
- Fn::Sub:
- "arn:aws:kms:*:${JoinedAccounts}:*"
- JoinedAccounts:
Fn::Join:
- ":*|arn:aws:kms:*:"
- { Ref: TrustedAccounts }
Condition:
StringEquals:
kms:ViaService:
Fn::Sub: s3.${AWS::Region}.amazonaws.com
- { Ref: AWS::NoValue }
- Action: iam:PassRole
Resource:
Fn::Sub: "${CloudFormationExecutionRole.Arn}"
Expand Down Expand Up @@ -600,7 +615,7 @@ Resources:
Type: String
Name:
Fn::Sub: '/cdk-bootstrap/${Qualifier}/version'
Value: '15'
Value: '16'
Outputs:
BucketName:
Description: The name of the S3 bucket owned by the CDK toolkit stack
Expand Down

0 comments on commit b6fc6a5

Please sign in to comment.