安全策略

简要概述

本节包含对 Kubernetes 集群环境安全的各种策略建议。

服务账户与RBAC

确保角色 cluster-admin 仅在需要时使用

Kubernetes 提供了一组默认的 RBAC 权限策略,其中 cluster-admin 允许用户访问任何资源以执行任何操作。

# kubectl get clusterrole cluster-admin -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  creationTimestamp: "2023-10-07T03:47:13Z"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: cluster-admin
  resourceVersion: "23814649"
  uid: 49271b7e-1864-425b-b8dc-8b3900955d03
rules:
- apiGroups:
  - '*'
  resources:
  - '*'
  verbs:
  - '*'
- nonResourceURLs:
  - '*'
  verbs:
  - '*'
# ^C

获取哪些用户绑定了 cluster-admin 角色列表:

kubectl get clusterrolebindings -o=custom-columns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECT:.subjects[*].name

确保最小化权限访问 Secret 资源

通过以下指令审查:

kubectl get roles --all-namespaces -o yaml
kubectl get clusterroles -o yaml

TODO;

确保最小化通配符在 Roles 与 ClusterRoles 使用

同上。

确保最小化用户创建 Pod 的能力

在集群中创建 Pod 的能力可以提供许多权限提升的机会,例如为这些 Pod 分配特权服务帐户,或挂载可访问敏感数据的hostPaths(除非实施了Pod安全策略来限制这种访问)

通过以上方式审查。

确保访问 Kubernetes API 的工作负载都使用特定服务帐户

Kubernetes 提供了一个默认的服务帐户 “default”,当创建的 Pod 非分配特定的服务帐户,将会关联使用它。

如果需要从 Pod 访问 Kubernetes API,则应为该 Pod 创建一个特定的服务帐户,并将权限授予该服务帐户,不应使用默认服务帐户来授予应用程序的权限,这样可以更容易地进行审核和审查。

默认服务帐户应配置为不提供服务帐户令牌,也不具有任何明确的权限分配。

可通过以下两种方式关闭服务账户挂载:

  1. 在 Pod 模版中设置 “automountServiceAccountToken: false”
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: registry.cn-hangzhou.aliyuncs.com/kube-image-repo/busybox:1.35.0
    name: nginx
    args:
    - /bin/sh
    - -c
    - "sleep 3600"
  automountServiceAccountToken: false
  1. 在创建 Service Account 中设置 “automountServiceAccountToken: false” 明确关闭
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nginx
automountServiceAccountToken: false

如果未关闭,服务账户授权会被挂载在容器以下位置:

/var/run/secrets/kubernetes.io/serviceaccount # df -h
Filesystem                Size      Used Available Use% Mounted on
tmpfs                     7.3G     12.0K      7.3G   0% /var/run/secrets/kubernetes.io/serviceaccount
......
/var/run/secrets/kubernetes.io/serviceaccount #
/var/run/secrets/kubernetes.io/serviceaccount # ls
ca.crt     namespace  token
/var/run/secrets/kubernetes.io/serviceaccount # ^C

确保不使用 system:masters 用户组

用户组 “system:masters” 是硬编码在 k8s 源码里面作为公知组,拥有集群管理员权限,避免在授权中直接使用。

# kubectl get clusterrolebinding cluster-admin  -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  creationTimestamp: "2023-10-07T03:47:13Z"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: cluster-admin
  resourceVersion: "23814711"
  uid: 35e25988-3032-40a6-999f-34c78fd36963
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:masters
# ^C

确保限制使用 Bind Impersonate Escalate 权限

TODO;

Pod 安全策略

确保至少存在一个活跃的策略控制机制

Pod 安全策略由两种方式:

  1. 基于 k8s 树内的 Pod Security Admission 控制器实现;
  2. 基于外部的访问控制系统通过 validation 与 mutation webhooks 实现。

确保至少使用其中一种,如果没有的话,以下这些敏感操作将无法被限制:

  1. 特权容器;
  2. 挂载 hostPath。

确保没用使用特权容器

对以下三个属性设置为 “true” 开启特权容器运行。

spec.containers[].securityContext.privileged: true
spec.initContainers[].securityContext.privileged: true
spec.ephemeralContainers[].securityContext.privileged: true

这里启动一个 pod 为特权容器:

---
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: registry.cn-hangzhou.aliyuncs.com/kube-image-repo/busybox:1.35.0
    name: nginx
    args:
    - /bin/sh
    - -c
    - "sleep 3600"
    securityContext:
      privileged: true

特权容器可以访问所有Linux内核功能和设备。以完全权限运行的容器几乎可以完成主机所能做的一切。此标志的存在是为了允许特殊的用例,如操作网络堆栈和访问设备。

确保最大限度地减少 hostPID 命名空间使用

通过添加 spec.hostPID: true 开启共享宿主进程命名空间,此时在容器内可以观察到宿主上所有运行的进程。

这是一个建议示列配置:

---
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: registry.cn-hangzhou.aliyuncs.com/kube-image-repo/busybox:1.35.0
    name: nginx
    args:
    - /bin/sh
    - -c
    - "sleep 3600"
  hostPID: false

确保最大限度地减少 hostIPC 命名空间使用

通过添加 spec.hostIPC: true 开启共享宿主进程间通信命名空间,此时在容器内可以与宿主上其他进程通过管道、套接字、消息队列、信号量等通讯。

这是一个建议示列配置:

---
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: registry.cn-hangzhou.aliyuncs.com/kube-image-repo/busybox:1.35.0
    name: nginx
    args:
    - /bin/sh
    - -c
    - "sleep 3600"
  hostICP: false

确保最大限度地减少 hostNetwork 命名空间使用

通过添加 spec.hostNetwork: true 开启共享宿主网络命名空间,此时在容器内使用同宿主网络IP、监听端口、数据流量等。

这是一个建议示列配置:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: registry.cn-hangzhou.aliyuncs.com/kube-image-repo/busybox:1.35.0
    name: nginx
    args:
    - /bin/sh
    - -c
    - "sleep 3600"
  hostNetwork: false

确保避免容器子进程可以获得比父进程更高的权限

在配置了 spec.securityContext.allowPrivilegeEscalation: true 情况下运行的容器有可能存在具有比父进程更高的权限。

这是一个建议示列配置:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: registry.cn-hangzhou.aliyuncs.com/kube-image-repo/busybox:1.35.0
    name: nginx
    args:
    - /bin/sh
    - -c
    - "sleep 3600"
  securityContext:
    allowPrivilegeEscalation: false

确保不使用 root 用户运行容器

容器可以以任何用户运行,虽然有运行时安全策略防护,在处于 root 运行的容器还是比普通用户更容器被攻破。

这是一个建议示列配置:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: registry.cn-hangzhou.aliyuncs.com/kube-image-repo/busybox:1.35.0
    name: nginx
    args:
    - /bin/sh
    - -c
    - "sleep 3600"
  securityContext:
    runAsNonRoot: false
    runAsUser: 65534

确保容器没有 NET_RAW 能力

TODO;

确保最小化 windows 容器没有 HostProcess 权限

TODO;

确保最小化使用 HostPath 挂载存储卷

通过 hostPath 的容器可以访问底层集群节点的文件系统,可以允许容器访问宿主上文件系统的任意区域,如:/etc/passwd。

所以必须存在策略限制挂载的路径。

确保避免使用 HostPorts 开放容器端口

主机端口将容器直接连接到宿主的网络,这可以绕过诸如网络策略之类的控制。

网络策略与 CNI

建议使用的 CNI 支持网络策略

TODO;

建议为命名空间定义默认网络策略

TODO;

kubectl get networkpolicy --all-namespaces

资源 Secrets 管理

建议机密数据作为文件挂载优先于环境变量

应用程序代码配置使用环境是相当常见,这将包括作为环境变量传递的任何机密值,这样任何有权访问日志的用户或应用都可以观察该值。

通过以下指令可以获取使用了环境变量:

kubectl get all -o jsonpath='{range .items[?(@..secretKeyRef)]} {.kind} {.metadata.name} {"\n"}{end}' -A

建议使用外部机密数据管理系统

如果您有更复杂的秘密管理需求,请考虑使用外部秘密存储和管理系统,而不是直接使用Kubernetes secrets。确保该解决方案需要身份验证才能访问机密,对机密的访问和使用进行审核,并对机密进行加密。某些解决方案还可以更容易地轮换机密。

因为默认 secrets 使用 base64 加密存储,这样只要集群管理人员拥有权限则可以对数据进行解密。

扩展准入控制器

建议使用 ImagePolicyWebhook 控制器

TODO;

https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/admission-controllers/#imagepolicywebhook

一般公共策略

通过命名空间在资源之间创建管理边界

针对不同业务通过命名空间隔离,限制用户权限范围可以减少错误或恶意活动的影响。Kubernetes 命名空间允许您将创建的资源划分为逻辑命名的组,在一个命名空间中创建的资源可以对其他命名空间隐藏,您可以创建其他名称空间,并将资源和用户附加到这些名称空间。

默认存在以下命名空间:

  1. “default” 当应用未主动配置部署的 “namespace” 时使用;
  2. “kube-system” 集群系统应用;
  3. “kube-node-lease” 维持主机心跳;
  4. “kube-public” 集群公共信息;

例如在实际使用中可根据需求自定义了以下空间:

  1. “cloud-monitor” 监控类业务部署;
  2. “dcim-system” 运维类服务部署;
  3. “biz-dev-uptime” 业务类开发部署;

确保启动 seccomp profile

TODO;

确保配置 Security Context 在 POD 与 Containers 中

TODO;

确保默认命名空间 default 没有正式应用

kubectl get pods -n default



最后修改 2023.10.19: feat: add kube-bench (e7be518)