Kubernetes 基础体系 · 第 52/83 篇。示例基于 Kubernetes 当前稳定 API;弃用、版本偏差、云厂商差异和生产风险会明确说明。
Kubernetes 审计日志:Policy、Stage、Backend、性能和合规留存
Kubernetes 审计(Audit)记录的是“谁在什么时间,通过什么请求,对 Kubernetes API 做了什么,以及 API 如何响应”。它位于 kube-apiserver 请求处理链路中,主要用于安全调查、权限验证、变更追踪和合规取证。
审计日志不是 Kubernetes Event:
- 审计日志记录 API 请求及其处理结果,例如某个用户创建了 Role、删除了 Pod,或某个 ServiceAccount 访问了 Secret。
- Kubernetes Event描述集群对象相关的状态变化或诊断信息,例如 Pod 调度失败、容器重启。
- 审计日志通常不能替代 kubelet、容器运行时、网络设备、云控制面或应用自身日志。
本文使用当前稳定的 audit.k8s.io/v1 API。不同 Kubernetes 发行版可能隐藏或托管 kube-apiserver 参数;云厂商也可能把审计日志接入自己的日志服务,因此具体开关和留存界面需要以实际发行版为准。
一、审计事件在请求链路中的位置
一个典型的 API 请求会经过认证、授权、准入和资源处理。审计事件由 kube-apiserver 生成,记录的是这条 API 请求在服务端的处理过程。
sequenceDiagram
participant C as 客户端
participant A as kube-apiserver
participant N as 认证 Authentication
participant Z as 授权 Authorization
participant D as 准入 Admission
participant S as 存储或处理器
participant B as 审计 Backend
C->>A: HTTP API 请求
A->>A: 生成 auditID / RequestReceived
A->>N: 认证
N-->>A: 用户、组、凭据身份
A->>Z: 授权检查
Z-->>A: allow 或 deny
A->>D: 准入检查
D-->>A: allow / mutate / deny
A->>S: 读取或修改资源
S-->>A: 结果
A->>B: 按 Policy 写入审计事件
A-->>C: HTTP 响应
A->>B: ResponseComplete
审计日志中的身份来自认证结果,例如:
{
"user": {
"username": "alice",
"groups": [
"developers"
]
}
}
这并不意味着审计系统重新验证了权限。认证组件负责回答“请求者是谁”,授权组件负责回答“是否允许”,审计组件负责记录处理过程。RBAC 中的 Role、ClusterRole 和 Binding 决定了授权结果,但审计日志不会自动展开成“具体是哪一条 RoleRule 命中了请求”。在当前常见实现中,审计事件还可能包含类似 authorization.k8s.io/decision 的注解,但调查时仍应结合 RBAC 对象和当时的配置进行复核,而不能只依赖一条注解。
审计事件通常包含以下信息:
auditID:一次 API 请求的标识,可用于关联不同阶段的事件。stage:该请求当前处于哪个审计阶段。requestURI、verb:请求路径和 API 动词。user、sourceIPs、userAgent:请求者身份、来源地址和客户端标识。objectRef:访问的 API group、resource、namespace、name。responseStatus:HTTP 状态码及状态信息。requestObject、responseObject:在相应审计级别下记录的请求体和响应体。requestReceivedTimestamp、stageTimestamp:请求收到和当前阶段发生的时间。annotations:处理链路中添加的审计注解。
这些字段不一定全部出现。例如,Metadata 级别不会记录请求体和响应体。
二、Stage:一个请求为什么可能产生多条记录
Stage 表示同一 API 请求在服务端处理过程中的观测点。当前稳定审计 API 定义了四个阶段:
| Stage | 含义 |
|---|---|
RequestReceived |
kube-apiserver 收到请求后立即记录 |
ResponseStarted |
服务端开始向客户端发送响应时记录,主要用于长时间运行的请求 |
ResponseComplete |
请求处理结束时记录 |
Panic |
请求处理过程中发生 panic 时记录 |
通常,一个普通的短请求可能看到:
RequestReceivedResponseComplete
长时间运行的请求,例如某些 watch、日志、exec、attach 或 port-forward 请求,可能先产生 ResponseStarted,在连接结束后再产生 ResponseComplete。如果客户端长期保持 watch 连接,那么最终完成阶段可能很晚,甚至在连接异常中断时才出现。
因此,不能简单地把每条审计记录都当成一次独立 API 操作。应使用 auditID 和 stage 聚合同一请求。
例如下面两条事件属于同一个请求:
{
"auditID": "2b7d...",
"stage": "RequestReceived",
"verb": "delete",
"requestURI": "/api/v1/namespaces/prod/pods/web-7d8",
"user": {
"username": "alice"
}
}
{
"auditID": "2b7d...",
"stage": "ResponseComplete",
"verb": "delete",
"requestURI": "/api/v1/namespaces/prod/pods/web-7d8",
"responseStatus": {
"code": 200
},
"user": {
"username": "alice"
}
}
审计策略中的 omitStages 可以针对匹配的规则省略某些阶段。例如省略 RequestReceived,可以减少普通请求的重复记录,但会失去“请求刚到达时”的时间点。省略 ResponseComplete 则会削弱对最终状态码和处理结果的分析能力。
对安全调查而言,ResponseComplete 往往最有价值,因为它包含最终响应状态;对请求洪峰和入口分析而言,RequestReceived 更接近请求到达时间。策略设计不是简单地“阶段越多越安全”,而是要在证据完整性和日志量之间取舍。
三、Policy:审计策略如何决定记录什么
审计策略使用 audit.k8s.io/v1 的 Policy 对象描述匹配条件和日志级别。它通常由 kube-apiserver 通过本地文件加载,而不是作为普通 Kubernetes 对象存储在集群中。
核心结构如下:
apiVersion: audit.k8s.io/v1
kind: Policy
omitStages:
- RequestReceived
rules:
- level: Metadata
users: ["alice"]
verbs: ["get", "list"]
resources:
- group: ""
resources: ["pods"]
- level: RequestResponse
resources:
- group: "apps"
resources: ["deployments"]
- level: Metadata
1. 规则是按顺序匹配的
审计规则通常采用“第一条匹配规则生效”的方式。因此,规则顺序决定结果。
上例中:
- Alice 读取 Pod 的请求首先命中第一条,级别是
Metadata。 - 对 Deployment 的其他请求命中第二条,级别是
RequestResponse。 - 其他未匹配请求命中最后一条,级别是
Metadata。
如果把最后一条放在第一条,所有请求都会先被它匹配,后面的规则不会发挥作用。这是审计策略最常见的配置错误之一。
2. 四个日志级别
审计级别从低到高为:
| Level | 记录内容 |
|---|---|
None |
不记录 |
Metadata |
记录请求元数据,不记录请求体和响应体 |
Request |
记录元数据和请求体 |
RequestResponse |
记录元数据、请求体和响应体 |
Metadata 不是“只记录用户名”。它通常仍包含 URL、资源引用、时间、状态码、来源地址、User-Agent 等信息。
Request 和 RequestResponse 需要特别谨慎。以下对象的请求体或响应体可能包含敏感信息:
Secret- 包含凭据的 ConfigMap
- ServiceAccount token 相关对象
- Admission webhook 配置
- 云厂商凭据或私有仓库凭据
- 自定义资源中的密码、API Token、证书私钥
Secret 的 data 通常只是 Base64 编码,并不是脱敏或加密。Base64 内容进入审计日志后,任何能读取该日志的人都可能恢复原始凭据。
3. 匹配条件
常用匹配条件包括:
users:认证后的用户名。userGroups:认证后的用户组。verbs:如get、list、watch、create、update、patch、delete。resources:API group 和 resource。namespaces:命名空间。resourceNames:具体资源名。nonResourceURLs:非资源 URL,例如/version、/metrics等。level:命中后使用的审计级别。omitStages:对命中规则忽略的阶段。
核心资源组使用空字符串表示:
resources:
- group: ""
resources: ["pods", "secrets"]
Deployment 属于 apps 组:
resources:
- group: "apps"
resources: ["deployments"]
RBAC 对象的 group 是 rbac.authorization.k8s.io:
resources:
- group: "rbac.authorization.k8s.io"
resources:
- "roles"
- "rolebindings"
- "clusterroles"
- "clusterrolebindings"
非资源 URL 不能放进 resources:
- level: Metadata
nonResourceURLs:
- "/version"
- "/healthz"
resourceNames 适合限制到具体对象,但对 list 和 watch 需要谨慎:客户端通常通过 fieldSelector=metadata.name=... 等方式限制名称,不能假设所有 list/watch 请求都会自然地包含单个资源名。
4. 一个较实用的策略示例
下面的策略实现了三个目标:
- Secret 只记录元数据,避免把凭据写入审计日志。
- RBAC 和工作负载控制器记录请求与响应,便于追踪高风险变更。
- 其他请求记录元数据,保留基本调查能力。
apiVersion: audit.k8s.io/v1
kind: Policy
# 减少普通请求的重复事件。
# 如果合规要求必须保留请求到达时间,应删除此项。
omitStages:
- RequestReceived
rules:
# Secret 不记录请求体和响应体。
- level: Metadata
resources:
- group: ""
resources:
- secrets
# RBAC 变更通常属于高风险权限操作。
- level: RequestResponse
resources:
- group: rbac.authorization.k8s.io
resources:
- roles
- rolebindings
- clusterroles
- clusterrolebindings
# 工作负载控制器变更便于追踪发布、镜像和副本数变化。
- level: RequestResponse
resources:
- group: apps
resources:
- deployments
- daemonsets
- statefulsets
# 非资源 API 只保留元数据。
- level: Metadata
nonResourceURLs:
- "/version"
- "/healthz"
- "/readyz"
- "/livez"
# 兜底规则必须明确存在,否则策略覆盖范围可能不符合预期。
- level: Metadata
这个示例仍可能暴露工作负载对象中的敏感环境变量或配置。是否对 Deployment 使用 RequestResponse,应取决于组织是否允许把完整对象发送到日志系统。更保守的方案是将工作负载控制器也改成 Metadata,再通过 Git、发布系统或准入系统记录变更内容。
四、Backend:审计事件写到哪里
Backend 是审计事件的输出端。Kubernetes 内置的主要后端是:
- Log backend:写入 kube-apiserver 本地文件。
- Webhook backend:通过 HTTP(S) 发送给外部审计接收端。
数据流可以表示为:
flowchart LR
R[API 请求] --> A[kube-apiserver]
A --> P[Audit Policy 匹配]
P --> E[Audit Event]
E --> L[Log Backend]
E --> W[Webhook Backend]
L --> F[本地审计文件]
W --> X[外部接收器]
F --> C[采集器/归档]
X --> S[集中日志或合规存储]
1. Log backend
典型配置参数包括:
--audit-policy-file=/etc/kubernetes/audit-policy.yaml
--audit-log-path=/var/log/kubernetes/audit.log
--audit-log-maxage=30
--audit-log-maxbackup=10
--audit-log-maxsize=100
这些参数的含义通常是:
--audit-policy-file:审计策略文件路径。--audit-log-path:审计日志文件路径。--audit-log-maxage:轮转文件保留天数。--audit-log-maxbackup:轮转文件数量。--audit-log-maxsize:单个文件达到指定大小后轮转,单位通常为 MB。
审计日志一般是逐行 JSON,便于 Fluent Bit、Filebeat、Vector 或其他采集器读取。配置文件轮转只能解决本地文件增长问题,不能自动满足长期留存、不可篡改和异地灾备要求。
本地文件的优点是链路简单、故障隔离明确;缺点是:
- kube-apiserver 节点损坏可能导致日志丢失。
- 节点磁盘满会影响审计写入,甚至影响 API 服务稳定性。
- 多个控制平面节点会产生多个文件,需要按时间和
auditID汇总。 - 本地文件权限配置不当时,控制平面节点上的其他进程可能读取敏感信息。
2. Webhook backend
Webhook backend 把审计事件发送到外部 HTTPS 服务。外部接收器通常负责:
- 校验证书和客户端身份。
- 校验请求格式。
- 持久化原始事件。
- 添加接收时间和归档编号。
- 将数据写入集中日志、对象存储或 WORM 存储。
- 返回成功或失败状态。
Webhook 使用 kubeconfig 风格的连接配置来描述服务器地址、TLS 和认证信息。不同 Kubernetes 版本对批处理、超时和重试参数的支持可能存在差异,应以目标版本的 kube-apiserver --help 和官方配置说明为准。
Webhook 常见工作模式是:
- blocking:API 请求等待审计后端处理。后端不可达或处理缓慢时,API 请求延迟上升,失败可能反映到请求链路。
- batch:先把事件放入队列,按批次发送。吞吐和延迟通常更好,但进程重启、队列溢出或长期后端故障可能导致事件丢失或延迟。
不能把 batch 理解为“可靠消息队列”。是否持久化、队列容量多大、重试多久,取决于 kube-apiserver 参数和外部接收器实现。
五、从请求到审计后端的故障路径
审计不是 API 请求之外的独立旁路。后端模式会改变故障表现。
情形一:策略文件错误
如果 kube-apiserver 启动时无法读取策略文件,常见结果是 apiserver 启动失败或无法按预期提供服务。生产变更不应直接覆盖正在使用的策略文件,而应:
- 在临时目录写入新文件。
- 使用 YAML 解析器检查语法。
- 检查 group、resource、规则顺序和默认规则。
- 在维护窗口更新控制面配置。
- 观察 kube-apiserver 健康状态和审计输出。
策略语法正确并不代表语义正确。最重要的语义检查是:每个高风险请求是否命中预期规则,以及是否被前面的宽泛规则提前匹配。
情形二:Webhook 后端变慢
假设一次 API 请求在 blocking 模式下必须等待审计后端确认,则粗略的请求额外延迟可以表示为:
其中:
- :构造和序列化审计事件的时间。
- :在后端队列中等待的时间。
- :到审计服务的网络往返时间。
- :接收端校验和持久化时间。
batch 模式可以通过一次发送多个事件降低每条事件的网络固定成本,但会增加等待批次形成的时间,并引入队列丢失边界。
情形三:本地磁盘不足
磁盘使用量可近似表示为:
其中:
- :每秒产生的审计事件数。
- :单条事件的平均字节数。
- :保存在本地的时间。
如果启用 RequestResponse, 可能显著增加;如果集群存在大量 watch、list 或控制器同步请求, 也可能很高。
本地轮转参数只能限制单个节点上的文件集合,不能替代对“采集是否成功”的监控。至少需要监控:
- 审计文件是否持续增长。
- 采集器是否能读取和发送文件。
- webhook 请求失败率和延迟。
- 审计接收端最后成功接收时间。
- 控制平面节点磁盘使用率。
- 按控制平面节点统计的事件量是否突然归零。
六、性能:真正昂贵的不是规则匹配本身
审计性能成本通常来自四个部分:
- 事件构造:提取用户、对象引用、状态和时间戳。
- 对象复制和序列化:
Request或RequestResponse可能需要处理完整 JSON 对象。 - 输出 I/O:本地磁盘写入或网络发送。
- 同步等待:blocking webhook 将后端延迟加入 API 请求延迟。
可以把总日志流量粗略写成:
其中 是第 类请求的速率, 是该类事件的平均大小, 是请求类别数量。
例如:
get pod以 Metadata 记录,事件可能较小。list pods的元数据仍是一条请求,但RequestResponse可能包含大量 Pod 列表。watch pods生命周期长,结束时可能产生很晚的完成事件。- Deployment 的完整响应可能包含较大的 PodTemplate 和注解。
因此,“把所有请求设置成 RequestResponse”不仅增加存储量,也可能增加序列化、内存和网络开销。
降低成本的正确方向
首先按证据需求区分级别:
- 日常访问追踪:
Metadata。 - 高风险权限对象:必要时使用
Request或RequestResponse。 - Secret:通常避免记录请求体和响应体。
- 高频健康检查:只记录 Metadata,或在有明确理由时使用
None。 - 长时间 watch:评估是否真的需要完整请求和响应体。
其次,避免把高频低价值请求设置为高等级。例如,仅为了知道某个控制器是否工作,就把所有 list/watch 设置为 RequestResponse,通常会产生大量无用数据。
最后,使用压测验证,而不是根据经验猜测。应分别测量:
- API 请求 p50、p95、p99 延迟。
- kube-apiserver CPU 和内存。
- 审计文件写入速率。
- webhook 接收延迟、失败率和队列长度。
- 控制器同步周期是否发生变化。
不要把网上的固定“审计开销百分比”直接套用到生产集群。对象大小、请求比例、存储介质、网络路径和后端实现都会改变结果。
七、部署和验证示例
1. 使用本地 Log backend
假设策略文件位于:
/etc/kubernetes/audit-policy.yaml
并且 kube-apiserver 能读取该文件。启动参数示例:
--audit-policy-file=/etc/kubernetes/audit-policy.yaml
--audit-log-path=/var/log/kubernetes/audit.log
--audit-log-maxage=30
--audit-log-maxbackup=10
--audit-log-maxsize=100
前置条件包括:
- 策略文件已经挂载到 kube-apiserver 所在节点或容器。
- 日志目录存在且 kube-apiserver 进程可写。
- 轮转后的文件由采集器读取。
- 多控制平面节点上的日志均被采集。
发起一个可识别的请求:
kubectl get pod -n prod web-7d8
kubectl auth can-i get pods -n prod
检查日志:
sudo tail -n 20 /var/log/kubernetes/audit.log
如果安装了 jq,可以筛选删除和 RBAC 变更:
sudo jq -c '
select(
.verb == "delete" or
(.objectRef.apiGroup == "rbac.authorization.k8s.io" and
(.verb == "create" or .verb == "update" or .verb == "patch" or .verb == "delete"))
)
' /var/log/kubernetes/audit.log
这里的 .objectRef.apiGroup 为空字符串时表示核心 API group,因此不能用它判断所有资源。筛选时应同时考虑 objectRef.resource、namespace、name 和 verb。
验证策略是否真正生效,应执行能命中不同规则的请求:
kubectl get secret -n prod app-credentials -o yaml
kubectl get deployment -n prod web -o yaml
kubectl get rolebinding -n prod
预期现象是:
- Secret 事件包含元数据,但不包含
requestObject或responseObject。 - Deployment 如果命中
RequestResponse,可能包含完整请求或响应对象。 - 因为策略配置了
omitStages: [RequestReceived],普通请求通常看不到RequestReceived事件。
如果日志中出现 Secret 内容,应立即停止继续扩大采集范围,并检查:
- Secret 请求是否被前面的规则命中。
- 是否有其他 kube-apiserver 实例使用了旧策略。
- 采集器或下游系统是否自行解析并保存了请求体。
- 该日志是否已被复制到更多位置。
如果 Secret 已经进入审计日志,不能只删除 Kubernetes Secret 就认为泄露已经消除;还需要按凭据轮换流程吊销和重新生成相关凭据,并处理审计存储中的副本。
八、审计与 RBAC:记录“被允许”,不等于解释“为什么被允许”
审计日志可以回答:
alice在什么时间请求了prod命名空间中的 Deployment,最终返回了什么状态。
但它通常不能单独完整回答:
这次请求具体由哪一个 RoleBinding、哪一条 RoleRule 授权?
回答第二个问题需要把多个证据源结合起来:
- 审计事件中的
user、groups、verb和objectRef。 - 当时生效的 Role、ClusterRole 和 Binding。
- 聚合 ClusterRole 的标签和聚合结果。
- API server 的授权配置。
- 如果是 ServiceAccount,还要确认命名空间和完整用户名,例如:
system:serviceaccount:prod:deployer。
一个常见误解是:运行下面的命令就能解释历史授权:
kubectl auth can-i get secret -n prod \
--as=alice
这个命令只检查“当前时刻”的授权结果。若 RoleBinding 在入侵后已被删除,当前检查结果不能还原历史状态。因此,合规或取证场景还需要保存 RBAC 对象的变更审计,以及配置仓库、GitOps 记录或不可变快照。
九、合规留存:轮转、归档和证据链不是一回事
合规留存至少包含四个不同问题:
1. 是否采集
策略决定哪些请求进入审计系统。没有采集到的数据,后续无法从留存系统恢复。
2. 是否完整
需要确认是否覆盖:
- 所有控制平面节点。
- 所有 API group 和非资源 URL。
- 关键的 ServiceAccount。
- 拒绝请求和成功请求。
- RBAC、准入配置和工作负载变更。
- 关键请求的最终响应阶段。
只记录成功请求会丢失权限探测和攻击失败阶段;只记录拒绝请求则无法重建实际变更。
3. 是否安全
审计日志本身可能包含敏感数据,应至少考虑:
- 传输使用 TLS。
- 接收端进行身份认证和访问控制。
- 存储加密。
- 读取权限与 Kubernetes 管理权限分离。
- 对操作人员的查询行为再次记录。
- 避免把完整 Secret、私钥和 Token 写入日志。
- 对日志中的用户标识、IP 和资源名评估隐私要求。
Kubernetes 审计 API 不会自动为组织建立防篡改证据链。拥有存储管理员权限的人可能删除或修改普通日志文件。因此,强合规场景通常需要外部不可变存储、对象锁定、版本保留、跨区域复制或 WORM 能力。具体控制要求取决于组织适用的法规和审计标准,不能仅凭 Kubernetes 的 --audit-log-maxage 推导出合规结论。
4. 是否可证明
一次审计事件至少应能证明:
- 事件来自哪个控制平面实例或可信接收器。
- 接收时间和 API server 处理时间。
- 事件是否完整落盘。
- 日志是否被轮转、归档或恢复过。
- 查询结果是否来自原始记录,而不是人工改写的报表。
实践中可以为归档文件生成哈希并保存到独立的访问受控系统,但哈希本身也必须受到保护;否则攻击者可以同时修改日志和哈希。更严格的方案需要签名、时间戳服务或不可变存储。
十、几个容易混淆的边界
审计日志不会记录所有 Kubernetes 活动
只有经过 kube-apiserver 的 API 请求才会进入这条审计链路。以下信息通常需要其他日志源:
- 容器内部系统调用。
- 容器标准输出和应用日志。
- kubelet 本地行为。
- 容器运行时事件。
- 节点上的文件操作。
- CNI、云负载均衡器或云 IAM 操作。
- 直接访问 etcd 的行为。
控制器创建 Pod 的请求通常会被审计,但审计中的用户往往是控制器使用的 ServiceAccount,而不是最初提交 Deployment 的人。因此,调查发布来源时需要沿着 Deployment、ReplicaSet、Pod 和发布系统记录继续关联。
get 和 list 的风险不同
get secret/foo 暴露单个对象,list secrets 可能暴露整个命名空间的对象集合。即使两者都使用 Metadata,批量读取行为仍然是重要的访问信号。
同样,watch 不是普通的单次读取。它可能长期占用连接,并在事件流结束很久后才出现完成阶段。检测数据外泄时,不能只搜索短时间内的 ResponseComplete。
kubectl 命令不是审计中的唯一客户端
审计事件的 userAgent 可能来自:
kubectl- controller-manager
- operator
- CI/CD 系统
- 云控制器
- 自定义客户端
- kubelet
不要依据 userAgent 单独判断操作者身份。可信身份应优先使用认证后的 user.username 和 groups,再结合来源 IP、服务账号、工作负载和时间线分析。
十一、生产检查顺序
部署或修改审计配置时,可以按照以下因果顺序验证:
- 先验证策略语义:确认规则顺序、group、resource、verb 和默认规则。
- 再验证事件内容:分别执行 Secret、RBAC、Deployment、拒绝请求和非资源 URL 请求。
- 再验证阶段:检查
auditID是否能把多个阶段关联起来。 - 再验证后端:检查本地文件或 webhook 接收端是否持续收到数据。
- 再验证故障表现:模拟接收端变慢、不可达和本地磁盘接近满的情况。
- 最后验证归档:确认轮转文件被采集、归档可检索、权限正确且无法由普通管理员随意删除。
审计系统的成功标准不是“配置文件存在”,而是发生一次真实 API 操作后,组织能够在可接受时间内回答:请求者是谁、请求了什么、是否成功、由哪个控制面处理、原始证据保存在哪里,以及这份证据是否仍然可信。
系列导航与关联阅读
- 系列入口:Kubernetes 完整学习路线:从 Pod 与控制面到安全、运维和 Operator
- 上一篇:Kubernetes 多租户:Namespace、RBAC、NetworkPolicy、Quota 和隔离极限
- 下一篇:ResourceQuota 与 LimitRange:租户预算、默认值、对象数量和治理
- 延伸:Kubernetes RBAC:Role、ClusterRole、Binding、聚合和最小权限
- 延伸:Kubernetes 事件响应:止损、证据、审计、凭据轮换和恢复
官方资料
本文依据 Kubernetes、CNCF 与相关项目官方文档重新梳理;正文和生产清单由 WR BLOG 编写。

评论
0 条讨论