Skip to content

Commit

Permalink
feat: check origin access control usage for cloudfront with s3 origin (
Browse files Browse the repository at this point in the history
…#1794)

Fixes #1582

CDK now supports [S3 Origin Access Control L2 construct](aws/aws-cdk#31254). Added a new rule to check if OAC is configured for CloudFront distributions using S3 as an origin.


* Bumped cdk version used in development
  * Added missing parameters in QuickSight tests accordingly
* Applied the existing OAI rule only to CloudFront Streaming distributions (CloudFront distributions will not be non-compliant if OAI is not configured any more)
* Added a new rule checking OAC usage. Included the rule to AWS Solutions packs as `AwsSolutions-CFR7`
  • Loading branch information
clueleaf authored Oct 7, 2024
1 parent 1992dfa commit ce7f549
Show file tree
Hide file tree
Showing 12 changed files with 238 additions and 145 deletions.
2 changes: 1 addition & 1 deletion .projen/deps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion .projenrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const { awscdk, vscode } = require('projen');
const project = new awscdk.AwsCdkConstructLibrary({
author: 'Arun Donti',
authorAddress: '[email protected]',
cdkVersion: '2.116.0',
cdkVersion: '2.156.0',
defaultReleaseBranch: 'main',
majorVersion: 2,
npmDistTag: 'latest',
Expand Down Expand Up @@ -87,5 +87,8 @@ new vscode.DevContainer(project, {
});
project.package.addField('resolutions', {
'jsii-rosetta': '~5.0.7',
'@babel/types': '7.25.7',
'@types/babel__traverse': '7.18.2',
'@types/prettier': '2.6.0',
});
project.synth();
9 changes: 5 additions & 4 deletions RULES.md

Large diffs are not rendered by default.

11 changes: 7 additions & 4 deletions package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion src/packs/aws-solutions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
CloudFrontDistributionGeoRestrictions,
CloudFrontDistributionHttpsViewerNoOutdatedSSL,
CloudFrontDistributionNoOutdatedSSL,
CloudFrontDistributionS3OriginAccessControl,
CloudFrontDistributionS3OriginAccessIdentity,
CloudFrontDistributionWAFIntegration,
} from '../rules/cloudfront';
Expand Down Expand Up @@ -858,13 +859,22 @@ export class AwsSolutionsChecks extends NagPack {
});
this.applyRule({
ruleSuffixOverride: 'CFR6',
info: 'The CloudFront distribution does not use an origin access identity with an S3 origin.',
info: 'The CloudFront Streaming distribution does not use an origin access identity with an S3 origin.',
explanation:
'Origin access identities help with security by restricting any direct access to objects through S3 URLs.',
level: NagMessageLevel.ERROR,
rule: CloudFrontDistributionS3OriginAccessIdentity,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'CFR7',
info: 'The CloudFront distribution does not use an origin access control with an S3 origin.',
explanation:
'Origin access controls help with security by restricting any direct access to objects through S3 URLs.',
level: NagMessageLevel.ERROR,
rule: CloudFrontDistributionS3OriginAccessControl,
node: node,
});
this.applyRule({
ruleSuffixOverride: 'APIG1',
info: 'The API does not have access logging enabled.',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
import { parse } from 'path';
import { CfnResource, Stack } from 'aws-cdk-lib';
import { CfnDistribution } from 'aws-cdk-lib/aws-cloudfront';
import { CfnBucket } from 'aws-cdk-lib/aws-s3';
import { NagRuleCompliance, NagRules } from '../../nag-rules';

/**
* CloudFront distributions use an origin access control for S3 origins
* @param node the CfnResource to check
*/
export default Object.defineProperty(
(node: CfnResource): NagRuleCompliance => {
if (node instanceof CfnDistribution) {
const distributionConfig = Stack.of(node).resolve(
node.distributionConfig
);
if (distributionConfig.origins != undefined) {
const origins = Stack.of(node).resolve(distributionConfig.origins);
for (const origin of origins) {
const resolvedOrigin = Stack.of(node).resolve(origin);
const resolvedDomainName = Stack.of(node).resolve(
resolvedOrigin.domainName
);
const originLogicalId = NagRules.resolveResourceFromInstrinsic(
node,
resolvedDomainName
);
for (const child of Stack.of(node).node.findAll()) {
if (child instanceof CfnBucket) {
const childLogicalId = NagRules.resolveResourceFromInstrinsic(
child,
child.ref
);
if (originLogicalId === childLogicalId) {
const resolvedAccessControlId = Stack.of(node).resolve(
resolvedOrigin.originAccessControlId
);
const originAccessControlId =
NagRules.resolveResourceFromInstrinsic(
node,
resolvedAccessControlId
);
if (originAccessControlId == undefined) {
return NagRuleCompliance.NON_COMPLIANT;
}
if (originAccessControlId.replace(/\s/g, '').length == 0) {
return NagRuleCompliance.NON_COMPLIANT;
}
}
}
}
}
}
return NagRuleCompliance.COMPLIANT;
}
return NagRuleCompliance.NOT_APPLICABLE;
},
'name',
{ value: parse(__filename).name }
);
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,17 @@ SPDX-License-Identifier: Apache-2.0
*/
import { parse } from 'path';
import { CfnResource, Stack } from 'aws-cdk-lib';
import {
CfnDistribution,
CfnStreamingDistribution,
} from 'aws-cdk-lib/aws-cloudfront';
import { CfnStreamingDistribution } from 'aws-cdk-lib/aws-cloudfront';
import { NagRuleCompliance } from '../../nag-rules';

/**
* CloudFront distributions use an origin access identity for S3 origins
* CloudFront Streaming distributions use an origin access identity for S3 origins
* Only applying to CloudFront Streaming distributions because CloudFront distributions should use origin access control instead
* @param node the CfnResource to check
*/
export default Object.defineProperty(
(node: CfnResource): NagRuleCompliance => {
if (node instanceof CfnDistribution) {
const distributionConfig = Stack.of(node).resolve(
node.distributionConfig
);
if (distributionConfig.origins != undefined) {
const origins = Stack.of(node).resolve(distributionConfig.origins);
for (const origin of origins) {
const resolvedOrigin = Stack.of(node).resolve(origin);
const resolvedDomainName = Stack.of(node).resolve(
resolvedOrigin.domainName
);
const s3Regex =
/^.+\.s3(?:-website)?(?:\..+)?(?:(?:\.amazonaws\.com(?:\.cn)?)|(?:\.c2s\.ic\.gov)|(?:\.sc2s\.sgov\.gov))$/;
if (s3Regex.test(resolvedDomainName)) {
if (resolvedOrigin.s3OriginConfig == undefined) {
return NagRuleCompliance.NON_COMPLIANT;
}
const resolvedConfig = Stack.of(node).resolve(
resolvedOrigin.s3OriginConfig
);
if (
resolvedConfig.originAccessIdentity == undefined ||
resolvedConfig.originAccessIdentity.replace(/\s/g, '').length == 0
) {
return NagRuleCompliance.NON_COMPLIANT;
}
}
}
}
return NagRuleCompliance.COMPLIANT;
} else if (node instanceof CfnStreamingDistribution) {
if (node instanceof CfnStreamingDistribution) {
const distributionConfig = Stack.of(node).resolve(
node.streamingDistributionConfig
);
Expand Down
1 change: 1 addition & 0 deletions src/rules/cloudfront/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ export { default as CloudFrontDistributionHttpsViewerNoOutdatedSSL } from './Clo
export { default as CloudFrontDistributionNoOutdatedSSL } from './CloudFrontDistributionNoOutdatedSSL';
export { default as CloudFrontDistributionS3OriginAccessIdentity } from './CloudFrontDistributionS3OriginAccessIdentity';
export { default as CloudFrontDistributionWAFIntegration } from './CloudFrontDistributionWAFIntegration';
export { default as CloudFrontDistributionS3OriginAccessControl } from './CloudFrontDistributionS3OriginAccessControl';
1 change: 1 addition & 0 deletions test/Packs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ describe('Check NagPack Details', () => {
'AwsSolutions-CFR4',
'AwsSolutions-CFR5',
'AwsSolutions-CFR6',
'AwsSolutions-CFR7',
'AwsSolutions-COG1',
'AwsSolutions-COG3',
'AwsSolutions-COG4',
Expand Down
Loading

0 comments on commit ce7f549

Please sign in to comment.