引言
笔记本丢了、云主机磁盘被快照、退役硬盘被转手——数据躺在磁盘上时,只要介质离开你的物理控制,未加密就等于公开。磁盘加密(at-rest encryption)把数据在写入块设备前加密、读取时解密,对上层文件系统完全透明,是保护「静态数据」最直接有效的手段。Linux 上的标准方案是 dm-crypt + LUKS。
本文从威胁模型讲起,厘清「加密什么、防住谁」,介绍 LUKS1 与 LUKS2 的格式差异,手把手过一遍 cryptsetup 的 luksFormat/open/luksDump 等操作,深入密钥槽与 passphrase/keyfile 管理,强调 LUKS 头备份这一「救命操作」,再介绍 TPM 与 clevis 自动解锁、算法选型与性能开销,最后给出常见故障的恢复流程。
前置:文件系统与磁盘管理基础。存储性能与 IO 栈见 Linux IO 栈与存储性能调优,备份与容灾见 备份与灾难恢复。
目录
- 1. 为什么需要磁盘加密:威胁模型
- 2. LUKS 格式演进:LUKS1 与 LUKS2
- 3. cryptsetup 基础操作
- 4. 密钥槽、passphrase 与 keyfile
- 5. LUKS 头备份与恢复
- 6. TPM 与 clevis 自动解锁
- 7. 加密性能与算法选型
- 8. 常见故障与恢复
- 9. 生产实践:全盘加密与云盘
- 10. 速查表
- 延伸阅读
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 对比:
| 维度 | LUKS1 | LUKS2 |
|---|---|---|
| 元数据格式 | 二进制 | JSON(可读、可扩展) |
| 头部冗余 | 无 | 主 + 副头部(抗损坏) |
| 密钥派生 | PBKDF2 | Argon2id(抗 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 |
| 获取 UUID | cryptsetup 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 |
| 校验设备 UUID | cryptsetup 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-xts | XTS | 支持并行、有硬件加速 | 首选 |
| aes-cbc | CBC | 串行、慢、需 ESSIV | 不推荐新用 |
| serpent-xts | XTS | 无硬件加速时更慢 | 备选 |
| 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 |
| 开机卡 cryptsetup | crypttab/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 + TPM2 | TPM 封印 |
| 集群 | LUKS + Tang | 网络绑定 |
| 云数据盘 | 云 KMS + LUKS | KMS + Tang |
# 密钥管理红线
# 1. 头部与 keyfile 异地备份(加密保存)
# 2. 定期演练恢复流程
# 3. 密钥不进 Git、不进镜像
# 4. 离职人员凭据及时 luksRemoveKey
一句话:加密不是「设了就完事」,而是「密钥全生命周期管理」——生成、分发、备份、轮换、吊销、恢复,每一环都要有流程,否则加密只会变成新的单点故障。
10. 速查表
| 需求 | 命令 |
|---|---|
| 查看内核支持 | grep dm_crypt /proc/crypto |
| 格式化 LUKS2 | cryptsetup luksFormat --type luks2 /dev/sdX |
| 打开映射 | cryptsetup luksOpen /dev/sdX name |
| 关闭映射 | cryptsetup luksClose name |
| 查看头部/状态 | cryptsetup luksDump / status |
| 获取 UUID | cryptsetup 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 只防「拆盘」;加密的难点从来不是加密本身,而是密钥的全生命周期管理。
延伸阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。