把 /home、/var 和 /usr 塞进加密分区,留一个 4096 MB 的明文根分区负责开机——这是 NetBSD 开发者近期在官方站点给出的一套磁盘加密折中方案。它的出发点很务实,既然 /bin/sh 这类基础系统二进制文件全网公开且一致,花力气加密它们纯属多余,不如只锁住 11263 MB 的第二分区来装用户数据。但这套看似聪明的极简操作,如果原封不动照抄到生产或随身设备上,防盗机密性会直接漏底。

问题出在操作系统底层的数据流动根本不按目录边界走。原文为了兼容标准安装器与 /rescue 救援工具,省掉了全盘加密 ramdisk(cgdroot)的繁琐改造,却在实操脚本里留下了三处明文缺口。同时,NetBSD 10.0 虽然为加密设备驱动 cgd 补齐了 Adiantum 宽块加密和 Argon2id,但命令行工具的历史默认参数依然埋着密码学降级的暗雷。

4GB 明文根分区的极简算盘与三处漏水口

类 Unix 系统做全盘加密一直有个引导悖论:内核还没加载,谁来弹窗让你输密码?NetBSD 传统的 cgdroot 方案是用一个微小的未加密内存盘(ramdisk)先跑起来,解锁后再切换根文件系统,但这会大幅改动启动流程,系统崩了连 /rescue 里的静态工具都不好使。

4GB明文根分区与未加密交换区留下数据泄露隐患
4GB明文根分区与未加密交换区留下数据泄露隐患

指南作者换了个思路。BSD 系统天生把基础系统与第三方包(/usr/pkg、/usr/local)严格分离,根目录下的系统文件平时几乎只读。于是作者在 sparc64 虚拟机上划出 4 GB 明文根分区保启动,把 /home、/var 和 /usr 全部拷进 11 GB 的 cgd 加密分区 /encfs,再用软链接指过去,最后靠 /etc/rc.conf 里的 critical_filesystems_local="/encfs" 在启动早期挂载。至于 /etc 里的 Wi-Fi 或 VPN 密码,改一下 wpa_supplicant_flags 参数或者做个软链接就能挪进加密区。

NetBSD 折中加密方案的目录切分与三处明文漏出 方案保护区(11263 MB cgd) /home 与 /usr(软链接指向 /encfs) /var(运行日志与常规数据库) 4096 MB 明文根分区仅留公开二进制 实际裸奔区(被忽略的底层通道) 旧目录残留:/home2、/var2 明文未擦除 独立 Swap 分区:内存换出页直接落盘 初始崩溃转储:先写 dump 分区再进 /var

账面逻辑很顺,但一落到实操脚本,机密性闭环立刻破功。

最扎眼的是数据迁移收尾动作。指南为了稳妥,用 /rescue/mv 把原本的明文目录改名为 /home2、/var2 和 /usr2,接着就宣告大功告成,全文没有任何安全擦除步骤。这意味着装完系统后生成的所有初期文件、SSH 密钥或配置,依然原封不动地躺在 4 GB 明文根分区的旧目录里。

更隐蔽的漏水口在内存交换与崩溃转储。指南在分区表里划出了独立的 swap 分区(sparc64 示例中的 wd0b 和 GPT 示例中的 dk1),却完全没给 swap 配加密。只要系统内存吃紧,正在处理的敏感文档或密钥材料就会被内核换出到明文 swap 扇区。同样,作者特意提到 /var 里的内核崩溃转储(crash dump)包含敏感内存快照,所以要把 /var 加密;但 NetBSD 的真实机制是,内核崩溃瞬间会先把内存镜像直接写进独立的 dump 设备(通常是 swap 分区),等下次重启跑 savecore 时才抄进 /var/crash。只锁 /var 不锁 dump 分区,等于把保险柜门焊死,却把备用钥匙丢在门垫下。

  • 风险.照搬该指南的管理员必须立刻在 /etc/sysctl.conf 中开启 vm.swap_encrypt=1 或为 swap 配置随机密钥 cgd,彻底禁用未加密 dump 设备,并安全擦除 /home2 等残留目录。

cgd 引擎盖下的现代密码学与默认参数暗雷

把视线从分区表移开,这则指南倒带出了一个真正值得关注的技术进展:NetBSD 的磁盘加密底座已经悄悄完成了一次现代密码学换代。

很多跑 NetBSD 的设备是老旧的 sparc64 工作站或低功耗 ARM 开发板,CPU 根本没有 AES-NI 硬件加速指令集,纯靠软件跑 AES 磁盘加密会把 I/O 拖垮。2024 年 3 月 28 日发布的 NetBSD 10.0 正式为 cgd 引入了 Adiantum 算法与 Argon2id 密钥派生。根据 Adiantum 算法作者公布的基准测试,在 ARM Cortex-A7 处理器上解密 4096 字节消息的速度达到 10.6 cycles/byte,比 AES-256-XTS 快 5 倍以上。虽然这是 4096 字节的算法级测试而非 cgd 在 512 字节扇区下的实测吞吐量,但足以让老机器摆脱算力瓶颈。

Adiantum 带来的不只是快,还有加密粒度的质变。传统 aes-xts 按 16 字节块加密,攻击者哪怕解不开数据,通过比对密文也能看出 512 字节扇区里的哪一个 16 字节块被改写过;而 Adiantum 是宽块(wide-block)加密,以整个 512 字节扇区为粒度做搅动,只要扇区里改动一个比特,整扇区密文全变。


但好武器配了旧准星。受制于历史兼容性包袱,cgdconfig 命令行工具至今留着两个反直觉的配置陷阱,不翻手册的管理员极易踩空:

配置维度表面写法或默认行为实际底层机制正确实操配置
密钥派生 (KDF)不传 -k 参数的默认状态默认跑老旧的 pkcs5_pbkdf2/sha1,抗 GPU 暴力破解弱必须显式指定 -k argon2id
XTS 密钥长度aes-xts 256XTS 需把密钥拆半,实际仅等效于双 AES-128 强度追求 256 位强度须写 aes-xts 512 或 adiantum 256

跳出分区执念:为什么全盘加密也防不住“邪恶女仆”

看完这些细节,我对这类折中加密指南的看法很明确:它解决的是设备在咖啡馆被顺走后的被动防窥,但凡攻击者有机会接触机器五分钟再还回来,这套防线和全盘加密一样脆弱。

缺乏完整性校验的磁盘加密无法抵御底层物理篡改(示意图)
缺乏完整性校验的磁盘加密无法抵御底层物理篡改(示意图)
密文没有签名校验,锁住再多目录也是给篡改者留后门。

在密码学与系统安全里,机密性(Confidentiality)和完整性认证(Authenticity)是两码事。韩非子在《五蠹》里写过一句很冷酷的话:“恃人之所以为我者,危矣;恃吾不可害者,安矣。”放在磁盘加密上,指望小偷只偷机器不改引导扇区,就是把安全寄托在对手的仁慈上。

横向对比三大开源系统的启动信任链,就能看清“加密了几个分区”其实是个伪命题:

三类系统磁盘加密的启动暴露面与防篡改边界 NetBSD (cgd / cgdroot) 暴露面:明文根分区或 未签名内核 + ramdisk 无密文完整性校验 物理接触可植入木马截密码 FreeBSD (默认 GELI) 暴露面:内核与 /boot 加密 仅暴露 EFI 分区 loader.efi 默认不开启篡改检测 替换未签名 loader 照样破功 Linux (LUKS + 签名 UKI) 暴露面:内核 + initramfs + 启动参数打包统一签名 Secure Boot 硬件级验签 输密码前阻断引导链篡改

NetBSD 的 cgd 官方手册写得很坦白:它只提供对抗被动攻击者的机密性,不提供密文完整性认证(Linux 常规 LUKS 与 FreeBSD 默认 GELI 同样不默认开启扇区级完整性校验,因为每写一个扇区都要额外算并存一串 MAC 标签,I/O 损耗太大)。

这就引出了著名的“邪恶女仆攻击”(Evil Maid Attack)。在这篇指南的方案里,4 GB 根分区里的 /sbin/init 和 /bin/sh 都是明文,攻击者拿走笔记本改掉二进制文件,你下次开机输密码时等于亲手把钥匙交出去。那改用 NetBSD 的 cgdroot 把根分区全锁上呢?引导程序、内核和 ramdisk-cgdroot.fs 依然是明文。再退一步,学 FreeBSD 的 GELI,用支持解密的 loader.efi 在加载内核前就要密码,把 /boot 和内核也锁进加密盘呢?只要那个索取密码的 loader.efi 没有经过硬件级签名校验,攻击者照样能替换它。

真正能挡住这一下的,不是把加密分区的地盘画得多大,而是像现代 Linux 发行版那样,用启用 Secure Boot 的固件去校验打包了内核、initramfs 和启动参数的签名统一内核镜像(UKI)。

  • 结论.在 BSD 生态补齐硬件级签名引导链之前,如果真要防范物理接触篡改,唯一靠谱的土办法是把那 4 GB 明文引导环境装在随身携带的独立 U 盘上,做到人走引导介质走。

回看这份标着最后更新于 2026 年 11 月 14 日的 NetBSD 开发者指南,它把 BSD 系统结构干净、目录职责分明的工程美感展现得很透彻,用软链接和挂载顺序就四两拨千斤地绕开了全盘引导改造。但安全工程最怕这种只看目录树的局部精巧。当 cgdconfig 的默认参数还停留在 PBKDF2-SHA1,当安装器 sysinst 还没把 swap 加密和残留清理做成默认闭环,盯着 /home 锁死大门却任由内存换出页在明文扇区里裸奔,这种省事的折中方案,终究只防君子不防内鬼。