はじめに
AWSを使っていると、こんな経験はないでしょうか。
- 離任したメンバーが作ったEC2がいつの間にか動き続けていた
- 「テスト用」のつもりで作ったRDSがそのまま残っていた
- 何のために作ったのかわからないリソースが増えていく
タグで管理すれば解決できる話ですが、「タグを付けるルール」はあっても「タグがなければ動かせない仕組み」がない限り、人は必ずミスをしてしまうものだと思います。
AWS OrganizationsのSCPを使えばタグなしリソースの作成を組織レベルで拒否できますが、個人・スタートアップ・小規模チームにはオーバースペックです。
そこで、SCPなしでもタグ管理によるセキュリティガバナンスを実現する TagWatchman を作りました。
なぜ作ったか
タグ管理の問題は単なるコスト管理の話ではありません。
セキュリティ的に見ると、「誰が何のために作ったかわからないリソース」は攻撃者がバックドアとして作ったインスタンスかもしれません。 管理外リソースは:
- パッチが当たらない
- 監査されない
- ログが見られない
つまり、「存在を把握できていないこと」自体がリスクです。
既存のOSSツールをいくつか調べましたが、いずれも:
- 定期スキャン型(数時間遅延がある)
- 対応サービスがEC2・RDS程度に限定
- 隔離フェーズがなく、通知か即削除の二択
- 導入が複雑
という課題がありました。これを解決するものを作ろうと思ったのが出発点です。
アーキテクチャ
Lambda 8関数の役割
| Lambda | 役割 |
|---|---|
| Detector | CloudTrailイベントからARNを抽出・タグチェック |
| Isolator | ネットワーク隔離(SG差し替え / ポリシー封鎖) |
| Notifier | メール通知(検知時・承認依頼時) |
| Recheck | 7日後にタグを再チェック |
| Restorer | タグが付与されたら隔離解除 |
| Approver | 承認URLクリックを処理 |
| Deleter | リソース削除 |
| CloudTrail Guardian | CloudTrailの無効化・削除を監視 |
工夫したポイント
① 即削除ではなく「隔離→承認→削除」
最初は検知したら即削除する設計を考えましたが、これは運用事故のリスクが高すぎると判断しました。
タグの付け忘れという操作ミスや、CI/CDが自動作成するリソースでタグ付与にラグがあるケースなど、即削除としてしまうとかえって事故発生のリスクがあると考えます。
そこで以下のフローにしました:
検知 → 即時隔離(通信遮断)→ 7日間の猶予
↓ この間にタグを付与 → 自動復旧
↓ タグなしで7日経過 → 承認メール
↓ 人間がクリック → 削除
隔離はするが、削除は人間が判断する。 これが安全なバランスだと考えています。
ただし、一部AWSサービスは、セキュリテイもしくは技術的な観点からネットワーク隔離以外の対応をしています。詳細は③ 対応AWSサービスにて説明します。
② 隔離 = 停止ではなくネットワーク遮断
隔離の方法として「インスタンスのStop」も考えましたが、これも強硬手段すぎると判断しました。RDSはStop後の起動に時間がかかりますし、誤検知のときのダメージが大きいので...
「害を与えられない状態にする」であって「リソースの停止・消去」ではない という思想で、通信遮断(SGの差し替え・バケットポリシーの全拒否)を選びました。
元のSGはタグに保存しているので、タグを付与すれば自動で復旧します。
③ 対応AWSサービス
Resource Groups Tagging APIを使うことで、サービスごとの個別実装なしに全AWSサービスのタグを統一取得できます。
| カテゴリ | サービス | 隔離方法 | 復旧方法 |
|---|---|---|---|
| コンピューティング | EC2, Lambda, ECS, EKS, Workspaces | SGを全拒否に差し替え / 同時実行数を0に設定 | タグ付与で自動復旧 |
| データベース | RDS, DynamoDB, ElastiCache, Redshift | SGを全拒否に差し替え / リソースポリシーで全拒否 | タグ付与で自動復旧 |
| ストレージ | S3, ECR | バケット/リポジトリポリシーで全拒否 | タグ付与で自動復旧 |
| メッセージング | SQS, SNS, Kinesis | ポリシーで全拒否 | タグ付与で自動復旧 |
| 分析 | OpenSearch | ポリシーで全拒否 | タグ付与で自動復旧 |
| 分析(通知のみ) | Glue | 通知のみ ※1 | — |
| オーケストレーション | Step Functions | リソースポリシーで全拒否 | タグ付与で自動復旧 |
| ネットワーク | IGW, NAT Gateway, VPC Peering, ElasticIP | 条件付き即時削除 ※2 | — |
| ネットワーク(通知のみ) | VPC | 通知のみ ※3 | — |
| 認証・認可 | IAM Role, IAM User | ポリシー全剥奪 + アクセスキー無効化 ※4 | タグ付与でタグ削除(ポリシー再付与は手動) |
| API | API Gateway | ステージ削除によるエンドポイント無効化 | タグ付与で自動復旧 |
| 監査 | CloudTrail | 専用フロー(自動再有効化) | — |
※1 Glueを通知のみにする理由
- リソースポリシーがデータカタログ全体に適用されるため、database単位での隔離が不可能
※2 ネットワーク系(条件付き即時削除)の挙動
- IGW: アタッチなし → 即時削除 / アタッチあり → 通知のみ
- NAT Gateway / VPC Peering: 即時削除
- ElasticIP: アタッチなし → 即時解放 / アタッチあり → 通知のみ
※3 VPCを通知のみにする理由
- 削除すると内部の全リソースの通信が止まる。影響範囲が大きすぎるため自動対応不可
※4 IAMリソースの挙動
- ポリシー剥奪・キー無効化は自動で実施
- 復旧(ポリシー再付与・キー再作成)は人間が手動で対応が必要
- メール通知に手動対応が必要な旨を明記
④ CloudTrail Guardianで生命線を守る
TagWatchmanはCloudTrailのイベントを受け取って動いています。つまりCloudTrailが止まるとTagWatchman自体が機能しなくなってしまいます。
攻撃者がバックドアを仕込む際にまずやることのひとつが「ログの無効化」です。これに対応するため、CloudTrailの操作を専用のLambdaで監視しています。
StopLogging → 自動で再有効化 + 🚨 CRITICAL警告
DeleteTrail → 🚨 CRITICAL警告(再作成は人間が対応)
UpdateTrail → ⚠️ WARNING警告
PutEventSelectors → ⚠️ WARNING警告
⑤ 猶予期間はSSMで可変
デフォルト7日ですが、AWS Systems Manager Parameter Storeで変更できます。再デプロイ不要で簡単に実施できます。
タグバリデーションの設計
単純に「タグキーが存在するか」だけでなく、値のバリデーションも実装しました。
-
Envは許可値リストと照合 -
Projectは前方一致で柔軟に対応 -
Ownedは空文字のみNG(値は自由、チーム名を推奨)
設定はSSM Parameter Storeから変更でき、再デプロイ不要で即時反映されます。
詳細な設定方法は購入者向けのドキュメントにて記載しています。
実際に動かしてみた
タグなしでS3バケットを作成したところ、約1〜2分後に以下のメールが届きました。
- リソースのARN
- 操作の種類(CreateBucket)
- 実行者のIAMロール
- 不足しているタグ
- 自動で隔離(通信遮断)した旨
- 7日後に削除承認メールが届く旨
実環境でリアルタイムに動作することを確認できました。
導入方法
現在 v1.0.0 をリリース準備中です。
近日中に販売を開始予定です。
リリース通知を受け取りたい方は ⭐ Star をお願いします!
👉 https://github.com/takada-neko/tag-watchman
AWS利用料
TagWatchman自体の追加コストはほぼゼロです。
| サービス | 月額概算 |
|---|---|
| Lambda(8関数) | $0〜 |
| Step Functions | $0〜 |
| EventBridge | $0〜 |
| SNS | $0〜 |
| CloudTrail(未設定の場合) | $2〜 |
CloudTrailがすでに有効な環境では追加費用ゼロで導入できます。
おわりに
TagWatchmanはSCPが使いづらい小規模チームでも、タグ管理によるセキュリティガバナンスを実現することを目的に作りました。
「隔離してから人間が判断する」という設計思想により、誤検知が発生しても安全な構築・保守・運用を実現できます。
フィードバックもお待ちしています。
この記事はZennにも掲載しています。

