Ubuntu Weekly Recipe

第846回TPMを用いて⁠Ubuntu上の暗号化ストレージの復号を自動的に行う

第831回の暗号化されたUbuntuのルートファイルシステムをリモートから復号する方法では、Ubuntuのストレージを暗号化する方法を紹介しました。ただし単に暗号化するだけだと、起動時にパスフレーズの入力を毎回求められてしまいます。これはリモートマシンを暗号化する際の支障となるため、第831回ではその回避策として起動時にストレージをマウントする前にSSHログインしてパスフレーズの入力するための仕組みを構築しました。

今回は、マシン本体にあるTPMを用いて自動的な復号を実現してみます。

Clevisによる自動復号

ストレージを起動時に自動的に復号するためにClevisを利用します。Clevisそのものは復号する処理を自動化するフレームワークで、⁠PIN」と呼ばれるプラグインを導入することで、さまざまな復号処理を行えます。たとえばLUKS PINを利用すれば、LVMとLUKSの組み合わせで暗号化されたストレージの復号処理を自動化できるのです。さらにTPM PINを利用すればTPMを用いた復号を実現できます。

ちなみに自動復号するということは、起動時のパスフレーズの入力が不要になることを意味します。これは端的に言うと、⁠セキュリティの低下」を意味します。たとえばノートPCを例に取ると、自動復号を設定していないマシンであれば、PCを紛失したところでパスフレーズがわからないので第三者はストレージの復号を行えません。しかしながら自動復号を設定している場合、第三者はそのPCを起動するだけで、ストレージが復号されてマウントされます。

もちろんログインしないと使えないので、それだけで直ちに情報流出に繋がるわけではありませんが、せっかく暗号化した意味が薄れてしまいます。よってただ自動的に復号するのではなく、特定の条件を満たした場合にのみ自動復号することが肝要です。

TPMとPCR値

TPM(Trusted Platform Module)とは、データの暗号化・復号やハッシュ値の計算・結果の不揮発領域への保存などを行えるセキュリティモジュールです。Windows 11の要件になっているため、最近のPCであれば大抵の場合はハードウェアのTPMチップか、ファームウェアで実現するfTPMが実装されていることでしょう。また仮想マシンなどでも、ソフトウェアで実現するsTPMを用いて実現できます[1]

ClevisとTPMは、PCR(Platform Configuration Registers)を用いて復号するかどうかを判断します。このPCR値は何かと言うと、TPMを利用してTPM内部に安全に上に保存されたハッシュ値です。具体的にはUEFI/BIOSやブートローダー、カーネルが「実行時の同一性を担保するためのハッシュ値などの文字列」を安全に保存し、参照したい場合などに使われます。

ブートローダーやカーネルの起動時の検証という意味では、似たような仕組みとしてセキュアブート(Secure Boot)存在します。これはUEFI/BIOS上に保存されている公開鍵を用いて、署名を検証できないバイナリは実行できないようにする仕組みです。これにより意図しないブートローダーやカーネルの実行を阻止します。

しかしながらセキュアブートは、UEFI/BIOSの差し替えに対しては脆弱です。実際、UEFI/BIOSの設定画面からかんたんにセキュアブートをオフにできてしまうことからも、想像できることでしょう。それに対してTPMとPCR値はこの差し替え・変更を検知できます[2]

たとえばTCG PC Client Specific Platform Firmware Profile Specification「3.3.4 PCR Usage」を参照してください。PC向けファームウェアにおけるPCRごとの値の使い方について記載されています。

図1 UEFI/BIOSファームウェアにおけるPCR値の利用例
図1

一般的なUEFI/BIOSであれば、PCR0からPCR7にかけて実行したBIOSコードやUEFIの設定、パーティションテーブルのハッシュ値などを保存します。これによりPCR値を参照すればUEFI/BIOSそのものや一部のUEFI設定が変更されていないかを検知できるのです。さらにUbuntuであればshimやGRUBが、各種コマンドやカーネル引数などの文字列、ロードしたバイナリのハッシュ値などを保存します。GRUBのドキュメントによると、GRUBはPCR8とPCR9を使用しているようです。

図2 GRUBはPCR8とPCR9を利用する
図2

実際のPCR値については、 systemd-analyzeやtpm2-toolsを用いて参照できます。

$ systemd-analyze pcrs
NR NAME                SHA256
 0 platform-code       97435af509018bb862c1cd4b88b8df3c954a124b78c9832de0abc413b0037272
 1 platform-config     f2c85baa17501c8f7e25294ef4690f954fccbe7942fa06aeb5835ff6258ae021
 2 external-code       3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969
 3 external-config     3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969
 4 boot-loader-code    16e54d995cef3eb6a2186cfedb3311b8e7130d5db9ecb7cb97d3c8a5127f212c
 5 boot-loader-config  efcb979bc5f0bfe8f120c98ede4b34a232fbce038263e976212a17da9d6a0bc3
 6 host-platform       3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969
 7 secure-boot-policy  5decfca65942dd58af28ec88b3597780ac144639ed8e107357c989fc5968840a
 8 -                   fec1f4c0c3d132f2d488ca14d0ef5b1dd15ed662e994994fe2192052f4ac39cb
 9 kernel-initrd       c5312ba195628110cb946979032c378abd5ba6a240f3e04390230297790f389a
10 ima                 4e33823240b32968038777cdae69ef8f6428fd69cf70cbce88ff298e5fc73ed8
11 kernel-boot         0000000000000000000000000000000000000000000000000000000000000000
12 kernel-config       0000000000000000000000000000000000000000000000000000000000000000
13 sysexts             0000000000000000000000000000000000000000000000000000000000000000
14 shim-policy         306f9d8b94f17d93dc6e7cf8f5c79d652eb4c6c4d13de2dddc24af416e13ecaf
15 system-identity     0000000000000000000000000000000000000000000000000000000000000000
16 debug               0000000000000000000000000000000000000000000000000000000000000000
17 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
18 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
19 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
20 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
21 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
22 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
23 application-support 0000000000000000000000000000000000000000000000000000000000000000

Ubuntu 24.04 LTSならsystemd-analyzeで十分ですが、古いUbuntuだと未対応ですので、tpm2-toolsをインストールしましょう。

$ sudo apt install tpm2-tools
$ sudo tpm2 pcrread
  sha1:
  sha256:
    0 : 0x97435AF509018BB862C1CD4B88B8DF3C954A124B78C9832DE0ABC413B0037272
    1 : 0x23AC9043FF4828816FAECF99F72DF1B54E6BEC5F8794E8A3F559900900B7FCB9
    2 : 0x3D458CFE55CC03EA1F443F1562BEEC8DF51C75E14A9FCF9A7234A13F198E7969
    3 : 0x3D458CFE55CC03EA1F443F1562BEEC8DF51C75E14A9FCF9A7234A13F198E7969
    4 : 0x16E54D995CEF3EB6A2186CFEDB3311B8E7130D5DB9ECB7CB97D3C8A5127F212C
    5 : 0xEFCB979BC5F0BFE8F120C98EDE4B34A232FBCE038263E976212A17DA9D6A0BC3
    6 : 0x3D458CFE55CC03EA1F443F1562BEEC8DF51C75E14A9FCF9A7234A13F198E7969
    7 : 0x5DECFCA65942DD58AF28EC88B3597780AC144639ED8E107357C989FC5968840A
    8 : 0x52CC5706BB161C3DCBF56F15A188DF82A2C6C705CDC29F756D0C0D19BBDA81EA
    9 : 0x3C439F5270D8345D0519715663DF43736ECFE0604B5A8F265EB66C74F705B0B0
    10: 0x894FE9BE3472786E7ECCFCBAAE11E30D786AEC774FC08878C23EABEB3084E8A8
    11: 0x0000000000000000000000000000000000000000000000000000000000000000
    12: 0x0000000000000000000000000000000000000000000000000000000000000000
    13: 0x0000000000000000000000000000000000000000000000000000000000000000
    14: 0x306F9D8B94F17D93DC6E7CF8F5C79D652EB4C6C4D13DE2DDDC24AF416E13ECAF
    15: 0x0000000000000000000000000000000000000000000000000000000000000000
    16: 0x0000000000000000000000000000000000000000000000000000000000000000
    17: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    18: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    19: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    20: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    21: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    22: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
    23: 0x0000000000000000000000000000000000000000000000000000000000000000
  sha384:
  sm3_256:

ちなみにPCR11やPCR17のように0や1で埋められているPCRは初期値です。UEFI/BIOSによっては複数のハッシュアルゴリズムに対応しているものもあります。

試しに/etc/default/grubGRUB_CMDLINE_LINUXdummyという文字列を追加してsudo update-grubを実行した上で再起動してみましょう。再起動前後のPCR値の差分をとった結果は次のとおりです。

     0 : 0x97435AF509018BB862C1CD4B88B8DF3C954A124B78C9832DE0ABC413B0037272
-    1 : 0x23AC9043FF4828816FAECF99F72DF1B54E6BEC5F8794E8A3F559900900B7FCB9
+    1 : 0xF2C85BAA17501C8F7E25294EF4690F954FCCBE7942FA06AEB5835FF6258AE021
     2 : 0x3D458CFE55CC03EA1F443F1562BEEC8DF51C75E14A9FCF9A7234A13F198E7969
     3 : 0x3D458CFE55CC03EA1F443F1562BEEC8DF51C75E14A9FCF9A7234A13F198E7969
     4 : 0x16E54D995CEF3EB6A2186CFEDB3311B8E7130D5DB9ECB7CB97D3C8A5127F212C
     5 : 0xEFCB979BC5F0BFE8F120C98EDE4B34A232FBCE038263E976212A17DA9D6A0BC3
     6 : 0x3D458CFE55CC03EA1F443F1562BEEC8DF51C75E14A9FCF9A7234A13F198E7969
     7 : 0x5DECFCA65942DD58AF28EC88B3597780AC144639ED8E107357C989FC5968840A
-    8 : 0x52CC5706BB161C3DCBF56F15A188DF82A2C6C705CDC29F756D0C0D19BBDA81EA
-    9 : 0x3C439F5270D8345D0519715663DF43736ECFE0604B5A8F265EB66C74F705B0B0
-    10: 0xB1218932F8D6B0CB9E732A136C63590498B8F30395DB901AA0A60A6C4E482854
+    8 : 0xFEC1F4C0C3D132F2D488CA14D0EF5B1DD15ED662E994994FE2192052F4AC39CB
+    9 : 0xC5312BA195628110CB946979032C378ABD5BA6A240F3E04390230297790F389A
+    10: 0xBB7CBE3C4A655865AD07F35A2EF0F0A219BA6A153BB130E6C1EA5659EB07968D
     11: 0x0000000000000000000000000000000000000000000000000000000000000000

PCR1とPCR8、PCR9、PCR10が変わったようです。他にもUEFI/BIOSの設定を変えるとどうなるか試してみても良いでしょう。

ちなみにPCRのハッシュ値はTPMのリセット時に初期値が記録されたあと何かイベントが記録されるたびに、その値が更新されます。つまり直前の値が異なると、それ以降のイベントが同じであっても最終的な値は変わります。どのイベントによってハッシュ値が変わったについては、次のコマンドを実行すれば参照できます。

$ sudo tpm2 eventlog /sys/kernel/security/tpm0/binary_bios_measurements

これらのPCR値を組み合わせとClevisを利用すれば、この特定の値の場合にのみストレージを自動復号できます。たとえばUSBブート時やBIOS更新時は自動的には復号させないなんてことも可能なのです[3]

TPMを利用する場合、どのPCR値を使って自動復号するのかの設定が重要なポイントになります[4]。当然、使われているすべてのPCR値を参照すれば耐性はあがりますが、利便性は下がります。システムのちょっとした更新のたびに自動復号が動かなくなるからです。

よくある例としては次の組み合わせが使われます。

  • PCR0: UEFI/BIOSが差し替えられていないこと
  • PCR1: UEFI/BIOSの設定や、起動に関するUEFI変数やパラメーターが差し替えられていないこと
  • PCR4: ESP等の起動するデバイスのパス
  • PCR7: セキュアブートの各変数の状態
  • PCR8: grub.cfgの中身
  • PCR9: GRUBが起動するカーネルとinitrd
  • PCR14: サードパーティのカーネルモジュールロード時に参照する変数

一番シンプルで手間が少ないのはPCR0とPCR1のみをチェックする方法です。これによりBIOSが差し替えられていないこと、異なるブートデバイスが接続されていないことを保証できます。当然のことながらBIOS更新時は、自動復号がされなくなります。

より安全に倒すならPCR7も入れておいても良いでしょう。ただしUbuntuを含む最近のOSは、たまに安全のためにdbx(禁止署名データベース)を更新することがあり、その場合はPCR7が変わってしまいます。

さらに確実性を期すのであれば、PCR4/PCR8/PCR9/PCR14あたりも対象にいれます。特に起動パラメーターを変更しシングルユーザーモードでログインさせないことを保証するためには、PCR8/9あたりの設定が必要です。しかしながらPCR4やPCR8、PCR9はカーネル等の更新でも変わってしまうため、若干手間がかかります。

今回はセキュアブートと併用することを想定して、PCR0、PCR1、PCR7、PCR9のみをチェックすることにしましょう[5]

ClevisとTPM PINをインストールする

ClevisとTPMの設定は、単にパッケージをインストールしてbindを実行するだけです。まずは必要なソフトウェアをインストールしておきます。

$ sudo apt install clevis-luks clevis-tpm2 clevis-initramfs

次にターゲットとなるデバイスを確認します。第831回の手順で暗号化ストレージを有効化したのであれば、dm_cryptの親にあたるパーティションがターゲットデバイスとなります。

$ lsblk -i -e7
NAME                        MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
nvme0n1                     259:0    0 238.5G  0 disk
|-nvme0n1p1                 259:1    0     1G  0 part  /boot/efi
|-nvme0n1p2                 259:2    0     2G  0 part  /boot
`-nvme0n1p3                 259:3    0 235.4G  0 part
  `-dm_crypt-0              252:0    0 235.4G  0 crypt
    `-ubuntu--vg-ubuntu--lv 252:1    0 235.4G  0 lvm   /var/snap/firefox/common/host-hunspell
                                                       /

上記の例だと「nvme0n1p3」がそれです。では実際にPCR0、PCR1、PCR7、PCR9のsha256ハッシュ値を元に、自動復号させます。

$ sudo clevis luks bind -d /dev/nvme0n1p3 tpm2 '{"pcr_bank":"sha256","pcr_ids":"0,1,7,9"}'
Enter existing LUKS password: ストレージを手動復号する際のパスフレーズを入力

Clevisで登録された鍵のスロット番号は次の方法で確認できます。

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

自動復号できなくなった場合は

TPMを用いた自動復号では、何らかの理由で指定したPCRの値が変わってしまった場合、パスフレーズの入力が求められます。この場合、Clevisの再設定が必要になります。再設定にはsudo clevis luks list -d デバイス名で取得したスロット番号が必要です。

$ sudo clevis luks regen -d /dev/nvme0n1p3 -s 1
Regenerating binding (device /dev/nvme0n1p3, slot 1):
Pin: tpm2, Config: '{"hash":"sha256","key":"ecc","pcr_bank":"sha256","pcr_ids":"0,1,7,9"}'
Do you want to proceed? [ynYN] y
WARNING:esys:src/tss2-esys/api/Esys_Unseal.c:295:Esys_Unseal_Finish() Received TPM Error
ERROR:esys:src/tss2-esys/api/Esys_Unseal.c:98:Esys_Unseal() Esys Finish ErrorCode (0x0000099d)
ERROR: Esys_Unseal(0x99D) - tpm:session(1):a policy check failed
ERROR: Unable to run tpm2_unseal
Unsealing jwk from TPM failed!
Enter existing LUKS password: ストレージを手動復号する際のパスフレーズを入力

気をつけなければいけないのは、PCR値が変わるような処理(カーネルの更新等)を実施して「再起動したあと」に上記を実施する必要があるという点です。一度再起動しないと更新後の正確なPCR値がわからないため、更新と一緒に実施しては意味がありません。自動復号できなくなった場合は、上記の実施を忘れないようにしてください。

もし使用するPCR値を変えたい場合は、一度unbindしてからもう一度bindすることになります。

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

$ sudo clevis luks bind -d /dev/nvme0n1p3 tpm2 '{"pcr_bank":"sha256","pcr_ids":"0,1,7,8,9"}'
Enter existing LUKS password: ストレージを手動復号する際のパスフレーズを入力

これでTPMを利用したストレージの自動復号に対応できました。ちなみに最初にもお伝えしたように、ノートPC等の可搬端末の場合は自動復号に対応するかどうかの検討も必要です。LinuxはWindowsと異なり、ログインパスワードがわからなくてもシステムに入る標準的な仕組みがいくつも存在します[6]

そのほとんどは起動デバイスを変更したり、GRUBからカーネルの起動パラメーターを変更しなくてはいけないため、PCR1やPCR9等を見るようにしておけば対策できるはずです。しかしながら他のセキュリティ対策と同じく、何か穴がある可能性もあります。保護されるデータの重要性と利便性のバランスを各自で考慮し、設定しましょう。

おすすめ記事

記事・ニュース一覧