暗色模式

Linux 磁盘加密:从 LUKS 原理到 cryptsetup 加密与解锁挂载

技术教程
2026-09-08
2
0
本文要点
  • LUKS 是磁盘/分区级的加密标准:它把整个块设备(整块盘、分区、云盘、镜像文件)变成密文,与 gpg、openssl 那种「只加密单个文件」的方式互补——文件本身的文件名、大小、目录结构也被一起藏起来
  • cryptsetup luksFormat 完成初始化,设备会被识别为 crypto_LUKS;解锁用 cryptsetup open,通过内核 dm-crypt 映射出 /dev/mapper/<名字> 明文设备,之后 mkfs、mount、读写全部照常
  • 实测:256 MiB 镜像加密后,直接 mount 会报 unknown filesystem type 'crypto_LUKS'——没有口令时内核看到的只是一堆熵;这正是「加密生效」最直观的证据
  • cryptsetup luksDump 可以查看加密头:实测为 LUKS2、aes-xts-plain64、512 位密钥、argon2id 抗暴力破解参数,头部约占 16 MiB
  • 口令只用来「解开」头里保存的主密钥,所以 换口令不用重加密数据;头损坏后数据也无法解锁,务必用 luksHeaderBackup 备份头部
  • 全部命令在 Ubuntu 24.04.4(内核 6.8、cryptsetup 2.7.0)上真实执行,截图即真实输出;演示用镜像文件 + loop 设备,不触碰服务器真实磁盘

Linux 磁盘加密:从 LUKS 原理到 cryptsetup 加密与解锁挂载

服务器上的敏感数据,通常靠两类手段保护:一类是权限与访问控制(防止别人读),另一类是加密(即使数据被拷走也读不出内容)。文件加密工具(openssl、GPG)能保护单个文件,但救不了「整块数据盘被拔走」「云盘快照外泄」「笔记本/移动硬盘丢失」这类场景——这时候需要的是磁盘级加密:让整块设备上的所有字节都以密文形态落盘。

Linux 生态里磁盘加密的事实标准就是 LUKS(Linux Unified Key Setup),配套工具是 cryptsetup。它的做法是在块设备层动手:加密后的设备上没有任何可识别的文件系统结构,解锁后由内核的 dm-crypt 模块映射出一个虚拟的明文设备,应用层完全无感。本文在一台 Ubuntu 24.04.4(内核 6.8、cryptsetup 2.7.0)上走完整条链路:制作加密盘 → 解锁挂载 → 验证加密真实性 → 备份头部,全部命令实测,截图即真实输出。

准备一块「假磁盘」:镜像文件 + loop 设备

演示机是台云服务器,没有第二块裸盘可供格式化。好在 LUKS 面向的是块设备,而 Linux 的 loop 设备可以把一个普通文件虚拟成块设备——把对真实磁盘的操作原样搬到镜像文件上,命令几乎一字不差,实验完删文件即可,完全不碰系统盘。这也是想在不重启的情况下给云服务器数据盘做加密评估时的常用手法。

先创建一个 256 MiB 的全零镜像文件:

dd if=/dev/zero of=/root/luks-demo.img bs=1M count=256 status=progress

实测输出 268435456 bytes (268 MB, 256 MiB) copied, 0.389 s, 690 MB/s。用 losetup 把它挂成 loop 设备:

losetup -fP --show /root/luks-demo.img

-f 让内核自动挑选一个空闲的 loop 设备号,--show 打印实际分配的结果。演示机上返回的是 /dev/loop5——注意这个编号因机器而异,你大概率会得到 /dev/loop0下文所有命令中的 /dev/loop5 请替换成你机器实际输出的编号

luksFormat 初始化:往设备里写入加密头

LUKS 设备上有一个 LUKS 头(header),相当于加密盘的"登记簿":它记录加密算法、密钥参数、口令槽等元数据。初始化就是写入这个头。cryptsetup luksFormat 需要提供口令;真实使用时建议直接交互输入(命令不加任何参数,终端会两次提示输入口令),本文为了在无人值守的脚本里演示,把口令放进一个权限 600 的口令文件,效果等价:

printf 'DemoPass-2026' > /root/luks-pass.txt
chmod 600 /root/luks-pass.txt
cryptsetup luksFormat --batch-mode --key-file /root/luks-pass.txt /dev/loop5

--batch-mode 跳过交互确认,--key-file 从文件读口令。命令成功时没有任何输出。验证一下设备现在的身份:

blkid /dev/loop5

实测输出中设备类型为 TYPE="crypto_LUKS"(UUID 每次 luksFormat 都会重新生成,以你机器实际输出为准)。内核已经认不出任何文件系统,只认 crypto_LUKS:此刻设备上的一切(包括之后写入的所有文件系统结构)都是密文。

luksDump 解读加密头:算法、密钥与口令槽

luksDump 可以查看加密头的完整内容,这是理解 LUKS 内部结构最好的窗口:

cryptsetup luksDump /dev/loop5

cryptsetup luksDump 查看加密头

逐段解读这张实测输出:

  • Version 2、Metadata area 16384、Keyslots area 16744448:这是 LUKS2 格式。头部由约 16 KiB 元数据区 + 约 16 MiB 口令槽区组成,共约 16 MiB——这也是后面 lsblk 里 256 MiB 镜像只能看到 240 MiB 数据区的原因,前 16 MiB 是头。
  • Data segments: cipher aes-xts-plain64、sector 512:数据区用 AES-XTS 模式加密,这是磁盘加密的标准模式(XTS 专为块设备设计,抗已知明文攻击与密文篡改)。plain64 指扇区号作为初始向量的编码方式。
  • Keyslots: 0: luks2,Key: 512 bits,Cipher key: 512 bits:LUKS2 默认生成 512 位 AES-256-XTS 主密钥(XTS 需要两把 256 位子密钥,故记 512 位)。LUKS2 支持最多 32 个口令槽,你可以给同一块盘设置多个口令(比如管理员一人一个),随时增删而无需重加密。
  • PBKDF: argon2id、Time cost 4、Memory 790893、Threads 2:这是口令派生函数参数。你的口令不会直接用作加密密钥,而是经 argon2id(内存硬函数,实测 Memory 790893 KiB,约 772 MiB,靠海量内存开销对抗 GPU 暴力破解)派生后,用于"解开"头里保存的、加密过的主密钥。攻击者拿不到头里的主密钥就无法解密数据,只能对每个猜测口令跑一遍完整的 argon2id——这正是 LUKS 抗离线暴力破解的关键。
  • Digests: pbkdf2、Iterations 93622:口令正确性的校验摘要。

一句话总结 LUKS 的密钥模型:数据用随机生成的主密钥加密,主密钥被口令派生出的密钥包裹后存在头里。所以改口令只是重新包裹一次主密钥,瞬间完成;也正是因为头里没有保存任何主密钥的明文副本,忘记口令 = 数据永久丢失,没有任何后门。

解锁与挂载:cryptsetup open 与 /dev/mapper

加密盘要先用 open 解锁。cryptsetup open 读取头部、验证口令、把主密钥交给内核的 dm-crypt 模块,然后映射出一个新的设备 /dev/mapper/&lt;名字&gt;——对上层来说这就是一块普通明文磁盘,读写时由内核实时解密/加密:

cryptsetup open /dev/loop5 crypt-demo --key-file /root/luks-pass.txt
mkdir -p /mnt/demo-crypt
mkfs.ext4 /dev/mapper/crypt-demo
mount /dev/mapper/crypt-demo /mnt/demo-crypt
echo 'TopSecret-2026-Demo' > /mnt/demo-crypt/secret.txt
cat /mnt/demo-crypt/secret.txt
df -h /mnt/demo-crypt

在解锁并挂载后,用 lsblk 查看设备树(限定设备参数,避免整机无关设备刷屏):

lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS /dev/loop5

lsblk 查看解密后的设备树

输出里有两层结构,很能说明问题:

  • loop5:底层加密设备,类型 loop,文件系统 crypto_LUKS
  • └─crypt-demo:dm-crypt 映射出的明文设备,类型 crypt,上面是 ext4,挂在 /mnt/demo-crypt

可以看到数据区只有 240 MiB(256 MiB 减去 16 MiB 头部)——大小对得上。cat 实测输出 TopSecret-2026-Demodf 实测 /dev/mapper/crypt-demo 209M 28K 192M 1% /mnt/demo-crypt,与普通磁盘毫无差别。解密只发生在内存里的映射层,落盘的一律是密文。

加密是真的:关闭后直接挂载的失败现场

解锁状态下一切正常,那关闭之后呢?卸载并关闭映射:

umount /mnt/demo-crypt
cryptsetup close crypt-demo

此时再去直接挂载镜像文件(内核会自动把它挂成 loop 设备并尝试识别文件系统):

mount /root/luks-demo.img /mnt/demo-crypt

直接挂载加密镜像失败

实测报错 mount: /mnt/demo-crypt: unknown filesystem type 'crypto_LUKS'。ext4 文件系统头此刻是密文,没有口令的内核只能看到一堆熵——这就是磁盘加密区别于文件加密的地方:文件系统结构、文件名、目录树全部不可见。想验证这一点,可以试试对镜像执行 strings /root/luks-demo.img | head,除了头部少量明文元数据外不会有任何可读内容。

头部备份:比数据本身更重要的 16 MiB

LUKS 头一旦损坏(比如磁盘前 16 MiB 出现坏道),即使口令正确也无法解锁——因为主密钥的包裹副本在头里。数据区本身没坏也没用。所以生产环境必须单独备份头部:

cryptsetup luksHeaderBackup /dev/loop5 --header-backup-file /root/luks-header.bak

实测备份文件为 -r-------- 1 root root 16777216 /root/luks-header.bak——正好 16 MiB,权限 600。备份应该放到另一块介质上(云上可放对象存储)。万一头部损坏,换块新盘重建分区后用 luksHeaderRestore 还原头部,再用原口令解锁即可:

cryptsetup luksHeaderRestore /dev/loop5 --header-backup-file /root/luks-header.bak
⚠️ luksHeaderRestore 会用备份头覆盖当前头,相当于把设备"恢复出厂"到备份时刻的状态。仅在确认头部损坏或设备被初始化错时使用;如果只是想撤销某次操作,请先做当前头的备份。

真实用法:开机自动解锁与安全提醒

给服务器数据盘做 LUKS 加密后,重启需要重新解锁。手动场景执行 cryptsetup open 即可;开机自动解锁则在 /etc/crypttab 里登记映射,并在 /etc/fstab 里照常挂载明文设备。以本文演示为例(真实场景使用分区的 UUID 而不是 loop 文件):

# /etc/crypttab
crypt-demo UUID=<用 blkid 查到的分区 UUID> none luks
# /etc/fstab
/dev/mapper/crypt-demo /mnt/demo-crypt ext4 defaults 0 2

none 表示开机交互输入口令;也可以用 keyfile 字段指定密钥文件实现全自动解锁——代价是密钥文件必须放在不加密的引导分区上,安全性弱一档,适合机房无人值守但数据仍需静态加密的场景。本文不把演示机配置成开机自动解锁(涉及 initramfs 与启动链,且演示用的 loop 设备重启即失效),这两段代码仅供理解,未在演示机执行。

最后几条实践经验:LUKS 没有后门,口令即一切,忘记口令请直接放弃这块盘;口令建议用密码管理器生成的高强度随机串,并额外做头部备份;系统盘建议安装时直接选 LUKS 整盘加密,数据盘/镜像文件加密则随时可补。

小结

把本文的命令串起来,就是一个完整的加密盘生命周期:dd 造镜像 → losetup 挂成块设备 → luksFormat 初始化 → open 解锁得到 /dev/mapper 明文设备 → 照常 mkfs/mount 使用 → close 归还 → luksHeaderBackup 备份头部。演示机的清理同样重要,删掉镜像、口令文件与备份,归还 loop 设备:

losetup -d /dev/loop5
rm -f /root/luks-demo.img /root/luks-pass.txt /root/luks-header.bak
rmdir /mnt/demo-crypt

磁盘加密与文件加密是互补关系:GPG/openssl 适合「把某个文件发给别人」的传输场景,LUKS 适合「整块存储落盘即密文」的静态场景。更完整的参考见 cryptsetup 官方文档Arch Wiki 的 dm-crypt 条目Linux 内核文档 LUKS 规范

发表评论

暂无评论,快来抢沙发吧!