在 Hetzner 上配置 NixOS RAIDZ 全盘加密

Tags: NixOS

1. 使用 kexec 进入 NixOS live 环境

从 Rescue 环境启动后,按照 NixOS Wiki 的指示,通过 kexec 进入 NixOS live 环境。

需要注意的是,进入 NixOS live 环境后,会有一个 pending 的 reboot(我猜是为了防止机器失联),因此我们需要把它取消掉:

1
shutdown -c

2. 在 NixOS 中对磁盘进行分区

我的 Hetnzer 杜甫上有四块硬盘,考虑到我的杜甫一般是 Legacy 启动,如果需要构建 RAIDZ 的话,每块磁盘都需要如下的分区结构:

  1. 1MB: BIOS boot partition
  2. 2GB: /boot
  3. Rest: RAIDZ

理论上也可以开工单给 Hetzner 让他们帮忙修改为 UEFI 启动,这样只需要在每个磁盘上创建一个 mirrored 的 ESP 分区即可,但我比 较懒不想等工单 23333 。

具体分区脚本参考了 mazzo.li

1
2
3
4
for disk in /dev/sda /dev/sdb /dev/sdc /dev/sdd; do
parted --script $disk mklabel gpt
parted --script --align optimal $disk -- mklabel gpt mkpart 'BIOS-boot' 1MB 2MB set 1 bios_grub on mkpart 'boot' 2MB 2000MB mkpart 'zfs-pool' 2000MB '100%'
done

接着,将所有的 /boot 分区格式化为 vfat:

1
mkfs.vfat /dev/sda2 && mkfs.vfat /dev/sdb2 && mkfs.vfat /dev/sdc2 && mkfs.vfat /dev/sdd2

3. 配置 zpool

1
2
3
4
5
6
7
8
zpool create \
-O mountpoint=none -o ashift=12 -O atime=off -O acltype=posixacl -O xattr=sa -O compression=lz4 \
-O encryption=aes-256-gcm -O keyformat=passphrase \
zroot raidz \
ata-xxxxx-part3 \
ata-xxxxx-part3 \
ata-xxxxx-part3 \
ata-xxxxx-part3

接着他会让你输入一下全盘加密的密钥。然后我们可以配置几个挂载点了;

1
2
3
4
5
6
7
8
9
zfs create -o mountpoint=legacy zroot/root
zfs create -o mountpoint=legacy zroot/root/home
zfs create -o mountpoint=legacy zroot/root/nix
mount -t zfs zroot/root /mnt
mkdir /mnt/home && mkdir /mnt/nix
mount -t zfs zroot/root/home /mnt/home
mount -t zfs zroot/root/nix /mnt/nix
mkdir /mnt/boot-1 && mkdir /mnt/boot-2 && mkdir /mnt/boot-3 && mkdir /mnt/boot-4
mount /dev/sda2 /mnt/boot-1 && mount /dev/sdb2 /mnt/boot-2 && mount /dev/sdc2 /mnt/boot-3 && mount /dev/sdd2 /mnt/boot-4

4. 配置 initrd 的 ssh 主机密钥

接着,我们给 initrd 配置一个 ssh 主机密钥,用来在系统重启后 prompt zfs 的密钥。

1
2
ssh-keygen -t ed25519 -N "" -f /mnt/boot-1/initrd-ssh-key
cp /mnt/boot-1/initrd-ssh-key /mnt/boot-2 && cp /mnt/boot-1/initrd-ssh-key /mnt/boot-3 && cp /mnt/boot-1/initrd-ssh-key /mnt/boot-4

5. 安装和配置 NixOS

首先请出经典节目:

1
nixos-generate-config --root /mnt

5.1 配置 SSH 登录

首先先配一下 SSH 登录,防止机器装好之后失联 23333。

1
2
3
4
5
6
users.users.root.openssh.authorizedKeys.keys = [
"ssh-rsa XXXXXXXXXXXX"
];

services.openssh.enable = true;
services.openssh.permitRootLogin = "prohibit-password";

5.2 配置 zfs 支持

提示:这个 hostId 可以通过 head -c 8 /etc/machine-id 来拿。

1
2
3
4
5
6
7
8
networking.hostName = "XXXX";
networking.hostId = "XXXXXXXX";

boot.supportedFilesystems = [ "zfs" ];

boot.loader.grub.enable = true;
boot.loader.grub.efiSupport = false;
boot.loader.grub.copyKernels = true;

5.3 定制 initrd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
boot.initrd.availableKernelModules = [ "r8169" ];
boot.kernelParams = [
# See <https://www.kernel.org/doc/Documentation/filesystems/nfs/nfsroot.txt> for docs on this
# ip=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:<dns0-ip>:<dns1-ip>:<ntp0-ip>
"ip=144.76.95.122::144.76.95.97:255.255.255.224:centaurea-initrd:enp3s0:off:8.8.8.8"
];
boot.initrd.network = {
enable = true;
ssh = {
enable = true;
port = 2222;
hostKeys = [
/boot-1/initrd-ssh-key
/boot-2/initrd-ssh-key
/boot-3/initrd-ssh-key
/boot-4/initrd-ssh-key
];
authorizedKeys = [
"ssh-rsa XXXX"
];
};
postCommands = ''
cat <<EOF > /root/.profile
if pgrep -x "zfs" > /dev/null
then
zfs load-key -a
killall zfs
else
echo "zfs not running -- maybe the pool is taking some time to load for some unforseen reason."
fi
EOF
'';
};

6. 注意事项

配置完成后就可以开始冒烟了,注意检查一下 fstab 是否良好。

我遇到了一个奇怪的问题,但并不是不能工作,在 initrd 里 NixOS 并不会 import 我的 pool,因此需要手动 import 之后 再手动输入密钥。只要 zpool 能够被 systemd 找到,他就会自动 kick in 完成 switch rootfs 的工作,输入密钥后静候即可 :)

考虑到手动挡破车开着也挺好的,我就不打算再花时间排查了。



知识共享许可协议 2000 - 2099 MonKey's Blog | 自豪地采用 Hexo + Pandoc + KaTeX + Highlight.js