diff --git a/examples/kitchen-sink/lib/index.ts b/examples/kitchen-sink/lib/index.ts index 740699d33..b16bb04e1 100644 --- a/examples/kitchen-sink/lib/index.ts +++ b/examples/kitchen-sink/lib/index.ts @@ -5,6 +5,8 @@ import { DatabaseCluster, + ClusterParameterGroup, + CfnDBCluster, } from '@aws-cdk/aws-docdb'; import { AmazonLinuxImage, @@ -121,6 +123,23 @@ export class KitchenSinkApp extends App { filesystem: fileSystem, }); + /** + * This option is part of enabling audit logging for DocumentDB; the other required part is the enabling of the CloudWatch exports below. + * + * Audit logs are a security best-practice. They record connection, data definition language (DDL), user management, + * and authorization events within the database, and are useful for post-incident auditing. That is, they can help you + * figure out what an unauthorized user, who gained access to your database, has done with that access. + * + * For more information about audit logging in DocumentDB, see: https://docs.aws.amazon.com/documentdb/latest/developerguide/event-auditing.html + */ + const parameterGroup = new ClusterParameterGroup(infrastructureStack, 'ParameterGroup', { + description: 'DocDB cluster parameter group with enabled audit logs', + family: 'docdb3.6', + parameters: { + audit_logs: 'enabled', + }, + }); + /* * DocumentDB database cluster for storing the render farm database. */ @@ -134,6 +153,7 @@ export class KitchenSinkApp extends App { InstanceClass.R4, InstanceSize.LARGE ), + parameterGroup, vpc, vpcSubnets: { onePerAz: true, @@ -151,6 +171,13 @@ export class KitchenSinkApp extends App { removalPolicy: RemovalPolicy.DESTROY }); + /** + * This option enable export audit logs to Amazon CloudWatch. + * This is second options that required for enable audit log. + */ + const cfnDB = database.node.findChild('Resource') as CfnDBCluster; + cfnDB.enableCloudwatchLogsExports = ['audit']; + /** * Bastion instance. * diff --git a/packages/aws-rfdk/lib/deadline/lib/repository.ts b/packages/aws-rfdk/lib/deadline/lib/repository.ts index 766b2b0b0..aff6bcf0d 100644 --- a/packages/aws-rfdk/lib/deadline/lib/repository.ts +++ b/packages/aws-rfdk/lib/deadline/lib/repository.ts @@ -15,6 +15,8 @@ import { import { CfnDBInstance, DatabaseCluster, + CfnDBCluster, + ClusterParameterGroup, } from '@aws-cdk/aws-docdb'; import { AmazonLinuxGeneration, @@ -296,6 +298,17 @@ export interface RepositoryProps { */ readonly removalPolicy?: RepositoryRemovalPolicies; + /** + * If this Repository is creating its own DocumentDB database, then this specifies if audit logging will be enabled + * + * Audit logs are a security best-practice. They record connection, data definition language (DDL), user management, + * and authorization events within the database, and are useful for post-incident auditing. That is, they can help you + * figure out what an unauthorized user, who gained access to your database, has done with that access. + * + * @default true + */ + readonly databaseAuditLogging?: boolean; + /** * If this Repository is creating its own Amazon DocumentDB database, then this specifies the number of * compute instances to be created. @@ -444,7 +457,26 @@ export class Repository extends Construct implements IRepository { if (props.database) { this.databaseConnection = props.database; + if (props.databaseAuditLogging !== undefined){ + this.node.addWarning(`The parameter databaseAuditLogging only has an effect when the Repository is creating its own database. + Please ensure that the Database provided is configured correctly.`); + } } else { + const databaseAuditLogging = props.databaseAuditLogging ?? true; + + /** + * This option is part of enabling audit logging for DocumentDB; the other required part is the enabling of the CloudWatch exports below. + * + * For more information about audit logging in DocumentDB, see: https://docs.aws.amazon.com/documentdb/latest/developerguide/event-auditing.html + */ + const parameterGroup = databaseAuditLogging ? new ClusterParameterGroup(this, 'ParameterGroup', { + description: 'DocDB cluster parameter group with enabled audit logs', + family: 'docdb3.6', + parameters: { + audit_logs: 'enabled', + }, + }) : undefined; + const instances = props.documentDbInstanceCount ?? Repository.DEFAULT_NUM_DOCDB_INSTANCES; const dbCluster = new DatabaseCluster(this, 'DocumentDatabase', { masterUser: {username: 'DocDBUser'}, @@ -457,8 +489,18 @@ export class Repository extends Construct implements IRepository { backup: { retention: props.backupOptions?.databaseRetention ?? Repository.DEFAULT_DATABASE_RETENTION_PERIOD, }, + parameterGroup, removalPolicy: props.removalPolicy?.database ?? RemovalPolicy.RETAIN, }); + + if (databaseAuditLogging) { + /** + * This option enable export audit logs to Amazon CloudWatch. + * This is second options that required for enable audit log. + */ + const cfnDB = dbCluster.node.findChild('Resource') as CfnDBCluster; + cfnDB.enableCloudwatchLogsExports = ['audit']; + } /* istanbul ignore next */ if (!dbCluster.secret) { /* istanbul ignore next */ diff --git a/packages/aws-rfdk/lib/deadline/test/repository.test.ts b/packages/aws-rfdk/lib/deadline/test/repository.test.ts index fdbb8c6f0..7418388cd 100644 --- a/packages/aws-rfdk/lib/deadline/test/repository.test.ts +++ b/packages/aws-rfdk/lib/deadline/test/repository.test.ts @@ -496,11 +496,90 @@ test('repository creates deadlineDatabase if none provided', () => { // THEN expectCDK(stack).to(haveResource('AWS::DocDB::DBCluster')); + expectCDK(stack).to(haveResource('AWS::DocDB::DBInstance')); + expectCDK(stack).to(haveResourceLike('AWS::DocDB::DBCluster', { + EnableCloudwatchLogsExports: [ 'audit' ], + }, ResourcePart.Properties)); + expectCDK(stack).to(haveResourceLike('AWS::DocDB::DBClusterParameterGroup', { + Parameters: { + audit_logs: 'enabled', + }, + }, ResourcePart.Properties)); expectCDK(stack).to(haveResourceLike('AWS::DocDB::DBInstance', { AutoMinorVersionUpgrade: true, })); }); +test('disabling Audit logging does not enable Cloudwatch audit logs', () => { + const testEFS = new EfsFileSystem(stack, 'TestEfsFileSystem', { + vpc, + }); + const testFS = new MountableEfs(stack, { + filesystem: testEFS, + }); + + // WHEN + new Repository(stack, 'repositoryInstaller', { + vpc, + fileSystem: testFS, + version: deadlineVersion, + databaseAuditLogging: false, + }); + + // THEN + expectCDK(stack).to(haveResource('AWS::DocDB::DBCluster')); + expectCDK(stack).notTo(haveResourceLike('AWS::DocDB::DBCluster', { + EnableCloudwatchLogsExports: [ 'audit' ], + }, ResourcePart.Properties)); + expectCDK(stack).notTo(haveResourceLike('AWS::DocDB::DBClusterParameterGroup', { + Parameters: { + audit_logs: 'enabled', + }, + }, ResourcePart.Properties)); +}); + +test('repository warns if databaseAuditLogging defined and database is specified', () => { + // GIVEN + const fsDatabase = new DatabaseCluster(stack, 'TestDbCluster', { + masterUser: { + username: 'master', + }, + instanceProps: { + instanceType: InstanceType.of( + InstanceClass.R4, + InstanceSize.LARGE, + ), + vpc, + vpcSubnets: { + onePerAz: true, + subnetType: SubnetType.PRIVATE, + }, + }, + }); + + // WHEN + const repo = new Repository(stack, 'repositoryInstaller', { + vpc, + version: deadlineVersion, + removalPolicy: { + filesystem: RemovalPolicy.DESTROY, + }, + database: DatabaseConnection.forDocDB({ database: fsDatabase, login: fsDatabase.secret! }), + databaseAuditLogging: true, + }); + + // THEN + expect(repo.node.metadata).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + type: 'aws:cdk:warning', + data: `The parameter databaseAuditLogging only has an effect when the Repository is creating its own database. + Please ensure that the Database provided is configured correctly.`, + }), + ]), + ); +}); + test('honors subnet specification', () => { // GIVEN const app = new App();