LVM on LUKS なディスクをWSLにマウントする
LVM on LUKS とは?
大容量かつ安全な(暗号化された)ディスクを構成したいときの選択肢の1つ。
WindowsでいうBitLockerみたいなやつのLinux版のソリューションです。
このWIKIを参考にして、私は以下のような構成にしました。
(今考えると、LUKS on LVMの方が私の用途に合っていたように思いますが、多分WIKIを誤読しました)
+---------------------------------------------+
| Logical Volume Group |
| /dev/mapper/vg-ext--lv-ext |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|
| | |
| Logical volume1 | Logical volume2 |
|_ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _|
| | |
| LUKS encrypted disk | LUKS encrypted disk |
| /dev/sda | /dev/sdb |
+---------------------------------------------+
つまり、
- 各ディスクはLUKSデバイスとして認識されて
- LUKSが解錠されればLVMのPV (物理ボリューム ≠ 物理ディスク) として認識される
- すべてのPVが揃えばLV (論理ボリューム) として認識され
- マウントが可能になる
という流れです。
Linuxで使う場合
だいたいのLinuxディストリビューションではLVMもLUKSも認識されるので何も問題ありません。
sudo cryptsetup luksOpen /dev/sda lv-a # 解錠
sudo cryptsetup luksOpen /dev/sdb lv-b # 解錠
# LVがlv-a, lv-bで構成されていれば自動的にLVが構成される
sudo mount /dev/mapper/vg-ext--lv-ext # マウント
みたいにすればよいです。
ちなみに3行目の自動構成に関しては (おそらく)udev
がデバイス構成変更の通知を受け取って、/lib/udev/rules.d/56-lvm.rules
とかの設定に従って自動的にLVを構成してくれています。(よくわからんが便利)
Windowsで使う場合
Windowsの場合は面倒です。
① WSLに外部ディスクの存在を伝える
Windowsしか外部ディスクの存在を知りません。
そのWindowsもLUKSなんてファイルシステムは知らないため、「不明 オフライン」になります。
スクリプト
これをWSLに伝えるには次のように powershellで wsl --mount DEVICE_ID --bare
を行います。
LUKSは不明なファイルシステム → パーティションを認識できない
ということで、 $disk.Partitions -eq 0
を使って判定しています。
$OutputEncoding = [System.Text.Encoding]::UTF8
$disks = Get-WmiObject -Query "SELECT * from Win32_DiskDrive"
foreach ($disk in $disks)
{
if ($disk.Partitions -eq 0)
{
wsl --mount $disk.DeviceID --bare
}
}
sleep 5
これでWSL側で外部デバイスがうまく認識されるようになります。
(LVMに限らず外部USB等をWSLに直接認識させる場合もこのようなマウントが必要です)
スクリプトをダブルクリックで実行したい
Windows起動直後にデスクトップにあるファイルをダブルクリックで実行できれば嬉しいですね。
管理者権限が必要なのでスタートアップだとおそらく失敗します。
管理権限が必要なPowerShellのスクリプトをダブルクリックで実行するには、
- スクリプト作成
- スクリプトのショートカットを作成
- プロパティの「リンク先」を次のように書き換えとすれば良いです。
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -File "対象ファイルのフルパス"
② LVMの自動認識のタイミングのバグ(たぶん)
通常の暗号化されていないLVMデバイスは、最初からLVMデバイスであると認識可能なので、
(おそらく) udev
によって自動的に特定の VG (Volume Group)のPVとして認識されます。
VGのメンバーとなるPVすべてが同時に存在する前提があるわけです。
しかし今回のような、最初はLUKSデバイスとして認識されるような環境だと
普通にデバイスを解錠するだけだと
1. 1/4のPVが存在
2. 2/4のPVが存在
3. 3/4のPVが存在
4. 4/4のPVが存在
というようにシーケンシャルに認識されてしまいます。
Linuxの場合、構成するすべてのディスクが存在して始めてLVMとして構成してくれます。
1. 1/4のPVが存在
2. 2/4のPVが存在
3. 3/4のPVが存在
4. 4/4のPVが存在 ← ここではじめてLVMとして構成される
WSLの場合、おそらくここに不具合があります。
1. 1/4のPVが存在
2. 2/4のPVが存在
3. 3/4のPVが存在 ← なぜかここでLVMとして構成される
4. 4/4のPVが存在
構成するPVが足りないわけで、こんな感じの表示になります。
sdf 8:80 0 7.3T 0 disk
└─lv-c412ee80 252:0 0 7.3T 0 crypt
└─vg--ext-lv--ext_rimage_0
252:4 0 14.6T 0 lvm
└─vg--ext-lv--ext
252:6 0 29.1T 0 lvm
sdg 8:96 0 7.3T 0 disk
└─lv-de258129 252:1 0 7.3T 0 crypt
└─vg--ext-lv--ext_rimage_1
252:5 0 14.6T 0 lvm
└─vg--ext-lv--ext
252:6 0 29.1T 0 lvm
sdh 8:112 0 7.3T 0 disk
└─lv-3fb5f267 252:2 0 7.3T 0 crypt
└─vg--ext-lv--ext_rimage_1
252:5 0 14.6T 0 lvm
└─vg--ext-lv--ext
252:6 0 29.1T 0 lvm
sdi 8:128 0 7.3T 0 disk
└─lv-01aa0890 252:7 0 7.3T 0 crypt # ← うまく認識されていない
vg--ext-lv--ext_rimage_0-missing_0_0 # ← missingになっている
│ 252:3 0 7.3T 0 lvm
└─vg--ext-lv--ext_rimage_0
252:4 0 14.6T 0 lvm
└─vg--ext-lv--ext
252:6 0 29.1T 0 lvm
解消方針
期待するよりも早い段階で、LVMのPVとして認識され、LVとして自動的に認識されているため、
自動認識を止めさせれば良いです。
デバイスの自動認識はudevが司っているはずなので、/lib/udev/rules.d/56-lvm.rules
を無効化すれば良さそうなのですが、WSLではうまくいきませんでした。
解決
Linuxでは問題なく、WSLでのみ起こっているため、多少Hackyな手法でも問題ないでしょう。
こんな感じで解決しました。
read -rsp "Enter passphrase: " passwd; echo
for uuid in "${uuids[@]}"; do
id=$(echo "$uuid" | awk -F'-' '{print $1}')
name=$(lsblk -o NAME,UUID | grep "$uuid" | awk '{print $1}')
is_mounted=$(lsblk | grep "lv-$id" | wc -l)
info "$name ($uuid) -> lv-$id (mounted: $is_mounted)"
# 末尾&で非同期に処理することでほぼ同時に解錠する
echo "$passwd" | sudo cryptsetup luksOpen "/dev/disk/by-uuid/$uuid" "lv-$id" &
done
wait # 非同期処理を待機
全文
WSLに外部デバイスを伝えるスクリプト
$OutputEncoding = [System.Text.Encoding]::UTF8
$disks = Get-WmiObject -Query "SELECT * from Win32_DiskDrive"
foreach ($disk in $disks)
{
if ($disk.Partitions -eq 0)
{
wsl --mount $disk.DeviceID --bare
}
}
sleep 5
解錠用のスクリプト
WSLの ~/di/ext
にマウントします
#!/bin/bash
set -eu
TARGET="$HOME/di/ext"
is_mounted() {
lsblk | grep $TARGET | grep -q lvm
}
info() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*"
}
if is_mounted; then
echo "Already mounted"
exit 0
fi
# list devices
info "Listing devices:"
uuids=()
while read -r uuid _ _; do
if ! [[ $uuid =~ ^\{?[A-F0-9a-f]{8}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{4}-[A-F0-9a-f]{12}\}?$ ]]; then
continue
fi
if ! sudo file -sL "/dev/disk/by-uuid/$uuid" | grep -q LUKS; then
continue
fi
uuids+=("$uuid")
done< <(sudo lsblk -o UUID,NAME,TYPE | grep disk | grep -v nvme)
# Check if locked disk exists
has_one=0
for uuid in "${uuids[@]}"; do
id=$(echo "$uuid" | awk -F'-' '{print $1}')
if ! lsblk | grep "lv-$id"; then
has_one=1
break
fi
done
info "Found ${#uuids[@]} LUKS devices. Unlocking.."
if [ $has_one -eq 1 ]; then
# unlock disks
read -rsp "Enter passphrase: " passwd; echo
for uuid in "${uuids[@]}"; do
id=$(echo "$uuid" | awk -F'-' '{print $1}')
name=$(lsblk -o NAME,UUID | grep "$uuid" | awk '{print $1}')
is_mounted=$(lsblk | grep "lv-$id" | wc -l)
info "$name ($uuid) -> lv-$id (mounted: $is_mounted)"
# FIXME: hacky way of unlocking disks in parallel.
# LVM is automatically scanned and activated when 3/4 disks are unlocked,
# so we need to unlock all disks at once before udev(?) activating LVM.
# This behavior occurs only on WSL (not on pure Linux).
echo "$passwd" | sudo cryptsetup luksOpen "/dev/disk/by-uuid/$uuid" "lv-$id" &
done
# wait for all disks to unlock
wait
sleep 3
fi
# mount disks
# Check if mounted
if ! is_mounted; then
info "Mounting disks.."
sudo mount /dev/mapper/vg--ext-lv--ext "$TARGET"
fi
info 'done'
Discussion