日志存储与检索:S3 桶存储策略、CloudWatch Logs 订阅与查询

到了这一步,你手头应该有源源不断的 KMS 审计日志流了。但日志这东西,存不好等于白查。我见过太多案例——日志全在,可要拉出来分析时,不是权限不对就是被生命周期策略删光了。这一节咱们就聊聊怎么把日志安顿好,并且能在需要的时候秒级找出来。

S3 桶存储策略:不只是把日志丢进去

很多人以为日志往 S3 一存就完事了。其实不然。KMS 审计日志的敏感度很高,谁访问过哪个密钥、从哪里调用的,这些信息一旦泄露,攻击者就能摸清你的加密资产全貌。我个人的习惯是:日志桶一定要单独建,不跟其他数据混用。

生命周期管理

审计日志有合规保留期,比如金融行业要求至少保留 7 年。你不能让它无限膨胀,也不能提前删掉。配置生命周期时,建议这样搞:

  • 过渡:日志生成 30 天后,自动转为 Glacier 或 Deep Archive,降低存储成本。
  • 过期删除:保留期满后自动清理,避免手动运维。
  • 注意:千万别忘了加一条“禁止删除”的桶策略,我见过有人误删了整个桶,后来靠备份恢复了两天。
存储类别 适用阶段 成本 检索速度
S3 Standard 0-30 天内频繁查询 中等 毫秒级
S3 Glacier 30 天 - 7 年归档 极低 数分钟到数小时
S3 Glacier Deep Archive 7 年以上长期保存 最低 12 小时内

嗯,这里要注意:如果你需要经常回溯半年前的日志,建议保留一个“热副本”或者使用 S3 Intelligent-Tiering,否则每次从 Glacier 取数据都得等很久,排查时急死人。

桶策略与加密

桶策略是安全的第一道门。我通常会用一条显式拒绝策略,只允许特定 IAM 角色(比如安全专用角色)访问,同时禁止公共访问。还要开启S3 服务端加密(SSE-S3 或 SSE-KMS),避免日志明文存储。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "DenyPublicRead",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::audit-logs-bucket/*",
            "Condition": {
                "Bool": {
                    "aws:SecureTransport": "false"
                }
            }
        }
    ]
}

另外,记得开启S3 访问日志,记录谁对这个桶做了什么操作。你觉得麻烦?我吃过一次亏——有次排查日志发现桶里莫名多了几个文件,就是因为没开访问日志,导致溯源花了整整两天。

CloudWatch Logs 订阅与查询

KMS 审计日志默认会送到 CloudWatch Logs 的 /aws/kms/ 日志组里。但光躺着不行,你得让它流动起来。

订阅过滤器:把日志实时推出去

订阅过滤器的本质是一个“水龙头”,它把 CloudWatch Logs 的日志流实时复制到下游目的地。我一般会考虑三种目的地:

  • Lambda 函数:适合做自定义处理,比如格式转换、脱敏、推送告警。
  • Amazon Elasticsearch Service(现改名为 OpenSearch):适合全文搜索和可视化分析。
  • Kinesis Data Firehose:适合大规模分发到 S3、Redshift、Splunk 等。

你会问:选哪种好?我举个例子:假如你们安全团队习惯用 Kibana 看日志,那就直接推到 OpenSearch。如果只需要长期存储和偶尔用 Athena 查,那推到 S3 就够了。我个人做过一个项目,因为流量太大,直接推 S3 成本太高,后来加了 Firehose 做压缩和分桶,省了 40% 的费用。

# 示例:配置订阅过滤器到 Lambda(AWS CLI)
aws logs put-subscription-filter \
    --log-group-name "/aws/kms/key" \
    --filter-name "KMSLogForward" \
    --filter-pattern "" \
    --destination-arn "arn:aws:lambda:us-east-1:123456789012:function:KMSLogProcessor" \
    --role-arn "arn:aws:iam::123456789012:role/MySubscriptionRole"

CloudWatch Logs Insights:不建索引也能搜

如果你不想额外搭建 Elasticsearch,CloudWatch Logs Insights 就是个救命稻草。它基于查询引擎,能直接跑在原始日志上,不需要预先建索引。

我常用的几个查询模式:

  • 查某时间段内所有 KMS 操作:fields @timestamp, @message | filter @logStream like /key/ | sort @timestamp desc
  • 统计调用次数最多的用户:stats count() by userIdentity.arn
  • 定位“密钥被禁用”事件:fields @timestamp, userIdentity.arn, eventName | filter eventName == "DisableKey"

嗯,这里有个小坑:Logs Insights 查询的数据量是按扫描的日志量计费的。如果你不限制时间范围,一次查询扫描几十 G 日志,账单可就不好看了。我建议每次查询都加上 @timestamp >= '2025-01-01T00:00:00Z' and @timestamp < '2025-01-02T00:00:00Z' 这种精确范围。

小技巧:如果你发现查询超时或太慢,可以尝试把日志组按小时分桶。CloudWatch Logs 支持跨日志组查询,但尽量保持每个组的数据量不超过 10GB,否则性能会大幅下降。

一张图看懂日志存储与检索架构

说实话,光靠文字很难把整个链路串起来。下面这张图总结了我们刚才讲的所有组件之间的关系。

KMS 审计日志存储与检索架构 AWS KMS 审计日志 CloudWatch Logs 订阅过滤器(Subscription Filter) Lambda 处理函数 OpenSearch S3 长期存储 CloudWatch Logs Insights 说明:直接通过 Logs Insights 查询可不经过订阅过滤器,成本较低;订阅推送到 S3/OpenSearch 适合大规模长期分析。

核心要点:S3 桶要有明确的生命周期和加密策略,CloudWatch Logs 订阅过滤器是日志分发的枢纽,而 Logs Insights 适合临时性快速排查。三者配合好了,你才能做到“存得住、找得着、查得快”。

避坑指南:我曾经踩过的几个坑

  • 订阅过滤器权限不足:创建订阅时,如果你的 Lambda 或 Firehose 角色没有 logs:PutLogEvents 权限,日志根本推不过去。我被坑过一次,折腾了两个小时才发现是角色没配。
  • S3 桶跨区域复制导致日志顺序乱:如果你用跨区域复制做灾备,源区域的日志和复制后的日志时间戳可能不是全局有序的。排查时要注意区分区域。
  • Logs Insights 查询超时:默认查询超时时间是 15 分钟,数据量大时很容易超时。我现在的做法是先用 head 100 看看数据格式,再精确限制时间范围。

警告:千万不要把 KMS 审计日志的 S3 桶设置成公开可读,哪怕只是“不小心”勾选了。我见过有公司因为桶权限配置错误,导致几百万条密钥操作记录泄露到公网,后续合规罚款高达数百万美元。

好了,关于日志存储与检索就先聊这么多。我觉得关键就一句话:想清楚你要怎么用这些日志,再决定存哪里、怎么存。下一块内容我们聊聊更具体的排查场景——怎么从这些日志里揪出“异常行为”。