AWSセキュリティ強化施策検証 AWS CloudTrailとLambdaでセキュリティグループ変更検知と自動修正機能を実装してみた

Amazon Web Services,AWS CloudTrail,AWS Lambda,システム監査,セキュリティ

プラットフォーム技術部の小林正彦です。

今回はシステム監査におけるIT統制施策の1つとして「セキュリティグループの変更検知と自動修正」を検証したので、その内容を紹介します。

みなさまのシステム監査対応の参考になれば幸いです。

システム監査については本記事で全体像をまとめています。
そもそもシステム監査とはどのような目的で何をおこなうものなのか、どのような施策があるのかについては以下の記事をご覧ください。

本記事の構成は以下の通りです。

  1. AWSでのIT統制のおさらい
  2. 変更検知と修正方法の整理
  3. CloudTrailとLambdaによる変更検知と自動修正処理の紹介(本記事の本題)
  4. 検証結果

今回紹介する内容は、弊社検証環境で実施したものではありますが、本番運用を想定したテストまでは実施していません。
参考にされる際は適用するシステムで問題がないか十分に調査・検証をしたうえで実装してください。

AWSでのIT統制のおさらい

情報セキュリティ脅威にはさまざまなものがあります。
参考としてIPAが公開している「情報セキュリティ10大脅威 2022」を参照すると「ランサムウェアによる被害」「標的型攻撃による機密情報の搾取」「内部不正による情報漏洩」などの脅威があげられています。

各脅威に対して「予防」「発見」「是正」の観点で施策を用意することが重要ですが、まずは「予防」の観点から環境を整理して、次に「発見」「是正」へと段階的に施策を検討・整備していくのが望ましいでしょう。

今回紹介する「セキュリティグループの変更検知と修正自動化」は「内部不正による情報漏洩」脅威に対する「発見・是正」的な統制施策の1つとして扱えると思います。(「是正」の部分は対応 / 回復 / 訂正など、さまざまな表現で定義されていますが目的は同じだと認識しています)

「発見・是正」の前段である予防的な統制(例としてID管理・アクセス制限・パッチ適用など)の紹介は本記事では割愛します。

パッチ適用については、以下のブログ記事もありますのでご覧ください。

変更検知と修正自動化方法の整理

検証内容と結果の説明の前に、AWSにおける変更検知と修正自動化の方法として3つ紹介します。

  1. AWS Configマネージドルール+SSM
  2. AWS Configカスタムルール+SSM
  3. AWS CloudTrail + Lambda(本記事紹介)

AWS Configマネージドルールを使用する方法

AWSリソースのインベントリと変更追跡機能を提供しているマネージドサービス「Config」にはリソースの設定値を評価する機能があり、リソースの設定値がルールに準拠していない場合にSSMオートメーションと連携してリソースを修復するアクションを実行できます。

下図はセキュリティグループルールの設定変更を検知して自動的に修復するまでの流れです。

ConfigとSSMオートメーションによる設定変更検知と自動修復
ConfigとSSMオートメーションによる設定変更検知と自動修復

リソースの状態を評価するためのルールはAWSからマネージドルールが提供されています。
このルールを使ってConfigとSSMによる変更検知と修正自動化システムが完成します。

マネージドルールのリストはこちらを参照してください。さまざまなリソースに対応するルールが用意されています。

参考までに、セキュリティグループに関連するマネージドルールを紹介します。

マネージドルール検知・修正内容
restricted-sshセキュリティグループのインバウンドルールに「SSH 0.0.0.0/0」のルールが存在する場合、ルール非準拠であると評価して該当のルールを削除するアクションを実行する。
restricted-common-ports セキュリティグループのインバウンドルールに「TCP任意ポート 0.0.0.0/0」のルールが存在する場合、ルール非準拠であると評価して該当のルールを削除するアクションを実行する。
vpc-default-security-group-closedデフォルトのセキュリティグループが無効化(ルールがすべて削除されている状態)されているかを確認する。無効化されていたら準拠と評価する。
ec2-security-group-attached-to-eni-periodicセキュリティグループが使用されているかどうか(リソースに割り当てられているか)を確認し、割り当てられていれば準拠と評価する。
セキュリティグループを対象にしたマネージドルール

マネージドルールは、検知・修正内容をシステムに適した内容に変更できない制約がありますが、容易に仕組みを実装できます。

AWS Configカスタムルールを使用する方法

こちらもConfigとSSMを使用した変更検知と自動修正の方法ですが、マネージドルールではなく自作のルール(カスタムルール)を用意してリソース設定値を評価し、非準拠であれば修復アクションを実行する仕組みです。

カスタムルールの作成方法としてLambdaを使用する方法とAWS Cloudformation Guardのポリシーを作成する2つの方法があります。

さきほどのマネージドルールを使用した場合とアクションの流れは変わりません。「②設定変更を検知」の箇所がLambdaあるいはCloudformation Guardに置き換わっています。

ConfigとSSMを使用した変更検知と自動修正の方法
ConfigとSSMを使用した変更検知と自動修正の方法

Lambdaを使用する場合はAWSが公開しているサンプルコードを使用できます。
例えばセキュリティグループに関するものだと「EC2_SECURITY_GROUP_BADINGRESSというサンプルコードがあります。

マネージドルールを使用する方法に比べて評価内容を柔軟に設定できますが、コードやポリシーの開発と管理運用の整備が必要です。

開発についてはサンプルに倣い実装できる部分もありますが、評価ルールの検討から設計・実装していくためのAWSリソース全般に関する知識に加え開発スキルも必要になるでしょう。

AWS CloudTrailとLambdaを使用する方法

先述の2方式とは異なり、ConfigではなくCloudTrailを使用した方法です。
本記事ではこの構成で変更検知と修正の動きを検証しました。
具体的な内容は「AWS CloudTrailとLambdaによる変更検知と修正処理の紹介」を参照してください。

CloudTrailイベントをトリガーにした修正自動化
CloudTrailイベントをトリガーにした修正自動化

CloudTrailのイベントログをソースに、CloudWatch Logsのフィルターを経由してLambdaを実行します。検知はCloudWatch、変更はLambdaが担います。

変更検知の機能はConfigカスタムルールと比較すると限定的ですが、評価ルールを作成・管理する必要がないため実装は容易です。

AWS CloudTrailとLambdaによる変更検知と修正処理の紹介

前置きが長くなりましたが、今回検証した処理の紹介にうつります。
さきほど紹介した「AWS CloudTrailとLambdaを使用する方法」の具体的な内容です。

目的

以下のような機能を実装します。

  • セキュリティグループのルール変更(追加・変更)を検知する
  • 変更されたルールを自動的に削除する
  • メンテナンス作業による変更の場合は修正しない
    具体的には、特定ユーザーによる変更作業は自動修正の対象外とする

機能概要

実装する機能と流れは以下の通りです。

  1. CloudTrailイベントログをCloudWatch Logsに出力する
  2. CloudWatch Logsサブスクリプションフィルターでセキュリティグループの変更イベントを検知する
  3. CloudWatch Logsサブスクリプションフィルターから修正Lambdaを実行する
    以降はLambda内の処理へとうつる
  4. 変更作業者を判定する(特定ユーザーの場合は修正をおこなわない)
  5. 変更されたルールIDを抽出する
  6. 対象のルールを削除する

変更された値の評価はせずに、変更が加えられた・追加されたルールを削除します。

必要な変更作業をしたのに削除されてしまう運用上の問題を回避するために、特定ユーザーによる変更作業であった場合は削除しない判定をおこないます。

構成図

こちらが構成図です。

CloudTrailイベントをトリガーにしたセキュリティグループ変更検知と自動修正
CloudTrailイベントをトリガーにしたセキュリティグループ変更検知と自動修正

ポイント:CloudTrailの利用

検知するソースイベントはConfigではなくCloudTrailのログイベントを使用します。

CloudTrailの「CloudWatch Logs – オプション」を有効化してCloudTrailの証跡ログをCloudWatch Logsに出力します。

出力された証跡ロググループにサブスクリプションフィルターを作成し、セキュリティグループ変更イベントを検知してログイベントをLambdaに渡します。

CloudTrailログのレファレンスはこちらを参照してください。「CloudWatch Logs – オプション」の有効化に追加料金は発生しませんが、CloudWatchおよびCloudWatch Logsの料金は適用されます。

ソースイベントがConfigの場合は「Config→SNS→Lambda」という構成にすることでConfigログをLambdaに渡すこともできますが、「Configログでは変更作業者(ユーザー情報)を取得できない」ため今回の検証では採用していません。

機能概要の4つ目にあげた「変更作業者を判定する」処理を実現するためには変更をおこなったユーザー情報が必要ですが、残念ながらConfigログにはユーザーを判断できる情報は含まれていません。

Configログに出力されているセキュリティグループIDからCloudTrailログをひもづけてユーザーを特定することもできると思いますが、ユーザー情報をCloudTrailログから抽出できるのであれば最初からCloudTrailログをソースにしてLambdaを実行する構成の方がシンプルになるのでCloudTrailを採用しました。

今回は特定のセキュリティグルールを変更前に戻す機能は実装していません。そのため、変更前後での設定値差分を参照する必要がなく、そういった観点でもConfigではなくCloudTrailログで十分と判断しました。

ポイント:ルール削除要否の判断

削除要否の判断はユーザー情報を基におこなっています。

「メンテナンスや開発で使用するユーザーによる変更イベントの場合は修正不要と判断してルールを削除しない」という方針のもとユーザー情報を評価します。

評価方法として、例えばSSMパラメータストアにメンテナンスフラグパラメータを用意しておいて、フラグが立っていたら削除しないといった方式も考えられます。

今回は使用するサービス / リソースを減らして極力シンプルな構成で検証したので、CloudTrailログに含まれる情報の中で実現できる構成にしています。

ポイント:サブスクリプションフィルター

CloudTrailログからセキュリティグループの変更イベントを検知するためにサブスクリプションフィルターを使用します。

サブスクリプションフィルターをトリガーにLambdaを実行するので「Lambdaサブスクリプションフィルター」を選択します。

Lambdaサブスクリプションフィルターは以下の2つを作成しました。

  • 既存のルール設定が変更されたイベントを検知するフィルター
  • 新しいルールが追加されたイベントを検知するフィルター

それぞれのフィルターの内容を紹介します。

変更検知フィルター:ModifySecurityGroupRules-filter

既存ルールの設定変更を検知するためのフィルターです。フィルター名称は任意設定です。

サブスクリプションフィルター名ModifySecurityGroupRules-filter
送信先実行するLamdba関数名
ログの形式Amazon CloudTrail
サブスクリプションフィルターのパターン{ ( $.requestParameters.ModifySecurityGroupRulesRequest.GroupId="sg-xxxxxxxxxxxxxxxxx" ) && ( $.eventName="ModifySecurityGroupRules" ) }
設定変更イベント検知用Lambdaサブスクリプションフィルター設定値

検証として特定のセキュリティグループ(GroupID)の変更イベント(ModifySecurityGroupRules)を検知するフィルターパターンを作成しました。

サブスクリプションフィルターは作成後に設定を変更できない仕様なので、設定を変えるには再作成しなければなりません。

対象のセキュリティグループを変更する運用には不向きな機能なので、本番運用を想定する場合は、サブスクリプションフィルターではなくLamdbaの環境変数として対象のセキュリティグループを指定しておく、あるいはSSMのパラメータストアに対象のセキュリティグループ情報をリストしておいてLambda実行時に判定する構成の方が実運用に適していると思います。

追加検知フィルター:AuthorizeSecurityGroup-filter

新規ルールを追加したイベントを検知するためのフィルターです。フィルター名称は任意設定です。

インバウンドルール、アウトバウンドルール両方のイベントを検知するパターンを記載しています。

サブスクリプションフィルター名AuthorizeSecurityGroup-filter
送信先実行するLamdba関数名
ログの形式Amazon CloudTrail
サブスクリプションフィルターのパターン{ ( $.requestParameters.groupId="sg-xxxxxxxxxxxxxxxxx" ) && ( ($.eventName="AuthorizeSecurityGroupIngress") || ($.eventName="AuthorizeSecurityGroupEgress")) }
ルール追加イベント検知用Lamdbaサブスクリプションフィルター設定値

フィルターの仕様については変更検知フィルターと同様です。

フィルターを2つに分けた理由は、既存ルール設定変更とルール追加ではイベントログのフォーマットが異なるためです。

グループIDとルールIDを下表に整理しています。

変更検知フィルター:ModifySecurityGroupRules追加検知フィルター:AuthorizeSecurityGroup
グループID requestParameters.ModifySecurityGroupRulesRequest.GroupId requestParameters.groupId
ルールID requestParameters.ModifySecurityGroupRulesRequest.SecurityGroupRule.SecurityGroupRuleId responseElements.securityGroupRuleSet.items.securityGroupRuleId
グループIDとグループルールIDの比較


上記の表のとおり、項目の階層と頭文字が大文字・小文字で異なるという差異があります。

1つのフィルターパターンで設定することも可能だと思いますが、フィルターパターンの記載が複雑になるので分けています。

またサブスクリプションフィルターは1つのロググループに対して2つまでという制約がある点もご注意ください。

ポイント:Lambda用のポリシー

Lambda用のポリシーとして以下を作成します。

ポリシー名用途
AWSLambdaBasicExecutionRoleCloudWatch Logsに実行ログを出力するためのポリシー。自動生成。
falsification-remediation-custompolicyセキュリティグループルールを削除するためのポリシー。
Lambda用のポリシー

falsification-remediation-custompolicy の内容は以下の通りです。
セキュリティグループルール情報の取得、インバウンドおよびアウトバウンドルールの削除を許可します。

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "VisualEditor0",
			"Effect": "Allow",
			"Action": [
				"ec2:RevokeSecurityGroupIngress",
				"ec2:RevokeSecurityGroupEgress"
			],
			"Resource": "arn:aws:ec2::123456789012:security-group/sg-xxxxxxxxxxxxxxxxx"
		},
		{
			"Sid": "VisualEditor1",
			"Effect": "Allow",
			"Action": "ec2:DescribeSecurityGroupRules",
			"Resource": ""
		}
	]
}

検証用のセキュリティグループのみをリソースに指定しています。
DescribeSecurityGroupRules」はすべてのリソースをサポートするアクションなのでリソース指定はできません。

Lambdaに実装した処理の概要

ここからはLambdaに実装した処理の紹介です。

イベントログのデコードと解凍

CloudWatch Logsから渡されるデータフィールドの値はBase64でエンコードされたgzipファイルアーカイブなので、デコードと解凍の処理が必要です。

本実装に限らないお作法的な処理なので詳細は割愛します。

decoded_data = base64.b64decode(event['awslogs']['data'])
json_data = json.loads(gzip.decompress(decoded_data))

参考)CloudWatch Logs で Lambda を使用する

eventNameを取得

リクエストされたイベント情報をeventNameフィールドから取得します。

サブスクリプションフィルターでも設定している以下の3つが取得対象のイベントです。

  • ModifySecurityGroupRules
  • AuthorizeSecurityGroupIngress
  • AuthorizeSecurityGroupEgress

ModifySecurityGroupRulesはセキュリティグループルールの設定値を変更した場合に記録されるイベントです。内部的には該当ルールのrevoke(削除)/authorize(追加)が順次実行されています。

AuthorizeSecurityGroupIngress(Egress)はルールを追加した際に記録されるイベントです。インバウンド・アウトバウンドで別のイベントとして記録されます。

参考)CloudTrailレコードの内容

セキュリティグループIDとセキュリティグループルールIDを取得

ModifySecurityGroupRulesイベントの場合

requestParametersフィールドから「GroupId」と「SecurityGroupRuleId」を取得します。

イベントログの例

"requestParameters": {
    "ModifySecurityGroupRulesRequest": {
        "SecurityGroupRule": {
            "SecurityGroupRuleId": "sgr-yyyyyyyyyyyyyyyyy",
            "tag": 1,
            "SecurityGroupRule": {
                "CidrIpv4": "0.0.0.0/8",
                "Description": test,
                "FromPort": 22,
                "ToPort": 22,
                "IpProtocol": "tcp"
            }
        },
        "GroupId": "sg-xxxxxxxxxxxxxxxxx"
    }
},
AuthorizeSecurityGroup~イベントの場合

AuthorizeSecurityGroupIngressあるいはAuthorizeSecurityGroupEgressイベントの場合はresponseElementsフィールドから「groupId」と「securityGroupRuleId」を取得します。

イベントログの例

"responseElements": {
    "requestId": "00000000-0000-0000-0000-000000000000",
    "_return": true,
    "securityGroupRuleSet": {
        "items": [
            {
                "groupOwnerId": "111111111111",
                "groupId": "sg-xxxxxxxxxxxxxxxxx",
                "securityGroupRuleId": "sgr-yyyyyyyyyyyyyyyyy",
                "isEgress": false,
                "ipProtocol": "tcp",
                "fromPort": 22,
                "toPort": 22,
                "cidrIpv4": "0.0.0.0/24"
            }
        ]
    }
},

ModifyとAuthorizeイベントでは出力されるフィールドのフォーマットが異なります。

またitemsのキー名もイベントによって仕様が異なる(Modifyは頭文字が大文字)ため、イベントにあわせてパラメータ名をセットします。

if eventName == 'ModifySecurityGroupRules':
    keyNameSecurityGroupRuleId = "SecurityGroupRuleId"
    keyNameGroupId = "GroupId"

elif eventName == 'AuthorizeSecurityGroup':
    keyNameSecurityGroupRuleId = "securityGroupRuleId"
    keyNameGroupId = "groupId"

ユーザー評価のためのプリンシパルIDを取得する

userIdentityエレメントからprincipalIdを取得します。

userIdentityエレメントには、リクエストをおこなったIAMアイデンティティのタイプと認証に関する詳細情報が含まれます。

その中の「principalId」を取得します。principalIdは呼び出しをおこなったエンティティの一意の識別子で「AID~」や「AROA~」のようなフォーマットです。

参考)useridentiryエレメントの詳細

このプリンシパルIDでユーザーを判定します。

イベントログの例

    "userIdentity": {
        "type": "IAMUser",
        "principalId": "AIDZZZZZZZZZZZZZZZZZZ",
        "arn": "arn:aws:iam::111111111111:user/falsification-operator",
        "accountId": "111111111111",
        "accessKeyId": "XXXXXXXXXXXXXXXXXXXX",
        "userName": "falsification-operator",
        "sessionContext": {
            "sessionIssuer": {},
            "webIdFederationData": {},
            "attributes": {
                "creationDate": "yyyy-mm-ddT07:18:20Z",
                "mfaAuthenticated": "false"
            }
        }
    },

セキュリティグループルールIDからルールの詳細を取得

ModifySecurityGroupRulesイベントログには、変更が行われたルールがインバウンドルールなのかアウトバウンドルールなのかを識別する情報がありません

この情報がないと、セキュリティグループルールIDがわかっても、どちらのルール(インバウンドかアウトバウンドか)に属しているのかわかりません。

つまり「RevokeSecurityGroupEgress」と「RevokeSecurityGroupIngress」のどちらを実行すればよいのか決定できないのです。

そのためrevokeを実行する前に「describe_security_group_rules」を実行して削除対象のルールがインバウンド/アウトバウンドのどちらに属しているのかを判別します。

事前に取得しているセキュリティグループルールID(SecurityGroupRuleID)を使って以下の処理を実行します。

client = boto3.client('ec2')
rule_info_dict = client.describe_security_group_rules(
    SecurityGroupRuleIds=[
        SecurityGroupRuleIds,
        ]
        )

出力結果の「IsEgress」に「True/False」のbooleanがセットされているので、その値から対象のセキュリティグループルールがインバウンドルールなのかアウトバウンドルールなのかを判断します。

IsEgressがTrueの場合はそのセキュリティグループルールがアウトバウンドルールであるということです。

リターンの例(インバウンドルール)

{
	'SecurityGroupRules': 
		[
			{
				'SecurityGroupRuleId': 'sgr-yyyyyyyyyyyyyyyyy', 
				'GroupId': 'sg-xxxxxxxxxxxxxxxxx', 
				'GroupOwnerId': '111111111111', 
				'IsEgress': False, 
				'IpProtocol': 'tcp', 
				'FromPort': 22, 
				'ToPort': 22, 
				'CidrIpv4': '0.0.0.0/8', 
				'Description': 'test', 
				'Tags': []
			}
		], 
	'ResponseMetadata': 
		{
			'RequestId': '00000000-0000-0000-0000-000000000000', 
			'HTTPStatusCode': 200, 
			'HTTPHeaders': 
				{
					'x-amzn-requestid': '00000000-0000-0000-0000-000000000000', 
					'cache-control': 'no-cache, no-store', 
					'strict-transport-security': 'max-age=31536000; includeSubDomains', 
					'vary': 'accept-encoding', 
					'content-type': 'text/xml;charset=UTF-8', 
					'transfer-encoding': 'chunked', 
					'date': 'Sun, 02 Oct 2022 06:11:53 GMT', 
					'server': 'AmazonEC2'
				}, 
				'RetryAttempts': 0
		}
}

ユーザーの判定

事前に取得したプリンシパルID(principalId)が定義していおいたIDと等しいかどうかを判定します。

等しい場合は、操作が許可された(メンテナンス用あるいは開発用など)ユーザーなので削除対応は不要であると判断します。

セキュリティグループルールの削除

セキュリティグループルールを削除します。

対象のルールがインバウンドルールかアウトバウンドルールかは事前に確認済みですので、インバウンドルールであれば「revoke_ingress」を、アウトバウンドルールであれば「revoke_egress」を実行します。

インバウンドルールを削除
security_group = ec2.SecurityGroup(securityGroupId)
response = security_group.revoke_ingress(
    SecurityGroupRuleIds=[
        securityGroupRuleId,
        ]
    )
アウトバウンドルールを削除
security_group = ec2.SecurityGroup(securityGroupId)
response = security_group.revoke_egress(
    SecurityGroupRuleIds=[
        securityGroupRuleId,
        ]
    )

処理の概要は以上です。

その他注意点

Lambdaが無限ループに陥るバグが存在しないかは気を付けましょう。

今回の検証で削除イベントは検知対象にしていないためループに陥る可能性は低いです。

例えば削除されたルールを元に戻す処理も実装する場合はサブスクリプションフィルターのフィルターパターンに削除イベントも追加します。

Lambdaによるセキュリティグループルール削除もLambdaを実行するトリガーになるので「削除→戻し→削除」の無限ループが発生する可能性があります。Lambdaの処理の中でループに陥らないように、principalIdがLambdaに割り当てているロールと一致する場合はスキップするなどの処理を忘れないようにしましょう。

Configログをトリガーにした場合は、イベントを限定してLambdaを実行できません。サブスクリプションフィルターの機能を利用することでイベントの選定が容易な構成となっています。

検証結果

長々と概要説明してきましたが、実際に検証した結果をまとめていきます。

検証用に以下の2つのユーザーを用意しました。

  • 変更作業用ユーザー:ark01
  • 参照用ユーザー:ark02

「ark01ユーザー」が開発業務やメンテナンスの中でリソースの設定変更を担当するので、「ark01ユーザー」による操作は想定内のものとして、自動修復(ルール削除)はしません。

一方で「ark02ユーザー」による変更作業は想定外のものとして扱い自動修復(ルール削除)を実行します。

ark02 ユーザーでルールを追加する

アウトバウンドルールを追加します。
80番ポートで任意の宛先へのアクセスを許可するルール(HTTP 80 0.0.0.0/0)を追加。

HTTP 80 0.0.0.0/0のプレビュー画面
プレビュー画面

セキュリティグループルールID「sgr-0bc3e63e463231fb8」のルールが追加されました。

HTTP 80 0.0.0.0/0のルール追加後の画面
ルール追加後の画面

AuthorizeSecurityGroupEgress イベントが記録される

ルール追加操作の結果、CloudTrailイベントに「AuthorizeSecurityGroupEgress」のイベントログが記録されます(21行目)。不要なIDはマスキングしています。

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "IAMUser",
        "principalId": "AIDZZZZZZZZZZZZZZZZZZ",
        "arn": "arn:aws:iam::111111111111:user/falsification-readonly",
        "accountId": "111111111111",
        "accessKeyId": "XXXXXXXXXXXXXXXXXXXX",
        "userName": "falsification-readonly",
        "sessionContext": {
            "sessionIssuer": {},
            "webIdFederationData": {},
            "attributes": {
                "creationDate": "2022-09-29T08:31:01Z",
                "mfaAuthenticated": "false"
            }
        }
    },
    "eventTime": "2022-09-29T08:40:58Z",
    "eventSource": "ec2.amazonaws.com",
    "eventName": "AuthorizeSecurityGroupEgress",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "AWS Internal",
    "userAgent": "AWS Internal",
    "requestParameters": {
        "groupId": "sg-xxxxxxxxxxxxxxxxx",
        "ipPermissions": {
            "items": [
                {
                    "ipProtocol": "tcp",
                    "fromPort": 80,
                    "toPort": 80,
                    "groups": {},
                    "ipRanges": {
                        "items": [
                            {
                                "cidrIp": "0.0.0.0/0",
                                "description": "test"
                            }
                        ]
                    },
                    "ipv6Ranges": {},
                    "prefixListIds": {}
                }
            ]
        }
    },
    "responseElements": {
        "requestId": "00000000-0000-0000-0000-000000000000",
        "_return": true,
        "securityGroupRuleSet": {
            "items": [
                {
                    "groupOwnerId": "111111111111",
                    "groupId": "sg-xxxxxxxxxxxxxxxxx",
                    "securityGroupRuleId": "sgr-yyyyyyyyyyyyyyyyy",
                    "description": "test",
                    "isEgress": true,
                    "ipProtocol": "tcp",
                    "fromPort": 80,
                    "toPort": 80,
                    "cidrIpv4": "0.0.0.0/0"
                }
            ]
        }
    },
    "requestID": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
    "eventID": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
    "readOnly": false,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "111111111111",
    "eventCategory": "Management",
    "sessionCredentialFromConsole": "true"
}

サブスクリプションフィルターがイベントを検知してLambdaが実行される

さきほどのルール追加ログの「AuthorizeSecurityGroupEgress」イベントをサブスクリプションフィルターが検知し、Lambdaが実行されて該当ルールが削除されます。

Lambdaによる削除処理の結果として「RevokeSecurityGroupEgress」イベントが記録されます(26行目)。purincipalIdがLambda関数名となっています(5行目)。

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "AROXXXXXXXXXXXXXXXXXXX:falsification-remediation",
        "arn": "arn:aws:sts::111111111111:assumed-role/falsification-remediation-role-XXXXXXX/falsification-remediation",
        "accountId": "111111111111",
        "accessKeyId": "XXXXXXXXXXXXXXXXXXXX",
        "sessionContext": {
            "sessionIssuer": {
                "type": "Role",
                "principalId": "AROAXXXXXXXXXXXXXXXXXXXX",
                "arn": "arn:aws:iam::111111111111:role/service-role/falsification-remediation-role-XXXXXXX",
                "accountId": "111111111111",
                "userName": "falsification-remediation-role-XXXXXXXX"
            },
            "webIdFederationData": {},
            "attributes": {
                "creationDate": "2022-09-29T08:43:31Z",
                "mfaAuthenticated": "false"
            }
        }
    },
    "eventTime": "2022-09-29T08:43:33Z",
    "eventSource": "ec2.amazonaws.com",
    "eventName": "RevokeSecurityGroupEgress",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "xx.xx.xx.xx",
    "userAgent": "Boto3/1.20.32 Python/3.9.13 Linux/4.14.255-276-224.499.amzn2.x86_64 exec-env/AWS_Lambda_python3.9 Botocore/1.23.32 Resource",
    "requestParameters": {
        "groupId": "sg-xxxxxxxxxxxxxxxxx",
        "ipPermissions": {},
        "securityGroupRuleIds": {
            "items": [
                {
                    "securityGroupRuleId": "sgr-yyyyyyyyyyyyyyyyy"
                }
            ]
        }
    },
    "responseElements": {
        "requestId": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
        "_return": true
    },
    "requestID": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
    "eventID": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
    "readOnly": false,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "111111111111",
    "eventCategory": "Management",
    "tlsDetails": {
        "tlsVersion": "TLSv1.2",
        "cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
        "clientProvidedHostHeader": "ec2.ap-northeast-1.amazonaws.com"
    }
}

2つのイベントログのeventTimeを比較して、ユーザーがルールを追加してからLambdaによって対象のルールが削除されるまでおおよそ3分で完了しています。

イベント発生から検知・修復までの時間はCloudTrailがログを出力するまでの時間に依存します。AWSからは、ログ出力までの時間は平均5分以内、遅くとも30分以内、ただし保証されるものではないという説明がされています。

イベント検知・修復の方式としてこの仕様を許容できるかどうかは、仕組みを採用する判断基準になるでしょう。

CloudTrail は、通常、API コールから平均 5 分以内にログを配信します。この時間は保証されません。詳細については、「AWS CloudTrail サービスレベルアグリーメント」をご覧ください。インサイトイベントは、通常、異常なアクティビティから 30 分以内にバケットに配信されます。

CloudTrail ログファイルの取得と表示

Configのマネージドルールを使った自動修復も数分かかるので、同程度のレスポンスタイムで検知から修復まで完了できました。

Lambda実行後、セキュリティグループのアウトバウンドルールを見るとルールが空になっています。

Lamdaによるrevoke後の状態

もう1つのユーザーである「ark01ユーザー」で同様の操作をした場合は、Lambdaは実行されますが特定ユーザーであると評価して削除処理は実行されないことを確認しました。

まとめ

本記事ではITシステム監査の統制施策の1つとして「セキュリティグループの変更検知・修復」について検証した内容をまとめました。

さまざまな脅威に対して「予防」「発見」「是正」の施策を用意し、それらが正しく機能する環境を用意することが重要です。

「予防」「発見」の施策は整備できているが「是正的な統制」については運用の中で対応することが多く、自動的に対応する機能・仕組みはまだこれからという場合がほとんどではないでしょうか。

クラウドサービス利用の環境ではマネージドサービスを活用することで容易に実現できるものもありますので、まずはそういった仕組みから取り入れて、段階的にシステムに適した仕組みを構築していく中で、今回検証したような自動化の方法を参考にしていただけると幸いです。

アークシステムではセキュリティ要求の高い金融のお客様でのAWSインフラ構築支援で多数実績がございます。

運用まで見据えしっかりしたAWS環境構築とセキュリティ対策をご要望のお客様はぜひご相談ください。

  • 株式会社アークシステムの予約・来訪管理システム BRoomHubs
  • 低コスト・短納期で提供するまるごとおまかせZabbix