上周末因为在学习 GnuPG 时更换新的 Yubikey 丢失了 LUKS 的密钥,备用密钥也用不了,我只能被迫重装系统。理论上,NixOS 重装的速度很快。刚好上次挑战重装 NixOS 的时候没有写博客记录,这次写一篇博客记录一下整个过程。

备份数据

幸好我在重启系统前意识到自己丢失了解密密钥,并开始着手备份,否则我就丢失全部的数据了。

备份的过程并不复杂。我 90% 的配置都写进了 flake 里。我并没有直接 persist 整个 .config 目录或 .local 目录,对他人来说不方便 Nix 化的 KDE 配置等桌面应用都被我手动 Nix 化了。剩下 10% 的这些配置不方便 Nix 化的、和配置和缓存混在一起的应用,比如各种电子应用、浏览器等,这些软件的目录都被我手动设置了 persistence ,稍做清理就可以直接打包了。

不过如何打包也是一个问题。除了备份文件的数据本身,对于一些要求严格的软件,文件的权限位也很重要。这种需求可以用和 Linux 相性最好的 tarball 来备份。使用这条指令给 persist 目录打 tarball :

tar -cvpf home.tar --atime-preserve --exclude exclude-path .config/ .local/

-c 创建新的 tarball 。

-v 打印详细信息。

-p 保留权限位。

-f 指定 tarball 文件路径,后面跟文件路径。

--atime-preserve 保留访问时间。

--exclude 打包时排除指定路径。

指令最后跟需要打包的目录,可以指定多个。

对临时备份而言,不压缩可以加快备份和恢复的速度。

关于 exclude 参数的几个要点:

  1. exclude 后面的路径不要跟斜杠 / ,否则 exclude 无效;

  2. 打包的目录使用相对路径,排除的文件只能接相对路径;

  3. 打包的目录使用绝对路径,排除的文件接相对路径或绝对路径;

建议打包的目录和排除的文件使用的使用路径时,保持一致,都是用绝对路径,或都是用相对路径。

刚才只备份了家目录下的数据。除了家目录,根目录下也可能会有需要备份的数据,比如 lanzaboote 的 EFI 证书。

备份文件建议放到 TTY 下方便访问的媒介,比如 U 盘或移动硬盘。并且把自己的 flake 完整复制一份到备份媒介中,方便安装使用。

准备 LiveCD

备份完成后,重启进入 NixOS LiveCD。

修改 Substituters

和 ArchISO 改软件源一样,首先修改 substituters 。LiveCD 下的 nix.conf 文件是 ro 且 symlink 到 ro 的文件系统的,需要先复制一份才能修改。

cp /etc/nix/nix.conf /etc/nix/nix1.conf
rm /etc/nix/nix.conf
mv /etc/nix/nix1.conf
chmod +w /etc/nix/nix1.conf

修改 substituters 项,添加中科大源和 garnix 的 cache 。别忘了最后还要把官方源添加上。

substituters = https://mirrors.ustc.edu.cn/nix-channels/store https://cache.garnix.io https://cache.nixos.org

中科大源同步的是官方的 cache ,和官方共用一个 key 。但 garnix 需要添加 garnix 的 key 。在 trusted-public-keys 项后追加 garnix 的公钥。

trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g=

连接网络

安装 NixOS 的过程中很可能需要访问外网,此时使用代理可以加速访问。需要一个额外的设备提供代理 HTTP / SOCKS5 代理,然后在本机设置系统代理:

proxy="http://192.168.0.2:1080"
export ALL_PROXY="$proxy"
export HTTP_PROXY="$proxy"
export HTTPS_PROXY="$proxy"
export all_proxy="$proxy"
export http_proxy="$proxy"
export https_proxy="$proxy"

安装前准备

给电脑重新分区,过程与 Arch Linux 无异。注意分区需要与 flake 中设置的旧系统的挂载设置一致,或现场修改为新的挂载设置。

这里推荐用 GPT 分区表的 partlabel 来定位分区。设置 partlabel 可以在分区完成后执行:

sgdisk --change-name=1:BOOT /dev/nvme0n1

1 需要修改的分区序号 BOOT 指定的 partlabel

之后就可以用 /dev/disk/by-partlabel/PARTLABEL 来定位分区了,这个功能兼容所有的 FS 且不会因 mkfs 而且丢失。

分好区后挂载分区。

重置 age 密钥

如果使用了 sops-nix 或 agenix 之类的工具加密自己的系统配置,别忘了重置自己的密钥。从旧系统中复制旧的密钥并不是好文明,建议就地创建新密钥并重新加密。

我自己糊了一个脚本自动化实现这个过程1

恢复 lanzaboote 密钥

需要提前恢复 lanzaboote 的 EFI 证书和密钥才能在安装的过程中签名。注意需要复制两份,一份复制到 persist 目录中,另一份复制到新系统根目录下。

安装系统

之后就可以执行 nixos-install 开始正式安装。这个过程不需要人工介入,视网络质量和系统配置的复杂程度可能需要花费半小时到一小时不等。

安装完别忘了恢复其他数据。恢复完数据后,重启就是和旧系统一模一样的新系统了。

总结

有了上次重装的踩坑,这次重装的耗时明显缩短了,大约花了六小时。其中近一半的时间花在了备份上,谨慎一点并不是什么坏事。还有一小时花在了写脚本上。实际花在安装系统上的时间不超过两个小时。

之后或许可以优化一下备份流程,这样就可以以更快的速度完成重装。

参考资料

  1. https://github.com/A1ca7raz/flamework/blob/main/scripts/rotate_sops_keys