《磁盘加密与 LUKS 密钥管理实战》

系统讲解 Linux 磁盘加密:威胁模型与加密层次、LUKS1 与 LUKS2 格式差异、cryptsetup 的 luksFormat/open/luksDump 等基础操作、密钥槽与 passphrase/keyfile 管理、LUKS 头备份与灾难恢复、TPM 与 clevis 自动解锁、加密算法选型与性能开销,以及常见故障的排查与恢复流程。

引言

笔记本丢了、云主机磁盘被快照、退役硬盘被转手——数据躺在磁盘上时,只要介质离开你的物理控制,未加密就等于公开。磁盘加密(at-rest encryption)把数据在写入块设备前加密、读取时解密,对上层文件系统完全透明,是保护「静态数据」最直接有效的手段。Linux 上的标准方案是 dm-crypt + LUKS。

本文从威胁模型讲起,厘清「加密什么、防住谁」,介绍 LUKS1 与 LUKS2 的格式差异,手把手过一遍 cryptsetup 的 luksFormat/open/luksDump 等操作,深入密钥槽与 passphrase/keyfile 管理,强调 LUKS 头备份这一「救命操作」,再介绍 TPM 与 clevis 自动解锁、算法选型与性能开销,最后给出常见故障的恢复流程。

前置:文件系统与磁盘管理基础。存储性能与 IO 栈见 Linux IO 栈与存储性能调优,备份与容灾见 备份与灾难恢复。


目录


1. 为什么需要磁盘加密:威胁模型

磁盘加密防的是「设备丢失/被盗/被退役」这类物理或介质层面的泄露,防不住「系统被入侵后在线读取」。

威胁模型对照:

威胁加密是否有效说明
笔记本/硬盘被盗有效无密钥无法读取
退役硬盘转售有效数据不可恢复
云盘快照泄露有效快照内容是密文
系统被入侵后读取无效此时已解密挂载
内存取证(冷启动)部分密钥可能残留在内存
供应链篡改引导需 Secure Boot需配合可信启动

加密的层次:

应用层加密(数据库透明加密、文件加密)
   ↓
文件系统层(fscrypt、eCryptfs)—— 按目录/文件
   ↓
块设备层(dm-crypt/LUKS)—— 整盘透明,本文重点
   ↓
硬件层(SED 自加密盘、NVMe OPAL)

块设备层加密的优点是对应用与文件系统完全透明:上层看到的就是一个普通块设备。

grep -E 'dm_crypt|aes' /proc/crypto | head   # 内核是否支持
lsmod | grep dm_crypt
lsblk -o NAME,FSTYPE,TYPE,MOUNTPOINT          # 现有加密设备
dmsetup ls

一句话:加密保护「静态数据」,不保护「运行中的数据」——它挡的是硬盘离开你控制的那一刻,而不是挡攻击者在你机器里跑起来之后。


2. LUKS 格式演进:LUKS1 与 LUKS2

LUKS 在磁盘头部存放「元数据 + 加密的主密钥副本」,LUKS2 用 JSON 元数据、支持更灵活的密钥槽与更抗损坏的冗余头部。

LUKS 的核心设计:用 passphrase 保护的不是数据本身,而是一个随机的主密钥(master key)。改密码只需重加密主密钥副本,不必重加密整个磁盘。

磁盘头部(LUKS header)
  ├─ 元数据(版本、算法、密钥槽信息)
  ├─ 密钥槽 0..N:每个槽存一份「被 passphrase 加密的主密钥」
  └─ 主密钥(master key)→ 用于实际加密数据区

LUKS1 与 LUKS2 对比:

维度LUKS1LUKS2
元数据格式二进制JSON(可读、可扩展)
头部冗余无主 + 副头部(抗损坏)
密钥派生PBKDF2Argon2id(抗 GPU 破解)
密钥槽8 个固定最多 32 个
在线重加密不支持支持
兼容性老系统现代发行版默认
# 查看设备是 LUKS1 还是 LUKS2
cryptsetup luksDump /dev/sda2 | head -5
# 格式化时指定版本
cryptsetup luksFormat --type luks2 /dev/sda2
cryptsetup luksFormat --type luks1 /dev/sda2   # 兼容老 GRUB

一句话:新部署一律 LUKS2——Argon2id 让暴力破解成本陡增,冗余头部能在头损坏时自救;只有老 GRUB 引导根分区等特殊场景才退回 LUKS1。


3. cryptsetup 基础操作

cryptsetup 的生命周期是「luksFormat 格式化 → luksOpen 打开 → 在映射设备上建文件系统 → 挂载」。

# 1. 格式化(会警告数据将被覆盖)
cryptsetup luksFormat /dev/sdb1
# 2. 打开:建立映射设备 /dev/mapper/cryptdata
cryptsetup luksOpen /dev/sdb1 cryptdata
# 3. 在映射设备上创建文件系统
mkfs.ext4 /dev/mapper/cryptdata
# 4. 挂载使用
mount /dev/mapper/cryptdata /mnt/secure
# 5. 用完卸载并关闭
umount /mnt/secure
cryptsetup luksClose cryptdata

查看与状态:

cryptsetup luksDump /dev/sdb1        # 查看头部信息
cryptsetup status cryptdata          # 查看映射状态
cryptsetup isLuks /dev/sdb1 && echo "is LUKS"
dmsetup ls --tree                    # 查看设备映射树

/etc/crypttab 与 /etc/fstab 配合实现开机自动挂载:

# /etc/crypttab:name  device  keyfile  options
cryptdata  UUID=<luks-uuid>  /root/keyfile  luks,discard
# /etc/fstab
/dev/mapper/cryptdata  /mnt/secure  ext4  defaults  0  2
操作命令
格式化cryptsetup luksFormat /dev/sdX
打开cryptsetup luksOpen /dev/sdX name
关闭cryptsetup luksClose name
查看头部cryptsetup luksDump /dev/sdX
查看状态cryptsetup status name
获取 UUIDcryptsetup luksUUID /dev/sdX

一句话:luksFormat 是不可逆的——它会覆盖目标设备上的所有数据,执行前务必用 lsblk 反复确认设备名,写错一个字母就是数据全毁。


4. 密钥槽、passphrase 与 keyfile

LUKS 支持多个密钥槽,每个槽可放一份「用不同凭据加密的主密钥」——这是「多把钥匙开同一把锁」的机制。

# 查看密钥槽占用情况
cryptsetup luksDump /dev/sdb1 | grep -A2 Keyslots
# 添加一个新 passphrase(占用空槽)
cryptsetup luksAddKey /dev/sdb1
# 删除某个 passphrase(需提供另一个有效凭据)
cryptsetup luksRemoveKey /dev/sdb1
# 修改某个槽的 passphrase
cryptsetup luksChangeKey /dev/sdb1
# 用 keyfile 作为凭据
cryptsetup luksAddKey /dev/sdb1 /root/keyfile

keyfile 的正确做法——用随机数据而非文本:

# 生成 4KB 随机 keyfile(用随机数据,不要用文本)
dd if=/dev/urandom of=/root/keyfile bs=4096 count=1
chmod 600 /root/keyfile
cryptsetup luksOpen /dev/sdb1 cryptdata --key-file /root/keyfile
cryptsetup luksAddKey /dev/sdb1 /root/keyfile   # 添加 keyfile 凭据

密钥槽管理要点:

操作命令注意
添加凭据luksAddKey占用一个空槽
删除凭据luksRemoveKey需另一个有效凭据
修改凭据luksChangeKey原地换密码
清空某槽luksKillSlot危险,确认槽号
查看槽位luksDump看 Keyslots 段

一句话:LUKS 槽位满或凭据丢失都可能把自己锁在门外——至少保留两把可用钥匙(一个记忆中的 passphrase + 一个异地保管的 keyfile),且每次增删后都验证一遍。


5. LUKS 头备份与恢复

LUKS 头是整块盘的「命门」:头部损坏 = 数据不可恢复,而头部仅几 MB——备份头部是磁盘加密的第一条铁律。

# 备份头部(LUKS2 默认 16MB,建议全量备份)
cryptsetup luksHeaderBackup /dev/sdb1 \
  --header-backup-file /root/luks-header-sdb1.img
# 确认备份文件
ls -lh /root/luks-header-sdb1.img
# 加密保存备份(备份本身也含敏感密钥材料)
gpg -c /root/luks-header-sdb1.img

恢复头部:

# 从备份恢复(会覆盖现有头部,危险操作)
cryptsetup luksHeaderRestore /dev/sdb1 \
  --header-backup-file /root/luks-header-sdb1.img
# 恢复前确认设备与备份匹配
cryptsetup luksUUID /dev/sdb1

LUKS2 支持分离头部(detached header),把头部单独存放,进一步提升安全性:

# 创建分离头部
cryptsetup luksFormat --header /root/header.img /dev/sdb1
# 用分离头部打开
cryptsetup luksOpen --header /root/header.img /dev/sdb1 cryptdata
# 备份分离头部
cp /root/header.img /backup/header-$(date +%F).img
场景命令
备份头部cryptsetup luksHeaderBackup
恢复头部cryptsetup luksHeaderRestore
分离头部格式化luksFormat --header file.img
分离头部打开luksOpen --header file.img
校验设备 UUIDcryptsetup luksUUID

一句话:「改过密码/槽位后立刻重新备份头部」——头部备份是「那个时刻」的快照,旧备份里的密钥槽可能已被删除,拿旧备份恢复等于回到过去。


6. TPM 与 clevis 自动解锁

手动输入密码不适合无人值守的服务器,TPM + clevis 能把密钥「封印」到硬件可信状态,实现开机自动解锁——但要在便利与安全间权衡。

TPM(可信平台模块)的核心能力是密封(seal)/解封(unseal):把密钥绑定到特定的 PCR 度量值,只有启动链未被篡改时才释放。

ls /dev/tpm*                                          # 确认 TPM 设备
apt install clevis clevis-luks clevis-tpm2 clevis-systemd
clevis luks bind -d /dev/sdb1 tpm2 '{"pcr_ids":"7"}'  # 绑定到 TPM2
clevis luks list -d /dev/sdb1                         # 查看绑定
clevis luks unlock -d /dev/sdb1 -n cryptdata          # 测试解锁

clevis luks bind 会在 LUKS 里新增一个密钥槽,存放「由 TPM 保护的解密材料」。

方式便利性安全性适用
手动 passphrase低高笔记本、关键服务器
keyfile(本地)中中有安全启动的服务器
TPM2 + clevis高中高无人值守、云主机
Tang 网络绑定高中集群、需网络
# 网络绑定(Tang 服务器,适合集群)
clevis luks bind -d /dev/sdb1 tang '{"url":"http://tang.example.com"}'
# 解除绑定
clevis luks unbind -d /dev/sdb1 -s 1

安全权衡:纯 TPM2 绑定(无 PIN)在「攻击者拿到整机」的场景下防护有限——因为整机启动时 PCR 匹配,TPM 会照常解封。加 PIN 或结合 Secure Boot 才更稳妥。

一句话:TPM 绑定防的是「拆盘」,不防「整机搬走」——要真正提升强度,必须叠加 Secure Boot 与 PCR 度量,或为关键盘保留一个手动 PIN。


7. 加密性能与算法选型

AES-XTS 是现代 CPU 上最快的选择,配合 AES-NI 硬件加速开销通常低于 5%——选错算法(如 CBC)才会成为瓶颈。

# 查看 CPU 是否支持 AES-NI
grep -o 'aes' /proc/cpuinfo | head -1
# 查看内核可用加密算法
cat /proc/crypto | grep -E 'name|driver' | head -20
# 基准测试(cryptsetup benchmark)
cryptsetup benchmark

cryptsetup benchmark 输出示例:

#  Algorithm |       Key |      Encryption |      Decryption
     aes-xts        512b      3200.0 MiB/s      3400.0 MiB/s
     aes-xts        256b      3100.0 MiB/s      3300.0 MiB/s
     aes-cbc        256b      1200.0 MiB/s      1300.0 MiB/s
    serpent-xts     512b       800.0 MiB/s       820.0 MiB/s
算法模式特点推荐度
aes-xtsXTS支持并行、有硬件加速首选
aes-cbcCBC串行、慢、需 ESSIV不推荐新用
serpent-xtsXTS无硬件加速时更慢备选
chacha20无无 AES-NI 的 ARM 上更快ARM 备选
# 指定算法与密钥长度格式化
cryptsetup luksFormat --cipher aes-xts-plain64 --key-size 512 \
  --hash sha256 /dev/sdb1
# 查看现有设备的算法
cryptsetup luksDump /dev/sdb1 | grep -E 'cipher|key'

--key-size 512 在 XTS 下表示两个 256 位密钥(XTS 需要两个密钥,故 key-size 是单密钥的两倍)。

一句话:有 AES-NI 就用 aes-xts-plain64——这是几乎零开销的默认选择;discard 能保 SSD 寿命但会泄露「哪些块被使用」的元信息,敏感场景应关闭。


8. 常见故障与恢复

加密盘的故障大多集中在「头部损坏、密钥丢失、挂载顺序」三类——提前备份头部与密钥,绝大多数都能救回来。

# 故障 1:设备识别为 LUKS 但打不开(密码正确)
cryptsetup luksDump /dev/sdb1          # 检查头部是否完整
dmesg | tail -30                        # 查看内核报错
# 若头部损坏,用备份恢复
cryptsetup luksHeaderRestore /dev/sdb1 --header-backup-file /root/header.img
# 故障 2:忘记密码但还有 keyfile
cryptsetup luksOpen /dev/sdb1 cryptdata --key-file /root/keyfile
cryptsetup luksAddKey /dev/sdb1        # 加回一个新密码
# 故障 3:开机卡在 cryptsetup 提示符(crypttab UUID/keyfile 错)
cat /etc/crypttab; blkid | grep crypto
cryptsetup luksOpen /dev/sdb1 cryptdata   # 手动解锁进入系统
update-initramfs -u                       # 修复后更新 initramfs
故障现象可能原因处理
No key available密码错/槽位被删用其它凭据或备份头部
Device is not a valid LUKS头部损坏luksHeaderRestore
开机卡 cryptsetupcrypttab/UUID/keyfile 错手动解锁 + 修 crypttab
修改密码后旧密码失效槽位被覆盖确认槽位,勿 luksKillSlot 错槽
磁盘显示为满/不可写映射设备未扩容cryptsetup resize
# 扩容:先扩底层分区,再扩映射设备,最后扩文件系统
growpart /dev/sdb 1
cryptsetup resize cryptdata
resize2fs /dev/mapper/cryptdata     # ext4
xfs_growfs /mnt/secure              # xfs

一句话:「备份头部 + 记住至少一个凭据」能救回 95% 的加密盘故障——剩下的 5% 是头部与凭据双丢,那在密码学上就是真的无解。


9. 生产实践:全盘加密与云盘

生产落地分两种:终端全盘加密(LUKS + LVM)与云主机数据盘加密(云厂商 KMS + 本地 LUKS 双保险)。

标准全盘加密布局(LUKS on LVM / LVM on LUKS):

LVM on LUKS(推荐,加密整个卷组):
  /dev/sda1 → /boot(不加密,GRUB 需要)
  /dev/sda2 → LUKS → LVM → root / swap / home

LUKS on LVM(灵活,按逻辑卷加密):
  /dev/sda2 → LVM → lv_root → LUKS
# LVM on LUKS 关键步骤
cryptsetup luksFormat /dev/sda2
cryptsetup luksOpen /dev/sda2 cryptlvm
pvcreate /dev/mapper/cryptlvm
vgcreate vg0 /dev/mapper/cryptlvm
lvcreate -L 50G vg0 -n root
mkfs.ext4 /dev/vg0/root
# 更新 crypttab 与 initramfs
echo "cryptlvm UUID=$(cryptsetup luksUUID /dev/sda2) none luks" >> /etc/crypttab
update-initramfs -u

云主机数据盘加密实践:

# 云盘 + 本地 LUKS 双保险(云 KMS + 自管 LUKS)
cryptsetup luksFormat --type luks2 /dev/nvme1n1
cryptsetup luksOpen /dev/nvme1n1 clouddata
mkfs.xfs /dev/mapper/clouddata
clevis luks bind -d /dev/nvme1n1 tang '{"url":"http://tang.internal"}'
场景方案密钥来源
笔记本LUKS + passphrase人工输入
无人值守服务器LUKS + TPM2TPM 封印
集群LUKS + Tang网络绑定
云数据盘云 KMS + LUKSKMS + Tang
# 密钥管理红线
# 1. 头部与 keyfile 异地备份(加密保存)
# 2. 定期演练恢复流程
# 3. 密钥不进 Git、不进镜像
# 4. 离职人员凭据及时 luksRemoveKey

一句话:加密不是「设了就完事」,而是「密钥全生命周期管理」——生成、分发、备份、轮换、吊销、恢复,每一环都要有流程,否则加密只会变成新的单点故障。


10. 速查表

需求命令
查看内核支持grep dm_crypt /proc/crypto
格式化 LUKS2cryptsetup luksFormat --type luks2 /dev/sdX
打开映射cryptsetup luksOpen /dev/sdX name
关闭映射cryptsetup luksClose name
查看头部/状态cryptsetup luksDump / status
获取 UUIDcryptsetup luksUUID /dev/sdX
添加/删除凭据cryptsetup luksAddKey / luksRemoveKey
备份头部cryptsetup luksHeaderBackup /dev/sdX --header-backup-file f
恢复头部cryptsetup luksHeaderRestore /dev/sdX --header-backup-file f
性能测试cryptsetup benchmark
TPM 绑定clevis luks bind -d /dev/sdX tpm2 '{"pcr_ids":"7"}'

一句话记忆:LUKS2 + aes-xts-plain64 是新盘标配;密钥槽至少留两把钥匙、头部备份是第一条铁律;无人值守用 TPM/clevis,但别忘了 TPM 只防「拆盘」;加密的难点从来不是加密本身,而是密钥的全生命周期管理。


延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「linux」更多文章

  1. 《Linux 故障排查工具箱:从 strace 到火焰图》
  2. 《文本处理三剑客:grep、sed、awk 与 jq 实战》
  3. 《Linux 时间同步:chrony、NTP 与高精度时间实践》