提升Docker安全性
最后更新于:2022-04-01 21:48:37
## 1.Docker的核心原理
Docker容器的的本质是宿主机上的进程.通过namespace实现资源隔离,cgroups实现资源的限制,通过写时复制的机制完成高效的文件操作.docker实现的核心技术-namespace 和 cgroups,其实并不是什么新技术,准确的说namespace 和 cgroups是linux的相关技术.docker对这种技术进行了封装,提高可操作性。
## 2.容器安全的痛点
容器经常拿来和虚拟机进行对比,容器的好处就不多说了!主要说说:容器的缺点-隔离性低导致的安全性较低. **通常来说,容器相比虚拟机还是不够安全的.**.
#### 2.1为什么容器不够安全?
这就要从虚拟机和容器的底层实现机制来对比,
* 虚拟机在hypervisor的基础上构建虚拟操作系统,有自己的操作系统内核.**容器与宿主机是共用同一个内核的**.
* 虚拟机也不是绝对安全的.找到HyperVisor的弱点,攻克SELinux控制,就可以攻破虚拟机,染指宿主机。但这是非常难的.
* 容器的隔离技术使用的是namespace,但namesapce的资源隔离仅限于以下6个方面
|namespace|隔离内容|内核版本|
|---|---|---|
|UTS|主机名与域名|Linux 2.6.19|
|IPC|信号量,消息队列和共享内存|Linux 2.6.19|
|PID|进程编号|Linux 2.6.24|
|Network|网络设备,网络栈,端口|始于Linux 2.6.24 完成于 Linux 2.6.29|
|Mount|文件挂载|Linux 2.4.19|
|User|用户用户组| 始于 Linux 2.6.23 完成于 Linux 3.8|
> 为提升安全性,您的linux内核请升级版本大于3.8
> docker1.10开始支持User namespace,但不是默认开启的.[《Docker 安全之用户资源隔离》](http://www.zimug.com/453.html)
这6种资源隔离看上去比较完备,但是仍然有很多的资源没有隔离,比如:
* SELinux
* Cgroups
* file systems under /sys
* /proc/sys, /proc/sysrq-trigger, /proc/irq, /proc/bus
* /dev/mem
* /dev/sd* file system devices
这将宿主机的资源暴露给了容器的访问者,会导致安全问题!
**同时笔者认为,docker容器安全的痛点也仅限于互联网公有云环境下。对于企业内部私有云也算不上痛点,通过对docker进行安全加固可以达到生产环境的安全级别的要求!欢迎批判!**,第4章节,我们将看看如何提升docker容器安全!
## 3.docker容器安全也有自己的优势
容器的资源隔离程度相对虚拟机较低,与宿主机共享内核.导致容器的安全性低.那么是不是说容器相对于虚拟机,在安全性上完全没有自己的优势呢?
笔者认为,还是有的:
1. 可以禁止SSH访问,防止很多正面攻击
2. 容器暴露的端口有限,容器的本质是宿主机的进程,他只需要暴露非常有限的端口来提供服务.
3. 可以利用容器云控制容器的生命周期,使其在不影响服务的情况下,尽可能的短命.攻击短生命周期的容器意义并不大.
3. 容器的文件系统分层,镜像层是只读的,只有容器层可写.容器层文件的生命周期与容器相同(不是数据卷).
## 4.如何提升容器安全性
### 4.1 使用User namespace
**User namespace是从docker1.10开始被支持,并且不是默认开启的.**
docker 使用namespace进行资源隔离,其中一种是user namespace.user namespace主要隔离了安全相关的标识符和属性,包括用户ID,用户组Id,root目录,key(密钥)以及特殊权限.
默认的情况下,docker容器使用的root用户和宿主机的root用户是同一个用户,尽管可以限制容器内root用户的权限(capability),但本质上仍然和宿主机root用户是同一个用户.
有了user namespace之后,我们就可以将宿主机上的普通用户映射为容器的root用户,这样容器中的实际用户为普通用户权限,可以将容器的安全程度提高一个等级!
关于具体的实现步骤,请查看我的这篇文章:
[《Docker 安全之用户资源隔离》](http://www.zimug.com/453.html)
### 4.2 使用OpenSSL保护docker daemon
默认情况下,运行docker命令需要访问本地的Unix Socket.也可以通过HTTP的方式完成远程访问.
如果你需要以安全的方式在网络中访问docker,最好使用TLS,指定tlsverify参数,设置tlscacert参数指向一个可信的CA证书.在这种方式下,只有经过CA权限验证通过的客户端才能访问docker deamon.
具体内容请访问官网:[《Protect the Docker daemon socket》](https://docs.docker.com/engine/security/https/#connecting-to-the-secure-docker-port-using-curl)
### 4.3 安全的组网方式
我们可以将容器的服务端口分为两种
* 第一种:直接为用户提供服务的端口,如:web应用常用8080
* 第二种:为其他应用的提供服务的端口.
笔者认为安全的组网方式就是:**尽量**不使用端口映射的方式将容器端口映射到宿主机.端口暴露的越少,安全性就越高.
**什么是尽量?如何尽量?**
我采用overlay的网络,或者叫应用层网络,覆盖网络,**但这绝不是唯一的方法**.
docker 1.9为我们提供了官方的跨宿主机组网方式overlay(还有其他的网络工具也是这种方式).容器应用的网络构建于物理网络之上,在逻辑上又独立于物理网络.
举个例子:ZF的各个部门是都一个宿主机,容器(职能人员)之间可以跨宿主机沟通.部门级别之间的沟通是物理网络,各部门的职能人员之间的沟通是应用网络.
最后形成的沟通结论,对外发布,交给新闻发言人,这个新闻发言人是物理网络对外服务的接口.
同样:
对于第一种直接为用户提供服务的端口,可以采用端口映射的方式映射到宿主机,他就是发言人.
对于第二种端口的消息,应用网络内部处理,不对外发布。
实现方式可以参考:[《基于consul的Docker-overlay跨多宿主机容器网络》](http://www.zimug.com/364.html)
### 4.4 限制docker容器的capability
**什么是capability?**
简单地说,就是执行调用操作的权限.Linux将超级用户的权限进行分组,每一组代表了所能执行的系统调用操作.
如果是root用户,剥夺它的某些capability,那么它将无法调用对应的系统操作.如果是普通用户,对它赋予某些capability,它也可以完成某些超级用户才能做的系统调用.
capability为系统的权限管理,提供了更加细粒度的权限划分.
**容器默认拥有的capability(能力)包括:**
* CHOWN: 更改文件UID和GID的能力
* DAC_OVERRIDE:忽略文件的读,写,执行访问权限的检查的能力
* FOWNER:越过可执行文件的拥有者权限检查,执行操作的能力
* FSETID:文件修改后保留setuid/setgid标志位
* SETGID:改变进程组id的能力
* SETUID:改变进程用户ID的能力
* SETFCAP:向security.capability写入能力属性的能力
* NET_RAW:创建RAW和PACKET套接字的能力
* MKNOD:使用mknod创建特殊文件的能力
* SYS_REBOOT:允许使用reboot或者kexec_load.kexec_load功能是加载新的内核作为reboot重新启动所需的内核
* SYS_CHROOT:使用chroot的能力
* KILL:越过权限检查,发送信号的能力
* NET_BIND_SERVICE:绑定常用端口的能力(端口号小于1024)
* AUDIT_WRITE:允许审计日志写入的能力
**查看容器的capability(能力)**
通过docker ps 和docker inspect 获得容器进程的id,查看容器的默认能力
```
$ pscap |grep 165536
1411 19337 165536 top chown, dac_override, fowner, fsetid, kill, setgid, setuid, setpcap, net_bind_service, net_raw, sys_chroot, mknod, audit_write, setfcap
```
**移除容器的capability(能力)**
使用--cap-drop移除能力,运行容器
```
docker run -it --cap-drop SETGID --cap-drop SETUID ubuntu:14.04 top
```
通过docker ps 和docker inspect 获得容器进程的id,查看容器的现有能力.setuid和setgid的能力被移除
```
1411 19803 165536 top chown, dac_override, fowner, fsetid, kill, setpcap, net_bind_service, net_raw, sys_chroot, mknod, audit_write, setfcap
```
**为容器新增capability(能力)**
使用--cap-add增加能力,运行容器
```
docker run -it --cap-add SYS_TIME ubuntu:14.04 top
```
通过docker ps 和docker inspect 获得容器进程的id,查看容器的现有能力.新增了sys_time能力
```
1411 20101 165536 top chown, dac_override, fowner, fsetid, kill, setgid, setuid, setpcap, net_bind_service, net_raw, sys_chroot, sys_time, mknod, audit_write, setfcap
```
**Linux都有哪些capability(能力)?**
[capabilities - overview of Linux capabilities](http://www.man7.org/linux/man-pages/man7/capabilities.7.html)
### 4.5 文件系统只读保护
Docker 可以设置容器的文件系统为只读模式.这样可以禁止,脚本注入方式的攻击,因为脚本无法保存.
```
$ docker run --rm -ti --read-only ubuntu bash
```
**注意:此方法不能与user namespace 同时使用**
**……未完待续,欢迎持续关注我的博客!**
## 参考
* Docker安全部署的17条建议
http://dockone.io/article/150
* 容器相比虚拟机更为安全的十三个方面
http://dockone.io/article/1427
* Docker背后的内核知识——Namespace资源隔离
http://www.infoq.com/cn/articles/docker-kernel-knowledge-namespace-resource-isolation
* Docker安全
https://segmentfault.com/a/1190000005794220
';