目次
- 目次
- はじめに
- 発見的ガードレールとは
- AWS Control Towerによって作成されるSNSトピック
- 発見的ガードレールの通知運用をどのように行うか
- AWS Control Towerの発見的ガードレールの準拠状況のみを通知する構成方法
- まとめ
はじめに
前回の記事で、AWS Control Towerを利用することでAWSマルチアカウント管理の利便性を高めることができることを紹介しました。本記事では、AWS Control Towerが既定でセットアップしてくれる機能の一つとして、AWS環境の設定値が発見的ガードレールに非準拠の場合に利用者に通知してくれる機能の使い方と課題、その課題をどのように回避できるかを説明します。
発見的ガードレールとは
発見的ガードレールとは、AWS Configルールを利用したAWS環境の設定値が望ましい状態になっているかを管理する仕組みを指します。「AWS 基礎セキュリティのベストプラクティス」等のAWS Security Hubのセキュリティ標準を有効化した際に、特定のコントロールに設定値が非準拠になってスコアが変動するのも同じ仕組みで実現しています。
AWS Security Hubの場合、運用者が月次などの頻度で能動的にマネージメントコンソールを確認することで最新のスコアを把握し、設定値の是正対応を行うといった運用を想定することができますが、場合によってはAmazon RDSインスタンスがパブリックアクセス可能の設定になっている場合など、あるべき形から逸れた設定値がAWS環境で設定された際に、即座に通知してほしいという要望もあるかもしれません。
そうした要望に応える形で、AWS Control TowerによってAWSマルチアカウント構成によるランディングゾーンを作成すると、発見的ガードレールの非準拠を任意の宛先に通知してくれるSNSトピックが作成されます。このSNSトピックにメールアドレスなどをサブスクライブすれば、設定の非準拠を検知した際に運用者まで任意の方法で通知が行われます。
AWS Control Towerによって作成されるSNSトピック
AWSマルチアカウント構成において発見的ガードレール非準拠の通知はAuditアカウントに集約されます。AuditアカウントにはAWS Control Towerによって用途に応じて3つのSNSトピックが作成されます。
SNSトピック名 | 説明 |
---|---|
aws-controltower-AllConfigNotifications | AWS Configの準拠・非準拠の状態変更時・リソースの変更時と、CloudTrailのログデリバリー時に通知が行われる |
aws-controltower-SecurityNotifications | Control Towerがサポートするリージョンに作成され、リージョン内のAWS Configの準拠・非準拠の状態変更時・リソースの変更時に通知される。このトピックへの通知は、aws-controltower-NotificationForwarderというLambda関数により、aws-controltower-AggregateSecurityNotificationsトピックにフォワードされる |
aws-controltower-AggregateSecurityNotifications | 各リージョンのaws-controltower-SecurityNotificationsに通知された内容が、aws-controltower-NotificationForwarderのLambda関数によってAWS Control Towerのホームリージョンのaws-controltower-AggregateSecurityNotificationsに集約して通知される |
Compliance notifications by SNS in the audit account - AWS Control Tower
Guidance on subscribing to SNS Topics - AWS Control Tower
SNSトピックによる通知はリージョンを跨げないため、間に通知をフォワードするLambda関数を挟んで、一つのSNSトピックに通知を集約する構成となっています。
公式ドキュメントによると、AWS Configの準拠状況を通知する用途なら各リージョンの通知が集約されるaws-controltower-AggregateSecurityNotificationsトピックにサブスクライブすることが推奨されており、より詳細な情報も通知したければaws-controltower-AllConfigNotificationsにサブスクライブするよう記載されています。
しかし、いずれのトピックにサブスクライブした場合も、実際にはかなり大量の通知が飛んでくることになり、運用者がそれらの通知を適切に捌く事は現実的でないのが実情です。というのも、aws-controltower-AggregateSecurityNotificationsトピックにサブスクライブした場合、AWS Security Hubの実態はAWS Configルールであるため、AWS Security Hubのコントロールで非準拠になった設定が全て通知され、単にAWS Control Tower標準の発見的ガードレールの非準拠状況だけでなく、AWS Security Hubの状況まで全て通知されるからです。この状況を避けるために、冒頭ではAWS Security Hubのスコアは運用者が月次などの任意の頻度で能動的に確認する例を記載しました。
一方、aws-controltower-AllConfigNotificationsにサブスクライブすると、それ以上に大量の通知が飛んでくるため、もっと運用が回らなくなります。
具体的には、AWS Configに関する以下の全ての通知が行われます。
- リソース設定の変更
- リソース設定履歴のアカウントへの配信
- 記録対象のリソースの設定スナップショットがアカウントで起動および配信
- リソースの準拠状態
- リソースに対してルールの評価が開始
- AWS Config からアカウントへの通知の配信失敗
Notifications that AWS Config Sends to an Amazon SNS topic - AWS Config
大量の通知が飛んでくることは公式ドキュメントでも触れられており、AWS Control Towerが作成してくれるSNSトピックにサブスクライブして運用者への通知を行うというのは、そのままでは現実的には難しいです。
SNS topics in AWS Control Tower are extremely noisy, by design.
発見的ガードレールの通知運用をどのように行うか
上記の図は各AWSアカウントで発生した発見的ガードレールを含むAWS Configルールの通知をAWS Control Towerがどのように構成するかを表したものです。
赤枠で囲った範囲がAWS Control Towerが作成する通知の仕組みであり、これまで説明した通知を実現する構成です。各アカウントのAWS Configルールに対してEventBridgeルールが設定され、リージョン内でSNSトピックに通知されます。SNSトピックにはLambda関数がサブスクライブされており、AuditアカウントのControl TowerホームリージョンのSNSトピックに通知が集約されます。
この仕組みにそのまま乗ると、先述の通り運用者に通知が大量に来ることになるため、対応策を考える必要があります。
AWSからは二つの方法が提示されており、一つは追加のLambda関数を作成してaws-controltower-AggregateSecurityNotificationsトピックにサブスクライブすることで、特定の通知内容をフィルターすることです。もう一つはAWS Control Towerが作成するのと同等の通知構成を自身で行い、EventBridgeルールで通知対象をフィルターする方法です。
Administrators who wish to filter out specific types of notifications from an SNS topic can create an AWS Lambda function and subscribe it to the SNS topic. Alternatively, you can set up an EventBridge rule to filter notifications, as described in this support article, How can I be notified when an AWS resource is non-compliant using AWS Config?
Compliance notifications by SNS in the audit account - AWS Control Tower
EventBridgeルールで通知対象をフィルターするなら、AWS Control Towerが作成するaws-controltower-ConfigComplianceChangeEventRuleを編集すれば早いということになりますが、残念ながらその操作はAWSからサポートされません。AWS Control Towerが作成したリソースを利用者が編集することはAWSのサポート外となり、もし編集した場合はControl Towerのバージョンアップや廃止の際に問題が発生する可能性があります。
本記事では、そうした理由からAWS Control Towerが作成するのと同等の構成を自身で作成して、aws-controltower-ConfigComplianceChangeEventRuleに相当するEventBridgeルールにて通知対象をフィルターする方法を紹介します。
AWS Control Towerの発見的ガードレールの準拠状況のみを通知する構成方法
先述の構成図の青枠で囲った範囲のリソースを個別に作成することで、特定の対象のみ通知する構成を行うことができます。本記事では、Control Towerが標準で管理するガードレールの非準拠のみを通知するようフィルターする構成とします。
作成するAWSリソースの設定値と説明を文章で全て網羅することはできないので、必要なリソースを一発でデプロイするCloudFormationテンプレートを記載します。
①Auditアカウントで通知用のSNSトピックを作成する
AWSTemplateFormatVersion: "2010-09-09" Description: Create Notification SNS Topic for Non Compliant Guardrails Parameters: EmailAddress: Description: Email Address for recieving Non Compliant Notification. Type: String OrgID: Description: Organization ID for recieving Non Compliant Notification. e.g. o-xxxxxxxxxx Type: String Resources: ## SNS Topic NonCompliantNotificationSNSTopic: Type: AWS::SNS::Topic Properties: TopicName: NonCompliantNotificationTopic ## SNS Topic Policy SNSNotificationPolicy: Type: AWS::SNS::TopicPolicy Metadata: cfn_nag: rules_to_suppress: - id: F18 reason: "Condition restricts permissions to current account." Properties: Topics: - !Ref NonCompliantNotificationSNSTopic PolicyDocument: Statement: - Sid: __default_statement_ID Effect: Allow Principal: AWS: "*" Action: - SNS:GetTopicAttributes - SNS:SetTopicAttributes - SNS:AddPermission - SNS:RemovePermission - SNS:DeleteTopic - SNS:Subscribe - SNS:ListSubscriptionsByTopic - SNS:Publish - SNS:Receive Resource: !Ref NonCompliantNotificationSNSTopic Condition: StringEquals: AWS:SourceOwner: !Sub ${AWS::AccountId} - Sid: AcceptNotificationsfromwithinOrganization Effect: Allow Principal: "AWS": "*" Action: sns:Publish Resource: !Ref NonCompliantNotificationSNSTopic Condition: StringEquals: aws:PrincipalOrgID: !Ref OrgID ## SNS Subscription NonCompliantEmailSubscription: Type: AWS::SNS::Subscription Properties: Protocol: email Endpoint: !Ref EmailAddress TopicArn: !Ref NonCompliantNotificationSNSTopic
②ガードレールの非準拠を監視したいアカウントで、非準拠通知に必要なIAMロールを作成する
AWSTemplateFormatVersion: "2010-09-09" Description: Configure SNS Notification Forward IAM Roles Parameters: SecurityTopicName: Type: String Default: NonCompliantNotificationTopic Description: Security Notification SNS Topic Name. AuditAccountId: Type: String MaxLength: 12 MinLength: 12 Description: AWS Account Id of the Audit account. RoleName: Type: String Default: SnsNotificationForwardRole Description: SNS Notification Forward IAM Roles. Resources: SnsNotificationForwardLambdaRole: Type: "AWS::IAM::Role" Properties: RoleName: !Sub ${RoleName} AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: "lambda.amazonaws.com" Action: - "sts:AssumeRole" Path: "/" ManagedPolicyArns: - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" Policies: - PolicyName: sns PolicyDocument: Statement: - Effect: Allow Action: - "sns:publish" Resource: !Sub arn:aws:sns:*:${AuditAccountId}:${SecurityTopicName} Outputs: SnsNotificationForwardRoleName: Description: SnsNotificationForwardRoleName Value: !Ref RoleName Export: Name: SnsNotificationForwardRoleName
③ガードレールの非準拠を監視したいアカウントで、非準拠通知に必要なリソースを作成する
AWSTemplateFormatVersion: "2010-09-09" Description: Configure Eventbridge Rule, local SNS Topic, notifications forwarding Lambda and CloudWatch Logs events to forward messages from local SNS Topic to Security Topic Parameters: RoleName: Type: String Default: SnsNotificationForwardRole Description: SNS Notification Forward IAM Roles. SecurityTopicName: Type: String Default: NonCompliantNotificationTopic Description: Security Notification SNS Topic Name. SecurityAccountId: Type: "String" MaxLength: 12 MinLength: 12 Description: AWS Account Id of the Audit account. SecurityAccountRegion: Type: "String" Default: ap-northeast-1 Description: AWS Control Tower Home Region of the Audit account. LogsRetentionInDays: Description: "Specifies the number of days you want to retain notification forwarding log events in the Lambda log group." Type: Number Default: 14 AllowedValues: [1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653] EnableConfigRuleComplianceChangeAlarm: Type: String Description: "Enable notifications for AWS Config rule compliance status changes" Default: true AllowedValues: - true - false Conditions: EnableConfigRuleChangeNotification: !Equals - !Ref EnableConfigRuleComplianceChangeAlarm - "true" Resources: ForwardSnsNotificationGroup: Type: "AWS::Logs::LogGroup" Properties: LogGroupName: /aws/lambda/LambdaNotificationForwarder RetentionInDays: !Ref LogsRetentionInDays ForwardSnsNotification: Type: "AWS::Lambda::Function" DependsOn: ForwardSnsNotificationGroup Properties: FunctionName: LambdaNotificationForwarder Description: SNS message forwarding function for aggregating account notifications. Code: ZipFile: !Sub | from __future__ import print_function import boto3 import json import os def lambda_handler(event, context): #print("Received event: " + json.dumps(event, indent=2)) region = os.environ.get('region') sns = boto3.client('sns', region_name=region) subject=event['Records'][0]['Sns']['Subject'] if subject is None: subject = 'None' message = event['Records'][0]['Sns']['Message'] try: msg = json.loads(message) message = json.dumps(msg, indent=4) if 'detail-type' in msg: subject = msg['detail-type'] except: print('Not json') response = sns.publish( TopicArn=os.environ.get('sns_arn'), Subject='Config非準拠を検知しました', Message=message ) print(response) return response Handler: "index.lambda_handler" MemorySize: 128 Role: !Sub arn:aws:iam::${AWS::AccountId}:role/${RoleName} Runtime: "python3.11" Timeout: 60 Environment: Variables: # region: !Sub ${AWS::Region} # sns_arn: !Sub arn:aws:sns:${AWS::Region}:${SecurityAccountId}:${SecurityTopicName} region: !Sub ${SecurityAccountRegion} sns_arn: !Sub arn:aws:sns:${SecurityAccountRegion}:${SecurityAccountId}:${SecurityTopicName} LocalSecurityTopic: Type: AWS::SNS::Topic Properties: DisplayName: LocalSecurityNotificationTopic TopicName: LocalSecurityNotificationTopic SNSNotificationPolicy: Type: AWS::SNS::TopicPolicy Metadata: cfn_nag: rules_to_suppress: - id: F18 reason: "Condition restricts permissions to current account." Properties: Topics: - !Ref LocalSecurityTopic PolicyDocument: Statement: - Sid: __default_statement_ID Effect: Allow Principal: AWS: "*" Action: - SNS:GetTopicAttributes - SNS:SetTopicAttributes - SNS:AddPermission - SNS:RemovePermission - SNS:DeleteTopic - SNS:Subscribe - SNS:ListSubscriptionsByTopic - SNS:Publish - SNS:Receive Resource: !Ref LocalSecurityTopic Condition: StringEquals: AWS:SourceOwner: !Sub ${AWS::AccountId} - Sid: TrustCWEToPublishEventsToMyTopic Effect: Allow Principal: Service: events.amazonaws.com Action: sns:Publish Resource: !Ref LocalSecurityTopic SNSNotificationSubscription: Type: "AWS::SNS::Subscription" Properties: Endpoint: !GetAtt ForwardSnsNotification.Arn Protocol: lambda TopicArn: !Ref LocalSecurityTopic SNSInvokeLambdaPermission: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction Principal: sns.amazonaws.com SourceArn: !Ref LocalSecurityTopic FunctionName: !GetAtt ForwardSnsNotification.Arn # Enable notifications for AWS Config Rule compliance changes for NON_COMPLIANT CWEventRuleComplianceChangeEventForNONCOMPLIANT: Type: AWS::Events::Rule Condition: EnableConfigRuleChangeNotification Properties: Name: GRConfigNonCompliant Description: "Event Rule to send notification on NON_COMPLIANT Config Rule compliance changes." EventPattern: { "source": ["aws.config"], "detail-type": ["Config Rules Compliance Change"], "detail": { "messageType": ["ComplianceChangeNotification"], "configRuleName": [{ "prefix": "AWSControlTower_" }], "newEvaluationResult": { "complianceType": ["NON_COMPLIANT"] }, }, } State: ENABLED Targets: - Id: !Sub "Compliance-Change-Topic" Arn: !Ref LocalSecurityTopic InputTransformer: InputPathsMap: "Account": "$.account" "resourceId": "$.detail.resourceId" "Region": "$.region" "Time": "$.time" "complianceType": "$.detail.newEvaluationResult.complianceType" "configRuleName": "$.detail.configRuleName" InputTemplate: | "次のリソースで、Configルール(ガードレール)の非準拠を検知しました。" "- Region : <Region> " "- Account : <Account>" "- Time : <Time>" "- resourceId : <resourceId>" "- configRuleName : <configRuleName>" "- complianceType : <complianceType>" Outputs: LocalSecurityTopic: Description: Local Security Notification SNS Topic ARN Value: !Ref LocalSecurityTopic LocalSecurityTopicName: Description: Local Security Notification SNS Topic Name Value: !GetAtt LocalSecurityTopic.TopicName
※1 通知先をControl Towerのホームリージョンに集約せず、リージョンごとにSNSトピックを作成して通知する場合、①のSNSトピックを通知対象リージョンごとに作成し、③のLambdaNotificationForwarderのLambda関数の環境変数regionとsns_arnをリージョンごとに設定ください。
※2 通知対象を変更する場合は、③のEventBridgeルールのEventPatternを変更ください。
まとめ
本記事では、AWS Control Towerによって設定される発見的ガードレール等の非準拠を通知する仕組みについて解説しました。そして、既定で作成される通知の対象が広範であるため、実運用での利用は厳しいことも説明しました。その課題に対してCloudFormationテンプレートを利用して個別の通知構成を行うことで、運用に耐えうる程度に通知対象を絞る方法について紹介しました。
AWS Control Towerが既定で作成する発見的ガードレールの通知の仕組みをよく理解できていなかった方や、既定の構成とは異なるワークアラウンドをお探しの方のお役に立てば幸いです。