第八章:Jail功能和控制

jail的root帐户完全控制着jail的用户区。她可以添加帐户、安装软件包、配置登录类和服务器等。是的,主机管理员可以通过只读挂载文件系统等技巧来屏蔽这些功能,但在大多数情况下,jail所有者控制着她的jail。

主机管理员还可以允许jail所有者对其jail进行更大的内核级访问。这包括允许jail所有者更改其jail主机名、为System V IPC分配内核内存、挂载文件系统和强制chflags(1)等功能。所有这些都要求jail所有者更权威地与内核交互,主机管理员必须有意启用所有这些功能。

许多先进的jail功能都有两个级别的访问控制。首先,必须在主机上启用该功能。如果主机管理员希望jail所有者能够挂载文件系统,他必须在主机上切换“jail可以挂载文件系统”。然而,这实际上并没有授予任何个人jail挂载文件系统的权力。主机管理员需要对单个jail设置该权限。虽然并非所有功能都有这种双层访问控制,但许多功能都有。当您在jail上设置权限时,它不起作用,请检查该功能是否有主机级控制。

在本章中,我们将把内存管理、安全级别和文件系统作为每jail功能的示例,但底层访问控制概念将在本书的其余部分中使用。

第八章:Jail功能和控制System V IPCsecurity.jail.sysvipcSysvipc 和标准 JailsSysvipc 和 Iocage安全级别和 chflags(1)设置Jail安全级别文件标志和安全级别挂载文件系统挂载前提条件fdescfsdevfsnullfsprocfs, linprocfs,linsysfstmpfsJail 内的 ZFS配置一个数据集配置一个ZFS授权的标准Jail在Jail内配置一个ZFS授权的 Iocage Jail次要特征更改主机名Dmesg锁定物理内存

System V IPC

System V IPC(进程间通信)或sysvipc是一种程序或一组程序可以留出一块内存并将其用作共享资源的方式。程序可以互相发送消息、留下记录、管理对象队列、涂鸦“嘿,比尔,看这个!”等等。把sysvipc想象成一个共享的白板,不同的进程可以相互通信。

虽然sysvipc的设计带有访问控制,但这些访问控制远远早于Unix虚拟化,更不用说jail了。多年来,主机上的所有jail共享一个共同的sysvipc命名空间。这意味着你无法安全地隔离jail中的数据库等程序。不过,所有受支持的FreeBSD版本都提供sysvipc命名空间转换,因此您现在可以在jail中安全地使用sysvipc。

Sysvipc分为三个组件:消息原语(message primitives——sysvmsg)、信号量原语(semaphore primitives——sysvsem)和共享内存原语(shared memory primitives——sysvshm)。这些原语中的每一个都可以设置为 newinheritdisable

new1 意味着主机为该功能提供了一个私有的sysvipc命名空间,与其他jail完全隔离。inherit2,告诉主机允许访问主机的sysvipc内存空间。使用继承通常是不可取的,除非您有意使用sysvipc进行jail之间的通信。最后,disable0 ,告诉主机在jail中禁用该类型的sysvipc。

我建议将所有这些图元(primitives)视为一个单元,并将它们全部设置为相同的值。是的,一个程序完全有可能只需要一两个原语。即使DoofusSQL的这个特定版本只需要共享内存,并且在没有其他原语的情况下运行良好,点发布也可能会添加另一个原语并导致奇怪的故障。

jail使用sysvipc的sysvmsg、sysvsem和sysvshm参数。sysvipc没有主机级访问控制。

security.jail.sysvipc

有一个sysctl security.jail.sysvipc,您将看到对allow.sysvipc参数的引用。这些已过时,用于控制每个jail对主机未转换的sysvipc命名空间的访问。启用它相当于将所有三个sysvipc参数设置为 inherit

如果它已经过时了,为什么还要提及它?一方面,有近二十年的文献提到它。如果一份文件说你必须启用它,停下来考虑一下你想实现什么。多年来,系统管理员对jail sysvipc有两种选择:完全禁用sysvipc,或允许访问主机的sysvipc命名空间。许多程序,特别是数据库,如果没有sysvipc就无法运行,因此主机管理员不得不谨慎地判断是否要监禁该服务。

在大多数情况下,如果一个文档说“启用sysvipc”,在现代FreeBSD上,你可以安全地将特定jail的sysvipc参数设置为 new 。像文档可能暗示的那样,你真正想要 inherit 的情况非常罕见。

在旧版FreeBSD上使用sysvipc意味着启用 security.jail.sysvipc sysctl ,然后在单个jail上启用sysvipc。如果您发现主机上仍在使用此功能,请将它们迁移到较新的系统。

Sysvipc 和标准 Jails

标准jail的默认sysvipc设置为 disable0 。这是FreeBSD多年来的默认设置,大多数程序都能很好地使用它。在 jail.conf 中使用单词或数值设置新的参数值。在这里,我们为jail logdb 启用了一个新的sysvipc命名空间,这样我就可以运行一个数据库并聚合我的日志条目:

重新启动jail,您将看到其中启用了sysvipc。

我们现在可以在这个jail里运行一个数据库。

Sysvipc 和 Iocage

iocage具有相同的 sysvmsgsysvsemsysvshm 参数。默认值是 new ,为每个jail创建一个新的sysvipc内存空间。它使用的内存比禁用sysvipc多,但程序只是工作。要更改这些参数,请使用单词值而不是数字。在这里,出于各种愚蠢的原因,包括但不限于管理命令,我让这个jail访问主机的sysvipc命名空间:

jail wdb2 现在已准备好将信息泄漏到任何其他jail集中,以继承sysvipc命名空间。

安全级别和 chflags(1)

虽然你可以改变jail的安全级别,但许多安全级别的保护和访问控制根本不适用于jail。默认情况下,jail无法访问磁盘设备,因此禁止编辑这些设备是无关紧要的。在让内核恐慌的jail这场不太可能的灾难中,主机处理所有的调试功能。虽然我们将在第9章中讨论使用自己的数据包过滤器的jail,但许多系统管理员在主机级别执行所有数据包过滤。jail不能改变系统时钟。

在jail中唯一具有实际影响的安全级别控制是文件标志执行,因此我们将重点讨论chflags(1) 。这意味着唯一真正重要的安全级别是 -11 。(如果你运行的是被监禁的防火墙,安全级别2很重要。)

标准jail默认为securelevel -1 ,而iocage jail默认为 2

设置Jail安全级别

jail的安全级别可以等于或高于host的安全级别。jail的安全级别不能低于host。在建造jail时,或者在jail内,可以提高安全级别。

参数 securevel 在创建时控制jail的安全级别。标准jail默认为主机的安全级别 -1 ,而iocage jail默认为安全级别 2 。在 jail.conf 或设置iocage时调整此参数,就像调整其他参数一样。

jail所有者可以通过 kern.securevel sysctl 增加jail的安全级别,或者在jail的 /etc/rc.conf 中设置新的安全级别。

这使得检查jail的安全级别变得复杂。iocage get securevelchecking jail.conf 将为您提供创建jail时的安全级别,但jail所有者可能已经更改了jail的安全级别。使用 jls 获得jail的实际安全级别。

securelevel 是一个参数,我总是查询jail的当前状态,而不是jail的创建方式。

文件标志和安全级别

虽然大多数文件标志都不与 securevel 交互,但 securelevel 直接控制 schgsappn 。在安全级别 1 或更高级别时,这些标志可防止删除或更改应用它们的文件。

如果主机以安全级别 0-1 运行,但jail以安全级别 1 或更高级别运行,则jail所有者无法删除这些标志或使用标记有这些标志的文件,但主机管理员可以。如果你有一个过于热情但缺乏经验的jail管理员,这可能会导致麻烦单,比如“我让我的主目录不可变,发送帮助!”不过,你也可以将其用作安全执法的一部分。如果你想阻止jail所有者替换jail中的系统二进制文件,可以将它们全部标记为不可变,并提高jail的安全级别。在升级jail之前,您必须在这些关键目录上运行 chflags -R noschg ,但对于某些环境(和某些用户)来说,这是值得的。

jail所有者默认情况下不能使用 chflags(1) ,因为这会导致上述支持票。如果你想让jail所有者能够设置文件标志,你必须在jail上设置 allow.chflags 参数。

挂载文件系统

文件系统是危险的。损坏的文件系统可能会使主机死机。默认情况下,Jails无法挂载文件系统。如果您授予jail所有者挂载一种文件系统的权限,他们可以挂载该类型的任何文件系统。主机管理员最好挂载jail所需的任何文件系统,最好是通过 fstab 文件(第4章)或自动挂载器。

但有时你无法控制它。也许jail的存在是专门用来检查和处理文件系统的,或者它可能需要一个以不规则的间隔挂载的文件系统,但在其他时候不会挂载。现实往往以这种方式带来不便。您可以授予jail权限来挂载特定类型的文件系统。

请记住,并非所有文件系统都可以在jail内进行管理。通过运行 lsvfs(8) 并查找“jail”标志来识别jail安全文件系统。一些常见的文件系统,如FAT、cd9660、NFS,甚至UFS,都无法在jail内安全管理。它们被紧密地集成到虚拟内存系统中,使得它们不容易被包含在内。ZFS是为虚拟化操作系统而编写的,因此使其具有jail安全性是相当简单的。您可以在jail中管理devfs、fdescfs和tmpfs等合成文件系统,以及FUSE和(可悲的是)procfs。您还可以管理Linux合成文件系统,如linprocfs和linysfs,但不能管理基于EXT的文件系统。

虽然不安全的文件系统不能在jail内进行管理,但它可以为jail提供数据存储。从主机装载和管理此类文件系统。

如果你仍然想在jail里管理文件系统,请继续阅读。

挂载前提条件

enforce_statfs 参数(第4章)控制挂载点可见性。默认设置 2 意味着除jail根目录之外的挂载点作为挂载点不可见。jail所有者甚至不能将他们知道存在的挂载点(如 /dev )视为挂载点,而只能将其视为目录。这阻止了在/以外的任何地方安装任何东西的机会。要允许jail所有者查看其他挂载点,从而挂载jail内可以感知到的任何东西,请将 enforce_statfs 设置为 1 。(您也可以将其设置为 0 ,但这会消除所有装载点可见性限制,几乎可以肯定这是一个糟糕的主意。)

不可见的挂载点可能会导致问题。jail所有者可能认为他们的jail有大量的磁盘空间,但也许主机管理员已经安装了一个单独的分区作为jail的 /home ,而该分区几乎已满。jail的所有者看不到这一点。不会在分区之间移动打开的文件。我不是说不要用多个不可见的分区配置你的jail,但我是说你应该让jail的所有者知道这件事。

既然jail可以看到挂载点,让我们谈谈权限。Jails有一个通用的 mount 参数 allow.mount 。它默认为 0 ,不允许jail中的所有 mount(8) 命令。将其设置为 1 可以挂载和卸载文件系统。

但是,您仍然需要按文件系统类型授予权限。Jails以 allow.mount 的形式为每个jail安全文件系统的新挂载和卸载权限提供单独的参数。以及文件系统名称,如 allow.mount.zfsallow.mount.devfs 。如果jail有一个参数授予挂载该文件系统类型的权限,则jail的根帐户可以使用标准的 mount(8) 命令来挂载和卸载这些文件系统。

挂载和卸载文件系统的权限不包括在创建jail时挂载 jail(8) 挂载的文件系统的权力。考虑 /devdevfs 。某些软件配置,如 chroots ,可能需要在jail的 /var/named 中添加额外的 devfs 。jail的root可以挂载和卸载该 devfs 实例。然而,jail的正式 /dev 需要在jail开始之前就存在。它的安装是由host控制的,而不是jail。jail所有者无法卸载jail的 /dev ,或由 mount.fdescfs 创建的 /dev/fd ,等等。

让我们依次讨论每个jail可管理的文件系统。

fdescfs

mount.fdescfs 参数控制jail是否应在启动时挂载 fdescfs 。iocage默认挂载 fdescfs 。我通常也会在标准jail的 jail.conf 中将 mount.fdescfs 设置为默认值,因为它几乎不需要任何成本,而且它的存在减少了我遇到的麻烦调用次数。

为什么jail所有者需要管理额外的 fdescfs 实例?Linux仿真需要在Linux chroot中添加额外的 fdescfs 。在jail配置中设置 allow.mount.fdescf ,让jail所有者管理自己的 fdescfs

devfs

jail的主设备文件系统 /dev 存在于jail的进程命名空间之外,无法从内部进行管理。主机必须对jail的 /dev 进行所有更改。 mount.devfs 参数控制主机在启动jail时是否创建 /dev ,启用它是通用的默认值。据我所知,没有任何不需要 /dev 的jailable应用程序。

如果jail需要额外的 devfs 实例,请在jail配置中设置 allow.mount.devfs

jail不能运行 devd(8) ,因此jail无法访问动态设备或 devfs 规则。新的 devfs 实例是jail的 /dev 的精确副本。如果你需要在jail中使用一组不同的 devfs 规则,请使用 mount.fstab 从主机配置它。

nullfs

带有 nullfs(5) 的环回式(lookback-style)挂载在jail里工作得很好。jail只能清空它能看到的挂载目录。使jail所有者能够使用 allow.mount.nullfs 执行这些操作。没有 mount.nullfs 参数;在创建jail时使用 fstab 文件配置null挂载。

procfs, linprocfs,linsysfs

这三个合成文件系统都适用于jail。 mount.procfs 参数告诉 jail(8) 在启动jail时挂载 /proc 。此挂载在jail的整个生命周期内都保持着。如果jail所有者打算挂载其他 procfs 实例,或者需要挂载和卸载 /proc ,她只需要 allow.mount.procfs 。强烈建议不要使用进程文件系统,我鼓励在不使用时卸载它。

FreeBSD的Linux模式需要 linprocfslinysfs 。jail所有者必须设置 allow.mount.linprocfsallow.mount.loinsysfs 参数,才能在jail内使用Linux软件。你可以在jail中使用Linux兼容性,而内核可以像jail一样运行Linux(第10章)。

tmpfs

设置 allow.mount.tmpfs 参数,以允许jail所有者创建和销毁内存文件系统。旧式的内存磁盘 md(4) 无法在jail内进行管理。即使您可以获得 /dev/md0 设备,传统上在它们上使用的UFS文件系统也无法在jail内进行管理。使用 tmpfs(5)

如果你授予此权限,jail可能会耗尽主机的内存。您需要限制jail可以访问的内存量,如第11章所述。

Jail 内的 ZFS

ZFS比任何其他文件系统都更了解jail。您可以将ZFS数据集及其所有子数据集的管理委托给jail。您可以允许jail所有者在他们的jail中创建新的数据集,调整这些数据集,并将新数据集分配给jail。jailed 属性表示数据集属于jail。

能够对ZFS数据集进行任意更改的jail所有者可以进行更改,使数据集与主机不兼容。如果他们创建了一个挂载点为 /var/log 的新数据集,她显然希望该数据集在jail中用作 /var/log 。如果主机挂载了该数据集,它将挂载在主机的 /var/log 上。这将造成的混乱程度不值得娱乐。主机无法挂载具有 jailed 属性集的ZFS数据集。您当然可以取消设置此设置,但请先检查数据集的挂载点。

不要将jail的主要数据集委托给jail。从主机管理jail意味着主机必须访问jail的基础系统及其软件包。你可以暂时从jail中释放一个数据集,重新配置所有挂载点,执行维护,并恢复jail所有者的配置,但这是一个很棘手的过程,很可能会出错。相反,为jail所有者提供一个私人数据集,他们可以尽情使用和滥用(abuse)。如果数据集需要放在像 /home 这样的关键系统位置,请在将jail的控制权交给用户之前执行初始设置。

配置一个数据集

在主机上,创建要委托给jail的数据集。将所有必要的文件复制到该数据集。记住,一旦数据集被委托给jail,如果不从jail中删除数据集并仔细检查数据集与主机的兼容性,您将无法从主机访问内容。

确实如此。其他事情都发生在jail里。

配置一个ZFS授权的标准Jail

为了管理ZFS,jail必须能够访问 /dev/zfs 。不要允许所有jail访问 /dev/zfs ;虽然他们没有管理数据集的权限,但你不需要jail所有者四处打听。专门为ZFS管理创建一个特殊的 devfs 规则集,并仅将其分配给需要它的jail。

为了让jail管理ZFS,jail必须具有 allow.mount.zfs 参数。它还需要通用的 allow.mount 参数,并且必须将 enforce_statfs 设置为 1 或更低,就像管理任何其他文件系统一样。此外,使用 exec.start 运行 zfs mount -a ,告诉jail挂载它能看到的所有zfs数据集。

在这里,我将数据集 cdr/cdr 关进jail,这是一个专用于保留呼叫详细记录的池中的顶级数据集。jail cdrpit 依赖于全局默认值来处理路径和IP地址等无聊的细节。

ZFS委托的神奇仙尘完全在两个 exec.created 参数中。第一个是 zfs set criminat=on cdr ,它在目标zfs数据集上设置属性。理论上,你只需要做一次,但如果主机管理员篡改了数据集,我们将在jail开始时进行验证。第二个命令 zfsj ail cdrpit cdr 告诉zfs将数据集 cdr 分配给jail cdrpit。这是另一个只需要执行一次的命令,但双重检查可能会防止出现问题。

jail的启动脚本可能需要jail数据集中的数据。因此,在我们开始jail之前,我们必须挂载所有ZFS数据集。这意味着重新定义默认的 exec.start ,以便jail在运行其启动脚本之前挂载其所有ZFS数据集。

在Jail内

登录jail并检查可用的数据集。

jail的ZFS知道我们的委托数据集,并希望管理它。如果你运行 mount ,你会看到jail的其他文件系统。如果你已经将ZFS数据集委托给了jail,那么jail的根目录很可能也是ZFS。jail将根文件系统识别为文件系统,但不识别为ZFS。jail无法访问根文件系统的ZFS特性,因为该数据集尚未委托给jail。

作为jail的所有者,委托的数据集是你的。您无法调整委托数据集的配额,但除此之外,配额归您所有。

配置一个ZFS授权的 Iocage Jail

jail cdrpit2在数据集cdr2/cdr上处理一组不同的呼叫详细记录。

jail_zfs 参数设置为 on 以启用jail内的zfs管理,然后设置 jail_zfs_dataset 参数以列出要委托给jail的数据集。给出不带池名的数据集名称。在这里,我创建了jail cdrpit2,并为其分配了顶级 cdr 数据集。

但是,您不能在iocage中设置委托数据集的装载点。您必须使用ZFS命令。登录jail,查看你的数据集:

委托数据集显示在iocage池下,尽管它实际上是一个单独的池。即使是如此多的细节也对jail所有者隐瞒了。根据需要更改其安装点。

就是这样。jail现在拥有这个数据集。

次要特征

jail有一堆小功能,从设置主机名到特殊内存和缓冲区访问,这些功能并不适合任何其他部分。

更改主机名

jail所有者可能希望使用标准的in-jail方法(如 hostname(1)/etc/rc.conf )更改jail的主机名。 allow.set_hostname 参数(iocage中的 allow_set_hostname )允许jail所有者更改jail主机名,默认为 on

更改jail主机名可能会使主机管理员感到困惑。jls 命令默认显示jail主机名,而不是jail名称。请记住,主机名与jail名称不同。在jail定义的开头设置jail名称,然后将其放入 name 参数中。jail的主机名是 host.hostname 参数。

如果主机名一致性对您或您的组织很重要,请在 jail.conf 中设置 allow.noset_hostname 参数;在iocage中,将 allow_set_hostname 设置为 0

Dmesg

Jails无法查看主机的 dmesg(8) 缓冲区或 kern.msgbuf sysctl 的内容。内核的消息隐藏在jail中。如果你想让jail能够查看主机的消息缓冲区,请设置 allow.read_msgbuf (iocage中的 allow_read_msgpuf )。

启用消息缓冲区访问后,jail中的任何用户都可以访问它。要将消息缓冲区可见性限制为仅在主机和允许的jail上的root,请将 sysctl security.bsd.unprivileged_read_msgbuf 设置为 0

任何设置组合都不允许 dmesg(8) 访问主机上的所有用户,而只允许jail上的root用户访问。

锁定物理内存

当程序请求内存时,内核会给它一块内存地址。程序无法知道这个内存是否位于内存芯片上,或者是否被换到磁盘上;够了,这是内存,它为程序存储东西。

然而,加密密钥等数据永远不应该被交换。处理此类机密数据的程序需要请求内存芯片上的内存,或者锁定物理内存。Jails默认不允许程序锁定物理内存,但您可以使用 allow.mlock 参数(iocage中的 allow_mlock )允许锁定物理内存。

如果 sysctl security.bsd.unprivileged_mlock 设置为 0 ,则只有root可以锁定jail和主机上的物理内存。

既然你可以随心所欲地微调你的jail,让我们以奇怪而可怕的方式将它们连接起来。