AWS Security Hub Auto Remediation
In this guide, we configure Security Hub account to be able to take action on any other accounts in the Organization. This is all triggered from CloudWatch Event Pattern. The result looks like this.
Cloudwatch Event Pattern
Here's an example of the CW Event Pattern.
resource "aws_cloudwatch_event_rule" "example" {
name = "Example"
description = "Example"
event_pattern = <<EOF
{
"source" : [
"aws.securityhub"
],
"detail-type" : [
"Security Hub Findings - Imported"
],
"detail" : {
"findings" : {
"GeneratorId":["arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0/rule/4.3"],
"Compliance":{
"Status":["FAILED"]
}
}
}
}
EOF
}
Cloudwatch Event Targets
SNS
Pass in a Topic arn and enable this target.
resource "aws_cloudwatch_event_target" "cloudwatch_event_target_sns" {
count = local.sns_arn == null ? 0:1
## [\.\-_A-Za-z0-9]+, 64 characters max
rule = aws_cloudwatch_event_rule.cloudwatch_event_rule.name
## some unique assignment ID, random will be assigned since not provided
## target_id
## This is the ARN of the target regardless of the type
arn = local.sns_arn
}
Be sure the SNS topic permission include this
{
"Sid": "allow_from_events",
"Effect": "Allow",
"Principal": {
"Service": "events.amazonaws.com"
},
"Action": "SNS:Publish",
"Resource": "arn:aws:sns:us-east-1:99999999999:security-hub-topic"
}
Lambda
Point to Lambda Function arn and enable this target.
resource "aws_cloudwatch_event_target" "cloudwatch_event_target_lambda" {
count = local.lambda_arn == null ? 0:1
## [\.\-_A-Za-z0-9]+, 64 characters max
rule = aws_cloudwatch_event_rule.cloudwatch_event_rule.name
## some unique assignment ID, random will be assigned since not provided
## target_id
## This is the ARN of the target regardless of the type
arn = local.lambda_arn
}
Lambda Function
Create the lambda function here
resource "aws_lambda_function" "default" {
## ([a-zA-Z0-9-_]+)
function_name = local.rule_name
filename = local.lambda_zip_file
role = local.lambda_role_arn
source_code_hash = local.lambda_hash
description = "blah"
handler = "lambda_function.lambda_handler"
runtime = "python3.8"
timeout = 60
memory_size = 128
}
Lambda Role
Permission Policy
data "aws_iam_policy_document" "lambda_role_basic" {
## Create log group
statement {
sid = "SidLogGroup"
actions = ["logs:CreateLogGroup"]
resources = ["arn:aws:logs:*:${local.self_account_id}:*"]
}
## Create log stream
statement {
sid = "SidStream"
actions = [
"logs:PutLogEvents",
"logs:CreateLogStream"
]
resources = ["arn:aws:logs:*:${local.self_account_id}:log-group:/aws/lambda/${var.rule_name_prefix}*:*"]
}
## Allow this role to assume target roles in other accounts
statement {
sid = "SidAssumeAccountRoles"
actions = ["sts:AssumeRole"]
resources = ["arn:aws:iam::*:role/${var.target_role_name}"]
}
}
resource "aws_iam_policy" "lambda_role_basic" {
name = "${var.target_role_name}-policy"
policy = data.aws_iam_policy_document.lambda_role_basic.json
}
Assume Role Policy
data "aws_iam_policy_document" "lambda_role_assume_policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["lambda.amazonaws.com"]
}
}
}
Role
resource "aws_iam_role" "lambda_role" {
## Create a new role and start with Assume Role Policy to let it be used by Lambda ## This is the role that is added to Lambda above
name = var.lambda_role_name
assume_role_policy = data.aws_iam_policy_document.lambda_role_assume_policy.json
}
resource "aws_iam_role_policy_attachment" "lambda_role_basic" {
## Attach the permission policy defined above
role = aws_iam_role.lambda_role.name
policy_arn = aws_iam_policy.lambda_role_basic.arn
}
Target Account's Role
Assume Role Policy
data "aws_iam_policy_document" "security_hub_assume_role_policy" {
## Allow the role from Main Account to assume this role
statement {
actions = ["sts:AssumeRole"]
principals {
type = "AWS"
identifiers = ["arn:aws:iam::${local.sechub_account_id}:role/${local.security_hub_lambda_role_name}"]
}
}
}
Role
resource "aws_iam_role" "security_hub_role" {
## Create a new role, start with Assume Role Policy
name = local.security_hub_role_name
assume_role_policy = data.aws_iam_policy_document.security_hub_assume_role_policy.json
}
Permission Policies
- Give SecHub account permission to take action inside this account when something triggers in SecHub
- This role needs to be assumable from SecHub account
- This role needs to do whatever we need to do to remediate any SecHub findings
- Can only have up to 20 attached policies to a role (10 is default, 20 is when upped the limit to max), so be wise how you split this
- Each policy can only be up to 5000 characters (1500 is default, 5000 is when upped the limit to max), so be wise how you word your policy
- The roles names of assumed and assumer needs to match exactly
ReadOnly Policy
resource "aws_iam_role_policy_attachment" "security_hub_read_policy" {
## Use built-in policy for this
role = aws_iam_role.security_hub_role.id
policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}
Ability to Update Security Hub Finding Policy
data "aws_iam_policy_document" "security_hub_edit_sechub_policy_doc" {
statement {
actions = [
"securityhub:CreateActionTarget",
"securityhub:UpdateFindings",
"securityhub:BatchDisableStandards",
]
resources = ["arn:aws:securityhub:*:${local.account_id}:hub/default"]
}
}
resource "aws_iam_policy" "security_hub_edit_sechub_policy" {
name = "Security-hub-edit-sechub-policy"
path = "/"
policy = data.aws_iam_policy_document.security_hub_edit_sechub_policy_doc.json
}
resource "aws_iam_role_policy_attachment" "security_hub_edit_sechub_policy_attach" {
role = aws_iam_role.security_hub_role.id
policy_arn = aws_iam_policy.security_hub_edit_sechub_policy.arn
}
Ability to Edit IAM Policy
data "aws_iam_policy_document" "security_hub_edit_IAM_policy_doc" {
statement {
actions = [
"IAM:DeleteAccessKey",
"IAM:Detach*",
"IAM:DeleteUserPolicy"
]
resources = ["*"]
}
}
resource "aws_iam_policy" "security_hub_edit_IAM_policy" {
name = "Security-hub-edit-IAM-policy"
path = "/"
policy = data.aws_iam_policy_document.security_hub_edit_IAM_policy_doc.json
}
resource "aws_iam_role_policy_attachment" "security_hub_edit_IAM_policy_attach" {
role = aws_iam_role.security_hub_role.id
policy_arn = aws_iam_policy.security_hub_edit_IAM_policy.arn
}