USR不可变操作系统与ParticleOS
ParticleOS是完全使用systemd的不可变基础设施构建起来的不可变元Linux发行版,它可以基于各种Linux发行版进行构建:ParticleOS。
它竭尽可能地使用现成的所有systemd的组件,以实现不可变的/usr/
和基于镜像的操作系统。这样一个系统的特点是:
- 系统以镜像的形式发行和更新,不存在软件包粒度的更新。软件包不再是更新的单元,而是一组系统中的代码。
- 全链路可信任,从引导到磁盘再到应用程序,所有存储都存在加密和完整性检查。
- 自我描述,你总是可以通过一组构建文件构建出一致的系统。
- 自更新,你完全可以对系统镜像进行迭代。
- 稳健可靠,具备容错。
- 可恢复,存在“出厂状态”。
- 系统资源和用户资源严格隔离,用户无权直接操作系统资源。
- 自适应,支持多种环境,包括容器、虚拟机和裸机。
- 仅仅只是一个磁盘镜像,对于安装,写入磁盘即可。
- 系统环境应当是最小化的,使用镜像进行扩展。
- 密钥应当在本地生成而不是预先配置,以防泄露。
- 容易修改且模块化。
- 统一,所有组件都使用统一的格式。
“不可变”
/usr/
是不可变的,这意味着/usr
应当以只读形式被挂载,并且保存最小化的所有必要文件。这意味着应用程序及其配置文件必须都保存在/usr/
目录下,且/usr/
必须是合并的。对于/usr/
以外的目录树,应当根据需要自动生成。这么做的好处在于:
- 可以使用Verity确保整个
/usr/
的完整性。 - 可以轻松地对系统进行A/B原子更新。
- 可以实现恢复出厂设置:只需要擦除除了
/usr/
目录意外的其他目录即可。
分区表
一个这样的系统的分区表应当是这样的:
- ESP。
- 只读的,Verity保护的,版本A的
/usr/
。 - 只读的,Verity保护的,版本B的
/usr/
。 - 可写的根。
所有这些分区都应当通过两个要素确定:
- 分区类型UUID。
- 分区GPT LABEL。
这些信息全部保存在GPT分区表中,所以整个镜像都是自解释的,我们不再需要/etc/fstab
。
启动
对于引导来说,统一内核(UKI)无疑是最佳选择,简单来说,UKI就是封装了initramfs、内核命令行参数、Splash图片和内核的UEFI PE二进制程序。通过这种合并,我们达成了两个目的:
- 简化更新。
- 简化Secure Boot签名。
UKI的每一个版本都恰好与一个/usr/
的版本相关联,两者总是同步更新的。这也就是说,要更新整个系统,也就是这么回事:写入一个新的/usr/
镜像,再更新一下UKI,就完成了。
适合这种场景的Bootloader毫无疑问是systemd-boot,它不仅非常轻量,而且还完全符合上述的引导规范,这意味着它不需要任何显式的配置,只需要放入UKI,它就能够自动识别到,并显示在引导项菜单中。
你可能会好奇在这种场景下内核该怎么知道引导时的根文件系统,解决方案是使用roothash=
和usrhash=
内核命令行参数,它们会被systemd-veritysetup-generator
和systemd-fstab-generator
读取,并在系统启动时进行挂载。
systemd-boot有能力对UKI进行版本排序,并在每次启动时自动启动最新的版本。当然。特定版本的内核也就意味着特定版本的/usr/
文件系统,因为——如前所述,其版本被固化在通过usrhash=
指定的内核命令行参数中。
我们暂时只考虑最简单的情况:不要使用XBOOTLDR
分区,直接将UKI放在ESP。在这种情况下,启动链路为:systemd-boot加载最新的UKI,其中包含了内核和initramfs,以及指向的/usr/
文件系统。
这已经很好了,但是还不够,因为我们还需要一个可写的根文件系统,以包含/etc/
和/var/
层次。这些目录中会保存机密信息(SSH密钥、数据库等等),因此/
很有必要进行加密——通过LUKS2。而且,最好将LUKS2绑定到TPM2芯片,TPM2中包含了一个持久的密钥以用于进行加密,只有在硬件和软件都经过验证时,TPM2才会进行解密。
这样做的好处在于,即使攻击者复制了你的硬盘,也无法启动你的操作系统,攻击者也没有办法通过修改根文件系统以绕过,因为修改不是使用正确的密钥进行的。
再次总结一下:我们现在有了一个可以启动的系统,它的/usr/
文件系统是只读且有Verity保护的,哈希值被固化在经过Secure Boot签名的UKI中,它的/
文件系统是可写的,但是通过TPM2 + LUKS2进行加密,只有在使用经过签名的平台,才能访问密钥。
我们之前提到过,加密密钥必须在首次启动时本地生成,而不能预先配置。但是问题来了:如果我们希望启动我们的系统,/
文件系统就必须存在,因此需要一个已经生成的密钥进行加密。但是,如果我们没有启动,又如何在/
下生成密钥呢?解决方案是,使用systemd-repart工具。这是一种声明式,纯粹累加的分区工具,它可以在initramfs中运行,并在启动时创建分区和文件系统,然后切换到根文件系统。此外,systemd-repart还可以对它创建的分区进行加密,并自动注册一个TPM2绑定的密钥。
回到开始,我们初始发布的镜像中,实际上只包含两个部分:
- ESP。
- 不可变的、Verity保护的、有签名的,版本A的
/usr/
文件系统。
就这么多,在一开始没有根文件系统,也没有B版本/usr/
文件系统。只有这两部分。然后,在用户第一次启动时,systemd-repart会注意到根文件系统不存在,然后进行创建、加密和创建文件系统,并将密钥注册到TPM2。systemd-repart还会创建第二个/usr/
分区,在之后用于AB更新(一开始为空,直到第一次更新发生)。完成后,initramfs将切换到根文件系统,并挂载/usr/
文件系统。
/usr/
文件系统中应当保存所有的systemd-tmpfiles和systemd-sysuser信息,对应的服务会据此配置根文件系统,创建所有必要的文件、目录和软链接。
更优雅的是,systemd-repart创建的根文件系统的大小是可以根据存储空间的五粒大小动态调整的,这使得镜像可以最小化。
出厂化
无论你是打算将电脑转手给其他人,还是想在紧急情况下恢复系统,都会希望存在工厂重置的逻辑。systemd-repart具备一个机制,在系统分区的配置中,可以通过特定的配置项将其标记为在工厂重置时进行删除。
实际的工厂重置应当可以通过两种方式触发:
- 通过特定的内核命令行参数(不太有意义,因为我们的内核命令行参数被封装在了UKI中)。
- 通过一个EFI变量。
在工厂重置的过程中,systemd-repart会擦除标记为可以进行工厂重置的分区。一旦完成,系统就会被恢复到最初状态:只有ESP和/usr/
文件系统,根文件系统已经完全消失。
如此一来,我们就可以重新创建一个新的根文件系统。
模块化
如果,/usr/
文件系统禁止写入,那么在传统意义上,我们就没有办法安装新的软件包。所以我们有两个表面上看起来矛盾的目标:
- 模块化,即能够向系统中添加组件。
- 不可变。
要提出解决方案,首先需要分析一些用例:
- 在最低层次对系统本身的扩展,它们与系统镜像中的资源应当处于同一命名空间,并受到相同的限制。举例来说,一个调试器或是一个硬件驱动。
- 独立的服务,它们运行在自己的命名空间中,但是仍然作为系统组件工作,并为其他应用程序提供服务。这种资源主要关注于为系统提供IPC API的能力。这种类型的组件仍然可以具有很高的特权,但集成的程度明显小于第一种类型。举例来说,一个VPN连接服务。
- 操作系统的实际载荷,这些内容与操作系统相对隔离。它们主要使用系统API,但不提供API。这类组件以最小权限运行,并隔离在自己的命名空间中。
当然,这三种模块之间的界限是模糊的,但是区分它们是有意义的,因为每种模块适合不同的机制。因此,建议是这样的:
- 对于系统扩展的情况,使用
systemd-sysext
镜像。该工具操作的系统扩展镜像与主机的磁盘镜像非常类似,它们也包含一个/usr/
分区,并启用Verity保护。不过,Sysext镜像仅仅包含对主机镜像的附加内容,即扩展主机的二进制文件。当Sysext镜像被激活时,它们会通过一个不可变的Overlayfs被挂载到主机的/usr/
树中。因此,任何在Sysext镜像中提供的文件都会立刻出现,就像它们本来就是主机的操作系统的一部分。不过,系统扩展镜像应当在主机操作系统本身的更新时一同构建,因为扩展镜像中的文件会依赖操作系统镜像中的文件。 - 对于相对独立的系统服务,便携式服务是首选。便携式服务在大多数方面都和常规系统服务类似,但是,它们在单独的磁盘镜像中运行,因此处于独立的命名空间中。便携式服务的镜像也包含不可变的
/usr/
文件系统,且启用了Verity保护。 - 最后,是操作系统的实际载荷,即应用程序。现有的生态系统中,最常用的格式是Flatpak和OCI容器。与系统扩展和便携式服务不同,这些应用程序的完整信任链难以合理实现。
这里需要强调的是,主操作系统镜像、系统扩展镜像和可以指服务镜像的格式是相同的:都是GPT磁盘镜像,具有一个不可变的/usr/
文件系统和相关的Verity数据。后两者还需要提供顶级Verity哈希的签名。这种统一性使得我们可以使用相同的工具来构建和处理所有这些镜像。最重要的是,通过单一的验证方式,验证变得简单明了:内核原生支持Verity签名检查(IMA子系统)。
通过在运行时从主机镜像、扩展镜像和便携式服务镜像组合系统,我们拥有一个良好模块化的系统,其中每个组件在每个输入输出操作中都经过加密验证,并且每个组件都直接在内核的IMA子系统中进行测量。
当然,对于桌面应用程序和OCI容器来说,这些性质会丢失,不过,这也足够了。
系统扩展镜像不支持依赖是有原因的:它并不是为了复制包管理器的细粒度打包逻辑,而是为了提供更大、更粗糙的功能集合的交付,与操作系统的生命周期保持一致。
便携式服务也可以和系统扩展镜像组合使用,这使得我们可以在多个便携式服务之间共享一个公共的运行时,并通过一个Overlayfs进行组合。
二级操作系统
一个不可变的宿主操作系统很棒,但是如果可以在某些情况下偏离不可变的目标也是有用的——即,提供一个桥梁,允许传统的包管理在这种情况下使用。
对于这个目的,建议使用systemd-nspawn容器。这些容器专注于操作系统虚拟化,可以以有效载荷的形式运行完整的操作系统镜像(和单服务的Docker容器不同)。
systemd-nspawn容器具备多种良好的特性,例如,它支持与主机本身相同级别的加密镜像验证。
开发模式
不可变的/usr/
文件系统使得我们写入自己的代码变得困难,因为我们不太可能拥有签名密钥的访问权限。
为了解决这个问题,有两种办法。第一种是允许用户通过本地开发者系统扩展来附加扩展/覆盖操作系统。在这种方案下,底层的加密验证将保持完整,但如果这种开发模式被明确启用,开发者可以从本地存储中添加更多资源,这些资源不受操作系统构建者的信任链的限制,而是受本地的信任链的限制。
第二种方法是,使扩展(或替换)受信任的验证密钥集变得简单,使用用户控制的本地密钥,以便能够轻松操作由本地开发者签名的内核、操作系统、扩展、便携服务或容器镜像,而无需操作系统构建者的参与。对于信任链下游的组件相对容易做到,即信任链上更高层的元素可以选择允许额外的证书以进行验证。
签名
Linux发行版中UEFI Secure Boot签名的现状相当悲惨。为了获得签名所做的工作是大量的,但实际上回报很少:因为initramfs完全没有保护,并且位于缺乏任何形式的加密完整性保护的分区上,任何攻击者都可以轻松地修改任何此类Linux系统的启动过程,并自由收集输入的FDE密码短语。
通过使用统一内核,这个重要的差距得以弥补,因此UEFI Secure Boot签名成为从固件到主机操作系统的引导链的一个环节。
容器化运行
在裸金属或虚拟机上启动磁盘映像通常意味着UEFI固件验证并调用引导加载程序,而引导加载程序调用内核,然后过渡到最终系统。这对于容器来说是不同的:在这里,容器管理器立即调用初始化系统,即PID 1。因此,验证逻辑必须不同:加密验证必须由容器管理器完成。
在这个模型中,操作系统镜像中不仅需要包含Verity数据分区(如上所述,为UEFI SecureBoot信任链所必需),而且还必须和另一个分区一起打包,该分区包含Verity分区的根哈希的PKCS#7签名。这样,操作系统镜像本身有两种“进入”信任链的方法:要么通过ESP中签名的统一内核(用于裸金属和虚拟机启动),要么通过使用存储在分区中的PKCS#7签名(用于容器启动)。
参数化内核
一个完全不可变且已签名的操作系统必须在使用用户数据之前建立对信任。对于/etc/
和/var/
,我们通过对根文件系统进行磁盘加密(结合完整性检查)来实现这一点。但是,根文件系统挂载的时机相对较晚,因此无法用于参数化引导过程。然而,在许多情况下,能够参数化引导过程是很重要的。
如果initramfs是由供应商预构建并与内核一起整体签名的,它就无法被直接修改以直接携带这些数据。解决方案是使用系统凭据(Credentials),这些凭据允许以加密和认证的方式将参数传递给系统(以及相关服务),并绑定到TPM2芯片。这意味着我们可以安全地将数据传递到initramfs中,以便它只能在其预定的系统上进行认证和解密,并与其UKI一起使用。
交换分区
交换分区是必要的,原因很简单,只有这样systemd-oomd.service
才能提供最佳结果。参考为交换分区辩护。
更新
我们现在对系统的结构有了大致的想法,接下来让我们关注部署周期:软件需要定期更新,未定期更新的软件存在安全问题。因此,任何现代系统都必须自动更新,而不需要用户进行可避免的交互。
这是systemd-sysupdate的工作。它是一个相对简单的A/B镜像更新器:它可以在分区、目录中的常规文件或目录中的子目录上操作。每个条目都有一个版本(对于分区,这个版本编码在GPT分区标签中,对于常规文件和目录,这个版本编码在文件名中):每当启动更新时,最旧的版本会被删除,最新的版本会被下载。
通过上述设置,系统更新变得非常简单。在每次更新时,systemd-sysupdate工具下载一个/usr/
文件系统分区,一个配套的Verity Hash分区,一个PKCS#7签名分区,并将其放入主机的分区表中(可能替换掉存储的最旧版本)。然后,它下载一个统一的内核映像,并将其放入EFI系统分区的/EFI/Linux
中(根据引导加载程序规范;可能会删除那里最旧的文件)。
这就是整个更新过程:从服务器下载四个文件,解压并以最简单的方式放入分区表或文件系统。与其他操作系统设计不同,不需要显式切换到新版本,上述systemd-boot逻辑会在新内核放入后自动选择最新的内核。
系统扩展镜像和便携式服务镜像在考虑更新时也非常方便:我们可以使用与主机镜像相同的systemd-sysupdate工具来更新这些镜像。磁盘格式的一致性使我们能够统一地进行更新。
启动评估
自动化操作系统更新并非没有风险:如果它们自动发生,而更新出现问题,这可能意味着系统可能会变砖。这当然不是理想的情况。因此,合理地自动处理这一问题至关重要。
systemd具备自动启动评估机制。这个机制很简单:每当一个新的UKI被放入系统时,它将以文件名中包含的小整数计数器值进行存储。每当UKI被systemd-boot选择启动时,计数器减一。一旦系统成功启动(由用户空间确定),计数器将从文件名中移除(这表示“此条目已知可用”)。如果计数器达到零,这表明它尝试启动了几次,每次都失败,因此显然是“坏的”。在这种情况下,systemd-boot将不再考虑该内核,并恢复到下一个较旧的内核(计数器不为零的)。
主目录
设备的用户及其主目录由systemd-homed管理。这意味着它们可以在设备之间轻松迁移。每个用户的数字UID分配仅在登录时进行,主目录中的文件根据需要通过uidmap
挂载进行映射。
它还允许我们使用属于用户本身的凭据单独保护每个用户的数据。即,用户的数据的机密性并不是绑定到系统范围的全盘加密,而是每个用户都有自己的加密主目录,其中用户的身份验证令牌(密码、FIDO2令牌、PKCS#11令牌、恢复密钥……)用作用户数据的身份验证和解密密钥。这为安全性带来了重大改善,因为这意味着用户的数据在用户实际登录时是加密不可访问的。
它还允许我们纠正传统 Linux 系统的另一个主要问题:系统挂起期间数据加密的工作方式。传统上,在Linux上,磁盘加密凭据(例如LUKS密码)在系统挂起时仍保留在内存中。这对安全性来说是一个糟糕的选择,因为我们中的许多人可能从未关闭他们的笔记本电脑,而是选择挂起。但是,如果在挂起期间解密密钥始终以未加密的形式存在,那么它可能会被攻击者从中读取。
通过使用用户的身份验证令牌加密用户的主目录,我们可以在进入系统挂起状态之前安全地“挂起”主目录(即清除访问所需的加密密钥)。这意味着当前访问主目录的任何进程在挂起期间将被冻结,但在系统挂起周期中这是预期的。为什么这比现状更好?在这个模型中,主目录的加密密钥材料在挂起期间被擦除,但可以在恢复时从系统代码安全地重新获取。然而,如果系统仅作为一个整体进行加密,那么系统代码本身也无法重新验证用户,因为它也会被冻结。通过将主目录加密与根文件系统加密分开,我们可以避免这个问题。
分区设置
我们已经多次讨论了操作系统的分区结构,但是每次都只涉及了特定的方面,现在让我们总结一下。
初始的操作系统镜像应该如下所示:
- 一个ESP,使用systemd-boot作为Bootloader,保存UKI。
- 一个不可变的
/usr/
数据分区,带有标签系统名称_版本号
(版本A)。 - 与
/usr/
数据分区具备相同标签的Verity Hash分区。 - 一个携带了
/usr/
数据分区的Verity根哈希及其PKCS#7签名的分区,具备相同标签。
在用户首次启动后,分区会多出:
5. 第二个/usr/
数据分区(版本B),初始标签为_empty
(当前没有有效存储内容的分区)。
6. 第二个/usr/
数据分区的Verity Hash分区,标签也为_empty
。
7. 第二个/usr/
数据分区的带有PKCS#7签名的Verity根哈希分区,标签为_empty
。
8. 一个根文件系统分区,已经加密并且密钥锁定在TPM2.
9. /home
文件系统,通过TPM2中的密钥进行完整性保护(加密是不必要的,systemd-homed可以承担)。
10. 交换分区,已经加密并且密钥锁定在TPM2.
然后,在第一次操作系统更新时,分区5、6、7会被填充新版本的操作系统,因此,它们的标签被更新为系统名称_新版本号
,重启后,systemd-boot会自动应用该版本。在下下次更新中,版本A的分区会被替换为系统名称_更新版本
,并在下下次重新启动时应用。
在恢复出厂设置时,分区8、9、10会被删除,然后在重新启动时,systemd-repart会使用一组新的密钥重新创建。
如图所示:
信任链
再来总结一下信任链(对于裸机/虚拟机),该链路中的每一段代码都经过签名和验证,并且人么密钥都被锁定在TPM2中。
- 固件(或Shim)验证systemd-boot。
- 一旦systemd-boot选择了一个UKI,它也会传递给固件/Shim进行验证。
- UKI中包含一个initramfs,这是第一个被加载的用户空间组件,它查找传递到initramfs的任何系统扩展,并通过Verity设置它们。内核会验证这些系统扩展镜像的Verity根Hash签名。
- initramfs还会找到传入的凭据,然后使用TPM2芯片中的密钥安全解锁。
- UKI中还包含一个内核命令行参数,其中包含一个
usrhash=
,用于确定要使用的/usr/
卷的根Hash。 - initramfs随后使用TPM2芯片中的密钥解锁加密的根文件系统。
- 随后,进入主系统,即受Verity保护的
/usr/
与加密的根文件系统的组合。随后还会使用TPM2芯片中的密钥激活两个额外的卷:/home/
文件系统和交换分区。
如图所示:
系统扩展镜像、便携式服务镜像、 systemd-nspawn容器镜像的验证与之不同:内核将这些Verity镜像及其PKCS#7签名与内核的密钥环直接进行验证。
文件系统选择
对于不可变的/usr/
文件系统,squashfs
(或erofs
)是不错的选择,但是任何其他以只读方式良好工作的文件系统也可以。
由systemd-homed管理的家目录毫无疑问应当使用Btrfs,因为它是唯一支持在线扩展和收缩的通用文件系统,systemd-homed可以利用这一点来管理存储。
对于根文件系统来说,Btrfs可能也是最好的选择,因为我们需要对其进行加密,但是,DM-Crypt只提供数据的机密性,而不提供数据的真实性(该功能由DM-Integrity负责)。由于Btrfs自身会进行完整的数据校验,因此它是在不使用DM-Integrity时的最佳选择。
安装与实例化
对于这样的操作系统开始,安装仅仅代表着使用dd
克隆到磁盘中。很简单。
当然,对于很多实际情况来说,这种方式不够用。比如对于多引导的情况,dd
会覆盖原本的数据。
为了这种情况,我们可以使用systemd-repart来以更智能,增量的方式把镜像克隆到目标磁盘中。毕竟,这个工具是纯粹的增量工具:它只会添加分区或在分区缺失或过小的情况下扩展它们。
systemd-repart已经具备了所有必要功能:不仅可以在目标磁盘上创建分区,还可以从原始安装磁盘复制块。安装操作将变成两步:调用systemd-repart,将/usr/
、其Verity和签名分区添加到目标介质中,并填充与安装介质相同的分区副本。然后调用bootctl
,将systemd-boot启动加载程序安装到ESP中。(好吧,这里少了一件事:统一的操作系统内核也需要放入ESP。目前,这可以通过简单的cp
调用来完成。从长远来看,这可能也应该是bootctl
负责的,如果被告知的话。)
所以,通过这个方案,我们有一个简单的方案来覆盖所有场景:
- 我们可以选择将镜像
dd
到磁盘。 - 或者我们可以将图像流式传输到现有的硬盘驱动器上,再向ESP添加几个新的文件。
当然,实际上事情比这更复杂:现有的ESP可能太小,无法容纳多个UKI。解决这个问题的方法是提供两个略有不同的systemd-repart分区定义文件集:理想情况下ESP足够大,以及一个后备情况,即ESP不够大,我们在此情况下添加一个额外的XBOOTLDR分区。在这种模式下,ESP承载引导加载程序,但UKI存储在XBOOTLDR分区中。这个场景并不像最初描述的没有XBOOTLDR的场景那么简单,但在各种工具中同样得到了很好的支持。
无论分区如何,Bootloader和UKI怎么安装在系统的硬盘上,在第一次启动时,路径都会相同:systemd-repart将被调用以创建根文件系统,并正确加密,如前面所讨论的。这意味着:用于磁盘加密的所有加密密钥材料仅在第一次启动时生成,安装阶段不加密任何内容。
Live系统、安装器系统与已安装系统
传统上,在Linux发行版上有三种常见的系统类型:“已安装”系统,即存储在设备主存储上的系统,是人们花费时间的主要地方;“安装器”系统,用于安装它们,其工作是复制和设置构成系统的软件包;以及“Live”系统,这是一种折中方案:在大多数方面表现得像已安装系统,但存储在可移动介质上。
我们希望尽可能消除这三个概念之间的区别:这三种镜像应该携带完全相同的/usr/
文件系统,并且应该适合以相同的方式进行复制。一旦安装,生成的镜像还可以作为另一个系统的安装程序,依此类推,产生某种“病毒”效应:如果你有一个镜像或已安装系统,它天然就是你可以通过简单的systemd-repart调用以1:1方式复制的东西。
构建镜像
说了这么多,如何根据此模型构建操作系统实例的初始镜像呢?
这些镜像没有什么特别之处:它们最终只是带有Linux文件系统的GPT磁盘镜像,遵循可发现分区规范。这意味着你可以使用任何工具集来构建符合要求的GPT磁盘镜像。
推荐的工具是MKOSI,它已经具备了丰富的工具集,可以覆盖以上所有的需求。
关键在于,这个模型并没有脱离包管理器,而是建立在它们之上:在这个模型中,我们可以在构建主机上组合镜像,但部署到运行时主机并不涉及单独的包。不能低估传统发行版在安全性、集成性和打磨方面带来的价值。但是,我们完全可以偏离将软件包视为运行时概念的想法,而是将其转变为构建时概念。
总结
通用发行版不会很快的将这种结构作为默认选项,这甚至不是我(波特林)的目标,这只是我的个人愿景,不指望人们完全认同。但是,我感兴趣的是找到和其他人的重合点,并共享想法。
我的目标因此是:
- 将发行版转换为一种镜像模型,这样的镜像可以轻松的从发行版构建。
- 找到重合点,分享其他项目的组件,以重新审视发行版是怎么组合在一起的。
- 让人们对基于通用发行版构建一个真实可用的镜像提起兴趣。我希望能有一个具备完全信任链的镜像,基于现有的Linux发行版构建,如Fedora和Arch。
FAQ
- 配置管理怎么办?
乍一看不可变系统似乎没办法进行自定义配置,但是,我们的模型中只有/usr
是不可变的,根文件系统的其他所有内容,包括/etc/
和/var/
实际上是可写的。这意味着传统的配置管理方式是可行的,但是软件包管理器不可用——因为它们需要写入/usr/
文件系统。所以:软件包需要在镜像构建时安装。 - 非UEFI + TPM2的系统怎么办?
上述内容都是围绕现代PC的功能集设计的,因此很大程度上依赖UEFI和TPM2。不过如果希望在非UEFI和非TPM2的硬件上实现类似的功能,实际上并不困难,因为上述大部分内容并不严格依赖UEFI或TPM2,对于许多情况我们都可以找到合理的替代方案——当然,缺少TPM2会削弱需许多安全系数。