dm-crypt/交换分区加密
本文介紹了多種加密交換分區的方法,可滿足不同的需求。若在每次重啟時都重新對交換分區進行加密(使用不同密鑰)的方式,可避免換出到交換分區上的敏感文件片段因未被覆蓋而長期存在,從而更好地保護數據。然而,若使用重新加密,則無法使用休眠功能。
若無需使用休眠功能,則可以通過配置/etc/crypttab,在每次啟動時以隨機密碼,使用dm-crypt plain模式進行解密。在關機時,隨機密碼將被丟棄,設備上只含有加密的、無法訪問的交換數據。
要使用該配置,只需要取消注釋/etc/crypttab中以swap開頭的一列,並修改「設備」(device)參數指向交換分區,例如:
/etc/crypttab
# <name> <device> <password> <options> swap /dev/sdX# /dev/urandom swap,cipher=aes-xts-plain64,size=512,sector-size=4096
上述配置將解密/dev/sdX#,並映射到/dev/mapper/swap。之後,像普通的swap分區一樣,將/dev/mapper/swap添加到/etc/fstab中,即可完成交換分區配置。若之前已經配置了非加密的交換分區,需要將其禁用(或直接將其在fstab中的條目指向/dev/mapper/swap)。上面給出的選項應當足以應對大多數場景。若需要了解每個選項的詳細解釋,可參見crypttab(5)、point cryptsetup FAQ 2.3。
/dev/sda, /dev/sdb)指定設備,簡單命名的順序可能在每次啟動時都不同,導致設備數據被意外清除。建議使用如下方式指定:
- 使用
by-id、by-path。然而,當出現硬體變化時,兩者仍可能會改變,詳見塊設備持久化命名#通過_id_和_通過路徑。 - 使用PARTLABEL、PARTUUID。
- 使用LVM邏輯卷名稱。
- 使用#文件系統UUID和文件系統標籤中介紹的方法。由於每次啟動時,都將重新加密設備,並使用
mkswap重新格式化swap分區, 不能直接使用文件系統標籤和文件系統UUID,參見cryptsetup FAQ。但可通過間接手段達成目的。
若要使用by-id持久化命名,首先,找到swap分區對應的設備:
# find -L /dev/disk -samefile /dev/sdaX
/dev/disk/by-id/ata-WDC_WD2500BEVT-22ZCT0_WD-WXE908VF0470-partX /dev/disk/by-id/wwn-0x60015ee0000b237f-partX
之後,修改配置文件,使用持久化引用(若上述命令返回多個結果,任選一個即可):
/etc/crypttab
# <name> <device> <password> <options> swap /dev/disk/by-id/ata-WDC_WD2500BEVT-22ZCT0_WD-WXE908VF0470-partX /dev/urandom swap,cipher=aes-xts-plain64,size=512,sector-size=4096
重啟並激活加密交換後,使用swapon -s命令可以看到交換分區對應映射後的設備(例如,/dev/dm-1);使用lsblk命令,可看到交換分區的FSTYPE為crypt。由於每次啟動時都重新加密並格式化交換分區,/dev/mapper/swap的文件系統UUID會在每次啟動時變化。
在crypttab中使用內核簡單命名(例如,/dev/sdX#)、ID命名(例如,/dev/disk/by-id/ata-SERIAL-partX)指定加密交換分區的目標設備有較大風險,設備名稱、分區結構的細微變化都可能導致指向錯誤的設備,從而造成數據丟失。使用PARTLABEL、PARTUUID(與分區,而不是文件系統、設備綁定)的風險相對較小,但若打算將目標設備用作其它用途,而忘記移除crypttab中的條目,數據仍可能被覆蓋(文件系統改變,分區不改變)。
更可靠的方法是使用文件系統標籤或文件系統UUID。但在默認情況下,每次重啟時,dm-crypt和mkswap都將覆蓋目標分區上的內容,導致文件系統標籤和文件系統UUID被移除。不過,可以指定swap分區的起始偏移量,以在設備開始處放置另一個空的、容量較小的文件系統。該文件系統僅用於提供持久性的文件系統UUID和文件系統標籤。
使用指定文件系統標籤創建文件系統:
# mkfs.ext2 -L cryptswap /dev/sdX# 1M
設備名後的1M參數限制文件系統的大小為1MiB,為之後加密的交換分區留出空間。
# blkid /dev/sdX#
/dev/sdX#: LABEL="cryptswap" UUID="b72c384e-bd3c-49aa-b7a7-a28ea81a2605" TYPE="ext2"
這樣,無論設備名稱、分區數量如何變化,始終可以使用文件系統UUID或文件系統標籤指向/dev/sdX#。
接下來,配置/etc/crypttab和/etc/fstab條目即可:
/etc/crypttab
# <name> <device> <password> <options> swap LABEL=cryptswap /dev/urandom swap,offset=2048,cipher=aes-xts-plain64,size=512,sector-size=4096
注意偏移量的指定(offset=2048),偏移量的單位為512位元組的扇區(不受dm-crypt扇區大小的影響),因此offset=2048相當於1MiB,跳過了提供UUID的文件系統的區域,因此不影響文件系統UUID和文件系統標籤,並正確處理了數據對齊。
/etc/fstab
# <filesystem> <dir> <type> <options> <dump> <pass> /dev/mapper/swap none swap defaults 0 0
在本配置中,只會嘗試使用特定文件系統標籤的設備作為加密交換分區,而不受設備名稱影響。若打算將加密交換分區用作其它用途,當格式化時,文件系統標籤被覆蓋,因此/etc/crypttab不會在下次啟動時意外覆蓋新的文件系統及數據。
桌面環境可能無法自動檢測到swap分區已被隨機密碼加密,不能用作休眠,仍錯誤顯示休眠按鈕。
若使用Xfce,可通過如下命令隱藏「休眠」、「混合睡眠」按鈕:
$ xfconf-query -c xfce4-session -np /shutdown/ShowHibernate -t bool -s false $ xfconf-query -c xfce4-session -np /shutdown/ShowHybridSleep -t bool -s false
下面介紹在需要進行休眠的情況下,設置加密swap的三種方法。若已經使用其中一種方法完成設置,需要注意系統換出的敏感數據可能在交換分區/交換文件中保留較長時間,直到被覆蓋。若要降低相關風險,可以設置系統任務,在特定時機(例如,系統關機時)重新加密交換分區/重置交換文件。
交換文件可放置在加密設備的文件系統中。依照Swap#交換文件中的方法創建交換文件,並完成休眠設置即可。
若採用加密根文件系統的配置,並使用基於systemd的initrd,則配置加密交換文件非常簡單、靈活。在完成配置加密的根文件系統後,只需要按常規方式創建並激活交換文件,並將其添加到/etc/fstab中即可,無需額外配置。
resume=內核參數時,注意其必須指向解密後產生的映射設備,該映射設備包含存儲有交換文件的文件系統。swapoff /dev/device將其禁用,並在/etc/crypttab中移除相關條目。使用cryptsetup-luksFormat(8)創建容納交換分區的加密容器:
# cryptsetup luksFormat --label swap /dev/device
打開該容器,並映射到/dev/mapper/swap:
# cryptsetup open /dev/disk/by-label/swap swap
在映射設備上創建swap文件系統:
# mkswap /dev/mapper/swap
若不使用Systemd#GPT分區自動掛載,需要將映射設備加入/etc/fstab中:
/dev/mapper/swap none swap defaults 0 0
要啟用休眠,添加resume=/dev/mapper/swap內核參數。詳見電源管理/掛起與休眠#傳遞休眠位置到_initramfs。
通過在TPM中存儲密鑰,可自動完成交換分區的加密與解密。
可以使用systemd-cryptenroll將密鑰註冊到LUKS容器和TPM中。之後,清除之前創建的密碼槽位:
# systemd-cryptenroll --tpm2-device auto /dev/device # systemd-cryptenroll --wipe-slot password /dev/device
可通過如下命令驗證結果:
# systemd-cryptenroll /dev/device SLOT TYPE 0 tpm2
若要從加密的交換分區中喚醒,必須在initramfs中解密該分區。
若使用基於systemd的inintramfs(使用sd-encrypt鉤子),則可使用如下任意一個方案:
- 正確設置
rd.luks.uuid=內核參數以解密交換分區; - 編輯
crypttab.initramfs,並重新生成 initramfs。
例如,若使用TPM加密交換分區:
/etc/crypttab.initramfs
swap UUID=56f8ee97-54b3-4a65-9282-688deb922527 none tpm2-device=auto
若使用基於busybox的initramfs(使用encrypt鉤子),需進行如下操作。
若交換分區所在設備與根文件系統的不同,則其不會被encrypt鉤子打開。由於從休眠中喚醒的過程在讀取/etc/crypttab前進行,不能在/etc/crypttab中進行配置以打開加密的交換分區,而需要修改/etc/mkinitcpio.conf並創建自定義鉤子,以在喚醒開始之前打開加密的交換分區。
encrypt鉤子,其只能解密單個設備(archlinux/mkinitcpio/mkinitcpio#231);若使用sd-encrypt鉤子,則可以解密多個設備(包括加密的交換分區),詳見Dm-crypt/系統配置#systemd-gpt-auto-generator。mkinitcpio-openswapAUR中包含了所需的鉤子及配置文件。
-o ro,noload掛載選項。這些選項適用於ext4文件系統,若使用其它文件系統,需修改openswap.conf,使用作用相似的選項。將openswap鉤子添加到/etc/mkinitcpio.conf的HOOKS數組中,注意應當添加到filesystem鉤子之前,encrypt鉤子之後。再在openswap鉤子之後添加resume鉤子。
HOOKS=(... encrypt openswap resume filesystems ...)
最後,重新生成 initramfs。
啟動時,openswap鉤子將打開加密的交換分區,以便內核從中喚醒。若使用自定義鉤子進行喚醒操作,需確保在HOOKS數組中,相關鉤子放置在openswap之後。注意,由於在initrd階段便掛載了交換分區,/etc/crypttab中無需包含交換分區相關條目。
創建密鑰文件:
# dd bs=512 count=4 if=/dev/random iflag=fullblock | install -m 0600 /dev/stdin /etc/cryptsetup-keys.d/swap.key
將密鑰文件添加到LUKS卷:
# cryptsetup luksAddKey /dev/device /etc/cryptsetup-keys.d/swap.key
修改dracut配置,加入resume模塊,並將swap.key文件加入到initramfs中(詳見Dracut#休眠):
/etc/dracut.conf.d/resume-from-hibernate.conf
add_dracutmodules+=" resume " install_items+=" /etc/cryptsetup-keys.d/swap.key "
在內核參數中添加與交換分區對應的rd.luks.name、rd.luks.key參數。
修改後的內核命令行應當與下例相似:
kernel /vmlinuz-linux cryptdevice=/dev/sda2:root root=/dev/mapper/root resume=/dev/mapper/swap rd.luks.name=fd839505-3213-4603-9a70-c5a96a24768f=swap rd.luks.key=/etc/cryptsetup-keys.d/swap.key ro
若交換分區位於LVM卷組內,且對應卷組在initramfs中被激活,只需要按照電源管理/掛起與休眠#休眠中的步驟進行配置即可。
- 系統日誌中出現形如
Stopped (with error) /dev/dm-1的錯誤。詳見systemd issue 1620。