はじめに
ここ数年、ランサムウェアや認証情報の窃取など、サイバー攻撃のニュースを見ない日はありません。インシデントが起きたとき、最初に問われるのは「何が起きたのか」「どこまで影響があるのか」を素早く把握し、対応方針を決めることです。
ただ、この初動調査は意外と手間がかかります。CloudTrailで変更者を探し、影響を受けたリソースを洗い出し、フローログで実際の通信を確認し……と、慣れていても数十分はかかります。
そこで今回は、AWS DevOps Agentにセキュリティインシデントの初動調査を任せてみました。AWSが公式ブログとあわせて公開しているサンプルを使い、東京リージョンで「セキュリティグループの全開放」を再現して、Agentがどこまで調べられるかを採点しています。
この記事でやること
- AWS DevOps Agentのセキュリティでの役割を整理する
- 公式サンプルを東京リージョンにデプロイする(そのままでは動かなかったので、修正点も紹介します)
- セキュリティグループをわざと全開放し、DevOps Agentに調査させる
- 調査結果を採点し、できたこと・できなかったことをまとめる
この記事でやらないこと
- アラームからDevOps Agentの調査を自動で始める連携(Webhook)の構築
- 公式サンプルのほか2つのシナリオ(認証情報の悪用、APIへの攻撃)
AWS DevOps Agentとは
AWS DevOps Agentは、障害対応や運用業務を支援するAIエージェントです。アラートやインシデントをきっかけに調査を始め、テレメトリ、デプロイ、設定変更の記録を突き合わせて、根本原因と対応計画を出してくれます。
セキュリティでの役割
先に押さえておきたいのは、DevOps Agentは脅威を「検知」しないということです。公式ブログでは、役割分担が次のように整理されています。
| 役割 | 担当するサービス |
|---|---|
| 脅威の検知 | Amazon GuardDuty |
| セキュリティ態勢の管理 | AWS Security Hub |
| 検出結果に基づくケース管理 | AWS Security Incident Response |
| 自分で作ったアラームを起点にした調査 | AWS DevOps Agent |
DevOps Agentがセキュリティの調査で答えるのは、次の4つの問いです。
- 誰がやったのか
- 何を変えたのか
- 何が晒されたのか
- 実際に悪用されたのか
また、DevOps Agentは対応策を「提案」するだけで、自分では実行しません。セキュリティインシデントでは、慌ててキーを無効化したり設定を戻したりすると、証拠が消えたり攻撃者に気づかれたりするおそれがあります。実行の判断を人間に残すのは、理にかなった設計です。
料金
エージェントが作業した時間に応じた、秒単位の課金です。新規利用者には2か月の無料トライアルがあり、調査(インシデント対応)は月20時間、チャット(オンデマンドのSREタスク)は月20時間まで無料です。調査中に使うCloudWatch Logs Insightsのクエリなど、ほかのサービスの料金は別にかかります。
検証構成
AWSの公式サンプルaws-samples/sample-devops-agent-security-triageを使います。DevOps Agent自体は含まれず、Agentが調べるための「証拠の出どころ」と、攻撃シナリオの舞台を作るテンプレートです。
| 区分 | 主なリソース | 役割 |
|---|---|---|
| 監査ログ | CloudTrailの証跡(S3とCloudWatch Logsの両方に配信) | 「誰が何をしたか」の証拠 |
| 監視 | メトリクスフィルタ、CloudWatchアラーム、SNSトピック | 怪しい操作を検知する |
| ネットワーク | VPC、プライベートサブネット、セキュリティグループ、EC2(t3.micro)、VPCフローログ | 今回のシナリオの舞台 |
| その他 | KMSキー、デモ用IAMユーザー、API Gateway+Lambdaなど | ほか2つのシナリオ用 |
今回試すのは、3つあるシナリオのうち 「セキュリティグループの全開放」 です。スクリプトが、EC2に付いたセキュリティグループのポート22(SSH)を0.0.0.0/0に開けます。
このシナリオでは、CloudTrailのログに対して、次の条件で反応するメトリクスフィルタが設定されています。
{ ($.eventName = "AuthorizeSecurityGroupIngress") && ($.requestParameters.ipPermissions.items[0].ipRanges.items[0].cidrIp = "0.0.0.0/0") }
なお、EC2にはパブリックIPがなく、VPCにもインターネットゲートウェイがありません。全開放しても、実際には外から届かない状態です。そのため、Agentが正しく調べられれば「晒されたが、悪用はされていない」という結論になるはずです。
サンプルのREADMEにあるとおり、検証用のサンドボックスアカウントで実行してください。デモ用のIAMユーザーやアクセスキー、全開放されたセキュリティグループなど、意図的に危険な状態を作ります。
手順
作業はすべて、東京リージョンのCloudShellとマネジメントコンソールで行います。
1. Agent Spaceを作成する
DevOps Agentのコンソールで「エージェントスペースを作成」を選びます。
- エージェントスペース名:
security-triage-lab - エージェントの応答言語:日本語
プライマリアカウントのロールは「新しいDevOpsエージェントロールを自動作成」を選びます。Agentは、このロールの権限でCloudTrailやログを読みに行きます。
「ウェブアプリを有効にする」も、ロールを自動作成にします。このウェブアプリ(オペレーターアプリ)が、調査を依頼したり結果を見たりするための作業画面です。
作成すると、このアカウントがプライマリソースとして自動で関連付けられます。「アクションロールのステータス」が「未設定」になっていますが、これはAgentがリソースを変更する権限を持っていない、ということです。今回は調査だけを任せるので、このままで構いません。
2. 公式サンプルを修正してデプロイする
CloudShellでサンプルを取得します。
git clone https://github.com/aws-samples/sample-devops-agent-security-triage.git
cd sample-devops-agent-security-triage
公式サンプルのままでは動かなかった
template.yamlをそのままデプロイしたところ、2か所でエラーになりました。
① S3バケットのACL設定
Bucket cannot have ACLs set with ObjectOwnership's BucketOwnerEnforced setting
2023年4月以降、新しく作るS3バケットはACLが無効(BucketOwnerEnforced)がデフォルトです。テンプレートでは、アクセスログ用バケットにAccessControl: LogDeliveryWriteというACLを設定しようとしていて、これが拒否されました。
ACLを削除し、代わりにバケットポリシーで、S3のサーバーアクセスログの書き込みを許可するよう修正しました。
- Sid: S3ServerAccessLogsPolicy
Effect: Allow
Principal:
Service: logging.s3.amazonaws.com
Action: s3:PutObject
Resource: !Sub '${LoggingBucket.Arn}/*'
Condition:
ArnLike:
aws:SourceArn:
- !Sub 'arn:${AWS::Partition}:s3:::${NamePrefix}-lab-data-${AWS::AccountId}-${AWS::Region}'
- !Sub 'arn:${AWS::Partition}:s3:::${NamePrefix}-trail-${AWS::AccountId}-${AWS::Region}'
StringEquals:
aws:SourceAccount: !Ref 'AWS::AccountId'
あわせて、ほかのバケットのAccessControl: Privateと、ログ用バケットが自分自身にアクセスログを出す設定も削除しました。
② VPC内のLambdaの実行ロール
The provided execution role does not have permissions to call CreateNetworkInterface on EC2
シナリオ3用のLambdaはVPC内に作られます。テンプレートでは、ネットワークインターフェースを作る権限に「このVPCの中だけ」という条件(ec2:Vpc)が付いていて、Lambda作成時の権限チェックを通りませんでした。
条件付きの権限を削除し、AWS管理ポリシーAWSLambdaVPCAccessExecutionRoleを付ける形に修正しました。
ManagedPolicyArns:
- !Ref PaymentApiPolicy
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole'
デプロイ
修正したテンプレートでデプロイします。
aws cloudformation deploy \
--template-file template.yaml \
--stack-name devops-agent-security-triage \
--capabilities CAPABILITY_NAMED_IAM \
--region ap-northeast-1 \
--parameter-overrides DemoUserPassword='<強いパスワード>' NamePrefix=sectriage LogRetentionDays=7
-
NamePrefixはsectriageのままにしてください。テンプレート内で、KMSキーのエイリアスalias/sectriage-lab-keyが直書きされています - CloudShellでは、改行(
\)が崩れることがあります。うまくいかない場合は1行にして貼り付けてください
既にCloudTrailの証跡がある環境では、このサンプルの証跡が2本目になります。管理イベントは最初の1コピーだけが無料で、2本目以降は10万件あたり2ドルかかります。検証が終わったら早めに削除しましょう。
3. スクリプトの設定
サンプルのスクリプトは、リージョンの初期値がus-east-1、スタック名の初期値がdevops-agent-security-triageになっています。東京リージョンで使うには、環境変数で上書きします。
export STACK=devops-agent-security-triage REGION=ap-northeast-1
CloudShellのセッションが切れると、この設定は消えます。スクリプトが「Stack with id ... does not exist」と言い出したら、もう一度設定してください。
4. セキュリティグループを全開放する
./scripts/scenario2-open-security-group.sh
ポート22が0.0.0.0/0に開きました。CloudTrailのログが届くまで5分ほど待つと、アラームが鳴ります。
5. DevOps Agentに調査を依頼する
本番運用では、アラームからWebhook経由で自動的に調査を始められますが、今回はオペレーターアプリから手動で依頼します。
オペレーターアプリの「インシデントレスポンス」を開き、「調査を開始」を選びます。
依頼文には、公式サンプルのプロンプトを日本語にしたものを使いました。
アカウント <アカウントID>、ap-northeast-1 のセキュリティグループが 0.0.0.0/0 に開放されました。
変更を行ったプリンシパル、時刻、送信元IPを特定してください。
そのセキュリティグループに紐づくすべてのリソースを一覧にしてください。
次に、変更以降に対象ポートで受理されたインバウンドトラフィックをVPCフローログで確認し、インスタンスに何か到達したかを教えてください。
証拠の場所:
- CloudTrailの管理イベント:ロググループ /aws/cloudtrail/sectriage
- VPCフローログ:ロググループ /aws/vpc/flowlogs/sectriage
- セキュリティグループ:<セキュリティグループID>
- 紐づくインスタンス:<インスタンスID>
「その他のオプション」で、調査の名前と優先度(今回は「高」)を設定できます。
調査結果
約11分で調査完了
02:43:13に調査を始め、02:54:31に完了しました。かかった時間は約11分です。
概要
Agentの結論は次のとおりです。
-
インシデント:
sectriage-demo-sgで、SSH(22/tcp)が0.0.0.0/0に開放された。対象はインスタンス1台のみ。インターネットゲートウェイのルートもパブリックIPもなく、インターネットからは到達不可能で、フローログ上の着信もゼロ - 原因:IAMユーザーが、CloudShellから手動でルールを追加した(CloudFormationの定義からのドリフト)
- 緩和策:追加されたルールだけを削除し、テンプレートどおりのインバウンドなしに戻す計画を提案済み(未実行・担当者判断)。再発防止として、AWS Configの自動是正と権限の制限を推奨
- 重大度:medium
根本原因
根本原因タブには、変更したIAMユーザーのARN、実行元(CloudShellのaws-cli)、CloudTrailのイベントID、時刻、送信元IPまで記載されていました。
また、私が検証の途中でやり直したため、セキュリティグループは2回開放されています。Agentは、1回目(16:51〜17:08Z)と2回目(17:37Z〜)の両方をとらえていました。
緩和計画
緩和計画は、次の5段階に分かれた手順書の形で提案されました。
- prepare:現在のルールを保存
- pre validate:影響範囲と依存通信を確認
- apply:ドリフトしたルールを削除
- post validate:状態とドリフト解消を確認
- rollback:VPCのCIDRに限定してSSHを戻す(承認時のみ)
いきなり「ルールを消しましょう」ではなく、事前の保存や事後の確認、ロールバックまで含めているのは、運用の現場の感覚に近いと感じました。
調査タイムライン
タイムラインを見ると、Agentは最初にdescribe_security_groupsやdescribe_network_interfaces、describe_instancesで今の状態を確認し、そのあとCloudTrailで過去の記録をたどっていました。
チャットで再発防止策を聞いてみた
調査画面から、追加で質問できます。「同じことが再発しないように、どんな対策をすべき?」と聞いてみました。
Agentは、今ある検知・防止の仕組みを調べたうえで、3つの弱点を挙げました。
- 検知はできるが、元に戻す仕組みがない:アラームが鳴るだけで、開いたルールは開いたまま
-
SSHの全開放をチェックするConfigルールがない:AWS Config自体は動いているが、
restricted-sshルールは設定されていない - 検知の条件に抜け穴がある
3つ目が、今回一番の発見です。
メトリクスフィルタは、1回の操作で追加されたルールのうち「1番目」しかチェックしていません(条件のitems[0]の部分)。そのため、複数のルールをまとめて追加し、0.0.0.0/0を2番目以降に入れると、アラームは鳴りません。
| 1回の操作で追加したルール | アラーム |
|---|---|
1番目に0.0.0.0/0
|
鳴る |
1番目に10.20.0.0/16、2番目に0.0.0.0/0
|
鳴らない |
Agentは、フィルタの定義を読んで、この抜け穴を自分で見つけてきました。
対策としては、AWS Configのマネージドルールrestricted-ssh(INCOMING_SSH_DISABLED)と、Systems Manager Automationによる自動修復をIaCで追加することを提案していました。
採点
| 項目 | 結果 | Agentの回答 |
|---|---|---|
| 変更したのは誰か | ◯ | IAMユーザーを特定 |
| いつ変更したか | ◯ | 2回の開放を、時刻つきで両方とらえた |
| どこから変更したか | ◯ | CloudShell(aws-cli)からの実行、送信元IPまで特定 |
| 影響を受けたリソース | ◯ | 対象はインスタンス1台のみと特定 |
| 実際に通信が届いたか | ◯ | IGW・パブリックIPなし、フローログの着信ゼロから「到達不可」と判断 |
想定していた「晒されたが、悪用はされていない」という結論に、きちんとたどり着きました。
できなかったこと・気になったこと
-
変更したユーザーの権限までは調べられなかった:そのユーザーが、本来セキュリティグループを変更してよい立場かどうかが分かれば、「担当者の操作ミス」なのか「不正な操作」なのかを見分ける手がかりになります。ところが、Agentのロールには、ユーザーに付いているポリシーを見る権限(
iam:ListAttachedUserPoliciesなど)がなく、ここは調べられませんでした。Agent自身も、タイムラインに「ギャップ(調べられなかったこと)」として記録しています。ここまで調べさせたい場合は、Agentのロールに、IAMの読み取り権限を追加します - 調査の質は、ログの質に左右される:今回はCloudTrailとフローログが揃っていたので、すべての問いに答えられました。ログが欠けていれば、当然答えられません
後片付け
サンプルのteardown.shで、バケットを空にしてからスタックを削除できます。
./scripts/teardown.sh
Agent Spaceは、使っていないときには課金されません。調査結果を見返したい間は残しておき、不要になったら削除してください。KMSキーは、スタック削除後に削除待ちになり、最低7日後に削除されます。
まとめ
- AWS DevOps Agentは、脅威を検知するのではなく、アラームを起点に「誰が・何を・何が晒されたか・悪用されたか」を調べる役割
- セキュリティグループの全開放を、約11分で、変更者・送信元・影響範囲・実害の有無まで特定できた
- 緩和計画は手順書の形で提案され、実行の判断は人間に残される
- 再発防止を聞くと、公式サンプルの検知ロジックの抜け穴まで指摘してきた
- 公式サンプルは、S3のACLとLambdaの権限の2か所を直さないと、そのままではデプロイできなかった
初動調査にかかる時間を短くできれば、そのぶん「どう対応するか」を考える時間に回せます。人間が判断するための材料を、素早く正確に揃えてくれる相棒として、十分に使えると感じました。
次の記事では、GuardDutyの検出結果を調査するための専用エージェント、GuardDuty investigation agentを試してみる予定です。













