当我们获取了一个容器的 shell,或许 cat /proc/1/cgroup 是我们首要要执行的。
毕竟从内核角度看容器技术的关键就是 CGroup 和 Namespace,或许应该再加一个 Capabilities。从 CGroup 信息中,不仅可以判断我们是否在容器内,也能很方便判断出当前的容器是否在 Kubernetes 的编排环境中。
没使用 Kubernetes 的 docker 容器,其 cgroup 信息长这样:
12:hugetlb:/docker/9df9278580c5fc365cb5b5ee9430acc846cf6e3207df1b02b9e35dec85e86c36
而 Kubernetes 默认的,长这样:
12:hugetlb:/kubepods/burstable/pod45226403-64fe-428d-a419-1cc1863c9148/e8fb379159f2836dbf990915511a398a0c6f7be1203e60135f1cbdc31b97c197
同时,这里的 CGroup 信息也是宿主机内当前容器所对应的 CGroup 路径,在后续的多个逃逸场景中获取 CGroup 的路径是非常重要的。
同类判断当前 shell 环境是否是容器,并采集容器内信息的还有很多,举个不完全的例子:
ps aux
ls -l .dockerenv
capsh --print
env | grep KUBE
ls -l /run/secrets/Kubernetes.io/
mount
df -h
cat /etc/resolv.conf
cat /etc/mtab
cat /proc/self/status
cat /proc/self/mounts
cat /proc/net/unix
cat /proc/1/mountinfo
其中 capsh –print 获取到信息是十分重要的,可以打印出当前容器里已有的 Capabilities 权限;历史上,我们曾经为了使用 strace 分析业务进程,而先设法进行容器逃逸忘记看当前容器的 Capabilities 其实已经拥有了 ptrace 权限,绕了一个大弯子。
但是,容器的 SHELL 环境里经常遇到无法安装新工具,且大部分常用工具都在镜像里被精简或阉割了。这时理解工具背后的原理并根据原理达到相同的效果就很重要。
以 capsh 为例,并非所有的容器镜像里都可以执行 capsh,这时如果想要获取当前容器的 Capabilities 权限信息,可以先 cat /proc/1/status 获取到 Capabilities hex 记录之后,再使用 capsh –decode 解码出 Capabilities 的可读字符串即可。
其他如 mount, lsof 等命令也类似。
另外一个比较常见就是 kubectl 命令的功能复现,很多情况下我们虽然获得了可以访问 APIServer 的网络权限和证书(又或者不需要证书)拥有了控制集群资源的权限,却无法下载或安装一个 kubectl 程序便捷的和 APIServer 通信,此时我们可以配置 kubectl 的 logging 登机,记录本地 kubectl 和测试 APIServer 的请求详情,并将相同的请求包发送给目标的 APIServer 以实现相同的效果。
kubectl create -f cronjob.yaml -v=8
如果需要更详细的信息,也可以提高 logging level, 例如 kubectl -v=10 等,其他 Kubernetes 组件也能达到相同的目的。