介紹如何使用 Cryptsetup 磁碟加密設定工具,搭配 LUKS 格式加密 Linux 的磁碟內容。
cryptsetup 是一套開放原始碼的磁碟加密設定工具,支援各種以 dm-crypt 為基礎的磁碟加密格式,包含基本的 plain dm-crypt 格式與 LUKS(Linux Unified Key Setup)加密格式。
在使用 plain dm-crypt 加密格式時,若對於密碼學沒有一定的基礎,有可能會因為使用者本身的操作錯誤,造成安全性降低的問題,而 LUKS 在設計上會避免這些問題,所以一般來說建議使用 LUKS。
若在 Ubuntu 或 Debian Linux 中,可以使用 apt
安裝 cryptsetup
套件:
# 安裝 cryptsetup 套件
sudo apt install cryptsetup
選擇一個要加密的硬碟,使用 cryptsetup
的 luksFormat
在其中建立一個 LUKS2 container,建立加密磁碟時會覆蓋所有磁碟中的資料,所以 cryptsetup
會詢問是否確認要執行,輸入大寫的 YES
之後,再輸入加密磁碟用的密碼即可建立 LUKS2 加密磁碟:
# 在 /dev/sdb 磁碟中建立 LUKS2 container sudo cryptsetup -v -y --type luks2 luksFormat /dev/vdb
WARNING! ======== This will overwrite data on /dev/vdb irrevocably. Are you sure? (Type 'yes' in capital letters): YES Enter passphrase for /dev/vdb: Verify passphrase: Key slot 0 created. Command successful.
建立好 LUKS2 加密磁碟之後,我們可以使用 cryptsetup
的 luksDump
指令查看磁碟內的 LUKS 標頭資訊:
# 查看 /dev/vdb 磁碟中的 LUKS 標頭資訊
sudo cryptsetup luksDump /dev/vdb
LUKS header information Version: 2 Epoch: 3 Metadata area: 16384 [bytes] Keyslots area: 16744448 [bytes] UUID: a2935dbb-bedd-4f87-9229-f22ac06f6350 Label: (no label) Subsystem: (no subsystem) Flags: (no flags) Data segments: 0: crypt offset: 16777216 [bytes] length: (whole device) cipher: aes-xts-plain64 sector: 512 [bytes] Keyslots: 0: luks2 Key: 512 bits Priority: normal Cipher: aes-xts-plain64 Cipher key: 512 bits PBKDF: argon2id Time cost: 4 Memory: 742242 Threads: 2 Salt: 72 c9 91 18 32 5c 6f 49 f4 01 6f 42 9f 08 e5 7f 33 7c d0 54 25 14 f9 20 23 c6 07 c6 eb de 99 92 AF stripes: 4000 AF hash: sha256 Area offset:32768 [bytes] Area length:258048 [bytes] Digest ID: 0 Tokens: Digests: 0: pbkdf2 Hash: sha256 Iterations: 113188 Salt: df d1 d1 28 0e b1 a5 e1 60 b3 df 01 98 03 06 b0 e3 f7 87 a3 a9 a8 32 0f 9f e5 ce 28 b2 ba f0 1f Digest: 61 a8 35 f5 2f cc 5b a9 6c c0 d2 29 54 09 52 a4 4b 63 84 54 ae 8c fd 0e f4 1f ea 46 06 ad a8 b2
若要使用 LUKS2 加密磁碟,要先以 cryptsetup
的 open
指令開啟加密磁碟,並且建立一個映射檔:
# 開啟加密磁碟,建立映射檔 sudo cryptsetup open --type luks /dev/vdb myEncDisk
Enter passphrase for /dev/vdb:
這行指令的最後兩個參數分別為加密磁碟以及映射檔名稱,開啟加密磁碟時要輸入密碼,若密碼正確,這行指令就會建立 /dev/mapper/myEncDisk
這個映射檔。
映射檔建立好之後,可以使用以下指令查看映射檔的狀態:
# 查看 myEncDisk 映射檔狀態 sudo cryptsetup -v status myEncDisk
/dev/mapper/myEncDisk is active. type: LUKS2 cipher: aes-xts-plain64 keysize: 512 bits key location: keyring device: /dev/vdb sector size: 512 offset: 32768 sectors size: 20938752 sectors mode: read/write Command successful.
在建立好 LUKS2 加密磁碟之後,建議使用 dd
指令先做一次完整的磁碟格式化,讓整顆填入 0
,從外界看起來磁碟會呈現亂數,這可以讓外部無從得知磁碟的使用狀況:
# 完整磁碟格式化 sudo dd if=/dev/zero of=/dev/mapper/myEncDisk bs=128M
通常完整格式化整顆磁碟需要非常久的時間,建議可以加上 pv
指令顯示執行的進度:
# 完整磁碟格式化(顯示進度) pv -tpreb /dev/zero | sudo dd of=/dev/mapper/myEncDisk bs=128M
或是在執行 dd
指令時加上 status=progress
參數:
# 完整磁碟格式化(顯示進度) sudo dd if=/dev/zero of=/dev/mapper/myEncDisk bs=128M status=progress
接著使用 mkfs.ext4
指令建立 Ext4 檔案系統:
# 在加密磁碟上建立 Ext4 檔案系統
sudo mkfs.ext4 /dev/mapper/myEncDisk
mke2fs 1.46.5 (30-Dec-2021) Creating filesystem with 2617344 4k blocks and 655360 inodes Filesystem UUID: 3bca19af-24c5-41df-9357-76a111e05515 Superblock backups stored on blocks: 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632 Allocating group tables: done Writing inode tables: done Creating journal (16384 blocks): done Writing superblocks and filesystem accounting information: done
加密磁碟的掛載方式跟普通磁碟類似,只不過加密磁碟要透過映射檔進行掛載:
# 建立掛載目錄 sudo mkdir /mnt/myEncDisk # 掛載加密磁碟 sudo mount /dev/mapper/myEncDisk /mnt/myEncDisk
掛載之後,使用 df
檢查磁碟掛載狀況:
# 檢查磁碟掛載狀況 df -h
Filesystem Size Used Avail Use% Mounted on tmpfs 1.6G 984K 1.6G 1% /run /dev/vda1 94G 4.2G 86G 5% / tmpfs 7.9G 0 7.9G 0% /dev/shm tmpfs 5.0M 0 5.0M 0% /run/lock tmpfs 1.6G 4.0K 1.6G 1% /run/user/1000 /dev/mapper/myEncDisk 9.8G 24K 9.3G 1% /mnt/myEncDisk
若要卸載加密磁碟,要先以 umount
卸載磁碟:
# 卸載加密磁碟
sudo umount /mnt/myEncDisk
接著再關閉加密磁碟:
# 關閉加密磁碟
sudo cryptsetup close myEncDisk
當我們完成第一次加密磁碟的建立流程之後,後續要使用加密磁碟時,只要以 cryptsetup
的 open
指令開啟加密磁碟,再用 mount
掛載後,即可使用:
# 開啟加密磁碟,建立映射檔 sudo cryptsetup open --type luks /dev/vdb myEncDisk # 掛載加密磁碟 sudo mount /dev/mapper/myEncDisk /mnt/myEncDisk
若要以 fsck
檢查 LUKS 加密磁碟,可以先以 umount
卸載磁碟:
# 卸載加密磁碟
sudo umount /mnt/myEncDisk
再以 fsck
透過映射檔檢查磁碟:
# 以 fsck 檢查加密磁碟 sudo fsck -vy /dev/mapper/myEncDisk
檢查完之後,重新掛載磁碟:
# 重新掛載加密磁碟
sudo mount /dev/mapper/myEncDisk /mnt/myEncDisk
每個 LUKS2 加密的磁碟最多可以設置 32 組密碼(key-slot),我們可以使用 cryptsetup
的 luksDump
指令查看目前的密碼設置狀態:
# 查看 /dev/sdb 磁碟中的 LUKS 標頭資訊
sudo cryptsetup luksDump /dev/vdb
LUKS header information Version: 2 Epoch: 3 Metadata area: 16384 [bytes] Keyslots area: 16744448 [bytes] UUID: a2935dbb-bedd-4f87-9229-f22ac06f6350 Label: (no label) Subsystem: (no subsystem) Flags: (no flags) Data segments: 0: crypt offset: 16777216 [bytes] length: (whole device) cipher: aes-xts-plain64 sector: 512 [bytes] Keyslots: 0: luks2 Key: 512 bits Priority: normal Cipher: aes-xts-plain64 Cipher key: 512 bits PBKDF: argon2id Time cost: 4 Memory: 742242 Threads: 2 Salt: 72 c9 91 18 32 5c 6f 49 f4 01 6f 42 9f 08 e5 7f 33 7c d0 54 25 14 f9 20 23 c6 07 c6 eb de 99 92 AF stripes: 4000 AF hash: sha256 Area offset:32768 [bytes] Area length:258048 [bytes] Digest ID: 0 Tokens: Digests: 0: pbkdf2 Hash: sha256 Iterations: 113188 Salt: df d1 d1 28 0e b1 a5 e1 60 b3 df 01 98 03 06 b0 e3 f7 87 a3 a9 a8 32 0f 9f e5 ce 28 b2 ba f0 1f Digest: 61 a8 35 f5 2f cc 5b a9 6c c0 d2 29 54 09 52 a4 4b 63 84 54 ae 8c fd 0e f4 1f ea 46 06 ad a8 b2
若要新增一組 LUKS 加密磁碟,可以使用 cryptsetup
的 luksAddKey
指令:
# 新增 LUKS 加密磁碟的密碼
sudo cryptsetup luksAddKey /dev/vdb
Enter any existing passphrase: Enter new passphrase for key slot: Verify passphrase:
在新增密碼時,必須輸入一組既有的密碼,然後再輸入兩次新增的密碼。完成後可以再次以 cryptsetup
的 luksDump
指令查看目前的密碼設置狀態。
# 查看 /dev/sdb 磁碟中的 LUKS 標頭資訊
sudo cryptsetup luksDump /dev/vdb
LUKS header information Version: 2 Epoch: 4 Metadata area: 16384 [bytes] Keyslots area: 16744448 [bytes] UUID: a2935dbb-bedd-4f87-9229-f22ac06f6350 Label: (no label) Subsystem: (no subsystem) Flags: (no flags) Data segments: 0: crypt offset: 16777216 [bytes] length: (whole device) cipher: aes-xts-plain64 sector: 512 [bytes] Keyslots: 0: luks2 Key: 512 bits Priority: normal Cipher: aes-xts-plain64 Cipher key: 512 bits PBKDF: argon2id Time cost: 4 Memory: 742242 Threads: 2 Salt: 72 c9 91 18 32 5c 6f 49 f4 01 6f 42 9f 08 e5 7f 33 7c d0 54 25 14 f9 20 23 c6 07 c6 eb de 99 92 AF stripes: 4000 AF hash: sha256 Area offset:32768 [bytes] Area length:258048 [bytes] Digest ID: 0 1: luks2 Key: 512 bits Priority: normal Cipher: aes-xts-plain64 Cipher key: 512 bits PBKDF: argon2id Time cost: 4 Memory: 952916 Threads: 2 Salt: 9d 75 d6 3f d3 36 50 6c 0d 3b d9 1e 4e c9 f7 e4 d5 56 8b 41 bf 3e c1 01 11 eb 13 49 46 f0 83 99 AF stripes: 4000 AF hash: sha256 Area offset:290816 [bytes] Area length:258048 [bytes] Digest ID: 0 Tokens: Digests: 0: pbkdf2 Hash: sha256 Iterations: 113188 Salt: df d1 d1 28 0e b1 a5 e1 60 b3 df 01 98 03 06 b0 e3 f7 87 a3 a9 a8 32 0f 9f e5 ce 28 b2 ba f0 1f Digest: 61 a8 35 f5 2f cc 5b a9 6c c0 d2 29 54 09 52 a4 4b 63 84 54 ae 8c fd 0e f4 1f ea 46 06 ad a8 b2
我們可以在執行 cryptsetup
時加上 --test-passphrase
參數,測試輸入的密碼符合哪一個 key-slot 中的密碼:
# 測試密碼 sudo cryptsetup --test-passphrase -v open /dev/vdb
No usable token is available. Enter passphrase for /dev/vdb: Key slot 0 unlocked. Command successful.
若要移除既有的 LUKS 加密磁碟密碼,可以使用 cryptsetup
的 luksRemoveKey
指令,執行之後輸入要移除的密碼:
# 移除 LUKS 加密磁碟的密碼
sudo cryptsetup luksRemoveKey /dev/vdb
Enter passphrase to be deleted:
若要移除指定 key-slot 中的密碼,可以使用 cryptsetup
的 luksKillSlot
指令:
# 移除 LUKS 加密磁碟中第 0 個 key-slot 密碼
sudo cryptsetup luksKillSlot /dev/vdb 0
Enter any remaining passphrase:
LUKS 與 dm-crypt 對於記憶體(RAM)的負荷較大,尤其是在結合 RAID5 + LUKS1 + XFS 的情況下,會讓平常沒有發現的記憶體問題浮現出來,各種情況的問題不同,但最常出問題的時機就是在複製大量資料(比記憶體大數倍)的狀況。
若要避免記憶體因素導致資料錯誤,在進行大量檔案的複製或搬移時,都要同時進行檔案的驗證,確保檔案的完整性。例如使用 tar
壓縮檔案時,可以使用 -d
參數來驗證壓縮黨的內容與原始資料相同。
另外也可以使用 MD5 等檢查碼來驗證檔案內容,從原始檔案先計算每個檔案的 MD5 檢查碼:
# 計算所有目錄與子目錄下每個檔案的 MD5 檢查碼 find . -type f -exec md5sum \{\} \; > checksum-file
然後再檔案完成搬移或複製之後,驗證 MD5 檢查碼:
# 驗證 MD5 檢查碼 md5sum -c checksum-file
如果檔案的驗證沒有通過,最有可能出問題的地方就是記憶體,而 CPU 的超頻也是會有一些嫌疑,位元的錯誤發生率比一般人想像中高出許多。
一些比較嚴重的記憶體問題還會造成電腦當機、CPU 狀態傾印、kernel panic 等,加密的硬碟對於記憶體問題會更敏感,一些小問題對於普通硬碟可能不會有太大的影響,但是加密的硬碟會把問題放大,在一些新穎的加密演算法中,如果在加密區塊中有一個位元錯誤,整個區塊資料解密之後就會有一半的位元是錯誤的,而在 cryptsetup 常用的配置(CBC、ESSIV、XTS)下,一個位元的錯誤就可以造成整個 512 位元組區塊資料的改變,這個影響遠超過單一位元的小錯誤。
在複製或搬移大量資料之後,縱使複製或搬移指令都沒有出現任何錯誤,也一定要進行資料的驗證,確保資料的正確性與完整性,若發現記憶體有損壞的情況,那所有的資料都存在有損壞的風險,都要進行驗證。
LUKS 加密磁碟標頭中包含了最關鍵的加密資訊,這標頭部分的資料損毀,會造成所有資料無法復原,所以標頭資訊的備份是相當重要的。
我們可以利用以下指令將 LUKS 加密磁碟的標頭資訊備份至檔案中:
# 備份 LUKS 加密磁碟標頭 sudo cryptsetup luksHeaderBackup /dev/vdb --header-backup-file myHeaderBackup.img
執行這一行指令之後,就會將 /dev/vdb
這個 LUKS 加密磁碟的標頭資訊備份至 myHeaderBackup.img
這個檔案中。
若使用錯誤的標頭檔案會造成整個硬碟的資料完全無法解密,所以在從備份檔案復原 LUKS 加密磁碟的標頭資訊之前,建議先以 --header
測試標頭資訊是否可以正常使用,避免選用錯誤的標頭備份檔:
# 自訂標頭檔,開啟加密磁碟,建立映射檔 sudo cryptsetup -v --header myHeaderBackup.img open --type luks /dev/vdb myEncDisk
建立映射檔之後,測試一下是否可以正常掛載:
# 測試掛載加密磁碟
sudo mount /dev/mapper/myEncDisk /mnt/myEncDisk
sudo umount /mnt/myEncDisk
sudo cryptsetup close myEncDisk
若備份的標頭檔可以正常使用,就可以進行復原動作,復原時要輸入大寫 YES
確認進行復原的動作:
# 復原 LUKS 加密磁碟標頭 sudo cryptsetup luksHeaderRestore /dev/vdb --header-backup-file myHeaderBackup.img
WARNING! ======== Device /dev/vdb already contains LUKS2 header. Replacing header will destroy existing keyslots. Are you sure? (Type 'yes' in capital letters): YES