跳到主要内容

安全策略

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 级别的安全要求

参考资料