容器运行配置规则
启用cgroups能力进行恰当的资源限制
为了防止通过耗尽系统资源引发拒绝服务(DoS)攻击,可使用特定的命令行参数被来启用一些资源限制。
Kbox的base_box脚本中通过docker启动参数“--cpuset-cpus”和“--memory”限制CPU和内存的使用。由于Kbox使用ext4文件系统,其不支持“--storage-opt”参数,因此Kbox未限制存储使用。
限制运行容器的Linux内核Capability能力
在默认情况下,Docker会通过有限的Linux内核Capability启动容器。也就是说,任何进程都会被授予必要的Capability,而不是root访问。通过Linux内核Capability,在通常需要root权限的几乎所有特定
Kbox的base_box脚本中通过以下方式默认关闭所有Capability再按需增加必要的,其中{"Capability 1", ..., "Capability n"}集合具体值可查询base_box脚本。
1
|
# docker run --cap-drop=ALL --cap-add={"Capability 1", ..., "Capability n"}
|
可通过以下命令查看是否只有必要的Capability被添加。
1
|
# docker ps --quiet --all |xargs docker inspect --format '{{ .Id }}:CapAdd={{ .HostConfig.CapAdd }}CapDrop={{ .HostConfig.CapDrop }}'
|
在Android容器运行时需要开启SYS_ADMIN Capability,且在apparmor中打开mount能力,容器具有mount能力后可以通过挂载cgroup、proc、sysfs等文件系统进行容器逃逸,建议用户在apparmor中对敏感文件系统进行过滤或对宿主机OS、内核进行安全加固。
限制容器通过SUID/GUID位获得额外权限
限制容器通过SUID或GUID位获取额外权限。SUID和GUID程序在受攻击导致任意代码执行(如缓冲区溢出)时将非常危险,因为它们将可以运行在进程文件所有者或组的上下文中。
建议通过以下步骤删除系统中不需要的SUID和GUID程序。
- 找到系统中不需要的SUID和GUID程序。
1 2
# find / -perm -4000 -exec ls -l {} \; 2>/dev/null # find / -perm -2000 -exec ls -l {} \; 2>/dev/null
- 移除SUID和GUID文件权限,下列命令中的“filename”和“directory”请根据实际情况修改。
1 2
# sudo chmod u-s filename # sudo chmod -R g-s directory
不要在容器上挂载主机系统敏感目录
主机系统敏感目录(包括根目录、/boot、/dev、/etc、/lib、/proc、/sys、/usr)不能作为容器卷挂载,尤其是在读写模式下。如果在读写模式下挂载,敏感目录中的文件可能会被更改。这些更改可能没有必要,还可能会带来安全问题,侵害docker主机。
Kbox的base_box脚本中将“/proc”、“/sys”、“/dev”目录下子目录的挂载方式修改为只读。
可通过以下命令确认,结果不匹配“Source:(/|/boot|/dev|/etc|/lib|/proc|/sys|/usr)\s+.*RW:true”,则修改成功。
1
|
# docker ps --quiet --all| xargs docker inspect --format '{{ .Id }}: Volumes={{ .Mounts }}' 2>/dev/null
|
容器中只运行必需的软件,不以root权限运行不受信的应用
容器中若使用不必要的软件,可能会加大容器的攻击面,同时也违背了最小和精简容器镜像的概念。因此,不要为容器安装或运行任何不必要的软件。
只打开容器需要的端口
容器可以只使用Dockerfile中为其镜像定义的端口运行,也可以任意传递运行时参数以打开端口列表。此外,随着时间的推移,Dockerfile可能会经历各种更改,暴露的端口列表可能与容器中运行的应用程序相关,也可能不相关。打开不需要的端口会加大容器和容器化应用程序的攻击面。因此不要暴露不需要的端口。
建议修改方式:
启动容器时,使用“--publish”或“-p”选项明确指定特定容器实例所需的端口。例如:
1
|
# docker run --interactive --tty --publish 5000 --publish 5001 --publish 5002 my_container /bin/bash
|
Kbox的base_box脚本中通过以下方式指定容器实例所需的端口。
1 2 3 4 |
local PORT for PORT in ${PORTS[@]}; do RUN_OPTION+=" -p $PORT " done |
设置容器CPU适当优先级
CPU时间默认在各容器之间平均分配。如有需要,可以使用CPU共享特性控制CPU时间在容器实例之间的分配。CPU共享功能将容器进行优先级排序,并禁止优先级低的容器频繁地占用CPU资源,确保优先级高的容器得到更好的服务。
Kbox的base_box脚本中通过启动参数“--cpu-shares”管理容器之间的CPU份额。
将传入的容器流量绑定到特定主机接口
Docker容器默认可以连接到外部世界,但外部世界不能连接到容器。每个对外连接显示都是源自主机的IP地址。只允许通过主机上的特定外部接口接触容器服务。如果主机上有多个网络接口,则容器可以接受任何网络接口上暴露的端口连接。这可能不是期望的结果,也可能不受保护。很多时候,特定接口会对外暴露,并且在这些接口上运行诸如入侵检测,入侵防御,防火墙,负载平衡等服务以筛选传入的公共流量。因此,不应接受任何接口上的传入连接,只接受来自特定外部接口的传入连接。这里主机接口通指网络接口,是主机与外部系统的连接通道,而主机端口是指该主机接口对应的TCP/UDP端口,接口一般包含IP地址和端口。
建议修改方式:
将容器端口绑定到所需主机端口上特定的主机接口。如下所示,将容器端口80绑定到49153上的主机端口,并且只接受来自10.2.3.4外部接口的传入链接。
1
|
# docker run --detach --publish 10.2.3.4:49153:80 nginx
|
实现此安全加固措施可修改Kbox的base_box脚本中“-p”选项内容,配置格式请参考示例,并根据实际情况自行配置可用ip地址:
1
|
# RUN_OPTION+=" -p XXX.XXX.XXX.XXX:$PORT "
|
将容器的根文件系统挂载为只读
容器的根文件系统应该被视为“黄金镜像”,避免任何写操作。应明确定义专门用于写操作的容器卷。
将容器的根文件系统挂载为只读可能会出现兼容性问题,请按照实际业务需求评估是否需要采纳该建议。
不要禁用默认seccomp配置文件
默认seccomp配置文件是一个指定可以执行哪些系统调用的白名单,进程可利用seccomp过滤来为传入的系统调用指定过滤器。Docker默认的seccomp配置文件在白名单基础上工作,允许311个系统调用而阻止其他所有的调用。大量的系统调用暴露给每个用户空间进程,其中许多系统调用在整个进程周期中都未使用。大多数应用程序根本不需要这些系统调用,因此可以使用精简版系统调用。精简版系统调用减小了暴露给应用程序的总内核面,从而提高了应用程序的安全性。
在Ubuntu操作系统中,seccomp配置文件默认开启。除非想修改或使用修改的seccomp配置文件,否则不需做任何操作。
在openEuler 22.03 系统方案中,Android容器运行时需要禁用seccomp配置文件(seccomp=unconfined)。用户需要评估应用场景是否允许禁用seccomp配置文件以及禁用的风险,建议禁用seccomp配置文件后对宿主机OS、内核进行安全加固。
限制文件句柄和fork进程数量
攻击者可以在容器内使用单个命令启动fork炸弹。该fork炸弹能使整个系统崩溃,需要重启主机才能使崩溃系统再次运行。同时攻击者可以开启大量文件句柄,耗尽文件句柄资源,造成拒绝服务攻击。具体设置fork进程、文件句柄的阈值,根据产品实际业务情况决定。
实现此安全加固措施可以修改Kbox的base_box脚本,修改方式请参考示例。该阈值需要客户根据应用场景进行合理的设置,数值不正确可能会导致容器无法使用。
# RUN_OPTION+=" --pids-limit XXX --files-limit XXX "
不要使用Docker默认的网络桥接接口Docker0
使用Docker用户自定义的容器网络,而不使用Docker默认的网络桥接接口docker0。docker0作为默认的网络接口,在没有配置相应网络过滤前,容易遭受ARP欺骗和MAC泛洪攻击。注意:Docker默认是运行在docker0桥接的。
建议修改方式:
- 删除旧网桥。在执行此操作之前,先删除服务器上所有容器。
1 2 3
# sudo systemctl stop docker # sudo ip link set dev docker0 down # sudo brctl delbr docker0
- 创建一个名称为“bridge0”的新网桥,以下示例仅作为格式参考,请根据实际情况自行配置可用网段,以保证容器连接网络正常。
1 2 3
# sudo brctl addbr bridge0 # sudo ip addr add XXX.XXX.XXX.XXX/XX dev bridge0 # sudo ip link set dev bridge0 up
上述可用网段可参考默认网桥接口docker0的配置,以下图为例,网段应设置为172.17.0.1/16。
可通过以下命令确认创建的新网桥“bridge0”详细信息。
1
# ifconfig bridge0
- 添加新网桥的配置信息。在“/etc/docker/daemon.json”文件中添加属性“"bridge": "bridge0"”。
1 2
# vim /etc/docker/daemon.json "bridge":"bridge0"
若“/etc/docker/daemon.json”文件不存在,则自行创建该文件。
1 2 3 4 5
# touch /etc/docker/daemon.json # vim /etc/docker/daemon.json { "bridge":"bridge0" }
- 重启Docker服务。
1 2
# systemctl daemon-reload # systemctl restart docker
重启Docker服务后,可能会重新拉起默认的网络桥接接口“docker0”,因此在执行4后,需要通过以下命令检查当前环境是否还存在“docker0”。
1
# ifconfig docker0
若仍然能够查询到“docker0”,则需要通过以下命令手动删除。
1 2
# sudo ip link set dev docker0 down # sudo brctl delbr docker0
每次重启服务器后,都需要先执行2重新创建新网桥“bridge0” ,才能重启docker服务