一次 kubectl 命令在 API Server 的审计日志里留下什么?策略里的几个级别到底差在哪?这篇单独起一个开了审计的集群,写一份自己的策略,用 kubectl 响应头里的 Audit-Id 把命令和日志对上,逐字段看一遍,并对比冷热缓存下一条命令发出的请求数。
kubectl -v=8 的响应头里有 Audit-Id:882712fc-bb6e-43fb-8eaf-ea60500ed957;日志里这个 auditID 共 2 条:
stage=RequestReceived level=RequestResponse verb=create uri=/api/v1/namespaces/k18/configmaps?fieldManager=kubectl-create&fieldVal code=- requestObject=无 responseObject=无
stage=ResponseComplete level=RequestResponse verb=create uri=/api/v1/namespaces/k18/configmaps?fieldManager=kubectl-create&fieldVal code=201 requestObject=有 responseObject=有
ResponseComplete 的关键字段:user.username=kubernetes-admin;user.groups=['kubeadm:cluster-admins', 'system:authenticated'];userAgent=kubectl/v1.36.1;sourceIPs=['172.18.0.1']
objectRef={"resource": "configmaps", "namespace": "k18", "name": "c1", "apiVersion": "v1"};responseStatus.code=201
annotations={"authorization.k8s.io/decision": "allow", "authorization.k8s.io/reason": "RBAC: allowed by ClusterRoleBinding \"kubeadm:cluster-admins\" of ClusterRole \"cluster-admin\" to Group \"kubeadm:cluster-admins\""}
requestObject.data={'password': 'hunter2'}(明文口令出现在了审计日志里)
同样的口令 hunter2 在日志文件里出现 1 行,其中属于 secrets 的有 0 行(Secret 的规则是 Metadata,只记元数据)策略里 k18 命名空间的 ConfigMap 是 RequestResponse 且不 omit 任何阶段,所以一次 kubectl create cm c1 有两条记录,auditID 相同:RequestReceived(还没处理,没有状态码)和 ResponseComplete(code=201,请求体和响应体都在)。后者里能看到 user.username=kubernetes-admin、userAgent=kubectl/v1.36.1、objectRef,以及 annotations 里的授权决定:authorization.k8s.io/decision: allow 和 RBAC: allowed by ClusterRoleBinding "kubeadm:cluster-admins" …。kubectl 的 -v=8 响应头里有同一个 Audit-Id,可以把命令和日志对上。
RequestResponse 把 --from-literal=password=hunter2 的明文写进了 requestObject.data;同样的口令放进 Secret,日志里一行都没有,因为 Secret 的规则是 Metadata。
创建 configmaps → stage=RequestReceived level=RequestResponse verb=create uri=/api/v1/namespaces/k18/configmaps?fieldManager=kubectl-create&fieldVal code=- requestObject=无 responseObject=无 创建 configmaps → stage=ResponseComplete level=RequestResponse verb=create uri=/api/v1/namespaces/k18/configmaps?fieldManager=kubectl-create&fieldVal code=201 requestObject=有 responseObject=有 创建 pods → stage=ResponseComplete level=Request verb=create uri=/api/v1/namespaces/k18/pods?fieldManager=kubectl-client-side-apply&fie code=201 requestObject=有 responseObject=无 创建 namespaces → stage=ResponseComplete level=Metadata verb=create uri=/api/v1/namespaces?fieldManager=kubectl-create&fieldValidation=Strict code=201 requestObject=无 responseObject=无
同样是“创建一个对象”:Metadata(namespaces,落到最后一条规则)只记谁、什么时候、对什么、结果如何;Request(pods)再加上 requestObject;RequestResponse(configmaps)再加上 responseObject。pods 和最后一条规则都 omitStages: ["RequestReceived"],所以只有一条 ResponseComplete——日志量直接少一半。
冷缓存(发现缓存目录是空的)kubectl get pods -n k18:审计日志里 3 条请求,其中 GET 发现类接口 2 条;真正列出 pods 的 1 条
get /api?timeout=32s
get /apis?timeout=32s
list /api/v1/namespaces/k18/pods?limit=500
…(共 3 条)
热缓存(紧接着再跑一次)kubectl get pods -n k18:1 条请求:list /api/v1/namespaces/k18/pods?limit=500
那条列出 pods 的请求:level=Request(k18 的 pods 规则是 Request,但 get/list 没有请求体,所以 requestObject=无)发现缓存是空的时,kubectl get pods -n k18 在日志里留下 3 条:GET /api、GET /apis(现代 kubectl 用聚合发现,两个请求就够)和真正的 list /api/v1/namespaces/k18/pods?limit=500;紧接着再跑只有最后 1 条。这个 list 在 pods 的 Request 规则下,但 list 没有请求体,所以 requestObject 为空。分析审计日志时要记得“一条命令”可能对应多条记录。
kubectl get pods --as system:serviceaccount:k18:nobody:Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:k18:nobody" cannot list resource "pods" in API group "" in the namespace "k18" 403:user=kubernetes-admin;impersonatedUser=system:serviceaccount:k18:nobody;code=403;decision=forbid;reason='' 403:user=system:anonymous;impersonatedUser=-;code=403;decision=forbid;reason=''
403 同样在日志里:kubectl get pods --as system:serviceaccount:k18:nobody 的记录里 user 仍是真实调用者 kubernetes-admin,被模拟的身份在 impersonatedUser,decision=forbid(reason 为空);不带令牌的 curl 则是 user=system:anonymous、同样 forbid。审计日志是回答“谁尝试过什么”的主要依据。