日志格式与标准:Syslog、JSON、CEF 在 PKI 中的应用

咱们做 PKI 日志监控,第一个要面对的难题就是日志格式。我不夸张地说,格式不统一,后面一切仪表盘都是白搭。你想想看,CA 签发证书、OCSP 响应、CRL 更新,不同厂商、不同组件出来的日志长得完全不一样。我刚开始搭建 ELK 那会儿,就吃过这个亏——硬生生花了三天才把混乱的格式梳理干净。

这一章我们就来拆解三种最主流的日志格式:Syslog、JSON、CEF。我会结合 PKI 场景,告诉你每种格式适合什么、坑在哪里、怎么在 Logstash 里把它们吃进去。

4.1 三种格式的家族谱系

先给一张全景图,看清楚它们各自的位置和关系。

PKI 日志三种格式对比 Syslog 格式 RFC 5424 / RFC 3164 优先级(PRI) | 时间戳 | 主机名 进程名 | PID | 消息体 PKI 场景:CA 系统默认输出 日志量大,字段解析复杂 JSON 格式 ECMAScript 标准 键值对嵌套 | 无固定字段 可包含 timestamp / event / cert PKI 场景:REST API 输出 ELK 原生友好,易于解析 CEF 格式(Common Event Format) ArcSight 标准 CEF:0|厂商|设备|版本|签名|名称|严重度|扩展字段 PKI 场景:安全设备、防火墙、SIEM 集成 字段预定义,适合跨平台关联

4.2 Syslog 格式 —— PKI 系统的“通用语言”

Syslog 是历史最悠久的日志协议,也是目前 CA 设备、Linux 服务器默认输出的格式。我记得第一次对接一家金融客户的 ADCS(Active Directory Certificate Services),发现它发的就是 Syslog,但消息体里夹杂着 Windows EventLog 的糟粕——嗯,要额外清洗。

Syslog 的核心结构如下:

RFC 5424 格式(推荐使用):
<PRI>VERSION TIMESTAMP HOSTNAME APPNAME PROCID MSGID [STRUCTURED-DATA] MSG

例子(CA 证书颁发事件):
<14>1 2025-03-12T14:32:17.128Z ca01.ad.corp lsass 4567 - [msEventLog severity=4 provider="Microsoft-Windows-ActiveDirectory_CertificateServices"] Certificate issued: serial=0xAB123456, subject=CN=node1.corp, SHA1=ABCDEF...

这里 PRI 的值(14)代表 facility=1(user-level)和 severity=6(informational)。我在项目中经常遇到有人忘记配置结构化数据(STRUCTURED-DATA),导致后面 Logstash 解析时丢失关键字段。注意!PKI 的证书序列号、主题、指纹最好都放在结构化数据里,不然提取会很痛苦。

个人经验: 如果你用 Syslog-ng 或者 Rsyslog 做转发,建议启用 RFC 5424 格式并加上 rfc5424 头选项。我在一个跨国项目中,因为用了老旧的 RFC 3164 格式(BSD style),解析时的时间戳没有时区,差点把事件时间全搞错。

4.3 JSON 格式 —— ELK 的“亲儿子”

JSON 不用多说,Elasticsearch 本身就用 JSON 序列化。如果你能控制 PKI 系统的应用层输出,我强烈建议直接输出 JSON。你想想看,Logstash 的 json 过滤器一上,字段自动映射,省掉无数 grok 正则的痛苦。

一个典型的 PKI JSON 日志长这样:

{
  "timestamp": "2025-03-12T14:32:17.128Z",
  "event": "certificate_issued",
  "ca": "ca01.ad.corp",
  "serial": "0xAB123456",
  "subject": {
    "CN": "node1.corp",
    "O": "Acme Corp",
    "C": "CN"
  },
  "fingerprint": {
    "sha1": "ABCDEF1234567890",
    "sha256": "1111222233334444"
  },
  "validity": {
    "not_before": "2025-03-12T00:00:00Z",
    "not_after": "2026-03-12T00:00:00Z"
  },
  "client_ip": "10.0.1.100"
}
避坑指数 ★★★★: 千万不要用多层嵌套太深的对象!ES 默认的字段深度限制是 20 层,而 PKI 的 subject 有时会包含多个 OU、DC,很容易超过。我曾经遇到 OCR(证书注册)日志里嵌套 8 层,导致索引 mapping 报错。建议提前展平:subject.CN、subject.O 这样一级对象就够了。

JSON 还有一个好处,可以携带 @version 和 tags 元字段,方便 Logstash 做分支处理。

4.4 CEF 格式 —— 安全运营的“国际语言”

CEF(Common Event Format)是 ArcSight 定义的标准化日志格式,很多安全设备和 PKI 网关(比如 SSL 解密设备、OCSP 代理)都会输出 CEF。说白了这个格式的好处是:字段命名统一,不同厂商的设备之间可以直接关联。比如你用 CEF 格式的证书吊销日志,可以直接跟防火墙的 SSL 握手日志做 timeline 关联。

CEF 的标准结构:

CEF:0|Vendor|Device|Version|SignatureID|Name|Severity|Extension

例子(OCSP 请求被拒绝):
CEF:0|OpenXPKI|OCSP|2.0|1003|Certificate revocation check|5|src=192.168.1.1 spt=443 act=denied reason=certificate_revoked cs1=0xDEADBEEF cs1Label=serialNumber

CEF 字段 含义 PKI 使用建议
SignatureID 事件编号(唯一标识事件类型) 1000~1999 建议分配给证书签发相关
Name 事件名称 用英文简洁词组,如 "Certificate Issued"
Severity 严重等级 (0-10) 证书吊销建议 8 以上,普通查询 1-3
Extension 扩展字段(key=value 对,空格分隔) 常用 cs1~cs6 存放证书序列号、主题等
我曾经踩过的坑: 扩展字段中的值如果包含空格,必须用反斜线转义或者用引号包裹。有一次我的 OCSP 日志里 subject 字段写成了 cs1=CN=server1, O=My Corp,Logstash 解析到 “My Corp” 时直接断开,后面全丢了。正确的写法是 cs1=CN=server1\,\ O=My\ Corp。

4.5 在 Logstash 中如何选择解析策略

说了这么多,落地到 ELK 时你到底该用哪种格式?我一般根据 PKI 组件的类型来定:

  • Linux 上的 CA 服务(如 EJBCA、OpenXPKI) – 优先启用 JSON 输出,如果不行就用 Syslog + 结构化数据。
  • Windows CA – 只能用 Syslog(通过 EventLog 转发),或者装第三方 Agent 转换为 CEF。
  • 安全设备(SSL 解密、防火墙) – 绝大多数支持 CEF,直接用 CEF。

Logstash 对应的解析配置片段:

Syslog 解析(需要 grok)

filter {
  grok {
    match => { "message" => "<%{POSINT:pri>%{SYSLOG5424LINE}" }
    overwrite => [ "message" ]
  }
  if [appname] =~ /lsass|ADCS|ejbca/ {
    kv {
      source => "msg"
      field_split => ","
      value_split => "="
    }
  }
}

JSON 解析(最省事)

filter {
  json {
    source => "message"
    target => "pki_event"
  }
  date {
    match => [ "[pki_event][timestamp]", "ISO8601" ]
  }
}

CEF 解析(使用 logstash-filter-translate 或 grok)

filter {
  grok {
    match => { "message" => "CEF:0\\|%{DATA:cef_vendor}\\|%{DATA:cef_device}\\|%{DATA:cef_version}\\|%{DATA:cef_sigid}\\|%{DATA:cef_name}\\|%{INT:cef_severity}\\|%{GREEDYDATA:cef_extension}" }
  }
  kv {
    source => "cef_extension"
    field_split => " "
    value_split => "="
    transform_key => "lowercase"
  }
}
性能提醒: 如果你的 PKI 日志峰值速率超过 5000 EPS,尽量别用复杂的 grok 解析 JSON。直接使用 json 过滤器,它的性能比 grok 高一个数量级。我曾经在客户环境下,把 Syslog 的 grok 改成 JSON 解析,CPU 直接从 70% 降到 15%。

4.6 一点总结

日志格式没有的“最好”,只有“最适合你的 PKI 环境”。我的建议是:能出 JSON 就出 JSON,不能出 JSON 就标准化到 CEF,实在没办法才用原生 Syslog。但不管你用哪种,一定要在 Logstash 中保持字段名的统一习惯——比如 [pki][serial_number]、[pki][subject],这样后续复用仪表盘模板时会非常爽。