Linuxの暗号化解除をコンソール以外で行うためにClevis-TangとDropbaerを使う
自宅内に勉強用のLinuxサーバーを建てるにあたって、ルートドライブの暗号化解除方法を検討しました。その結果Clevis-Tangによる解除とDropbearによるSSH接続を採用することで、サーバーの物理コンソールと並行した暗号化解除を採用しました。以下、選定理由と設定方法を書いておきます。
LinuxはUbuntu 24.04 Serverを使用し、VMWare Workstation上で実験しました。その後、ここに記した方法をDebian 12にも利用できることを実機で確認しています。
Debian/Ubuntuの全ディスク暗号化とinitramfs
まず大事なことから。Debian/Ubuntuの全ディスク暗号化(FDE : Full Disk Encryption)はディスク全体を暗号化するわけではありません。Linux本体が格納するルート以下をすっぽり暗号化するのは事実ですが、それ以外のパーティションは暗号化されません。
Debian 12 と Ubuntu 24.04でインストーラにFDEをさせた場合のパーティション構成を以下の表に現します。
パーティション | 暗号化 | リンク位置 | 説明 |
---|---|---|---|
EFIシステムパーティション(ESP) | しない | /boot/efi | UEFIに対してブートローダーの位置を示すデータ。 |
GRUB格納域 | しない | /boot | GRUBブートローダーとLinuxのイメージが格納される。 |
ルートボリューム | する | / | 上記以外の全てが格納される |
なお、ESPの使い方はUEFIの要求さえ満たせばよいのでディストリビューション次第なところがあります。たとえばGRUBやLinuxのイメージまで格納することも可能です。上の表はDebianやUbuntuの例です。
この表からわかるように、リセット直後にEFIがESPを参照してGRUBの実行を行うところまでは一切暗号化されていません。GRUBは暗号化されていない/bootからLinuxのイメージを取り出すとメモリ上に展開します。そのうえでLinuxの実行前の準備としてinitramfsと呼ばれるファイルシステムを主記憶上に展開し、その中の小規模システムを使ってLinuxシステム実行前に必要な準備を開始します。
ここで言う必要な準備の中にはルートボリュームの暗号化解除も含みます。インストールしたての状態では、initramfsの中に組み込まれた機能の一つがコンソールからユーザーに対して暗号化解除のためのパスフレーズの入力を促します。FDEインストールをしたことがある人ならば見慣れた光景です。
initramfsには管理者が必要とする機能を追加することも可能です。そこで、リモートからの暗号解除機能や、別のサーバーと協調した暗号解除のための機能を組み込めば、ルートボリュームの暗号解除方法をカスタム化することが可能です。initramfsに組み込まれたそれぞれの機能は並行動作しますので、インストールの順序に気を使う必要もありません。
このように柔軟に暗号化解除の方法を構成できるのが暗号化されていないinitramfsの利点です。一方で、暗号化されていないinitramfsには、第三者によって改竄を受けるリスクもあります。そこで、GRUBをESPに置いて/bootも暗号化してしまうやりかたもあります。この方法は改竄のリスクは小さくはなりますが、柔軟性に欠けるためにオペレータがコンソールの前に座って暗号を解除しなければなりません。
さて、暗号化と説明なしに書いてきましたが実際のルートボリューム暗号化を担うのは、Debian/Ubuntuの場合はLVMの中に置かれたLUKS暗号化ボリュームです。LUKS(Linux Unified Key Setup-on-disk-format)はディスク暗号化を安全に行うために開発されたツールセットであり、ひとつのボリュームに異なる8つのキーを設定することができます。8つのキーのうちどれを使ってもボリュームの暗号化を解除できます。そのため、initramfsに組み込んだ複数の暗号解除機構それぞれが独自のキーを使うことができます。
検討した方法と選定理由
サーバーの物理コンソールに張り付かなくても暗号化解除できる手段として以下の4つの方法を検討しました。
- ClevisでTangピンを使う
- ClevisでTPMピンを使う
- Dropbear SSHサーバーを使う
- Tailscaleを使う
このうち、1と3を採用しました。理由は後述します。
ClevisでTangピンを使う
Clevisは暗号化解除のためのフレームワークで、解除方式をピンと呼ばれるプラグインとして選ぶことができます。ClevisはDebianのリポジトリに採用されているだけでなく、Clevisをinitramfsで使用するためのclevis-initramfsパッケージまでリポジトリに用意されています。clevis-initramfsをリポジトリからインストールすると自動的にinitramfsが再構築されるため、手軽に利用できるのがメリットです。
Clevisは暗号フレームワークですが、ここでClevisが暗号化および復号するのはLUKSボリュームのキーです。つまりClevisによってキーを暗号化して保存しておき、しかるべき方法復号したキーをつかってさらにLUKSを復号するというのが処理の流れです。「しかるべき方法」は複数用意されており、ピンと呼ばれています。ピンの種類を変えることでClevisは柔軟な方法でキーを暗号化することができます。
Tangピンを使う場合には、暗号を解除したいサーバーのほかにTangサーバーを用意しなければなりません。このサーバーはClevisに対して公開鍵を提供するほか、その公開鍵で暗号化されたデータを復号する機能を持っています。
適切な設定をした場合、Clevis-Tang構成はユーザーの介入なしにLUKS暗号化ボリュームの暗号を解除できます。これによって、「Tangサーバーが同じサブネットワークにあるときに限り」LUKS暗号化ボリュームの暗号を自動的に解除するシステムを構築できます。
自動解除はサーバーが窃盗された時にデータを盗まれるリスクがあります。しかしClevisにTangピンを適切に設定した構成だと、盗難先のネットワークに同じく盗難されたTangサーバーを接続しなければ起動しません。また、TangサーバーにFDEをかけてパスフレーズで保護しておけば盗難先で電源を入れてもFDEのパスフレーズが人手で突破されない限りClevis側も暗号が解除されません。
つまり、ClevisとTangピンを使う構成は「正常な運用場所」という地理的な縛りの下で暗号化を自動解除していると言えます。地理的な条件が満たせない場合にはTangサーバーの暗号パスフレーズを手作業で解除することを強います。Tangサーバーは複数のClevisに対応できますので、多数のPCをFDE化する場合にも好都合です。
ClevisでTPMピンを使う
ClevisはTangサーバーのほかにTPMをピンとして使うこともできます。この場合Tangサーバーの代わりを務めるのがTPMになるだけです。
TPMをピンにした場合は電源を入れると自動的に暗号が解除されるため、暗号解除の手間がかかりません。
欠点は利点の裏返しです。サーバーを盗んだ側からするとFDEの暗号は勝手に解除されます。ユーザーパスワードさえ突破してしまえば内部は覗き放題です。
ClevisでTPMをピンとして使う場合、暗号化の御利益は「PCから取り出されたストレージの中を覗くことができない」にとどまることになります。無論、これはストレージを廃棄する際などには重要なことです。
Dropbear SSHサーバーを使う
Dropbearは軽量のSSH実装です。機能は限られていますが軽量ゆえに取り回しがよく、initramfsの中で使用することができます。これを利用するとinitramfsを実行中のサーバーにリモートから接続して、オペレーターがFDEの暗号解除パスフレーズを入力することができます。
これはオペレーターの介在無しに自動的暗号を解除する前二者に比べるとやや面倒な方法ですが、普段使っている暗号解除が機能しないときのフォールバックとして大変重宝します。
Dropbear SSHをinitrafmsで使用する場合、SSHクライアントはIPアドレスを直打ちします。これはサーバーとクライアントが同じサブネットワークにつながっていなければならないことを意味します。このため、盗難されたサーバーが別ネットワークに接続されても誤って起動する心配はありません。
Tailscaleを使う
Tailscaleは手軽に使えるVPNとして非常に人気があります。Tailscaleを使えるとホームルーターのポートに穴をあけなくとも外出先から自宅のホームサーバーにアクセスできます。同様に遠隔地に分散している企業のサーバー群を容易に1か所からコントロールできます。
Tailscaleをinitramfsに組み込んだ実装がtailscale-initramfsとして公開されています。これを使ってinitramfsを実行中のサーバーに入りこみ、遠隔操作で暗号を解除することができます。
欠点としてはinitramfsが暗号化されていないため、サーバーが物理的に盗まれた場合はtailscaleのキーを容易に盗まれてしまうという問題があります。この場合、そのキーを使って自身のtailscaleネットワーク(tailnet)に簡単に潜り込まれてしまいます。TailscaleのACLを使ってそのキーでできることを制限しておけば、tailnet内の他のサーバーやPCに対して攻撃をされるリスクは小さくなります。しかし、サーバーの偽装を阻止することはできません。
サーバーが盗難にあったことに気が付かない場合、偽装サーバーにうっかりログオンしようとして暗号解除用のパスフレーズを盗まれるリスクがあります。そして本当に暗号解除用のパスフレーズを盗まれた場合はそれを使って盗まれたサーバーが起動可能になります。結局、ClevisのTPMピンの場合と同様にユーザーパスワードを突破されるとストレージ内部を読まれてしまいます。
選定した方法
上の4つの方法を検討しました。サーバーを盗んだ犯人の手元でサーバーを起動させないということを目的とすると、TPMを使うことは選定から外れます。また、Tailscaleは偽装サーバーによってLUKS暗号化ボリュームのパスフレーズを盗まれる危険性があります。
これらのことから次の2つの方法をホームサーバーに組み込むことにしました。
- ClevisでTangピンを使う
- Dropbear SSHサーバーを使う
なお、以上の判断は私のホームサーバーに関しての個人的なポリシーに過ぎません。Tailscaleを利用する方法は、サーバーが物理的に盗まれたことに気づきさえすれば、簡単にTailscaleの管理コンソールからノードを削除できます。この場合、犯人はサーバーを偽装することもできません。
設定作業
以下、特に説明しない限りUbuntu 24.04 Serverでの作業です。各々のサーバーはFDEインストールされているものとします。説明のために、サーバー名を以下の通りとします。
サーバー名 | 役割 | initramfsのIPアドレス | サーバー稼働中のIPアドレス |
---|---|---|---|
tang-server | Tangによる認証用 | DHCPが割り当て | 固定 (ttt.ttt.ttt.ttt) |
home-server | 宅内サーバー | 固定 (hhh.hhh.hhh.hhh) | DHCPが割り当て |
また、クライアントを含めてこれらは同一サブネットに属するものとします。
なお、initramfsの中ではmDNSを使えなかったため、IPアドレスを直打ちしています。固定のIPアドレスを使っているのはそのためです。いろいろ調べてみましたが、initramfsの中ではmDSNが使えるとも使えないとも書いた資料は見当たりませんでした。業務用サーバーを構築する人はmDNSなんか使わないでしょうから、実装する動機もないのかもしれません。
tang-serverのTangサービスの設定
Tangサービスを設定してhome-serverのための認証サーバーとして働くようにします。
IPアドレスの固定化
別稿の『Netplanを使ってDebian/UbuntuのIPアドレスを固定にする』の方法にしたがって、tang-serverにDHCPサーバーが割り振らない固定アドレスを設定します。この固定アドレスはサーバー稼働中にずっと使われます。
tang-serverのIPアドレスは仮に ttt.ttt.ttt.ttt とします。
Tangのインストール
tang-serverにTangをインストールし、サービスを有効化します。
sudo apt install -y tang
sudo systemctl enable tangd.socket
ファイアーウォールの設定
次にファイアーウォールを設定します。Tangはポート80で動作しますが、保守用にSSHのポート22も開けておきます。
sudo ufw enable
sudo ufw allow 80
sudo ufw allow 22
Tangサービスの状態確認
systemctlコマンドで状態を確認できます。
sudo systemctl status tangd.socket
activeになっていることがわかります。また、ポート80で待ち受けています。
● tangd.socket - Tang Server socket
Loaded: loaded (/usr/lib/systemd/system/tangd.socket; enabled; preset: enabled)
Active: active (listening) since Mon 2025-01-20 00:19:41 UTC; 3min 9s ago
Docs: man:tang(8)
Listen: [::]:80 (Stream)
Accepted: 0; Connected: 0;
Tasks: 0 (limit: 2218)
Memory: 8.0K (peak: 1.5M)
CPU: 9ms
CGroup: /system.slice/tangd.socket
Jan 20 00:19:41 tang-server systemd[1]: Starting tangd.socket - Tang Server socket...
Jan 20 00:19:41 tang-server systemd[1]: Listening on tangd.socket - Tang Server socket.
Tangキーの確認
Tangサービスは要求に応じてサーバー固有のキーを返します。このキーは公開鍵であり、tang-show-keysコマンドで確認できます。
sudo tang-show-keys [ポート番号]
例えば、ポート80を使うなら次のようになります。
sudo tang-show-keys 80
これに対して、以下のようにキーが返されます。
TAftHUwZfIYokobfiZU7KN4BxGWEvyGOIwOBbOB2WIg
このキーはあとでClevisの設定をするときに同じものがもう一度出てきます。
home-serverのClevisの設定
home-serverのClevisにtang-serverを参照させて、LUKS暗号化ボリュームの暗号を解除させるようにします。
インストールとSSHキーの設定
まず、home-serverにclevisをインストールします。
clevis-luksパッケージはClevisにLUKS暗号化ボリュームを操作させるために必要です。また、clevis-initramfsパッケージをインストールすると、自動的にupdate-initramfsが実行されてinitramfs起動時にclevisが実行されるようになります。
sudo apt install -y clevis clevis-luks clevis-initramfs
Clevis-Tangの連携テスト
インストールしたClevisがtang-serverと連携できることを確認しておきます。まず、平文のテストファイルを作ります。
echo "The quick brown fox" > unencrypted.txt
これをtang-serverが返すキー(公開鍵)で暗号化します。tang-serverのIPアドレスは仮にttt.ttt.ttt.tttとします。
clevis encrypt tang '{"url":"http://ttt.ttt.ttt.ttt"}' < unencrypted.txt > secret.txt
2番目の引数"tang"は、暗号化に際してTangサーバーをピンに使うことを指定しています。コマンドを実行すると、tang-serverから取得したキーを信用して良いかと質問されます。さきに確認したtang-serverのキーと同じであれば、yと答えます
The advertisement contains the following signing keys:
TAftHUwZfIYokobfiZU7KN4BxGWEvyGOIwOBbOB2WIg
Do you wish to trust these keys? [ynYN]
これで、暗号化ファイルsecret.txtが作られます。一応中を確認して暗号化されていることを確かめてください。
最後にClevisがこの暗号を解除できることを確認します。
clevis decrypt < secret.txt
このとき、tang-serverを指定する必要はありません。この暗号にTangピンを使用したこととtang-serverのアドレスはメタデータとして暗号内部に記されています。Clevisはそれを頼りに暗号解除をtang-serverに依頼します。
LUKS暗号化ボリュームの特定
次にhome-serverのLUKS暗号化ボリュームがどのパーティションに存在するかを特定します。これはlsblkで調べることができます。
lsblk
以下は私のコンピュータの例です。
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 20G 0 disk
├─sda1 8:1 0 953M 0 part /boot/efi
├─sda2 8:2 0 1.8G 0 part /boot
└─sda3 8:3 0 17.3G 0 part
└─dm_crypt-0 252:0 0 17.3G 0 crypt
└─ubuntu--vg-ubuntu--lv 252:1 0 10G 0 lvm /
sr0 11:0 1 2.6G 0 rom
dm_crypt-0は、TYPE欄にcryptとあるように暗号化されています。よって、/dev/sda3が目的のパーティションとなります。ボリュームを特定できたので、キースロットを表示してみましょう。LUKS暗号化ボリュームには8つのキースロットがあり、それぞれに暗号化キーを収めることができます。
sudo cryptsetup luksDump /dev/sda3
初期状態ではスロット0だけが使われています。これはインストール時に暗号化に使ったパスフレーズです。
LUKS header information
Version: 2
...
Keyslots:
0: luks2
Key: 512 bits
...
Tokens:
Digests:
...
Tangピンを使ってLUKSとtang-serverを連結する
上で特定したhome-serverの暗号化ボリュームを含む物理パーティション名とtang-serverのIPアドレスを使って、ClevisとLUKSを連結します。
sudo clevis luks bind -d /dev/sda3 tang '{"url": "http://ttt.ttt.ttt.ttt"}'
最初に、指定したLUKSボリュームの暗号解除パスフレーズを聞かれます。これはhome-serverのインストール時に指定した暗号化パスフレーズです。パスフレーズが適合したらtang-serverのキーが表示されます。このキーが正しければyを押します。
Enter existing LUKS password:
The advertisement contains the following signing keys:
TAftHUwZfIYokobfiZU7KN4BxGWEvyGOIwOBbOB2WIg
Do you wish to trust these keys? [ynYN]
これで連結できました。
連結結果をcryptsetupで確認する
先ほど調べたLUKS暗号化ボリュームのキースロットをもう一度見ましょう。
sudo cryptsetup luksDump /dev/sda3
スロット数が1つ増え、Clevisのトークンが追加されていることがわかります。
LUKS header information
Version: 2
...
Keyslots:
0: luks2
Key: 512 bits
...
1: luks2
Key: 512 bits
...
Tokens:
0: clevis
Keyslot: 1
Digests:
...
連結結果をClevisで確認する
clevisコマンドでLUKS内部を見てみましょう。
sudo clevis luks list -d /dev/sda3
キースロット1にttt.ttt.ttt.tttによる認証が必要なキーを収めていることがわかります。これはcryptsetupのダンプ結果と整合します。
1: tang '{"url":"http://ttt.ttt.ttt.ttt"}'
テスト
以上で作業は終了です。tang-serverを同じサブネット内で稼働させてhome-serverを再起動して下さい。手作業でパスフレーズを入力せずともhome-serverが起動するはずです。
一方、tang-serverを停めてhome-serverを再起動すると、コンソールからのパスフレーズ入力を待ち続けます。もちろん、コンソールから入力してやれば正常に起動します。
home-serverのDropbearの設定
Dropbear SSHサーバーをhome-serverにインストールして、他のPCからSSH接続してLUKS暗号化ボリュームの暗号を解除できるようにします。なお、この節の内容は全面的にUbuntu Weekly Recipeの『暗号化されたUbuntuのルートファイルシステムをリモートから復号する方法』をなぞっています。
DropbearはSSHの実装の一つで、必要なメモリやCPUリソースが小さくなるよう設計されたソフトウェアです。このソフトウェアをinitramfsに組み込むことで、別のPCからinitramfs状態にあるLinuxにログインしてLUKS暗号化ドライブのパスフレーズを手動で入力しようというのがこの節でやりたいことです。
Debian/UbuntuはDropbearのパッケージのほか、initramfsへ組み込むためのdropbear-initramfsパッケージを用意しており、aptコマンドでインストールするだけでinitramfsの更新まで済んでしまいます。
initramfsのIPアドレスの固定化
initramfsの中ではmDNSが使えないため、home-serverに接続して暗号を解除するにはSSHクライアントからIPアドレスを直打ちする必要があります。一方でDHCPでIPアドレスを割り振ると、どのIPアドレスが割り振られたか判断できません。そこで、initramfsが使用するIPアドレスを固定化する必要があります。
initramfsが使用するIPアドレスの固定化については、別稿の『カーネル引数を使ってinitramfsのIPアドレスを固定にする』を参照してください。
home-serverのinitramfsのIPアドレスは、仮にhhh.hhh.hhh.hhhとします。
Dropbearのインストール
aptコマンドでインストールすればupdate-initramfsまで終わります。ただしこの状態ではSSH認証キーを書き込んでいないため、遠隔からログインは出来ません。
sudo apt install dropbear-initramfs
ポート番号の設定
念のためポート番号を2222に変更します。これは、initramfsとLinux本体で同じIPアドレスを使いまわす場合に、ポートまで同じだとSSHホストキーが異なるとしてエラーが表示されるからです。
initramfsとLinux本体が違うアドレスを使うのならこの変更は不要です。
/etc/dropbear/initramfs/dropbear.confのDROPBEAR_OPTIONSを以下のように変更します。
#
# Command line options to pass to dropbear(8)
#
DROPBEAR_OPTIONS="-p 2222"
SSH公開鍵を登録する
initramfsのDropbearにSSH接続するために、自分のSSH公開鍵を登録します。登録するには公開鍵が格納されたauthorized_keysを/etc/dropbear/initfsramディレクトリに配置します。
以下の例では~/.sshディレクトリにあるauthorized_keysをそのままコピーしています。
sudo cp ~/.ssh/authorized_keys /etc/dropbear/initramfs/authorized_keys
authorized_keysをコピーしたらupadte-initramfsを実行します。
sudo update-initramfs -u -k all
リモート端末からのLUKS暗号解除
以上の設定が終わったらhome-serverを再起動します。同じサブネットワークにある端末からSSH接続してください。hhh.hhh.hhh.hhhはhome-serverのinitramfsのIPアドレスです。
ssh -p 2222 root@hhh.hhh.hhh.hhh
これでinitramfsのtoyboxシェルにログインできます。toyboxの中で以下のコマンドを実行します。
cryptroot-unlock
LUKS暗号化ドライブのパスフレーズを聞かれますので入力してください。これで暗号が解除されてhome-serverが起動します。
Discussion