K8s in Action 閱讀筆記——【13】Securing cluster nodes and the network
13.1 Using the host node’s namespaces in a pod
Pod中的容器通常在不同的Linux名稱空間下運(yùn)行,這使得它們的進(jìn)程與其他容器或節(jié)點(diǎn)默認(rèn)名稱空間下運(yùn)行的進(jìn)程隔離開(kāi)來(lái)。
例如,我們學(xué)習(xí)到每個(gè)Pod都擁有自己的IP和端口空間,因?yàn)樗褂闷渥约旱木W(wǎng)絡(luò)名稱空間。同樣,每個(gè)Pod也擁有自己的進(jìn)程樹(shù),因?yàn)樗凶约旱腜ID名稱空間,并且它還使用自己的IPC名稱空間,只允許在同一Pod中的進(jìn)程通過(guò)IPC(Inter-Process Communication)機(jī)制相互通信。
13.1.1 Using the node’s network namespace in a pod
某些Pod(通常是系統(tǒng)Pod)需要在主機(jī)的默認(rèn)名稱空間中運(yùn)行,允許它們查看和操作節(jié)點(diǎn)級(jí)別的資源和設(shè)備。例如,一個(gè)Pod可能需要使用節(jié)點(diǎn)的網(wǎng)絡(luò)適配器而不是自己的虛擬網(wǎng)絡(luò)適配器。可以通過(guò)將Pod規(guī)范中的hostNetwork屬性設(shè)置為true來(lái)實(shí)現(xiàn)這一點(diǎn)。
在這種情況下,Pod使用節(jié)點(diǎn)的網(wǎng)絡(luò)接口而不是它自己的網(wǎng)絡(luò)接口,如圖13.1所示。這意味著Pod不會(huì)獲得自己的IP地址,如果它運(yùn)行綁定端口的進(jìn)程,該進(jìn)程將綁定到節(jié)點(diǎn)的端口。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-eVGECMzg-1686405749288)(https://note-image-1307786938.cos.ap-beijing.myqcloud.com/typora/image-20230610150830900.png)]
運(yùn)行Pod后,可以使用以下命令查看其確實(shí)使用主機(jī)的網(wǎng)絡(luò)名稱空間(例如,它可以看到所有主機(jī)的網(wǎng)絡(luò)適配器)。
kubectl exec pod-with-host-network -- ifconfig
cni0 Link encap:Ethernet HWaddr 0E:DB:96:27:9A:29
......
docker0 Link encap:Ethernet HWaddr 02:42:5D:5E:AE:54
inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0
......
ens3 Link encap:Ethernet HWaddr 1E:00:49:00:00:59
inet addr:33.33.33.108 Bcast:33.33.33.255 Mask:255.255.255.0
......
flannel.1 Link encap:Ethernet HWaddr FA:36:23:30:02:19
inet addr:10.244.1.0 Bcast:0.0.0.0 Mask:255.255.255.255
......
ifb0 Link encap:Ethernet HWaddr A6:98:4B:6B:61:AB
inet6 addr: fe80::a498:4bff:fe6b:61ab/64 Scope:Link
......
lo Link encap:Local Loopback
......
tunl0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
......
veth3d45ae3c Link encap:Ethernet HWaddr 92:E5:55:02:3D:36
......
當(dāng)Kubernetes控制面組件作為Pod部署時(shí),會(huì)發(fā)現(xiàn)這些Pod使用了hostNetwork選項(xiàng),讓它們表現(xiàn)得好像它們沒(méi)有運(yùn)行在Pod中一樣。
13.1.2 Binding to a host port without using the host’s network namespace
允許Pod綁定到節(jié)點(diǎn)的默認(rèn)名稱空間中的端口,但仍具有自己的網(wǎng)絡(luò)名稱空間。這是通過(guò)在Pod規(guī)范的spec.containers.ports
字段中使用容器端口(hostPort)屬性來(lái)實(shí)現(xiàn)的。
不要將使用hostPort的Pod與通過(guò)NodePort服務(wù)暴露的Pod混淆。它們是兩個(gè)不同的東西,如下圖所示:
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-KFL4zOlz-1686405749290)(https://note-image-1307786938.cos.ap-beijing.myqcloud.com/typora/image-20230610152136088.png)]
首先,在圖中你會(huì)注意到,當(dāng)一個(gè)Pod使用hostPort時(shí),連接到節(jié)點(diǎn)的端口直接轉(zhuǎn)發(fā)到在該節(jié)點(diǎn)上運(yùn)行的Pod上,而使用NodePort服務(wù)時(shí),連接到節(jié)點(diǎn)的端口將被轉(zhuǎn)發(fā)到一個(gè)隨機(jī)選擇的Pod上(可能在另一個(gè)節(jié)點(diǎn)上)。另一個(gè)區(qū)別是,使用hostPort的Pod只會(huì)在運(yùn)行此類Pod的節(jié)點(diǎn)上綁定節(jié)點(diǎn)的端口,而NodePort服務(wù)會(huì)在所有節(jié)點(diǎn)上綁定端口,即使在不運(yùn)行此類Pod的節(jié)點(diǎn)上(如圖13.2中的節(jié)點(diǎn)3上)也會(huì)綁定端口。
重要的是要理解,如果一個(gè)Pod使用特定的hostPort,每個(gè)節(jié)點(diǎn)只能安排一個(gè)該P(yáng)od的實(shí)例,因?yàn)閮蓚€(gè)進(jìn)程不能綁定到同一個(gè)host端口。調(diào)度器在調(diào)度Pod時(shí)會(huì)考慮到這一點(diǎn),因此不會(huì)將多個(gè)Pod調(diào)度到同一節(jié)點(diǎn)上,如圖13.3所示。如果有三個(gè)節(jié)點(diǎn)并想要部署四個(gè)Pod副本,則只會(huì)安排三個(gè)Pod(一個(gè)Pod將保持掛起狀態(tài))。
讓我們看看如何在Pod的YAML定義中定義hostPort。下面的示例顯示了運(yùn)行的kubia Pod并將其綁定到節(jié)點(diǎn)的端口9000的YAML:
# 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"]
創(chuàng)建此Pod后,可以通過(guò)該P(yáng)od調(diào)度的節(jié)點(diǎn)的9000端口訪問(wèn)它。如果有多個(gè)節(jié)點(diǎn),則會(huì)看到在其他節(jié)點(diǎn)上無(wú)法通過(guò)該端口訪問(wèn)Pod。
$ curl http://yjq-k8s4:9000
You've hit kubia-hostport
hostPort功能主要用于公開(kāi)系統(tǒng)服務(wù),這些服務(wù)使用DaemonSet部署到每個(gè)節(jié)點(diǎn)。
13.1.3 Using the node’s PID and IPC namespaces
與hostNetwork
選項(xiàng)類似的是hostPID
和hostIPC Pod
規(guī)范屬性。當(dāng)你將它們?cè)O(shè)置為true時(shí),Pod的容器將使用節(jié)點(diǎn)的PID和IPC名稱空間,分別允許在容器中運(yùn)行的進(jìn)程查看節(jié)點(diǎn)上的所有其他進(jìn)程或通過(guò)IPC與它們通信。參見(jiàn)以下示例。
# 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"]
你應(yīng)該還記得,Pod通常只能看到它們自己的進(jìn)程,但是如果你運(yùn)行此Pod,然后在容器內(nèi)列出進(jìn)程,你會(huì)看到運(yùn)行在主機(jī)節(jié)點(diǎn)上的所有進(jìn)程,而不僅僅是在容器中運(yùn)行的進(jìn)程,如以下示例所示。
# kubectl exec pod-with-host-pid-and-ipc -- ps aux | head -10
PID USER TIME COMMAND
1 root 5h43 {systemd} /sbin/init
2 root 0:06 [kthreadd]
3 root 0:00 [rcu_gp]
4 root 0:00 [rcu_par_gp]
6 root 0:00 [kworker/0:0H-kb]
8 root 0:00 [mm_percpu_wq]
9 root 3:22 [ksoftirqd/0]
10 root 52:55 [rcu_sched]
11 root 0:56 [migration/0]
通過(guò)將hostIPC屬性設(shè)置為true,Pod的容器中的進(jìn)程也可以通過(guò)Inter-Process Communication與運(yùn)行在節(jié)點(diǎn)上的所有其他進(jìn)程通信。
13.2 Configuring the container’s security context
除了允許Pod使用主機(jī)的Linux名稱空間之外,還可以通過(guò)securityContext屬性在Pod及其容器上配置其他安全相關(guān)功能,可以直接在Pod規(guī)范下指定,也可以在各個(gè)容器的規(guī)范中指定。
配置安全上下文允許你執(zhí)行各種操作:
- 指定容器中的進(jìn)程將以哪個(gè)用戶(用戶ID)身份運(yùn)行。
- 防止容器以root用戶身份運(yùn)行(容器默認(rèn)以容器鏡像中定義的用戶身份運(yùn)行,因此可能希望防止容器以root用戶身份運(yùn)行)。
- 以特權(quán)模式運(yùn)行容器,使其對(duì)節(jié)點(diǎn)的內(nèi)核具有完全訪問(wèn)權(quán)限。
- 通過(guò)添加或刪除權(quán)限,配置細(xì)粒度的權(quán)限,與在特權(quán)模式下給予容器所有可能權(quán)限的方式相反。
- 設(shè)置SELinux(安全增強(qiáng)Linux)選項(xiàng),以對(duì)容器進(jìn)行強(qiáng)制鎖定。
- 防止進(jìn)程寫入容器的文件系統(tǒng)。
首先,運(yùn)行一個(gè)默認(rèn)的安全上下文選項(xiàng)的Pod(完全不指定它們),這樣你就可以看到與自定義安全上下文的Pod相比它的行為如何:
$ kubectl run pod-with-defaults --image alpine --restart Never -- /bin/sleep 999999
pod/pod-with-defaults created
現(xiàn)在讓我們看看容器運(yùn)行的用戶和組ID,以及它所屬的組。你可以通過(guò)在容器內(nèi)運(yùn)行id命令來(lái)查看:
$ 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)運(yùn)行。它還是多個(gè)其他組的成員。
容器運(yùn)行的用戶是指定在容器鏡像中的。在一個(gè)Dockerfile中,使用USER指令來(lái)實(shí)現(xiàn)這一點(diǎn)。如果省略了這一指令,則容器以root身份運(yùn)行。
現(xiàn)在,將運(yùn)行一個(gè)容器以不同用戶身份運(yùn)行的Pod。
13.2.1 Running a container as a specific user
要在容器鏡像中內(nèi)嵌的用戶ID不同的情況下運(yùn)行pod,需要設(shè)置pod的securityContext.runAsUser
屬性。通過(guò)如下的示例,你可以將容器運(yùn)行為用戶名為guest 的用戶,該用戶在alpine容器鏡像中的用戶ID為405。
# 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
現(xiàn)在,為了查看runAsUser屬性的效果,在這個(gè)新的pod中運(yùn)行id命令,就像之前那樣:
$ kubectl exec pod-as-user-guest -- id
uid=405(guest) gid=100(users) groups=100(users)
可以看到,容器正在以guest用戶身份運(yùn)行。
13.2.2 Preventing a container from running as root
假設(shè)你不關(guān)心容器以哪個(gè)用戶身份運(yùn)行,但你仍然希望防止其以root身份運(yùn)行,該怎么辦呢?
假設(shè)你部署了一個(gè)pod,其中包含了一個(gè)使用了Dockerfile中的USER daemon指令的容器鏡像,它使得容器在daemon用戶下運(yùn)行。那如果一個(gè)攻擊者可以訪問(wèn)你的鏡像注冊(cè)表并在相同標(biāo)簽下推送一個(gè)不同的鏡像會(huì)怎么樣呢?攻擊者的鏡像被配置為root用戶下運(yùn)行。當(dāng)Kubernetes調(diào)度了一個(gè)你的pod的新實(shí)例后,Kubelet會(huì)下載攻擊者的鏡像,并運(yùn)行其中的所有代碼。
盡管容器大多數(shù)情況下與主機(jī)系統(tǒng)相互隔離,但以root身份運(yùn)行容器的進(jìn)程仍然被認(rèn)為是一種糟糕的做法。例如,當(dāng)主機(jī)目錄被掛載到容器中時(shí),如果在容器中運(yùn)行的進(jìn)程以root身份運(yùn)行,它將完全訪問(wèn)掛載的目錄,但如果以非root身份運(yùn)行,則不會(huì)。
為了防止前面描述的攻擊場(chǎng)景,你可以指定pod的容器需要以非root身份運(yùn)行,如下所示:
# 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身份運(yùn)行
如果你部署這個(gè)pod,它將會(huì)被調(diào)度但不被允許運(yùn)行:
$ kubectl get pod pod-run-as-non-root
NAME READY STATUS RESTARTS AGE
pod-run-as-non-root 0/1 CreateContainerConfigError 0 92s
# Error: container has runAsNonRoot and image will run as root
13.2.3 Running pods in privileged mode
有時(shí)候,pod需要能夠執(zhí)行其所在節(jié)點(diǎn)的所有操作,例如使用受保護(hù)的系統(tǒng)設(shè)備或其他內(nèi)核特性,這是常規(guī)容器無(wú)法訪問(wèn)的。
其中一個(gè)這樣的pod的例子是kube-proxy
pod,它需要修改節(jié)點(diǎn)的iptables規(guī)則以使服務(wù)正常工作。
為了完全訪問(wèn)節(jié)點(diǎn)的內(nèi)核,pod的容器運(yùn)行在特權(quán)模式下。這可以通過(guò)將容器的securityContext屬性中的privileged
權(quán)限設(shè)置為true來(lái)實(shí)現(xiàn)。你可以使用下面清單中的YAML創(chuàng)建一個(gè)特權(quán)的pod:
# pod-privileged.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-privileged
spec:
containers:
- name: main
image: alpine
command: ["/bin/sleep", "999999"]
securityContext:
privileged: true
現(xiàn)在,你可以部署這個(gè)pod并與之前運(yùn)行的非特權(quán)pod進(jìn)行比較。
如果你熟悉Linux,你可能知道它有一個(gè)特殊的文件目錄稱為/dev,它包含了系統(tǒng)上所有設(shè)備的設(shè)備文件。這些不是磁盤上的常規(guī)文件,而是用于與設(shè)備通信的特殊文件。現(xiàn)在,我們通過(guò)列出非特權(quán)容器在它的/dev目錄中可見(jiàn)的文件來(lái)查看哪些設(shè)備可見(jiàn),如下:
$ kubectl exec -it pod-with-defaults -- ls /dev
core null shm termination-log
fd ptmx stderr tty
full pts stdin urandom
mqueue random stdout zero
該清單顯示了所有設(shè)備。設(shè)備列表相當(dāng)短?,F(xiàn)在,將其與下面的清單進(jìn)行比較,該清單顯示了特權(quán)pod可以看到的設(shè)備文件:
$ kubectl exec -it pod-privileged -- ls /dev
autofs snapshot tty45 ttyS30
bsg snd tty46 ttyS31
btrfs-control sr0 tty47 ttyS4
bus stderr tty48 ttyS5
core stdin tty49 ttyS6
cpu stdout tty5 ttyS7
cpu_dma_latency termination-log tty50 ttyS8
cuse tty tty51 ttyS9
dri tty0 tty52 ttyprintk
ecryptfs tty1 tty53 udmabuf
fb0 tty10 tty54 uhid
fd tty11 tty55 uinput
full tty12 tty56 urandom
fuse tty13 tty57 userio
hidraw0 tty14 tty58 vcs
hpet tty15 tty59 vcs1
hwrng tty16 tty6 vcs2
i2c-0 tty17 tty60 vcs3
input tty18 tty61 vcs4
......
可以看到設(shè)備列表比之前長(zhǎng)得多。事實(shí)上,特權(quán)容器可以看到主機(jī)節(jié)點(diǎn)的所有設(shè)備。這意味著它可以自由地使用任何設(shè)備。
13.2.4 Adding individual kernel capabilities to a container
與讓一個(gè)容器擁有特權(quán)和無(wú)限權(quán)限相比,從安全的角度來(lái)看,更為安全的方法是僅賦予它訪問(wèn)它實(shí)際需要的內(nèi)核功能。Kubernetes允許你為每個(gè)容器添加能力或刪除其中的一部分,這使你可以微調(diào)容器的權(quán)限并限制攻擊者潛在入侵的影響。
例如,通常情況下容器無(wú)法更改系統(tǒng)時(shí)間(硬件時(shí)鐘的時(shí)間)。你可以通過(guò)嘗試在pod-withdefaults pod中設(shè)置時(shí)間來(lái)確認(rèn)這一點(diǎn):
$ kubectl exec -it pod-with-defaults -- date +%T -s "12:00:00"
date: can't set date: Operation not permitted
12:00:00
如果你想允許容器更改系統(tǒng)時(shí)間,可以在容器能力列表中添加一個(gè)稱為CAP_SYS_TIME的能力,如下所示:
# 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:
add:
- SYS_TIME
在這個(gè)新創(chuàng)建的pod的容器中運(yùn)行相同的命令,系統(tǒng)時(shí)間會(huì)被成功更改。
像這樣添加能力要比使用privileged:true給容器賦予完全的特權(quán)要好得多。但是這需要你了解每個(gè)能力的作用。
Linux內(nèi)核能力的列表可以在Linux手冊(cè)中找到。
13.2.5 Dropping capabilities from a container
之前你已經(jīng)了解如何添加能力,但你也可以取消容器的某些可能存在的能力。例如,默認(rèn)賦予容器的能力之一是CAP_CHOWN能力,它允許進(jìn)程更改文件系統(tǒng)中的文件所有權(quán)。
你可以通過(guò)將pod-with-defaults pod中/tmp目錄的所有權(quán)更改為guest用戶來(lái)驗(yàn)證這一點(diǎn),例如:
# 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
為了防止容器這樣做,你需要通過(guò)在容器的securityContext.capabilities.drop屬性下列出該能力,如下例所示,來(lái)撤銷該能力:
$ kubectl exec pod-drop-chown-capability -- chown guest /tmp
chown: /tmp: Operation not permitted
command terminated with exit code 1
通過(guò)刪除CHOWN能力,你不允許在此pod中更改/tmp目錄的所有者。
13.2.6 Preventing processes from writing to the container’s filesystem
防止容器向其文件系統(tǒng)中寫入數(shù)據(jù),通過(guò)將容器的securityContext.readOnlyRootFilesystem屬性設(shè)置為true可以實(shí)現(xiàn)點(diǎn),如下例所示:
# 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
mountPath: /volume
readOnly: false
volumes:
- name: my-volume
emptyDir:
當(dāng)你部署這個(gè)pod時(shí),容器是作為root用戶運(yùn)行的,具有寫入根目錄的權(quán)限,但嘗試在根目錄中寫入文件會(huì)失敗:
$ kubectl exec -it pod-with-readonly-filesystem -- touch /new-file
touch: /new-file: Read-only file system
另一方面,允許向掛載的卷中寫入數(shù)據(jù):
$ kubectl exec -it pod-with-readonly-filesystem -- touch /volume/newfile
$ kubectl exec -it pod-with-readonly-filesystem -- ls -la /volume/newfile
-rw-r--r-- 1 root root 0 Jun 10 12:58 /volume/newfile
當(dāng)你將容器文件系統(tǒng)設(shè)置為只讀時(shí),你可能需要在應(yīng)用程序?qū)懭氲拿總€(gè)目錄中都掛載一個(gè)卷(例如,日志、磁盤緩存等)。
為了增強(qiáng)安全性,在生產(chǎn)環(huán)境中運(yùn)行pod時(shí),請(qǐng)將容器的readOnlyRootFilesystem屬性設(shè)置為true。
13.2.7 Sharing volumes when containers run as different users
在第6章中,我們解釋了如何使用卷在pod的容器之間共享數(shù)據(jù)。你可以在一個(gè)容器中寫入文件并在另一個(gè)容器中讀取文件,沒(méi)有任何問(wèn)題。
但這僅僅是因?yàn)閮蓚€(gè)容器都作為root用戶運(yùn)行,它們可以完全訪問(wèn)卷中的所有文件?,F(xiàn)在想象一下使用我們之前解釋過(guò)的runAsUser選項(xiàng)。你可能需要將這兩個(gè)容器作為兩個(gè)不同的用戶運(yùn)行(也許你正在使用兩個(gè)來(lái)自第三方容器鏡像的容器,每個(gè)容器都在自己特定的用戶下運(yùn)行其進(jìn)程)。如果這兩個(gè)容器使用卷來(lái)共享文件,它們可能無(wú)法讀取或?qū)懭氡舜说奈募?/p>
這就是Kubernetes允許你為在容器中運(yùn)行的所有pod指定補(bǔ)充組的原因,使它們可以共享文件,而不受它們運(yùn)行的用戶ID的影響。這可以使用以下兩個(gè)屬性來(lái)完成:fsGroup
和supplementalGroups
。
讓我們看看如何在一個(gè)pod中使用它們,然后再看看它們的效果如何。下面的示例描述了一個(gè)有兩個(gè)容器共享同一個(gè)卷的pod:
# pod-with-shared-volume-fsgroup.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-with-shared-volume-fsgroup
spec:
# 在pod級(jí)別定義
securityContext:
fsGroup: 555
supplementalGroups: [666, 777]
containers:
- name: first
image: alpine
command: ["/bin/sleep", "999999"]
securityContext:
# 第一個(gè)容器的用戶id
runAsUser: 1111
volumeMounts:
- name: shared-volume
mountPath: /volume
readOnly: false
- name: second
image: alpine
command: ["/bin/sleep", "999999"]
securityContext:
# 第二個(gè)容器的用戶id
runAsUser: 2222
volumeMounts:
- name: shared-volume
mountPath: /volume
readOnly: false
# 兩個(gè)容器共享一個(gè)卷
volumes:
- name: shared-volume
emptyDir:
創(chuàng)建這個(gè)pod后,在第一個(gè)容器中運(yùn)行一個(gè)shell,并查看容器運(yùn)行的用戶和組ID:
$ kubectl exec -it pod-with-shared-volume-fsgroup -c first -- sh
/ $ id
uid=1111 gid=0(root) groups=0(root),555,666,777
id命令顯示該容器正在以1111用戶ID運(yùn)行,正如在pod定義中指定的一樣。有效的組ID是0(root),但群組ID 555、666和777也與該用戶相關(guān)聯(lián)。
在pod定義中,你將fsGroup設(shè)置為555。因此,掛載的卷將由組ID為555擁有,如下所示:
ls -l / | grep volume
drwxrwsrwx 2 root 555 4096 Jun 10 13:08 volume
如果你在掛載的卷的目錄中創(chuàng)建一個(gè)文件,該文件的所有者是用戶ID 1111(這是容器正在運(yùn)行的用戶ID)和組ID 555:
/ $ echo foo > /volume/foo
/ $ ls -l /volume
total 4
-rw-r--r-- 1 1111 555 4 Jun 10 13:12 foo
這與其他情況下新創(chuàng)建的文件的所有權(quán)設(shè)置不同。通常,當(dāng)一個(gè)用戶創(chuàng)建文件時(shí),使用的是用戶的有效群組ID(在你的情況下為0)。你可以通過(guò)在容器的文件系統(tǒng)中創(chuàng)建文件而不是在卷中來(lái)查看這一點(diǎn):
/ $ echo foo > /tmp/foo
/ $ ls -l /tmp
total 4
-rw-r--r-- 1 1111 root 4 Jun 10 13:13 foo
正如你所看到的,當(dāng)進(jìn)程在一個(gè)卷中創(chuàng)建文件時(shí),會(huì)使用fsGroup安全上下文屬性(但這取決于使用的卷插件),而supplementalGroups屬性定義了用戶關(guān)聯(lián)的附加組ID列表。
這就結(jié)束了關(guān)于容器安全上下文配置的部分。接下來(lái),我們將看到集群管理員如何限制用戶這樣做。
13.3 Restricting the use of security-related features in pods
集群管理員可以通過(guò)創(chuàng)建一個(gè)或多個(gè)PodSecurityPolicy資源來(lái)限制先前介紹的與安全相關(guān)的功能的使用。
13.3.1 Introducing the PodSecurityPolicy resource
PodSecurityPolicy是一個(gè)集群級(jí)別的(非namespaced)資源,它定義了用戶可以或不能在他們的pod中使用的安全相關(guān)功能。維護(hù)PodSecurityPolicy資源中配置的策略的工作是由運(yùn)行在API服務(wù)器中的PodSecurityPolicy admission控制插件執(zhí)行的。
PodSecurityPolicy admission控制插件可能沒(méi)有在你的集群中啟用。在運(yùn)行以下示例之前,請(qǐng)確保已啟用該插件。
當(dāng)有人向API服務(wù)器提交一個(gè)pod資源時(shí),PodSecurityPolicy admission控制插件根據(jù)配置的PodSecurityPolicies驗(yàn)證pod定義。如果pod符合集群的策略,它被接受并存儲(chǔ)到etcd中;否則它會(huì)被立即拒絕。插件還可以根據(jù)策略中配置的默認(rèn)值修改pod資源。
13.4 Isolating the pod network
在本章的其余部分,我們將探討如何通過(guò)限制哪些Pod可以連接到哪些Pod來(lái)保護(hù)Pod之間的網(wǎng)絡(luò)。
這取決于在群集中使用的容器網(wǎng)絡(luò)插件是否提供了相應(yīng)的配置。如果網(wǎng)絡(luò)插件支持,可以通過(guò)創(chuàng)建NetworkPolicy資源來(lái)配置網(wǎng)絡(luò)隔離。
NetworkPolicy適用于匹配其標(biāo)簽選擇器的Pod,并指定可以訪問(wèn)匹配Pod的源或可從匹配Pod訪問(wèn)的目標(biāo)。這是通過(guò)分別設(shè)置入口規(guī)則和出口規(guī)則來(lái)配置的。兩種類型的規(guī)則都可以匹配滿足Pod選擇器的Pod,滿足Namespace選擇器的所有具有標(biāo)簽的命名空間中的所有Pod,或使用無(wú)類域間路由(CIDR)表示法指定的網(wǎng)絡(luò)IP塊(例如,192.168.1.0/24)。
我們將探討入口和出口規(guī)則以及所有三種匹配選項(xiàng)。
13.4.1 Enabling network isolation in a namespace
默認(rèn)情況下,給定命名空間中的Pod可以被任何人訪問(wèn)。首先,你需要更改此設(shè)置。你將創(chuàng)建一個(gè)名為default-deny NetworkPolicy,這將防止所有客戶端連接到你的命名空間中的任何Pod。NetworkPolicy定義如下:
# network-policy-default-deny.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
# 適用于default命名空間下所有pod
podSelector:
當(dāng)你在某個(gè)命名空間中創(chuàng)建此網(wǎng)絡(luò)策略時(shí),任何人都無(wú)法連接到該命名空間中的任何Pod。
在集群中使用的CNI插件或其他類型的網(wǎng)絡(luò)解決方案必須支持NetworkPolicy,否則對(duì)Pod之間的連接性將沒(méi)有任何影響
13.4.2 Allowing only some pods in the namespace to connect to a server pod
現(xiàn)在,要允許客戶端連接到命名空間中的Pod,你必須明確表明誰(shuí)可以連接到這些Pod,也就是說(shuō),哪些Pod。讓我們通過(guò)一個(gè)例子來(lái)探討如何實(shí)現(xiàn)這一點(diǎn)。
假設(shè)在名為foo的命名空間中有一個(gè)正在運(yùn)行的PostgreSQL數(shù)據(jù)庫(kù)Pod和一個(gè)使用該數(shù)據(jù)庫(kù)的Web服務(wù)器Pod。還有其他Pod也在該命名空間中,你不希望它們連接到數(shù)據(jù)庫(kù)。為了保護(hù)網(wǎng)絡(luò),你需要在與數(shù)據(jù)庫(kù)Pod相同的命名空間中創(chuàng)建以下清單中顯示的NetworkPolicy資源:
# network-policy-postgres.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: postgres-netpolicy
spec:
podSelector:
matchLabels:
app: database
ingress:
- from:
- podSelector:
matchLabels:
app: webserver
ports:
- port: 5432
此示例NetworkPolicy允許具有app=webserver標(biāo)簽的Pod連接到具有app=database標(biāo)簽的Pod,并且只能使用端口5432。其他Pod無(wú)法連接到數(shù)據(jù)庫(kù)Pod,而且除了數(shù)據(jù)庫(kù)Pod的端口5432之外,沒(méi)有人(甚至是Web服務(wù)器Pod)可以連接到其他任何端口。圖13.4顯示了這一點(diǎn)。
客戶端Pod通常通過(guò)Service與Server Pod連接而不是直接連接到Pod,但這并不會(huì)改變什么。在通過(guò)Service連接時(shí),NetworkPolicy也會(huì)得到執(zhí)行。
13.4.3 Isolating the network between Kubernetes namespaces
現(xiàn)在,讓我們看另一個(gè)例子,在這個(gè)例子中,多個(gè)租戶正在使用同一個(gè)Kubernetes集群。每個(gè)租戶可以使用多個(gè)命名空間,每個(gè)命名空間都有一個(gè)標(biāo)簽指定它所屬的租戶。例如,其中一個(gè)租戶是Manning。他們的所有命名空間都已標(biāo)記為tenant:manning
。在他們的某個(gè)命名空間中,他們運(yùn)行一個(gè)購(gòu)物車微服務(wù),需要對(duì)他們?nèi)魏蚊臻g中運(yùn)行的所有Pod都可用。顯然,他們不希望其他租戶訪問(wèn)他們的微服務(wù)。
為了保護(hù)他們的微服務(wù),他們創(chuàng)建以下清單中顯示的NetworkPolicy資源。
# network-policy-cart.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: shoppingcart-netpolicy
spec:
podSelector:
matchLabels:
app: shopping-cart
ingress:
- from:
- namespaceSelector:
matchLabels:
tenant: manning
ports:
- port: 80
該NetworkPolicy確保只有運(yùn)行在標(biāo)記為tenant:manning的命名空間中的Pod可以訪問(wèn)他們的購(gòu)物車微服務(wù),如圖13.5所示。
如果購(gòu)物車提供商還想向其他租戶(也許是他們的合作伙伴公司之一)提供訪問(wèn)權(quán)限,他們可以創(chuàng)建一個(gè)額外的NetworkPolicy資源或向其現(xiàn)有的NetworkPolicy添加一個(gè)額外的ingress規(guī)則。
在多租戶的Kubernetes集群中,租戶通常不能自己添加標(biāo)簽(或注釋)到他們的命名空間。如果他們這樣做,他們將能夠規(guī)避基于namespaceSelector的ingress規(guī)則。
13.4.4 Isolating using CIDR notation
你可以使用CIDR表示法中的IP塊來(lái)指定哪些可以訪問(wèn)NetworkPolicy中目標(biāo)Pod。與其指定Pod或命名空間選擇器不同。例如,要允許先前部分中的購(gòu)物車Pod僅從192.168.1.1到255范圍內(nèi)的IP訪問(wèn),可以如此指定:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-500711.html
ingress:
- from:
- ipBlock:
cidr: 192.168.1.0/24
13.4.5 Limiting the outbound traffic of a set of pods
在所有先前的例子中,你一直使用ingress規(guī)則限制匹配NetworkPolicy的Pod選擇器的入站流量,但你也可以通過(guò)egress規(guī)則限制它們的出站流量。如下所示:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-500711.html
# network-policy-egress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: egress-net-policy
spec:
podSelector:
matchLabels:
app: webserver
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- port: 5432
到了這里,關(guān)于K8s in Action 閱讀筆記——【13】Securing cluster nodes and the network的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!