1.Kubernetes 架构
典型的k8s中包含两类节点:
- Master-Node:主要负责对集群进行调度,管理容器的创建、销毁等。
- Worker-Node:主要负责对运行容器化应用
一套典型的高可用k8s集群架构如下图所示:
由于已经安装好了生产环境,因此对照生产环境中的docker镜像对比学习系统组件。
1.1 Master节点组件
使用kubeadm安装集群时,master节点中的docker环境下会下载如上图所示对应的docker-image。
可以看到,所下载的docker image与架构图中各部分基本上一一对应(多出来了flannel、pause、coredns),各组成部分功能角色如下:
- kube-apiserver:通过调用kube-apiserver提供的api接口,用户可以对集群中的node、deployments、pods、service进行管理。我们使用的kubectl工具就是通过调用api对集群进行的管理。apiserver是唯一一个会和etcd集群进行交互的组件,它会在etcd集群中存取数据。
- kube-scheduler:对pods的部署调度就是这个组件负责的,该组件会对Worker-Node的资源进行统计,然后决策应该将pod部署在哪一台Worker-Node中。
- kube-controller-manager:这个里面包含了许多控制程序,这些控制程序会对集群中部署内容的状态进行监控,例如当某个deployment的image变化时,该组件负责对pods进行更新。
- kube-proxy:为pods和外部用户创建代理网络,使得外部用户能够访问到pods中提供的服务。
- flannel:是一种网络规划服务,简单来说,通过该服务能够让集群中的各个docker容器处于同一个虚拟内网之中。
- pause:pause容器主要用于给pod中的各个业务容器共享网络和挂载卷,例如一个php容器和一个mysql容器,可以同时将自己的网络和一个pause容器进行共享。此时,若mysql通过localhost的3306端口对外提供服务,则php容器可以通过localhost:3306访问到该mysql提供的服务。
- coredns:集群内部的dns服务器。
- etcd:键值对数据库,官方提供了两种master集群的部署方式,etcd既可以和master节点安装在一起,也可以单独做成一个集群。
1.2 Worker节点组件
Worker节点上运行的组件如下:
kube-proxy:pods内部的容器和外部用户通信就是靠这个proxy代理
flannel:和master节点里的用途一样。
pause:和master节点里的用途一样。
kubelet:通过kubadm安装的worker-node中这个服务是直接本机运行的,主要负责跟master节点汇报、获取工作内容。
2.容器的基本原理
容器技术需要解决几个主要的技术问题,即:
- 限制每一个容器能够使用资源的数量,不能让单一容器大量消耗cpu、内存等资源
- 容器间隔离,不能让一个容器无限制的访问其余容器的网络或进程内存
- 文件系统隔离,使用某个镜像建立一个容器,对该容器的文件系统中的文件修改,不能影响其他使用该镜像创建的容器
- 特权分配问题,容器技术本质上是对进程进行权限、资源访问限制。而进程中有时会需要一定的特权保证程序正常运行。
以下针对上述技术问题分别学习docker中的解决方案。
2.1 cgroup
2.1.1 cgroup简介
cgroup全称control group,是linux操作系统提供的一个特性,用于解决第一个问题即资源限制问题。有点类似于组策略对于组内成员的管理,cgroup也是以分组的方式对进程能够访问的资源进行集中管控。在容器技术中,主要用于限制容器能够使用的资源数量。例如,我们在执行创建docker容器的时候,就可以指定参数,要求容器最多只能使用多少cpu或者内存。
比如我们可以限制某个docker容器最多只能使用128M内存,
root@worker-node:~# docker run -d --memory 128MB testapp:v1.0 WARNING: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap. f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1 root@worker-node:~# docker exec f5eabd cat /proc/1/cgroup 12:devices:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope 11:freezer:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope 10:pids:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope 9:memory:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope 8:blkio:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope 7:cpuset:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope 6:perf_event:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope 5:rdma:/ 4:net_cls,net_prio:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope 3:cpu,cpuacct:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope 2:hugetlb:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope 1:name=systemd:/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope 0::/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope root@worker-node:~# cat /sys/fs/cgroup/memory/system.slice/docker-f5eabd1e9dc8b10a529564048e1c9a53444cbfb47fff25cc81811c6beab38dc1.scope/memory.limit_in_bytes 134217728
当拿到某台主机shell时,可以先看/proc/1/cgroup
文件来确定是否处于容器环境中,一般的docker容器下的内容如下图所示,会出现明显的docker字样:
而身处k8s的pods中时,则会看到明显的kube字样,如下图所示,也会存在明显的kubepods字样:
cgroup V1共包含13个子系统, 13个子系统概述如下:
子系统 | 功能 |
---|---|
cpu | 主要用于限制进程的cpu使用率 |
cpuacct | 提供进程对于cpu使用情况的统计能力限制 |
cpuset | 可以帮助cgroup内的进程绑定一组cpu或者非统一内存访问节点 |
freezer | 可以挂起或者恢复cgroup内的进程 |
hugt1b | 限制组内进程使用大页内存 |
blkio | 用于限制组内进程对指定块存储设备的I/O使用速率 |
memory | 用于限制组内进程内存、内核内存、swap空间使用 |
perf_event | 允许组内进程执行perf(性能)监控 |
pids | 限制组内进程最大创建进程的数量 |
rdma | 限制组内进程是否允许使用RDMA(Remote Direct Memory Access) |
net_cls | 给由cgroup内进程创建的网络数据包添加classsid |
net_prio | 用于设置网络流量优先级 |
devices | 限制进程能够创建(mknod)或者读写devices |
2.2 Namespace
容器间隔离依靠操作系统所提供的namespace(命名空间)机制。linux命名空间包含了以下6种不同的命名空间:
- Mount namespace:功能类似于chroot。隔离mount操作,子进程中所有的mount操作都不会对父进程产生影响
- UTS(UNIX Time-sharing System) namespace:隔离主机名和网络信息服务
- IPC namespace:隔离进程间通信
- PID namespace:隔离PID
- Network namespace:隔离网络
- User namespace: 隔离用户和组
参考https://www.cnblogs.com/linhaifeng/p/6657119.html中的代码进行学习效果及佳!
2.3 rootfs
镜像的本质就是一个rootfs,简单来说就是docker容器的根目录。docker容器启动后会以镜像为基础创建一个只读的rootfs,然后挂载一个可写的层用于记录修改。通过这种方式,即可对于同一镜像产生的不同容器实现文件系统的隔离。
2.4 capabilities
capabilities是对linux操作系统中root用户所拥有特权的细化,早期使用suid对某个文件赋予执行时具备root用户全部权限,例如passwd修改用户密码的命令就是这样。但一次性赋予用户全部root特权较为危险,因此linux操作系统对root用户特权进行了拆分,也就是capabilities(kernel2.2之后才有的)。
可以使用capsh
命令查看或编辑capabilities,例如可以使用capsh --print查看当前特权列表:
容器中可能没有该命令,可以读取/proc/1/status
查看当前容器所具有的特权列表:
root@161f90c0aab4:/# cat /proc/1/status ...省略... CapInh: 0000003fffffffff CapPrm: 0000003fffffffff CapEff: 0000003fffffffff CapBnd: 0000003fffffffff CapAmb: 0000000000000000 ...省略...
上面命令执行结果包含了五个Capabilities集合,各集合含义如下:
名称 | 说明 |
---|---|
CapInh | Inheritable集合,可继承特权集合。 |
CapPrm | Permitted集合,允许申请的特权集合。该集合中没有的特权是不允许线程申请的 |
CapEff | Effective集合,有效特权结合,内核在检查的时候检查的就是这个集合,也是我们重点关注的集合。 |
CapBnd | Bounding集合,边界特权集合,如果某个特权不在该集合中,即使在Permitted集合里该线程也不能添加到Bounding集合 |
CapAmb | Ambient集合,linux4.3新增的一个集合。具体可以参考Linux-capabilities |
对于当前容器所拥有的权限,可以查看CapEff中的值,然后使用capsh --decode
来显示特权:
现有的特权详情如下:
特权名 | 说明 | 备注 |
---|---|---|
CAP_AUDIT_CONTROL | 进行开启和关闭内核审计,是否允许修改审计规则 | |
CAP_AUDIT_READ | 通过socket读取审计日志 | |
CAP_AUDIT_WRITE | 向审计日志写入记录 | 默认就有 |
CAP_BLOCK_SUSPEND | 阻止系统挂起 | |
CAP_BPF | 使用BPF包过滤器 | |
CAP_CHECKPOINT_RESTORE | 修改/proc/sys/kernel/ns_last_pid,能否使用set_tid,能否读取其他进程的/proc/[pid]/map_files | |
CAP_CHOWN | 修改文件的UIDs和GIDs | 默认就有 |
CAP_DAC_OVERRIDE | 绕过文件的读写执行权限检查、执行open_by_handle_at | 默认就有,配合CAP_DAC_READ_SEARCH可以向宿主机内写入文件进行逃逸 |
CAP_DAC_READ_SEARCH | 绕过文件读执行检查、执行open_by_handle_at、使用AT_EMPTY_PATH flag创建到文件的链接 | 可以读取宿主机文件 |
CAP_FOWNER | 忽略文件UID和进程UID必须相同的限制 | 默认就有 |
CAP_FSETID | 文件被修改时不清除set-user-ID 和 set-group-ID | 默认就有 |
CAP_IPC_LOCK | 锁定内存或使用大页 | |
CAP_IPC_OWNER | 绕过System V IPC object的权限检查 | |
CAP_KILL | 对不属于自己的进程发送Signal | 默认就有 |
CAP_LEASE | 修改文件锁的FL_LEASE标志 | |
CAP_LINUX_IMMUTABLE | 设置FS_APPEND_FL and FS_IMMUTABLE_FL标志 | |
CAP_MAC_ADMIN | 修改MAC配置 | |
CAP_MAC_OVERRIDE | 覆盖强制访问控制(Mandatory Access Control) | |
CAP_MKNOD | 允许使用mknod | 默认就有,容器逃逸时有时会使用该命令挂载宿主机的硬盘 |
CAP_NET_ADMIN | 网卡配置,ip修改,修改route等 | |
CAP_NET_BIND_SERVICE | 绑定服务在1024号以下端口 | 默认就有 |
CAP_NET_BROADCAST | Socket广播或混杂模式监听 | |
CAP_NET_RAW | 使用原始套接字 | 默认就有 |
CAP_PERFMON | 多种性能监控 | |
CAP_SETGID | 允许改变进程GID | 默认就有 |
CAP_SETFCAP | 为文件设置任意特权 | 默认就有 |
CAP_SETPCAP | 将当前集合全部转移到另一PID | 默认就有 |
CAP_SETUID | 改变进程UID | |
CAP_SYS_ADMIN | 可以挂载文件系统、设置磁盘配额等 | 可用于逃逸,在宿主机执行命令 |
CAP_SYS_BOOT | 可以重启系统 | |
CAP_SYS_CHROOT | 使用chroot | 默认就有 |
CAP_SYS_MODULE | 允许加载内核模块 | 可以通过加载内核模块逃逸 |
CAP_SYS_NICE | 设置进程优先级 | |
CAP_SYS_PACCT | 允许使用acct(切换进程统计开关) | |
CAP_SYS_PTRACE | 利用ptrace调试任意进程 | 配合与宿主机pid namespace共享,可注入进程逃逸 |
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 | 捕捉唤醒系统的消息 |
参考文献:
https://www.aquasec.com/cloud-native-academy/kubernetes-101/kubernetes-complete-guide/
http://www.dockone.io/article/9004
https://www.cnblogs.com/linhaifeng/p/6657119.html
https://man7.org/linux/man-pages/man7/cgroups.7.html
https://blog.csdn.net/alex_yangchuansheng/article/details/102796001
https://man7.org/linux/man-pages/man7/capabilities.7.html
https://book.hacktricks.xyz/linux-unix/privilege-escalation/linux-capabilities