LVM on LUKS

出自 Alpine Linux

简介

本文档描述了如何在完全加密的磁盘(除了引导加载程序分区)上设置 Alpine Linux。 我们将在加密分区内安装一个 LVM 容器,该容器包含根分区和交换分区。 为了加密包含 LVM 卷组的分区,使用了 dm-crypt(由 cryptsetup 命令管理)及其 LUKS 子系统。

注意:setup-alpine 安装脚本自 v3.13 版本起支持加密安装。 默认加密选项不会加密交换分区,也不会使用 LUKS,但使用起来更容易得多。

请注意,您的 /boot/ 分区必须是非加密的,才能与 Syslinux 一起使用。 当使用 GRUB2 时,可以从加密分区启动,以提供一层保护,防止 Evil Maid 攻击,但 Syslinux 不支持这一点。

存储设备名称

要查找您的存储设备名称,您可以安装 util-linux (apk add util-linux) 并使用 lsblk 命令查找您的设备,或者您可以使用 BusyBox 的 blkiddf 命令进行有根据的猜测,如果您安装到 USB、SATA 或 SCSI 设备,则运行 ls /dev/sd*,对于软盘,则运行 ls /dev/fd*,对于 IDE (PATA) 设备,则运行 ls /dev/hd*

以下文档使用 /dev/sda 设备作为安装目标。 如果您的环境对存储设备使用不同的名称,请在示例中使用相应的设备名称。

在 LUKS 分区之上使用 LVM 设置 Alpine Linux

要在 LUKS 加密分区之上运行的逻辑卷上安装 Alpine Linux,您不能使用官方安装程序。 安装需要您在 Alpine Linux Live CD 环境中运行几个手动步骤。

准备临时安装环境

在开始安装 Alpine Linux 之前,请准备临时环境

启动最新的 Alpine Linux 安装 CD。 在登录提示符下,使用 root 用户,无需密码即可登录。 现在我们将按照 Setup-alpine 脚本进行操作,并在过程中进行更改。

按此顺序运行脚本

# setup-keymap
# setup-hostname
# setup-interfaces
# rc-service networking start

如果您配置的是静态网络(即,您没有配置任何接口使用 DHCP),请运行 setup-dns

如果您正在使用 Wi-Fi,您可能需要运行 rc-update add wpa_supplicant boot

注意:在 0.45 之前的 OpenRC 版本上,请使用 urandom 代替 seedrng
# passwd
# setup-timezone
# rc-update add networking boot
# rc-update add seedrng boot
# rc-update add acpid default
# rc-service acpid start

编辑您的 /etc/hosts 文件,使其看起来像这样,将 <hostname> 替换为您的主机名,将 <domain> 替换为您的 TLD(如果您没有 TLD,请使用 'localdomain')

提示:BusyBox 中的默认文本编辑器是 vi(发音为 vee-eye)。

/etc/hosts 的内容

127.0.0.1 <hostname> <hostname>.<domain> localhost localhost.localdomain ::1 <hostname> <hostname>.<domain> localhost localhost.localdomain
# setup-ntp
# setup-apkrepos
# apk update
# setup-sshd

这里是我们偏离安装脚本的地方。

安装以下设置 LVM 和 LUKS 所需的软件包

注意:parted 分区编辑器是高级分区和 GPT 磁盘标签所必需的。 BusyBox fdisk 是一个功能非常精简的版本,功能极少
# apk add lvm2 cryptsetup e2fsprogs parted mkinitfs

创建分区布局

根据您的主板、bios 功能和配置,我们可以使用 MBR(传统 BIOS)或 GUID 分区表 (GPT) 中的分区表。 我们将描述这两种方法以及示例布局。

BIOS/MBR 与 DOS 磁盘标签

我们将使用非加密的 /boot 分区来分区存储设备,以便与 Syslinux 引导加载程序一起使用。 Syslinux 旨在与传统 BIOS 和 MSDOS MBR 分区表一起使用。
Syslinux 确实支持 GPT 分区表,但 GRUB2 是 UEFI 的更好选择(只有 GPT 才有可能使用 UEFI)。

+---------------------------+------------------------+-----------------------+
| Partition name            | Partition purpose      | Filesystem type       |
+---------------------------+------------------------+-----------------------+
| /dev/sda1                 | Boot partition         | ext4                  |
| /dev/sda2                 | LUKS container         | LUKS                  |
| |-> /dev/mapper/lvmcrypt  | LVM container          | LVM                   |
|  |-> /dev/vg01/root       | Root partition         | ext4                  |
|  |-> /dev/vg01/swap       | Swap partition         | swap                  |
+---------------------------+------------------------+-----------------------+
警告:这将删除现有的分区表,并使您的数据非常难以恢复。 如果您想双启动,请在此处停止并咨询专家。


创建一个大约 100MB 的分区用于启动,然后将剩余空间分配给您的 LUKS 分区。

# parted -a optimal
(parted) mklabel msdos
(parted) mkpart primary ext4 0% 100M
(parted) set 1 boot on
(parted) mkpart primary ext4 100M 100%

要在 parted 中查看您的分区表,请键入 print。 您的结果应如下所示

(parted) print
Model: ATA TOSHIBA ******** (scsi)
Disk /dev/sda: 1000GB
Sector size (logical/physical): 512B/4096B
Partition Table: msdos
Disk Flags:

Number  Start   End     Size    Type     File system  Flags
 1      1049kB  99.6MB  98.6MB  primary  ext4         boot
 2      99.6MB  1000GB  1000GB  primary  ext4

UEFI 与 GPT 磁盘标签

我们将加密除挂载在 /boot/efi 的 EFI 系统分区之外的整个磁盘。 这意味着 GRUB2 将解密 LUKS 卷并从那里加载内核,从而防止拥有物理访问您计算机权限的人在您的计算机未解锁时恶意地在您的启动分区中安装 rootkit(或 bootkit)。 分区方案将如下所示

+---------------------------+------------------------+-----------------------+
| Partition name            | Partition purpose      | Filesystem type       |
+---------------------------+------------------------+-----------------------+
| /dev/sda1                 | EFI system partition   | fat32                 |
| /dev/sda2                 | LUKS container         | LUKS                  |
| |-> /dev/mapper/lvmcrypt  | LVM container          | LVM                   |
|  |-> /dev/vg01/root       | Root partition         | ext4                  |
|  |-> /dev/vg01/boot       | Boot partition         | ext4                  |
|  |-> /dev/vg01/swap       | Swap partition         | swap                  |
+---------------------------+------------------------+-----------------------+
警告:这将删除现有的分区表,并使您的数据非常难以恢复。 如果您想双启动,请在此处停止并咨询专家。


创建一个大约 200MB 的 EFI 系统分区,然后将剩余空间分配给您的 LUKS 分区。

# parted -a optimal
(parted) mklabel gpt
(parted) mkpart primary fat32 0% 200M
(parted) name 1 esp
(parted) set 1 esp on
(parted) mkpart primary ext4 200M 100%
(parted) name 2 crypto-luks

可选:使用随机数据覆盖 LUKS 分区

如果您的硬盘驱动器以前未加密,则应执行此操作。 它有助于清除旧的、未加密的数据,并使攻击者更难确定您在驱动器上有多少数据(如果他们可以访问加密内容)。

# dd if=/dev/urandom of=/dev/sda2 bs=1M

加密 LVM 物理卷分区

要加密稍后将包含 LVM PV 的分区,您可以使用默认设置(aes-xts-plain64 密码,256 位密钥和 Argon2 哈希,迭代时间为 2000 毫秒),或者您可以使用这些设置,这些设置增加了安全性,但现代计算机上的性能下降不明显

默认设置

# cryptsetup luksFormat /dev/sda2

Luks1 针对安全性进行了优化

# cryptsetup -v -c serpent-xts-plain64 -s 512 --hash sha512 --iter-time 5000 --use-random luksFormat --type luks1 /dev/sda2

Luks2 针对安全性进行了优化

# cryptsetup -v -c aes-xts-plain64 -s 512 --hash sha512 --pbkdf pbkdf2 --iter-time 5000 --use-random luksFormat /dev/sda2

在 LUKS2 和 LUKS1 之间转换

有时可以将 LUKS2 卷转换为 LUKS1 卷。 首先备份 LUKS 标头,以便在出现问题时可以恢复

# cryptsetup luksHeaderBackup /dev/sda2 --header-backup-file sda2-luks-header-backup

然后通过添加新密钥来确保所有密钥都使用 pbkdf2

# cryptsetup luksAddKey --pbkdf pbkdf2 /dev/sda2

使用 cryptsetup luksRemoveKey /dev/sda2 删除使用 argon2iargon2id 的密钥。 您可以使用 cryptsetup luksDump /dev/sda2 检查密钥信息。

现在您可以尝试转换,尽管它可能不起作用。

# cryptsetup convert /dev/sda2 --type luks1

创建逻辑卷和文件系统

打开 LUKS 分区

# cryptsetup luksOpen /dev/sda2 lvmcrypt

lvmcrypt 上创建 PV

# pvcreate /dev/mapper/lvmcrypt

/dev/mapper/lvmcrypt PV 中创建 vg0 LVM VG

# vgcreate vg0 /dev/mapper/lvmcrypt

BIOS/MBR 的 LV 创建

这将创建一个 2GB 的交换分区和一个占用剩余空间的根分区。 此设置适用于那些不需要使用休眠/挂起到磁盘状态的人。 如果您确实需要挂起到磁盘,请创建一个略大于 RAM 大小的交换分区(在 # lvcreate -L 之后更改大小)。

# lvcreate -L 2G vg0 -n swap
# lvcreate -l 100%FREE vg0 -n root

在前几个步骤中创建的 LV 会自动标记为活动状态。 要验证,请输入

# lvscan

UEFI/GPT 的 LV 创建

这将创建一个 2GB 的交换分区、一个 2GB 的引导分区和一个占用剩余空间的根分区。 此设置适用于那些不需要使用休眠/挂起到磁盘状态的人。 如果您确实需要挂起到磁盘,请创建一个略大于 RAM 大小的交换分区(在 # lvcreate -L 之后更改大小)。

# lvcreate -L 2G vg0 -n swap
# lvcreate -L 2G vg0 -n boot
# lvcreate -l 100%FREE vg0 -n root

在前几个步骤中创建的 LV 会自动标记为活动状态。 要验证,请输入

# lvscan

创建并挂载文件系统

使用 ext4 文件系统格式化 rootboot LV

# mkfs.ext4 /dev/vg0/root

格式化交换 LV

# mkswap /dev/vg0/swap

在安装 Alpine Linux 之前,您必须挂载分区和 LV。 将根 LV 挂载到 /mnt/ 目录

# mount -t ext4 /dev/vg0/root /mnt/

接下来格式化您的引导分区,创建一个挂载点,然后挂载它

  • 如果您使用的是 BIOS 和 MBR
# mkfs.ext4 /dev/sda1
# mkdir -v /mnt/boot
# mount -t ext4 /dev/sda1 /mnt/boot
  • 如果您使用的是 UEFI 和 GPT
# apk add dosfstools
# mkfs.fat -F32 /dev/sda1
# mkfs.ext4 /dev/vg0/boot
# mkdir -v /mnt/boot
# mount -t ext4 /dev/vg0/boot /mnt/boot
# mkdir -v /mnt/boot/efi
# mount -t vfat /dev/sda1 /mnt/boot/efi

最后,激活您的交换分区

# swapon /dev/vg0/swap

安装 Alpine Linux

在此步骤中,您将在 /mnt/ 目录中安装 Alpine Linux,该目录包含已挂载的文件系统结构

# setup-disk -m sys /mnt/

安装程序下载最新的软件包以安装基本安装。 此外,安装程序会自动在 /etc/fstab 文件中创建挂载点的条目,该文件当前挂载在 /mnt/ 目录中。

注意:在此步骤中,自动写入主引导记录 (MBR) 失败。 稍后,您将手动将 MBR 写入磁盘。

交换 LV 不会自动添加到 fstab 文件中。 因此我们需要将以下行添加到 /mnt/etc/fstab 文件

/dev/vg0/swap    swap    swap    defaults    0 0

编辑 /mnt/etc/mkinitfs/mkinitfs.conf 文件,并将 cryptsetup 模块附加到 features 参数

features="... cryptsetup"

如果您正在使用 GRUB 和加密的 /boot,您必须添加 cryptkey 功能,以便 Alpine 可以使用密钥文件在启动时进行解密。

注意:当提示输入密码以在启动时解密分区时,Alpine Linux 默认使用 en-us 键盘映射。 如果您在临时环境中更改了键盘映射,并希望在启动密码提示符下使用它,请务必将 keymap 功能添加到上面的列表中。
注意:检查 mkinitfs -L 的输出,并添加系统启动所需的必要功能。 您可能需要添加 kms 才能在启动时看到密码提示符。 您可能还需要:usblvmext4nvme...

重建初始 RAM 磁盘

# mkinitfs -c /mnt/etc/mkinitfs/mkinitfs.conf -b /mnt/ $(ls /mnt/lib/modules/)

该命令使用 mkinitfs.conf 文件中设置的设置(在 -c 参数中)来生成 RAM 磁盘。 该命令在 /mnt/ 目录中执行,并且 RAM 磁盘是使用已安装内核的模块生成的。 如果不使用 $(ls /mnt/lib/modules/) 选项设置内核版本,mkinitfs 将尝试使用临时环境中安装的内核版本生成 RAM 磁盘,这可能与 setup-disk 实用程序安装的最新版本不同。

安装引导加载程序

要将您的存储设备的 UUID 放入文件中以供以后使用,请运行此命令

# blkid -s UUID -o value /dev/sda2 > ~/uuid
提示:为了轻松地将 UUID 读取到文件中,以便您不必手动输入,请在 vi 中打开该文件,然后键入 :r /root/uuid 以将 UUID 加载到新行上。

BIOS 搭配 Syslinux

安装 Syslinux 软件包

# apk add syslinux

编辑 /mnt/etc/update-extlinux.conf 并将以下内核选项附加到 default_kernel_opts 参数,将 <UUID> 替换为 /dev/sda2 的 UUID

default_kernel_opts="... cryptroot=UUID=<UUID of sda2> cryptdm=lvmcrypt"

cryptroot 参数设置包含加密卷的设备/分区的 ID,cryptdm 参数使用我们已经在上面配置的映射名称。

我们还可以仔细检查 modulesroot 是否设置正确,例如

modules=sd-mod,usb-storage,ext4,cryptsetup,keymap,cryptkey,kms,lvm
root=UUID=<UUID of /dev/mapper/vg0-root>

由于 update-extlinux 实用程序仅在 /boot/ 目录上运行,因此暂时将根目录更改为 /mnt/ 目录并更新引导加载程序配置

# chroot /mnt/
# update-extlinux
# exit
由于我们没有在 /mnt/ chroot 中挂载 /dev/proc,因此当我们运行 update-extlinux 命令时,可能会发生一些错误。 但您很可能可以忽略这些错误。

将 MBR(不带分区表)写入 /dev/sda 设备

# dd bs=440 count=1 conv=notrunc if=/mnt/usr/share/syslinux/mbr.bin of=/dev/sda

UEFI 搭配 Grub

为了避免每次启动都必须键入两次解密密码(一次用于 GRUB,一次用于 Alpine),请将密钥文件添加到您的 LUKS 分区。 文件名很重要。

# touch /mnt/crypto_keyfile.bin
# chmod 600 /mnt/crypto_keyfile.bin
# dd bs=512 count=4 if=/dev/urandom of=/mnt/crypto_keyfile.bin
# cryptsetup luksAddKey /dev/sda2 /mnt/crypto_keyfile.bin

此密钥文件已加密存储(它在您的 LUKS 分区中),因此它的存在不会影响系统安全性。

挂载 Grub EFI 安装程序所需的文件系统到安装

# mount -t proc /proc /mnt/proc
# mount --rbind /dev /mnt/dev
# mount --make-rslave /mnt/dev
# mount --rbind /sys /mnt/sys

然后运行 chroot

# chroot /mnt
# source /etc/profile
# export PS1="(chroot) $PS1"

为 EFI 安装 GRUB2 并(可选)删除 syslinux

# apk add grub grub-efi efibootmgr
# apk del syslinux

编辑 /etc/default/grub 并将以下内核选项添加到 GRUB_CMDLINE_LINUX_DEFAULT 参数,将 <UUID> 替换为加密分区的 UUID(在本例中为 /dev/sda2

cryptroot=UUID=<UUID> cryptdm=lvmcrypt cryptkey

cryptroot 参数设置包含加密卷的设备/分区的 ID,cryptdm 参数使用我们上面配置的映射名称。 cryptkey 参数指示您之前创建的文件 /crypto_keyfile.bin 的存在。

要使 GRUB 能够解密 LUKS 分区并读取 LVM 卷,请添加

GRUB_PRELOAD_MODULES="luks cryptodisk part_gpt lvm"

如果使用 Alpine v3.11 或更高版本,还应将 GRUB_ENABLE_CRYPTODISK=y 添加到 /etc/default/grub

Luks1

# (chroot) grub-install --target=x86_64-efi --efi-directory=/boot/efi
# (chroot) grub-mkconfig -o /boot/grub/grub.cfg
# (chroot) exit

Luks2

注意:该方法仍处于实验阶段,您可能会在下次操作系统更新时失去对操作系统的访问权限

创建一个预配置 grub 文件:/root/grub-pre.cfg

set crypto_uuid=00001
cryptomount -u $crypto_uuid
set root='lvmid/00002/00003'
set prefix=($root)/boot/grub
insmod normal
normal

您可以找到

  • 00001 与 blkid 并找到您的加密磁盘的 uuid,即 /dev/nvme0n1p2,从 UUID 中删除连字符
  • 00002 与 vgdisplay & VG UUID
  • 00003 与 lvdisplay & 根分区 / 的 LV UUID
# (chroot) grub-mkimage -p /boot/grub -O x86_64-efi -c /root/grub-pre.cfg -o /tmp/grubx64.efi luks2 part_gpt cryptodisk lvm ext2 gcry_rijndael pbkdf2 gcry_sha512
# (chroot) install -v /tmp/grubx64.efi /boot/efi/EFI/grub/
# (chroot) grub-mkconfig -o /boot/grub/grub.cfg
# (chroot) exit

卸载卷和分区

卸载 /mnt/ 分区,停用 LVM 卷,关闭 LUKS 分区并重启

# cd
# umount -l /mnt/dev
# umount -l /mnt/proc
# umount -l /mnt/sys
# umount /mnt/boot/efi
# umount /mnt/boot
# swapoff /dev/vg0/swap
# umount /mnt
# vgchange -a n
# cryptsetup luksClose lvmcrypt
# reboot

故障排除

通用步骤

如果您的系统无法启动,您可以验证设置并修复不正确的配置。

重启并再次执行准备临时安装环境中的步骤。

设置 LUKS 分区并激活 LV

# cryptsetup luksOpen /dev/sda2 lvmcrypt
# vgchange -ay

挂载文件系统

验证您是否正确运行了安装 Alpine Linux部分中描述的步骤。 如果需要,更新配置,卸载分区,然后重启。

系统找不到启动设备

* GPT partition table on a motherboard that runs BIOS instead of UEFI
* running an MSDOS/MBR/Syslinux install without enabling legacy boot mode in the UEFI settings

启动时看到 “can not mount /sysroot”

* incorrect device UUID
* missing module in /mnt/etc/update-extlinux.conf or /mnt/etc/mkinitfs/mkinitfs.conf

normal.mod 未找到

* re-install grub-install --target=x86_64-efi

安全启动

如果安全启动抱怨引导加载程序未签名,您可以禁用它或调整指南以签名 GRUB。 如果您正在使用 Syslinux,那么当您启用传统引导模式时,应该会自动禁用安全启动。

加固

  • 为了加固,您应该禁用 DMA[1] 并安装加固版本的 AES (TRESOR[2] 或 Loop-Amnesia[3]),因为默认情况下,带有 luks 的 cryptsetup 默认使用 AES。
  • 根据 Wikipedia 在 BIOS 中禁用 DMA 并设置 BIOS 密码。[4]
  • 将使用 DMA 的内核模块和任何未使用的扩展模块(FireWire、CardBus、ExpressCard、Thunderbolt、USB 3.0、PCI Express 和使用 DMA 的热插拔模块)列入黑名单。

在启动时挂载额外的加密文件系统

如果您希望在启动期间自动解密和挂载其他加密的 LUKS 分区,例如,如果您的 /home 在单独的物理驱动器上,则需要额外的步骤。

注意:这不适用于主加密分区 /dev/sda2 中的卷

为了便于说明,我们将假设 /dev/sdb1 包含一个应挂载在 /home 的 LVM 卷。

创建一个密钥文件并将其添加到 LUKS 分区

# dd bs=512 count=4 if=/dev/urandom of=/root/crypt-home-keyfile.bin
# cryptsetup luksAddKey /dev/sdb1 /root/crypt-home-keyfile.bin

Alpine 与 Gentoo 一样,使用 dmcrypt 服务而不是 /etc/crypttab。 将以下行添加到 /etc/conf.d/dmcrypt

target=crypt-home
source='/dev/sdb1'
key='/root/crypt-home-keyfile.bin'

/etc/fstab 添加一个条目,将 vg1 更改为您的 LVM 卷组的名称

/dev/vg1/home /home ext4 rw,relatime 0 2

启用 dmcrypt 和 lvm 服务以在启动时启动

# rc-update add dmcrypt boot
# rc-update add lvm boot

重启后,分区应自动解密并挂载。

参见