FedoraでClevisとTangを使ったNBDE設定ガイド:ローカルネットワーク内でLUKSを自動アンロックする

Fedora tutorial - IT technology blog
Fedora tutorial - IT technology blog

FedoraサーバーをLUKS暗号化ディスクで運用するのは、最初から当然の選択だった。物理盗難時のデータ保護は最低限の要件――交渉の余地はない。しかしリブートが必要になると問題が生じる:コンソールかdropbear経由のSSHで手動パスフレーズを入力しなければならず、誰も常駐していない場合は非常に不便だ。調べていくうちに、ClevisとTangを組み合わせたNBDEがまさにこの問題を解決することがわかった――安全なローカルネットワーク内で起動時にディスクを自動アンロックしてくれる。

このセットアップをhomelabで6ヶ月間運用した後、2台のプロダクションサーバーに適用した。この記事は、つまずいた箇所も含めて、実際に行ったことをそのまま記録したものだ。

NBDEの仕組み

NBDE(Network-Bound Disk Encryption)は「ネットワークに紐付いた」ディスク暗号化モデルだ――Tangサーバーがあるローカルネットワークに接続している時だけ、ディスクが自動アンロックされる。ネットワーク外に持ち出したり、ケーブルを抜いたりすれば――ディスクは完全に保護されたままだ。

主要な3つのコンポーネント:

  • Tang:シンプルなHTTPサーバーで、ディスクアンロック用のキーマテリアルを提供する。TangはクライアントのシークレットをいっさいI保存しない――McCallum-Relyeaプロトコルに基づいた設計だ。
  • Clevis:LUKSと統合するクライアントサイドのツール。ClevisはディスクをTangサーバー(またはTPM、あるいはその両方の組み合わせ)に「ピン」し、起動時の自動アンロックを処理する。
  • LUKS:Linuxのディスク暗号化機能で、複数のキースロットをサポートする――Clevisは元のパスフレーズに触れることなく、新しいキースロットを追加する。

重要なポイント:TangはLUKSパスフレーズを知らない。アンロックプロセスはECDH(楕円曲線Diffie-Hellman)を使用する――Tangはネットワーク上で「存在して応答する」だけでよい。シークレットの保存なし、集中的な漏洩ポイントなし。

環境の準備

同じLANネットワーク内の2台のマシンを使用する:

  • TangサーバーFedora Server、IP 192.168.1.10――Raspberry Pi、VM、あるいはローカルネットワーク内の古いマシンでも構わない。
  • クライアント:LUKSディスクを持つFedora Workstation/Server、IP 192.168.1.20

要件:Fedora 36以上(筆者はFedora 40を使用)、既にLUKS暗号化済みのディスク、同一サブネット内の2台のマシン。

Tangサーバーのインストール

Tangサーバー(192.168.1.10)で:

sudo dnf install tang -y
sudo systemctl enable --now tangd.socket

Tangはソケットアクティベーション経由で動作する――常駐サービスは不要で、リクエストが来た時だけ「起動」する。デフォルトでポート7500をリッスンする。

Tangのファイアウォールを開放する:

sudo firewall-cmd --add-service=tangd --permanent
sudo firewall-cmd --reload

クライアントマシンから、Tangが動作しているか素早くテストする:

curl http://192.168.1.10:7500/adv

payloadフィールドを含むJSONが返ってくれば、Tangが正しく動作している。これはadvertisement――Clevisがバインディングに使うTangサーバーの公開鍵だ。

TangのThumbprintを取得する

Thumbprintはバインディング時にTangサーバーを検証するために使用し、中間者攻撃を防ぐ。Tangマシン上で実行する:

sudo tang-show-keys

出力は3DqH5h1TxhJGRD9pZm...のようなbase64文字列だ。メモしておく――バインドのステップで必要になる。

クライアントへのClevisのインストール

クライアントマシン(192.168.1.20)で、Clevisとdracutモジュールをインストールしてinitramfsに統合する:

sudo dnf install clevis clevis-luks clevis-dracut -y

LUKSディスクをTangにバインドする

LUKSパーティションを特定する:

lsblk -f | grep crypto_LUKS

パーティションが/dev/sda2だとする。Tangにバインドする際、thumbprintは先のステップで取得した値に置き換える:

sudo clevis luks bind -d /dev/sda2 tang '{"url":"http://192.168.1.10:7500","thp":"3DqH5h1TxhJGRD9pZm"}'

Clevisが新しいキースロットを追加するためにLUKSパスフレーズを求めてくる――入力すれば完了だ。元のパスフレーズはそのまま残る。Clevisは新しいスロットを追加するだけで、既存のものは削除しない。

バインド後のキースロットを確認する:

sudo cryptsetup luksDump /dev/sda2 | grep -E "Keyslot|ENABLED"

initramfsの再生成――最も忘れがちなステップ

バインドが完了したら、すぐにinitramfsを再生成しなければならない――このステップを飛ばすとClevisが起動時に実行されない:

sudo dracut --force

最初にまさにここでつまずいた――バインドは完了したがdracutを忘れ、リブートしても従来通り手動でパスフレーズを入力しなければならなかった。原因を突き止めるのに30分かかった。

initramfs内のネットワーク設定

ここには微妙な問題がある:クライアントはTangサーバーに接続するためにネットワークが必要だが、ルートファイルシステムはLUKSでロックされている――先にアンロックしなければならない。古典的な「鶏と卵」問題だ。clevis-dracutはネットワークスタックをinitramfsに直接組み込むことでこれを解決する。

initramfs内でネットワークを有効にするカーネルパラメータを追加する(DHCP):

sudo grubby --update-kernel=ALL --args="rd.neednet=1 ip=dhcp"

スタティックIPを使用する場合(サーバーでは一般的):

sudo grubby --update-kernel=ALL --args="rd.neednet=1 ip=192.168.1.20::192.168.1.1:255.255.255.0:myhostname:eth0:none"

新しい設定を反映するためにinitramfsを再度生成する:

sudo dracut --force

本番リブート前のテスト

バインディングを確認するために手動アンロックする――Tangが動作していればパスフレーズは不要だ:

sudo clevis luks unlock -d /dev/sda2

パスフレーズを求めてこなければ、セットアップは正しい。この時点で本番リブートをしても安全だ。

Tangサーバーが利用できない場合の対処

プロダクションデプロイ前に最も心配だった質問:Tangサーバーがダウンした時、クライアントマシンは起動できるのか?

答えは:起動できる、ただし手動でパスフレーズを入力する必要がある。Clevisはinitramfsでタングへの接続を試み、約30秒後にタイムアウトし、LUKSパスフレーズの入力プロンプトにフォールバックする。元のパスフレーズは正常に機能する。

Tangがダウンしている時に黒い画面を30秒眺めながら待つのはかなり不快だ。タイムアウトを短縮する:

sudo grubby --update-kernel=ALL --args="rd.clevis.tang.timeout=10"
sudo dracut --force

Tangキーの定期ローテーション

約6ヶ月ごとに、キーが漏洩した場合のリスクを減らすためにTangキーをローテーションすることを推奨する。Fedora 40はTang 14以上を使用しており、tangd-keygenコマンドはもはや存在しない。キーはサービスの初回起動時に自動生成され、/var/db/tang/に保存される。

jose(tangパッケージに付属)で新しいkey exchangeを追加し、サービスをリロードする:

# 現在のキーを確認
ls /var/db/tang/
sudo tang-show-keys

# 新しいkey exchangeを生成
sudo jose jwk gen -i '{"alg":"EC","crv":"P-521","key_ops":["deriveKey"]}' \
  | sudo tee /var/db/tang/rotate-exc.jwk > /dev/null
sudo systemctl reload tangd.socket

各クライアントで、新しいキーにre-bindする(SLOTはClevisが使用しているキースロット番号に置き換える):

# Clevisが使用しているスロットを確認
sudo clevis luks list -d /dev/sda2

# そのスロットをre-bind
sudo clevis luks regen -d /dev/sda2 -s SLOT
sudo dracut --force

古いキーは手動で削除するまで/var/db/tang/に残る――まだre-bindしていないクライアントが引き続き機能するためのセーフティネットとして機能する。

6ヶ月間で学んだこと

NBDEの優れた点は、セットアップが完了したら気にしなくていいことだ――サーバーはカーネルアップデート後に自動リブートし、深夜にパスフレーズを入力するために起きている必要がない。運用面で最も満足していることだ。

実際の運用を通じて学んだいくつかのこと:

  • TangサーバーはクライアントよりもI先に起動しなければならない:停電後に両方が同時にリブートすると、クライアントはタイムアウトして手動パスフレーズが必要になる。TangマシンへのUPSは価値ある投資だ。
  • Tangのアップタイムを別途監視する:Uptime KumaにシンプルなHTTPチェックを追加した――Tangがダウンするとすぐにアラートが来て、そのタイミングでクライアントサーバーがリブートしないようにできる。
  • Tangは必ず別のマシンに置く:当たり前に聞こえるが、それでも聞かれることがある。TangとクライアントをI同じマシンに置くと、暗号化の意味が完全に失われる。
  • 定期的にフォールバックをテストする:毎四半期、Tangサーバーをシャットダウンしてクライアントをリブートし、フォールバックパスフレーズが機能することを確認している。本当に必要な時になって、パスフレーズを忘れていたことに気づかないようにしよう。

まとめ

ClevisとTangを使ったNBDEは、両方を同時に実現させてくれる:十分強力なディスク暗号化と、リブートのたびにパスフレーズを入力しなくてよい利便性。トレードオフはTangサーバーの可用性への依存だが――フォールバックパスフレーズは引き続き機能するため、このリスクは許容範囲内だ。

スタック全体がFedoraの公式リポジトリに含まれている――他の多くの自作ソリューションとは異なり、サードパーティのリポジトリを追加する必要がない。

Share: