Security HubのCloudWatch関連の設定をAWS CLIで一括投入する

## Security Hubのアラーム多いな
Security HubでPCI DSSやCIS AWS Foundations Benchmarkのセキュリティ標準を有効化した場合に、CloudWatchでCloudTrailを監視しエラー通知するよう求められる。
具体的には、CloudWatchログのメトリクスフィルターを作成し、アラートを設定すれば解決するが、設定数が14と多いのでAWS CLIで一括投入できるコマンドを作成した。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3303491/1b8d2662-168d-a776-7a31-c7a8faa0bb33.png)

## どんな構成が必要なのか
CloudWatch.1~14（14個）についてSecurity Hubにより監査され、CloudWatchを追加する必要がある。

例）RootAccountUsage
>  rootユーザーは、AWSアカウント内のすべてのサービスおよびリソースに無制限にアクセスできます。日常的なタスクでルートユーザーを使用しないことを強く推奨します。ルートユーザーの使用を最小限に抑え、アクセス管理に最小特権の原則を採用することで、偶発的な変更や高度な特権を持つ資格情報の意図しない開示のリスクを低減します。

このような具合で、セキュリティ的に危険な操作である項目（14個）をCloudWatchにてCloudTrailのログをフィルターし、通知することが求められる。

https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/cloudwatch-controls.html#cloudwatch-1

最終的にCloudWatchのMetricsとAlarmをAWS CLIで一括設定する。
SNSトピックについてはここでは設定せず、必要に応じて後からSNSトピックを各アラームに設定する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3303491/721e3953-be49-b1e5-c634-36ed51014be1.png)

## 前提条件
- CloudTrailログがCloudWatchの/aws/cloudtrailロググループに出力されている。
- SNSトピックへの通知は設定しない。（このままではエラーは解消されないので、追加でSNSトピックを作成しアラームを連携する必要がある。）

## 一括設定コマンド

#### CloudWatch Metric Filter

```bash
aws logs put-metric-filter \
        --log-group-name /aws/cloudtrail \
        --filter-name RootAccountUsage \
        --filter-pattern "{$.userIdentity.type=\"Root\" && $.userIdentity.invokedBy NOT EXISTS && $.eventType !=\"AwsServiceEvent\"}" \
        --metric-transformations "metricName=RootAccountUsage,metricNamespace=LogMetrics,metricValue=1"
aws logs put-metric-filter \
        --log-group-name /aws/cloudtrail \
        --filter-name UnauthorizedAPICalls \
        --filter-pattern "{($.errorCode=\"*UnauthorizedOperation\") || ($.errorCode=\"AccessDenied*\")}" \
        --metric-transformations "metricName=UnauthorizedAPICalls,metricNamespace=LogMetrics,metricValue=1"
aws logs put-metric-filter \
        --log-group-name /aws/cloudtrail \
        --filter-name ConsoleSigninWithoutMFA \
        --filter-pattern "{ ($.eventName = \"ConsoleLogin\") && ($.additionalEventData.MFAUsed != \"Yes\") && ($.userIdentity.type = \"IAMUser\") && ($.responseElements.ConsoleLogin = "Success") }" \
        --metric-transformations "metricName=ConsoleSigninWithoutMFA,metricNamespace=LogMetrics,metricValue=1"
aws logs put-metric-filter \
        --log-group-name /aws/cloudtrail \
        --filter-name IAMPolicyChanges \
        --filter-pattern "{($.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)}" \
        --metric-transformations "metricName=IAMPolicyChanges,metricNamespace=LogMetrics,metricValue=1"
aws logs put-metric-filter \
        --log-group-name /aws/cloudtrail \
        --filter-name CloudTrailChanges \
        --filter-pattern "{($.eventName=CreateTrail) || ($.eventName=UpdateTrail) || ($.eventName=DeleteTrail) || ($.eventName=StartLogging) || ($.eventName=StopLogging)}" \
        --metric-transformations "metricName=CloudTrailChanges,metricNamespace=LogMetrics,metricValue=1"
aws logs put-metric-filter \
        --log-group-name /aws/cloudtrail \
        --filter-name ConsoleAuthenticationFailure \
        --filter-pattern "{($.eventName=ConsoleLogin) && ($.errorMessage=\"Failed authentication\")}" \
        --metric-transformations "metricName=ConsoleAuthenticationFailure,metricNamespace=LogMetrics,metricValue=1"
aws logs put-metric-filter \
        --log-group-name /aws/cloudtrail \
        --filter-name DisableOrDeleteCMK \
        --filter-pattern "{($.eventSource=kms.amazonaws.com) && (($.eventName=DisableKey) || ($.eventName=ScheduleKeyDeletion))}" \
        --metric-transformations "metricName=DisableOrDeleteCMK,metricNamespace=LogMetrics,metricValue=1"
aws logs put-metric-filter \
        --log-group-name /aws/cloudtrail \
        --filter-name S3BucketPolicyChanges \
        --filter-pattern "{($.eventSource=s3.amazonaws.com) && (($.eventName=PutBucketAcl) || ($.eventName=PutBucketPolicy) || ($.eventName=PutBucketCors) || ($.eventName=PutBucketLifecycle) || ($.eventName=PutBucketReplication) || ($.eventName=DeleteBucketPolicy) || ($.eventName=DeleteBucketCors) || ($.eventName=DeleteBucketLifecycle) || ($.eventName=DeleteBucketReplication))}" \
        --metric-transformations "metricName=S3BucketPolicyChanges,metricNamespace=LogMetrics,metricValue=1"
aws logs put-metric-filter \
        --log-group-name /aws/cloudtrail \
        --filter-name AWSConfigChanges \
        --filter-pattern "{($.eventSource=config.amazonaws.com) && (($.eventName=StopConfigurationRecorder) || ($.eventName=DeleteDeliveryChannel) || ($.eventName=PutDeliveryChannel) || ($.eventName=PutConfigurationRecorder))}" \
        --metric-transformations "metricName=AWSConfigChanges,metricNamespace=LogMetrics,metricValue=1"
aws logs put-metric-filter \
        --log-group-name /aws/cloudtrail \
        --filter-name SecurityGroupChanges \
        --filter-pattern "{($.eventName=AuthorizeSecurityGroupIngress) || ($.eventName=AuthorizeSecurityGroupEgress) || ($.eventName=RevokeSecurityGroupIngress) || ($.eventName=RevokeSecurityGroupEgress) || ($.eventName=CreateSecurityGroup) || ($.eventName=DeleteSecurityGroup)}" \
        --metric-transformations "metricName=SecurityGroupChanges,metricNamespace=LogMetrics,metricValue=1"
aws logs put-metric-filter \
        --log-group-name /aws/cloudtrail \
        --filter-name NetworkACLChanges \
        --filter-pattern "{($.eventName=CreateNetworkAcl) || ($.eventName=CreateNetworkAclEntry) || ($.eventName=DeleteNetworkAcl) || ($.eventName=DeleteNetworkAclEntry) || ($.eventName=ReplaceNetworkAclEntry) || ($.eventName=ReplaceNetworkAclAssociation)}" \
        --metric-transformations "metricName=NetworkACLChanges,metricNamespace=LogMetrics,metricValue=1"
aws logs put-metric-filter \
        --log-group-name /aws/cloudtrail \
        --filter-name NetworkGatewayChanges \
        --filter-pattern "{($.eventName=CreateCustomerGateway) || ($.eventName=DeleteCustomerGateway) || ($.eventName=AttachInternetGateway) || ($.eventName=CreateInternetGateway) || ($.eventName=DeleteInternetGateway) || ($.eventName=DetachInternetGateway)}" \
        --metric-transformations "metricName=NetworkGatewayChanges,metricNamespace=LogMetrics,metricValue=1"
aws logs put-metric-filter \
        --log-group-name /aws/cloudtrail \
        --filter-name RouteTableChanges \
        --filter-pattern "{($.eventName=CreateRoute) || ($.eventName=CreateRouteTable) || ($.eventName=ReplaceRoute) || ($.eventName=ReplaceRouteTableAssociation) || ($.eventName=DeleteRouteTable) || ($.eventName=DeleteRoute) || ($.eventName=DisassociateRouteTable)}" \
        --metric-transformations "metricName=RouteTableChanges,metricNamespace=LogMetrics,metricValue=1"
aws logs put-metric-filter \
        --log-group-name /aws/cloudtrail \
        --filter-name VPCChanges \
        --filter-pattern "{($.eventName=CreateVpc) || ($.eventName=DeleteVpc) || ($.eventName=ModifyVpcAttribute) || ($.eventName=AcceptVpcPeeringConnection) || ($.eventName=CreateVpcPeeringConnection) || ($.eventName=DeleteVpcPeeringConnection) || ($.eventName=RejectVpcPeeringConnection) || ($.eventName=AttachClassicLinkVpc) || ($.eventName=DetachClassicLinkVpc) || ($.eventName=DisableVpcClassicLink) || ($.eventName=EnableVpcClassicLink)}" \
        --metric-transformations "metricName=VPCChanges,metricNamespace=LogMetrics,metricValue=1"
```

#### CloudWatch Alert
```bash
aws cloudwatch put-metric-alarm \
        --alarm-name RootAccountUsage \
        --namespace LogMetrics \
        --metric-name RootAccountUsage \
        --threshold 1 \
        --comparison-operator GreaterThanOrEqualToThreshold \
        --period 60 \
        --evaluation-periods 1 \
        --datapoints-to-alarm 1 \
        --statistic Sum \
        --treat-missing-data notBreaching
aws cloudwatch put-metric-alarm \
        --alarm-name UnauthorizedAPICalls \
        --namespace LogMetrics \
        --metric-name UnauthorizedAPICalls \
        --threshold 1 \
        --comparison-operator GreaterThanOrEqualToThreshold \
        --period 60 \
        --evaluation-periods 1 \
        --datapoints-to-alarm 1 \
        --statistic Sum \
        --treat-missing-data notBreaching
aws cloudwatch put-metric-alarm \
        --alarm-name ConsoleSigninWithoutMFA \
        --namespace LogMetrics \
        --metric-name ConsoleSigninWithoutMFA \
        --threshold 1 \
        --comparison-operator GreaterThanOrEqualToThreshold \
        --period 60 \
        --evaluation-periods 1 \
        --datapoints-to-alarm 1 \
        --statistic Sum \
        --treat-missing-data notBreaching
aws cloudwatch put-metric-alarm \
        --alarm-name IAMPolicyChanges \
        --namespace LogMetrics \
        --metric-name IAMPolicyChanges \
        --threshold 1 \
        --comparison-operator GreaterThanOrEqualToThreshold \
        --period 60 \
        --evaluation-periods 1 \
        --datapoints-to-alarm 1 \
        --statistic Sum \
        --treat-missing-data notBreaching
aws cloudwatch put-metric-alarm \
        --alarm-name CloudTrailChanges \
        --namespace LogMetrics \
        --metric-name CloudTrailChanges \
        --threshold 1 \
        --comparison-operator GreaterThanOrEqualToThreshold \
        --period 60 \
        --evaluation-periods 1 \
        --datapoints-to-alarm 1 \
        --statistic Sum \
        --treat-missing-data notBreaching
aws cloudwatch put-metric-alarm \
        --alarm-name ConsoleAuthenticationFailure \
        --namespace LogMetrics \
        --metric-name ConsoleAuthenticationFailure \
        --threshold 1 \
        --comparison-operator GreaterThanOrEqualToThreshold \
        --period 60 \
        --evaluation-periods 1 \
        --datapoints-to-alarm 1 \
        --statistic Sum \
        --treat-missing-data notBreaching
aws cloudwatch put-metric-alarm \
        --alarm-name DisableOrDeleteCMK \
        --namespace LogMetrics \
        --metric-name DisableOrDeleteCMK \
        --threshold 1 \
        --comparison-operator GreaterThanOrEqualToThreshold \
        --period 60 \
        --evaluation-periods 1 \
        --datapoints-to-alarm 1 \
        --statistic Sum \
        --treat-missing-data notBreaching
aws cloudwatch put-metric-alarm \
        --alarm-name S3BucketPolicyChanges \
        --namespace LogMetrics \
        --metric-name S3BucketPolicyChanges \
        --threshold 1 \
        --comparison-operator GreaterThanOrEqualToThreshold \
        --period 60 \
        --evaluation-periods 1 \
        --datapoints-to-alarm 1 \
        --statistic Sum \
        --treat-missing-data notBreaching
aws cloudwatch put-metric-alarm \
        --alarm-name AWSConfigChanges \
        --namespace LogMetrics \
        --metric-name AWSConfigChanges \
        --threshold 1 \
        --comparison-operator GreaterThanOrEqualToThreshold \
        --period 60 \
        --evaluation-periods 1 \
        --datapoints-to-alarm 1 \
        --statistic Sum \
        --treat-missing-data notBreaching
aws cloudwatch put-metric-alarm \
        --alarm-name SecurityGroupChanges \
        --namespace LogMetrics \
        --metric-name SecurityGroupChanges \
        --threshold 1 \
        --comparison-operator GreaterThanOrEqualToThreshold \
        --period 60 \
        --evaluation-periods 1 \
        --datapoints-to-alarm 1 \
        --statistic Sum \
        --treat-missing-data notBreaching
aws cloudwatch put-metric-alarm \
        --alarm-name NetworkACLChanges \
        --namespace LogMetrics \
        --metric-name NetworkACLChanges \
        --threshold 1 \
        --comparison-operator GreaterThanOrEqualToThreshold \
        --period 60 \
        --evaluation-periods 1 \
        --datapoints-to-alarm 1 \
        --statistic Sum \
        --treat-missing-data notBreaching
aws cloudwatch put-metric-alarm \
        --alarm-name NetworkGatewayChanges \
        --namespace LogMetrics \
        --metric-name NetworkGatewayChanges \
        --threshold 1 \
        --comparison-operator GreaterThanOrEqualToThreshold \
        --period 60 \
        --evaluation-periods 1 \
        --datapoints-to-alarm 1 \
        --statistic Sum \
        --treat-missing-data notBreaching
aws cloudwatch put-metric-alarm \
        --alarm-name RouteTableChanges \
        --namespace LogMetrics \
        --metric-name RouteTableChanges \
        --threshold 1 \
        --comparison-operator GreaterThanOrEqualToThreshold \
        --period 60 \
        --evaluation-periods 1 \
        --datapoints-to-alarm 1 \
        --statistic Sum \
        --treat-missing-data notBreaching
aws cloudwatch put-metric-alarm \
        --alarm-name VPCChanges \
        --namespace LogMetrics \
        --metric-name VPCChanges \
        --threshold 1 \
        --comparison-operator GreaterThanOrEqualToThreshold \
        --period 60 \
        --evaluation-periods 1 \
        --datapoints-to-alarm 1 \
        --statistic Sum \
        --treat-missing-data notBreaching
```