侧边栏壁纸
博主头像
汪洋

即使慢,驰而不息,纵会落后,纵会失败,但一定可以达到他所向的目标。 - 鲁迅

  • 累计撰写 191 篇文章
  • 累计创建 74 个标签
  • 累计收到 112 条评论

Kubernetes - 安全之审计

汪洋
2024-02-19 / 0 评论 / 6 点赞 / 492 阅读 / 8,719 字

初次 QA

起因:2302 班级以为同学在技术交流群中探讨能够监控一些资源修改的问题

回答:可以开启审计日志记录,脚本捕捉事件筛选也可,通过 goclient 监听也可, Aqua Security、Sysdig Secure 这些产品也支持

关于此次回答基于 Auditing 的方式,将操作通过实验的方式进行演示

基本概念

Kubernetes 审计日志提供了与安全相关的、按时间顺序排列的记录集, 记录每个用户、使用 Kubernetes API 的应用以及控制面自身引发的活动。

Auditing 的来龙去脉?

审计记录最初产生于 kube-apiserver 内部。每个请求在不同执行阶段都会生成审计事件;这些审计事件会根据特定策略 被预处理并写入后端。策略确定要记录的内容和用来存储记录的后端。 当前的后端支持日志文件和 webhook。

Auditing 阶段

阶段描述
RequestReceived此阶段对应审计处理器接收到请求后,并且在委托给其余处理器之前生成的事件。
ResponseStarted在响应消息的头部发送后,响应消息体发送前生成的事件。 只有长时间运行的请求(例如 watch)才会生成这个阶段。
ResponseComplete当响应消息体完成并且没有更多数据需要传输的时候。
Panic当 panic 发生时生成。

审计策略

已定义的审计级别有:

审计日志描述
None符合这条规则的日志将不会记录。
Metadata记录请求的元数据(请求的用户、时间戳、资源、动词等等), 但是不记录请求或者响应的消息体。
Request记录事件的元数据和请求的消息体,但是不记录响应的消息体。 这不适用于非资源类型的请求。
RequestResponse记录事件的元数据,请求和响应的消息体。这不适用于非资源类型的请求。

实验记录

环境说明

## OS
[root@k8s-master01 audit-logs]# cat /etc/redhat-release 
Rocky Linux release 9.3 (Blue Onyx)

## kubernetes Version
[root@k8s-master01 audit-logs]# kubectl  get node
NAME           STATUS   ROLES           AGE   VERSION
k8s-master01   Ready    control-plane   25d   v1.29.1
k8s-node01     Ready    <none>          25d   v1.29.1
k8s-node02     Ready    <none>          25d   v1.29.1

常见审计策略文件

# vi /etc/kubernetes/audit-policy/policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# 设置机密资源的审计日志级别为Metadata
- level: Metadata
  resources:
  - group: ""
    resources: ["secrets"]
# 设置节点的审计日志级别为RequestResponse
- level: RequestResponse
  userGroups: ["system:nodes"]
# 对于其他内容,不要记录任何内容
- level: None

注意 rules 字段必须在审计策略文件中提供。没有(0)规则的策略将被视为非法配置

# 当然我们也可以使用最低限度的审计策略文件在 Metadata 级别记录所有请求
# 在 Metadata 级别为所有请求生成日志

# apiVersion: audit.k8s.io/v1beta1
# kind: Policy
# rules:
# - level: Metadata

更多Policy配置请参考:https://kubernetes.io/zh-cn/docs/reference/config-api/apiserver-audit.v1/#audit-k8s-io-v1-Policy

# 记录 deployment 变化的方式
# apiVersion: audit.k8s.io/v1
# kind: Policy
# rules:
# - level: Metadata
#   resources:
#   - group: apps
#     resources: ["deployments"]
#   verbs: ["create", "update", "delete"]
#   namespace: "default"

添加审计日志

通过对 kube-apiserver.yaml 的配置开启后端审计日志

标志配置描述
--audit-log-path指定用来写入审计事件的日志文件路径。不指定此标志会禁用日志后端。
--audit-log-maxage定义保留旧审计日志文件的最大天数。
--audit-log-maxbackup定义要保留的审计日志文件的最大数量。
--audit-log-maxsize定义审计日志文件的最大大小(兆字节)。
--audit-policy-file定义审计日志文件的策略。

这里我们设置审计日志文件的策略/etc/kubernetes/audit-policy/policy.yaml,将审计日志写入/etc/kubernetes/audit-logs/audit.log当中(注意路径需要创建mkdir /etc/kubernetes/audit-logs/),设置最大保留2个审记日志,每个最多7兆。

spec:
  containers:
  - command:
    - kube-apiserver
    - --audit-policy-file=/etc/kubernetes/audit-policy/policy.yaml
    - --audit-log-path=/etc/kubernetes/audit-logs/audit.log
    - --audit-log-maxsize=7
    - --audit-log-maxbackup=2

注意组要挂载相关卷

volumeMounts:
  - mountPath: /etc/kubernetes/audit-policy/policy.yaml
    name: audit-policy
    readOnly: true
  - mountPath: /etc/kubernetes/audit-logs
    name: audit-logs
    readOnly: false
volumes:
  - name: audit-policy
    hostPath:
      path: /etc/kubernetes/audit-policy/policy.yaml
      type: File
  - name: audit-logs
    hostPath:
      path: /etc/kubernetes/audit-logs
      type: DirectoryOrCreate

由于是kube-apiserver是静态pod,所以稍微等待就可以了。(为了保险起见也可以重启kubelet)

$ systemctl daemon-reload
$ systemctl restart kubelet
我的完整 apiserver 配置信息
# cat /etc/kubernetes/manifests/kube-apiserver.yaml
apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 192.168.10.11:6443
  creationTimestamp: null
  labels:
    component: kube-apiserver
    tier: control-plane
  name: kube-apiserver
  namespace: kube-system
spec:
  containers:
  - command:
    - kube-apiserver
    - --advertise-address=192.168.10.11
    - --allow-privileged=true
    - --authorization-mode=Node,RBAC
    - --client-ca-file=/etc/kubernetes/pki/ca.crt
    - --enable-admission-plugins=NodeRestriction
    - --enable-bootstrap-token-auth=true
    - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
    - --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
    - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
    - --etcd-servers=https://127.0.0.1:2379
    - --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
    - --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
    - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
    - --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
    - --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
    - --requestheader-allowed-names=front-proxy-client
    - --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
    - --requestheader-extra-headers-prefix=X-Remote-Extra-
    - --requestheader-group-headers=X-Remote-Group
    - --requestheader-username-headers=X-Remote-User
    - --secure-port=6443
    - --service-account-issuer=https://kubernetes.default.svc.cluster.local
    - --service-account-key-file=/etc/kubernetes/pki/sa.pub
    - --service-account-signing-key-file=/etc/kubernetes/pki/sa.key
    - --service-cluster-ip-range=10.10.0.0/12
    - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
    - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
    - --audit-policy-file=/etc/kubernetes/audit-policy/policy.yaml
    - --audit-log-path=/etc/kubernetes/audit-logs/audit.log
    - --audit-log-maxsize=7
    - --audit-log-maxbackup=2
    image: registry.aliyuncs.com/google_containers/kube-apiserver:v1.29.1
    imagePullPolicy: IfNotPresent
    livenessProbe:
      failureThreshold: 8
      httpGet:
        host: 192.168.10.11
        path: /livez
        port: 6443
        scheme: HTTPS
      initialDelaySeconds: 10
      periodSeconds: 10
      timeoutSeconds: 15
    name: kube-apiserver
    readinessProbe:
      failureThreshold: 3
      httpGet:
        host: 192.168.10.11
        path: /readyz
        port: 6443
        scheme: HTTPS
      periodSeconds: 1
      timeoutSeconds: 15
    resources:
      requests:
        cpu: 250m
    startupProbe:
      failureThreshold: 24
      httpGet:
        host: 192.168.10.11
        path: /livez
        port: 6443
        scheme: HTTPS
      initialDelaySeconds: 10
      periodSeconds: 10
      timeoutSeconds: 15
    volumeMounts:
    - mountPath: /etc/ssl/certs
      name: ca-certs
      readOnly: true
    - mountPath: /etc/pki
      name: etc-pki
      readOnly: true
    - mountPath: /etc/kubernetes/pki
      name: k8s-certs
      readOnly: true
    - mountPath: /etc/kubernetes/audit-policy/policy.yaml
      name: audit-policy
      readOnly: true
    - mountPath: /etc/kubernetes/audit-logs
      name: audit-logs
      readOnly: false
  hostNetwork: true
  priority: 2000001000
  priorityClassName: system-node-critical
  securityContext:
    seccompProfile:
      type: RuntimeDefault
  volumes:
  - hostPath:
      path: /etc/ssl/certs
      type: DirectoryOrCreate
    name: ca-certs
  - hostPath:
      path: /etc/pki
      type: DirectoryOrCreate
    name: etc-pki
  - hostPath:
      path: /etc/kubernetes/pki
      type: DirectoryOrCreate
    name: k8s-certs
  - name: audit-policy
    hostPath:
      path: /etc/kubernetes/audit-policy/policy.yaml
      type: File
  - name: audit-logs
    hostPath:
      path: /etc/kubernetes/audit-logs
      type: DirectoryOrCreate
status: {}

审计日志解析

通过上述的配置后,将会生成如下审计日志

[root@k8s-master01 audit-logs]# ls /etc/kubernetes/audit-logs/
    audit.log
[root@k8s-master01 audit-logs]# cat /etc/kubernetes/audit-logs/audit.log 
{"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"Metadata","auditID":"3f9e34f0-52e8-44bc-88fb-10ae742d2a0a","stage":"ResponseStarted","requestURI":"/api/v1/secrets?allowWatchBookmarks=true\u0026resourceVersion=100443\u0026timeout=7m55s\u0026timeoutSeconds=475\u0026watch=true","verb":"watch","user":{"username":"system:kube-controller-manager","groups":["system:authenticated"]},"sourceIPs":["192.168.10.11"],"userAgent":"kube-controller-manager/v1.29.1 (linux/amd64) kubernetes/bc401b9/shared-informers","objectRef":{"resource":"secrets","apiVersion":"v1"},"responseStatus":{"metadata":{},"code":200},"requestReceivedTimestamp":"2024-02-19T01:13:32.195666Z","stageTimestamp":"2024-02-19T01:13:32.197428Z","annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":"RBAC: allowed by ClusterRoleBinding \"system:kube-controller-manager\" of ClusterRole \"system:kube-controller-manager\" to User \"system:kube-controller-manager\""}}

分析效果

  • 审计事件类型:Event
  • API 版本:audit.k8s.io/v1
  • 事件级别:Metadata
  • 审计 ID:3f9e34f0-52e8-44bc-88fb-10ae742d2a0a
  • 事件阶段:ResponseStarted
  • 请求 URI:/api/v1/secrets?allowWatchBookmarks=true&resourceVersion=100443&timeout=7m55s&timeoutSeconds=475&watch=true
  • HTTP 方法:watch
  • 用户:system:kube-controller-manager
    • 用户名:system:kube-controller-manager
    • 用户组:system:authenticated
  • 源 IP 地址:192.168.10.11
  • 用户代理:kube-controller-manager/v1.29.1 (linux/amd64) kubernetes/bc401b9/shared-informers
  • 对象引用:
    • 资源:secrets
    • API 版本:v1
  • 响应状态:
    • 状态码:200
    • 元数据:{}
  • 请求接收时间戳:2024-02-19T01:13:32.195666Z
  • 事件阶段时间戳:2024-02-19T01:13:32.197428Z
  • 注释:
    • 授权决定:allow
    • 授权原因:RBAC: allowed by ClusterRoleBinding "system:kube-controller-manager" of ClusterRole "system:kube-controller-manager" to User "system:kube-controller-manager"

根据这些信息,可以得出以下结论

用户 system:kube-controller-manager 通过 kube-controller-manager 用户代理向 Kubernetes API 发起了一个 watch 操作,观察 Secrets 资源的变化。
RBAC 授权允许了这个操作,授权原因是由 ClusterRoleBinding "system:kube-controller-manager" 授予的 ClusterRole "system:kube-controller-manager" 对用户 "system:kube-controller-manager" 的权限。

0

评论区