LUKS 磁盘加密实战:dm-crypt 加密分区、crypttab 自动解锁与头部备份恢复演练

为什么个人站长也该考虑磁盘加密

提到磁盘加密,很多人的第一反应是「那是政企和笔记本用户的事,我一个小站长加密磁盘干嘛」。但把场景想具体一点,威胁其实是真实存在的:

VPS 服务商可以在宿主机层面直接读取你的虚拟磁盘,甚至在你不知情的情况下对快照做镜像;云厂商的自动备份、快照文件如果权限配置不当,可能被同账号下的其他凭证访问;一台移动硬盘或者淘汰的旧服务器,处理不当就直接把上面的数据库、配置、密钥一起送给了别人。磁盘加密解决的不是「攻击者通过网络入侵」这类问题——那是防火墙和应用安全的事——它解决的是**物理或底层介质被直接读取时的数据保密性**。

在 Linux 上做全盘或分区级加密,标准方案是 dm-crypt + LUKS。它的工作方式是在块设备层面透明加解密:上层文件系统、应用完全无感知,数据落到物理磁盘时已经是密文。LUKS(Linux Unified Key Setup)是这套方案的标准头部格式,负责管理密钥槽、加密算法参数和校验信息。

先理解 LUKS 的头部:一切灾难的源头都在这里

在动手之前必须先讲清楚 LUKS 头部,因为绝大多数「加密盘数据全毁」的事故,都是头部损坏或密钥槽丢失造成的。

LUKS 的头部(header)位于加密分区的开头,它不存数据,而是存了这些东西:加密算法和模式、密钥派生函数的参数、以及若干个(LUKS1 有 8 个、LUKS2 默认 32 个)密钥槽。每个密钥槽里存的不是你的密码本身,而是被你的密码加密过的**主密钥**(master key)。数据区实际上是用这个主密钥加密的,密码只是用来解开主密钥。

这个设计带来一个关键结论:只要有一个密钥槽能解开、头部完好,数据就能恢复;反过来,头部一旦损坏,哪怕你记得密码,数据也永久丢失。所以部署 LUKS 的第一条纪律不是备份数据,而是备份头部:

# 备份 LUKS 头部(务必存到磁盘之外的地方)
cryptsetup luksHeaderBackup /dev/sdb1 \
  --header-backup-file /root/luks-header-sdb1.img

# 头部很小,LUKS2 约 16MB,LUKS1 约 2MB
ls -lh /root/luks-header-sdb1.img

这个头部备份文件要立刻拷走到另一台机器或对象存储,绝不能只留在同一块盘上——盘都坏了,备份自然也没了。备份文件本身包含密钥材料,权限设成 600,并且要意识到它和密码同等敏感。

加密一个新分区:完整流程

下面以一块数据盘 /dev/sdb 为例,做一个加密分区。第一步先确认设备,务必反复核对,因为 luksFormat 会把目标设备头部直接覆盖,选错盘等于直接毁掉上面的数据:

lsblk
# 确认 /dev/sdb 是那块空盘,再往下走

# 创建 LUKS2 容器(推荐 LUKS2,元数据可冗余、支持更大密钥槽数量)
cryptsetup luksFormat --type luks2 /dev/sdb

# 会要求输入两次密码,建议用长口令而非复杂短密码

LUKS 的密码强度不能按网站密码的标准来衡量,因为它面对的是离线暴力破解——攻击者拿到磁盘后可以不限次数地尝试。一个 8 位随机字符的密码在专用硬件面前撑不了太久,更稳妥的做法是使用一条足够长的口令短语,或者只用密码作为第二层保护、真正的解锁靠密钥文件。

格式化完成后打开容器:

# 打开:把 /dev/sdb 解密后映射为 /dev/mapper/cryptdata
cryptsetup luksOpen /dev/sdb cryptdata
# 需要密码,输入即可

# 在映射出来的设备上建文件系统
mkfs.ext4 /dev/mapper/cryptdata

# 挂载
mkdir -p /mnt/secure
mount /dev/mapper/cryptdata /mnt/secure
df -h /mnt/secure

注意一个容易混淆的点:所有文件系统操作(mkfs、fsck、mount)针对的都是 /dev/mapper/cryptdata 这个映射设备,而不是 /dev/sdb 本身。直接对 /dev/sdb 做 mkfs 会把 LUKS 头部覆盖掉,数据瞬间全毁——这是新手最常见的事故。

开机自动解锁:crypttab 与密钥文件

手动 luksOpen 每次重启都要输密码,对服务器不现实。标准做法是 /etc/crypttab 配合密钥文件。先生成密钥文件:

# 生成一个 4KB 随机密钥文件
dd if=/dev/urandom of=/root/cryptdata.key bs=512 count=8
chmod 600 /root/cryptdata.key

# 把该密钥加入一个空的 LUKS 密钥槽
cryptsetup luksAddKey /dev/sdb /root/cryptdata.key

然后写 /etc/crypttab,格式是「映射名 设备 密钥文件 选项」:

cryptdata  /dev/sdb  /root/cryptdata.key  luks,discard

配合 /etc/fstab 里的挂载项:

/dev/mapper/cryptdata  /mnt/secure  ext4  defaults,nofail  0  2

这里有几个必须注意的取舍。第一个是 discard 选项:它允许 TRIM 指令穿透到物理盘,对 SSD 寿命和性能有帮助,但 TRIM 会泄露「哪些块是空的」这一元信息。如果你加密的目的是对抗能读到底层介质的对手,那就不该开 discard;如果你更在意 SSD 性能和寿命,那就开。这是个安全与性能的权衡,没有标准答案,但要有意识地选。

第二个是密钥文件的存放位置。密钥文件放在已经被加密的根分区上,等于把钥匙锁在它要开的箱子里,开机时 order 不对就解不开。务实的方案是:根分区不加密、只加密数据分区,密钥文件放在根分区上并把根分区权限收紧;或者把密钥放在 initramfs 能访问的独立小分区。无论哪种,密钥文件的权限必须是 600 且不能让普通用户读到。

第三个是 nofail。如果加密盘解锁失败(比如密钥文件丢失),fstab 里没有 nofail 的话系统会卡在启动阶段进不了系统,只能进救援模式。加上 nofail 至少能保证系统正常起来,可以登进去排查。

密钥槽管理:加人、换密码、吊销

LUKS 真正的灵活性在于密钥槽。你可以有多个独立凭证同时能解开同一块盘:

# 查看所有密钥槽的使用情况
cryptsetup luksDump /dev/sdb | grep -A 20 "Keyslots"

# 新增一个密码(会占用一个空槽)
cryptsetup luksAddKey /dev/sdb

# 删除某个密钥槽(先确认槽号)
cryptsetup luksKillSlot /dev/sdb 1

这个机制的实际用途很直接:给运维同事一个独立密码,人员离职时只删他那一个槽,不影响你自己的;或者同时保留密码槽和密钥文件槽,密码用于人工恢复、密钥文件用于自动解锁。删除密钥槽前一定要确认自己还有另一个能用的槽,删掉最后一个可用槽等于把自己锁在门外。

性能开销:到底慢多少

磁盘加密的性能开销主要看 CPU 支持。现代 x86 CPU 有 AES-NI 指令集,能硬件加速 AES,实测加解密吞吐通常能到 GB/s 级别,对大多数网站场景(数据库、静态文件)的影响在个位数百分比。判断方法:

# 看 CPU 是否支持 AES-NI
grep -o aes /proc/cpuinfo | head -1
# 有输出说明支持,加密性能基本不用担心

# 实测加密映射设备的顺序读写
cryptsetup benchmark

如果 CPU 不支持 AES-NI(一些低端 VPS 或者被厂商禁用了该特性),AES 会走软件实现,开销可能达到 30% 甚至更高,这种情况下要谨慎评估。另外加密会消耗额外的 CPU,如果你的场景是 CPU 本来就紧张的高并发服务,加密层会成为不可忽视的成本。

故障排查与恢复

现象一:luksOpen 报 No key available with this passphrase。先确认密码没错(大小写、输入法、键盘布局),再 luksDump 看密钥槽是否还在。如果槽存在但都打不开,那基本是头部损坏。这时就需要用到之前的头部备份:cryptsetup luksHeaderRestore /dev/sdb --header-backup-file /root/luks-header-sdb1.img。恢复头部会覆盖当前头部,所以要在确认当前头部已无法使用之后再做。

现象二:卸载时报 device is busy。说明还有进程在使用该设备上的文件。用 lsof +D /mnt/secure 或 fuser -mv /mnt/secure 找出占用者,别用 -l 强制卸载,那会导致内存里的数据丢失甚至文件系统损坏。正确顺序是:停掉相关服务、umount、cryptsetup luksClose cryptdata。

现象三:磁盘容量对不上。LUKS2 头部默认占用 16MB,所以 /dev/mapper/cryptdata 会比 /dev/sdb 小 16MB 左右,这是正常的元数据开销,不是故障。如果差异远大于此,检查是不是分区本身没占满整块盘。

恢复演练。加密盘最怕的不是坏,而是「坏了才发现从没验证过头部备份能用」。至少要演练一次完整流程:在一台测试机上用头部备份恢复、用密钥文件解锁、挂载、读取数据。演练用的密码和密钥文件可以是临时的,但流程必须真跑通。

几点务实的建议

第一,不要为了「显得专业」盲目给根分区加密。加密根分区需要 initramfs 支持、开机要输密码、远程服务器一旦重启没人输密码就直接失联,风险远大于收益。个人站长的常见合理方案是「根分区不加密 + 数据分区加密」,或者只对备份文件和数据库导出做加密。

第二,加密不能替代权限管理和访问控制。加密防的是介质被直接读取,不防你 SSH 密码被撞破、也不防应用漏洞。真正的安全是多层叠加的。

第三,把「头部备份 + 密钥文件 + 密码」这三样东西分开存放、至少有一份离线。加密盘的数据丢失几乎只有两种原因:忘了密码,和丢了头部。这两件事都可以通过提前规划避免,但都不可逆。

Last modification:September 29th, 2026 at 09:25 pm

Leave a Comment