在容器上内嵌了containers字段,额外还定义了hostPort,就意味着可以像docker一样,让pod运行的那个主机打开一个DNAT的端口映射,把宿主机的某个请求做地址转换后转发至内部的容器之上。而hostNetwork所提供的功能是让pod直接共享了它所在的宿主机的网路名称空间。

以上的两个操作都属于特权操作,正常情况下,一个多租户的K8S,使用以上两种方式来让外部的流量进入k8s将会造成隔离级的开放。而k8s为了安全运行pod级容器专门设计了一种叫安全上下文的概念(SecurityContext)。

  • SecuritContext:安全上下文主要是允许用户和管理员定义容器或Pod的特权或访问控制机制,以配置容器、主机以及主机之上的其他容器之间的隔离方式或隔离级别。安全上下文就是一组决定容器如何创建和运行的约束条件,他们代表创建和运行容器时使用的运行时参数。

SecurityContext级别

Pod上的SecurityContext有两个级别:

  • Pod级别:对Pod内的所有容器生效
  • 容器级别:只对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
apiVersion: v1
kind: Pod
metadata: {…}
spec:
securityContext: # Pod级别的安全上下文,对内部所有容器均有效
runAsUser <integer> # 以指定的用户身份运行容器进程,默认由镜像中的USER指定
runAsGroup <integer> # 以指定的用户组运行容器进程,默认使用的组随容器运行时
supplementalGroups <[]integer> # 为容器中1号进程的用户添加的附加组;
fsGroup <integer> # 为容器中的1号进程附加的一个专用组,其功能类似于sgid
runAsNonRoot <boolean> # 是否以非root身份运行
seLinuxOptions <Object> # SELinux的相关配置
sysctls <[]Object> # 应用到当前Pod上的名称空间级别的sysctl参数设置列表
windowsOptions <Object> # Windows容器专用的设置
containers:
- name: …
image: …
securityContext: # 容器级别的安全上下文,仅生效于当前容器
runAsUser <integer> # 以指定的用户身份运行容器进程
runAsGroup <integer> # 以指定的用户组运行容器进程
runAsNonRoot <boolean> # 是否以非root身份运行
allowPrivilegeEscalation <boolean> # 是否允许特权升级
capabilities <Object> # 于当前容器上添加(add)或删除(drop)的内核能力
add <[]string> # 添加由列表定义的各内核能力
drop <[]string> # 移除由列表定义的各内核能力
privileged <boolean> # 是否运行为特权容器,一般开放给k8s系统级别容器。不到万不得已不启用。
procMount <string> # 设置容器的procMount类型,默认为DefaultProcMount;
readOnlyRootFilesystem <boolean> # 是否将根文件系统设置为只读模式
seLinuxOptions <Object> # SELinux的相关配置
windowsOptions <Object> # windows容器专用的设置

SecurityContext示例

以普通用户运行容器

1.创建配置清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
root@k8s-master01:~/yaml/chapter04# vim securitycontext-runasuser-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: securitycontext-runasuser-demo
namespace: default
spec:
containers:
- name: demo
image: ikubernetes/demoapp:v1.0
imagePullPolicy: IfNotPresent
env:
- name: PORT
value: "8080"
securityContext:
runAsUser: 1001 # 使用普通用户,普通用户默认不具备监听1024内端口
runAsGroup: 1001

2.应用配置文件

1
2
root@k8s-master01:~/yaml/chapter04# kubectl apply -f securitycontext-runasuser-demo.yaml
pod/securitycontext-runasuser-demo created

3.验证pod是否运行为普通用户

1
2
3
4
5
root@k8s-master01:~/yaml/chapter04# kubectl exec securitycontext-runasuser-demo -- ps aux
PID USER TIME COMMAND
1 1001 0:00 python3 /usr/local/bin/demo.py
14 1001 0:00 ps aux
# 已运行为普通用户1001

设定容器级capabilities

可设定的capabilities有以下:

  • CAP_CHOWN:改变文件UID和GID
  • CAP_MKNOD:能使用mknod(),创建设备文件;
  • CAP_NET_ADMIN:网络管理权限。
  • CAP_SYS_ADMIN:大部分管理权限
  • CAP_SYS_TIME:管理内核时钟
  • CAP_SYS_MODULE:装卸载内核模块
  • CAP_NET_BIND_SERVICE:是否允许能够将绑定1024以内端口

设定网络管理权限能力

1.编写配置清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
root@k8s-master01:~/yaml/chapter04# vim securitycontext-capabilities-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: securitycontext-capabilities-demo
namespace: default
spec:
containers:
- name: demo
image: ikubernetes/demoapp:v1.0
command: ["/bin/sh","-c"]
args: ["/sbin/iptables -t nat -A PREROUTING -p tcp --dport 8080 -j REDIRECT --to-port 80 && /usr/bin/python3 /usr/local/bin/demo.py"]
securityContext:
capabilities:
add: ['NET_ADMIN'] # 给与网络管理权限能力,容器默认启动为root权限,但是没有修改iptable规则的能力,需要添加。
drop: ['CHOWN'] # 删除修改文件属主属组能力

2.引用容器清单

1
2
3
4
5
6
root@k8s-master01:~/yaml/chapter04# kubectl apply -f securitycontext-capabilities-demo.yaml
pod/securitycontext-capabilities-demo created

root@k8s-master01:~/yaml/chapter04# kubectl get pods securitycontext-capabilities-demo -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
securitycontext-capabilities-demo 1/1 Running 0 49s 10.244.2.18 k8s-node02 <none> <none>

3.验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 测试请求8080嫩否被转发
root@k8s-master01:~/yaml/chapter04# curl 10.244.2.18:8080
iKubernetes demoapp v1.0 !! ClientIP: 10.244.0.0, ServerName: securitycontext-capabilities-demo, ServerIP: 10.244.2.18!

# 查看容器内iptables规则
root@k8s-master01:~/yaml/chapter04# kubectl exec securitycontext-capabilities-demo -- iptables -t nat -nvL
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
1 60 REDIRECT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 redir ports 80

Chain INPUT (policy ACCEPT 1 packets, 60 bytes)
pkts bytes target prot opt in out source destination

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination


# 测试chown能否使用
root@k8s-master01:~/yaml/chapter04# kubectl exec securitycontext-capabilities-demo -- chown 2:2 /etc/fstab
chown: /etc/fstab: Operation not permitted
command terminated with exit code 1

设定容器内sysctls的参数

我们还可以改变pod内部的网络名称空间的内核参数,而事实上目前在容器能安全允许pod修改的参数只有三个,并且其作用并非很大。如果要启用其他非安全参数,则需要在k8s启动时,允许非安全参数也能在pod内进行设定。

pod内可安全设定的内核参数

k8s默认允许设定的安全的内核参数有以下三个:

  • kernel.shm_rmid_forced
  • net.ipv4.ip_local_port_range
  • net.ipv4.tcp_syncookies

除此之外都不允许设定,如要设定需要修改k8s启动参数。

非安全参数设定方法示例

1.修改/etc/default/kubelet文件,加入需要启用的非安全参数,如果没有此文件可以自己创建,kubelet会自动读取

1
2
3
4
5
root@k8s-master01:~/yaml/chapter04# vim /etc/default/kubelet
KUBELET_EXTRA_ARGS='--allowed-unsafe-sysctls=net.core.somaxconn,net.ipv4.ip_unprivileged_port_start'

# net.core.somaxconn 设定系统级别默认入栈队列大小,默认128
# net.ipv4.ip_unprivileged_port_start 设定非特权用户默认可以使用的端口的起始值,默认1024

2.重启kubelet

1
root@k8s-master01:~/yaml/chapter04# systemctl restart kubelet

注意:以上操作所有节点都需要修改,因为pod调度到那个节点上是未知的。

参数设定示例

1.创建配置清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
root@k8s-master01:~/yaml/chapter04# vim securitycontext-sysctls-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: securitycontext-sysctls-demo
namespace: default
spec:
securityContext:
sysctls:
- name: kernel.shm_rmid_forced
value: "0"
- name: net.ipv4.ip_unprivileged_port_start # 设定非特权用户监听的端口
value: "0"
containers:
- name: demoapp
image: ikubernetes/demoapp:v1.0
imagePullPolicy: IfNotPresent
securityContext:
runAsUser: 1001 # 为了显示出非特权用户监听的特权端口的效果此处使用1001用户
runAsGroup: 1001

# 由于此清单中net.ipv4.ip_unprivileged_port_start为非安全参数所以需要修改/etc/default/kubelet文件

2.在所有节点修改/etc/default/kubelet文件,加入允许设定的非安全参数,并重启kubelet

1
2
3
4
5
6
root@k8s-master01:~/yaml/chapter04# vim /etc/default/kubelet
KUBELET_EXTRA_ARGS='--allowed-unsafe-sysctls=net.ipv4.ip_unprivileged_port_start'

root@k8s-master01:~/yaml/chapter04# systemctl restart kubelet

# 以上操作在所有节点上执行

3.引用配置清单创建pod

1
2
3
4
5
root@k8s-master01:~/yaml/chapter04# kubectl apply -f securitycontext-sysctls-demo.yaml
pod/securitycontext-sysctls-demo created
root@k8s-master01:~/yaml/chapter04# kubectl get pods securitycontext-sysctls-demo -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
securitycontext-sysctls-demo 1/1 Running 0 15s 10.244.3.18 k8s-node03 <none> <none>

4.验证用户及监听

1
2
3
4
5
6
7
8
9
10
11
# 监听端口为80
root@k8s-master01:~/yaml/chapter04# kubectl exec securitycontext-sysctls-demo -- ss -tnl
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:80 0.0.0.0:*

# 用户为1001
root@k8s-master01:~/yaml/chapter04# kubectl exec securitycontext-sysctls-demo -- ps aux
PID USER TIME COMMAND
1 1001 0:00 python3 /usr/local/bin/demo.py
15 1001 0:00 ps aux