本文共 6792 字,大约阅读时间需要 22 分钟。
在本系列有关的第一篇文章中,我写道“容器不包含”。 在第二篇文章中,我将介绍为什么以及我们正在为此做些什么。
Docker,Red Hat和开源社区正在共同努力,以使Docker更加安全。 当我查看安全性容器时,我希望保护主机免受容器中的进程的侵害,并且还希望保护彼此之间的容器。 借助Docker,我们使用了 ,即“将多个缓解性安全控件组合在一起以保护资源和数据的实践”。
基本上,我们希望设置尽可能多的安全屏障以防止突围。 如果特权进程可以脱离一种遏制机制,则我们希望将其与下一种机制隔离。 使用Docker,我们希望利用尽可能多的Linux安全机制。
幸运的是,借助Red Hat Enterprise Linux(RHEL)7,我们可以获得大量的安全功能。
一些Linux内核文件系统必须安装在容器环境中,否则进程将无法运行。 幸运的是,大多数这些文件系统都可以作为“只读”挂载。 大多数应用都不需要写这些文件系统。
Docker将这些文件系统作为“只读”安装点安装到容器中。
。 / sys
。 / proc / sys 。 / proc / sysrq-trigger 。 / proc / irq 。 / proc /总线通过将这些文件系统安装为只读,特权容器进程将无法对其进行写入。 它们不会影响主机系统。 当然,我们还阻止特权容器进程将文件系统重新安装为读/写的能力。 我们阻止了在容器中完全挂载任何文件系统的功能。 我将说明获得功能时如何阻止安装。
Docker使用文件系统。 这意味着容器可以使用与容器基础相同的文件系统映像。 当容器将内容写入映像时,它将被写入特定于容器的文件系统。 这样可以防止一个容器看到另一个容器的更改,即使它们已写入同一文件系统映像。 同样重要的是,一个容器不能更改图像内容以影响另一个容器中的过程。
Linux功能在其主页上有很好的解释:
为了执行权限检查,传统的UNIX实现将进程分为两类:特权进程(其有效用户ID为0,称为超级用户或root)和非特权进程(其有效UID为非零)。 特权进程绕过所有内核权限检查,而无特权的进程则根据进程的凭据(通常是:有效的UID,有效的GID和补充组列表)接受完全的权限检查。 从内核2.2开始,Linux将传统上与超级用户相关联的特权划分为不同的单元,称为功能,可以独立地启用和禁用这些功能。 功能是每个线程的属性。
删除功能可能导致应用程序中断,这意味着我们在Docker中平衡了功能,可用性和安全性。 这是Docker使用的当前功能列表:chown,dac_override,fowner,kill,setgid,setuid,setpcap,net_bind_service,net_raw,sys_chroot,mknod,setfcap和audit_write。
不断地来回争论默认情况下应允许或拒绝哪些功能。 Docker允许客户使用命令行选项来操作默认列表,以运行Docker。
Docker删除了其中一些功能,其中包括:
CAP_SETPCAP | 修改流程功能 |
CAP_SYS_MODULE | 插入/删除内核模块 |
CAP_SYS_RAWIO | 修改内核内存 |
CAP_SYS_PACCT | 配置流程记帐 |
CAP_SYS_NICE | 修改流程优先级 |
CAP_SYS_RESOURCE | 覆盖资源限制 |
CAP_SYS_TIME | 修改系统时钟 |
CAP_SYS_TTY_CONFIG | 配置tty设备 |
CAP_AUDIT_WRITE | 编写审核日志 |
CAP_AUDIT_CONTROL | 配置审核子系统 |
CAP_MAC_OVERRIDE | 忽略内核MAC策略 |
CAP_MAC_ADMIN | 配置MAC配置 |
CAP_SYSLOG | 修改内核printk行为 |
CAP_NET_ADMIN | 配置网络 |
CAP_SYS_ADMIN | 全部抓住 |
让我们仔细看看表中的最后一对。 通过删除容器的CAP_NET_ADMIN,容器进程无法修改系统网络,这意味着将IP地址分配给网络设备,设置路由规则,修改 。
容器启动之前,所有网络连接均由Docker守护程序设置。 您可以从容器外部而不是内部管理容器网络接口。
CAP_SYS_ADMIN是特殊功能。 我相信这是内核的全部功能。 当内核工程师在内核中设计新功能时,他们应该选择与该功能所允许的功能最匹配的功能。 或者,他们应该创建一种新功能。 问题是,最初只有32个功能插槽可用。 如有疑问,内核工程师将退回到使用CAP_SYS_ADMIN。 这是CAP_SYS_ADMIN根据/ usr / include / linux / capability允许执行的操作的列表。
Allow configuration of the secure attention key | 允许管理随机设备 |
Allow examination and configuration of disk quotas | 允许设置域名 |
Allow setting the hostname | 允许调用bdflush() |
Allow mount() and umount(), setting up new smb connection | 允许一些autofs根ioctl |
Allow nfsservctl | 允许VM86_REQUEST_IRQ |
Allow to read/write pci config on alpha | 在mips上允许irix_prctl(setstacksize) |
Allow flushing all cache on m68k (sys_cacheflush) | 允许删除信号量 |
Used instead of CAP_CHOWN to "chown" IPC message queues, semaphores and shared memory | 允许锁定/解锁共享内存段 |
Allow turning swap on/off | 允许套接字凭据传递时伪造的pid |
Allow setting readahead and flushing buffers on block devices | 允许在软驱中设置几何 |
Allow turning DMA on/off in xd driver | 允许管理md设备(大多数情况下,但还有一些额外的ioctl) |
Allow access to the nvram device | 允许管理apm_bios,串行和bttv(TV)设备 |
Allow manufacturer commands in isdn CAPI support driver | 允许读取pci配置空间的非标准化部分 |
Allow DDI debug ioctl on sbpcd driver | 允许设置串口 |
Allow sending raw qic-117 commands | 允许在SCSI控制器上启用/禁用标记队列并发送任意SCSI命令 |
Allow setting encryption key on loopback filesystem | 允许设置区域回收策略 |
Allow tuning the ide driver |
从容器中删除CAP_SYS_ADMIN的两个最重要的功能是阻止进程执行挂载syscall或修改名称空间。 您不想让您的容器进程挂载随机文件系统或重新挂载只读文件系统。
--cap-add --cap-drop
Docker run还具有一项功能,您可以在其中调整容器所需的功能。 这意味着您可以删除容器不需要的功能。 例如,如果您的容器不需要setuid和setgid,则可以通过执行以下操作删除此访问权限:
docker run --cap-drop setuid --cap-drop setgid -ti rhel7 /bin/sh
您甚至可以删除所有功能或全部添加它们:
docker run --cap-add all --cap-drop sys-admin -ti rhel7 /bin/sh
该命令将添加除sys-admin之外的所有功能。
Docker为运行进程设置的一些名称空间也提供了一定的安全性。
PID名称空间隐藏了系统上正在运行的所有进程,但当前容器中正在运行的进程除外。 如果看不到其他进程,则使攻击该进程更加困难。 您不能轻易地追踪或追踪它们。 并且,杀死进程名称空间的pid 1将自动杀死容器中的所有进程,这意味着管理员可以轻松地停止容器。
网络名称空间可用于实现安全性。 管理员可以使用路由规则和iptables设置容器的网络,以便容器内的进程只能使用某些网络。 我可以想象人们设置了三个过滤容器:
对系统的一种攻击可以描述为拒绝服务。 这是一个或一组进程使用系统上所有资源的位置,从而阻止其他进程执行。 可用于通过控制任何Docker容器可以使用的资源量来减轻这种情况。 例如,可以设置CPU cgroup,以便管理员仍可以登录到Docker容器试图控制CPU并杀死它的系统。 正在开发新的cgroup,以帮助控制进程避免使用过多的资源,例如打开的文件或多个进程。 Docker将在这些cgroup可用时加以利用。
Docker利用特殊的cgroup,可让您指定可以在容器中使用的设备节点。 它阻止进程创建和使用可用于攻击主机的设备节点。
设备节点允许进程更改内核的配置。 控制哪些设备节点可用可以控制进程在主机系统上可以执行的操作。
默认情况下,在容器中创建以下设备节点。
/dev/console,/dev/null,/dev/zero,/dev/full,/dev/tty*,/dev/urandom,/dev/random,/dev/fuse
Docker映像也随nodev一起挂载,这意味着即使在映像中预先创建了设备节点,容器中的进程也无法使用它与内核进行通信。
注意:也可以通过删除CAP_MKNOD功能来阻止设备节点的创建。 Docker选择不这样做,以允许进程创建一组有限的设备节点。 在期货部分,我将提到--opt命令行选项,我想使用它删除此功能。
Apparmor可用于支持它的系统上的Docker容器。 但是我使用不支持AppArmor的RHEL和Fedora,因此您将不得不在其他地方研究此安全机制。 (此外,众所周知,我使用SELinux。)
首先,介绍一下SELinux:
SELinux实现了强制访问控制系统。 这意味着对象的所有者对对象的访问没有控制权或裁量权。 内核强制执行强制访问控制。 我已经在的 (以及随后的 )中描述了SELinux实施的工作方式。
我将使用该文章中的一些动画片来描述我们如何使用SELinux来控制允许对Docker容器进程的访问。 我们对Docker容器使用两种SELinux强制实施。
类型强制保护主机免受容器内进程的侵害
我们用于运行Docker容器的默认类型是svirt_lxc_net_t。 所有容器进程都以这种类型运行。
容器中的所有内容均标记为svirt_sandbox_file_t类型。
允许svirt_lxc_net_t管理带有svirt_sandbox_file_t标签的任何内容。
svirt_lxc_net_t也能够读取/执行主机上/ usr下的大多数标签。
不允许使用svirt_lxc_net_t运行的进程打开/写入系统上的任何其他标签。 不允许读取/ var,/ root,/ home等中的任何默认标签。
基本上,我们希望允许进程读取/执行系统内容,但默认情况下,除非其位于容器中,否则我们不允许其使用系统上的任何“数据”。
如果所有容器进程都使用svirt_lxc_net_t运行,并且所有内容都标记为svirt_sandbox_file_t,那么是否不允许容器进程攻击在其他容器中运行的进程以及其他容器所拥有的内容?
这就是多类别安全性实施的来源,如下所述。注意,我们在类型标签中使用了“ net”。 我们用它来表示这种类型可以使用完整的网络。 我正在研究Docker的补丁,以允许用户指定用于容器的替代类型。 例如,您将可以指定以下内容:
docker run -ti --security-opt label:type:lxc_nonet_t rhel7 /bin/sh
然后,容器内部的进程将不允许使用任何网络端口。 同样,我们可以轻松地编写一个Apache策略,该策略仅允许容器在Apache端口上侦听,但不允许在任何端口上进行连接。 使用这种类型的策略,即使您的容器被破解,也可以防止其成为垃圾邮件机器人,并且黑客可以控制容器中的apache进程。
多类别安全性(MCS)保护一个容器免受其他容器的侵害
多类别安全性基于多级安全性(MLS)。 MCS利用了SELinux标签MLS Field的最后一个组件。 MCS强制执行可保护容器彼此之间。 启动容器时,Docker守护进程会选择一个随机的MCS标签(例如s0:c1,c2)分配给该容器。 Docker守护程序使用此MCS标签标记容器中的所有内容。 守护程序启动容器进程时,它告诉内核使用相同的MCS标签来标记进程。 内核仅允许容器进程读取/写入自己的内容,只要进程MCS标签与文件系统内容MCS标签匹配即可。 内核阻止容器进程读取/写入以其他MCS标签标记的内容。
防止黑客入侵的容器进程攻击不同的容器。 Docker守护程序负责确保没有容器使用相同的MCS标签。 是演示如果OpenShift容器能够从根系统进入系统,将会发生什么情况。 相同的基本策略用于限制Docker容器。
如前所述,我正在为Docker制作补丁,以允许指定不同的SELinux内容。 我将允许管理员指定容器的标签。
docker run --ti --rm --label-opt level:TopSecret rhel7 /bin/sh
这将使人们可以在多级安全性(MLS)环境中开始运行容器,这对于需要MLS的环境很有用。
SELinux当前仅与设备映射器后端一起使用。 SELinux不适用于BTFS。 BTRFS还不支持上下文安装标签,这会阻止SELinux在容器通过mount命令启动时重新标记所有内容。 内核工程师正在研究此问题,如果将它合并到容器中,则可能正在修复Overlayfs。
由于类型强制仅允许容器进程在容器中读取/写入svirt_sandbox_file_t,因此卷挂载可能会成为问题。 卷安装只是目录到容器的绑定安装,目录的标签不会更改。 为了允许容器进程读取/写入内容,您需要将类型标签更改为svirt_sandbox_file_t。
卷挂载/ var / lib / myapp
chcon -Rt svirt_sandbox_file_t / var / lib / myapp我为docker编写了一个补丁,该补丁尚未在上游进行合并以自动设置这些标签。 使用该补丁程序,泊坞窗将自动将卷重新标记为私有标签“ Z”或共享标签“ z”。
docker run -v /var/lib/myapp:/var/lib/myapp:Z ... docker run -v /var/lib/myapp:/var/lib/myapp:z ...
希望这将很快合并。
我们添加了许多安全机制,以使Docker容器比在裸机上运行应用程序更安全,但是您仍然需要维护良好的安全实践,正如我在有关此主题的第一篇文章中所谈到的那样。
我的下一篇有关Docker安全性的文章将介绍我们下一步将如何工作以进一步保护Docker容器。
翻译自:
转载地址:http://vlnzd.baihongyu.com/