😐
【Terraform】AWS Security HubのCIS用ログメトリクスフィルター・アラーム
概要
AWS Security Hub がサポートしている CIS AWS Foundations Benchmark のコントロールの項目 3.1 ~ 3.14 で必要なログメトリクスフィルターとアラームについての Terraform のコードです。
それらのリソースは、量が多くて作成するのが面倒かなと思いきや、名前とフィルターパターンが異なるだけだったので、for_each
で簡単に作成できます。
環境
Terraform 1.2.5
Terraform AWS Provider 4.22.0
前提
以下のリソースはすでに存在している想定です。
- CloudTrail ログの出力先の CloudWatch ロググループ
- CIS アラームを受信する SNS トピック
コード
local.tf
locals {
alarms = {
"CIS-3.1-UnauthorizedAPICalls" = "{($.errorCode=\"*UnauthorizedOperation\") || ($.errorCode=\"AccessDenied*\")}"
"CIS-3.2-ConsoleSigninWithoutMFA" = "{($.eventName = \"ConsoleLogin\") && ($.additionalEventData.MFAUsed != \"Yes\") && ($.userIdentity.type = \"IAMUser\") && ($.responseElements.ConsoleLogin = \"Success\") }"
"CIS-3.3-RootAccountUsage" = "{$.userIdentity.type=\"Root\" && $.userIdentity.invokedBy NOT EXISTS && $.eventType !=\"AwsServiceEvent\"}"
"CIS-3.4-IAMPolicyChanges" = "{($.eventName=DeleteGroupPolicy) || ($.eventName=DeleteRolePolicy) || ($.eventName=DeleteUserPolicy) || ($.eventName=PutGroupPolicy) || ($.eventName=PutRolePolicy) || ($.eventName=PutUserPolicy) || ($.eventName=CreatePolicy) || ($.eventName=DeletePolicy) || ($.eventName=CreatePolicyVersion) || ($.eventName=DeletePolicyVersion) || ($.eventName=AttachRolePolicy) || ($.eventName=DetachRolePolicy) || ($.eventName=AttachUserPolicy) || ($.eventName=DetachUserPolicy) || ($.eventName=AttachGroupPolicy) || ($.eventName=DetachGroupPolicy)}"
"CIS-3.5-CloudTrailChanges" = "{($.eventName=CreateTrail) || ($.eventName=UpdateTrail) || ($.eventName=DeleteTrail) || ($.eventName=StartLogging) || ($.eventName=StopLogging)}"
"CIS-3.6-ConsoleAuthenticationFailure" = "{($.eventName=ConsoleLogin) && ($.errorMessage=\"Failed authentication\")}"
"CIS-3.7-DisableOrDeleteCMK" = "{($.eventSource=kms.amazonaws.com) && (($.eventName=DisableKey) || ($.eventName=ScheduleKeyDeletion))}"
"CIS-3.8-S3BucketPolicyChanges" = "{($.eventSource=s3.amazonaws.com) && (($.eventName=PutBucketAcl) || ($.eventName=PutBucketPolicy) || ($.eventName=PutBucketCors) || ($.eventName=PutBucketLifecycle) || ($.eventName=PutBucketReplication) || ($.eventName=DeleteBucketPolicy) || ($.eventName=DeleteBucketCors) || ($.eventName=DeleteBucketLifecycle) || ($.eventName=DeleteBucketReplication))}"
"CIS-3.9-AWSConfigChanges" = "{($.eventSource=config.amazonaws.com) && (($.eventName=StopConfigurationRecorder) || ($.eventName=DeleteDeliveryChannel) || ($.eventName=PutDeliveryChannel) || ($.eventName=PutConfigurationRecorder))}"
"CIS-3.10-SecurityGroupChanges" = "{($.eventName=AuthorizeSecurityGroupIngress) || ($.eventName=AuthorizeSecurityGroupEgress) || ($.eventName=RevokeSecurityGroupIngress) || ($.eventName=RevokeSecurityGroupEgress) || ($.eventName=CreateSecurityGroup) || ($.eventName=DeleteSecurityGroup)}"
"CIS-3.11-NetworkACLChanges" = "{($.eventName=CreateNetworkAcl) || ($.eventName=CreateNetworkAclEntry) || ($.eventName=DeleteNetworkAcl) || ($.eventName=DeleteNetworkAclEntry) || ($.eventName=ReplaceNetworkAclEntry) || ($.eventName=ReplaceNetworkAclAssociation)}"
"CIS-3.12-NetworkGatewayChanges" = "{($.eventName=CreateCustomerGateway) || ($.eventName=DeleteCustomerGateway) || ($.eventName=AttachInternetGateway) || ($.eventName=CreateInternetGateway) || ($.eventName=DeleteInternetGateway) || ($.eventName=DetachInternetGateway)}"
"CIS-3.13-RouteTableChanges" = "{($.eventName=CreateRoute) || ($.eventName=CreateRouteTable) || ($.eventName=ReplaceRoute) || ($.eventName=ReplaceRouteTableAssociation) || ($.eventName=DeleteRouteTable) || ($.eventName=DeleteRoute) || ($.eventName=DisassociateRouteTable)}"
"CIS-3.14-VPCChanges" = "{($.eventName=CreateVpc) || ($.eventName=DeleteVpc) || ($.eventName=ModifyVpcAttribute) || ($.eventName=AcceptVpcPeeringConnection) || ($.eventName=CreateVpcPeeringConnection) || ($.eventName=DeleteVpcPeeringConnection) || ($.eventName=RejectVpcPeeringConnection) || ($.eventName=AttachClassicLinkVpc) || ($.eventName=DetachClassicLinkVpc) || ($.eventName=DisableVpcClassicLink) || ($.eventName=EnableVpcClassicLink)}"
}
}
security_hub.tf
resource "aws_cloudwatch_log_metric_filter" "example" {
for_each = local.alarms
name = each.key
pattern = each.value
log_group_name = aws_cloudwatch_log_group.example.name
metric_transformation {
name = each.key
namespace = "LogMetrics"
value = "1"
}
}
resource "aws_cloudwatch_metric_alarm" "example" {
for_each = local.alarms
alarm_name = each.key
alarm_description = each.key
statistic = "Average"
comparison_operator = "GreaterThanOrEqualToThreshold"
threshold = 1
period = 300
evaluation_periods = 1
metric_name = each.key
namespace = "LogMetrics"
treat_missing_data = "notBreaching"
alarm_actions = [aws_sns_topic.example.arn]
ok_actions = [aws_sns_topic.example.arn]
}
おまけ
検証環境とステージング環境は同じ AWS アカウントで、本番環境だけは別の AWS アカウントである場合があるかもしれません。
その場合、検証環境とステージング環境は同じ AWS アカウントであるため、ログメトリクスフィルターとアラームが重複してしまいます。
重複して作成されないようにするには、以下のとおりfor_each
の部分をいじって、if
文で検証環境か本番環境の場合にのみリソースが作成されるようにします。[1]
security_hub.tf
resource "aws_cloudwatch_log_metric_filter" "example" {
for_each = { for key, value in local.alarms : key => value if var.common["env"] == "development" || var.common["env"] == "production" }
name = each.key
pattern = each.value
log_group_name = aws_cloudwatch_log_group.example.name
metric_transformation {
name = each.key
namespace = "LogMetrics"
value = "1"
}
}
resource "aws_cloudwatch_metric_alarm" "example" {
for_each = { for key, value in local.alarms : key => value if var.common["env"] == "development" || var.common["env"] == "production" }
alarm_name = each.key
alarm_description = each.key
statistic = "Average"
comparison_operator = "GreaterThanOrEqualToThreshold"
threshold = 1
period = 300
evaluation_periods = 1
metric_name = each.key
namespace = "LogMetrics"
treat_missing_data = "notBreaching"
alarm_actions = [aws_sns_topic.example.arn]
ok_actions = [aws_sns_topic.example.arn]
}
-
もっと良い書き方があるかもしれません。もしあればぜひ教えていただきたいです。 ↩︎
Discussion