Skip to content

Commit c3ed6e0

Browse files
feat(core): Additional enhancements (#669)
* feat(core): Additional enhancements - enable access logging on Rsyslog NLB - Enable DeletionProtection on ELB - Deny non https calls to S3 buckets * Updating installerBucket Policy to allow only secure requests * Fix adding subscription filter to loggroups - Using .map instead of regular for loop * Fix adding subscription filter for existing user by changing construct name - add additional back-off, retry conditions
1 parent 96b8f35 commit c3ed6e0

File tree

19 files changed

+336
-121
lines changed

19 files changed

+336
-121
lines changed

src/deployments/cdk/src/apps/phase-3.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as rsyslogDeployment from '../deployments/rsyslog';
66
import { ImportedVpc } from '../deployments/vpc';
77
import { VpcOutput } from '@aws-accelerator/common-outputs/src/vpc';
88
import { getStackJsonOutput } from '@aws-accelerator/common-outputs/src/stack-output';
9-
import { CentralBucketOutput, AccountBucketOutput } from '../deployments/defaults';
9+
import { CentralBucketOutput, AccountBucketOutput, AesBucketOutput } from '../deployments/defaults';
1010
import * as securityHub from '../deployments/security-hub';
1111
import * as macie from '../deployments/macie';
1212
import * as transitGateway from '../deployments/transit-gateway';
@@ -25,6 +25,12 @@ import * as awsConfig from '../deployments/config';
2525
*/
2626

2727
export async function deploy({ acceleratorConfig, accountStacks, accounts, context, outputs }: PhaseInput) {
28+
const aesLogArchiveBucket = AesBucketOutput.getBucket({
29+
accountStacks,
30+
config: acceleratorConfig,
31+
outputs,
32+
});
33+
2834
/**
2935
* Code to create Peering Connection Routes in all accounts
3036
*/
@@ -73,6 +79,7 @@ export async function deploy({ acceleratorConfig, accountStacks, accounts, conte
7379
accountStacks,
7480
config: acceleratorConfig,
7581
outputs,
82+
aesLogArchiveBucket,
7683
});
7784

7885
// Import all VPCs from all outputs
@@ -95,6 +102,7 @@ export async function deploy({ acceleratorConfig, accountStacks, accounts, conte
95102
vpcs: allVpcs,
96103
centralBucket,
97104
context,
105+
aesLogArchiveBucket,
98106
});
99107

100108
// Deploy Security Hub Step-2

src/deployments/cdk/src/deployments/alb/step-1.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,11 @@ export interface AlbStep1Props {
2727
accountStacks: AccountStacks;
2828
config: AcceleratorConfig;
2929
outputs: StackOutput[];
30+
aesLogArchiveBucket: s3.IBucket;
3031
}
3132

3233
export async function step1(props: AlbStep1Props) {
33-
const { accountStacks, config, outputs } = props;
34-
35-
const aesLogArchiveBucket = AesBucketOutput.getBucket({
36-
accountStacks,
37-
config,
38-
outputs,
39-
});
34+
const { accountStacks, config, outputs, aesLogArchiveBucket } = props;
4035

4136
const vpcConfigs = config.getVpcConfigs();
4237
for (const { ouKey, accountKey, albs: albConfigs } of config.getAlbConfigs()) {

src/deployments/cdk/src/deployments/defaults/step-1.ts

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,7 @@ function createCentralBucketCopy(props: DefaultsStep1Props) {
9292
encryptionKey,
9393
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
9494
removalPolicy: cdk.RemovalPolicy.RETAIN,
95-
});
96-
97-
// TODO: Remove and use fields directly when CDK enhanced s3.Bucket.
98-
(bucket.node.defaultChild as s3.CfnBucket).addPropertyOverride('OwnershipControls', {
99-
Rules: [
100-
{
101-
ObjectOwnership: 'BucketOwnerPreferred',
102-
},
103-
],
95+
objectOwnership: s3.ObjectOwnership.BUCKET_OWNER_PREFERRED,
10496
});
10597

10698
// Let the bucket name be generated by CloudFormation
@@ -137,6 +129,21 @@ function createCentralBucketCopy(props: DefaultsStep1Props) {
137129
}),
138130
);
139131

132+
// Allow only https requests
133+
bucket.addToResourcePolicy(
134+
new iam.PolicyStatement({
135+
actions: ['s3:*'],
136+
resources: [bucket.bucketArn, bucket.arnForObjects('*')],
137+
principals: anyAccountPrincipal,
138+
conditions: {
139+
Bool: {
140+
'aws:SecureTransport': 'false',
141+
},
142+
},
143+
effect: iam.Effect.DENY,
144+
}),
145+
);
146+
140147
new CfnCentralBucketOutput(masterAccountStack, 'CentralBucketOutput', {
141148
bucketArn: bucket.bucketArn,
142149
bucketName: bucket.bucketName,
@@ -310,15 +317,7 @@ function createAesLogBucket(props: DefaultsStep1Props) {
310317
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
311318
encryption: s3.BucketEncryption.S3_MANAGED,
312319
removalPolicy: cdk.RemovalPolicy.RETAIN,
313-
});
314-
315-
// TODO: Remove and use fields directly when CDK enhanced s3.Bucket.
316-
(logBucket.node.defaultChild as s3.CfnBucket).addPropertyOverride('OwnershipControls', {
317-
Rules: [
318-
{
319-
ObjectOwnership: 'BucketOwnerPreferred',
320-
},
321-
],
320+
objectOwnership: s3.ObjectOwnership.BUCKET_OWNER_PREFERRED,
322321
});
323322

324323
// Let the bucket name be generated by CloudFormation
@@ -354,6 +353,21 @@ function createAesLogBucket(props: DefaultsStep1Props) {
354353
}),
355354
);
356355

356+
// Allow only https requests
357+
logBucket.addToResourcePolicy(
358+
new iam.PolicyStatement({
359+
actions: ['s3:*'],
360+
resources: [logBucket.bucketArn, logBucket.arnForObjects('*')],
361+
principals: [new iam.AnyPrincipal()],
362+
conditions: {
363+
Bool: {
364+
'aws:SecureTransport': 'false',
365+
},
366+
},
367+
effect: iam.Effect.DENY,
368+
}),
369+
);
370+
357371
new CfnAesBucketOutput(logAccountStack, 'AesLogBucketOutput', {
358372
bucketArn: logBucket.bucketArn,
359373
bucketName: logBucket.bucketName,

src/deployments/cdk/src/deployments/defaults/step-2.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,21 @@ function createDefaultS3Buckets(props: DefaultsStep2Props) {
8282
}),
8383
);
8484

85+
// Allow only https requests
86+
bucket.addToResourcePolicy(
87+
new iam.PolicyStatement({
88+
actions: ['s3:*'],
89+
resources: [bucket.bucketArn, bucket.arnForObjects('*')],
90+
principals: [new iam.AnyPrincipal()],
91+
conditions: {
92+
Bool: {
93+
'aws:SecureTransport': 'false',
94+
},
95+
},
96+
effect: iam.Effect.DENY,
97+
}),
98+
);
99+
85100
buckets[accountKey] = bucket;
86101

87102
new CfnAccountBucketOutput(accountStack, 'DefaultBucketOutput', {

src/deployments/cdk/src/deployments/rsyslog/step-2.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { ImageIdOutputFinder } from '@aws-accelerator/common-outputs/src/ami-out
1717
import { IamRoleOutputFinder } from '@aws-accelerator/common-outputs/src/iam-role';
1818
import { Context } from '../../utils/context';
1919
import { CfnLoadBalancerOutput } from '../alb/outputs';
20+
import { AesBucketOutput } from '../defaults';
2021

2122
export interface RSysLogStep1Props {
2223
accountStacks: AccountStacks;
@@ -25,10 +26,11 @@ export interface RSysLogStep1Props {
2526
vpcs: Vpc[];
2627
centralBucket: s3.IBucket;
2728
context: Context;
29+
aesLogArchiveBucket: s3.IBucket;
2830
}
2931

3032
export async function step2(props: RSysLogStep1Props) {
31-
const { accountStacks, config, outputs, vpcs, centralBucket, context } = props;
33+
const { accountStacks, config, outputs, vpcs, centralBucket, context, aesLogArchiveBucket } = props;
3234

3335
for (const [accountKey, accountConfig] of config.getMandatoryAccountConfigs()) {
3436
const rsyslogConfig = accountConfig.deployments?.rsyslog;
@@ -54,7 +56,7 @@ export async function step2(props: RSysLogStep1Props) {
5456
}
5557

5658
const rsyslogTargetGroup = createTargetGroupForInstance(accountStack, 'RsyslogTG', vpc.id);
57-
createNlb(accountKey, rsyslogConfig, accountStack, vpc, rsyslogTargetGroup.ref);
59+
createNlb(accountKey, rsyslogConfig, accountStack, vpc, rsyslogTargetGroup.ref, aesLogArchiveBucket);
5860
createAsg(
5961
accountKey,
6062
rsyslogConfig,
@@ -74,6 +76,7 @@ export function createNlb(
7476
accountStack: AcceleratorStack,
7577
vpc: Vpc,
7678
targetGroupArn: string,
79+
aesLogArchiveBucket: s3.IBucket,
7780
) {
7881
const nlbSubnetIds: string[] = [];
7982
for (const subnetConfig of rsyslogConfig['web-subnets']) {
@@ -95,6 +98,7 @@ export function createNlb(
9598
scheme: 'internal',
9699
subnetIds: nlbSubnetIds,
97100
ipType: 'ipv4',
101+
aesLogArchiveBucket,
98102
});
99103

100104
// Add default listener

src/installer/cdk/src/index.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -325,16 +325,23 @@ async function main() {
325325
// Encryption is not necessary for this pipeline so we create a custom unencrypted bucket
326326
const installerArtifactsBucket = new s3.Bucket(stack, 'ArtifactsBucket', {
327327
removalPolicy: cdk.RemovalPolicy.DESTROY,
328+
objectOwnership: s3.ObjectOwnership.BUCKET_OWNER_PREFERRED,
328329
});
329330

330-
// TODO: Remove and use fields directly when CDK enhanced s3.Bucket.
331-
(installerArtifactsBucket.node.defaultChild as s3.CfnBucket).addPropertyOverride('OwnershipControls', {
332-
Rules: [
333-
{
334-
ObjectOwnership: 'BucketOwnerPreferred',
331+
// Allow only https requests
332+
installerArtifactsBucket.addToResourcePolicy(
333+
new iam.PolicyStatement({
334+
actions: ['s3:*'],
335+
resources: [installerArtifactsBucket.bucketArn, installerArtifactsBucket.arnForObjects('*')],
336+
principals: [new iam.AnyPrincipal()],
337+
conditions: {
338+
Bool: {
339+
'aws:SecureTransport': 'false',
340+
},
335341
},
336-
],
337-
});
342+
effect: iam.Effect.DENY,
343+
}),
344+
);
338345

339346
new codepipeline.Pipeline(stack, 'Pipeline', {
340347
role: installerPipelineRole,

src/lib/cdk-constructs/package.json

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,55 +17,56 @@
1717
"@types/hash-sum": "1.0.0",
1818
"@types/jest": "25.2.1",
1919
"@types/node": "12.12.6",
20-
"babel-jest": "25.2.0",
21-
"jest": "25.2.4",
22-
"prettier": "2.2.0",
23-
"ts-jest": "25.3.0",
24-
"ts-node": "8.8.1",
25-
"eslint": "7.10.0",
2620
"@typescript-eslint/eslint-plugin": "4.4.0",
2721
"@typescript-eslint/parser": "4.4.0",
22+
"babel-jest": "25.2.0",
23+
"eslint": "7.10.0",
2824
"eslint-config-prettier": "6.12.0",
2925
"eslint-config-standard": "14.1.1",
30-
"eslint-plugin-standard": "4.0.1",
3126
"eslint-plugin-deprecation": "1.1.0",
32-
"eslint-plugin-promise": "4.2.1",
3327
"eslint-plugin-import": "2.22.1",
34-
"eslint-plugin-node": "11.1.0",
3528
"eslint-plugin-jsdoc": "30.6.4",
29+
"eslint-plugin-node": "11.1.0",
3630
"eslint-plugin-prefer-arrow": "1.2.2",
31+
"eslint-plugin-promise": "4.2.1",
3732
"eslint-plugin-react": "7.21.3",
33+
"eslint-plugin-standard": "4.0.1",
3834
"eslint-plugin-unicorn": "22.0.0",
35+
"jest": "25.2.4",
36+
"prettier": "2.2.0",
37+
"ts-jest": "25.3.0",
38+
"ts-node": "8.8.1",
3939
"typescript": "3.8.3"
4040
},
4141
"dependencies": {
42+
"@aws-accelerator/custom-resource-cfn-sleep": "workspace:^0.0.1",
43+
"@aws-accelerator/custom-resource-ec2-keypair": "workspace:^0.0.1",
44+
"@aws-accelerator/custom-resource-elb-deletion-protection": "workspace:^0.0.1",
45+
"@aws-accelerator/custom-resource-r53-dns-endpoint-ips": "workspace:^0.0.1",
46+
"@aws-accelerator/custom-resource-s3-put-bucket-replication": "workspace:^0.0.1",
47+
"@aws-accelerator/custom-resource-s3-template": "workspace:^0.0.1",
48+
"@aws-accelerator/custom-resource-security-hub-accept-invites": "workspace:^0.0.1",
49+
"@aws-accelerator/custom-resource-security-hub-enable": "workspace:^0.0.1",
50+
"@aws-accelerator/custom-resource-security-hub-send-invites": "workspace:^0.0.1",
4251
"@aws-cdk/aws-autoscaling": "1.85.0",
4352
"@aws-cdk/aws-budgets": "1.85.0",
4453
"@aws-cdk/aws-cloudformation": "1.85.0",
54+
"@aws-cdk/aws-config": "1.85.0",
4555
"@aws-cdk/aws-ec2": "1.85.0",
4656
"@aws-cdk/aws-iam": "1.85.0",
4757
"@aws-cdk/aws-kms": "1.85.0",
4858
"@aws-cdk/aws-lambda": "1.85.0",
59+
"@aws-cdk/aws-route53resolver": "1.85.0",
4960
"@aws-cdk/aws-s3": "1.85.0",
5061
"@aws-cdk/aws-s3-assets": "1.85.0",
62+
"@aws-cdk/aws-securityhub": "1.85.0",
5163
"@aws-cdk/aws-ssm": "1.85.0",
5264
"@aws-cdk/core": "1.85.0",
53-
"@aws-cdk/aws-securityhub": "1.85.0",
54-
"@aws-cdk/aws-route53resolver": "1.85.0",
55-
"@aws-cdk/aws-config": "1.85.0",
56-
"@aws-accelerator/custom-resource-cfn-sleep": "workspace:^0.0.1",
57-
"@aws-accelerator/custom-resource-ec2-keypair": "workspace:^0.0.1",
58-
"@aws-accelerator/custom-resource-s3-template": "workspace:^0.0.1",
59-
"@aws-accelerator/custom-resource-security-hub-accept-invites": "workspace:^0.0.1",
60-
"@aws-accelerator/custom-resource-security-hub-enable": "workspace:^0.0.1",
61-
"@aws-accelerator/custom-resource-security-hub-send-invites": "workspace:^0.0.1",
62-
"@aws-accelerator/custom-resource-r53-dns-endpoint-ips": "workspace:^0.0.1",
63-
"@aws-accelerator/custom-resource-s3-put-bucket-replication": "workspace:^0.0.1",
65+
"aws-sdk": "2.787.0",
6466
"hash-sum": "2.0.0",
6567
"ip-num": "1.2.2",
6668
"pascal-case": "3.1.1",
67-
"tempy": "0.5.0",
68-
"aws-sdk": "2.787.0"
69+
"tempy": "0.5.0"
6970
},
7071
"jest": {
7172
"preset": "ts-jest",

src/lib/cdk-constructs/src/s3/bucket.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,11 @@ export class Bucket extends s3.Bucket {
3838
noncurrentVersionExpiration: props.versioned ? cdk.Duration.days(props.expirationInDays) : undefined,
3939
},
4040
],
41+
objectOwnership: s3.ObjectOwnership.BUCKET_OWNER_PREFERRED,
4142
});
4243

4344
// Get the underlying resource
4445
this.resource = this.node.findChild('Resource') as s3.CfnBucket;
45-
46-
// TODO: Remove and use fields directly when CDK enhanced s3.Bucket.
47-
this.resource.addPropertyOverride('OwnershipControls', {
48-
Rules: [
49-
{
50-
ObjectOwnership: 'BucketOwnerPreferred',
51-
},
52-
],
53-
});
5446
}
5547

5648
replicateFrom(principals: iam.IPrincipal[], organizationId: string, prefix: string) {

src/lib/cdk-constructs/src/security-hub/security-hub.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import * as cdk from '@aws-cdk/core';
2-
import { CfnHub } from '@aws-cdk/aws-securityhub';
32
import { SecurityHubEnable } from '@aws-accelerator/custom-resource-security-hub-enable';
43
import { SecurityHubSendInvites } from '@aws-accelerator/custom-resource-security-hub-send-invites';
54
import { SecurityHubAcceptInvites } from '@aws-accelerator/custom-resource-security-hub-accept-invites';

src/lib/cdk-constructs/src/vpc/alb.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as cdk from '@aws-cdk/core';
22
import * as s3 from '@aws-cdk/aws-s3';
33
import * as elb from '@aws-cdk/aws-elasticloadbalancingv2';
4+
import { ElbDeletionProtection } from '@aws-accelerator/custom-resource-elb-deletion-protection';
45

56
export interface ApplicationLoadBalancerProps extends cdk.StackProps {
67
albName: string;
@@ -26,6 +27,11 @@ export class ApplicationLoadBalancer extends cdk.Construct {
2627
subnets: subnetIds,
2728
securityGroups: securityGroupIds,
2829
});
30+
31+
new ElbDeletionProtection(this, 'AlbDeletionProtection', {
32+
loadBalancerArn: this.resource.ref,
33+
loadBalancerName: albName,
34+
});
2935
}
3036

3137
logToBucket(bucket: s3.IBucket) {

0 commit comments

Comments
 (0)