それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

clevis initramfs でtpm を使ってルートの自動アンロックをやる

clevis が ubuntu で対応してた。

clevis ツールでdrucat を使わなくても initramfs で、起動時にLUKSのアンロックができるようになってたらしい。

これは便利ですね。

参考資料にしたブログがよく書いてくれてるので、とても簡単だった。

準備

  • /boot のバックアップ
  • tpm/2.0 デバイス
  • clevis-initramfs
  • 作業用のSSHアンロック
  • 作業用のserialポート

clevis が動作しないときに備えて、シリアル・ポートとSSHアンロックを最初に準備しておく。

実験環境を作る

いきなり、PCでやるとミスったときに initramfsを書き戻すのが手間なので、実験用に、Nested VMで、ホスト側でswtpm 有効化して、ゲスト側でtpm2 を使った仮想マシンを作成して実験をすることにする。

ttyS0 をgrub 起動でやる

以前やって試しておいた、grub でシリアル・コンソールを有効にする方法で、grub を有効にする。これでシリアルケーブルを繋いだraspiを挿しておけば、万が一のときリモートからアンロックができる。

bootのバックアップ

やかしたら面倒なので、バックアップをとっておく。

sudo tar cvzf boot.tgz /boot

luksのssh のアンロック

これも以前やって試しておいた。起動時にdm-cryptをSSHでリモートアンロックする方法で、clevis が動作しないときに、リモートから何とかできる経路を用意しておく。

clevis のインストール

clevis を使った initramfs と clevis のtpm2対応パッケージを入れておく

sudo apt install clevis clevis-tpm2 clevis-initramfs

アンロック対象の暗号ブロックデバイスの確認

アンロックの対象になる、ブロックデバイスの名前を確認しておく。

takuya@u0201:~$ lsblk
vda                         252:0    0   40G  0 disk
├─vda1                      252:1    0  512M  0 part  /boot/efi
├─vda2                      252:2    0    1G  0 part  /boot
└─vda3                      252:3    0 38.5G  0 part
  └─dm_crypt-0              253:0    0 38.5G  0 crypt
    └─ubuntu--vg-ubuntu--lv 253:1    0   20G  0 lvm   /

今回は、仮想マシンで行っているので /dev/vda が対象で、EFIブートなので対象パーティション/dev/vda3 になる。

PCR (Platform Configuration Registers) の確認

PCR を使って、TPMに登録してハッシュ値を計算した結果から、データ改竄をチェックするのに必要。ここではTPMバイスで使える暗号学的ハッシュ関数を確認しておく

takuya@u0201:~$ sudo tpm2_getcap pcrs
[sudo] password for takuya:
selected-pcrs:
  - sha1: [ ]
  - sha256: [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 ]
  - sha384: [ ]
  - sha512: [ ]

sha256が使えるとわかる。

clevis に鍵設定する

clevis に鍵を設定する。ここではパスフレーズを使う。

takuya@u0201:~$ sudo clevis luks bind -d /dev/vda3 tpm2 '{"pcr_bank":"sha256","pcr_ids":"7"}'
Enter existing LUKS password:
## ちょっと時間かかる

登録されたかチェック

takuya@u0201:~$ sudo clevis luks list -d /dev/vda3
1: tpm2 '{"hash":"sha256","key":"ecc","pcr_bank":"sha256","pcr_ids":"7"}'

ここ出てくる 1 という番号は cryptsetyp luksDump /dev/vda3 で 確認できるキースロットの番号に対応している。

initramfs を更新する

sudo update-initramfs -u -k 'all'

再起動してチェック

takuya@u0201:~$ sudo reboot

initramfs に clevis が登録されたかチェック

lsinitramfs で initramfs の中身を見れる。

takuya@u0201:~$ sudo lsinitramfs /boot/initrd.img-$(uname -r) | grep clevis
scripts/local-bottom/clevis
scripts/local-top/clevis
usr/bin/clevis
usr/bin/clevis-decrypt
usr/bin/clevis-decrypt-sss
usr/bin/clevis-decrypt-tang
usr/bin/clevis-decrypt-tpm2
usr/bin/clevis-luks-common-functions
usr/bin/clevis-luks-list

削除

clevis 側の設定を削除する

sudo clevis luks unbind -d /dev/vda3 -s 1
The unbind operation will wipe a slot. This operation is unrecoverable.
Do you wish to erase LUKS slot 1 on /dev/vda3? [ynYN] y

スロットにいくつか残ってる場合は、一覧(List)をみながら順番に消す。

たとえば、下記の例では、4つのLUKSスロットを使っている。

sudo clevis luks list -d /dev/vda3
0: tpm2 '{"hash":"sha256","key":"ecc","pcr_bank":"sha256","pcr_ids":"7"}'
1: tpm2 '{"hash":"sha256","key":"ecc","pcr_bank":"sha256","pcr_ids":"7"}'
4: tpm2 '{"hash":"sha256","key":"ecc","pcr_bank":"sha256","pcr_ids":"7"}'
5: tpm2 '{"hash":"sha256","key":"ecc","pcr_bank":"sha256","pcr_ids":"7"}'

順番に消す。(LUKSのヘッダからキースロットを削除している)

sudo clevis luks unbind -d /dev/vda3 -s 0
sudo clevis luks unbind -d /dev/vda3 -s 1
sudo clevis luks unbind -d /dev/vda3 -s 4
sudo clevis luks unbind -d /dev/vda3 -s 5

tpm 側で削除する.tpmをまっさらにしたら全部消える。tpm側で削除しても clevis 側に残るので注意

sudo tpm2_clear

やってみた結果

めっちゃ手軽になってて感動した。

2022-01-07 追記

Debian/11 でも問題なく同じ手順で動くことを確認した。

luks の自動unlock が動いてたときのcryttab は本当に何もしてないシンプルな構成。これでdm-cryptとtpm2さえあれば安心ができますね。

takuya@d0201:~$ cat /etc/crypttab
vda3_crypt UUID=276f641e-c6cd-489b-8b5b-33e65c6405e6 none luks,discard

2022/01/07 追加

initramfs がだいぶサイズが大きくなるので、/boot が200MBでは容量不足になった。注意が必要。

takuya@:~$ sudo update-initramfs -u -k 'all'
update-initramfs: Generating /boot/initrd.img-5.10.0-10-amd64
setupcon: The keyboard model is unknown, assuming 'pc105'. Keyboard may be configured incorrectly.
pigz: abort: write error on <stdout> (No space left on device)
E: mkinitramfs failure pigz 28
update-initramfs: failed for /boot/initrd.img-5.10.0-10-amd64 with 1.

2022-01-09 追記

最終的に出来上がった initrd は 75Mになった。ブート時に使うには、相当でかいパッケージです。

takuya@:~$ ll -lh /boot/initrd.img-5.10.0-10-amd64
-rw------- 1 root root 75M  1月  9 01:47 /boot/initrd.img-5.10.0-10-amd64

/boot に最低2つのinitradのブートイメージが用意される。万が一のためにカレントと1世代前のinitrdが2つ用意される。これで、75MB✕2=150MBが消費される。

更新時は、新しくinitrdを作って、mvで旧来のファイルを置き換えるので、 +75MBが追加で必要。

さらに、update-initramfs -c -k all したときには、1世代前のinitradも再構成されるので、+75Mが必要

つまり、clevis化した/bootには、最低でも4つにカーネルが入るだけの容量が必要で、最低でも 300Mが必要になる。

古いカーネルなど複数バージョンのカーネルを持ってる場合はもっと必要になるかもしれません。

なので、 /bootには1GBくらい割り当てないと詰むと思います。

今回、私は見事に、ここで容量不足で詰んでしまいました。luks化された領域ディスク内で開始セクタを簡単に動かせないので、バックアップをとってSSDをまっさらにして/boot を1GB確保する羽目になり、時間が思い切り溶けました。

でも、これでTPM化されて安心です。

参考資料