作者:马辛·瓦西奥内克(Marcin Wasiucionek)
引言
在Kubernetes安全领域,一个常见的建议是让容器以非root用户身份运行。但是,在容器中以root身份运行,实际会带来哪些安全隐患呢?在Docker镜像和Kubernetes配置中,这一最佳实践常常被重点强调。在Kubernetes清单文件中,可以通过以下代码实现:
securityContext:runAsNonRoot: true # 确保runAsUser指定的用户ID不是0。如果用户ID设置为0,Pod将无法运行。runAsUser: 1000 # 将容器中运行的进程的用户ID设置为1000。
你可以只设置runAsUser
字段,而不包含runAsNonRoot
。然而,如果你指定了runAsNonRoot
,那么也必须定义runAsUser
。
容器以root身份运行为何危险⚠️
当容器以root身份运行时,会出现多种潜在的攻击途径。让我们通过实验来探究一下。
搭建实验环境🛠️
为了说明以root身份运行容器的风险,我们将使用两个相似的容器进行实际测试:
- 一个默认以root身份运行的
alpine:3.20.2
容器。 - 一个自定义的
alpine:3.20.2
容器,配置为以非root用户身份运行。
以下是用于创建非root容器的Dockerfile:
# 使用Alpine镜像作为基础镜像
FROM alpine:3.20.2# 添加一个名为'nonroot'的非root用户,用户ID为1000
RUN adduser -D -u 1000 nonroot# 将后续命令的用户设置为'nonroot'
USER nonroot
# 可选:设置默认命令
CMD ["sh"]
在本地环境中,我们使用Minikube 1.32.0和Kubernetes版本v1.28.3。通过以下命令构建镜像,并使其在Minikube集群中可用:
eval $(minikube docker-env)
docker build. -t nonroot:1.0.0
接下来,我们在Kubernetes中部署这些容器。在这个演示中,我使用hostPath
卷来测试权限,不过强烈建议不要在家庭实验室之外使用hostPath
(我会在另一篇文章中解释原因)!
root用户的Pod定义如下:
apiVersion: v1
kind: Pod
metadata:name: root
spec:containers:- name: alpineimage: alpine:3.20.2command: ["/bin/sh", "-c"]args: ["while true; do sleep 100; done"]volumeMounts:- mountPath: /hostname: hostvolumes:- name: hosthostPath:path: /etc/kubernetes/manifests # 挂载节点上的静态Pod目录type: Directory
非root用户的Pod定义如下:
apiVersion: v1
kind: Pod
metadata:name: nonroot
spec:containers:- name: alpineimage: nonroot:1.0.0command: ["/bin/sh", "-c"]args: ["while true; do sleep 100; done"]securityContext:runAsUser: 1000 # 确保容器以用户ID为1000的非root用户运行volumeMounts:- mountPath: /hostname: hostvolumes:- name: hosthostPath:path: /etc/kubernetes/manifests # 挂载节点上的静态Pod目录type: Directory
测试潜在攻击🧪
- 下载恶意软件🦠
一种常见的攻击方式是下载并执行恶意软件包。我通过尝试从dev.to获取一些数据来测试这一点。
由于容器中最初未安装curl
,我尝试安装它:
在root容器中安装成功,但在非root容器中安装失败。让我们尝试获取数据。
在root容器中操作正常,但在非root容器中被阻止。这表明以非root用户身份运行可以有效地缓解这种攻击。其他措施,如使用只读文件系统,可以进一步增强安全性,我将在未来的文章中介绍。
- 访问主机资源🔒
我将一个主机目录挂载到了Pod(再次强调 —— 请不要在家庭实验室之外这样做!)。通过这种访问权限,攻击者可以尝试访问包含静态Pod manifests 的目录,并尝试运行恶意Pod(尽管下载恶意镜像应该会被集群策略阻止)。这可以通过向静态清单目录(通常是Kubernetes节点上的/etc/kubernetes/manifests
)添加新的清单文件来实现。有了这种访问权限,攻击者可以尝试:- 通过部署拦截集群内网络流量的Pod来执行中间人攻击,并捕获敏感信息。
- 部署带有反向shell的后门Pod(你可以在https://www.revshells.com/ 找到示例),接受来自黑客机器的连接。
- 运行一个Pod,将包含敏感数据的卷中的数据传输到外部实体。
- 通过从ETCD存储中读取机密信息来扩大攻击范围,并进一步渗透基础设施。
- 使用你的资源运行加密货币挖掘程序以获取经济利益。
让我们使用以下清单文件部署一个加密货币挖掘程序的模拟:
apiVersion: v1
kind: Pod
metadata:name: crypto - miner
spec:containers:- name: miner - containerimage: busyboxcommand: ["/bin/sh", "-c", "while true; do echo 'Mining in progress...'; sleep 5; done"]
并从两个Pod中添加静态Pod文件:
如你所见,非root用户的Pod上的命令被拒绝。它在root用户的Pod上成功运行,并将加密货币挖掘程序的清单文件添加到了静态Pod目录中。集群中创建了这个Pod吗?
是的,它在集群中运行,并且在出现故障或重启时将被重新调度。
攻击者通过访问节点上的hostPath
还可以做的另一件事是读取主机上的/etc/passwd
文件。该文件不包含明文密码,但它让攻击者了解系统中存在的用户。结合其他一些数据源和/etc/shadow
,这些信息可能会让攻击者进一步利用系统。
- 权限提升🚫
你难道不能将非root用户切换为root用户,然后做同样的事情吗?让我们试试。
不,你不能。从非root用户切换到root用户的尝试失败了,这表明在没有sudo
权限的情况下,权限提升是不可行的。因此,如果非root用户不在sudoers
列表中,风险就会降低。
如何预防?🛡️
为了防止与以root身份运行容器相关的安全问题,请遵循以下最佳实践:
- 使用非root用户:始终在你的Docker容器中定义并使用非root用户。🧑💻
- 利用Kubernetes安全上下文:使用Kubernetes安全上下文(Security Context)指定容器执行的用户。🔐
结论✨
在Kubernetes中以非root用户身份运行容器,通过缓解常见的攻击途径,显著增强了安全性。希望本文能让你了解这一最佳实践的重要性。
参考文献
- Understanding the Docker USER instruction
- Kubernetes Security Context
- Alpine Docker images on Docker Hub
- Kubernetes Pod Security Standards
- https://medium.com/@marcin.wasiucionek/why-is-running-as-root-in-kubernetes-containers-dangerous-e5f1a116080e