字数:约4200字 | 阅读时间:12分钟
“Kubernetes安全不是加一把锁,而是建立纵深防御体系。”


在Kubernetes集群中,Pod是最小的调度单位,也是安全攻击的首要目标。无论是容器逃逸、横向移动还是数据窃取,攻击者的最终目的往往落脚在Pod层面。Kubernetes 1.29引入了多项安全增强,但很多团队对这些能力的理解还停留在”听说过”的阶段。

本文从实战角度出发,深入讲解Pod Security Standards、网络策略与入站出站流量的精细控制,结合真实案例与代码示例,帮助你构建真正可落地的容器安全体系。

一、Pod Security Standards:你的Pod是否符合最低要求?

Kubernetes 1.29延续了Pod Security admission(PSA,正式毕业成为GA特性),提供了三种预置策略级别:

级别 说明
privileged 完全放开,不做任何限制(仅用于系统组件)
baseline 最小安全基线,防止已知特权提升
restricted 严格限制,要求生产级安全标准

1.1 快速验证你的Namespace

1
2
3
4
5
6
7
# 查看命名空间当前的策略模式
kubectl get namespace <namespace> -o yaml | grep -A 5 pod-security.kubernetes.io

# 检查当前Pod是否符合baseline级别
kubectl label --overwrite ns <namespace> \
pod-security.kubernetes.io/enforce=baseline \
pod-security.kubernetes.io/enforce-version=v1.29

1.2 baseline vs restricted:差异在哪里?

baseline级别允许一些危险能力,但要求必须显式声明;restricted则完全禁止。

1
2
3
4
5
6
7
8
9
10
# baseline允许的:必须显式声明
securityContext:
capabilities:
add: ["NET_ADMIN"] # 需要显式声明才会被放行

# restricted完全禁止的(即使声明也不允许)
# - NET_ADMIN
# - SYS_ADMIN
# - SYS_MODULE
# - DAC_READ_SEARCH

实战建议:先从baseline开始,验证无误后逐步迁移到restricted。直接用restricted会遇到大量Pod无法调度的麻烦。

1.3 多命名空间批量应用

生产环境中往往有几十个命名空间,逐个配置费时费力。使用Kustomize可以批量处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# base/ns-patch.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
patches:
- patch: |
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: v1.29
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/warn-version: v1.29
target:
kind: Namespace
name: production

二、网络策略:Pod级别的防火墙

Pod默认情况下可以自由通信,这意味着某个被攻陷的Pod可以探测整个集群的内网。NetworkPolicy是Kubernetes的网络隔离机制,相当于Pod级别的”白名单防火墙”。

2.1 默认拒绝:先禁后放

网络策略的第一原则:默认拒绝所有入站流量

1
2
3
4
5
6
7
8
9
10
11
# default-deny-all.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {} # 选择所有Pod
policyTypes:
- Ingress # 明确声明:拒绝所有入站
- Egress # 明确声明:拒绝所有出站

这个策略创建后,namespace内的所有Pod都无法收发流量。必须为需要通信的Pod添加允许规则

2.2 允许特定Pod之间的通信

最常见的场景:Web层可以访问Backend层,但禁止Backend主动访问Web层。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# allow-web-to-backend.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-web-to-backend
namespace: production
spec:
podSelector:
matchLabels:
app: backend # 目标:backend Pod
policyTypes:
- Ingress # 控制入站流量
ingress:
- from:
- podSelector:
matchLabels:
app: web # 来源:web Pod
ports:
- protocol: TCP
port: 8080 # 只允许访问8080端口

2.3 限制出站流量:DNS是特殊案例

出站流量中最容易被忽略的是DNS。Pod需要DNS才能解析Service地址,因此必须单独允许。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# allow-dns-egress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: production
spec:
podSelector: {} # 应用于所有Pod
policyTypes:
- Egress
egress:
# 规则1:DNS (kube-dns Service的IP)
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
# 规则2:允许访问集群内其他命名空间的Pod(按标签)
- to:
- podSelector: {}
ports:
- protocol: TCP
port: 80
port: 443
# 规则3:拒绝其他所有出站
- to:
- podSelector: {}
ports:
- protocol: TCP
port: 1-65535

2.4 使用CIDR限制IP段访问

对于需要访问特定IP段的服务(如云数据库):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# allow-specific-cidr.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-db-egress
namespace: production
spec:
podSelector:
matchLabels:
app: database-client
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: "10.0.0.0/8" # 允许访问内网IP段
except:
- 10.0.0.0/16 # 排除某些子网
ports:
- protocol: TCP
port: 5432 # PostgreSQL默认端口

三、安全上下文:Pod和容器的权限控制

Security Context决定了容器运行时具有哪些权限。设置得当可以大幅降低容器逃逸的风险。

3.1 Pod级别与容器级别的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
runAsNonRoot: true # Pod级别:不允许以root运行
runAsUser: 1000 # Pod级别:指定运行用户ID
runAsGroup: 1000 # Pod级别:指定运行组ID
fsGroup: 1000 # Pod级别:指定FSGroup
seccompProfile: # Pod级别:启用seccomp
type: RuntimeDefault
sysctls: # Pod级别:sysctl参数限制
- name: net.ipv4.ping_group_range
value: "1 1000"
containers:
- name: app
image: myapp:latest
securityContext:
allowPrivilegeEscalation: false # 容器级别:禁止提权
readOnlyRootFilesystem: true # 容器级别:只读根文件系统
capabilities:
drop: # 容器级别:删除所有能力
- ALL
add:
- NET_BIND_SERVICE # 只添加需要的最小能力
seccompProfile:
type: RuntimeDefault

3.2 常用能力(Capabilities)说明

Capability 含义 生产环境建议
NET_BIND_SERVICE 允许绑定低于1024的端口 按需添加
NET_ADMIN 允许网络管理 禁止
SYS_ADMIN 超级管理员权限 禁止
SYS_MODULE 加载内核模块 禁止
DAC_READ_SEARCH 绕过文件权限检查 禁止

黄金法则:所有容器都应该drop: [ALL],然后按需add最小集。

3.3 验证安全上下文是否生效

1
2
3
4
5
6
7
8
9
# 检查Pod的securityContext设置
kubectl get pod <pod-name> -o jsonpath='{.spec.securityContext}'

# 检查容器的capabilities
kubectl exec <pod-name> -- cat /proc/self/status | grep Cap

# 验证是否以非root用户运行
kubectl exec <pod-name> -- id
# 期望输出:uid=1000 gid=1000 groups=1000

四、ServiceAccount与RBAC:最小权限原则

Pod通过ServiceAccount与Kubernetes API交互。默认情况下,每个Pod都绑定了defaultServiceAccount,具有查看cluster信息的权限,这是安全隐患。

4.1 不再使用的SA权限立即收回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 创建仅限当前Pod使用的SA
apiVersion: v1
kind: ServiceAccount
metadata:
name: myapp-sa
namespace: production
automountServiceAccountToken: false # 默认关闭自动挂载
---
# 创建仅包含所需权限的Role
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: myapp-read-configmaps
namespace: production
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
resourceNames: ["myapp-config"] # 精确到特定资源名称
---
# 将Role绑定到特定SA
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: myapp-sa-read-configmaps
namespace: production
subjects:
- kind: ServiceAccount
name: myapp-sa
namespace: production
roleRef:
kind: Role
name: myapp-read-configmaps
apiGroup: rbac.authorization.k8s.io
---
# Pod引用专属SA
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
namespace: production
spec:
serviceAccountName: myapp-sa # 显式指定SA
...

4.2 ClusterRoleBinding的最小化

很多集群管理员习惯给SA绑定cluster-admin角色,这是极度危险的。以下是正确做法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 错误示例:权限过大
subjects:
- kind: ServiceAccount
name: default
namespace: production
roleRef:
kind: ClusterRole
name: cluster-admin # 危险!

# 正确示例:精确权限
subjects:
- kind: ServiceAccount
name: myapp-sa
namespace: production
roleRef:
kind: Role # 用Role而非ClusterRole,只在当前命名空间有效
name: myapp-specific-permissions

五、实战案例:生产环境Nginx安全加固

以下是一个完整的生产级Nginx Pod配置,涵盖前面讲到的所有安全维度:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
apiVersion: v1
kind: Pod
metadata:
name: nginx-secure
namespace: production
labels:
app: nginx
environment: production
spec:
serviceAccountName: nginx-sa
securityContext:
runAsNonRoot: true
runAsUser: 101 # Nginx默认用户
fsGroup: 101
seccompProfile:
type: RuntimeDefault
containers:
- name: nginx
image: nginx:1.27-alpine
ports:
- containerPort: 80
name: http
- containerPort: 443
name: https
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
resources:
requests:
memory: "64Mi"
cpu: "50m"
limits:
memory: "128Mi"
cpu: "200m"
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d
readOnly: true
- name: nginx-cache
mountPath: /var/cache/nginx
- name: nginx-run
mountPath: /var/run
volumes:
- name: nginx-config
configMap:
name: nginx-config
- name: nginx-cache
emptyDir:
medium: Memory
sizeLimit: "32Mi"
- name: nginx-run
emptyDir:
medium: Memory
sizeLimit: "1Mi"

六、安全检查清单

检查项 命令 期望结果
不以root运行 kubectl get pod -o jsonpath='{.spec.securityContext.runAsNonRoot}' true
不允许权限提升 kubectl get pod -o jsonpath='{.spec.containers[*].securityContext.allowPrivilegeEscalation}' false
删除所有Capabilities kubectl get pod -o jsonpath='{.spec.containers[*].securityContext.capabilities.drop}' [ALL]
只读根文件系统 kubectl get pod -o jsonpath='{.spec.containers[*].securityContext.readOnlyRootFilesystem}' true
启用Seccomp kubectl get pod -o jsonpath='{.spec.securityContext.seccompProfile.type}' RuntimeDefault
NetworkPolicy存在 kubectl get networkpolicy 列出至少一个策略
Pod使用专属SA kubectl get pod -o jsonpath='{.spec.serviceAccountName}' default

七、总结

Kubernetes安全不是单点配置,而是纵深防御体系:

  1. Pod Security Standards:通过baselinerestricted的渐进式迁移,确保Pod满足最小权限要求
  2. NetworkPolicy:以default-deny-all为起点,按业务需要显式放行,控制Pod间通信
  3. Security Context:从容器级别限制权限,以drop: [ALL]为起点,按需add最小能力集
  4. RBAC:ServiceAccount最小权限原则,禁止使用cluster-admin,精确到资源级别

下一篇文章我们将深入讲解Kubernetes的Secret管理、Pod加密存储、以及在多租户场景下的安全隔离策略。


延伸阅读