
Docker容器是最流行的容器化技术。 最初,它主要用于开发和测试环境,随着时间的推移,它开始转为生产。 Docker容器在生产环境中开始大量繁殖,就像雨后的蘑菇一样,但是,使用该技术的人很少考虑如何安全发布Docker容器。
基于
OWASP ,我们准备了一系列规则,这些规则的实施将极大地保护基于Docker容器的环境。
规则0
主机和Docker必须包含所有当前更新。
为了防止导致从容器环境逃逸到主机系统的已知漏洞(通常会导致主机系统上的特权升级),为主机OS,Docker Engine和Docker Machine安装所有补丁非常重要。
此外,容器(与虚拟机不同)与主机一起使用内核,因此在容器内部运行的内核漏洞利用程序直接在主机内核中执行。 例如,在隔离良好的容器中运行的内核特权升级漏洞(例如Dirty COW)将导致对主机的根访问。
规则一
不要访问docker守护进程的套接字
Docker服务(守护程序)使用UNIX套接字/var/run/docker.sock进行传入API连接。
此资源的所有者必须是root用户。 而且没有其他办法。 更改对此套接字的访问权限实质上等同于授予对主机系统的根访问权限。
另外,您不应该在容器中弄乱/var/run/docker.sock套接字,在没有套接字的情况下也可以这样做,因为在这种情况下,破坏容器中的服务将导致对主机系统的完全控制。 如果您的容器使用以下内容:
-v /var/run/docker.sock://var/run/docker.sock
或对于docker-compose:
volumes: - "/var/run/docker.sock:/var/run/docker.sock"
迫切需要改变这一点。
最后-在没有绝对需要的情况下,
永远不要使用Docker TCP套接字,尤其是在不使用其他保护方法(至少是授权)的情况下。 默认情况下,Docker TCP套接字在外部接口0.0.0.0:2375(对于HTTPs为2376)上打开端口,并允许您完全控制容器以及潜在的主机系统。
规则二
在容器内配置一个非特权用户
配置容器以使用非特权用户是避免特权升级攻击的最佳方法。 这可以通过多种方式完成:
1.使用“ docker run”命令的“ -u”选项:
docker run -u 4000 alpine
2.在映像构建期间:
FROM alpine RUN groupadd -r myuser && useradd -r -g myuser myuser < root-, , > USER myuser
3.在Docker守护程序中启用对“用户名称空间”(用户环境)的支持:
--userns-remap=default
在
官方文档中阅读有关此内容的更多信息。
在Kubernetes中,后者是通过runAsNonRoot选项在
安全上下文中配置的:
kind: ... apiVersion: ... metadata: name: ... spec: ... containers: - name: ... image: .... securityContext: ... runAsNonRoot: true ...
规则三
限制容器功能
在Linux上,从内核2.2开始,有一种方法可以控制特权进程的功能,称为
Linux内核功能 (有关详细信息,请参见链接)。
Docker默认使用一组预定义的内核功能。 它允许您使用以下命令更改此设置:
--cap-drop — --cap-add —
最好的安全设置是先禁用所有功能(--cap-drop all),然后仅连接必要的功能。 例如,像这样:
docker run --cap-drop all --cap-add CHOWN alpine
最重要的(!):避免使用–privileged标志运行容器!
在Kubernetes中,通过功能选项在安全上下文中配置Linux内核功能约束:
kind: ... apiVersion: ... metadata: name: ... spec: ... containers: - name: ... image: .... securityContext: ... capabilities: drop: - all add: - CHOWN ...
规则四
使用no-new-privileges标志
启动容器时,使用--security-opt = no-new-privileges标志很有用,它可以防止容器内部的特权升级。
在Kubernetes中,可通过allowPrivilegeEscalation选项在安全上下文中配置Linux内核功能约束:
kind: ... apiVersion: ... metadata: name: ... spec: ... containers: - name: ... image: .... securityContext: ... allowPrivilegeEscalation: false ...
规则五
关闭容器间通信
默认情况下,在Docker中启用容器间通信,这意味着所有容器都可以彼此通信(使用docker0网络)。 可以通过使用–icc = false标志运行Docker服务来禁用此功能。
规则六
使用Linux安全模块(Linux安全模块-seccomp,AppArmor,SELinux)
默认情况下,Docker已经将配置文件用于Linux安全模块。 因此,
切勿禁用安全配置文件! 他们可以做的最大事情就是收紧规则。
seccomp的默认配置文件位于
此处 。
Docker还使用AppArmor进行保护,当容器启动时,Docker Engine本身会为AppArmor生成默认配置文件。 换句话说,代替:
$ docker run --rm -it hello-world
启动:
$ docker run --rm -it --security-opt apparmor=docker-default hello-world
该
文档还提供了一个用于nginx的AppArmor配置文件的示例,这很有可能(必要!)使用:
规则七
限制容器资源
该规则非常简单:为了防止容器在下一次DoS / DDoS攻击期间吞噬所有服务器资源,我们可以分别为每个容器设置内存使用限制。 您可以限制:内存量,CPU,容器重新启动的次数。
因此,让我们按顺序进行。
记忆-m或--memory选项容器可以使用的最大内存量。 最小值为4m(4兆字节)。
选项-内存交换用于配置交换(交换文件)的选项。 巧妙配置:
- 如果--memory-swap> 0,则还必须设置–memory标志。 在这种情况下,内存交换会显示容器与交换一起可使用的总内存量。
- 一个简单的例子。 如果--memory =“ 300m”,并且--memory-swap =“ 1g”,则该容器可以使用300MB内存和700MB交换空间(1g-300m)。
- 如果--memory-swap = 0,则忽略该设置。
- 如果--memory-swap设置为与--memory相同的值,则该容器将不具有交换功能。
- 如果未指定--memory-swap,但指定了--memory,则交换次数将等于指定的内存量的两倍。 例如,如果--memory =“ 300m”,并且未设置--memory-swap,则该容器将使用300MB内存和600MB交换空间。
- 如果--memory-swap = -1,则容器将使用主机系统上所有可能的交换。
给女主人的提示:在容器内启动
的免费实用程序并不显示容器可用交换的实际价值,而是主机交换的数量。
选项-禁用oom-kill允许您启用或禁用OOM(内存不足)杀手。
注意! 您只能使用--memory选项设置来关闭OOM Killer,否则,如果容器内存不足,内核可能会开始杀死主机系统进程。
其他内存管理配置选项(例如--memory-swappiness,-memory-reservation和--kernel-memory)更多地用于调整容器的性能。
中央处理器选项--cpus该选项设置容器可以使用多少可用处理器资源。 例如,如果我们有一台带有两个CPU的主机,并且将--cpus =“ 1.5”设置为1,则该容器将保证使用一个半处理器。
选项--cpuset-cpus配置特定内核或CPU的使用。 可以用连字符或逗号指定该值。 在第一种情况下,将在第二个特定内核中指示允许的内核范围。
容器重启次数 --restart=on-failure:<number_of_restarts>
此设置可设置Docker在意外崩溃时将尝试重新启动容器的次数。 如果容器的状态已更改为运行,则重置计数器。
建议设置一个较小的正数,例如5,这样可以避免无休止的服务重启。
规则八
使用只读文件系统和卷
如果容器不应该在任何地方写任何东西,那么您需要尽可能多地使用只读文件系统。 这将大大增加潜在入侵者的生命。
使用只读文件系统启动容器的示例:
docker run --read-only alpine
以只读模式连接卷的示例:
docker run -v volume-name:/path/in/container:ro alpine
规则9
使用容器安全性分析工具
必须使用工具来检测具有已知漏洞的容器。 它们还不是很多,但是它们是:
•免费:
•商业:
对于Kubernetes,有一些用于检测配置错误的工具: