Ubuntu Linux从加密的根分区中启动的过程分析
在现代 Linux 系统中,很多用户选择加密根分区以提高数据安全性。本文将详细解释 Ubuntu Linux 如何从加密的根分区启动。
1. 安装 Ubuntu 并启用加密根分区
在安装 Ubuntu 时,可以选择“LVM 与加密”选项。启用此选项后,Ubuntu 安装程序会自动配置加密分区,使根分区通过 LUKS(Linux Unified Key Setup)加密,确保系统文件和用户数据的安全性。
1.1 启用加密的分区结构
选择加密安装选项后,Ubuntu 会自动创建一个 LUKS 加密的物理卷,并在其上配置 LVM。LVM(逻辑卷管理)允许在加密的物理分区上灵活创建逻辑卷,如根卷和交换分区,从而便于分区管理。
1.2 /etc/crypttab 文件
安装完成后,系统生成 /etc/crypttab
文件(类似于 /etc/fstab
),该文件定义了需要在启动时解密的加密分区。/etc/crypttab
文件会被打包到 initramfs
中,以便在系统启动过程中读取和执行解密操作,确保根分区的正确挂载。
安装过程相对简便,下面我们深入探讨系统的启动过程。
2. GRUB 加载 Linux 内核
在 Ubuntu 的默认加密方案中,ESP 分区和 /boot
目录未加密,只有根分区被加密。因此,GRUB(GRand Unified Bootloader)可以直接访问 /boot
分区中的内核和 initramfs 文件。
2.1 GRUB 启动流程
在启动过程中,GRUB 首先加载未加密的 /boot
分区上的内核和 initramfs。initramfs 是启动早期阶段使用的一个临时内存文件系统,包含了解密分区所需的工具和脚本。GRUB 将这些文件加载至内存中,并将控制权移交给内核。
注:若将
/boot
目录也置于加密分区,GRUB 启动流程会更加复杂,但仍可以实现启动。本文主要讨论/boot
分区未加密的情况。
2.2 Linux 内核启动
加载内核和 initramfs 后,GRUB 将控制权交给内核。此时,内核开始执行其启动流程,并初始化 initramfs,以准备解密根分区。
关于grub加载内核的详细过程可以参考这篇文章 Linux操作系统开机启动的过程–使用GRUB引导
3. initramfs 挂载加密的根分区
内核启动后,initramfs 接管系统的早期启动阶段,负责解密和挂载根分区。
3.1 解密根分区的主要逻辑
initramfs 主要通过 cryptroot
脚本完成解密根分区的任务。cryptroot
脚本位于 /usr/share/initramfs-tools/scripts/local-top/cryptroot
。流程如下:
init # 这是 initramfs 的初始化脚本
-> mount_top()
-> local_top()
-> cryptroot # 解密加密的根分区
-> run_keyscript() # 提示用户输入密码
-> unlock_mapping() # 调用 cryptsetup 解密
-> mountroot
-> local_mount_root() # 挂载解密后的根分区
3.2 识别加密的根分区
update-initramfs
命令会将 /etc/crypttab
文件打包进 initramfs 中。cryptroot
脚本读取 /etc/crypttab
文件以识别加密分区的详细信息。
3.2.1 /etc/crypttab
文件的结构
/etc/crypttab
文件中,每行表示一个加密分区,其格式如下:
<name> <device> <password> <options>
# 示例
sda5_crypt UUID=ba5bfb44-7a96-4973-b1d1-d42f989b3d57 none luks,discard
- name:加密分区的逻辑名称,如
sda5_crypt
,在解密后作为挂载点的标识。 - device:设备路径或 UUID(唯一标识符),指向加密设备。
- password:可以是密码文件的路径,也可以设置
none
以在启动时提示用户输入密码。 - options:额外选项,如加密类型或解密方式等。
在启动过程中,initramfs
的cryptroot
脚本负责解密根分区(打开根分区),以便后续挂载操作。这个过程涉及解析/etc/crypttab
文件,通过其中的参数来调用cryptsetup
工具打开加密的根分区。以下是详细的过程:
3.3 解密根分区
对于每个加密分区,cryptroot
脚本根据 /etc/crypttab
中的信息使用 cryptsetup
命令解密。具体过程如下:
- 检查设备路径:
cryptroot
检查device
字段,确保指定的设备路径或 UUID 存在并可访问。若设备路径无效,cryptroot
会记录错误并跳过该分区。 - 输入密码:
如果/etc/crypttab
的password
字段为none
,系统会在启动时提示用户手动输入密码。
若指定了密码文件,cryptroot
会尝试从文件中读取密码,并传递给cryptsetup
进行解密。 - 执行解密命令:
使用cryptsetup open
命令并传入相应参数,cryptroot
解密分区并将其映射到/dev/mapper/<name>
下。例如,以下命令用于解密并映射分区:
这里的cryptsetup open --type luks /dev/sda5 sda5_crypt
--type luks
指定加密类型为LUKS
,/dev/sda5
是物理设备路径,而sda5_crypt
是解密后的设备名称。在成功解密后,系统会在/dev/mapper/sda5_crypt
中生成一个映射设备。 - 验证解密状态:
cryptsetup
会返回解密的状态码。若解密失败,cryptroot
会显示错误信息并可能中止启动过程,提示用户重新尝试输入密码或检查设备配置。 - 应用额外选项:
cryptroot
会根据options
字段设置其他参数。例如,若指定了discard
选项,则启用 TRIM 支持,优化对 SSD 的性能。
此时,根分区已经解密,系统可以将其挂载为根文件系统,从而为启动提供访问权限。
3.4 挂载根分区
3.4.1 进入挂载阶段
解密操作完成后,解密后的设备通常位于 /dev/mapper/<name>
路径下(例如 /dev/mapper/sda5_crypt
)。接下来,initramfs 将进入挂载阶段。此阶段的主要任务是将解密后的设备挂载为根文件系统,以便系统能够正确加载其他文件和服务。
启动流程中 initramfs 的核心脚本 init
将执行以下步骤:
init # initramfs 的初始化脚本
-> mount_top()
-> local_top() # 处理解密和加载操作
-> mountroot # 进入根分区的挂载阶段
-> local_mount_root() # 实际挂载解密后的根分区
3.4.2 local_mount_root() 函数完成挂载
local_mount_root()
是 initramfs 中负责挂载根文件系统的关键函数,其主要步骤如下:
- 确定挂载设备:
local_mount_root()
首先确定应挂载的设备,即已成功解密的根分区。例如,根分区的挂载目标通常是/dev/mapper/sda5_crypt
。该设备路径在cryptroot
解密阶段已被映射,因此可以直接用于挂载操作。 - 挂载根分区:
local_mount_root()
使用标准的mount
命令将解密的根分区挂载到根目录/
上。简化后的逻辑大致如下, 实际过程比这稍微复杂一点:
这个操作将解密的分区作为系统的根文件系统挂载,以便系统能够访问并读取该分区上的所有目录和文件。mount /dev/mapper/sda5_crypt /
- 检查挂载状态:
挂载完成后,local_mount_root()
会验证是否成功挂载。如果挂载失败,系统可能会进入紧急模式(emergency mode),要求用户进行进一步的手动干预。这种检查对于确保根文件系统的可用性和完整性非常重要。
经过这些步骤后,initramfs 的任务已全部完成,解密的根分区已成功挂载为系统的根文件系统,系统将继续加载用户空间环境中的服务和应用,完成引导流程。