Linux

Linux 以 Cryptsetup 與 LUKS 加密磁碟教學與範例

介紹如何使用 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

建立 LUKS2 加密磁碟

選擇一個要加密的硬碟,使用 cryptsetupluksFormat 在其中建立一個 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 加密磁碟時,會將原本磁碟中的資料抹除,就類似普通的格式化硬碟動作,所以執行之前要先確認磁碟中沒有任何重要資料。

建立好 LUKS2 加密磁碟之後,我們可以使用 cryptsetupluksDump 指令查看磁碟內的 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 加密磁碟,要先以 cryptsetupopen 指令開啟加密磁碟,並且建立一個映射檔:

# 開啟加密磁碟,建立映射檔
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

掛載既有加密磁碟

當我們完成第一次加密磁碟的建立流程之後,後續要使用加密磁碟時,只要以 cryptsetupopen 指令開啟加密磁碟,再用 mount 掛載後,即可使用:

# 開啟加密磁碟,建立映射檔
sudo cryptsetup open --type luks /dev/vdb myEncDisk

# 掛載加密磁碟
sudo mount /dev/mapper/myEncDisk /mnt/myEncDisk

以 fsck 檢查 LUKS 加密磁碟

若要以 fsck 檢查 LUKS 加密磁碟,可以先以 umount 卸載磁碟:

# 卸載加密磁碟
sudo umount /mnt/myEncDisk

再以 fsck 透過映射檔檢查磁碟:

# 以 fsck 檢查加密磁碟
sudo fsck -vy /dev/mapper/myEncDisk

檢查完之後,重新掛載磁碟:

# 重新掛載加密磁碟
sudo mount /dev/mapper/myEncDisk /mnt/myEncDisk

變更 LUKS 加密磁碟密碼

每個 LUKS2 加密的磁碟最多可以設置 32 組密碼(key-slot),我們可以使用 cryptsetupluksDump 指令查看目前的密碼設置狀態:

# 查看 /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 加密磁碟,可以使用 cryptsetupluksAddKey 指令:

# 新增 LUKS 加密磁碟的密碼
sudo cryptsetup luksAddKey /dev/vdb
Enter any existing passphrase:
Enter new passphrase for key slot:
Verify passphrase:

在新增密碼時,必須輸入一組既有的密碼,然後再輸入兩次新增的密碼。完成後可以再次以 cryptsetupluksDump 指令查看目前的密碼設置狀態。

# 查看 /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 加密磁碟密碼,可以使用 cryptsetupluksRemoveKey 指令,執行之後輸入要移除的密碼:

# 移除 LUKS 加密磁碟的密碼
sudo cryptsetup luksRemoveKey /dev/vdb
Enter passphrase to be deleted:

若要移除指定 key-slot 中的密碼,可以使用 cryptsetupluksKillSlot 指令:

# 移除 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 加密磁碟的標頭資訊備份至檔案中:

# 備份 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

參考資料

Share
Published by
Office Guide
Tags: 資訊安全

Recent Posts

Python 使用 PyAutoGUI 自動操作滑鼠與鍵盤

本篇介紹如何在 Python ...

9 個月 ago

Ubuntu Linux 以 WireGuard 架設 VPN 伺服器教學與範例

本篇介紹如何在 Ubuntu ...

9 個月 ago

Linux 網路設定 ip 指令用法教學與範例

本篇介紹如何在 Linux 系...

9 個月 ago

Windows 使用 TPM 虛擬智慧卡保護 SSH 金鑰教學與範例

本篇介紹如何在 Windows...

10 個月 ago

Linux 以 Shamir’s Secret Sharing 分割保存金鑰教學與範例

介紹如何在 Linux 中使用...

11 個月 ago

Linux 以 Cryptsetup、LUKS 加密 USB 隨身碟教學與範例

介紹如何在 Linux 系統中...

11 個月 ago