[linux]基于安全启动和TPM的根分区/全硬盘加密
Windows可以利用bitlocker加密硬盘,并利用TPM保存密钥,以便在开机时自动解密。安全启动通过检验bios和bootloader的签名,令TPM得以仅允许受信的程序访问密钥,从而实现仅在受信的情况下才解密硬盘。 将TPM用于密钥,相对其他密钥存储手段更不安全,但具有无需其他设备即可自动解密的优点,当然这是在平台本身已经实现了TPM的基础上。 为了在linux中也能使用这两项技术,我们需要完成这些步骤: 准备安全启动密钥,以及设置好加密的luks分区。 替换BIOS安全启动密钥。 签名内核和各阶段bootloader,设置引导参数,设置BIOS引导项,并启动安全启动。 在luks中登记TPM密钥。 设置initrd在启动时从TPM读取密钥,解密luks分区并挂载。 为了进一步提高安全性,这里建议由bios直接引导内核,从而杜绝利用中间阶段的bootloader获取解密密钥的可能性。当然,使用中间阶段的bootloader并不必然影响安全性。 另外,内核默认不会检查initrd和引导参数的签名,安全启动也不会对引导参数做检查。因此,为了防止initrd和引导参数被篡改,这里建议将参数和initrd打包进内核中。 下文将以archlinux为例,演示整个流程。为了最大限度地保证流程通用性,这里将首先演示如何在安装archlinux时设置全硬盘加密,之后演示如何在已经设置了全硬盘加密的系统上,设置安全启动和TPM。因此,已经设置好了全硬盘加密的用户,也可以参考下文的演示。为了安全性或方便,下文展示的安装流程将与常用的安装流程有所区别,将在出现时标出。 理论上,其他发行版可以使用同样的方式启用基于TPM的硬盘加密,但具体操作可能有(wan)所(quan)不同。 下文将使用: 那么,在开始之前:请准备好TPM密钥失效时用于解密luks分区的备用手段 以及,仅使用TPM可能降低安全性。 第一步:安装时加密根分区 那么首先第一步,我们熟练得引导进liveusb,然后,当然是联网了。 iwctl:station connect 这里networkctl显示网络已经设置好了 接下来准备分区 这是我的分区表,这里就不建议抄作业了。我这里ESP真实的空间需求其实最多100MB就够了,不过1G也不大嘛。 这里至少需要EFI系统分区和根分区,如果有swap的话,swap也是建议加密的。另外,建议有需求的朋友使用LVM,这样可以提供更好的分区灵活性。 命令在后面会列出来 接下来我们准备加密的swap和root,这里主要用到的是`cryptosetup luksFormat`和`cryptosetup open`。在准备好luks之后,这里根分区选择了ext4,所以用mkswap和mkfs.ext4为解密后的设备建立文件系统。其他文件系统,比如btrfs,xfs,zfs,f2fs等都是可以考虑的选择。另外也别忘了格式化EFI系统分区。因为要由BIOS直接读取,所以ESP不能加密。
接下来要挂载根分区进行pacstrap,在此之前建议先调整pacman mirrorlist。liveusb提供了reflector,但如果你偏好的源不在列表内,还是需要手动调整的。
网络管理选择了networkmanager,又因为nm依赖wpa_supplicant,所以这样就可以在安装好的系统中使用wifi了。虽然说iwd配合systemd-networkd的体验更好,但为了桌面环境下的体验,所以还是选择了nm。(小知识:nm目前是gnome负责的)。或者说,如果你想要完整的gnome或kde体验,nm还是需要的。但如果你更喜欢轻量GUI或命令行,或者你需要更灵活的网络配置,那么networkd是更好的选择。 一般来说,之后就可以挂载ESP,安装内核并配置initrd了。 mount /dev/nvme0n1p1 /mnt/boot,然后,pacstrap /mnt linux linux-firmware。 接下来我们要做一些额外的修改,为了方便,我们先arch-chroot /mnt。 首先需要修改的是mkinitcpio.conf,我们需要添加sd-encrypt的hook。 (鉴于nano已经不在base group中了,所以这次首先要安装你喜欢的编辑器) 这里systemd和sd-encrypt是必须的,不过不懂的话不建议乱动,只加上sd-encrypt就好了 如果需要由内核进行微代码更新,也可以一并安装intel-ucode或amd-ucode。 为了之后方便,我们这里不使用bootloaderunix 分区加密,而是直接制作unified kernel image,也就是将内核,引导参数和initrd打包在一起。但一般来说,如果使用不依赖TPM和secure boot的硬盘加密,这一步并不是必要的。
这里是arch wiki展示的如何制作unified kernel image的脚本,不过我们只需要.cmdline, .linux和.initrd就够了。在此之前,如果需要使用微代码更新,我们需要先将两者合并:
最终组成了这样的脚本: 这里之后会提供抄作业的选项 将内核直接放在ESP:/EFI/boot/bootx64.efi的好处是,这个位置会直接被BIOS引导,不需要再手动添加启动项。想要放在别的地方也是可以的。 我们需要在/etc/cmdline中指定需要解开和挂载的分区,这里必须用UUID指定,因此要先通过lsblk -f查看对应分区的UUID,其中的uuid可以使用某些手段进行复制(比如vim),但如果你不是很擅长某些工具的话,手动复制一遍也不是那么。。。emm。。。
其中swapDevice和cryptroot只是两个名字,按你的喜好随便起就好了。另外这里引导参数不建议加quiet loglevel=3或者vga=current这种东西。 当然你也可以使用crypttab,crypttab.initramfs和fstab来完成这一过程,其实工作量也差不多,不过crypttab的好处是可以不用UUID。 另外别忘了在/etc/fstab中配置swap和/boot的自动挂载。 然后,根据installation guide,我们还需要设置语言,时区,以及,root密码。 这里重点提一下root密码,因为安装时不设置的话是不能用root账号登录的。当然如果你选择新建一个sudoer账号那么就不需要设置root密码了。 这样就可以重启了。 但是在这样的配置下,我们每次开机都要输入密码以解锁luks,这的确有些麻烦。 第二步:安全启动 如果一切顺利,你现在应该已经拔掉liveusb了。那么进入系统之后,我们要做的第一件事依然是,联网。。。如果你按照我的建议选择了neworkmanager,那么首先你需要启用并启动它的服务。`systemctl enable --now NetworkManager` (大小写敏感),然后`nmcli device wifi connect password `。 接下来我们会生成安全启动密钥,配置内核自动签名,并启用安全启动。 第一步是生成密钥。安全启动使用三种密钥,其中PK是平台的主密钥,KEK是每次更新数据库时使用的密钥,DB则是直接对应bootloader的密钥。KEK被PK签名,DB被KEK签名,而bootloader被DB签名。 我们可以使用openssl手动完成这一步骤,不过sbkeys(AUR)提供了自动化生成密钥的脚本。这段脚本同时发布在AUR和github。但因为不建议在root下使用makepkg,所以这次我们来手动安装依赖。根据sbkeys的PKGBUILD,它依赖efitools,coreutils,bash,util-linux,openssl,wget。因为coreutils,bash,util-linux和openssl(通过pacman->curl)已经被base依赖,另外我们这次并不会使用需要wget的功能,所以我们只需要额外安装efitools,以及git。
其中我们需要将PK,KEK和DB的公钥证书(.cer,.crt,.esl, .auth的任一个)登记入UEFI设置,因为不同主板支持的格式可能不同。 如果BIOS目前处于setup mode,有些主板支持在linux下直接登记证书,我们可以使用sbkeysync完成这一步骤。但有些BIOS可能仅支持在固件设置界面修改密钥,那么我们就需要手动登记密钥了。 如果需要手动登记密钥,这里建议将除了*.key以外的所有文件复制入ESP,以便之后重启进入固件设置时登记。因为.key文件含有安全启动私钥,而ESP并未加密,所以这里强烈建议不要将任何.key文件复制到ESP。 不要将任何.key文件复制到ESP。 不要将任何.key文件复制到ESP。
接下来是签名内核,并配置自动签名内核的pacman hook。我们可以修改unifiedkernel image脚本来完成签名,并且将同一脚本用于pacman hook。签名需要用到sbsigntools,然后efitools和sbkeys可以删掉了(是卸磨杀驴的豪杰)。
在此之前,为了最大限度得保证initrd和内核的安全性,我们将取消/boot的自动挂载(可选)
然后参考/usr/share/libalpm/hooks/90-mkinitcpio-install.hook创建新的hook。
我们需要修改[Action]节的Exec项,将其改为/root/mk_kernel.sh,并且[Trigger]的Target中增加/boot/amd-ucode.img (或者/boot/intel-ucode.img)。 Archwiki的方法是代替mkinitcpio的hook,但因为我们需要首先制作unified image,所以这里选择了在mkinitcpio的hook之后执行该hook。 这里直接使用之前准备好的mk_kernel.sh即可,但需要增加一行签名内核的命令。 AUR的sbupdate也提供了相似的功能。 之后就要重启了。不过重启之前建议将未签名的内核也复制到ESP中备用。(如果签名内核能成功引导那么就可以删掉了)。
启用安全启动,进入安全启动密钥管理设置,首先清除平台的默认密钥。 接着,我们将对应的密钥登记到密钥管理中,我们需要登记PK(platform key),KEK(key exchange keys)和DB(authorized signatures)。 另外这一步完成后建议在BIOS中设置管理员密码。 然后保存退出,如果一切顺利的话,我们能看到签名内核已经经过安全启动验证成功启动了。 这一步结束之后,ESP中除了bootx64.efi以外的其他文件就都可以删除了。 /root/SB下实际上也只需要保留DB.crt和DB.key,不过反正也不大。 第三步:TPM 这里我们将使用systemd-cryptenroll,为了使用tpm2,需要安装tpm2-tss。 目前大多数平台应该已经支持了TPM 2,有些老平台可能在使用TPM 1.2,但systemd-cryptenroll不支持TPM 1.2。
这里我们使用了TPMPCR 01 7,这三个PCR分别代表了BIOS二进制,UEFI设置和安全启动状态。如果任一状态改变, 之后我们需要修改内核引导参数,添加tpm2-device=auto的options。注意,如果tpm失败,这可能导致根分区无法正常挂载,因此建议同时制作使用tpm和不使用tpm的两个内核,以备TPM状态改变后重新登记密钥。 在重新制作内核之前,我们需要重新生成mkinitcpio以包含tpm2-tss的依赖,否则initrd环境的systemd将无法使用tpm。 如果一切顺利,重启之后就可以看到根分区被自动解锁了。 当禁用安全启动后,根分区将无法自动解锁,启动时会提示输入密码,这也就说明配置成功了。 以上。 (编辑:威海站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |