1. 简介

Linux内核会根据需要从磁盘自动加载内核模块
与Kubernetes特别相关的是,即使没有特权的进程也可以通过创建适当类型的套接字来加载某些与网络协议相关的内核模块。
这可能使攻击者利用管理员认为未使用的内核模块中的安全漏洞。

2. 最小权限原则(POLP)

POLP principle of least privilege 够用就行,不用多给权限

提高安全性
降低被攻击面
增强系统稳定性

权限审核—确保所有用户只有能完成本职工作的权限
设置所有用户最低权限—根据需要提权
权限分离—管理员和普通用户

3. apparmor

类似于centos、rhel系统里的selinux,主要作用是设置哪些进程能够访问哪些文件,不能访问哪些文件

3.1 安装

1
apt-get install apparmor-profiles apparmor-utils

规则目录

1
cd /etc/apparmor.d/

3.2 编写规则

对于apparmor来说,规则文件有几种类型:

3.2.1 标准配置文件

1
2
3
4
#include <tunables/global>
/usr/sbin/nginx flags=(模式标志 相对标志 附加标志) {
...规则...
}

第一行是#include表示加载其他的规则文件,<>里的内容指的是/etc/apparmor.d里的子目录里的文件,比如这里trunables/global,指的是加载/etc/apparmor.d/tunables/global这个文件,除了#include前面的#必须要加之外,其他的#开始的都是注释。
在下面的打括号里,也可以通过#include加载其他的文件

这里还有一个flags=,在编写apparmor规则文件的时候,需要使用到标志,含有三种类型的标志:模式标志,相对标志,附加标志。每种标志在flags里的顺序不重要,在flags里写的是标志列表:

  • 模式标志的值有
    enforce – 不能写入在flags里,不写就是enforce
    complain – 允许
  • 相对标志的值有
    chroot_relative
    namespace_relative
  • 附加标志的值有
    attach_disconnected
    no_attach_disconnected
    上面两个值互斥
    chroot_attach
    chroot_no_attach
    上面两个值互斥

如果在flags里没有指定标志的话,默认是enforce,namespace_relative,no_attach_disconnected,chroot_no_attach

3.2.2 未关联的配置文件

用于手动指定到需要限制的程序

1
2
3
profile profile1 {
#include <abstractions/base>
}

3.3 生成配置

1
aa-autodep nginx

生成的是标准的配置文件

1
2
3
4
5
6
7
8
9
10
11
root@node-0:/etc/apparmor.d# cat usr.sbin.nginx 
# Last Modified: Sun Sep 25 19:18:44 2022
#include <tunables/global>

/usr/sbin/nginx flags=(complain) {
#include <abstractions/base>

/lib/x86_64-linux-gnu/ld-*.so mr,
/usr/sbin/nginx mr,

}

了解规则文件的结构
上面/usr/sbin/nginx指的是下面的规则是用于限制此程序的。complain模式

3.3.1 设置enforce模式

1
2
aa-enforce nginx
aa-logprof
1
2
3
4
5
6
7
8
9
10
root@node-0:/etc/apparmor.d# cat usr.sbin.nginx 
# Last Modified: Sun Sep 25 19:36:40 2022
#include <tunables/global>

/usr/sbin/nginx {
#include <abstractions/base>
#include <abstractions/lxc/container-base>

}

在规则文件里没有了complain字样

3.3.2 拒绝访问

1
2
3
4
5
6
7
8
9
root@node-0:/etc/apparmor.d# cat usr.sbin.nginx
# Last Modified: Sun Sep 25 19:36:40 2022
#include <tunables/global>

/usr/sbin/nginx {
#include <abstractions/base>
#include <abstractions/lxc/container-base>
deny /data/www/deny/* r,
}

这里增加了一句deny /data/www/deny/* r, 意思是拒绝nginx进程读目录/data/www/deny/里的东西
这里的语法是
deny /path/file 权限1 权限2 权限3… 这里是拒绝程序对/path/file使用权限1权限2权限3…
allow path/file 权限1 权限2 权限3… 这里是允许程序对/path/file使用权限1权限2权限3…
如果没有写allow的话,默认就是allow,所以可以写成/path/file 权限1 权限2 权限3…

每行最后面要以逗号结束。常见的权限有:
r 读
w 写 和a互斥
a 追加写,和w互斥
x 可执行,但是单写x的话,只能在deny的后面写,在allow里不能独立用x,必须要ix,ux等组合使用
ix 继承执行
m 允许PROT_EXEC与mmap(2)调用
k 锁定,如果应用程序有权限访问文件,apparmor便允许锁定文件

3.3.3 重读配置

1
apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx

3.4 k8s环境里使用

在k8s环境里,使用apparmor只是对特定的pod做限制,并非限制worker节点上的某个进程,所以创建未关联的配置文件。

3.4.1 先查看是否存在名字叫profile1的配置

1
apparmor_status |grep profile1

3.4.2 创建一个配置

3.4.2.1 允许特定文件
1
2
3
4
5
6
7
8
root@node-0:/etc/apparmor.d# cat aa.conf 
#include <tunables/global>
profile profile1 {
#include <abstractions/base>
#include <abstractions/bash>
file,
allow /usr/bin/sleep rwix,
}

这里是定义给未关联的配置文件,名字叫做 profile1,此文件里只加载了abstractions/base,基本上是不允许任何程序运行,然后添加此规则。

3.4.2.2 拒绝目录
1
2
3
4
5
6
#include <tunables/global>
profile profile1 flags=(attach_disconnected) {
#include <abstractions/base>
file,
deny /tmp/ r,
}

这种写法是拒绝对/tmp 这个目录本身进行读取访问,但不影响读取此目录里的内容。注意这里/tmp/后面要带"/“,写作”/tmp/" 不能写成"/tmp"否则 deny 条目不生效。

这里可以看到是没法获取/tmp 目录里的内容,但是不影响读取访问/tmp 里子目录以及查看/tmp 里文件的内容的。

3.4.2.3 拒绝文件
1
2
3
4
5
6
#include <tunables/global>
profile profile1 flags=(attach_disconnected) {
#include <abstractions/base>
file,
deny /tmp/* r,
}

这里写成 deny /tmp/* r, 意思是拒绝读取/tmp 目录里的文件,但不影响访问/tmp 目录本身,也不影响访问/tmp 里子目录里的文件。

这里可以正常访问/tmp 里的内容,也能读取目录/tmp/xx 里的数据,能够查看/tmp/xx/xx.txt 的内容,但是没法访问/tmp/aa.txt。

3.4.2.4 递归拒绝
1
2
3
4
5
6
#include <tunables/global>
profile profile1 flags=(attach_disconnected) {
#include <abstractions/base>
file,
deny /tmp/** r,
}

这里写成 deny /tmp/** r, 意思是拒绝读取/tmp 目录里的文件以及/tmp 里子目录里的文件,但不影响访问/tmp 目录本身。

这里可以正常访问/tmp 里的内容,但是已经读取不到目录/tmp/xx 里的数据,也不能够查看/tmp/xx/xx.txt 的内容,也没法访问/tmp/aa.txt

3.4.3 安装配置

1
apparmor_parser -q /etc/apparmor.d/aa.conf 

3.4.4 创建pod

添加annotations应用配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
root@vms40:~/demo3# cat pod3.yaml 
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod3
name: pod3
annotations:
container.apparmor.security.beta.kubernetes.io/pod3: localhost/profile1
spec:
nodeName: vms41.rhce.cc
containers:
- image: hub.c.163.com/library/centos
imagePullPolicy: IfNotPresent
name: pod3
resources: {}
command: ["/bin/sh","-c","sleep 1d"]
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}

这里在 pod3 里添加了
annotations:
  container.apparmor.security.beta.kubernetes.io/pod3: localhost/profile1
这里的 pod1 是容器名,必须要和下面 containers 里定义的容器的名字保持一致,最后的 localhost/profile1 指的是此 pod 所在机器(localhost)上的 profile1 规则

4. seccomp

seccomp(全称secure computing mode 安全计算模式)
是linux kernel从2.6.23版本开始所支持的一种安全机制。

在Linux系统里,大量的系统调用(systemcall)直接暴露给用户态程序。但是,并不是所有的系统调用都被需要,而且不安全的代码滥用系统调用会对系统造成安全威胁。通过seccomp,我们限制程序使用某些系统调用,这样可以减少系统的暴露面,同时是程序进入一种“安全”的状态。

4.1 系统调用

简单执行cat查看系统调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
root@vms40:~/demo3# strace -fqc cat /etc/issue
Ubuntu 18.04.5 LTS \n \l

% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
0.00 0.000000 0 3 read
0.00 0.000000 0 1 write
0.00 0.000000 0 6 close
0.00 0.000000 0 5 fstat
0.00 0.000000 0 7 mmap
0.00 0.000000 0 4 mprotect
0.00 0.000000 0 2 munmap
0.00 0.000000 0 3 brk
0.00 0.000000 0 3 3 access
0.00 0.000000 0 1 execve
0.00 0.000000 0 1 arch_prctl
0.00 0.000000 0 1 fadvise64
0.00 0.000000 0 4 openat
------ ----------- ----------- --------- --------- ----------------
100.00 0.000000 41 3 total

4.2 限制系统调用

SCMP_ACT_KILL:当进程进行对应的系统调用时,内核发出SIGSYS信号终止该进程,该进程不会接受到这个信号
SCMP_ACT_TRAP:当进程进行对应的系统调用时,该进程会接收到SIGSYS信号,并改变自身行为
SCMP_ACT_ERRNO:当进程进行对应的系统调用时,系统调用失败,进程会接收到errno返回值
SCMP_ACT_TRACE:当进程进行对应的系统调用时,进程会被跟踪
SCMP_ACT_ALLOW:允许进程进行对应的系统调用行为
SCMP_ACT_LOG:记录所有信息

查看内容是否支持
grep CONFIG_SECCOMP= /boot/config-$(uname -r)

4.3 编写规则

https://docs.docker.com/engine/security/seccomp/
docker容器中默认禁用了44个系统调用

默认的配置
https://raw.githubusercontent.com/moby/moby/master/profiles/seccomp/default.json

4.3.1 允许所有调用

1
2
3
4
cat aa1.json
{
"defaultAction": "SCMP_ACT_ALLOW"
}

4.3.2 拒绝所有调用

1
2
3
4
cat bb.json
{
"defaultAction": "SCMP_ACT_ERRNO"
}

4.3.3 自定义调用规则

1
2
3
4
docker run --rm -it --security-opt seccomp=./aa1.json hello-word

等同于unconfined(允许所有)
docker run -it --rm --name=c1 --security-opt seccomp=unconfined hello-world

4.3.4 sysdig

使用sysdig监测容器,看看使用了哪些系统调用

4.3.4.1 安装sysdig
1
2
3
4
5
6
curl -s https://s3.amazonaws.com/download.draios.com/DRAIOS-GPG-KEY.public | sudo apt-key add -
sudo curl -s -o /etc/apt/sources.list.d/draios.list
https://s3.amazonaws.com/download.draios.com/stable/deb/draios.list
sudo apt-get update
sudo apt-get -y install linux-headers-$(uname -r)
sudo apt-get -y install sysd

以容器方式运行
docker run -i -dt --name sysdig --restart=always --privileged -v /var/run/docker.sock:/host/var/run/docker.sock -v
/dev:/host/dev -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v
/usr:/host/usr:ro sysdig/sysdig
alias xxx='docker exec -it sysdig sysdi

4.3.4.2 sysdig使用
sysdig -p "*%evt.time,%proc.name,%evt.type" container.id=5c9431eb8723

evt.num: 递增的事件号
evt.time: 事件发生的时间
evt.cpu: 事件被捕获时所在的 CPU,也就是系统调用是在哪个 CPU 执行的。比较上面的例子中,值 0 代表机器的第一个 CPU
proc.name: 生成事件的进程名字,也就是哪个进程在运行
thread.tid: 线程的 id,如果是单线程的程序,这也是进程的 pid
evt.dir: 事件的方向(direction),> 代表进入事件,< 代表退出事件
evt.type: 事件的名称,比如 open、stat等,一般是系统调用
evt.args: 事件的参数。如果是系统调用,这些对应着系统调用的参数

4.3.5 k8s中使用seccomp

官网文档:https://kubernetes.io/docs/tutorials/security/seccomp/

4.3.5.1 创建规则

存放规则的目录

1
mkdir -p /var/lib/kubelet/seccomp/

创建规则

1
2
3
4
root@vms41:/var/lib/kubelet/seccomp# cat aa.json 
{
"defaultAction": "SCMP_ACT_LOG"
}
4.3.5.2 创建pod
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Pod
metadata:
name: audit-pod
labels:
app: audit-pod
spec:
securityContext:
seccompProfile:
type: Localhost
localhostProfile: aa.json
containers:
- name: test-container
image: hashicorp/http-echo:0.2.3
args:
- "-text=just made some syscalls!"
securityContext:
allowPrivilegeEscalation: false