跳到主要内容

安全策略

Kubernetes 安全是一个多层次的话题,涉及集群基础设施、工作负载和数据的保护。理解 Kubernetes 的安全模型对于生产环境部署至关重要。本章将详细介绍 Kubernetes 的认证、授权、准入控制、Pod 安全和网络安全策略。

安全架构概览​

当用户或应用访问 Kubernetes API 时,请求会经过一系列安全检查。理解这个流程有助于我们正确配置安全策略。

三个安全层次:

  1. 认证(Authentication):确认请求者的身份
  2. 授权(Authorization):确认请求者是否有权限执行操作
  3. 准入控制(Admission Control):验证和修改请求内容

认证(Authentication)​

认证是确认"你是谁"的过程。Kubernetes 支持多种认证方式,可以同时启用多种。

认证方式对比​

方式说明适用场景
X.509 证书客户端证书认证用户访问、组件间通信
Bearer Token静态 Token 或 JWTCI/CD、自动化脚本
ServiceAccount TokenPod 内部使用应用访问 API
Bootstrap Token集群引导时使用新节点加入集群
OIDC外部身份提供商集成企业环境、SSO
Webhook外部认证服务自定义认证逻辑

ServiceAccount 详解​

ServiceAccount 是 Pod 访问 Kubernetes API 的身份标识。每个命名空间都有一个默认的 ServiceAccount(default),但最佳实践是为应用创建专用的 ServiceAccount。

创建 ServiceAccount:

apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: production

在 Pod 中使用 ServiceAccount:

apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
serviceAccountName: my-app-sa
automountServiceAccountToken: false # 如果不需要访问 API,禁用自动挂载
containers:
- name: app
image: my-app:latest

Token 投射(Token Projection)

从 Kubernetes 1.20 开始,推荐使用 Token 投射方式获取 ServiceAccount Token:

apiVersion: v1
kind: Pod
metadata:
name: my-app
spec:
serviceAccountName: my-app-sa
containers:
- name: app
image: my-app:latest
volumeMounts:
- name: token
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
readOnly: true
volumes:
- name: token
projected:
sources:
- serviceAccountToken:
path: token
expirationSeconds: 3600 # Token 有效期 1 小时
audience: api # Token 的目标受众

这种方式生成的 Token 具有过期时间,更加安全。

授权(Authorization)​

授权是确认"你能做什么"的过程。Kubernetes 支持多种授权模式,其中 RBAC(基于角色的访问控制)是最常用的。

RBAC 核心概念​

RBAC 使用四个核心 API 对象来管理权限:

对象作用范围说明
Role命名空间定义命名空间内的权限规则
ClusterRole集群定义集群范围的权限规则
RoleBinding命名空间将 Role 绑定到主体(用户/组/SA)
ClusterRoleBinding集群将 ClusterRole 绑定到主体

Role 和 ClusterRole​

Role(命名空间级别):

Role 定义在特定命名空间内的权限。例如,允许读取 default 命名空间的 Pod:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # "" 表示核心 API 组
resources: ["pods"] # 资源类型
verbs: ["get", "list", "watch"] # 允许的操作

ClusterRole(集群级别):

ClusterRole 定义集群范围的权限,用于:

  • 集群范围的资源(如 nodes、persistentvolumes)
  • 非资源端点(如 /healthz)
  • 跨所有命名空间的资源
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: node-reader
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list", "watch"]

跨命名空间读取的 ClusterRole:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-reader-all-namespaces
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]

RoleBinding 和 ClusterRoleBinding​

RoleBinding 绑定 Role 到主体:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
# 绑定到用户
- kind: User
name: alice
apiGroup: rbac.authorization.k8s.io
# 绑定到 ServiceAccount
- kind: ServiceAccount
name: my-app-sa
namespace: default
# 绑定到组
- kind: Group
name: developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io

RoleBinding 绑定 ClusterRole(限制在命名空间内):

这是一个常用的模式:使用 ClusterRole 定义通用权限,然后通过 RoleBinding 将其限制在特定命名空间。

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods-in-development
namespace: development
subjects:
- kind: User
name: bob
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: pod-reader-all-namespaces
apiGroup: rbac.authorization.k8s.io

这个绑定让 bob 只能读取 development 命名空间的 Pod,而不是所有命名空间。

ClusterRoleBinding(跨所有命名空间):

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: read-secrets-global
subjects:
- kind: Group
name: security-team
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io

常用 Verbs 说明​

Verb说明示例
get获取单个资源kubectl get pod my-pod
list列出资源集合kubectl get pods
watch监听资源变化kubectl get pods -w
create创建资源kubectl create
update更新现有资源kubectl apply
patch部分更新资源kubectl patch
delete删除资源kubectl delete
deletecollection删除资源集合kubectl delete pods --all

资源名称限制(resourceNames)​

可以限制 Role 只对特定名称的资源生效:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: configmap-updater
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["app-config", "db-config"] # 只对这两个 ConfigMap 生效
verbs: ["get", "update"]

子资源权限​

某些资源有子资源,如 Pod 的日志(pods/log)或执行命令(pods/exec):

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-logs-reader
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list"]

聚合 ClusterRole​

聚合 ClusterRole 可以自动组合其他 ClusterRole 的规则:

# 聚合角色
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: monitoring
aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.example.com/aggregate-to-monitoring: "true"
rules: [] # 控制平面会自动填充

# 被聚合的角色
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: monitoring-pods
labels:
rbac.example.com/aggregate-to-monitoring: "true"
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]

RBAC 最佳实践​

1. 遵循最小权限原则

只授予必要的权限,避免使用通配符:

# 不好 - 权限过大
rules:
- apiGroups: [""]
resources: ["*"]
verbs: ["*"]

# 好 - 精确授权
rules:
- apiGroups: [""]
resources: ["pods", "services"]
verbs: ["get", "list"]

2. 使用命名空间隔离

将不同环境的应用部署在不同命名空间,通过 RBAC 控制访问:

# 开发环境只读
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: developers-read-only
namespace: development
subjects:
- kind: Group
name: developers
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: view
apiGroup: rbac.authorization.k8s.io

3. 定期审计权限

# 检查用户权限
kubectl auth can-i get pods --as=alice
kubectl auth can-i delete deployments --as=alice -n production

# 检查 ServiceAccount 权限
kubectl auth can-i list secrets --as=system:serviceaccount:default:my-sa

准入控制(Admission Control)​

准入控制器在请求经过认证和授权后,对请求进行验证和修改。

常用准入控制器​

控制器功能
NamespaceLifecycle防止在正在删除的命名空间中创建资源
LimitRanger强制资源限制
ServiceAccount自动挂载 ServiceAccount
DefaultStorageClass设置默认 StorageClass
DefaultTolerationSeconds设置默认容忍时间
NodeRestriction限制 kubelet 只能修改自己的节点
PodSecurity强制 Pod 安全标准
ValidatingAdmissionWebhook验证准入 Webhook
MutatingAdmissionWebhook变更准入 Webhook

Pod 安全标准​

Kubernetes 1.25 引入了 Pod Security Standards,替代已废弃的 PodSecurityPolicy。它定义了三个安全级别:

级别说明
privileged不受限制,适用于系统和基础设施 Pod
baseline最小限制,禁止明显的提权
restricted严格限制,遵循最佳实践

在命名空间级别启用:

apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
# 强制执行 restricted 策略
pod-security.kubernetes.io/enforce: restricted
# 版本号(可选)
pod-security.kubernetes.io/enforce-version: latest
# 审计模式
pod-security.kubernetes.io/audit: restricted
# 警告模式
pod-security.kubernetes.io/warn: restricted

三种模式:

  • enforce:违反策略时拒绝请求
  • audit:记录审计事件,但允许请求
  • warn:返回警告信息,但允许请求

Pod 安全上下文​

安全上下文定义 Pod 或容器的安全配置:

apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
# Pod 级别安全配置
runAsNonRoot: true # 必须以非 root 用户运行
runAsUser: 1000 # 以 UID 1000 运行
runAsGroup: 1000 # 以 GID 1000 运行
fsGroup: 1000 # 挂载的卷以 GID 1000 所属
seccompProfile:
type: RuntimeDefault # 使用默认 seccomp 配置
containers:
- name: app
image: nginx:latest
securityContext:
# 容器级别安全配置
allowPrivilegeEscalation: false # 禁止提权
readOnlyRootFilesystem: true # 只读根文件系统
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop:
- ALL # 丢弃所有 Linux capabilities

安全上下文字段说明:

字段说明
runAsUser以指定 UID 运行进程
runAsGroup以指定 GID 运行进程
runAsNonRoot必须以非 root 用户运行
fsGroup挂载卷的组 ID
allowPrivilegeEscalation是否允许进程提权
readOnlyRootFilesystem根文件系统是否只读
capabilities.add添加 Linux capabilities
capabilities.drop丢弃 Linux capabilities
seccompProfileseccomp 过滤配置

NetworkPolicy 网络策略​

NetworkPolicy 控制 Pod 之间的网络流量,实现网络层面的隔离。它类似于云环境中的安全组,但工作在 Pod 级别。

前提条件​

NetworkPolicy 需要网络插件支持。常见的支持 NetworkPolicy 的 CNI 插件:

  • Calico
  • Cilium
  • Weave Net
  • Kube-OVN

如果使用不支持 NetworkPolicy 的网络插件(如 Flannel 默认配置),创建 NetworkPolicy 资源不会报错,但不会生效。

隔离概念​

理解 NetworkPolicy 的关键在于理解"隔离"的概念:

默认行为:

  • Pod 默认是非隔离的,允许所有入站和出站流量
  • 当 NetworkPolicy 选择了某个 Pod(通过 podSelector),该 Pod 在策略指定的方向上变为隔离状态

隔离状态:

  • 隔离状态:只允许策略明确允许的流量
  • 非隔离状态:允许所有流量

NetworkPolicy 结构​

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: example-policy
namespace: default
spec:
podSelector: # 选择应用策略的 Pod
matchLabels:
app: backend
policyTypes: # 策略类型
- Ingress
- Egress
ingress: # 入站规则
- from:
- podSelector: # 允许来自特定 Pod
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
egress: # 出站规则
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 3306

选择器类型​

NetworkPolicy 支持四种选择器:

1. podSelector(同一命名空间内的 Pod)

ingress:
- from:
- podSelector:
matchLabels:
app: frontend

2. namespaceSelector(整个命名空间)

ingress:
- from:
- namespaceSelector:
matchLabels:
environment: production

3. namespaceSelector + podSelector(特定命名空间内的特定 Pod)

注意:这两个选择器必须写在同一个 from 元素中:

ingress:
- from:
- namespaceSelector:
matchLabels:
user: alice
podSelector:
matchLabels:
role: client

4. ipBlock(IP 地址块)

egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
except:
- 10.0.1.0/24 # 排除特定范围

默认策略示例​

默认拒绝所有入站流量:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {} # 选择所有 Pod
policyTypes:
- Ingress # 不定义 ingress 规则,意味着拒绝所有

默认拒绝所有出站流量:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
spec:
podSelector: {}
policyTypes:
- Egress

默认拒绝所有流量(入站和出站):

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress

默认允许所有入站流量:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
spec:
podSelector: {}
ingress:
- {} # 空规则表示允许所有
policyTypes:
- Ingress

实战示例​

多层级应用的网络隔离:

# 前端层:允许来自 Ingress Controller 的流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-policy
spec:
podSelector:
matchLabels:
tier: frontend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
ports:
- port: 8080
egress:
- to:
- podSelector:
matchLabels:
tier: backend
ports:
- port: 8080
---
# 后端层:只允许来自前端,可访问数据库
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-policy
spec:
podSelector:
matchLabels:
tier: backend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
tier: frontend
ports:
- port: 8080
egress:
- to:
- podSelector:
matchLabels:
tier: database
ports:
- port: 3306
# 允许 DNS 查询
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- port: 53
protocol: UDP
---
# 数据库层:只允许来自后端
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database-policy
spec:
podSelector:
matchLabels:
tier: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
tier: backend
ports:
- port: 3306

端口范围​

Kubernetes 1.25+ 支持指定端口范围:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: port-range-policy
spec:
podSelector:
matchLabels:
app: myapp
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- port: 32000
endPort: 32768 # 允许 32000-32768 端口范围
protocol: TCP

NetworkPolicy 最佳实践​

1. 先拒绝再允许

先创建默认拒绝策略,再创建允许策略:

# 第一步:默认拒绝
kubectl apply -f default-deny-all.yaml

# 第二步:按需允许
kubectl apply -f allow-specific.yaml

2. 不要忘记 DNS

如果禁止了出站流量,需要显式允许 DNS:

egress:
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- port: 53
protocol: UDP

3. 测试网络策略

# 使用临时 Pod 测试连通性
kubectl run test --rm -it --image=busybox -- wget -qO- http://backend:8080

# 查看网络策略
kubectl get networkpolicy -A

# 详细查看
kubectl describe networkpolicy backend-policy

4. 使用标签选择器而非名称

NetworkPolicy 不支持直接指定命名空间名称,但可以使用标准标签:

# Kubernetes 自动为每个命名空间添加 kubernetes.io/metadata.name 标签
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: production

Secret 加密存储​

Secret 默认以明文存储在 etcd 中,建议启用加密。

加密配置​

# encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- kms:
name: my-kms
endpoint: unix:///tmp/kms-provider.socket
cachesize: 100
- aescbc:
keys:
- name: key1
secret: <base64-encoded-key>
- identity: {} # 兜底,用于解密未加密的 Secret

配置 kube-apiserver 使用加密配置:

kube-apiserver --encryption-provider-config=/etc/kubernetes/encryption-config.yaml

加密现有 Secret​

# 加密所有 Secret
kubectl get secrets --all-namespaces -o json | kubectl replace -f -

# 验证加密
kubectl describe secret -n kube-system <secret-name>

安全检查清单​

集群层面​

  • 启用 RBAC
  • 启用准入控制器
  • 启用 etcd 加密
  • 启用审计日志
  • 限制 API Server 访问
  • 定期更新集群版本

工作负载层面​

  • 为应用创建专用 ServiceAccount
  • 配置 Pod 安全上下文
  • 使用 NetworkPolicy 隔离网络
  • 不以 root 运行容器
  • 设置资源限制

访问控制层面​

  • 遵循最小权限原则
  • 定期审计权限
  • 使用命名空间隔离环境
  • 启用多因素认证(OIDC)

常用命令​

# 检查权限
kubectl auth can-i get pods --as=alice
kubectl auth can-i delete deployments --as=bob -n production

# 查看 RBAC 资源
kubectl get roles,rolebindings -n default
kubectl get clusterroles,clusterrolebindings

# 查看 ServiceAccount
kubectl get serviceaccounts -A

# 查看 NetworkPolicy
kubectl get networkpolicy -A

# 查看 Pod 安全上下文
kubectl get pod <name> -o jsonpath='{.spec.securityContext}'

# 测试网络连通性
kubectl run test --rm -it --image=busybox -- wget -qO- http://service:port

小结​

本章我们学习了:

  1. 认证:确认请求者身份,ServiceAccount 的使用
  2. 授权:RBAC 的核心概念和配置方法
  3. 准入控制:Pod 安全标准的实施
  4. Pod 安全:安全上下文配置
  5. 网络策略:NetworkPolicy 实现网络隔离
  6. Secret 加密:保护敏感数据

Kubernetes 安全是一个系统工程,需要在多个层面协同配置。在生产环境中,务必遵循最小权限原则,定期审计权限配置。

练习​

  1. 创建一个 ServiceAccount,并赋予只读权限
  2. 配置 RoleBinding,限制特定用户只能访问特定命名空间
  3. 创建 NetworkPolicy,实现三层架构的网络隔离
  4. 配置 Pod 安全上下文,实现 restricted 级别的安全要求

参考资料​