一.pod安全上下文
1.简介
K8s对Pod和容器提供的安全机制,可以设置Pod特权和访问控制。
2.限制维度
自主访问控制(Discretionary Access Control):基于用户ID(UID)和组ID(GID),来判定对对象(例如文件)的访问权限。
安全性增强的 Linux(SELinux): 为对象赋予安全性标签。
以特权模式或者非特权模式运行。
Linux Capabilities: 为进程赋予 root 用户的部分特权而非全部特权。
AppArmor:定义Pod使用AppArmor限制容器对资源访问限制
Seccomp:定义Pod使用Seccomp限制容器进程的系统调用。
AllowPrivilegeEscalation: 禁止容器中进程(通过 SetUID 或 SetGID 文件模式)获得特权提升。当容器以特权模式运行或者具有CAP_SYS_ADMIN能力时,AllowPrivilegeEscalation总为True。
readOnlyRootFilesystem:以只读方式加载容器的根文件系统。
3.配置参数
securityContext下配置的参数是针对该pod里的所有容器生效。
containers下配置的参数只对某个容器生效,不同容器配置各自需要的参数。
pod安全上下文配置参数 | 解释 |
---|
spec.securityContext | Pod级别的安全上下文,对内部所有容器均有效 |
spec.securityContext.runAsUser < integer > | 以指定的用户身份运行容器进程,默认由镜像中的USER指定 |
spec.securityContext.runAsGroup < integer > | 以指定的用户组运行容器进程,默认使用的组随容器运行时 |
spec.securityContext.fsGroup < integer > | 数据卷挂载后的目录文件设置为该组 |
spec.securityContext.runAsNonRoot < boolean > | 是否以非root身份运行 |
spec.securityContext.seLinuxOptions < Object > | SELinux的相关配置 |
spec.securityContext.sysctls < lObject > | 应用到当前Pod的名称空间级别的sysctl参数设置列表 |
spec.containers. securityContext | 容器级别的安全上下文,仅生效于当前容器 |
spec.containers. securityContext.runAsUser < integer > | 以指定的用户身份运行容器进程 |
spec.containers. securityContext.runAsGroup < integer > | 以指定的用户组运行容器进程 |
spec.containers. securityContext.runAsNonRoot < boolean > | 是否以非root身份运行 |
spec.containers. securityContext.allowPrivilegeEscalation < boolean > | 是否允许特权升级 |
spec.containers. securityContext.capabilities < Object > | 于当前容器上添加 (add) 删除 (drop)的内核能力 |
spec.containers. securityContext.add < [ ]string > | 添加由列表定义的各内核能力 |
spec.containers. securityContext.drop< [ ]string > | 移除由列表定义的各内核能力 |
spec.containers. securityContext.privileged < boolean > | 是否运行为特权容器 |
spec.containers. securityContext.readOnlyRootFilesystem < boolean > | 是否将根文件系统设置为只读模式 |
spec.containers. securityContext.selinuxOptions < opect > | SeLinux的相关配置 |
4.案例
4.1设置容器以普通用户运行
背景:
容器中的应用程序默认以root账号运行的,这个root与宿主机root用户权限是一样的,拥有大部分对Linux内核的系统调用权限,不安全,所以我们应该将容器里的程序以普通用户运行,减少应用程序对权限的使用。
需求:
设置容器以普通用户运行。
实现思路:
Dockerfile里使用USER指定运行用户。
K8s里指定spec.securityContext.runAsUser,指定容器默认用户UID。
#创建镜像并制定运行用户
USER ww
#pod.yaml文件
spec:
securityContext:
runAsUser: 1000 ##这里的id必须是构建镜像里存在的用户id,创建用户时不指定id默认就是从1000开始。
fsGroup: 1000 ##数据卷挂载后的目录属组设置为该组。
containers:
- image: nginx
name: nginx
securityContext:
allowPrivilegeEscalation: false ##不允许提权。
4.2设置容器以root用户运行
背景:
容器中有些应用程序可能需要访问宿主机设备、修改内核等需求,默认情况下,容器没有这个能力,这时可以考虑给容器设置特权模式。
需求:
避免使用特权容器。
使用思路:
docker方式:容器时指定–privileged参数,表示该容器内的程序是以root用户启动,跟在宿主机上用root用户启动一个进程完全没有区别,都可以调用内核的所有参数。
K8s方式:在pod安全上下文设置参数
containers:
- image: nginx
name: nginx
securityContext:
privileged: true
5.Linux Capabilities
5.1简介
Capabilities 的出现就是弥补直接在deployment.yaml中指定特权从而导致权限过大的弊端。
它是一个内核级别的权限调用解决方案,它允许对内核调用权限进行更细粒度的控制,而不是简单地以 root 身份能力授权,但比之前讲的Seccomp限制的容器进程系统调用更粗略一些。
Capabilities 包括更改文件权限、控制网络子系统和执行系统管理等功能。在pod安全上下文中添加或删除Capabilities,可以做到容器精细化权限控制。
capability权能名称 | 解释 |
---|
CAP_AUDIT_CONTROL | 启用和禁用内核审计;改变审计过滤规则;检索审计状态和过滤规则 |
CAP_AUDIT_READ | 允许通过 multicast netlink 套接字读取审计日志 |
CAP_AUDIT_WRITE | 将记录写入内核审计日志 |
CAP_BLOCK_SUSPEND | 使用可以阻止系统挂起的特性 |
CAP_CHOWN | 修改文件所有者的权限 |
CAP_DAC_OVERRIDE | 忽略文件的 DAC 访问限制 |
CAP_DAC_READ_SEARCH | 忽略文件读及目录搜索的 DAC 访问限制 |
CAP_FOWNER | 忽略文件属主 ID 必须和进程用户 ID 相匹配的限制 |
CAP_FSETID | 允许设置文件的 setuid 位 |
CAP_IPC_LOCK | 允许锁定共享内存片段 |
CAP_IPC_OWNER | 忽略 IPC 所有权检查 |
CAP_KILL | 允许对不属于自己的进程发送信号 |
CAP_LEASE | 允许修改文件锁的 FL_LEASE 标志 |
CAP_LINUX_IMMUTABLE | 允许修改文件的 IMMUTABLE 和 APPEND 属性标志 |
CAP_MAC_ADMIN | 允许 MAC 配置或状态更改 |
CAP_MAC_OVERRIDE | 覆盖 MAC(Mandatory Access Control) |
CAP_MKNOD | 允许使用 mknod() 系统调用 |
CAP_NET_ADMIN | 允许执行网络管理任务 |
CAP_NET_BIND_SERVICE | 允许绑定到小于 1024 的端口 |
CAP_NET_BROADCAST | 允许网络广播和多播访问 |
CAP_NET_RAW | 允许使用原始套接字 |
CAP_SETGID | 允许改变进程的 GID |
CAP_SETFCAP | 允许为文件设置任意的 capabilities |
CAP_SETPCAP | 参考 capabilities man page |
CAP_SETUID | 允许改变进程的 UID |
CAP_SYS_ADMIN | 允许执行系统管理任务,如加载或卸载文件系统、设置磁盘配额等 |
CAP_SYS_BOOT | 允许重新启动系统 |
CAP_SYS_CHROOT | 允许使用 chroot() 系统调用 |
CAP_SYS_MODULE | 允许插入和删除内核模块 |
CAP_SYS_NICE | 允许提升优先级及设置其他进程的优先级 |
CAP_SYS_PACCT | 允许执行进程的 BSD 式审计 |
CAP_SYS_PTRACE | 允许跟踪任何进程 |
CAP_SYS_RAWIO | 允许直接访问 /devport、/dev/mem、/dev/kmem 及原始块设备 |
CAP_SYS_RESOURCE | 忽略资源限制 |
CAP_SYS_TIME | 允许改变系统时钟 |
CAP_SYS_TTY_CONFIG | 允许配置 TTY 设备 |
CAP_SYSLOG | 允许使用 syslog() 系统调用 |
CAP_WAKE_ALARM | 允许触发一些能唤醒系统的东西(比如 CLOCK_BOOTTIME_ALARM 计时器) |
5.2使用
5.2.1案例1
需求:添加指定权限。容器默认没有挂载文件系统能力,添加SYS_ADMIN权能增加这个功能。
[root@k8s-master1 flask-demo]# cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: cap-pod
spec:
containers:
- image: busybox
name: test
command:
- sleep
- 24h
securityContext:
capabilities:
add: ["SYS_ADMIN"] ##添加挂载权限。
5.2.2案例2
需求:只读容器文件系统。只读挂载容器文件系统,防止恶意二进制文件创建。
spec:
containers:
- image: test:latest
name: flask-demo
securityContext:
runAsUser: 1000
readOnlyRootFilesystem: true ##添加此行
二.OPA Gatekeeper
1.OPA简介
OPA(Open Policy Agent):是一个开源的、通用策略引擎,可以将策略编写为代码。提供一个种高级声明性语言-Rego来编写策略,并把决策这一步骤从复杂的业务逻辑中解耦出来。是PSP的替代方案,属于外部准入控制器。
作用:
拒绝不符合条件的YAML部署。
允许使用哪些仓库中的镜像。
允许在哪个时间段访问系统。
所有 Pod 必须有资源限制
2.OPA Gatekeeper简介
Gatekeeper 是基于 OPA的一个 Kubernetes 策略解决方案,可替代PSP或者部分RBAC功能。因为OPA与K8s对接偏向于底层,不好使用,所以社区就基于OPA引擎开发了OPA Gatekeeper的解决方案,方便使用。
当在集群中部署了Gatekeeper组件,APIServer所有的创建、更新或者删除操作都会触发Gatekeeper来处理,如果不满足策略则拒绝。
3.安装安装Gatekeeperd
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/master/deploy/gatekeeper.yaml
Gatekeeperd的资源对象
Template模板:在后面我们需要自定义限制策略,策略是怎么实现的就需要我们先写一个实现逻辑,这里就把这个实现逻辑称之为模板,使用rego语言。
Contsraint约束:自定义限制策略,当我们创建任何一个资源时可以先通过这个约束来进行过滤处理,违反约束就代表能限制你创建自动动作,不让你创建这个资源。所以约束就是负责K8s资源对象的过滤或者为Template模板提供输入参数。
4.实现思路
先使用rego语言编写Template模板,在这个模板里自定义实现逻辑,就相当于写一个脚本逻辑。
自定义Constraint约束,约束格式类似rbac授权那种,要限制什么资源,这个资源在什么组,限制某个命名空间的资源。
当我们创建一个资源时,比如创建deployment时,若是触发了约束,则就会带入到模板里,看是否能违反约束,若是违反约束就相当于出发了开关,则禁止创建deployment。
5.案例1
需求:
禁止容器启用特权。若用户创建的资源里有开启特权容器参数,则限制用户不能创建资源。
实现代码逻辑:
violation函数返回的是布尔值True或者False。我取deployment.yaml文件里的特权容器参数,若返回值为false,则不会继续往下执行函数,属于放行不做拦截;若返回值为true,则说明整个函数表达式已通过,代表违反约束需要拦截,就相当于触碰到开关起到拦截作用。
5.1 编写模板
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: hello
spec:
crd:
spec:
names:
kind: hello
targets:
- target: admission.k8s.gatekeeper.sh
rego: | ##自定义匹配策略,使用rego语言编写。
package admission ##传入一个依赖包,唯一的,类似命名空间。
violation[{"msg": msg}] { ##定义一个key-value变量。
containers = input.review.object.spec.template.spec.containers ##获取自定义对象的pod.yaml里对应的字段。
c_name := containers[0].name ##获取第一个容器名称并赋予变量名为c_name,0代表第一个。
containers[0].securityContext.privileged ##获取第一个容器的securityContext.privileged值。这里是判断返回是否为true,返回true说明违反约束
msg := sprintf("提示:'%v'容器禁止启用特权!",[c_name]) ##打印结果
}
5.2编写约束
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: hello
metadata:
name: hello
spec:
match:
kinds:
- apiGroups: ["apps"]
kinds:
- "Deployment"
- "DaemonSet"
- "StatefulSet"
5.3创建测试
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
securityContext:
privileged: true
root@master01:~# kubectl apply -f nginx.yaml
Error from server (Forbidden): error when creating "nginx.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [hello] 提示:'nginx'容器禁止启用特权!
6.案例2
需求:
禁止创建service为NodePort类型的资源。若用户创建的service资源里有NodePort参数,则限制用户不能创建service资源。
6.1 编写模板
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: hello
spec:
crd:
spec:
names:
kind: hello
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package kubernetes.admission
violation[{"msg": msg}] {
input.review.kind.kind = "Service"
input.review.operation = "CREATE"
input.review.object.spec.type = "NodePort"
msg := "不允许创建NodePort类型的service!"
}
6.2编写约束
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: hello
metadata:
name: hello
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Service"]
namespaces:
- "default"
6.3创建测试
apiVersion: v1
kind: Service
metadata:
name: nginx
spec:
type: NodePort
selector:
app: nginx
ports:
- port: 80
targetPort: 80
nodePort: 33333
root@master01:~# kubectl apply -f nginx.yaml
Error from server (Forbidden): error when creating "nginx.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [hello] 不允许创建NodePort类型的service!
三.gvisor
1.简介
容器的应用程序可以直接访问Linux内核的系统调用,容器在安全隔离上还是比较弱,虽然内核在不断地增强自身的安全特性,但由于内核自身代码极端复杂,CVE 漏洞层出不穷。所以要想减少这方面安全风险,就是做好安全隔离,阻断容器内程序对物理机内核的依赖。
Google开源的一种gVisor容器沙箱技术就是采用这种思路,gVisor隔离容器内应用和内核之间访问,提供了大部分Linux内核的系统调用,巧妙的将容器内进程的系统调用转化为对gVisor的访问。
gVisor兼容OCI,与Docker和K8s无缝集成,很方面使用。
2.gvisor架构
Runsc 是一种 Runtime 引擎,负责容器的创建与销毁。
Sentry 负责容器内程序的系统调用处理。
Gofer 负责文件系统的操作代理,IO请求都会由它转接到Host上。
3.k8s+containerd集成gvisor
3.1下载gvisor二进制文件
set -e
ARCH=$(uname -m)
URL=https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH}
wget ${URL}/runsc ${URL}/runsc.sha512 \
${URL}/containerd-shim-runsc-v1 ${URL}/containerd-shim-runsc-v1.sha512
sha512sum -c runsc.sha512 \
-c containerd-shim-runsc-v1.sha512
rm -f *.sha512
chmod a+rx runsc containerd-shim-runsc-v1
sudo mv runsc containerd-shim-runsc-v1 /usr/local/bin
3.2修改containerd
root@master01:~# vim /etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
--------------------------------------------------------------------------
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]
runtime_type = "io.containerd.runsc.v1"
---------------------------------------------------------------------------
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
root@master01:~# systemctl restart containerd
3.3创建runtimeclass策略
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: gvisor
handler: runsc
3.4创建pod指定runtimeclass
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
nodeName: master01
runtimeClassName: gvisor
containers:
- name: nginx
image: nginx
3.5创建查看
root@nginx:/# uname -a
Linux nginx 4.4.0 #1 SMP Sun Jan 10 15:06:54 PST 2016 x86_64 GNU/Linux
root@nginx:/# uname -r
4.4.0
root@nginx:/# dmesg
[ 0.000000] Starting gVisor...
[ 0.468654] Creating process schedule...
[ 0.724332] Digging up root...
[ 1.008464] Synthesizing system calls...
[ 1.437898] Daemonizing children...
[ 1.644510] Checking naughty and nice process list...
[ 2.071129] Searching for socket adapter...
[ 2.374845] Conjuring /dev/null black hole...
[ 2.792060] Committing treasure map to memory...
[ 3.253193] Accelerating teletypewriter to 9600 baud...
[ 3.532026] Creating bureaucratic processes...
[ 3.939059] Setting up VFS...
[ 4.127618] Setting up FUSE...
[ 4.476305] Ready!
评论区