CloudTrail 日志结构解析

日志拿到手,第一件事就是看懂它的结构。我记得刚接触 CloudTrail 时,瞅着一大串 JSON 眼花缭乱。后来琢磨明白了,其实结构非常规整。你只要抓住几个核心字段,审计排查基本就稳了。

日志格式概览

CloudTrail 默认使用 JSON 格式,每行一个独立事件。每条日志就是一个 JSON 对象,顶层大概 20 多个字段。我建议你先记住下面这张表里的,其他字段查文档就行。

字段名类型说明
eventVersionString日志格式版本,目前是 1.08
userIdentityObject谁发起的请求
eventTimeString事件 UTC 时间,ISO8601 格式
eventSourceString服务域名,比如 ec2.amazonaws.com
eventNameStringAPI 操作名字,比如 RunInstances
awsRegionString发生区域
sourceIPAddressString来源 IP,要是来自 AWS 服务可能是 aws-internal
userAgentString客户端标识
requestParametersObject请求参数,里面藏着关键配置
responseElementsObject响应数据
resourcesArray操作涉及的资源列表
readOnlyBoolean是否为只读操作
eventTypeStringAwsApiCall / AwsServiceEvent / AwsConsoleSignin 等
managementEventBoolean是否为管理事件(默认就是)
recipientAccountIdString接收方账户 ID
eventIDString全局唯一 ID,去重时特别有用
eventCategoryStringManagement / Data / Insight

重点记住: 排查问题时我一般先看 eventTime、eventName、sourceIPAddress 和 userIdentity。这四个组合起来就能定位到 90% 的异常。

核心字段详解——别被嵌套层数吓到

先说 userIdentity。它里面还有一层对象,包含这些子字段:

  • type:IAMUser、AssumedRole、Root、AWSService 等
  • principalId:主体 ID
  • arn:主体的 ARN
  • accountId:账户 ID
  • accessKeyId:访问密钥 ID(如果使用)
  • userName:用户名(仅 IAMUser 时有)
  • sessionContext:如果是临时凭证,这里会包含 sessionIssuer 和 webIdFederationData

举个例子:一个 IAM 用户通过控制台操作,userIdentity 大概长这样:

"userIdentity": {
    "type": "IAMUser",
    "principalId": "AIDACKCEVSQ6C2EXAMPLE",
    "arn": "arn:aws:iam::123456789012:user/zhangsan",
    "accountId": "123456789012",
    "accessKeyId": "ASIAIOSFODNN7EXAMPLE",
    "userName": "zhangsan",
    "sessionContext": null
}

requestParameters 和 responseElements 这两个字段最容易被忽略。说白了,它们就是请求和响应的完整参数副本。我遇到过一次运维跑脚本批量创建安全组,结果规则配错了,导致端口暴露。就是通过 requestParameters 里的 IpPermissions 数组发现源 IP 是 0.0.0.0/0 的。

resources 字段是个数组,每个元素包含 ARN 和 accountId。比如创建 EC2 实例时,会列出实例本身的 ARN 以及可能关联的安全组、子网。注意:不是所有 API 都会填充这个字段,控制台的操作往往比 CLI/SDK 填充得更全。

JSON 结构示例——一句一句读

下面给一个完整的日志示例,我加了中文注释便于理解:

{
  "eventVersion": "1.08",
  "userIdentity": {
    "type": "AssumedRole",
    "principalId": "AROAJEXAMPLE:ec2-launch-session",
    "arn": "arn:aws:sts::123456789012:assumed-role/EC2-Role/ec2-launch-session",
    "accountId": "123456789012",
    "accessKeyId": "ASIAIOSFODNN7EXAMPLE",
    "sessionContext": {
      "sessionIssuer": {
        "type": "Role",
        "principalId": "AROAJEXAMPLE",
        "arn": "arn:aws:iam::123456789012:role/EC2-Role",
        "accountId": "123456789012",
        "userName": "EC2-Role"
      },
      "webIdFederationData": null,
      "attributes": {
        "creationDate": "2024-10-15T08:00:00Z",
        "mfaAuthenticated": "false"
      }
    }
  },
  "eventTime": "2024-10-15T08:01:23Z",
  "eventSource": "ec2.amazonaws.com",
  "eventName": "RunInstances",
  "awsRegion": "ap-northeast-1",
  "sourceIPAddress": "203.0.113.50",
  "userAgent": "console.aws.amazon.com",
  "requestParameters": {
    "instancesSet": {
      "items": [{
        "instanceType": "t3.micro",
        "imageId": "ami-0abcdef1234567890"
      }]
    },
    "groupSet": {
      "items": [{"groupId": "sg-12345678"}]
    }
  },
  "responseElements": {
    "instancesSet": {
      "items": [{"instanceId": "i-0abcd1234efgh5678"}]
    }
  },
  "resources": [{
    "ARN": "arn:aws:ec2:ap-northeast-1:123456789012:instance/i-0abcd1234efgh5678",
    "accountId": "123456789012"
  }],
  "readOnly": false,
  "eventType": "AwsApiCall",
  "managementEvent": true,
  "recipientAccountId": "123456789012",
  "eventID": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
  "eventCategory": "Management"
}

小技巧: 使用 Amazon Athena 查询日志时,我会直接解析 requestParameters 里的 JSON 路径。比如查找所有设置了 0.0.0.0/0 的安全组规则。不需要写复杂的正则,用 json_extract 就行了。

日志结构关系图——一眼看穿层级

下面这张图把日志的 JSON 层级关系画了出来。我自己画图时喜欢把 userIdentity 标成蓝色,因为它是识别主体的关键。requestParameters 和 responseElements 标成橙色,表示动态数据。其他基础字段用绿色表示。

CloudTrail 日志 JSON 结构图 CloudTrail Event (根) userIdentity eventTime / eventSource / ... requestParameters type, principalId arn, accountId accessKeyId, userName sessionContext (sessionIssuer 等) 实例类型 / 镜像ID / ... responseElements 说明:蓝色=用户身份,绿色=基础字段,橙色=请求参数 红色=实例细节,青色=响应数据,紫色=会话上下文 … 还有 eventID / resources / readOnly 等 20+ 字段

注意: 数据事件(如 S3 的 GetObject)的日志格式和这里的管理事件略有不同——eventCategory 为 Data,而且 requestParameters 里可能包含桶名和对象键。如果你开启了数据事件,记得单独分析,不要混在一起查。

字段使用技巧——我的实战经验

我个人习惯在 Athena 里建一张视图,把常用字段提取出来。比如:

SELECT 
  eventtime,
  eventname,
  sourceipaddress,
  useridentity.username,
  json_extract(requestparameters, '$.instanceType') AS instance_type
FROM cloudtrail_logs
WHERE eventname = 'RunInstances'
  AND eventtime > current_timestamp - interval '7' day
ORDER BY eventtime DESC;

这样做的好处是查异常非常快。我曾经发现一个账号每天凌晨 3 点都有 RunInstances 和 TerminateInstances 成对出现,顺着 useridentity.sessioncontext.sessionissuer.arn 找到了一个测试用的 Automation 脚本。虽然是合法操作,但证明了日志追踪能力。

避坑指南: 我曾经犯过一个错误——直接用 readOnly = false 过滤所有写入操作,却忘了有些 API 的 readOnly 字段可能缺失(早期事件)。后来统一用 eventtype = 'AwsApiCall' AND eventname NOT LIKE 'Get%' AND eventname NOT LIKE 'Describe%' AND eventname NOT LIKE 'List%' 更靠谱。

你想想看,只要把 JSON 结构搞透了,后面不管是写查询、做告警还是对接安全分析工具,都顺理成章。嗯,这一章的内容够用了。