保障集群内节点和网络安全
在 pod 中使用宿主节点的 Linux 命名空间
pod 中的容器通常在分开的 Linux 命名空间中运行。 这些命名空间将容器中的进程与其他容器中,或者宿主机默认命名空间中的进程隔离开来。
例如,每一个 pod 有自己的 IP 和端口空间,这是因为它拥有自己的网络命名空间。类似地,每一个 pod 拥有自己的进程树,因为它有自己的 PID 命名空间 。同样地 pod 拥有自己的 IPC 命名空间,仅允许同一 pod 内的进程通过进程间通信 (InterProcess Communication, 简称IPC) 机制进行交流。
在 pod 中使用宿主节点的网络命名空间
部分 pod (特别是系统 pod ) 需要在宿主节点的默认命名空间中运行,以允许它们看到和操作节点级别的资源和设备。例如,某个 pod 可能需要使用宿主节点上的网络适配器,而不是自己的虚拟网络设备。这可以通过将 pod spec 中的 hostNetwork 设置为true实现。
spec.hostNetwork:true
在这种情况下,这个 pod 可以使用宿主节点的网络接口,而不是拥有自己独立的网络。这意味着这个 pod 没有自己的IP地址;如果这个 pod 中的某一进程绑定了某个端口,那么该进程将被绑定到宿主节点的端口上。
vim pod-with-host-network.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-with-host-network
spec:
hostNetwork: true # 使用宿主机节点的网络名称空间
containers:
- name: main
image: alpine
command: ["/bin/sleep", "999999"]
kubectl exec pod-with-host-network ifconfig
docker0 Link encap:Ethernet HWaddr 02:42:8D:D4:A6:6B
inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0
...
ens33 Link encap:Ethernet HWaddr 00:0C:29:5F:F0:DB
inet addr:192.168.160.135 Bcast:192.168.160.255 Mask:255.255.255.0
inet6 addr: fe80::494b:af17:216a:3ef0/64 Scope:Link
...
flannel.1 Link encap:Ethernet HWaddr B6:87:54:A6:FA:9F
inet addr:10.244.1.0 Bcast:10.244.1.0 Mask:255.255.255.255
...
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
...
绑定宿主节点上的端口而不使用宿主节点的网络命名空间
一个与此有关的功能可以让 pod 在拥有自己的网络命名空间的同时,将端口绑定到宿主节点的端口上。这可以通过配置 pod 的 spec.containers.ports 字段中某个容器某一端口的 hostPort 属性来实现。
不要混淆使用 hostPort 的 pod 和通过 NodePort 服务暴露的 pod ,它们是不同的。
对于一个使用 hostPort 的 pod,到达宿主节点的端口的连接会被直接转发到 pod 的对应端口上;
然而在 NodePort 服务中,到达宿主节点的端口的连接将被转发到随机选取的 pod 上(这个 pod 可能在其他节点上);
另外一个区别是,对于使用 hostPort 的 pod ,仅有运行了这类 pod 的节点会绑定对应的端口;而 NodePort 类型的服务会在所有的节点上绑定端口,即使这个节点上没有运行对应的 pod (如图中所示的节点3)。
很重要的一点是,如果一个 pod 绑定了宿主节点上的一个特定端口,每个宿主节点只能调度一个这样的 pod 实例,因为两个进程不能绑定宿主机上的同一个端口。调度器在调度 pod 时会考虑这一点, 所以它不会把两个这样的 pod 调度到同一个节点上。
如果要在3个节点上部署4个这样的 pod 副本,只有3个副能够成功部署(剩余1个pod 保持Pending 状态)
vim kubia-hostport.yaml
apiVersion: v1
kind: Pod
metadata:
name: kubia-hostport
spec:
containers:
- image: luksa/kubia
name: kubia
ports:
- containerPort: 8080 # 该容器可以通过 pod IP 的8080端口访问
hostPort: 9000 # 也可以通过所在节点的9000端口访问
protocol: TCP
kubectl get pod -o wide
kubia-hostport 1/1 Running 0 10m 10.244.1.7 node1 <none> <none>
curl http://10.244.1.7:8080
You've hit kubia-hostport
curl http://192.168.160.135:9000 node1IP
You've hit kubia-hostport
使用宿主节点的 PIO IPC 命名空间
pod spec 中的 hostPID 和 hostIPC 选项与 hostNetwork 相似。当它们被设置为 true 时, pod 中的容器会使用宿主节点的 PID 和 IPC 名空间 ,分别允许它们看到宿主机上的全部进程,或通过 IPC 机制与它们通信。
vim pod-with-host-pid-and-ipc.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-with-host-pid-and-ipc
spec:
hostPID: true
hostIPC: true
containers:
- name: main
image: alpine
command: ["/bin/sleep", "999999"]
[root@master aq]# kubectl exec pod-with-host-pid-and-ipc ps aux
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
PID USER TIME COMMAND
1 root 0:03 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
2 root 0:00 [kthreadd]
3 root 0:00 [ksoftirqd/0]
5 root 0:00 [kworker/0:0H]
7 root 0:00 [migration/0]
...
将 hostIPC 设置为 true,pod 中的进程就可以通过进程间通信机制与宿上的其他所有进程进行通信。
配置节点的安全上下文
除了让 pod 使用宿主节点的 Linux 命名空间,还可以在 pod 或其所属容器的描述中通过 security-Context 边项配置其他与安全性相关的特性。这个选项可以运用于整个 pod ,或者每个 pod 中单独的容器。
了解安全上下文中可以配置的内容
配置安全上下文可以允许你做很多事
- 指定容器中运行进程的用户(用户 ID )
- 阻止容器使用 root 用户运行(容器的默认运行用户通常在其镜像中指定,所以可能需要阻止容器 root 用户运行)
- 使用特权模式运行容器,使其对宿主节点的内核具有完全的访问权限
- 与以上相反,通过添加或禁用内核功能,配置细粒度的内核访问权限
- 设置SELinux( Security Enhaced Linux, 安全增强型 Linux )选项,加强对容器的限制。
- 阻止进程写入容器的根文件系统
运行 pod 而不配置安全上下文
kubectl run pod-with-defaults --image alp ine --restart Never -- /bin/sleep 999999
pod/pod-with-defaults created
kubectl exec pod-with-defaults id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
这个容器在用户 ID (uid )为0的用户,即 root ,用户组 ID (gid )为 0(同样是 root )的用户组下运行。它同样还属于一些其他的用户组。
注意: #F44336 容器运行时使用的用户在镜像中指定 Dockerfile 中,这是通过使用 USER 命令实现的。 如果该命令被省略,容器将使用 root 用户运行。
使用指定用户运行容器
为了使用一个与镜像中不同的用户 ID 来运行 pod, 需要 设置该 podsecurityContext.runAsUser 选项。可以通过以下代码清单来运行一个使用 guest 用户运行的容器,该用户在 alpine 镜像中的用户 ID 405。
vim pod-as-user-guest.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-as-user-guest
spec:
containers:
- name: main
image: alpine
command: ["/bin/sleep", "999999"]
securityContext:
runAsUser: 405 # 需要指明一个用户ID,而不是用户名(id 405 对应 guest用户)
kubectl exec pod-as-user-guest id
uid=405(guest) gid=100(users)
阻止容器以 root 用户运行
假设有一个己经部署好的 pod,它使用一个在 Dockefie 使用 USER daemon 命令制作的镜像,使其在 daemon 用户下运行。 如果攻击者获取了访问镜像仓库的权限, 并上传了一个标签完全相同,在 root 用户下运行的镜像,会发生什么?当Kubernetes 的调度器运行该 pod 的新实例时,kubelet 会下载攻击者的镜像, 并运行该镜像中的任何代码。
虽然容器与宿主节点基本上是隔离的,使用 root 用户运行容器中的进程仍然是一种不好的实践。 例如,当宿主节点上的一个目录被挂载到容器中时,如果这个容器中的进程使用了 root 用户运行,它就拥有该目录的完整访问权限; 如果用非 root用户运行,则没有完整权限。
为了防止以上的攻击场景发生,可以进行配置,使得 pod 中的容器以非 root 用户运行。
vim pod-run-as-non-root.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-run-as-non-root
spec:
containers:
- name: main
image: alpine
command: ["/bin/sleep", "999999"]
securityContext:
runAsNonRoot: true # 这个容器只允许以非 root 用户运行
部署这个 pod 之后,它会被成功调度,但是不允许运行:
kubectl get pod pod-run-as-non-root
NAME READY STATUS RESTARTS AGE
pod-run-as-non-root 0/1 CreateContainerConfigError 0 85s
使用特权模式运行 pod
有时 pod 需要做它们的宿主节点上能够做的任何事,例如操作被保护的系统设备,或使用其他在通常容器中不能使用的内核功能。这种 pod 的一个样例就是 kube-proxy pod ,修改宿主机的 iptables 来让 uberne es 中的服务规 生效。
为获取宿主机内核的完整权限,该 pod 需要在特权模式下运行。这可以通过将容器的 securityContext 中的 privileged 设置为 true 实现 。(给予容器无限力量)
vim pod-privileged.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-privileged
spec:
containers:
- name: main
image: alpine
command: ["/bin/sleep", "999999"]
securityContext:
privileged: true # 这个容器将在特权模式下运行
/dev 是特殊目录,该目录包含系统中所有设备对应的设备文件,这些文件不是磁盘上的常规文件,而是用于与设备通信的特殊文件 。
为容器单独添加内核功能
一个更加安全的做法是只给予它使用真正需要的内核功能的权限。 Kubernetes允许为特定的容器添加内核功能, 或禁用部分内核功能, 以允许对容器进行 更加精细的权限控制, 限制攻击者潜在侵入的影响。
例如 一个容器通常不允许修改系统时间(硬件时钟的时间)。 可以通过在 pod-with-defaults pod 中修改设定时间来验证:
[root@master anquan]# kubectl exec -it pod-as-user-guest -- date
Thu Jan 20 10:42:59 UTC 2022
[root@master anquan]# kubectl exec -it pod-as-user-guest -- date +%T -s "12:00:00"
date: can't set date: Operation not permitted
12:00:00
[root@master anquan]# kubectl exec -it pod-as-user-guest -- date
Thu Jan 20 10:43:25 UTC 2022
如果需要允许容器修改系统时间,可以在容器的 capbilities 里 add 一项名为 CAP_SYS_TIME 的功能
vim pod-add-settime-capability.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-add-settime-capability
spec:
containers:
- name: main
image: alpine
command: ["/bin/sleep", "999999"]
securityContext:
capabilities: # 在内核功能 securityContext 中添加或禁用
add:
- SYS_TIME # 在这里添加了 SYS_TIME 功能
注意: #F44336 Linux内核功能的名称通常以CAP_开头。但在podspec中指定内核功能时,必须省略CAP_前缀。
[root@master anquan]# kubectl exec -it pod-add-settime-capability -- date
Thu Jan 20 10:50:58 UTC 2022
[root@master anquan]# kubectl exec -it pod-add-settime-capability -- date +%T -s "12:00:00"
12:00:00
[root@master anquan]# kubectl exec -it pod-add-settime-capability -- date
Thu Jan 20 12:00:01 UTC 2022
在容器中禁用内核功能
也可以禁用容器中的内核功能。
例如,默认情况下容器拥有CAP_CHOWN权限,允许进程修改文件系统中文件的所有者。
[root@master anquan]# kubectl exec pod-privileged -- chown ftp /tmp
[root@master anquan]# kubectl exec pod-privileged -- ls -ls / | grep tmp
0 drwxrwxrwt 1 ftp root 6 Nov 24 09:20 tmp
[root@master anquan]#
为了阻止容器的此种行为,在容器中 securityContext.capabilities.drop 列表加入选项,以禁用这个修改文件所有者的内核功能。
vim pod-drop-chown-capability.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-drop-chown-capability
spec:
containers:
- name: main
image: alpine
command: ["/bin/sleep", "999999"]
securityContext:
capabilities:
drop: # 在这里禁止了容器修改文件的所有者
- CHOWN
禁用 CHOWN 内核功能后, 不允许在这个 pod 中修改文件所有者
[root@master anquan]# kubectl create -f pod-drop-chown-capability.yaml
pod/pod-drop-chown-capability created
[root@master anquan]# kubectl exec -it pod-drop-chown-capability -- ls -la / | grep tmp
drwxrwxrwt 2 root root 6 Nov 24 09:20 tmp
[root@master anquan]# kubectl exec -it pod-drop-chown-capability -- chown ftp /tmp
chown: /tmp: Operation not permitted
command terminated with exit code 1
[root@master anquan]# kubectl exec -it pod-drop-chown-capability -- ls -la / | grep tmp
drwxrwxrwt 2 root root 6 Nov 24 09:20 tmp
阻止对容器根文件系统的写入
因为安全原因可能需要阻止容器中的进程对容器的根文件系统进行写入,仅允许它们写入挂载的存储卷。可以通过阻止容器写入自己的根文件系统(应用的可执行代码的通常储存位置)来防止。将容器的 securityContext.readOnlyRootFilesystem 设置为 true 来实现。
vim pod-with-readonly-filesystem.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-with-readonly-filesystem
spec:
containers:
- name: main
image: alpine
command: ["/bin/sleep", "999999"]
securityContext: # 这个容器的根文件系统不允许写入
readOnlyRootFilesystem: true
volumeMounts:
- name: my-volume # 但是向/volume写入是允许的,
mountPath: /volume # 因为这个目录挂载了一个存储卷
readOnly: false
volumes:
- name: my-volume
emptyDir:
这个pod中的容器虽然以root用户运行, 拥有/目录的写权限, 但在该目录下写入一个文件会失败,对挂载的卷的写入是允许的
[root@master anquan]# kubectl exec -it pod-with-readonly-filesystem -- touch /new-file
touch: /new-file: Read-only file system
command terminated with exit code 1
[root@master anquan]# kubectl exec -it pod-with-readonly-filesystem -- touch /volume/new-file
[root@master anquan]# kubectl exec -it pod-with-readonly-filesystem -- ls -la /volume/new-file
-rw-r--r-- 1 root root 0 Jan 21 02:35 /volume/new-file
如果容器的根文件系统是只读的, 你很可能需要为应用会写入的每一个目录(如日志、 磁盘缓存等)挂载存储卷。
设置pod级别的安全上下文
以上的例子都是对单独的容器设置安全上下文。 这些选项中的一部分也可以从pod级别设定(通过pod.spec.securityContext 属性)。它们会作为pod中每一个容器的默认安全上下文, 但是会被容器级别的安全上下文覆盖。
容器使用不同用户运行时共享存储卷
使用存储卷在pod的不同容器中共享数据。可以顺利地一个容器中写入数据,在另一个容器中读出这些数据。但这只是因为两个容器都以 root 用户运行,对存储卷中的所有文件拥有全部权限。
现在假设使用前面介绍的 runAsUser 选项。你可能需要在一个 pod 中用两个不同的用户运行两个容器(可能是两个第三方的容器,都以它们自己的特定用户运行进程)。如果这样的两个容器通过存储卷共享文件,它们不一定能够读取或写入另一个容器的文件。
因此,Kubernetes 允许为 pod 中所有容器指定 supplemental 组,以允许它们无论以哪个用户 ID 运行都可以共享文件 。这可以通过以下两个属性设置:
- fsGroup
- supplementalGroups
vim pod-with-shared-volume-fsgroup.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-with-shared-volume-fsgroup
spec:
securityContext: # fsGroup和supplementalGroups在 pod级别的安全上下文中定义
fsGroup: 555
supplementalGroups: [666, 777]
containers:
- name: first
image: alpine
command: ["/bin/sleep", "999999"]
securityContext:
runAsUser: 1111 # 第一个容器使用的用户ID为1111
volumeMounts:
- name: shared-volume # 两个容器使用同一存储卷
mountPath: /volume
readOnly: false
- name: second
image: alpine
command: ["/bin/sleep", "999999"]
securityContext:
runAsUser: 2222 # 第二个容器使用的用户ID为2222
volumeMounts:
- name: shared-volume # 两个容器使用同一存储卷
mountPath: /volume
readOnly: false
volumes:
- name: shared-volume
emptyDir:
查看第一个容器的用户ID和组ID
[root@master anquan]# kubectl exec -it pod-with-shared-volume-fsgroup -c first -- id
uid=1111 gid=0(root) groups=555,666,777
id 命令显示, 这个pod运行在ID为1111 的用户下, 它的用户组为0 (root),,但用户组555、 666、 777也关联到了该用户下。
在pod的定义中, 将fsGroup设置成了555, 因此, 存储卷属千用户组ID为 555 的用户组。
[root@master anquan]# kubectl exec -it pod-with-shared-volume-fsgroup -c first -- ls -l / | grep volume
drwxrwsrwx 2 root 555 6 Jan 21 03:05 volume
该容器在这个存储卷所在目录中创建的文件, 所属的用户ID为1111 (即该容器运行时使用的用户ID), 所属的用户组ID为555
/ $ echo foo >/volume/foo
/ $ ls -l /volume/
total 4
-rw-r--r-- 1 1111 555 4 Jan 21 03:13 foo
这个文件的所属用户情况与通常设置下的新建文件不同。 在通常情况下, 某一用户新创建文件所属的用户组ID,与该用户的所属用户组ID相同,在这种情下是0。在这个容器的根文件系统中创建一个文件, 可以验证这一点:
/ $ echo foo >/tmp/foo
/ $ ls -l /tmp/
total 4
-rw-r--r-- 1 1111 root 4 Jan 21 03:16 foo
安全上下文中的 fsGroup 属性当进程在存储卷中创建文件时起作用
而 supplementalGroups 属性定义了某个用户所关联的额外的用户组
限制pod使用安全相关的特性
部署一个特权模式的 pod ,需要有一种机制阻止用户使用其中的部分功能。 集群管理入员可以通过创建 PodSecurityPolicy 资源来限制对以上提到的安全相关的特性的使用。
PodSecurityPolicy资源介绍
PodSecurityPolicy 是一种集群级别(无命名空间)的资源,它定义了用户能否在 pod 中使用各种安全相关的特性。维护 PodSecurityPolicy 资源中配置策略的工作由集成在 API 服务器中的 PodSecurityPolicy 准入控制插件完成。
当有人向 API 服务器发送 pod 资源时, PodSecurityPolicy 准入控制插件会将这个 pod 与已经配置的 PodSecurityPolicy 进行校验。 如果这个 pod 符合集群中已有安全策略, 它会被接收并存入 etcd; 否则它会立即被拒绝。 这个插件也会根据安全策略中配置的默认值对 pod 进行修改。
了解 PodSecurityPolicy 可以做的事
一个 PodSecurityPolicy 资源可以定义以下事项:
- 是否允许 pod 使用宿主节点的 PID、 IPC、 网络命名空间
- pod 允许绑定的宿主节点端口
- 容器运行时允许使用的用户 ID
- 是否允许拥有特权模式容器的 pod
- 允许添加哪些内核功能, 默认添加哪些内核功能, 总是禁用哪些内核功能
- 允许容器使用哪些 SELinux 选项
- 容器是否允许使用可写的根文件系统
- 允许容器在哪些文件系统组下运行
- 允许 pod 使用哪些类型的存储卷
检视一个 PodSecurityPolicy 样例
以下样例阻止了 pod 使用宿主节点的 PID、 IPC、 网络命名空间, 运行特权模式的容器, 以及绑定大多数宿主节点的端口(除11 000 ~ 11 000 和13 000 ~ 14 000范围内的端口)。 它没有限制容器运行时使用的用户、 用户组和SELinux 选项。
PodSecurityPolicy 在 Kubernetes v1.21 版本中被弃用,将在 v1.25 中删除。
vim pod-security-policy.yaml
apiVersion: extensions/v1beta1
kind: PodSecurityPolicy
metadata:
name: default
spec:
hostIPC: false # 容器不允许使用宿主节点的 IPD PID
hostPID: false # 和网络命名空间
hostNetwork: false
hostPorts: # 容器只能绑定宿主节点的10000-11000端口(含端点)或13000-14000端口
- min: 10000
max: 11000
- min: 13000
max: 14000
privileged: false # 容器不能在特权模式下运行
readOnlyRootFilesystem: true # 容器强制使用只读的根文件系统
runAsUser: # 容器可以以任意用户和用户组运行
rule: RunAsAny
fsGroup:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
seLinux: # 它们也可以使用任何SElinux选项
rule: RunAsAny
volumes: # pod可以使用所有类型的存储卷
- '*'
隔离pod 的网络
如何通过限制 pod 可以与其他哪些 pod 通信,来确保 pod 之间的网络安全。
是否可以进行这些配置取决于集群中使用的容器网络插件。如果网络插件支持,可以通过NetworkPolicy 资源配置网络隔离。
一个 NetworkPolicy 会应用在匹配它的标签选择器的 pod 上,指明这些允许访问这些 pod 源地址,或这些 pod 可以访问的目标地址。这些分别由入向(ingress)和出向(egress)规则指定。这两种规则都可以匹配由标签选择器选出的 pod ,或者一个 namespace 中的所有 pod ,或者通过无类别域间路由( Classless Inter-Domain Routing, CIDR )指定的 IP 地址段。
在一个命名空间中启用网络隔离
在默认情况下,某一名称空间中的 pod 可以被任意来源访问。首先,需要改变这个设定。需要创建一个 default-deny NetworkPolicy ,它会阻止任何客户端访问的 pod 。
vim network-policy-default-deny.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: # 空的标签选择器匹配名称空间中的所有 pod
在任何一个特定的命名 间中创建该 NetworkPolicy 之后,任何客户端都不能访问该命名空间中的 pod 。
注意: 集群中的 CNI 插件或其他网络方案需要支持 NetworkPolicy ,否则 NetworkPolicy 将不会影响 pod 之间的可达性。
允许同一命名空间中的部分 pod 访问 个服务端 pod
为了允许同一名称空间中的客户端 pod 访问该名称空间的 pod ,需要指明哪些 pod 可以访问。
假设在 foo namespace 中有一个 pod 运行 PostgreSQL 数据库,以及一个使用该数据库的网页服务器 pod,其他 pod 也在这个名称空间中运行,然而你不允许它们连接数据库。为了保障网络安全,需要再数据库 pod 所在的名称空间中创建一个 NetworkPolicy 资源。
vim network-policy-postgres.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: postgres-netpolicy
spec:
podSelector: # 这个策略确保了对具有 app=database 标签的 pod 的访问安全性
matchLabels:
app: database
ingress:
- from: # 它只允许来自具有 app=webserer 标签的 pod 的访问
- podSelector:
matchLabels:
app: webserver
ports: # 允许对这个端口的访问
- port: 5432
例子中的 NetworkPoliy 允许具有 app=webserer 标签的 pod 访问具有 app=database 的 pod 的访问,并且仅限访问 5432 端口。
客户端 pod 通常通过 Service 而非直接访问 pod 来访问服务器 pod ,但这对结果没有改变。NetworkPolicy 在通过 Service 访问时仍然会被执行。
在不同 Kubernetes 命名空间之间进行网络隔离
来看有另一个多个租户使用同一 Kubernetes集群的例子。
每个租户有多个命名空间,每个命名空间中有一个标签指明它们属于哪个租户。例如,有一个租户Manning, 它的所有命名空间中都有标签 tenant:manning 。其中的一个命名空间中运行了 一个微服务Shopping Cart,它需要允许同一租户下所有命名空间的所有 pod 访问。显然,其他租户禁止访问这个微服务。
vim network-policy-cart.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: shoppingcart-netpolicy
spec:
podSelector: # 该策应用于具有 app=shopping-cart 标签的 pod
matchLabels:
app: shopping-cart
ingress:
- from:
- namespaceSelector: # 只有在具有 tenant=manning 标签的名称空间中运行的 pod 可以访问该微服务
matchLabels:
tenant: manning
ports:
- port: 80
保证了只有具有 tenant=manning 标签的命名空间中运行的 pod 可以访问Shopping Cart 微服务
如果shopping cart服务的提供者需要允许其他租户访间该服务,他们可以创建一个新的NetworkPolicy资源,或者在之前的 Networkpolicy 中添加一条入向规则。
注意: 在多租户的 Kubernetes 集群中,通常租户不能为他们的命名空间添加标签(或注释)。否则,他们可以规避基于namespaceSelector的入向规则。
使用CIDR隔离网络
除了通过在 pod 选择器或命名空间选择器定义哪些 pod 可以访问 NetworkPolicy 资源中指定的目标 pod, 还可以通过CIDR表示法指定一 个IP段。
例如,为了允许 IP 在192.168.1.1到192. I 68.1.255范围内的客户端访问之前提到的shopping cart的 pod, 可以在入向规则中加入 ingress。
vim network-policy-cidr.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: ipblock-netpolicy
spec:
podSelector:
matchLabels:
app: shopping-cart
ingress: # 这条入向规则来自192.168.1.0/24 IP段的客户端的流量
- from:
- ipBlock:
cidr: 192.168.1.0/24
限制pod的对外访问流量
已经通过入向规则限制了进入pod的访问流量。然而,也可以通过出向规则限制pod的对外访问流量。
vim network-policy-egress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: egress-net-policy
spec:
podSelector: # 这个策略应用于包含app=webserver标签的pod
matchLabels:
app: webserver
egress: # 限制 pod 的出网流量
- to:
- podSelector: # webserver的pod只能与有 app=webserve 标签的 pod 通信
matchLabels:
app: database
ports:
- port: 5432
以上的 NetworkPolicy 仅允许具有标签 app=webserver 的 pod 访问具有标签 app=database 的pod, 除此之外不能访问任何地址(不论是其他pod,还是任何其他的IP, 无论在集群内部还是外部)。