标准jails将FreeBSD的副本放到磁盘上,并像其他安装一样使用它。当你有一堆jail运行相同的版本,甚至可能运行相同的包时,存储数TB的重复文件会让人觉得不雅(inelegant)。
iocage jail通过使用ZFS克隆来优化磁盘使用率。这很好,只要你的jail不长到需要跨主要版本升级的时间。替换ZFS克隆中的所有文件后,它将成为操作系统的另一个副本,但依赖于底层快照。再次:不雅。
没有一个jail系统能优雅地解决这个问题。相反,我提供了三种不同的方法来解决这个问题。
iocage提供了一个clone命令,允许您复制现有的jail。当然,你可以对标准jail做同样的事情,但iocage为你提供了一个方便的脚本来复制。
克隆的下一步是一个模板,一个你可以完美配置然后按需复制的jail。
有些环境有许多必须完全同步的jail。环境要求所有服务器运行完全相同的FreeBSD版本,或者所有包在可证明的编程上完全相同。或者,也许你的磁盘空间非常短缺,但需要几个jail。使用基本jail在所有这些jail之间共享一个文件系统。
你应该使用哪一种?这完全取决于你的需求。不过,不要跳过你认为是你首选的解决方案。本章中的每一节都引用了前面部分中讨论的概念,因此在部署之前请阅读整个章节。
这不是一个完整的优化列表。人们用硬链接甚至符号链接重新实现基础jail。人们煞费苦心地优化jail内容,构建定制的二进制文件,并设置任意数量的智能系统。如果你找到了比我讨论的更适合你的东西,那就用它。Unix提供了源源不断的绳索,你可以随心所欲地吊死自己。
第六章:空间优化克隆标准Jail克隆Iocage克隆清除克隆模板创建Iocage模板使用Iocage模板模板和ZFS标准Jail模板更新模板基础 Jail基础 Jail 包Iocage 基础 Jails定制Iocage基础JailsIocage基础Jail重建标准基础 Jails默认的基础 Jail fstab 文件标准基础 Jail 崩溃包和标准基础 Jails用包准备原始Jail用软件包准备衍生Jail重建标准 Jails
如果你想要一个现有jail的精确副本,克隆它。克隆是jail的一个分支;它一开始与原版完全相同,但当两个jail发生任何变化时,它就会有所不同。对原始jail的更新不会传播到其克隆:如果需要这种传播,请考虑基础jail。
克隆最常用于一次性复制,例如复制应用程序服务器,以便您可以测试升级。它们通常是短暂的。
对于标准jail来说,克隆非常简单。在UFS上,使用 tar(1)
甚至 cp -rp
在另一个目录中复制源目录树,然后在 jail.conf 中将副本配置为新的jail。如果你使用的是ZFS,你可以使用ZFS克隆。
真正地。就是这样。
然而,正如我们将在下一节中看到的那样,从标准jail创建模板更有用。
iocage克隆是原始jail数据集的ZFS克隆,加上该jail的iocage支持文件的新副本。你不能克隆一个正在运行的jail。使用 iocage clone
克隆jail。第一个参数必须是要克隆的jail的名称或UUID。使用 -n
参数为jail指定一个新名称,并在名称后添加任何要更改的参数作为命令行参数。在这里,我将jail www1克隆到www2,并使用ip4_addr参数为其分配一个新的IP地址:
xxxxxxxxxx
# iocage clone www1 -n www2 ip4_addr="203.0.113.234"
www2 successfully cloned!
当您为克隆分配名称时,iocage会更改克隆 /etc/rc.conf 中的主机名值以匹配。如果不为克隆命名,iocage会为新jail生成一个随机UUID。同样,虽然这对自动化来说是完美的,但对人类来说并不是那么好。
也许你需要一大群克隆。 iocage clone
的 -c
参数允许您指定一次创建多少个克隆:
xxxxxxxxxx
# iocage clone www1 -c5
6ed60d8f-0274-47bf-86d4-b4483cbaa1d2 successfully cloned!
bc586521-6313-4ef3-9a39-e6f569e3c0c9 successfully cloned!
…
每个克隆都分配了一个UUID,打印在命令行上。如果你想在创建jail时给它们命名,可以单独创建它们,并使用 -n
指定名称。
所有这些jail都完全一样。这就引出了一个新问题…
克隆最初是原始jail的精确复制品。你希望你的jail都有完全相同的文件吗?几乎可以肯定不是。如果没有别的,每个jail都需要唯一的SSH主机密钥。用户帐户可能包含您不想携带到克隆中的临时信息。
修复 sshd(8)
主机密钥很容易。如果存在主机密钥文件,sshd(8)
将使用它们。删除密钥文件会强制 sshd
创建新文件。
你可能还有其他你不想在jail里复制的元素。在我的环境中,我特别不希望原始主机的 /root/.ssh/known_hosts 分布在新的jail中;主机密钥更改过于频繁,几个月后,一个虚假的条目会让我感到痛苦。我使用了一个简单得可笑的脚本 newjailclean.sh ,它只接受一个参数,即新jail的名称:
xxxxxxxxxx
rm /iocage/jails/$1/root/etc/ssh/*key*
rm /iocage/jails/$1/root/root/.ssh/known_hosts
克隆继承了原始jail的所有设置和配置,包括 /etc/passwd 中的设置和配置。这肯定会导致任何系统管理员组之间的争用:模板应该有用户帐户还是根密码?是否最好有一个初始默认根密码,您需要在每个新主机上更改该密码?或者,您是否应该冒险没有root密码?在我看来,“此主机有旧的根密码”的故障模式不如“此主机没有根密码”可怕,但您必须评估您环境中的风险。无论你做出什么决定,都要把结果写进你的jail清洁脚本中。
使用模板时,您的脚本将变得尤为重要。
模板是一个完美的jail,专门设计用于复制出其他jail。它基本上是克隆的变体,除了模板jail设置为只读以避免意外更改。除了测试之外,模板几乎从不作为实际的jail运行。当您从模板部署jail时,您可以根据jail的角色添加其他程序和配置。只有当您打算从模板部署多个jail时,模板才有价值。
将模板配置为网络的标准基线,包括对环境至关重要的所有文件和服务。我的所有系统,无论是操作系统还是虚拟化方法,都共享一个 sudoers sudo配置文件。我的服务器通过LDAP进行身份验证,因此它们需要 ldap.conf 和 nsswitch.conf 。作为一名负责任的系统管理员,我在所有生产、暂存和测试服务器上禁用基于密码的身份验证,所以我的jail需要一个自定义的 sshd_config 。某些环境具有自定义PAM配置。我在模板中添加了ansible或nagios等管理用户,以及回退帐户来处理那些不可避免的LDAP故障。
就像克隆一样,模板中的任何错误都会被复制到基于该模板的所有jail中。然而,它比克隆的风险要高一点。虽然克隆通常用于复制现有的jail,但模板用于部署新的jail。完美配置模板jail。理想情况下,您的组织有一个如何建立新系统的清单。虽然jail不能加密自己的磁盘或拥有串行控制台,但“配置LDAP”和“安装tmux”等步骤当然适用。使用你能从清单中偷走的一切,然后测试一切。SSH服务器是否以新配置启动?LDAP身份验证有效吗?logging怎么样;每个jail是运行自己的 syslogd(8)
,还是将所有日志消息转发到syslog jail?
测试完模板后,让其他人再次测试。您可以在部署之前修复一次模板,也可以在以后修复所有jail。
从模板创建模板是完全可能的,但我建议不要这样做。一个组织模板的想法,你可以快照和克隆它来为不同类型的服务器创建模板,也可以快照和复制它来创建单个服务器,这可能会让人觉得很有诱惑力,但这样的树很脆弱,很难进行更改。抵制这种诱惑,特别是如果你正在使用ZFS。
ZFS上的标准jail可以使用ZFS快照作为模板。在UFS上,通过标记模板jail的目录并在需要时提取它来构建一个标准的jail模板。
iocage的模板支持更复杂,功能更丰富。
最初,你的iocage模板是一个无聊的老jail,就像其他jail一样。给它一个临时IP,允许它访问所有必要的网络资源。我将测试LDAP和SSH功能,因此jail需要我网络上的地址。
xxxxxxxxxx
# iocage create -n dnstemplate ip4_addr="203.0.113.245" -r 11.2-RELEASE
与其他jail完全一样,dnstemplate是FreeBSD版本iocage目录的ZFS克隆。在这种情况下,它是11.2的克隆。您将修改clone dnstemplate,然后克隆dnstemplote以创建更多jail。
我的所有服务器都需要 tmux
、 openldap-client
、 emacs-nox
和 sudo
软件包。此外,我的DNS服务器运行BIND 9.13。我将所有这些安装在模板jail中:
xxxxxxxxxx
# iocage pkg dnstemplate install openldap-client sudo bind913 emacs-nox tmux
现在,我将网络的重要配置文件复制到模板jail中,并创建标准用户。
我的模板jail旨在作为DNS服务器的基础模板,虽然我已经安装了该组织的首选DNS服务器,但我还不会配置DNS。权威和递归DNS服务器的配置非常不同。
当iocage基于模板创建一个jail时,它也会复制模板的所有参数。如果基于此模板的jail需要特定的非默认iocage参数,也可以调整这些参数。
一旦您确定模板jail正常工作,请使用“克隆”下讨论的克隆清理脚本删除任何不能在基于此模板的jail上复制的临时文件。
在最终确定模板之前,回收任何临时设置,如临时借用的IP。iocage允许您将一个IP分配给多个jail,但您需要配置这些jail以避免TCP/IP端口重叠,或者确保您不会同时使用该IP运行多个jail。未来,你会感激你在它们出现在你面前时防止它们重叠。
一旦你的模板jail完全按照需要设置好,通过设置模板参数将其转换为模板。这使得jail在ZFS级别为只读,并将数据集从 /iocage/jails 移动到/iocage/templates。
xxxxxxxxxx
# iocage set template=yes dnstemplate
dnstemplate converted to a template.
即使boot属性设置为on,模板jail也不会在系统启动时启动,并且无法手动启动。运行jail将涉及写入数据集,我们不能这样做。
使用 iocage list -t
命令查看所有模板:
xxxxxxxxxx
# iocage list -t
+-----+-------------+-------+--------------+---------------+
| JID | NAME | STATE | RELEASE | IP4 |
+=====+=============+=======+==============+===============+
| - | dnstemplate | down | 11.2-RELEASE | - |
+-----+-------------+-------+--------------+---------------+
有关模板的更多详细信息,请添加 -l
标志:
xxxxxxxxxx
# iocage list -tl
我们现在可以基于此模板部署jail。
要基于模板创建jail,请使用带有 -t
标志的 iocage create
。将模板名称作为参数。在这里,我基于模板dnstembolate创建了一个jail dns3:
xxxxxxxxxx
# iocage create -t dnstemplate -n dns3
看看结果:
xxxxxxxxxx
# iocage list
+-----+--------+-------+--------------+---------------+
| JID | NAME | STATE | RELEASE | IP4 |
+=====+========+=======+==============+===============+
| - | dns3 | down | 11.2-RELEASE | - |
…
iocage不会将每个jail的项目(如IPv4和IPv6地址)唯一地复制到新jail。如果我需要,我必须设置IP地址。我还必须在系统启动时设置启动。在这里,我只需一个命令即可完成所有操作:
xxxxxxxxxx
# iocage set boot=on ip4_addr="203.0.113.243" dns3
Property: boot has been updated to on
Property: ip4_addr has been updated to 203.0.113.243
我现在可以启动jail并配置其特定功能,相信其所有核心服务都能正常工作。
让我们看看iocage模板在文件系统级别是如何工作的。 zfs get
的默认输出非常宽,因此我使用 -o
选项从输出中修剪不需要的列。我还命令输出更容易解释发生了什么。
xxxxxxxxxx
# zfs get -o name,value -r origin iocage | grep dnstemplate
iocage/iocage/releases/11.2-RELEASE/root@dnstemplate -
iocage/iocage/templates/dnstemplate -
iocage/iocage/templates/dnstemplate/root iocage/iocage/releases/11.2-RELEASE/root@dnstemplate
iocage/iocage/templates/dnstemplate/root@dns3 -
iocage/iocage/jails/dns3/root
iocage/iocage/templates/dnstemplate/root@dns3
第一个条目,iocage/iocage/releases/11.2-RELEASE/root@dnstemplate ,是iocage的FreeBSD 11.2安装目录的快照。运行11.2的所有安装的jail都是此数据集的克隆。
第二个条目,iocage/iocage/templates/dnstemplate 是一个数据集,包含dnstemplate jail及其iocage配置文件。
第三个是属于jail dnstempolate的文件的数据集,iocage/icage/templates/dnstempolate/root 。它是第一个快照中11.2版本的克隆。
第四,我们有快照 iocage/iocage/templates/dnstemplate/root@dns3 。这是我们从模板dnstembolate创建jail dns3时的状态。
最后,我们有 iocage/iocage/jailes/dns3/root ,这是jail dns3中文件的数据集。这是第四个条目中数据集的克隆。
作为一个整体,我们克隆了11.2安装目录以创建模板dnstemplate,配置了dnstempllate,然后克隆了dnsteeplate以创建jail dns3。所有基于dnstempolate的jail都依赖于11.2版本,即使我们最终将其升级到FreeBSD 13、15或30。如果你使用一个模板作为另一个模板的基础,然后从中创建jail,那么这棵树会变得更加复杂。
由此很容易看出,在没有iocage的情况下,如何创建基于ZFS的jail模板。
如果你在ZFS上使用标准jail,你可以使用ZFS快照来构建自己的模板。如果您使用的是UFS,请使用 tar(1)
构建模板。无论哪种方式,先建造你的原始(pristine)jail。按照使用iocage时的方式进行测试。
要通过ZFS克隆使用模板,每个jail都必须是自己的数据集。在这里,我为我的DNS服务器模板创建了一个ZFS数据集:
xxxxxxxxxx
# zfs create jail/dnstemplate
如果您正在运行UFS,请创建目录 /jaire/dnstempolate 。
无论您使用什么文件系统,都要将所选的 base.txz 解压缩到该目录中,并为dnstempelate创建 /etc/jail.conf 条目。将 /etc/resolv.conf 复制到jail中。启动jail并安装所需的软件包。
xxxxxxxxxx
# pkg -j dnstemplate install openldap-client sudo bind913 emacs-nox tmux
现在,将其他关键的通用文件添加到模板jail中,如 sudoers
和 ldap.conf
。分配默认的根密码并创建任何本地用户。记住,基于此模板的jail继承了所有这些用户和密码。测试您的模板jail,以验证LDAP和sudo等核心功能是否按预期运行。
作为最后一步,删除不应复制的文件:/root/known_hosts、SSH主机密钥和任何其他临时文件。理想情况下,创建一个应该删除的文件列表,并编写一个脚本来销毁它们。更新模板时,您将再次需要它。
关闭模板jail。现在可以复制了。
对于ZFS,对jail的数据集进行快照,并从快照中创建克隆。我按日期命名我的快照。在这里,我根据模板为jail dns1创建文件系统:
xxxxxxxxxx
# zfs snapshot jail/dnstemplate@2018-11-08
# zfs clone jail/dnstemplate@2018-11-08 jail/dns1
虽然创建一个服务器需要做很多工作,但根据此模板创建其他服务器是一个命令。
xxxxxxxxxx
# zfs clone jail/dnstemplate@2018-11-08 jail/dns2
# zfs clone jail/dnstemplate@2018-11-08 jail/dns3
# zfs clone jail/dnstemplate@2018-11-08 jail/dns4
这说明了模板只值得批量使用。
UFS上的模板使用了更多的空间,但许多人发现在概念上扔文件块比ZFS快照更简单。把你的模板jail备份到tarball上。我强烈建议在tarball名称中包含日期,这样您就可以轻松更新模板。
xxxxxxxxxx
# cd /jail/dnstemplate
# tar -czvf /jail/media/dnstemplate-2018-11-08.tgz .
当你想部署jail时,释放tarball:
xxxxxxxxxx
# cd /jail/dns1
# tar -xpf /jail/media/dnstemplate-2018-11-08.tgz
无论你使用的是UFS还是ZFS,在 /etc/jail.conf 中配置dns1,你就可以开始了。
经验丰富的系统管理员都明白,制作和部署一个完美抛光的服务器模板是宇宙需要淘汰该模板的唯一借口。安全公告和补丁每天都会到来。每次通过任何方法部署新的jail时,都要立即运行 freebsd-update
和 pkg-upgrade
(第5章)来更新它。但是,模板会使补丁复杂化。
模板和根据模板创建的jail是不同的实体。对模板的更新仅影响基于该模板创建的新jail,而不影响现有jail。修补由模板构建的iocage jail,或由ZFS克隆构建的标准jail,会导致这些jail变得更大。您需要更新模板,以便新部署的jail不那么过时,需要更少的补丁。
每次部署新jail时,你都应该更新你的模板吗?不一定。大多数安全更新相对较小,不断更新模板意味着额外的测试。不过,从不更新模板也不是一个好主意。每当底层FreeBSD有新的发布版本时,我通常都会更新我的模板。如果我安装了12.0版本的模板,我会更新12.1、12.2等版本的模板。这可以最大限度地减少新ZFS克隆的大小增加。
当您将iocage jail声明为模板时,iocage会将ZFS数据集设置为只读。您需要声明模板jail不再是模板,启动并更新它,然后再次声明它是模板。
xxxxxxxxxx
# iocage set template=no dnstemplate
dnstemplate converted to a jail.
Property: template has been updated to no
虽然你的模板应该有一个基于模板的每个jail的现有快照,但我建议现在创建一个特定的升级前快照。我以日期命名快照。如果不将快照名称指定为最终参数,iocage将根据当前日期和时间分配一个快照名称。
xxxxxxxxxx
# iocage snapshot dnstemplate Snapshot: iocage/iocage/templates/dnstemplate@2019-02-25_20:17:46 created.
现在升级jail,重新启动jail,并升级软件包:
xxxxxxxxxx
# iocage update dnstemplate
# iocage restart dnstemplate
# iocage pkg dnstemplate upgrade -y
您的系统和软件包现在已更新,因此有些东西被巧妙地破坏了。这就是规则。解决问题,确认一切正常,然后关闭jail。
删除任何不应传播到新jail的文件,例如模板的SSH主机密钥。还记得在上一节中,我建议你编写一个脚本来删除这些文件吗?这就是原因。运行您方便的模板清理脚本。
xxxxxxxxxx
# templateclean.sh dnstemplate
现在将jail切换回模板:
xxxxxxxxxx
# iocage set template=yes dnstemplate
你的jail现在更新了。从该模板克隆的新jail将获得所有已安装软件的最安全版本……如果您在下一次安全咨询之前创建它们。
也许jail管理中最大的痛点是应用安全补丁。您必须立即应用补丁,如果不是更早的话,但磁盘的吞吐量有限。当你有数百个jail时,修补是一项不可忽视的任务,并会对性能产生明显的影响。同样,一些环境需要它们的jail来运行完全相同的FreeBSD版本,通常是软件包。如果您管理数十个jail或需要细致的版本同步,请考虑 base jails。
“base jail”一词起源于人们将基本操作系统重新装载为所有jail的文件系统的时代。曾几何时,磁盘非常小,以至于操作系统占据了系统存储的很大一部分。将基础设施重新用于jail是一笔可观的节省。在这个多TB磁盘的时代,这感觉很古怪。
如今,该术语描述了任何源于这种配置的jail设置。大多数情况下,磁盘上的某个地方都有FreeBSD安装,就像普通jail一样,但它会被重新挂载以供其他jail使用。“base jail”一词可能指的是所使用的jail类型、其他jail所依据的jail,或者基于另一个jail的jail。换句话说,base jail是以base jail为基础的base jail。为了清楚起见,我将其他jail的base jail称为origin jail(原始jail)。建在那个基地上的jail(该死!)我称之为derived jail(衍生jail)。一旦你理解了一切是如何运作的,并且可以从上下文中推断出“基础”的确切含义,就可以随意称一切为基础jail,让下一代感到困惑。
FreeBSD的 nullfs(5)
允许将目录连接到目录树的多个点。现代的base jail利用nullfs从单个jail的文件系统中挂载密钥目录,而不是许多其他jail。派生jail以只读方式挂载这些目录,因此派生jail不能与对等派生jail混在一起。更新原始jail会神奇地更新所有jail中的二进制文件和其他关键文件。它不会为您更新 /etc ,但会处理所有二进制文件和库。
基础jail使软件管理复杂化。考虑一下模板。从模板而不是从版本部署jail的主要原因是,所有jail都需要一组通用的包和配置文件。不过,将包纳入原始jail很快就会变得复杂。这并不是说将包放入原始jail在技术上很复杂,但这些包的管理可能会陷入混乱。
最明显的方法是将源jail的 /usr/local 挂载到派生jail中。如果你的所有派生jail都需要完全相同的软件包,这很有效。但他们呢?真的吗?您的web服务器中有多少具有其他服务器所不需要的特殊包?由于 /usr/local 是只读挂载的,jail管理员无法安装其他软件包。您在原始jail中安装的任何软件包都可用于所有jail。你必须按角色将jail分开。好的。假设你需要一大堆nginx服务器jail,所以你创建了一个nginx源jail。然而,你的一些应用程序需要PHP 5.6,而另一些则需要PHP 7。FreeBSD的包管理系统不支持安装多个版本的PHP。您是否手动安装这两个模块,一个是 /usr/local/bin/php56 ,另一个是 /usr/local/bi/php7 ,并手动安装所有模块?或者,你是否按照非常精确的软件要求仔细划分你的jail?当一个jail需要特定的 php-xmlreader
包,但另一个jail明确需要不安装该包时,你会怎么做?
你选择你的痛苦,这就是你所做的。
另一种选择是通过设置 $PREFIX
,使用 poudriere
构建使用不同包根目录的包。您可以将origin jail的软件包放在 /usr/pkg 中,如NetBSD,并为真正的本地软件包保留 /usr/local 。然而,许多软件无法识别安装在根目录或 /usr/local 之外的软件。NetBSD勇敢的pkgsrc开发人员做了大量的工作来使 /usr/pkg 中的软件正常工作,但许多上游项目不接受这些补丁,因为什么样的疯子需要在 / 或 /usr/local 之外安装软件?默认的FreeBSD软件包可能无法识别 /usr/pkg 中的软件,从而导致软件安装重复或冲突。
这是可以做到的。工作量很大。你想做那项工作吗?如果您选择执行该工作,请提交补丁。
或者,您可以使用 poudriere
构建直接安装在根目录下的包。不要将 sudo(1)
安装为 /usr/local/bin/sudo ,而是将其放在 /bin/sudo 中。我讨厌这种解决方案,即使它奏效了。大部分。FreeBSD的一个关键优势是基本系统和软件包之间的明确分离。包中的文件可能会覆盖基本系统文件。同样,可以将包基于 / ,但需要仔细测试。
即使您的软件完全相同,您的服务器配置也可能不同。所有那些通常出现在 /usr/local/etc 中但jail管理员需要编辑的配置文件都必须放在派生jail的本地某个地方。例如,虽然FreeBSD的Apache port 使用默认配置文件 /usr/local/etc/apache24/httpd.conf ,但该目录将从派生的jail挂载。如果jail管理员不能编辑它,她会很沮丧。你需要一个配置目录,比如 /etc/apache24 。也许你需要一个 /etc/rc.conf 条目来将Apache指向该目录中的配置文件。也许你会保留标准配置文件的位置,但编辑它以从本地配置目录中吸收其他配置文件。全局使用的配置文件,如 ldap.conf 和 sudoers ,可以保留在 /usr/local/ 等目录中。
或者,您可以根据角色和软件要求对jail进行细致的分组,并开发一个相对无痛的过程,在源jail之间迁移派生jail。幸运的是,这并不难。
iocage默认使用release作为原始jail。这最大限度地提高了每个派生jail的灵活性,但最大限度地减少了共享的本地定制。修补该版本会更新每个jail的FreeBSD安装,但不会影响软件包或配置。
通过在创建jail时添加 -b
标志来创建派生jail:
xxxxxxxxxx
# iocage create -r 11.2-RELEASE -b -n dns3
dns3 successfully created!
# iocage start dns3
在定制新的jail之前,让我们检查一下iocage用于此基础jail的文件系统和挂载。将终端扩展到120个字符左右,然后继续。
xxxxxxxxxx
# mount | grep dns3
iocage/iocage/jails/dns3 on /iocage/jails/dns3 (zfs, local, nfsv4acls)
iocage/iocage/jails/dns3/root on /iocage/jails/dns3/root (zfs, local, nfsv4acls)
这些数据集用于jail的iocage目录和jail的根目录,就像其他jail一样。
xxxxxxxxxx
/iocage/releases/11.2-RELEASE/root/bin on /iocage/jails/dns3/root/bin (nullfs, local, read-only)
这是第一个base jail魔法。FreeBSD发行版的 /bin 目录以只读方式挂载在jail的 /bin 文件夹中。同样的重新挂载也会发生在 /boot 、/lib 、/libexec 、/research 、/sbin 、/usr/bin 、/usr/include 、/usr/lib 、/usr/libexec 、/usr/sbin 、usr/share 、/usr/libdata 和 /usr/lib32 上。在所有这些之后,您将找到出现在所有iocage jail中的标准 devfs(5)
和 fdescfs(5)
挂载。
在原始(origin) jail中进行了所有这些重新装载和覆盖后,衍生(derived) jail内部还有什么需要处理的呢?首先,一切都在 /etc 。本地配置完全委托给衍生jail。由于用户帐户不是由jail集中管理的,/usr/home 需要保留在衍生的jail中。每个jail都管理自己的日志和包,因此衍生的jail包括 /usr/local 和 /var 。如果你的jail包含FreeBSD源代码,那么你可能是为了破解它,所以jail会得到自己的副本。像 /var/home 这样的目录都与派生的jail一起使用。
在创建派生jail时,iocage仅填充由派生jail管理的目录。当 /bin 和 /sbin 从源jail重新挂载时,iocage会清空派生jail的 /bin 和 /sbin 。如果您对iocage将哪些文件复制到派生jail以及哪些文件从源jail被重新挂载有疑问,请停止jail并检查派生jail的目录树。你自己测试一下。这里,base jail dns3正在运行:
xxxxxxxxxx
# cd /iocage/jails/dns3/root
# ls bin
[ df kill ps
…
停止jail然后再看看:
xxxxxxxxxx
# iocage stop dns3
# ls bin
#
卸载源jail的目录后,派生jail的 /bin 为空。
被jail的用户对他们的文件系统有不同的看法。jail看起来是一个连续的文件系统,而不是一堆null挂载。jail对根目录之外的那些神秘文件系统一无所知,因此这些挂载是不可见的。
xxxxxxxxxx
# iocage exec dns3 mount iocage/iocage/jails/dns3/root on / (zfs, local, nfsv4acls)
一个拥有jail root访问权限的好奇用户可以通过 touch(1)
轻松识别只读目录。一旦她开始寻找,她很快就会意识到自己在一个base jail里。不要指望base jail的隐蔽性来保证任何形式的安全。
要查看系统上的所有base jail,请运行 iocage list -B
:
xxxxxxxxxx
# iocage list -B
+-----+------+-------+--------------+---------------+
| JID | NAME | STATE | RELEASE | IP4 |
+=====+======+=======+==============+===============+
| - | dns3 | down | 11.2-RELEASE | 203.0.113.243 |
+-----+------+-------+--------------+---------------+
对于只包括base jail的长jail列表,添加 -l
标志:
xxxxxxxxxx
# iocage list -Bl
您可以使用其他iocage列表标志,如 -q
和 -s
来修改输出。
当你想更新一次基本操作系统安装,并让这些更新自动复制到所有派生jail时,使用FreeBSD版本作为源的派生jail非常方便。但是,如果你想要一个定制的原始jail,包括额外的文件呢?使用模板作为基础jail。
使用 -t
标志创建派生jail,就像创建其他基于模板的jail一样,但添加 -b
标志将其创建为base jail。在这里,我创建了一个名为dns4的派生jail,它使用模板dnstempolate作为原始jail:
xxxxxxxxxx
# iocage create -t dnstemplate -b -n dns4
您应用于dnstembolate的任何更新都会自动传播到dns4。
无论你如何小心地将你的基础jail分配给他们的角色,最终你都需要将一个衍生jail转移到另一个原始jail。iocage的未来版本将包括一个自动执行此操作的选项,但目前您需要通过备份旧的派生jail并将其恢复到新的派生jail来手动完成基础jail的重建。
在主机上,使用 tar(1)
备份派生的jail。--one-file-system
标志告诉 tar
不要跨越文件系统边界。它不会跨越保存从源jail重新挂载的文件的null挂载,因此只包括派生jail文件系统中的内容。
xxxxxxxxxx
# tar -czf /home/mwlucas/dns4derived.tgz --one-file-system /iocage/jails/dns4/root/
创建一个新的派生jail并恢复关键文件。您需要手动比较 /etc/rc.conf 等文件并构建新的配置。如果原始jail提供 /usr/src ,则可以使用 mergemaster(1)
。您可以还原哪些确切的文件,哪些不能还原,哪些必须评估和重建,这取决于您如何使用基本jail。
你可以保留派生的jail,并手动更改jail的 fstab
以指向新的来源,但这超出了iocage管理的范围,可能会出现问题。
使用FreeBSD标准jail管理工具的基础jail只比使用iocage稍微复杂一些。您必须准备原始jail,创建一个 fstab ,准备派生jail,并配置 jail.conf 。我们将创建一个原始jail(ldapbase)和一个派生jail(ldap3),并创建一个模板,以方便使用相同的原始jail部署更多jail。
像其他jail一样创建你的原始jail。如果你要在基础jail中包含软件包,请在源jail中安装这些软件包。如果愿意,可以从模板创建源jail。你的每个派生jail都有一个独立的 /etc ,因此源jail的加密密钥不能泄露到派生jail中。在主机的 /etc/jail.conf 中配置源jail,并验证其是否按预期运行。jail应该能够接入互联网,提供shell,干净地启动和停止,以及您所期望的所有其他功能。我的原始jail是为LDAP服务器设计的,名为 ldapbase 。由于这个原始jail不应该在系统启动时自动启动,我将其从主机的 rc.conf 设置中排除。
决定你的基础jail从源jail提供哪些目录,哪些目录必须在派生jail中。Base jail默认为“所有未由原始jail提供的目录都属于派生jail”,但值得考虑的是,在尝试部署之前,此特定应用程序是否必须写入原始jail通常提供的特定目录。至少,原始标准jail必须提供iocage原始jail提供的目录。您将使用此列表为您的jail构建文件系统表。
还记得在第4章中,我们讨论了 mount(8)
如何使用 /etc/fstab 以外的fstab文件吗?我们将使用 mount.fstab
参数为每个jail提供自己的文件系统表,语法与 /etc/fstab 完全相同。jail系统在创建jail之前挂载这些文件系统,并在关闭jail后卸载它们。
将每个jail的fstab存放在jail外。您可能会发现为每个jail创建一个目录来包含jail元数据很有用,就像iocage一样,或者为不同类型的每个jail文件创建目录。
我为fstab文件创建了一个 /haires/fstab/ 目录,每个文件都以jail命名。我的第一个派生jail将被称为ldap3,因此它使用 ldap3.fstab 。这是一个典型的fstab,它提供了基于iocage的派生jail继承的相同文件系统。
xxxxxxxxxx
/jail/ldapbase/bin /jail/ldap3/bin nullfs ro 0 0
/jail/ldapbase/boot /jail/ldap3/boot nullfs ro 0 0
/jail/ldapbase/lib /jail/ldap3/lib nullfs ro 0 0
/jail/ldapbase/libexec /jail/ldap3/libexec nullfs ro 0 0
/jail/ldapbase/rescue /jail/ldap3/rescue nullfs ro 0 0
/jail/ldapbase/sbin /jail/ldap3/sbin nullfs ro 0 0
/jail/ldapbase/usr/bin /jail/ldap3/usr/bin nullfs ro 0 0
/jail/ldapbase/usr/include /jail/ldap3/usr/include nullfs ro 0 0
/jail/ldapbase/usr/lib /jail/ldap3/usr/lib nullfs ro 0 0
/jail/ldapbase/usr/libexec /jail/ldap3/usr/libexec nullfs ro 0 0
/jail/ldapbase/usr/sbin /jail/ldap3/usr/sbin nullfs ro 0 0
/jail/ldapbase/usr/share /jail/ldap3/usr/share nullfs ro 0 0
/jail/ldapbase/usr/libdata /jail/ldap3/usr/libdata nullfs ro 0 0
/jail/ldapbase/usr/lib32 /jail/ldap3/usr/lib32 nullfs ro 0 0
现在为派生jail创建一个模板。
衍生jail包括原始jail中没有的一切。为标准jail创建派生jail模板的最简单方法是使用用于创建原始jail的模板,并删除基础jail提供的所有内容。fstab文件提供了一个方便的要清空的目录列表。不要删除目录——你不能在不存在的目录上挂载任何东西!仅删除内容。
xxxxxxxxxx
# tar -xpf template.tgz -C /jail/ldap3
# rm -rf /jail/ldap3/boot/*
# rm -rf /jail/ldap3/bin/*
…
您可以将此保存为派生jail的新模板。由于原始jail包含升级过程中修补的所有内容,因此派生jail模板的有效期比大多数模板都长。
您可能会发现无法删除某些文件。
xxxxxxxxxx
# rm -rf /jail/ldap3/lib/*
rm: /jail/ldap3/lib/libc.so.7: Operation not permitted
rm: /jail/ldap3/lib/libcrypt.so.5: Operation not permitted
rm: /jail/ldap3/lib/libthr.so.3: Operation not permitted
这些文件安装时设置了不可变标志。清除该标记,然后重试:
xxxxxxxxxx
# chflags -R noschg /jail/ldap3/lib/
如果你的标准jail模板包含包,这些包会被复制到原始jail和派生jail中。上面的fstab不会将原始jail的 /usr/local 重新挂载到派生jail上,因此jail拥有这些包的独立副本。该模板还包括包数据库 /var/db/pkg ,因此模板化的jail可以管理这些包。你可能想把原始jail的包也包括在内……或者不包括在内。同样,基础jail的一揽子管理是一个痛苦的选择问题。
最后,为您的派生jail创建一个 jail.conf 条目。“Base jail”和相关单词在 jail.conf 中没有出现。创建派生jail所需的只是 mount.fstab
参数,该参数指定在启动jail之前要挂载的fstab。
xxxxxxxxxx
ldap3 {
ip4.addr="203.0.113.239";
mount.fstab="/jail/fstab/ldap3.fstab";
}
我总是想把 mount.fstab
设置为默认值。然而,如果这是默认设置,那么没有fstab,您就无法启动或停止任何配置的jail。不过,使用 touch(1)
创建一个空的fstab文件可以满足jail系统的要求,或者您可以在jail定义中设置 mount.nofstab
。如果将 mount.fstab
设置为默认值,则必须为每个jail创建一个fstab,或者为fstab-free jail设置一个属性。
xxxxxxxxxx
mount.fstab="/jail/fstab/$name.fstab";
哪种方式适合您的环境?我只知道,无论你做出什么选择,都会惹恼你。
我对jail很不好。当他们威胁要熬夜时,我在他们的腿之间放了一根棍子,让他们绊倒了。有时,我会毫不客气地打断他们。对于普通jail来说,除了恢复任何肮脏的数据库外,这并不是什么大问题,但一个崩溃的基础jail留下了残骸。jail关闭过程会卸载启动期间安装的任何文件系统。如果jail崩溃,您需要手动卸载这些文件系统。
xxxxxxxxxx
# umount -a -F /jail/fstab/ldap3.fstab
在卸载剩余的文件系统之前,派生jail不会重新启动。这似乎没有必要,但 jail(8)
认为,如果它负责这些文件系统,它会确保安装了正确的文件系统。
一套标准的软件包在单一来源的jail中集中管理,同时满足每个jail的所有需求,这是柏拉图式的理想,长期追求,永远无法实现。所有适用于在iocage基础jail分发包的问题也适用于标准基础jail。你最终会得到几个不同的原始jail,每个jail都有自己的套餐,并根据需要在这些原始jail之间转移衍生jail。在这里,我们遇到了jail管理中最致命的部分,也是系统管理员的克星:组织。
系统管理员喜欢创建组织方案。当我们开始一项新的任务或工作时,我们做的第一件事就是抱怨前任缺乏组织专业知识,并大声宣布我们新的改进方案将永远处理每个可能用例的所有问题,同时优化资源使用。在六个月内,我们正在寻找一种新的方式来组织一切,因为这个环境“出乎意料地复杂”。我们与这个系统斗争了一段时间,最终放弃了整个工作,寻找新的工作。
许多人在决定使用基础jail时提到的一个因素是优化磁盘空间的使用。这种完全自然的效率倾向并没有错,但与基础jail相结合,它直接导致了组织的噩梦。我见过几个设置,在这些设置中,人们安装一个单一的源jail,从中派生jail以用于不同的包加载,然后根据每个jail的需要,小心地将特定的 /usr/local 和 /var/db/pkg 目录交叉挂载和重新挂载到其他派生jail。是的,它无疑是节省空间的,但只需要交换几GB的磁盘,就会大大增加因管理开销而导致的停机机会。
一旦你发现自己在考虑从衍生jail中衍生jail,退后一步,扇自己耳光,直到你恢复理智。基础jail很复杂。不要增加它们的复杂性。相反,以最简单的方式做这件复杂的事情。
我建议为每种类型的包加载创建一个源jail。假设我有一堆网络服务器jail。它们都运行相同的web服务器软件,但有不同的PHP版本。为您想要的每组可用包创建一个源jail,并为源jail提供一个代表该包集的名称。如果我有一组运行FreeBSD 12和PHP 5.6(现在EOL)的jail,以及另一组运行FreeBSD 12和PHP 7.1的jail,我可能会称之为12php56和12php71。我不会在名称中包含FreeBSD次要版本,因为我将在FreeBSD 12的整个生命周期内升级这些jail。
让我们从创建12php56原始jail开始。将 base.txz 从FreeBSD 12中提取到 /jail/12php56 中:
xxxxxxxxxx
# tar -xpf /jail/media/12.0/base.txz -C /jail/12php56/
现在做基础设置:
xxxxxxxxxx
# cp /etc/resolv.conf /jail/12php56/etc/
# cp /etc/localtime /jail/12php56/etc/
# chroot /jail/12php56/ passwd root
# touch /jail/12php56/etc/fstab
由于我在 jail.conf 中将 mount.fstab
设置为 default
,因此每个源jail都需要一个空白 jail(8)
fstab文件。
xxxxxxxxxx
# touch /jail/fstab/12php56.fstab
现在为这个原始jail创建一个jail.conf条目:
xxxxxxxxxx
12php56 {
ip4.addr="203.0.113.221";
}
如果一切正常,jail应该开始:
xxxxxxxxxx
# service jail start 12php56
Starting jails: 12php56.
jls(8)
命令显示jail正在运行。我做得对吗?呵呵。太酷了!
获取最新的安全补丁:
xxxxxxxxxx
# freebsd-update -f /etc/jail-update.conf -b /jail/12php56/ --currently-running `jexec -l 12php56 freebsd-version` fetch install
现在在jail里安装所需的软件包。确保你把正确的版本放在正确的jail里——在PHP 5.6 jail里安装PHP 7.1会破坏整个努力。
xxxxxxxxxx
# pkg -j 12php56 install apache24 php56 mariadb103-server
从技术上讲,使用PHP 5.6的web服务器有一个完整的原始jail。不过,在这里多做一点努力可以节省你以后的工作。请记住,默认配置文件都位于 /usr/local 中,将从原始jail以只读方式挂载。您的派生jail将需要自己的配置文件来提供许多服务。现在在 /etc/rc.conf 中提供合适的标志和示例将为您以后节省大量时间。例如,Apache允许您使用 -f
标志设置自定义配置文件。在 rc.conf 中添加占位符条目将有助于将来的你感谢现在的你(Future You appreciate Present You)。
xxxxxxxxxx
apache24_flags="-f /etc/httpd.conf"
即使您需要更改配置文件位置,一个好的示例也会有所帮助。
您不需要为所有程序提供替代配置。一些配置,比如 sudo(1)
使用的 sudoers ,可能应该完全从源代码jail进行管理。
现在,让我们从这个起源中推导出一个jail。
让我们建立 www8,一个基于 12php56 的基础jail。派生jail与源jail完全相同,但不包括源jail提供的目录。找出源jail的内容,并为此创建一个fstab。这是www8的fstab, www8.fstab 。它看起来很像任何其他派生jail的fstab,但包含 /usr/local 和 /var/db/pkg 的条目:
xxxxxxxxxx
/jail/12php56/bin /jail/www8/bin nullfs ro 0 0
/jail/12php56/boot /jail/www8/boot nullfs ro 0 0
/jail/12php56/lib /jail/www8/lib nullfs ro 0 0
/jail/12php56/libexec /jail/www8/libexec nullfs ro 0 0
/jail/12php56/rescue /jail/www8/rescue nullfs ro 0 0
/jail/12php56/sbin /jail/www8/sbin nullfs ro 0 0
/jail/12php56/usr/bin /jail/www8/usr/bin nullfs ro 0 0
/jail/12php56/usr/include /jail/www8/usr/include nullfs ro 0 0
/jail/12php56/usr/lib /jail/www8/usr/lib nullfs ro 0 0
/jail/12php56/usr/libexec /jail/www8/usr/libexec nullfs ro 0 0
/jail/12php56/usr/sbin /jail/www8/usr/sbin nullfs ro 0 0
/jail/12php56/usr/share /jail/www8/usr/share nullfs ro 0 0
/jail/12php56/usr/libdata /jail/www8/usr/libdata nullfs ro 0 0
/jail/12php56/usr/lib32 /jail/www8/usr/lib32 nullfs ro 0 0
/jail/12php56/usr/local /jail/www8/usr/local nullfs ro 0 0
/jail/12php56/var/db/pkg /jail/www8/var/db/pkg nullfs ro 0 0
这个派生jail的全部意义在于,它从源jail获取所有包,那么为什么要拖动 /var/db/pkg 呢?用户可以使用 pkg(8)
查询包数据库,省去了用“是的,安装了 php56-xmlwriter,问题是你的坏代码”来回答问题单的麻烦。
我们的派生jail运行FreeBSD 12.0,就像原始jail一样。如果你有一个包含12.0衍生jail的tarball,你可以使用它。如果没有,请从我们的新源jail准备一个。在这里,我将我们的原始jail /jail/12php56 完整地复制到 /jail/www8 中:
xxxxxxxxxx
# tar cfC - /jail/12php56/ . | tar xpfC - /jail/www8/
现在清空源jail将提供的所有目录。记住,不要破坏目录,只破坏目录的内容。您无法在不存在的目录上挂载文件系统。使用 www8.fstab 文件作为检查表:
xxxxxxxxxx
# rm /jail/www8/bin/*
…
# rm -rf /jail/www8/usr/local/*
# rm -rf /jail/www8/var/db/pkg/*
我建议使用完整的文件路径,以避免意外清空主机的 /bin 。
我们从未启动过 sshd(8)
或使用过SSH客户端,因此不需要销毁主机密钥或任何 known_hosts 文件。
现在,您有了FreeBSD 12.0 jail的目录树。保存它,以便下次想要一个jail时可以跳过清除这些目录:
xxxxxxxxxx
# tar cfC /jail/media/12.0-pkgs.tgz /jail/www8/ .
现在在 /etc/jail.conf 中配置派生jail。如果您已将 mount.fstab
定义为默认属性,则只需设置一个IP地址。
现在您已经设置了一个单独的jail并保存了派生jail的tarball,创建其他派生jail只需将tarball提取到另一个目录中,创建一个 fstab ,并创建一个 jail.conf 条目。由于tarball不包含任何软件包,您可以将其用于同一版本的任何源jail。
所有软件都过时了。PHP 5.6已经过了生命周期,最终你会有时间停止使用它。你需要将所有应用程序迁移到较新版本的PHP。与其在原始jail中安装较新的软件并尝试同时迁移所有派生jail,不如将每个派生jail逐一重新设置为运行该软件不同版本的原始jail。将派生jail更改为依赖于运行相同FreeBSD版本的不同源jail是微不足道的。只需编辑派生jail的fstab,即可获取另一个源jail的目录。你可以在jail运行时安全地编辑jail的fstab;jail(8)
可以卸载nullfs挂载,即使fstab中的挂载设备不正确。
是的,改变一个标准base jail的源jail就像全球搜索和替换以及重新启动一样简单。
重设jail的棘手之处在于整合不同FreeBSD版本之间的更改。jail使构建测试服务器变得相当简单,特别是如果你使用ZFS并使用克隆。是的,您必须将应用程序配置为在不同的IP地址上运行,或者可能使用主机文件,但这与jail完全无关。虽然你可以尽量减少一些应用程序的更改,但你仍然需要升级基础jail等。最简单的方法是在originjail上共享 /usr/src 并使用mergemaster(8)
。一旦您执行了其中的一些升级,您将非常清楚可以保留哪些文件,哪些文件需要大规模替换,哪些文件还需要小心处理。