安全策略
Kubernetes 安全是一个多层次的话题,涉及集群基础设施、工作负载和数据的保护。理解 Kubernetes 的安全模型对于生产环境部署至关重要。本章将详细介绍 Kubernetes 的认证、授权、准入控制、Pod 安全和网络安全策略。
安全架构概览
当用户或应用访问 Kubernetes API 时,请求会经过一系列安全检查。理解这个流程有助于我们正确配置安全策略。
三个安全层次:
- 认证(Authentication):确认请求者的身份
- 授权(Authorization):确认请求者是否有权限执行操作
- 准入控制(Admission Control):验证和修改请求内容
认证(Authentication)
认证是确认"你是谁"的过程。Kubernetes 支持多种认证方式,可以同时启用多种。
认证方式对比
| 方式 | 说明 | 适用场景 |
|---|---|---|
| X.509 证书 | 客户端证书认证 | 用户访问、组件间通信 |
| Bearer Token | 静态 Token 或 JWT | CI/CD、自动化脚本 |
| ServiceAccount Token | Pod 内部使用 | 应用访问 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 |
seccompProfile | seccomp 过滤配置 |
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
小结
本章我们学习了:
- 认证:确认请求者身份,ServiceAccount 的使用
- 授权:RBAC 的核心概念和配置方法
- 准入控制:Pod 安全标准的实施
- Pod 安全:安全上下文配置
- 网络策略:NetworkPolicy 实现网络隔离
- Secret 加密:保护敏感数据
Kubernetes 安全是一个系统工程,需要在多个层面协同配置。在生产环境中,务必遵循最小权限原则,定期审计权限配置。
练习
- 创建一个 ServiceAccount,并赋予只读权限
- 配置 RoleBinding,限制特定用户只能访问特定命名空间
- 创建 NetworkPolicy,实现三层架构的网络隔离
- 配置 Pod 安全上下文,实现 restricted 级别的安全要求