CloudWatch — ログ監視・メトリクスフィルター・異常検知
CloudWatch Logs/Alarms/Insights のセキュリティ監視設計、メトリクスフィルター、クロスアカウント集約を徹底解説
CloudWatch は AWS のネイティブな監視・ログ集約サービスだ。セキュリティ監視の要として、ログの収集・分析・アラート化を一元管理する。本講では、SCS-C03 で頻出される CloudWatch の設計パターンと落とし穴を徹底解説する。
CloudWatch セキュリティ監視パイプライン
Loading diagram...
クロスアカウント ログ集約アーキテクチャ
Loading diagram...
1. CloudWatch Logs — ログの構造と設計
ロググループ・ログストリームの階層設計
CloudWatch Logs は ロググループ と ログストリーム の階層構造を持つ。
階層構造:
- ロググループ(Log Group)
- ログストリーム(Log Stream) — アプリケーション・インスタンス・Lambda等
- ログイベント(Log Event) — 時系列のログメッセージ
- メタデータ — Timestamp, Message, Sequence Token
- ログストリーム(Log Stream) — アプリケーション・インスタンス・Lambda等
セキュリティ監視向けの設計例:
/aws/cloudtrail/prod→us-east-1→2026/04/27/.../aws/vpc/flowlogs/prod→eni-xxx-all-traffic/eni-yyy-rejected/aws/lambda/auth-handler→2026-04-27T12:34:56.789Z-$LATEST/2026-04-27T12:35:00.000Z-$LATEST/aws/waf/prod→regional-waf-logs
命名規則のベストプラクティス
| パターン | 推奨度 | 理由 |
|---|---|---|
/aws/service/account-id/region | ⭐⭐⭐ | 検索性が高く、複数アカウント集約に最適 |
/logs/env-service-component | ⭐⭐⭐ | 環境別・サービス別で自然なツリー構造 |
| 時系列パーティショニング | ⭐⭐ | クエリ範囲を狭められるが、過度な分割は避ける |
ロググループの保持期間設定
# AWS CLI: 30日保持に設定
aws logs put-retention-policy \
--log-group-name /aws/cloudtrail/prod \
--retention-in-days 30
# 無期限保持の設定(コスト注意)
aws logs put-retention-policy \
--log-group-name /aws/cloudtrail/prod \
--retention-in-days 0
費用最適化のポイント:
- 保持期間の短縮: セキュリティ規制を確認した上で、不要な長期保持は避ける
- ログフィルターでの絞り込み: エージェント側でノイズログを排除
- S3 への自動エクスポート: 長期保存が必要な場合は Kinesis Firehose で S3 にアーカイブ
2. メトリクスフィルター — セキュリティ脅威の自動検知
メトリクスフィルターは CloudWatch Logs をスキャンしてメトリクスに変換する。リアルタイムに異常を検知する仕組みの要だ。
2.1 不正な API コール検知
CloudTrail ログを監視して、認可されていない API 呼び出しを自動検知。
// CloudTrail ログの例(UnauthorizedOperation)
{
"eventVersion": "1.08",
"eventID": "abc123",
"eventSource": "ec2.amazonaws.com",
"eventName": "AuthorizeSecurityGroupIngress",
"awsRegion": "us-east-1",
"sourceIPAddress": "203.0.113.0",
"userAgent": "aws-cli/2.0.0",
"requestParameters": null,
"responseElements": null,
"errorCode": "UnauthorizedOperation",
"errorMessage": "User: arn:aws:iam::123456789012:user/attacker is not authorized..."
}
メトリクスフィルター設定
# CloudTrail ログから UnauthorizedOperation をメトリクス化
aws logs put-metric-filter \
--log-group-name /aws/cloudtrail/prod \
--filter-name unauthorized-api-calls \
--filter-pattern '[... , errorCode = "UnauthorizedOperation" || errorCode = "AccessDenied" , ...]' \
--metric-transformations \
metricName=UnauthorizedAPICallsMetric,\
metricNamespace=CloudTrailMetrics,\
metricValue=1
フィルターパターンの文法
// 単一フィールド マッチ
[... , errorCode = "UnauthorizedOperation" , ...]
// 複数条件の AND
[... , eventSource = "iam.amazonaws.com" , eventName = "DeleteUser" , ...]
// OR 条件(複数フィルターを組み合わせ)
フィルター 1: errorCode = "UnauthorizedOperation"
フィルター 2: errorCode = "AccessDenied"
→ アラームは OR で結合
// キーワード マッチ(完全一致)
[... , eventName = "ConsoleLogin" , errorCode = "Failed*" , ...]
2.2 ルートアカウント使用の検知
ルートアカウントの操作は実務では極力避けるべき。検知して即座にアラート。
// CloudTrail ログ(ルートアカウント)
{
"userIdentity": {
"type": "Root",
"principalId": "123456789012",
"arn": "arn:aws:iam::123456789012:root",
"accountId": "123456789012",
"invokedBy": "signin.amazonaws.com"
},
"eventName": "ConsoleLogin",
"sourceIPAddress": "203.0.113.0"
}
メトリクスフィルター設定
aws logs put-metric-filter \
--log-group-name /aws/cloudtrail/prod \
--filter-name root-account-usage \
--filter-pattern '[... , userIdentity.type = "Root" , ...]' \
--metric-transformations \
metricName=RootAccountUsageMetric,\
metricNamespace=CloudTrailMetrics,\
metricValue=1
2.3 IAM 変更の検知
IAM ポリシー・ロール・ユーザーの作成・削除・変更は監視対象の最重要項目。
# IAM 変更を検知
aws logs put-metric-filter \
--log-group-name /aws/cloudtrail/prod \
--filter-name iam-policy-changes \
--filter-pattern '[eventSource = iam.amazonaws.com && (eventName = PutUserPolicy || eventName = PutRolePolicy || eventName = PutGroupPolicy || eventName = DeleteUserPolicy || eventName = DeleteRolePolicy || eventName = DeleteGroupPolicy || eventName = AttachUserPolicy || eventName = AttachRolePolicy || eventName = DetachUserPolicy || eventName = DetachRolePolicy)]' \
--metric-transformations \
metricName=IAMPolicyChangesMetric,\
metricNamespace=CloudTrailMetrics,\
metricValue=1
試験で狙われるポイント
- ✅ メトリクスフィルターは ログ受信後にのみ適用される(過去ログには遡及しない)
- ✅ フィルターパターンは JSON フィールド マッチング が基本
- ✅ 複数フィルターを組み合わせる場合は複合アラームを使用
- ✅ メトリクスフィルター自体はフィルターパターン作成時点から有効化
3. CloudWatch Logs Insights — セキュリティ調査クエリ
Logs Insights は SQL ライクなクエリで大規模ログを高速検索する。セキュリティインシデント調査で強力。
3.1 セキュリティ調査用クエリ例
特定 IP からの失敗ログイン検索
fields @timestamp, userIdentity.principalId, sourceIPAddress, errorMessage
| filter eventName = "ConsoleLogin" and errorCode = "Failed*"
| stats count() as failed_attempts by sourceIPAddress
| filter failed_attempts > 5
特定期間の IAM 権限昇格の検知
fields @timestamp, userIdentity.principalId, eventName, requestParameters
| filter eventSource = "iam.amazonaws.com"
| filter eventName in ["PutUserPolicy", "AttachUserPolicy", "CreateAccessKey"]
| stats count() as permission_escalation_attempts by userIdentity.principalId, eventName
管理者権限の過度な使用検知
fields @timestamp, userIdentity.principalId, eventName, sourceIPAddress
| filter userIdentity.sessionContext.sessionIssuer.principalId = "*-admin*"
| filter eventSource in ["s3.amazonaws.com", "ec2.amazonaws.com", "iam.amazonaws.com"]
| stats count() as admin_actions by userIdentity.principalId, eventName
| filter admin_actions > 100
VPC Flow Logs から拒否されたトラフィック検索
fields @timestamp, srcaddr, dstaddr, dstport, action
| filter action = "REJECT"
| stats count() as rejected_packets by srcaddr, dstaddr, dstport
| filter rejected_packets > 1000
クロスアカウント API 呼び出し検索
fields @timestamp, userIdentity.arn, eventName, recipientAccountId
| filter userIdentity.accountId != recipientAccountId
| stats count() as cross_account_calls by userIdentity.arn, eventName
3.2 Logs Insights のクォータと制限
| 制限項目 | 値 | 備考 |
|---|---|---|
| クエリ実行時間 | 最大 15分 | タイムアウトを避けるため時間範囲を制限 |
| スキャン対象ログサイズ | 無制限 | 高速化のため絞り込み条件を活用 |
| 並列実行 | アカウント全体で最大 20 | 同時実行クエリを計画的に |
| クエリ結果行数 | 最大 10,000 行 | 統計処理で集約することで対応 |
4. サブスクリプションフィルター — リアルタイムログ処理
メトリクスフィルターは集計のみだが、サブスクリプションフィルターは詳細なログ処理を可能にする。
4.1 Lambda へのストリーミング
CloudWatch Logs から Lambda へログをリアルタイムに送信して、カスタムロジックで処理。
# Lambda 関数をターゲットとするサブスクリプションフィルター作成
aws logs put-subscription-filter \
--log-group-name /aws/cloudtrail/prod \
--filter-name stream-to-lambda-security-analysis \
--filter-pattern "[eventSource, eventName, errorCode, ...]" \
--destination-arn arn:aws:lambda:us-east-1:123456789012:function:SecurityAnalyzer
Lambda 関数の例(Python)
import json
import base64
import boto3
import urllib.parse
from datetime import datetime
sns_client = boto3.client('sns')
def lambda_handler(event, context):
# CloudWatch Logs からの圧縮データをデコード
compressed_data = base64.b64decode(event['awslogs']['data'])
# gzip で解凍
import gzip
log_data = gzip.decompress(compressed_data).decode('utf-8')
log_events = json.loads(log_data)['logEvents']
security_alerts = []
for log_event in log_events:
message = json.loads(log_event['message'])
# セキュリティ脅威の判定
if should_alert(message):
security_alerts.append({
'timestamp': datetime.fromtimestamp(log_event['timestamp'] / 1000).isoformat(),
'principal': message.get('userIdentity', {}).get('principalId'),
'event': message.get('eventName'),
'source_ip': message.get('sourceIPAddress'),
'error': message.get('errorCode')
})
# アラート送信
if security_alerts:
send_alert(security_alerts)
return {'statusCode': 200}
def should_alert(message):
# ルートアカウント検知
if message.get('userIdentity', {}).get('type') == 'Root':
return True
# 認可エラー検知
if message.get('errorCode') in ['UnauthorizedOperation', 'AccessDenied']:
return True
# IAM 変更検知
if message.get('eventSource') == 'iam.amazonaws.com':
return True
return False
def send_alert(alerts):
message = json.dumps(alerts, indent=2, ensure_ascii=False)
sns_client.publish(
TopicArn='arn:aws:sns:us-east-1:123456789012:security-alerts',
Subject='[Security Alert] CloudTrail 異常検知',
Message=message
)
4.2 Kinesis Firehose への送信
高スループットのログを Firehose で S3 にアーカイブ。リアルタイム分析と長期保存を両立。
# Kinesis Firehose をターゲットに設定
aws logs put-subscription-filter \
--log-group-name /aws/vpc/flowlogs/prod \
--filter-name stream-to-firehose \
--filter-pattern "[version, account, interface_id, srcaddr, dstaddr, srcport, dstport, protocol, packets, bytes, windowstart, windowend, action, tcpflags, type, pkt_srcaddr, pkt_dstaddr, flow_direction, traffic_type, ...]" \
--destination-arn arn:aws:kinesis:us-east-1:123456789012:firehose/vpc-flowlogs-archive
Firehose の変換ラムダ関数(メタデータ付与)
import json
import base64
def lambda_handler(event, context):
output = []
for record in event['records']:
# Base64 デコード
payload = json.loads(base64.b64decode(record['data']).decode('utf-8'))
# VPC Flow Logs フィールド定義
fields = payload.split(' ')
enhanced_log = {
'version': fields[0],
'account_id': fields[1],
'interface_id': fields[2],
'srcaddr': fields[3],
'dstaddr': fields[4],
'srcport': int(fields[5]),
'dstport': int(fields[6]),
'protocol': fields[7],
'packets': int(fields[8]),
'bytes': int(fields[9]),
'action': fields[12],
'threat_detected': is_suspicious(fields[3], fields[4], fields[12])
}
# 出力レコード
output_record = {
'recordId': record['recordId'],
'result': 'Ok',
'data': base64.b64encode(
(json.dumps(enhanced_log) + '\n').encode('utf-8')
).decode('utf-8')
}
output.append(output_record)
return {'records': output}
def is_suspicious(src, dst, action):
# ブロックリスト確認
blocklist = ['192.0.2.0/24', '198.51.100.0/24']
return action == 'REJECT' and any(ip_in_range(src, block) for block in blocklist)
def ip_in_range(ip, cidr):
# CIDR 判定ロジック(簡略版)
return True
5. クロスアカウント集約 — マルチアカウント環境での監視
複数の AWS アカウントからログを中央集約する。セキュリティ運用の要。
5.1 送信側アカウント(ログ送信元)
# CloudWatch Logs グループを作成
aws logs create-log-group \
--log-group-name /aws/cloudtrail/prod
# 送信側の IAM ロール作成(クロスアカウント)
cat > trust-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::CENTRAL-ACCOUNT:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "unique-external-id-12345"
}
}
}
]
}
EOF
aws iam create-role \
--role-name CloudTrailLogsToDestination \
--assume-role-policy-document file://trust-policy.json
# ロールにポリシー追加
cat > policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "logs:PutLogEvents",
"Resource": "arn:aws:logs:*:CENTRAL-ACCOUNT:log-group:/aws/cloudtrail/central:*"
}
]
}
EOF
aws iam put-role-policy \
--role-name CloudTrailLogsToDestination \
--policy-name AllowCloudTrailLogs \
--policy-document file://policy.json
5.2 受信側アカウント(ログ送信先)
# リソースベースのポリシーで送信元を許可
cat > resource-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::SOURCE-ACCOUNT-1:role/CloudTrailLogsToDestination",
"AWS": "arn:aws:iam::SOURCE-ACCOUNT-2:role/CloudTrailLogsToDestination"
},
"Action": [
"logs:PutLogEvents",
"logs:CreateLogStream"
],
"Resource": "arn:aws:logs:us-east-1:CENTRAL-ACCOUNT:log-group:/aws/cloudtrail/central:*",
"Condition": {
"StringEquals": {
"aws:SourceAccount": ["SOURCE-ACCOUNT-1", "SOURCE-ACCOUNT-2"]
}
}
}
]
}
EOF
aws logs put-resource-policy \
--policy-name AllowMultiAccountCloudTrail \
--policy-text file://resource-policy.json
5.3 送信側での設定(CloudTrail → 中央ロググループ)
# CloudTrail を更新して中央ロググループに送信
aws cloudtrail update-trail \
--name security-trail-prod \
--cloud-watch-logs-group-arn arn:aws:logs:us-east-1:CENTRAL-ACCOUNT:log-group:/aws/cloudtrail/central:* \
--cloud-watch-logs-role-arn arn:aws:iam::SOURCE-ACCOUNT:role/CloudTrailRole
クロスアカウント集約のベストプラクティス
| 項目 | 推奨 |
|---|---|
| 外部 ID | 必ず使用(セキュリティ向上) |
| リソースベースのポリシー | 中央で一元管理 |
| ロググループ命名 | /aws/central/account-id/region で アカウント特定可能に |
| メトリクスフィルター | 中央側で設定(単一管理) |
| タグ付与 | SourceAccount, SourceService を付与 |
6. 暗号化 — KMS 連携
CloudWatch Logs を KMS で暗号化。コンプライアンス要件や機密ログ保護が必須。
# KMS キー作成
aws kms create-key \
--description "CloudWatch Logs Encryption Key" \
--key-usage ENCRYPT_DECRYPT
KEY_ID=$(aws kms describe-key --key-id alias/cloudwatch-logs --query 'KeyMetadata.KeyId' --output text)
# KMS キー別名設定
aws kms create-alias \
--alias-name alias/cloudwatch-logs \
--target-key-id $KEY_ID
# CloudWatch Logs グループを KMS で暗号化
aws logs associate-kms-key \
--log-group-name /aws/cloudtrail/prod \
--kms-key-id arn:aws:kms:us-east-1:123456789012:key/$KEY_ID
KMS ポリシー(CloudWatch Logs サービスに権限を付与)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Allow CloudWatch Logs",
"Effect": "Allow",
"Principal": {
"Service": "logs.amazonaws.com"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:CreateGrant",
"kms:DescribeKey"
],
"Resource": "*",
"Condition": {
"ArnLike": {
"kms:ViaService": "logs.us-east-1.amazonaws.com"
}
}
}
]
}
暗号化時の注意点
- ✅ KMS キーとロググループが 同じリージョン に存在する必要あり
- ✅ CloudWatch Logs がキーに対して
Decrypt権限を持つ必要がある - ✅ 暗号化はログ取り込み時に自動的に適用される(遡及しない)
- ✅ クロスアカウント集約時は中央側の KMS キー使用
7. CloudWatch Alarms — セキュリティアラート設定
7.1 標準アラーム
メトリクスフィルターで生成されたメトリクスに基づくアラーム。
# UnauthorizedOperation メトリクスのアラーム
aws cloudwatch put-metric-alarm \
--alarm-name unauthorized-api-calls-alarm \
--alarm-description "Alert on unauthorized API calls" \
--metric-name UnauthorizedAPICallsMetric \
--namespace CloudTrailMetrics \
--statistic Sum \
--period 300 \
--threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold \
--evaluation-periods 1 \
--alarm-actions arn:aws:sns:us-east-1:123456789012:security-alerts
7.2 複合アラーム(Composite Alarms)
複数のアラームを組み合わせて、より複雑な条件でアラート。
# 複合アラーム: IAM 変更 AND UnauthorizedOperation の場合
aws cloudwatch put-composite-alarm \
--alarm-name security-incident-escalation \
--alarm-description "Alert when IAM changes are followed by unauthorized calls" \
--alarm-rule "ALARM(iam-policy-changes-alarm) AND ALARM(unauthorized-api-calls-alarm)" \
--alarm-actions arn:aws:sns:us-east-1:123456789012:security-critical
7.3 異常検出アラーム(Anomaly Detection)
機械学習で過去のデータから異常パターンを学習・検出。
# 異常検出アラーム: CloudTrail イベント数の異常
aws cloudwatch put-metric-alarm \
--alarm-name cloudtrail-events-anomaly \
--alarm-description "Detect anomalies in CloudTrail event count" \
--comparison-operator LessThanLowerOrGreaterThanUpperThreshold \
--evaluation-periods 2 \
--threshold-metric-id e1 \
--metrics '[
{
"id": "m1",
"return_data": false,
"metric": {
"namespace": "AWS/CloudTrail",
"metric_name": "EventCount",
"stat": "Sum",
"period": 300
}
},
{
"id": "e1",
"expression": "ANOMALY_DETECTION_BAND(m1, 2)",
"return_data": true
}
]' \
--alarm-actions arn:aws:sns:us-east-1:123456789012:security-alerts
異常検出の仕組み
過去 2 週間のデータ → 機械学習モデル → 標準偏差を計算
正常範囲: 平均値 ± (標準偏差 × 係数)
異常判定: データが範囲外
例: API 呼び出し回数
- 平均: 1000 /日
- 標準偏差: 100
- 係数: 2
→ 正常範囲: 800 ~ 1200
→ 実際: 2000 → アラート!
7.4 セキュリティ用アラームパターン集
| 検知項目 | メトリクス | 閾値 | 通知先 |
|---|---|---|---|
| ルートアカウント使用 | RootAccountUsageMetric | >= 1 (5分) | Critical SNS |
| IAM ポリシー変更 | IAMPolicyChangesMetric | >= 1 (5分) | Critical SNS |
| 認可エラー | UnauthorizedAPICallsMetric | >= 5 (5分) | Warning SNS |
| コンソールログイン失敗 | ConsoleLoginFailureMetric | >= 3 (5分) | Warning SNS |
| API 呼び出し異常 | CloudTrailEventCount (異常検出) | 2σ | Info SNS |
8. CloudWatch Agent — ログ/メトリクス収集
CloudWatch Agent は EC2 / オンプレミスから詳細ログ・メトリクスを収集。旧 Logs Agent より高機能。
8.1 インストールと初期設定
# EC2 での CloudWatch Agent インストール (Amazon Linux 2)
wget https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm
sudo rpm -U ./amazon-cloudwatch-agent.rpm
# 設定ウィザード(対話形式)
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard
# または JSON 設定ファイルで直接設定
cat > /opt/aws/amazon-cloudwatch-agent/etc/cloudwatch-config.json << 'EOF'
{
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/log/secure",
"log_group_name": "/aws/ec2/secure-logs",
"log_stream_name": "{instance_id}",
"retention_in_days": 30
},
{
"file_path": "/var/log/audit/audit.log",
"log_group_name": "/aws/ec2/audit-logs",
"log_stream_name": "{instance_id}",
"retention_in_days": 90
}
]
}
}
},
"metrics": {
"namespace": "CustomMetrics",
"metrics_collected": {
"cpu": {
"measurement": [
{
"name": "cpu_usage_idle",
"rename": "CPU_IDLE",
"unit": "Percent"
}
],
"totalcpu": false,
"metrics_collection_interval": 60
},
"disk": {
"measurement": [
{
"name": "used_percent",
"rename": "DISK_USED",
"unit": "Percent"
}
],
"metrics_collection_interval": 60,
"resources": ["/"]
}
}
}
}
EOF
# Agent 起動
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \
-a fetch-config \
-m ec2 \
-s \
-c file:/opt/aws/amazon-cloudwatch-agent/etc/cloudwatch-config.json
8.2 セキュリティ監視用の設定
{
"logs": {
"logs_collected": {
"files": {
"collect_list": [
{
"file_path": "/var/log/secure",
"log_group_name": "/aws/ec2/security",
"log_stream_name": "{instance_id}-secure",
"retention_in_days": 90,
"timezone": "UTC"
},
{
"file_path": "/var/log/audit/audit.log",
"log_group_name": "/aws/ec2/security",
"log_stream_name": "{instance_id}-auditd",
"retention_in_days": 365,
"timezone": "UTC"
},
{
"file_path": "/var/log/apache2/access.log",
"log_group_name": "/aws/ec2/web-access",
"log_stream_name": "{instance_id}",
"retention_in_days": 30
},
{
"file_path": "/var/log/apache2/error.log",
"log_group_name": "/aws/ec2/web-errors",
"log_stream_name": "{instance_id}",
"retention_in_days": 30
}
]
}
}
},
"metrics": {
"metrics_collected": {
"netstat": {
"measurement": [
{"name": "tcp_established", "unit": "Count"}
],
"metrics_collection_interval": 60
}
}
}
}
8.3 CloudWatch Agent vs 旧 Logs Agent
| 機能 | CloudWatch Agent | Logs Agent |
|---|---|---|
| ログ収集 | ⭕ | ⭕ |
| メトリクス収集 | ⭕ | ❌ |
| マルチプラットフォーム | ⭕ (Linux/Windows/Mac) | ⭕ (Linux のみ) |
| 設定管理 | Systems Manager Parameter Store | 手動管理 |
| サポート状況 | アクティブ開発中 | サポート終了予定 |
推奨: 新規構築では CloudWatch Agent を使用
9. VPC Flow Logs との連携
VPC Flow Logs は ネットワークトラフィックを CloudWatch Logs に記録。セキュリティ分析の必須ソース。
9.1 VPC Flow Logs の有効化と CloudWatch Logs 送信
# VPC Flow Logs の作成
aws ec2 create-flow-logs \
--resource-type VPC \
--resource-ids vpc-12345678 \
--traffic-type ALL \
--log-destination-type cloud-watch-logs \
--log-group-name /aws/vpc/flowlogs/prod \
--deliver-logs-permission-iamrolesarn arn:aws:iam::123456789012:role/flowlogsRole \
--log-format '${version} ${account-id} ${interface-id} ${srcaddr} ${dstaddr} ${srcport} ${dstport} ${protocol} ${packets} ${bytes} ${start} ${end} ${action} ${tcp-flags} ${type} ${pkt-srcaddr} ${pkt-dstaddr}'
9.2 VPC Flow Logs の分析クエリ
// 拒否されたトラフィック検索
fields @timestamp, srcaddr, dstaddr, dstport, action
| filter action = "REJECT"
| stats count() as rejected_packets, sum(bytes) as total_bytes by srcaddr, dstaddr, dstport
| sort total_bytes desc
| limit 100
// ポートスキャンの検知
fields @timestamp, srcaddr, dstport, action
| filter action = "REJECT" and protocol = 6
| stats count() as connection_attempts by srcaddr, dstport
| filter connection_attempts > 100
| sort connection_attempts desc
// 異常なデータ転送の検知
fields @timestamp, srcaddr, dstaddr, bytes
| stats sum(bytes) as total_bytes by srcaddr, dstaddr
| filter total_bytes > 1000000000
| sort total_bytes desc
10. 制限事項と落とし穴
10.1 リアルタイム性の限界
ログ生成 → CloudWatch Logs 送信 → メトリクスフィルター適用 → メトリクス生成 → アラーム評価
遅延: 数秒~数分(ネットワーク遅延、バッチ処理の影響)
対策
- 高速検知が必要な場合は EventBridge ルール + Lambda を併用
- メトリクスフィルターだけに依存しない
10.2 メトリクスフィルターの制限
制限項目 値
フィルターパターン長 1024 文字
ロググループあたり 最大 10 個
メトリクスネームスペース 250 個
対策: 複数ロググループへの分散
# サービス別にロググループを分割
/aws/security/cloudtrail
/aws/security/config
/aws/security/guardduty
/aws/security/waf
10.3 Logs Insights のクォータ
最大実行時間: 15 分
最大結果行数: 10,000 行
同時実行数: アカウント全体で最大 20
対策
// 期間を絞る
| filter @timestamp > ago(1h) and @timestamp < ago(30m)
// 統計処理で集約
| stats count() as occurrences by field1, field2
| sort occurrences desc
| limit 1000
10.4 クロスリージョン制約
CloudWatch Logs はリージョンサービス。グローバル集約が必要な場合は複雑になる。
東京(ap-northeast-1) → Lambda → Kinesis → 中央リージョン → S3
バージニア(us-east-1) → Lambda → Kinesis ↓
シドニー(ap-southeast-2) → Lambda → Kinesis
Lambda で複数リージョンのログをまとめて送信
11. 統合アーキテクチャ — エンドツーエンド監視設計
セキュリティ監視のベストプラクティス統合例
┌─────────────────────────────────────────────────────────────────┐
│ Data Sources │
├─────────────────────────────────────────────────────────────────┤
│ CloudTrail → CloudWatch Logs │
│ VPC Flow Logs → CloudWatch Logs │
│ Config Changes → CloudWatch Logs │
│ GuardDuty Findings → CloudWatch Events │
│ WAF Logs → CloudWatch Logs │
└──────────────┬──────────────────────────────────────────────────┘
│
┌──────────────▼──────────────────────────────────────────────────┐
│ Processing Layer │
├─────────────────────────────────────────────────────────────────┤
│ 1. メトリクスフィルター(集計) │
│ - ルートアカウント → RootAccountUsageMetric │
│ - IAM 変更 → IAMPolicyChangesMetric │
│ - API エラー → UnauthorizedAPICallsMetric │
│ │
│ 2. サブスクリプションフィルター(詳細分析) │
│ - CloudWatch Logs → Lambda → 異常検知ロジック │
│ - CloudWatch Logs → Kinesis Firehose → S3 (長期保存) │
│ │
│ 3. Logs Insights(深掘り調査) │
│ - セキュリティ侵害の根因分析 │
│ - ユーザー行動の異常検知 │
└──────────────┬──────────────────────────────────────────────────┘
│
┌──────────────▼──────────────────────────────────────────────────┐
│ Alerting & Response │
├─────────────────────────────────────────────────────────────────┤
│ メトリクス → CloudWatch Alarm → SNS (Critical) │
│ → 複合アラーム → SNS (Escalation) │
│ → 異常検出 → Lambda (自動対応) │
│ │
│ SNS トピック: │
│ - security-critical: セキュリティ侵害対応チーム │
│ - security-warning: セキュリティ監視チーム │
│ - security-info: 定期レポート配信 │
└──────────────┬──────────────────────────────────────────────────┘
│
┌──────────────▼──────────────────────────────────────────────────┐
│ Long-term Storage & Audit │
├─────────────────────────────────────────────────────────────────┤
│ CloudWatch Logs (30日保持) → Kinesis Firehose │
│ ↓ │
│ S3 (年単位保持) │
│ ↓ │
│ Athena (クエリ) │
│ Lake Formation (governance) │
└─────────────────────────────────────────────────────────────────┘
試験で狙われるポイント
必出パターン 1: メトリクスフィルターの動作範囲
問題: CloudTrail ログが生成される前に作成したメトリクスフィルターは、過去ログに適用されるか?
答: ❌ 適用されない。メトリクスフィルターは 設定後のログのみ をスキャンする。
対策: 過去ログの分析は Logs Insights を使用。メトリクスフィルターは継続監視に使う。
必出パターン 2: クロスアカウント集約の認可
問題: 複数アカウント間でログを集約する際、必ず設定すべき 2 つの IAM ポリシーは?
答:
- 送信側: AssumeRole ポリシーで中央アカウントを許可
- 受信側: リソースベースのポリシーで送信側ロールを許可
// 受信側: ロググループのリソースポリシー
{
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::SOURCE:role/CloudTrailRole"
},
"Action": ["logs:PutLogEvents", "logs:CreateLogStream"],
"Resource": "arn:aws:logs:region:CENTRAL:log-group:*"
}
]
}
必出パターン 3: 暗号化時の KMS キーの条件
問題: CloudWatch Logs を KMS で暗号化する際、KMS キーが別リージョンにあるとどうなるか?
答: ❌ 失敗。CloudWatch Logs と KMS キーは 同じリージョン に存在する必要がある。
必出パターン 4: 異常検出アラームの学習期間
問題: 異常検出アラームはいつから機械学習を開始するか?
答: アラーム作成後 約 2 週間。それまでは十分な学習データがないため機能しない。
必出パターン 5: CloudWatch Agent の権限
問題: CloudWatch Agent が CloudWatch Logs にログを送信するために必要な IAM アクションは?
答:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogGroups",
"logs:DescribeLogStreams"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"cloudwatch:PutMetricData"
],
"Resource": "*"
}
]
}
まとめ
CloudWatch はセキュリティ監視の統合プラットフォーム。
| レイヤー | 機能 | 用途 |
|---|---|---|
| ログ集約 | CloudWatch Logs | 全セキュリティログの一元管理 |
| メトリクス化 | メトリクスフィルター | リアルタイム異常検知 |
| 詳細分析 | Logs Insights | セキュリティ侵害の根因追跡 |
| リアルタイム処理 | サブスクリプションフィルター | カスタム脅威検知ロジック |
| アラート・対応 | CloudWatch Alarms | セキュリティ体制の自動化 |
| 長期保存 | Kinesis Firehose → S3 | コンプライアンス対応 |
セキュリティ監視設計の原則
- 多層防御: メトリクスフィルター + Logs Insights + Lambda で多角的に監視
- スケーラビリティ: クロスアカウント集約で複雑性を管理
- 保持期間の最適化: 規制要件と コスト のバランス
- 自動対応: Lambda + SNS で人手を最小化
- 長期監査証跡: S3 + Athena で過去検証に耐える
CloudWatch はシンプルに見えて、細部に秘密あり。制限を理解し、統合アーキテクチャで補完する——それが実運用の秘訣だ。