34.B站薪享宏福笔记——第十二章(4)安全参数
12 B站薪享宏福笔记——第十二章
—— Kubernetes 中必备的工具组件
12.4 安全参数
—— 限制对应的权限集
12.4.1 节点名字空间共享
(1)共享主机网络 hostNetwork
注意:node01、node02 端口已被 Ingress-nginx 占用,master01 没有安装 Ingress-nginx,所以可以使用 master01 进行实验
# node01、node02 都安装了 ingress-nginx ,主机 80 端口被占用 [root@k8s-master01 12.4]# kubectl get pod -o wide -n ingress NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES ingress-nginx-controller-gwr9q 1/1 Running 21 (175m ago) 17d 192.168.66.13 k8s-node02 <none> <none> ingress-nginx-controller-zzzr5 1/1 Running 20 (175m ago) 17d 192.168.66.12 k8s-node01 <none> <none> ingress-nginx-defaultbackend-89db9d699-cvcrn 1/1 Running 16 (175m ago) 18d 10.244.85.195 k8s-node01 <none> <none> jaeger-ddb59666b-dd4vj 1/1 Running 16 (175m ago) 18d 10.244.85.234 k8s-node01 <none> <none> # master01 上因为有污点,所以 daemonset 控制器没有安装 ingress-nginx 到 master01 [root@k8s-master01 12.4]# ss -na|grep '0.0.0.0:80' # node01、node02 上 80 端口被占用 [root@k8s-node01 ~]# ss -na|grep '0.0.0.0:80' tcp LISTEN 0 4096 0.0.0.0:80 0.0.0.0:*
[root@k8s-master01 12.4]# cat 1.hostnetwork.yaml apiVersion: v1 kind: Pod metadata: name: hostnetwork-pod spec: hostNetwork: true nodeName: k8s-master01 containers: - name: myapp-container image: myapp:v1.0 ports: - containerPort: 80
4.期望:主机网络模式(当前 Pod 与当前所在节点共享网络):开启、固定 Pod 所在节点的名称、容器组:容器名、基于镜像版本、端口:容器端口:80(即 Pod 使用主机网络的 80 端口)
[root@k8s-master01 12.4]# kubectl apply -f 1.hostnetwork.yaml pod/hostnetwork-pod created [root@k8s-master01 12.4]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES hostnetwork-pod 1/1 Running 0 9s 192.168.66.11 k8s-master01 <none> <none> [root@k8s-master01 12.4]# ss -tnulp|grep :80 tcp LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=113109,fd=6),("nginx",pid=113098,fd=6))
使用浏览器访问:192.168.66.11、192.168.66.11/hostname.html 可以使用主机的网络进行访问
(2)启动主机端口 hostPort
注意:确定 8080 端口没有被占用,主机端口实际是主机进行了 dnet 转换,将主机端口经过路由,再转入容器端口
[root@k8s-master01 12.4]# cat 2.hostport.yaml apiVersion: v1 kind: Pod metadata: name: pod-with-hostport spec: containers: - name: myapp-container image: myapp:v1.0 ports: - containerPort: 80 hostPort: 8080
4.期望:容器组:容器名、基于镜像版本、端口:容器端口:80、主机端口:8080(即 经过 dnet转换,将主机的 8080 端口的访问 转入 容器的 80 端口)
[root@k8s-master01 12.4]# kubectl apply -f 2.hostport.yaml pod/pod-with-hostport created [root@k8s-master01 12.4]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES hostnetwork-pod 1/1 Running 0 17m 192.168.66.11 k8s-master01 <none> <none> pod-with-hostport 1/1 Running 0 5s 10.244.58.202 k8s-node02 <none> <none>
上面查得 Pod 运行在 node02 节点上,所以使用 node02 节点的 IP 地址,通过浏览器访问:192.168.66.13:8080、192.168.66.13:8080/hostname.html 可以使用主机的端口进行访问
(3)共享主机 PID 和 IPC hostPID and hostIPC
注意:通过共享主机 PID 和 IPC ,Pod 可以和主机进行进程间通信
[root@k8s-master01 12.4]# cat 3.pid_ipc.yaml apiVersion: v1 kind: Pod metadata: name: hostpid-hostipc-pod spec: hostPID: true hostIPC: true containers: - name: myapp-container image: myapp:v1.0
4.期望:主机PID:开启、主机IPC:开启、容器组:容器名、基于镜像版本
[root@k8s-master01 12.4]# kubectl apply -f 3.pid_ipc.yaml pod/hostpid-hostipc-pod created [root@k8s-master01 12.4]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES hostnetwork-pod 1/1 Running 0 37m 192.168.66.11 k8s-master01 <none> <none> hostpid-hostipc-pod 1/1 Running 0 6s 10.244.85.238 k8s-node01 <none> <none> pod-with-hostport 1/1 Running 0 20m 10.244.58.202 k8s-node02 <none> <none> # Pod 中 1号进程 默认应该是启动命令本身(此 Pod 是基于 nginx 封装,所以 1号进程 应该是 nginx ) # 但是此时 Pod 中 1号进程是 systemd,是 物理机 将 PID/IPC 共享到此 Pod 内部的原因 [root@k8s-master01 12.4]# kubectl exec -it hostpid-hostipc-pod -- /bin/sh / # ps -ef|wc -l 207 / # ps -ef|head PID USER TIME COMMAND 1 root 0:02 /usr/lib/systemd/systemd --switched-root --system --deserialize 31 2 root 0:00 [kthreadd] 3 root 0:00 [pool_workqueue_] 4 root 0:00 [kworker/R-rcu_g] 5 root 0:00 [kworker/R-rcu_p] 6 root 0:00 [kworker/R-slub_] 7 root 0:00 [kworker/R-netns] 9 root 0:00 [kworker/0:0H-ev] 10 root 0:00 [kworker/u512:0-] / # exit
# 消除实验影响 [root@k8s-master01 12.4]# kubectl delete pod --all pod "hostnetwork-pod" deleted pod "hostpid-hostipc-pod" deleted pod "pod-with-hostport" deleted
12.4.2 节点安全上下文(pod 级)
(1)概念
指定容器中运行进程的用户(用户 ID)
阻止容器使用 root 用户运行(容器的默认运行用户通常在其镜像中指定,所以可能需要阻止容器以 root 用户运行。如果阻止了 root 用户,但容器中没有创建对应普通用户或用普通用户运行,也可能使容器或 Pod 运行失败)
使用特权模式运行容器,使其对宿主节点的内核具有完全的访问权限(有些服务需要特权模式运行,例如:cAdvisor:猫头鹰监控,需要特权模式运行)
与以上相反,通过添加或禁用内核功能,配置细粒度的内核访问权限(权限细分)
设置 SELinux 选项,加强对容器的限制
阻止进程写入容器的根文件系统(超越权限)
(2)使用特定用户运行 pod
# 当不指定使用的特定用户运行时,容器启动会使用自带的用户 [root@k8s-master01 12.4]# cat 4.pod.yaml apiVersion: v1 kind: Pod metadata: name: user-id-pod-1 spec: containers: - name: alpine-container image: alpine command: ["/bin/sleep", "9999"] [root@k8s-master01 12.4]# kubectl apply -f 4.pod.yaml pod/user-id-pod-1 created [root@k8s-master01 12.4]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES user-id-pod-1 1/1 Running 0 14s 10.244.58.208 k8s-node02 <none> <none> # 默认使用了镜像中的 root 用户启动容器 [root@k8s-master01 12.4]# kubectl exec -it user-id-pod-1 -- /bin/sh / # 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) / # exit
[root@k8s-master01 12.4]# cat 5.user.yaml apiVersion: v1 kind: Pod metadata: name: user-id-pod-2 spec: containers: - name: alpine-container image: alpine command: ["/bin/sleep", "9999"] securityContext: runAsUser: 405
4.期望:容器组:容器名、基于镜像版本、启动命令、安全上下文:使用用户:405(alpine 用户自带的用户,已有)
[root@k8s-master01 12.4]# kubectl apply -f 5.user.yaml pod/user-id-pod-2 created [root@k8s-master01 12.4]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES user-id-pod-1 1/1 Running 0 10m 10.244.58.208 k8s-node02 <none> <none> user-id-pod-2 1/1 Running 0 10s 10.244.85.242 k8s-node01 <none> <none> # 固定用户后,可以使用固定的用户启动命令 [root@k8s-master01 12.4]# kubectl exec -it user-id-pod-2 -- /bin/sh / $ id uid=405(guest) gid=100(users) groups=100(users) / $ exit
# 消除实验影响 [root@k8s-master01 12.4]# kubectl delete pod --all pod "user-id-pod-1" deleted pod "user-id-pod-2" deleted
(3)阻止容器以 root 用户运行
注意:特殊情况下,即使容器镜像使用的是普通用户,也可能需要明确指定 runAsUser,这可能是由于容器镜像中的用户与宿主机上的 用户UID 不匹配导致的,这种情况下,即使容器中的用户是普通用户,Kubernetes 也可能会认为其是 root 用户,因为它的 UID 可能与宿主机上的 root 用户的UID 匹配。
因此,即使容器中的用户是普通用户,也建议明确指定 runAsUser,以确保容器以正确的用户身份运行
# 错误启动示范
[root@k8s-master01 12.4]# cat 6.non_root.yaml apiVersion: v1 kind: Pod metadata: name: non-root-user-pod-false spec: containers: - name: myapp-container image: myapp:v1.0 securityContext: runAsNonRoot: true [root@k8s-master01 12.4]# kubectl apply -f 6.non_root.yaml pod/non-root-user-pod-false created [root@k8s-master01 12.4]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES non-root-user-pod-false 0/1 CreateContainerConfigError 0 7s 10.244.58.212 k8s-node02 <none> <none> # 不使用镜像的 root 用户,有时可能会因为启动用户问题导致 pod 启动失败
[root@k8s-master01 12.4]# kubectl describe pod non-root-user-pod-false .......... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Scheduled 34s default-scheduler Successfully assigned default/non-root-user-pod-false to k8s-node02 Normal Pulled 5s (x5 over 33s) kubelet Container image "myapp:v1.0" already present on machine Warning Failed 5s (x5 over 33s) kubelet Error: container has runAsNonRoot and image will run as root (pod: "non-root-user-pod-false_default(24bf44c5-0e9c-466f-928c-d4de26f9ce25)", container: myapp-container)
[root@k8s-master01 12.4]# cat 7.non_root_user.yaml apiVersion: v1 kind: Pod metadata: name: non-root-user-pod-true spec: containers: - name: alpine-container image: alpine command: ["/bin/sleep", "9999"] securityContext: runAsNonRoot: true runAsUser: 405
4.期望:容器组:容器名、基于镜像版本、启动命令、安全上下文:不使用 root 用户运行:开启、使用运行的用户:405
[root@k8s-master01 12.4]# kubectl apply -f 7.non_root_user.yaml pod/non-root-user-pod-true created [root@k8s-master01 12.4]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES non-root-user-pod-false 0/1 CreateContainerConfigError 0 3m47s 10.244.58.212 k8s-node02 <none> <none> non-root-user-pod-true 1/1 Running 0 5s 10.244.58.209 k8s-node02 <none> <none> [root@k8s-master01 12.4]# kubectl exec -it non-root-user-pod-true -- /bin/sh / $ id uid=405(guest) gid=100(users) groups=100(users) / $ exit
# 消除实验影响 [root@k8s-master01 12.4]# kubectl delete pod --all pod "non-root-user-pod-false" deleted pod "non-root-user-pod-true" deleted
(4)使用特权模式运行 pod
# 非特权用户,权限受到限制 [root@k8s-master01 12.4]# cat 8.pod.yaml apiVersion: v1 kind: Pod metadata: name: privileged-pod-1 spec: containers: - name: privileged-container image: myapp:v1.0 [root@k8s-master01 12.4]# kubectl apply -f 8.pod.yaml pod/privileged-pod-1 created [root@k8s-master01 12.4]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES privileged-pod-1 1/1 Running 0 4s 10.244.58.218 k8s-node02 <none> <none> # /dev 下 权限受到限制,存放的是设备文件 [root@k8s-master01 12.4]# kubectl exec -it privileged-pod-1 -- /bin/sh / # ls /dev core full null pts shm stdin termination-log urandom fd mqueue ptmx random stderr stdout tty zero / # exit
[root@k8s-master01 12.4]# cat 9.privileged.yaml apiVersion: v1 kind: Pod metadata: name: privileged-pod-2 spec: containers: - name: privileged-container image: myapp:v1.0 securityContext: privileged: true
4.期望:容器组:容器名、基于镜像版本、安全上下文:特权模式:开启
[root@k8s-master01 12.4]# kubectl apply -f 9.privileged.yaml pod/privileged-pod-2 created [root@k8s-master01 12.4]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES privileged-pod-1 1/1 Running 0 5m3s 10.244.58.218 k8s-node02 <none> <none> privileged-pod-2 1/1 Running 0 5s 10.244.85.243 k8s-node01 <none> <none> # /dev 下设备文件明显变多,能使用的设备变多,比如 cpu、磁盘、port 等 [root@k8s-master01 12.4]# kubectl exec -it privileged-pod-2 -- /bin/sh / # ls /dev autofs fuse nvram snapshot tty14 tty27 tty4 tty52 tty8 vcs vcsu bsg hidraw0 port snd tty15 tty28 tty40 tty53 tty9 vcs1 vcsu1 bus hpet ppp sr0 tty16 tty29 tty41 tty54 ttyS0 vcs2 vcsu2 core hwrng ptmx stderr tty17 tty3 tty42 tty55 ttyS1 vcs3 vcsu3 cpu input pts stdin tty18 tty30 tty43 tty56 ttyS2 vcs4 vcsu4 cpu_dma_latency kmsg random stdout tty19 tty31 tty44 tty57 ttyS3 vcs5 vcsu5 dm-0 loop-control rfkill termination-log tty2 tty32 tty45 tty58 udmabuf vcs6 vcsu6 dm-1 mapper rtc0 tty tty20 tty33 tty46 tty59 uhid vcsa vfio dma_heap mcelog sda tty0 tty21 tty34 tty47 tty6 uinput vcsa1 vga_arbiter dmmidi mem sda1 tty1 tty22 tty35 tty48 tty60 urandom vcsa2 vhci dri midi sda2 tty10 tty23 tty36 tty49 tty61 usbmon0 vcsa3 vhost-net fb0 mqueue sg0 tty11 tty24 tty37 tty5 tty62 usbmon1 vcsa4 vhost-vsock fd net sg1 tty12 tty25 tty38 tty50 tty63 usbmon2 vcsa5 vmci full null shm tty13 tty26 tty39 tty51 tty7 userfaultfd vcsa6 zero / # exit
# 消除实验影响 [root@k8s-master01 12.4]# kubectl delete pod --all pod "privileged-pod-1" deleted pod "privileged-pod-2" deleted
(5)为容器单独添加内核功能
注意:不是修改了 Pod IP地址 就可以直接在集群中使用的,因为 网络插件 calico 底层是由 ipvsadm/iptables 对原 Pod 的 IP地址进行路由,手动新修改了 IP 地址后,将会路由不到
# 上一个实验的 Pod 是标准无特权的 Pod ,用来作对比 [root@k8s-master01 12.4]# cat 8.pod.yaml apiVersion: v1 kind: Pod metadata: name: privileged-pod-1 spec: containers: - name: privileged-container image: myapp:v1.0 [root@k8s-master01 12.4]# kubectl apply -f 8.pod.yaml pod/privileged-pod-1 created [root@k8s-master01 12.4]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES privileged-pod-1 1/1 Running 0 4s 10.244.58.222 k8s-node02 <none> <none> # 这里 Pod 内虽是 root 用户,但是不能修改 IP ,因为 Pod 内的 root 是被宿主机使用namespace 隔离后的 伪root 用户 [root@k8s-master01 12.4]# kubectl exec -it privileged-pod-1 -- /bin/sh / # 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) / # ifconfig eth0 Link encap:Ethernet HWaddr DE:42:4A:15:BD:C7 inet addr:10.244.58.222 Bcast:0.0.0.0 Mask:255.255.255.255 inet6 addr: fe80::dc42:4aff:fe15:bdc7/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:5 errors:0 dropped:0 overruns:0 frame:0 TX packets:8 errors:0 dropped:1 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:446 (446.0 B) TX bytes:656 (656.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) / # ifconfig eth0 10.244.58.222 netmask 255.255.255.255 ifconfig: SIOCSIFADDR: Operation not permitted / # exit
[root@k8s-master01 12.4]# cat 10.system.yaml apiVersion: v1 kind: Pod metadata: name: sys-time-pod spec: containers: - name: sys-time-container image: myapp:v1.0 securityContext: capabilities: add: ["NET_ADMIN"]
4.期望:容器组:容器名、基于镜像版本、安全上下文:能力:添加:网络管理员
[root@k8s-master01 12.4]# kubectl apply -f 10.system.yaml pod/sys-time-pod created [root@k8s-master01 12.4]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES privileged-pod-1 1/1 Running 0 13m 10.244.58.222 k8s-node02 <none> <none> sys-time-pod 1/1 Running 0 4s 10.244.58.248 k8s-node02 <none> <none> # 下面虽然修改了 Pod IP 地址,但是因为没有网络插件的路由,所以不能通过手动修改的 IP地址访问,这里只是用以体现拥有了添加的 网络管理员权限 [root@k8s-master01 12.4]# kubectl exec -it sys-time-pod -- /bin/sh / # 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) / # ifconfig eth0 Link encap:Ethernet HWaddr F2:E4:D5:94:E1:EE inet addr:10.244.58.248 Bcast:0.0.0.0 Mask:255.255.255.255 inet6 addr: fe80::f0e4:d5ff:fe94:e1ee/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:5 errors:0 dropped:0 overruns:0 frame:0 TX packets:8 errors:0 dropped:1 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:446 (446.0 B) TX bytes:656 (656.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) / # ifconfig eth0 10.244.58.201 netmask 255.255.255.255 / # ifconfig eth0 Link encap:Ethernet HWaddr F2:E4:D5:94:E1:EE inet addr:10.244.58.201 Bcast:10.255.255.255 Mask:255.255.255.255 inet6 addr: fe80::f0e4:d5ff:fe94:e1ee/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:5 errors:0 dropped:0 overruns:0 frame:0 TX packets:9 errors:0 dropped:1 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:446 (446.0 B) TX bytes:726 (726.0 B) lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:65536 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:0 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B) / # exit # 两个 IP 都访问不通,原 IP 被修改,calico 的路由规则入站时,流量被拒绝,而手动修改的 IP,没有 calico 添加的路由策略,没办法入站 [root@k8s-master01 12.4]# curl 10.244.58.248 # 原 Pod IP ^C [root@k8s-master01 12.4]# curl 10.244.58.201 # 现 Pod IP ^C
# capabilities 能力 能够添加的权限,使用 capsh 命令(需安装 libcap 工具包): [root@k8s-master01 12.4]# capsh --print Current: =ep Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read,cap_perfmon,cap_bpf,cap_checkpoint_restore Ambient set = Current IAB: Securebits: 00/0x0/1'b0 (no-new-privs=0) secure-noroot: no (unlocked) secure-no-suid-fixup: no (unlocked) secure-keep-caps: no (unlocked) secure-no-ambient-raise: no (unlocked) uid=0(root) euid=0(root) gid=0(root) groups=0(root) Guessed mode: UNCERTAIN (0)
# 消除实验影响 [root@k8s-master01 12.4]# kubectl delete pod --all pod "privileged-pod-1" deleted pod "sys-time-pod" deleted
(6)在容器中禁用内核功能
# 第 4 个实验的 Pod 是标准无特权的 Pod ,用来作对比 [root@k8s-master01 12.4]# cat 8.pod.yaml apiVersion: v1 kind: Pod metadata: name: privileged-pod-1 spec: containers: - name: privileged-container image: myapp:v1.0 [root@k8s-master01 12.4]# kubectl apply -f 8.pod.yaml pod/privileged-pod-1 created [root@k8s-master01 12.4]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES privileged-pod-1 1/1 Running 0 5s 10.244.58.226 k8s-node02 <none> <none> # 创建文件,可以修改文件的用户和用户组 [root@k8s-master01 12.4]# kubectl exec -it privileged-pod-1 -- /bin/sh / # cd ~ # touch 1.txt ~ # cat /etc/passwd root:x:0:0:root:/root:/bin/ash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin news:x:9:13:news:/usr/lib/news:/sbin/nologin uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin operator:x:11:0:operator:/root:/bin/sh man:x:13:15:man:/usr/man:/sbin/nologin postmaster:x:14:12:postmaster:/var/spool/mail:/sbin/nologin cron:x:16:16:cron:/var/spool/cron:/sbin/nologin ftp:x:21:21::/var/lib/ftp:/sbin/nologin sshd:x:22:22:sshd:/dev/null:/sbin/nologin at:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin squid:x:31:31:Squid:/var/cache/squid:/sbin/nologin xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin games:x:35:35:games:/usr/games:/sbin/nologin postgres:x:70:70::/var/lib/postgresql:/bin/sh cyrus:x:85:12::/usr/cyrus:/sbin/nologin vpopmail:x:89:89::/var/vpopmail:/sbin/nologin ntp:x:123:123:NTP:/var/empty:/sbin/nologin smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin guest:x:405:100:guest:/dev/null:/sbin/nologin nobody:x:65534:65534:nobody:/:/sbin/nologin nginx:x:100:101:Linux User,,,:/var/cache/nginx:/sbin/nologin ~ # chown nobody:nobody 1.txt ~ # ls -ltr total 0 -rw-r--r-- 1 nobody nobody 0 Aug 12 08:39 1.txt ~ # exit
[root@k8s-master01 12.4]# cat 11.non_chown.yaml apiVersion: v1 kind: Pod metadata: name: disable-chown-pod spec: containers: - name: nginx-container image: myapp:v1.0 securityContext: capabilities: drop: - CHOWN
4.期望:容器组:容器名、基于镜像版本、安全上下文:能力:删除:chown 命令
[root@k8s-master01 12.4]# kubectl apply -f 11.non_chown.yaml pod/disable-chown-pod created # 这里与老师视频不一样,视频中 容器启动命令会自动修改文件的 用户与用户组,因为已经删除了 chown ,所以启动时报错,这个镜像容器启动时没有更改文件命令,所以能够启动,但是进入 Pod 中,确实不能修改用户与用户组了,显示受限 # 如果像视频中一样,可以 kubectl describe 、kubectl logs 查看
[root@k8s-master01 12.4]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES disable-chown-pod 1/1 Running 0 57s 10.244.58.215 k8s-node02 <none> <none> privileged-pod-1 1/1 Running 0 8m53s 10.244.58.226 k8s-node02 <none> <none> [root@k8s-master01 12.4]# kubectl exec -it disable-chown-pod -- /bin/sh / # cd ~ # touch 1.txt ~ # chown nobody:nobody 1.txt chown: 1.txt: Operation not permitted ~ # exit command terminated with exit code 1
# 消除实验影响 [root@k8s-master01 12.4]# kubectl delete pod --all pod "disable-chown-pod" deleted pod "privileged-pod-1" deleted
(7)阻止对容器根文件系统的写入
# 本实验不需要做对比,因为之前的 Pod 是默认使用 root 用户运行,所以一定可以创建文件 [root@k8s-master01 12.4]# cat 12.readonly_file.yaml apiVersion: v1 kind: Pod metadata: name: pod-with-readonly-filesystem spec: containers: - name: main image: alpine command: ["/bin/sleep", "9999"] securityContext: readOnlyRootFilesystem: true volumeMounts: - name: my-volume mountPath: /volume readOnly: false volumes: - name: my-volume emptyDir:
4.期望: 容器组:容器名、基于镜像版本、启动命令、安全上下文:只读 roo 文件系统:开启、挂载卷:卷名、挂载容器目录、只读:不开启(即 不只读,是读写权限 ) 定义卷:卷名、空目录
[root@k8s-master01 12.4]# kubectl apply -f 12.readonly_file.yaml pod/pod-with-readonly-filesystem created [root@k8s-master01 12.4]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod-with-readonly-filesystem 1/1 Running 0 7s 10.244.58.214 k8s-node02 <none> <none> # 只对某一个卷内可以进行读写,其他都是只读,提高文件安全性 [root@k8s-master01 12.4]# kubectl exec -it pod-with-readonly-filesystem -- /bin/sh / # ls bin dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var volume / # touch 1.txt touch: 1.txt: Read-only file system / # touch /root/1.txt touch: /root/1.txt: Read-only file system / # touch /volume/1.txt / # exit
# 消除实验影响 [root@k8s-master01 12.4]# kubectl delete pod --all pod "pod-with-readonly-filesystem" deleted
(8)文件组与附加组 fsGroup 与 supplementalGroups
注意:对文件进行更改时,文件的组和附加组会成为文件的用户组,注意只有 fsGroup,没有 fsUser
[root@k8s-master01 12.4]# cat 13.group.yaml apiVersion: v1 kind: Pod metadata: name: pod-with-shared-volume-fsgroup spec: securityContext: fsGroup: 555 supplementalGroups: [666, 777] containers: - name: first image: alpine command: ["/bin/sleep", '9999'] securityContext: runAsUser: 1111 volumeMounts: - name: shared-volume mountPath: /volume readOnly: false - name: second image: alpine command: ["/bin/sleep", '9999'] securityContext: runAsUser: 2222 volumeMounts: - name: shared-volume mountPath: /volume readOnly: false volumes: - name: shared-volume emptyDir:
4.期望:Pod 安全上下文:文件组:555、附加组:666、777 容器组:容器名1、基于镜像版本、启动命令、容器安全上下文:运行使用用户:1111、卷挂载:卷名、挂载容器路径、只读:否(即不只读,是读写方式) 容器名2、基于镜像版本、启动命令、容器安全上下文:运行使用用户:2222、卷挂载:卷名、挂载容器路径、只读:否(即不只读,是读写方式) 定义卷:卷名、空目录
[root@k8s-master01 12.4]# kubectl apply -f 13.group.yaml pod/pod-with-shared-volume-fsgroup created [root@k8s-master01 12.4]# kubectl get pod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES pod-with-shared-volume-fsgroup 2/2 Running 0 2m32s 10.244.58.224 k8s-node02 <none> <none> # 当前容器中 用户是 1111,用户组是 root,但是当创建文件时,文件的用户组是 555 # 文件组可以在同 Pod 中,跨容器去修改同 Pod 中的其他容器内文件,另外一个容器的用户在本容器的用户组内 # 附加组可以跨 Pod 修改文件,将其它 Pod的用户信息添加到本 Pod 的附加组中,本 Pod 可以跨 Pod 去修改文件或两Pod 共同使用文件 [root@k8s-master01 12.4]# kubectl exec -it pod-with-shared-volume-fsgroup -c first -- /bin/sh ~ $ id uid=1111 gid=0(root) groups=0(root),555,666,777 ~ $ cd /volume/ /volume $ touch 1.txt /volume $ ls -ltr total 0 -rw-r--r-- 1 1111 555 0 Aug 12 14:33 1.txt /volume $ exit
# 消除实验影响 [root@k8s-master01 12.4]# kubectl delete pod --all pod "pod-with-shared-volume-fsgroup" deleted
12.4.3 PodSecurityAdmission(namespace 级)
(1)概念
从 Kubernetes v1.21 开始,Pod Security Policy(对 Pod 安全进行策略定义的功能,PSP) 将被弃用,并将在 v1.25 中删除,Kubernetes 在 v1.22 版本引入了 Pod Security Admission(PSA) 作为其替代者(两者没有关联,不是继承关系)
Pod Security Admission(PSA) 机制在易用性和灵活性上都有了很大提升,从使用角度有以下三点显著不同:
可以在集群中默认开启,只要不设置约束条件就不会触发对 Pod 的校验
只在命名空间级别生效,可以为不同的命名空间通过添加标签的方式设置不同的安全限制
根据实践预设了三种安全等级,不需要由用户单独去设置每一项安全条件(官方根据大量使用场景,确定预置了三种安全等级)
(2)PSA 安全策略分类
为了广泛的覆盖安全应用场景,Pod Security Standards 渐进式的定义了三种不同的 Pod 安全标准策略:
策略 | 描述 |
Privileged | 不受限制的策略,提供最大可能范围的权限许可,此策略允许已知的特权提升 |
Baseline | 限制性最弱的策略,禁止已知的策略提升,允许使用默认(规定最少)的Pod 配置 |
Restricted |
限制性非常强的策略,遵循当前的保护 Pod 的最佳实践(官方推荐,同时规则也是最多的) |
(3)PSA 设定模式
在 Kubernetes 集群中开启了 pod Security admission 后,就可以通过给 namespace 设置 label 的方式来实施 Pod Security Standards,其中有三种设定模式可选用
模式 | 描述 |
enforce | 违反安全标准策略的 Pod 将被拒绝 |
audit | 违反安全标准策略触发向审计日志中记录的事件添加审计注释,但其行为被允许 |
warn | 违反安全标准策略将触发面向用户的警告,但其行为被允许 |
(4)Label 模板设置
设定模式及安全标准策略等级
pod-security.kubernetes.io/<MODE>.<LEVEL>
MODE 必须是 enforce,audit 或 warn 其中之一
LEVEL 必须是 privileged,baseline 或 restricted 其中之一
此选项是非必填的,用来锁定使用哪个版本的安全标准(类似法律需要更新迭代)
pod-security.kubernetes.io/<MODE>-version:<VERSION>
MODE 必须是 enforce,audit 或 warn 其中之一
VERSION 必须是一个有效的 kubernetes minor version(例如 v1.23) 或者 latest
(5)pod Security Standards 示例演示 - Baseline
Baseline(基线) 策略目标是应用于常见的容器化应用,禁止已知的特权提升,在官方的介绍中此策略针对的是应用运维人员和非关键性应用开发人员,在该策略中包括(即基线的限制):必须禁止共享宿主命名空间、禁止容器特权、限制 Linux 能力、禁止 hostPath 卷、限制宿主机端口、设定 AppArmor、SElinux、Seccomp、Sysctls 等安全策略
AppArmor、SElinux、Seccomp、Sysctls:(系统内核级)
AppArmor(Application Armor):是一种 Linux 内核安全模块,它允许系统管理员为每个程序指定安全策略,以限制程序的行为,这些策略定义了哪些文件、目录和网络资源可以由应用程序访问,以及程序在系统上运行时应该具有哪些权限
SELinux(Security-Enhanced Linux):SELinux 是 Linux 内核的一个安全子系统,它提供了强大的访问控制机制,可以强制实施安全策略。SELinux 使用安全上下文来定义对象(如文件、进程等)的安全策略,并根据这些安全策略来控制对这些对象的访问
Seccomp(Secure Computing Mode):Seccomp 是 Linux内核的一个安全特性,它允许程序在沙盒环境中运行,并限制其可以执行的系统调用
Sysctls(System Control Parameters):Sysctls 是 Linux内核的一个功能,它允许系统管理员动态地配置内核参数,这些参数可以控制系统的各种行为,如网络栈参数、内存管理参数等。
违反 Baseline 策略存在的风险:
特权容器可以看到宿主机设备
挂载 procfs 后可以看到宿主机进程,打破了进程隔离
可以打破网络隔离
挂载运行时 socket 后,可以不受限制的与运行时通信
(6)pod Security Standards 示例演示 - Restricted
Restricted [rɪˈstrɪktɪd] 策略目标是实施当前保护 Pod 的最佳实践,在官方介绍中此策略主要针对运维人员和安全性很重要的应用开发人员,以及不太被信任的用户,该策略包含所有的 baseline 策略的内容,额外增加:限制可以通过 PersistentVolumes 定义的非核心卷类型、禁止(通过 SetUID 或 SetGID 文件模式)获得特权提升、必须要求容器以非 root 用户运行、Containers 不可以将 runAsUser 设置为 0、容器组必须弃用 ALL capabilities 并且只允许添加 NET_BIND_SERVICE 能力
Restricted 策略进一步的限制在容器内获取 root 权限,linux 内核功能。例如针对 kubernetes 网络的中间人攻击需要拥有 Linux 系统内核的 CAP_NET_RAW 权限来发送 ARP 包
(7)PSA 当前局限性
posSecurity admission 只是对 pod 进行安全标准的检查,不支持对 pod 进行修改,不能为 pod 设置默认的安全配置(不符合安全策略创建时提示错了,但不会自动调整符合的安全策略)
podSecurity admission 只支持官方定义的三种安全标准策略,不支持灵活的自定义安全标准策略,这使得不能完全将 PSP(Pod Security Policy) 规则迁移到 PSA,需要进行具体的安全规则考量(不能灵活自定义)
12.4.4 Baseline(基线)实验
(1)创建带有基线的名称空间
创建名为 my-baseline-namespace 的 namespace,并设定 enforce 和 warn 两种模式都对应 Baseline 等级的 Pod 安全标准策略 [root@k8s-master01 12.4]# cat 14.baseline.yaml apiVersion: v1 kind: Namespace metadata: name: my-baseline-namespace labels: pod-security.kubernetes.io/enforce: baseline pod-security.kubernetes.io/enforce-version: v1.29
3.元数据:名称空间名字、标签:开启的PSA模式:baseline 基线、kubernetes 版本:v1.29
[root@k8s-master01 12.4]# kubectl apply -f 14.baseline.yaml namespace/my-baseline-namespace created [root@k8s-master01 12.4]# kubectl get namespace NAME STATUS AGE ........ my-baseline-namespace Active 7s ........
(2)创建违反基线的 Pod
[root@k8s-master01 12.4]# cat 15.pod.yaml apiVersion: v1 kind: Pod metadata: name: hostnamespaces1 namespace: my-baseline-namespace spec: containers: - image: myapp:v1.0 name: myapp securityContext: privileged: true hostPID: true
4.期望:容器组:基于镜像版本、容器名、安全上下文:特权模式:开启 共享主机Pid:开启
# 应用时报错,因为违反了 策略 [root@k8s-master01 12.4]# kubectl apply -f 15.pod.yaml Error from server (Forbidden): error when creating "15.pod.yaml": pods "hostnamespaces1" is forbidden: violates PodSecurity "baseline:v1.29": host namespaces (hostPID=true), privileged (container "myapp" must not set securityContext.privileged=true)
(3)创建不违反基线的 Pod
# 创建不违反 baseline 策略的 pod,设定 Pod 的 hostPID=false,securityContext.privileged=false(不违反基线规则,或者不写也可以) [root@k8s-master01 12.4]# cat 16.pod.yaml apiVersion: v1 kind: Pod metadata: name: hostnamespaces2 namespace: my-baseline-namespace spec: containers: - image: myapp:v1.0 name: myapp securityContext: privileged: false hostPID: false
[root@k8s-master01 12.4]# kubectl apply -f 16.pod.yaml pod/hostnamespaces2 created [root@k8s-master01 12.4]# kubectl get pod -o wide -n my-baseline-namespace NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES hostnamespaces2 1/1 Running 0 17s 10.244.58.227 k8s-node02 <none> <none>
# 消除实验影响 [root@k8s-master01 12.4]# kubectl delete pod --all -n my-baseline-namespace pod "hostnamespaces2" deleted
12.4.5 Restricted(限制)实验
注意:在不同的版本上,策略会发生变化(类似因之前发布的法律有漏洞,后面会颁布新的法律,新的法律可以修改不同应用场景)
(1)创建带有 Restricted 的名称空间
# 创建名为 my-restricted-namespace 的 namespace,并设定 enforce 模式对应 Restricted 等级的 Pod 安全标准策略 [root@k8s-master01 12.4]# cat 17.restricted.yaml apiVersion: v1 kind: Namespace metadata: name: my-restricted-namespace labels: pod-security.kubernetes.io/enforce: restricted pod-security.kubernetes.io/enforce-version: v1.29
3.元数据:名称空间名字、标签:开启的PSA模式: restricted 策略、kubernetes 版本:v1.29
[root@k8s-master01 12.4]# kubectl apply -f 17.restricted.yaml namespace/my-restricted-namespace created [root@k8s-master01 12.4]# kubectl get namespace NAME STATUS AGE .......... my-restricted-namespace Active 8s ..........
(2)创建违反 Restricted 策略的 Pod
# 创建基础 pod,也被拒绝 [root@k8s-master01 12.4]# cat 18.pod.yaml apiVersion: v1 kind: Pod metadata: name: pod namespace: my-restricted-namespace spec: containers: - image: myapp:v1.0 name: myapp [root@k8s-master01 12.4]# kubectl apply -f 18.pod.yaml Error from server (Forbidden): error when creating "18.pod.yaml": pods "pod" is forbidden: violates PodSecurity "restricted:v1.29": allowPrivilegeEscalation != false (container "myapp" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "myapp" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "myapp" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "myapp" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
(3)创建不违反 Restricted 策略的 Pod
# 创建不违反 restricted 策略的 pod,默认 Pod 中有些权限已经违反了 restricted 的策略,应用会报错
[root@k8s-master01 12.4]# cat 19.pod.yaml apiVersion: v1 kind: Pod metadata: name: runasnonroot0 namespace: my-restricted-namespace spec: containers: - name: myapp image: alpine securityContext: runAsNonRoot: true runAsUser: 102 allowPrivilegeEscalation: false capabilities: drop: - ALL securityContext: runAsNonRoot: true seccompProfile: type: RuntimeDefault
4.期望:容器组:容器名、基于镜像版本、容器安全上下文、不使用 root 运行:开启、运行使用用户:102 用户、允许额外扩展添加动作:关闭、capabilities 策略:删除:所有策略 Pod 安全上下文:不使用 root 运行:开启、限制容器的配置文件:类型:默认类型
[root@k8s-master01 12.4]# kubectl apply -f 19.pod.yaml pod/runasnonroot0 created
# 消除实验影响 [root@k8s-master01 12.4]# kubectl delete pod --all -n my-restricted-namespace pod "runasnonroot0" deleted
———————————————————————————————————————————————————————————————————————————
无敌小马爱学习