我们都遇到过这种情况:你只是想尝试一段命令行,但安装进程却如同抵押贷款申请那般繁琐。如果不是强制要求完成这么多步骤,你的开发环境会被永远不会再使用的库弄乱。自然, Docker 来了以后,你惊异地发现尝试一个新工具变得更容易了。但它是不是太容易了?也许是时候问问自己:在你寻求一个简单命令行工具的时候,是不是偶然间就给了 Docker 太多的系统权限?本文系国内 ITOM 管理平台 OneAPM 工程师编译整理:

在用了 Docker 容器一段时间之后,我开始在所有的容器工程里运行。从那时起我激动不已。虽然不是所有的都和正常运行的 Docker 程序不一样,但这些容器都非常短暂。对我来说最大的收获是使用工具的时候不必再去搭建环境了。 Docker 就是我需要的全部。我会用一个快速案例告诉你这多么简单。在下面的 Dockerfile ,我从一个很小的 Linux 发行版开始,配置一些必要环境,然后安装 xml2json 工具。我的系统上不需要有 Node.js ,而现在只要安装了 Docker ,工具就能移植到其他系统中。作为额外的奖励, clean-up 操作是一个单元。删除 Docker 容器,你会知道你摆脱工具的每一个最后痕迹。

FROM alpine:latest
MAINTAINER Matthew Close <matthew.close@ctl.io>
# bash alias
# alias dxml2json='docker run -i --rm mclose/xml2json'
# usage example
# somefile.xml | dxml2json > somefile.json

RUN apk --update add nodejs && rm /var/cache/apk/*
RUN npm install -g xml2json-command
ENTRYPOINT ["/usr/bin/xml2json"]

然而,当我试图去熄灭我对工具更简易安装的渴望时,我也通过这种方式运行了几个容器:

docker run -v /var/run/docker.sock:/var/run/docker.sock  
或 -v /var/run:/var/run or -v /var:/var 

要共享容器的存储空间,运行下面命令:

/var/run/docker.sock

我当然会想知道: /var/run/docker.sock 命令是什么意思? 和其他容器共享存储空间意味着什么? 它总是安全的吗?

下面是我学到的。 /var/run/docker.sock 是 Unix 域套接字。套接字在你最喜欢的 Linux 发行版中使用,允许不同进程相互通信。和 Unix 中的所有东西一样, 套接字也是文件。在 Docker 中,/var/run/docker.sock 是与 Docker 主进程进行通信的方式。因为它是一个文件,我们可以和其它容器共享。

那么使用容器时共享套接字你会获得什么?相当多的事实证明。当你启动 Docker 并共享套接字的时候,你给了容器操控 Docker 主机的权限。你的容器现在可以启动或终止其它容器,在 Docker 主机拖入或创建镜像,甚至写入到主机文件系统。共享 Docker 套接字也让使用一些真正伟大的工具成为可能。这里有一个又一个和全部的工具宝库。除了标准工具,共享 Docker 套接字有助于 Docker 应用的持续集成。试想一个 Jenkins Docker 容器控制你的 Docker 发展管道。在某些情况下允许容器获取套接字是非常有益的。

但风险是什么?可能是 Docker 主机系统的安全性。这不是新发现,但我刚才描述的可能造成威胁的那种力量绝对值得注意。

例如,我要在 Docker 容器内安装一个 Docker 。我不想运行 Docker 进程,只想能够使用命令行 Docker。我可以用一个命令做到:

docker run -it --rm -v  /var/run/docker.sock:/var/run/docker.sock -v $(which docker):/bin/docker ubuntu:latest /bin/bash 

但我现在要使用一个 Docker 文件。更多关于为什么我后来做了这个选择,这就是 Docker 文件。

FROM ubuntu:vivid

RUN apt-get update && \
    apt-get install -y apt-transport-https

RUN apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys        58118E89F3A912897C070ADBF76221572C52609D && \
echo "deb https://apt.dockerproject.org/repo ubuntu-vivid main" > /etc/apt/sources.list.d/docker.list && \
apt-get update && \
apt-get install -y docker-engine=1.8.3-0~vivid \
    socat && \
apt-get clean && \
rm -rf /var/lib/apt-lists/*

CMD ["/bin/bash"]

构建容器后,运行它并快速浏览:

docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock  mclose/docker-1.8.3

注意当在容器里运行 docker ps 命令的时候,能够看到容器外 Docker 主机上运行着什么。

root@fa6e2e60fd1a:/# docker ps
CONTAINER ID        IMAGE                 COMMAND                 CREATED             STATUS              PORTS               NAMES
fa6e2e60fd1a        mclose/docker-1.8.3   "/bin/bash"         17     seconds ago      Up 16 seconds                           adoring_bell
root@fa6e2e60fd1a:/# ls /
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc        root  run  sbin  srv  sys  tmp  usr  var
root@fa6e2e60fd1a:/#

下面就有意思了。让我们在运行中的容器里用命令行开一个新容器:

运行 Docker 容器时的安全风险:别丢了你的套接字 技术分享

所以,这是非常有意思的。我已经开始在另一个容器里共享 Docker 主机的根文件系统。来看看为什么知道谁可以谁不可以在你的系统上运行 Docker 非常重要?我可以在 Docker 主机上创建一个 SUID root shell 。然后我可以在 Docker 主机上以 root 权限运行而不会留下任何活动日志。

可能有些人会想,“但是我在操作系统上运行。Docker机器已经把我完全隔离了 。”再想想,看看上面列出的第一个目录。意识到什么了吗?是的: /Users 。你的操作系统的主目录在的地方。请记住每一个 Docker 机器都会挂载 /Users 目录。所以这个容器不仅能在 Docker 主机上执行读写操作,它也能在你操作系统的主目录执行读写操作。

你大可不必现在就开始恐慌。总体上,使用 Docker 的回报还是很大的,你只需要采取一些预防措施。

从查看哪些用户有能力运行 Docker 命令开始。你应该考虑给他们全部的系统管理员权限。为什么?因为,正如上面的例子,能运行 Docker 的用户能绕过诸如 sudo 之类的访问控制工具。

当涉及到用 /var/run/docker.sock 命令运行容器的时候,要警惕容器中的 Dockerfile 。在 github.com 上你可能从某人的 repo 操作里获取 Dockerfile 。一些你可以信任的 GitHub 项目的标志:开发的积极性、好的文档还有星星数量。

接下来看看 Dockerfile 。列出基础系统的列表, FROM 行是否可信?你会在自己的系统上安装 Dockerfile 运行路线中的全部应用吗?如果看到像上面我的 Dockerfile , Docker被安装在哪里,要知道为什么。(这就是为什么我用了上面的 Dockerfile 而不是单个的命令。)你全面理解 Dockerfile 中的每一行吗?如果没有,看看命令参考。看看它们是个好主意,甚至能得到几个安装的辅助 shell脚本。确保你了解正在传递到 Docker 的运行命令行参数。我这里已经列出了所有的 -v 操作,但你应该知道当你运行一个容器时的其它操作。如果看到不理解的,最好从官方文档哪里得到更好的理解。

需要明确的是,我不是建议你回头去看项目源码。没人有那样的时间。在审查 Dockerfile 后,你必须信任你将要运行的容器。

当最终运行它的时候,如果容器使用一个共享的 Docker 套接字,容器应该与数据库和管理系统一样被视为一个高价值的资产。这意味着使用网络访问限制,如防火墙和 VPN ,如果容器暴露超出 Docker 网络。

将来不久的 Docker 发行版可能会缓解与容器共享操作  /var/run/docker.sock 的一些风险。在 Docker 1.9.0 测试版本里使用命名空间作为一个非常有前途的解决方案。

Docker 是一个非常灵活的工具,但你要小心如何来使用它。如果没有的话,你可能会放弃比你想象的还多的访问。然而,在这一点上,当你运行容器的时候我希望你对 /var/run/docker.sock 有一个更好的理解。如果你查看和理解了一个容器的 Dockerfile ,以及它如何运行,你会更好的了解所涉及到的安全风险,并采取适当的行动。

编译自 Matthew 的Tutorial- Understanding the Security Risks of Running Docker Containers: Don't Lose Your Sock(et)s

OneAPM 是中国基础软件领域的新兴领军企业。致力于帮助企业用户提供全栈式的性能管理以及 IT 运维管理服务,通过一个探针就能够完成日志分析、安全防护、 APM 基础组件监控、集成报警以及大数据分析等功能。想阅读更多优秀文章,请访问 OneAPM 官方技术博客 OneAPM 官方技术博客