in security_hub_correlation_cdk/lib/securityhub-correlation-blog-stack.ts [12:323]
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// DynamoDB Table for SecurityHub Findings
const DynamoDBencryptionKey = new Key(this, 'DynamoDBencryptionKey', {
enableKeyRotation: true
});
const dynamoTable = new Table(this, 'items', {
partitionKey: {
name: 'Id',
type: AttributeType.STRING
},
sortKey: {
name: 'ResourceId',
type: AttributeType.STRING
},
stream: StreamViewType.NEW_IMAGE,
encryption: TableEncryption.CUSTOMER_MANAGED,
encryptionKey: DynamoDBencryptionKey,
timeToLiveAttribute: 'ExpDate',
billingMode: BillingMode.PAY_PER_REQUEST,
pointInTimeRecovery: true,
tableName: 'security-hub-correlation-table',
// The default removal policy is RETAIN, which means that cdk destroy will not attempt to delete
// the new table, and it will remain in your account until manually deleted. By setting the policy to
// DESTROY, cdk destroy will delete the table (even if it has data in it)
removalPolicy: cdk.RemovalPolicy.DESTROY, // NOT recommended for production code
});
const dynamodb_GSI_name = 'ddb_sh_index'
dynamoTable.addGlobalSecondaryIndex({
indexName: dynamodb_GSI_name,
partitionKey: {
name: 'ResourceId',
type: AttributeType.STRING
},
sortKey: {
name: 'Types',
type: AttributeType.STRING
},
});
// Custom Security Hub Lambda Function Resources
const lambdaCreateSHFindingRole = new iam.Role(this, 'lambdaCreateSHFindingRole', {
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
roleName: "CustomSHFindingAutomationRole",
managedPolicies: [
iam.ManagedPolicy.fromManagedPolicyArn(this, 'lambdaCustomSHLogExecutionPolicy', 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole')
]
});
const CreateCustomSHFindingFunction = new Function(this, 'CreateCustomSHFindingFunction', {
runtime: Runtime.PYTHON_3_8,
code: Code.fromAsset(join(__dirname, "../lambdas/create_sh_finding/")),
handler: 'create_sh_finding.lambda_handler',
description: 'Create AWS Security Hub finding from DynamoDB match.',
timeout: cdk.Duration.seconds(300),
role: lambdaCreateSHFindingRole,
reservedConcurrentExecutions: 100,
environment: {
DYNAMODB_TABLE: dynamoTable.tableName,
DYNAMODB_GSI: dynamodb_GSI_name
},
});
const lambdaCreateSHFindingPolicyDoc = new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
sid: "CreateSHFinding",
effect: iam.Effect.ALLOW,
actions: [
"securityhub:BatchImportFindings"
],
resources: [
'*'
]
}),
],
});
const lambdaCreateSHFindingManagedPolicy = new iam.ManagedPolicy(this, 'lambdaCreateSHFindingManagedPolicy', {
description: '',
document:lambdaCreateSHFindingPolicyDoc,
managedPolicyName: 'lambdaCreateSHFindingManagedPolicy',
roles: [lambdaCreateSHFindingRole]
});
const CreateCustomSHFindingFunction_target = new LambdaFunction(CreateCustomSHFindingFunction)
dynamoTable.grantReadWriteData(CreateCustomSHFindingFunction);
// DynamoDB Lambda Function Resources
const lambdaCreateDDBEntry = new iam.Role(this, 'lambdaCreateDDBEntry', {
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
roleName: "Dynamo_SH_Entry",
managedPolicies: [
iam.ManagedPolicy.fromManagedPolicyArn(this, 'lambdaSHExecutionPolicy', 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole')
]
});
const CreateDynamoDBSHEntry = new Function(this, 'CreateDDBSHentry', {
runtime: Runtime.PYTHON_3_8,
code: Code.fromAsset(join(__dirname, "../lambdas/create_ddb_sh_entry/")),
handler: 'create_ddb_sh_entry.lambda_handler',
description: 'Create Security Hub Findings entry in DynamoDB.',
timeout: cdk.Duration.seconds(300),
role: lambdaCreateDDBEntry,
reservedConcurrentExecutions: 100,
environment: {
DYNAMODB_TABLE: dynamoTable.tableName,
// DynamoDB Entry Time to Live (TTL) in days
DYNAMODB_TTL: '30'
},
});
const lambdaCreateDDBEntryPolicyDoc = new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
sid: "DynamoDBTableAccess",
effect: iam.Effect.ALLOW,
actions: [
"dynamodb:BatchGetItem",
"dynamodb:BatchWriteItem",
"dynamodb:ConditionCheckItem",
"dynamodb:PutItem",
"dynamodb:DescribeTable",
"dynamodb:DeleteItem",
"dynamodb:GetItem",
"dynamodb:Scan",
"dynamodb:Query",
"dynamodb:UpdateItem"
],
resources: [
dynamoTable.tableArn
]
}),
new iam.PolicyStatement({
sid: "DynamoDBKMSKeyUse",
effect: iam.Effect.ALLOW,
actions: [
"kms:Encrypt",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
resources: [
DynamoDBencryptionKey.keyArn
]
}),
],
});
const lambdaCreateDDBEntryManagedPolicy = new iam.ManagedPolicy(this, 'lambdaCreateDDBEntryManagedPolicy', {
description: '',
document:lambdaCreateDDBEntryPolicyDoc,
managedPolicyName: 'lambdaAddDDBManagedPolicy',
roles: [lambdaCreateDDBEntry]
});
const CreateDynamoDBSHEntry_target = new LambdaFunction(CreateDynamoDBSHEntry)
// DynamoDB Event Stream for new DynamoDB entries to check Security Hub Correlation
CreateCustomSHFindingFunction.addEventSource(new DynamoEventSource(dynamoTable, {
startingPosition: StartingPosition.LATEST,
}));
// CloudWatch EventBridge rule for GuardDuty findings to DynamoDB
const RespondSecurityHubEvent_GuardDuty = new Rule(this, 'RespondSecurityHubEvent_GuardDuty', {
description: 'Creates a DynamoDB entry for specific GuardDuty Finding being generated.',
enabled: true,
eventPattern: {
"source": [
"aws.securityhub"
],
"detailType": [
"Security Hub Findings - Imported"
],
"detail": {
"findings": {
"ProductName":[
"GuardDuty"
],
"Types":[
{ "prefix": "TTPs/Exfiltration:S3-"},
{ "prefix": "Unusual Behaviors/VM/Behavior:EC2-NetworkPortUnusual"},
{ "prefix": "TTPs/Command and Control/Backdoor:EC2-"},
{ "prefix": "TTPs/Initial Access/UnauthorizedAccess:EC2-SSHBruteForce"},
{ "prefix": "TTPs/Initial Access/UnauthorizedAccess:EC2-RDPBruteForce"},
],
"Workflow": {
"Status": [
"NEW"
]
},
"RecordState": [
"ACTIVE"
],
}
}
},
ruleName: 'Create-Dynamo-Entry-from-Security-Hub-GuardDuty',
targets: [CreateDynamoDBSHEntry_target]
}
);
// CloudWatch EventBridge rule for Security Hub findings to DynamoDB
const RespondSecurityHubEvent_SecurityHub = new Rule(this, 'RespondSecurityHubEvent_SecurityHub', {
description: 'Creates a DynamoDB entry for specific Security Hub Finding being generated.',
enabled: true,
eventPattern: {
"source": [
"aws.securityhub"
],
"detailType": [
"Security Hub Findings - Imported"
],
"detail": {
"findings": {
"ProductName":[
"Security Hub"
],
//"Title":[
// { "prefix": "EC2.18 Security groups should only allow unrestricted incoming traffic for authorized ports"},
// { "prefix": "EC2.9"}
//],
"ProductFields": {
"ControlId": [
"EC2.18",
"EC2.9"
]
},
"Workflow": {
"Status": [
"NEW"
]
},
"RecordState": [
"ACTIVE"
],
}
}
},
ruleName: 'Create-Dynamo-Entry-Security-Hub',
targets: [CreateDynamoDBSHEntry_target]
}
);
// CloudWatch EventBridge rule for Macie findings to DynamoDB
const RespondSecurityHubEvent_Macie = new Rule(this, 'RespondSecurityHubEvent_Macie', {
description: 'Creates a DynamoDB entry for specific Macie Finding being generated.',
enabled: true,
eventPattern: {
"source": [
"aws.securityhub"
],
"detailType": [
"Security Hub Findings - Imported"
],
"detail": {
"findings": {
"ProductName":[
"Macie"
],
"Types":[
{ "prefix": "Sensitive Data Identifications/PII/SensitiveData:"}
],
"WorkflowState": [
"NEW"
],
"RecordState": [
"ACTIVE"
],
}
}
},
ruleName: 'Create-Dynamo-Entry-from-Security-Hub-Macie',
targets: [CreateDynamoDBSHEntry_target]
}
);
// CloudWatch EventBridge rule for Inspector findings to DynamoDB
const RespondSecurityHubEvent_Inspector = new Rule(this, 'RespondSecurityHubEvent_Inspector', {
description: 'Creates a DynamoDB entry for specific Inspector Finding being generated.',
enabled: true,
eventPattern: {
"source": [
"aws.securityhub"
],
"detailType": [
"Security Hub Findings - Imported"
],
"detail": {
"findings": {
"ProductName":[
"Inspector"
],
"Severity": {
"Label": ["CRITICAL"]
},
"WorkflowState": [
"NEW"
],
"RecordState": [
"ACTIVE"
],
}
}
},
ruleName: 'Create-Dynamo-Entry-from-Security-Hub-Inspector',
targets: [CreateDynamoDBSHEntry_target]
}
);
}