Ubuntu 20.04 を LVM on LUKS でインストールする


メインの開発マシンには Dell XPS 13 9380 に Ubuntu 19.04 (Disco Dingo) をインストールし Ubuntu 19.10 (Eoan Ermine) へ apt-get dist-upgrade したものを使用していました。 1年近く使っていると不要になったパッケージや設定ファイル、キャッシュが残ったままで本来必要だったものってなんだっけ?状態になっていました。 Focal Fossa がリリースされたのもあって、これを機に Ubuntu 20.04 (Focal Fossa) を真っ更な状態からインストールします。

Disco Dingo をインストールしたときにルートパーティションとは別に /var パーティションに 15 GB 割り当てていました。 この /var が意外と容量を取りまして、docker image や KVM image はデフォルトで /var 内に置くようになっており、 すぐに容量いっぱいになったので一時的 image を home directory に保存するよう設定していました。 マシン依存で docker や KVM の設定を書き換えたり、細かいとこではログファイルの格納場所を変更する必要がでてくる可能性もあって、 その都度設定ファイルを書き直す必要が出てくるのではという不安がありました。 なので、今回は LVM でパーティショニングしインストール後でもパーティションサイズを変更できるようにします。

万一マシンを紛失した場合のことを考えて、LUKS でディスク暗号化もします。 どういった構成にするのか、ディスク暗号化の方法等は ArchWiki(dm-crypt/Encrypting an entire system - ArchWiki)がとても参考になりました。

この記事では Ubuntu 20.04 (Focal Fossa) を LVM on LUKS でインストールする方法を紹介します。 LVM や UEFI, LUKS とは、ということは別の記事で紹介したいと思います。

また、Ubuntu を Full Disk Encryption でインストールする方法については Ubuntu の公式 Wiki (Full_Disk_Encryption_Howto_2019 - Community Help Wiki)が参考になりました。 今回紹介する方法はこちらを基本に自分用環境に少しカスタマイズしている、という認識で読んでもらえたらと思います。

やること

  • Ubuntu 20.04 (Focal Fossa) のインストール
  • LVM on LUKS + /boot 暗号化
  • UEFI Boot のみで、 Legacy Boot は非対応
  • GPT + EFI-SP
  • KVM + libvirt 検証環境で動作確認

最終目的は XPS 13 9380 にインストールすることですが、まずは検証環境でインストールして動作確認した後に XPS 13 9380 にインストールしますので、KVM + libvirt 環境での紹介になります。

実機でも基本的なやり方は変わらずインストールできました。 LVM の構成やデバイス名、割当サイズが変わるくらいでした。

パーティション構成

+----------------------+-----------------------+-------------------------------+
| EFI System Partition | Boot Partition        | Linux LUKS Partition          |
| (ef00)               | (8300)                | (8309)                        |
|                      |                       |                               |
| /dev/vda1            | /dev/vda2             | /dev/vda3                     |
|                      |                       |                               |
| unencrypted          | encrypted             | encrypted                     |
|                      +-------------------------------------------------------+
|                      | /dev/mapper/luks_boot | /dev/mapper/luks_vda3         | LUKS Mapping
|                      +---------------------------------------+---------------+
|                      |                       | /dev/vg1/swap | /dev/vg1/root | LVM
|                      |                       +-------------------------------+
| /boot/efi            | /boot                 | swap          | /             | Mount Point
+----------------------+-----------------------+---------------+---------------+

大きく3つのパーティションに分けます。

  • /dev/vda1: UEFI boot 用のパーティション
  • /dev/vda2: 初期 RAM ディスクや Linux カーネルの実行ファイルを格納する boot パーティション
  • /dev/vda3: LVM 用で、//home をマウントポイントとするパーティション

/dev/vda2, /dev/vda3 を暗号化し、dm-crypt でマッピングを作成します。 更にルートパーティション用のマッピング(/dev/mapper/luks_vda3)を LVM の PV とし、VG を作成し swap, / マウントポイント用の LV を作成します。

上記の構成でインストールしていきます。 ちなみに実機インストールでは swap, / に加えて /var, /home 用の LV を追加しました。 ここは各々の構成に合わせて設定すれば良いと思います。

インストール方法

KVM + libvirt 検証 VM を作るところからインストールまで紹介します。

※ 既存環境を破壊するコマンドが出てきますので、各々の環境に合わせて必要なコマンドを実行してください。

前準備

  1. Desktop installer ISO image を公式(Ubuntu 20.04 LTS (Focal Fossa))からダウンロードします。
  2. VM を用意します。

    設定例

    setup-vm-libvirt-01

    setup-vm-libvirt-02

    setup-vm-libvirt-03

    setup-vm-libvirt-04

    setup-vm-libvirt-05

    setup-vm-libvirt-06

    ※ UEFI Boot mode で起動させるために Customize configuration before install にチェックを入れて Overvier > Details > Firmware: UEFI x86_64... を選択する必要があります

    setup-vm-libvirt-07

    UEFI Boot mode だとこのような真っ黒い画面の GRUB boot loader が表示されます。

setup-vm-libvirt-08

この画面が開けたら準備完了です。

インストール

コマンドをまとめたものと実行・出力例は コマンド実行例 に記載しておきます。 どうしてもうまくインストールできない、コマンドの出力が合わない等あればこちらを参考にしてください。

パーティショニング

Installation type で Erase disk and install Ubuntu を選択することで、パーティション設定とディスク暗号化の設定をおまかせすることができます。 この GUI からのインストールだとパーティションのカスタマイズと LVM の設定はできないので、インストール前に手動でパーティション設定・ディスク暗号化の設定を行います。

Try Ubuntu ボタンをクリックしてターミナルを開きます。

install-01

root 権限が必要なコマンドばかり実行するので root になります。

sudo -i

暗号化デバイスを open するのに必要なパスフレーズとデバイス名を後で使い回せるよう環境変数に入れておきます。 変数でもいいのですが、後々 chroot して引き継げるよう環境変数にしてます。 DEV, DM ではインストール先デバイスを指定します。

read -sr PASSPHRASE
export PASSPHRASE
export DEV="/dev/vda"
export DM="vda"

パーティション全削除して新たにパーティションを切ります。 同じデバイスに他 OS がある場合は注意してください。 ここでは必要最小限だけパーティションを切ります。

sgdisk コマンドでパーティションを作成しますが、GPT パーティションが作れればいいので fdisk, gparted 等でも作成できると思います。

sgdisk --zap-all "${DEV}"
sgdisk --new=1:0:+100M --change-name=1:"EFI System" --typecode=1:ef00 "${DEV}"
sgdisk --new=2:0:+500M --change-name=2:"Boot"       --typecode=2:8300 "${DEV}"
sgdisk --new=3:0:0     --change-name=3:"Linux LUKS" --typecode=3:8309 "${DEV}"
sgdisk --print "${DEV}"

LUKS の初期化をして上記で作成したパーティションの暗号化をします。 luks2 は未対応です。

--key-file, -d はファイルからパスフレーズを読み込むオプションです。 対話モードだと都度入力するのが大変なので、 - を指定して標準入力から読み込むようにします。

--batch-mode, -q では対話モードで確認をしないようにします。

printf %s "${PASSPHRASE}" | cryptsetup luksFormat --type=luks1 --key-file - --batch-mode "${DEV}2"
printf %s "${PASSPHRASE}" | cryptsetup luksFormat --type=luks1 --key-file - --batch-mode "${DEV}3"

初期化された LUKS パーティションを open します。 LUKS デバイスとのマッピングを作成して /dev/mapper で使えるようにします。

printf %s "${PASSPHRASE}" | cryptsetup open -d - "${DEV}2" luks_boot
printf %s "${PASSPHRASE}" | cryptsetup open -d - "${DEV}3" "luks_${DM}3"

マッピングできているか確認します。 luks_boot, luks_vda3 があれば問題なくマウントされています。

ls -l /dev/mapper/
control  luks_boot  luks_vda3

作成したパーティションをフォーマットします。 EFI boot パーティション(/dev/vda1)は VFAT 32 bit でフォーマットします。

EFI boot パーティションは USB ドライブ等の removable disk からでもブートできるようにするため FAT12 または FAT16, FAT32 でフォーマットされています。

mkfs.ext4 -L boot /dev/mapper/luks_boot
mkfs.vfat -F 32 -n EFI-SP "${DEV}1"

LVM の PV, VG を作成します。

pvcreate /dev/mapper/"luks_${DM}3"
vgcreate vg1 /dev/mapper/"luks_${DM}3"

LVM の LV を作成します。

lvcreate -L 4G -n swap vg1
lvcreate -l 100%FREE -n root vg1

以上まで問題なければ Install Ubuntu 20.04 LTS を開いてインストールを開始します。

Ubuntu インストール

設定例

gui-install-01

gui-install-02

gui-install-03

gui-install-04

作成されたパーティションをにマウントポイントを割り当ててフォーマットします

gui-install-05

gui-install-06

gui-install-07

gui-install-08

パーティションへの設定が終わったら Device for boot loader installation/dev/vda を選択して Install Now ボタンをクリックします。

gui-install-09

gui-install-10

gui-install-11

gui-install-12

GRUB でディスク暗号化を有効

ユーザ設定後、パッケージインストールや GRUB の設定が自動で進められます。

start-to-download-01

このときに以下のコマンドを実行します。 これを実行しないと installer が grub-install で入力を求められてインストールが失敗します。

echo "GRUB_ENABLE_CRYPTODISK=y" >> /target/etc/default/grub

start-to-download-02

Ubuntu インストール後

インストールが完了するとこのようなポップアップが表示され、テストし続けるか再起動するか選択させられます。

引き続きターミナルからディスク暗号化等の設定をする必要があるので、Continue Testing ボタンをクリックします。

finish-to-download-01

パーティショニング で使用していたターミナルに戻ります。

インストールされた / パーティションを /target へマウントします。 もし /etc パーティションを分けているのであればそちらもマウントする必要があります。

mount /dev/mapper/vg1-root /target

proc, sys, dev と DNS 名前解決のための etc/resolv.conf/target へマウントします。

--rbind オプションではマウント元でマウントしているデバイスをマウントし直します。

for n in proc sys dev etc/resolv.conf; do mount --rbind "/$n" "/target/$n"; done

chroot して /etc/fstab で記載されているデバイスをマウントします。

chroot /target
mount -a

この時点では LUKS の key にはパスフレーズのみ設定している状態です。 後に /etc/crypttab で keyfile から LUKS パーティションを自動マウントできるようにするため keyfile を作成・追加します。

apt install -y cryptsetup-initramfs
echo "KEYFILE_PATTERN=/etc/luks/*.keyfile" >> /etc/cryptsetup-initramfs/conf-hook
echo "UMASK=0077" >> /etc/initramfs-tools/initramfs.conf

mkdir /etc/luks
dd if=/dev/urandom of=/etc/luks/boot_os.keyfile bs=4096 count=1

chmod u=rx,go-rwx /etc/luks
chmod u=r,go-rwx /etc/luks/boot_os.keyfile

printf %s "${PASSPHRASE}" | cryptsetup luksAddKey -d - "${DEV}2" /etc/luks/boot_os.keyfile
printf %s "${PASSPHRASE}" | cryptsetup luksAddKey -d - "${DEV}3" /etc/luks/boot_os.keyfile

LUKS パーティションを自動マウントするよう設定します。

echo "luks_boot UUID=$(blkid -s UUID -o value ${DEV}2) /etc/luks/boot_os.keyfile luks,discard" >> /etc/crypttab
echo "luks_${DM}3 UUID=$(blkid -s UUID -o value ${DEV}3) /etc/luks/boot_os.keyfile luks,discard" >> /etc/crypttab

初期 RAM ディスクを更新します。

update-initramfs -uk all

chroot から抜けて再起動します。

exit
reboot

再起動

再起動が始まると以下のような画面が表示されるので Enter キーを押します。

reboot-after-installation-01

起動すると Enter passphrase for hd0,gpt2 (ec4555eb53fe40cb9acae3b2ff65b686): と表示されます。 UUID の部分は環境ごとに異なります。 設定したパスフレーズを入力し、暗号化されている /boot パーティションを使用できる状態にします。 入力しても文字は表示されないので注意してください。

reboot-after-installation-02

この画面が表示されればインストール完了です。

reboot-after-installation-03

はまったこと

誤って /boot パーティションサイズを 100MiB にしていて、 update-initramfs コマンドで初期 RAM disk 作成時に容量超過でコマンド失敗しました。 初期 RAM ディスク(initrd.img-5.4.0-28-generic)は 80 MiB くらいあるので、複数作成しようとすると失敗します。 エラーメッセージ読んても原因がわからず辛かった…

さいごに

Ubuntu 20.04 (Focal Fossa) を LVM on LUKS でインストールする方法を紹介しました。 他の方のブログや公式サイトの情報では細かい手順まで記載してなかったり断片的だったりで一貫した情報が少なく、 本当に可能なのか不安だった中で検証環境と XPS 13 にインストールできたので安心しました。 この記事がお役に立てれば幸いです。

コマンド実行例

Install Ubuntu 20.04 on XPS 13 9380, LVM on LUKS

実行例
ubuntu@ubuntu:~$ dconf write /org/gnome/desktop/input-sources/xkb-options "['ctrl:nocaps']"
ubuntu@ubuntu:~$ sudo -i
root@ubuntu:~# read -sr PASSPHRASE
root@ubuntu:~# echo ${PASSPHRASE}
password
root@ubuntu:~# export PASSPHRASE
root@ubuntu:~# export DEV="/dev/vda"
root@ubuntu:~# export DM="vda"
root@ubuntu:~# sgdisk --zap-all "${DEV}"
Creating new GPT entries in memory.
GPT data structures destroyed! You may now partition the disk using fdisk or
other utilities.
root@ubuntu:~# sgdisk --new=1:0:+100M --change-name=1:"EFI System" --typecode=1:ef00 "${DEV}"
Creating new GPT entries in memory.
Setting name!
partNum is 0
The operation has completed successfully.
root@ubuntu:~# sgdisk --new=2:0:+500M --change-name=2:"Boot"       --typecode=2:8300 "${DEV}"
Setting name!
partNum is 1
The operation has completed successfully.
root@ubuntu:~# sgdisk --new=3:0:0     --change-name=3:"Linux LUKS" --typecode=3:8309 "${DEV}"
Setting name!
partNum is 2
The operation has completed successfully.
root@ubuntu:~# sgdisk --print "${DEV}"
Disk /dev/vda: 125829120 sectors, 60.0 GiB
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 6DDBA433-4A1E-40A7-8123-CCD4E567AF46
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 125829086
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          206847   100.0 MiB   EF00  EFI System
   2          206848         1230847   500.0 MiB   8300  Boot
   3         1230848       125829086   59.4 GiB    8309  Linux LUKS
root@ubuntu:~#
root@ubuntu:~# printf %s "${PASSPHRASE}" | cryptsetup luksFormat --type=luks1 --key-file - --batch-mode "${DEV}2"
root@ubuntu:~# printf %s "${PASSPHRASE}" | cryptsetup luksFormat --type=luks1 --key-file - --batch-mode "${DEV}3"
root@ubuntu:~#
root@ubuntu:~# printf %s "${PASSPHRASE}" | cryptsetup open -d - "${DEV}2" luks_boot
root@ubuntu:~# printf %s "${PASSPHRASE}" | cryptsetup open -d - "${DEV}3" "luks_${DM}3"
root@ubuntu:~# ls /dev/mapper/
control  luks_boot  luks_vda3
root@ubuntu:~#
root@ubuntu:~# mkfs.ext4 -L boot /dev/mapper/luks_boot
mke2fs 1.45.5 (07-Jan-2020)
Creating filesystem with 127488 4k blocks and 127488 inodes
Filesystem UUID: 14ec2569-976f-453d-8261-d3b3d6099140
Superblock backups stored on blocks:
	32768, 98304

Allocating group tables: done
Writing inode tables: done
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done

root@ubuntu:~# mkfs.vfat -F 32 -n EFI-SP "${DEV}1"
mkfs.fat 4.1 (2017-01-24)
root@ubuntu:~#
root@ubuntu:~# pvcreate /dev/mapper/"luks_${DM}3"
  Physical volume "/dev/mapper/luks_vda3" successfully created.
root@ubuntu:~# vgcreate vg1 /dev/mapper/"luks_${DM}3"
  Volume group "vg1" successfully created
root@ubuntu:~#
root@ubuntu:~# lvcreate -L 4G -n swap vg1
  Logical volume "swap" created.
root@ubuntu:~# lvcreate -l 100%FREE -n root vg1
  Logical volume "root" created.
root@ubuntu:~# lvs
  LV   VG  Attr       LSize   Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  root vg1 -wi-a----- <55.41g
  swap vg1 -wi-a-----   4.00g
..

..
root@ubuntu:~# echo "GRUB_ENABLE_CRYPTODISK=y" >> /target/etc/default/grub
..

..
root@ubuntu:~# mount /dev/mapper/vg1-root /target
root@ubuntu:~# for n in proc sys dev etc/resolv.conf; do mount --rbind "/$n" "/target/$n"; done
root@ubuntu:~# chroot /target
root@ubuntu:/# mount -a
root@ubuntu:/# apt install -y cryptsetup-initramfs
Reading package lists... Done
Building dependency tree
Reading state information... Done
cryptsetup-initramfs is already the newest version (2:2.2.2-3ubuntu2).
0 upgraded, 0 newly installed, 0 to remove and 29 not upgraded.
root@ubuntu:/# echo "KEYFILE_PATTERN=/etc/luks/*.keyfile" >> /etc/cryptsetup-initramfs/conf-hook
root@ubuntu:/# echo "UMASK=0077" >> /etc/initramfs-tools/initramfs.conf
root@ubuntu:/# mkdir /etc/luks
root@ubuntu:/# dd if=/dev/urandom of=/etc/luks/boot_os.keyfile bs=4096 count=1
1+0 records in
1+0 records out
4096 bytes (4.1 kB, 4.0 KiB) copied, 0.000639065 s, 6.4 MB/s
root@ubuntu:/# chmod u=rx,go-rwx /etc/luks
root@ubuntu:/# chmod u=r,go-rwx /etc/luks/boot_os.keyfile
root@ubuntu:/# printf %s "${PASSPHRASE}" | cryptsetup luksAddKey -d - "${DEV}2" /etc/luks/boot_os.keyfile
root@ubuntu:/# printf %s "${PASSPHRASE}" | cryptsetup luksAddKey -d - "${DEV}3" /etc/luks/boot_os.keyfile
root@ubuntu:/# echo "luks_boot UUID=$(blkid -s UUID -o value ${DEV}2) /etc/luks/boot_os.keyfile luks,discard" >> /etc/crypttab
root@ubuntu:/# echo "luks_${DM}3 UUID=$(blkid -s UUID -o value ${DEV}3) /etc/luks/boot_os.keyfile luks,discard" >> /etc/crypttab
root@ubuntu:/# update-initramfs -uk all
update-initramfs: Generating /boot/initrd.img-5.4.0-28-generic
update-initramfs: Generating /boot/initrd.img-5.4.0-26-generic
root@ubuntu:/# exit
root@ubuntu:~# reboot

参考