CentOS Stream 9でのLUKSとClevis/Tang設定ガイド:自動ディスク暗号化(NBDE)

CentOS tutorial - IT technology blog
CentOS tutorial - IT technology blog

CentOS 8がEOLになった件で、1週間以内に5台のサーバーをRocky Linuxへ急いでマイグレーションしなければなりませんでした。最も後悔したことの一つは、移行前にNBDEをセットアップする時間がなかったことです——結果的に新しい環境で一から設定し直すことになりました。この経験を活かして、今回はCentOS Stream 9向けに詳細な手順を記録しておきます。

背景:従来のLUKSが抱える実際の問題

データセンターにbare-metalサーバーがあり、ディスクがLUKSで暗号化されているとします。サーバーを再起動するたびに——カーネルアップデート、停電、メンテナンスにかかわらず——コンソールで手動でパスフレーズを入力するために待機しなければなりません。5〜10台のサーバーでもうんざりしますが、より大規模なフリートや夜間に人が常駐していない環境では言うまでもありません。

NBDE(Network-Bound Disk Encryption)はまさにこの問題を解決します。内部ネットワーク内(Tang serverが存在する場所)でサーバーが起動する際にディスクが自動的にアンロックされ、サーバーがネットワーク外に持ち出された場合は手動でパスフレーズを要求します。これがポイントです——automationを妨げることなく物理的なセキュリティを確保できます。

NBDEが最も適しているケース:

  • 機密データ(PII、金融データ)を保持するデータベースサーバー
  • コロケーションデータセンターのBare-metalまたはVM
  • コンプライアンス要件:HIPAA、PCI-DSS、ISO 27001
  • 担当者なしでunattended-upgradesの後に自動再起動するサーバー

アーキテクチャは2つの主要コンポーネントで構成されています:

  • Tang server:McTLSプロトコルを使用するkey保管専用のサーバー。Tangはクライアントのkeyを保存しません——Diffie-Hellmanによるkey導出プロセスに参加するだけです。Serverが侵害されても、LUKS keyが漏洩することはありません。
  • Clevis:クライアント側のFramework。複数の"pin"(tang、tpm2、sss)をサポートし、LUKS volumeをTangまたはTPMにBindします。

起動フロー:Dracut initramfs → ClevisがnetworkでTangに接続 → Tangがkey導出のためのデータを返す → LUKSがアンロック → 起動継続。

TangとClevisのインストール

1. Tang Serverのインストール

Tangはアンロックが必要なサーバー自体ではなく、専用のサーバーまたは別のVMで実行する必要があります。CentOS Stream 9の場合:

dnf install tang -y
systemctl enable --now tangd.socket
firewall-cmd --add-service=tangd --permanent
firewall-cmd --reload

Tangがリッスンしているか確認し、thumbprintを取得します(Clevisをbindする際に必要):

systemctl status tangd.socket

# keyのthumbprintを取得する
jose jwk thp -i /var/db/tang/*.jwk

thumbprintを保存してください——例:abc123XYZdef456。bindステップで使用します。

2. クライアント側のClevisインストール

自動暗号化が必要なCentOS Stream 9サーバーの場合:

dnf install clevis clevis-luks clevis-dracut -y

# TPM2サポートも必要な場合
dnf install clevis-tpm2 -y

詳細設定

ステップ1:Bindが必要なLUKS volumeを特定する

# LUKS deviceを確認する
lsblk -f | grep crypto
blkid | grep LUKS

# LUKS headerの情報を確認する(使用中のslot数、UUIDなど)
cryptsetup luksDump /dev/sda3

新しいディスクを追加した際の混乱を避けるため、device nameではなくUUIDを使用することをお勧めします。blkidの出力からUUIDを保存してください。

ステップ2:LUKSをTangにBind

# TANG_SERVER_IPとTHUMBPRINTを実際の値に置き換える
clevis luks bind -d /dev/sda3 tang \
  '{"url":"http://TANG_SERVER_IP","thp":"THUMBPRINT"}'

Clevisは新しいslotを追加するために現在のLUKS passphraseを要求します。成功すると、LUKSに2つのslotが作成されます:slot 0(元のpassphrase)とslot 1(Clevis/Tang)。確認:

clevis luks list -d /dev/sda3
# 出力:
# 1: tang '{"url":"http://192.168.1.50","thp":"abc123..."}'

ステップ3:initramfsの再構築(最もよく見落とされるステップ)

これは私がかつてスキップしたステップで、その結果、本番サーバーのreboot後に午前2時にpassphrase入力を待つことになりました。BootloaderにClevisを組み込むためにinitramfsを再構築する必要があります:

dracut -fv --regenerate-all

initramfsにClevisが含まれていることを確認:

lsinitrd /boot/initramfs-$(uname -r).img | grep clevis

ステップ4:/etc/crypttabの更新

データvolume(rootではない)の場合、/etc/crypttabに追加します:

# フォーマット: name  device  keyfile  options
data-encrypted  UUID=your-luks-uuid  none  _netdev,x-initrd.attach

_netdevオプションはsystemdにmount前にnetworkが必要であることを通知します。x-initrd.attachはinitrd内で処理されることを保証します。これら2つのオプションがない場合、volumeは正しい起動順序でアンロックされません。

ステップ5(オプション):SSSによるHA——2台のTang server

高可用性が必要な場合、SSS(Shamir’s Secret Sharing)を使用することで、threshold付きで複数のpinを組み合わせることができます。例えば、2台のTang serverのうち少なくとも1台が必要な場合:

clevis luks bind -d /dev/sda3 sss \
  '{"t":1,"pins":{"tang":[{"url":"http://tang1.internal","thp":"THUMB1"},{"url":"http://tang2.internal","thp":"THUMB2"}]}}'

この設定により、2台のTang serverのうち1台がダウンしてもサーバーを起動できます。

テストとモニタリング

rebootなしのテスト

# Clevisが復号できるかテスト
clevis luks unlock -d /dev/sda3 -n test-unlock
# 成功した場合 → /dev/mapper/test-unlockが作成される

# テスト後にクリーンアップ
cryptsetup close test-unlock

# Tang接続を直接テスト
curl -sS http://TANG_SERVER_IP/adv | jose fmt -j- -g payload -y -o-
# 出力はkeyを含むJSON——空の場合はfirewallを確認

起動時のログ確認

# 現在の起動セッションのClevisログ
journalctl -b 0 | grep -i clevis

# Tang serverのリアルタイムログ
journalctl -u tangd -f

Prometheus BlackboxでTangをモニタリング

Tangにはビルトインのmetricsがありません。Blackbox Exporterを使用して/advエンドポイントを確認します:

# blackbox.yml
modules:
  tang_check:
    prober: http
    http:
      valid_status_codes: [200]
      method: GET
      fail_if_body_not_matches_regexp:
        - "keys"
# prometheus.yml
- job_name: 'tang'
  metrics_path: /probe
  params:
    module: [tang_check]
  static_configs:
    - targets:
      - http://TANG_SERVER_IP/adv
  relabel_configs:
    - source_labels: [__address__]
      target_label: __param_target
    - target_label: __address__
      replacement: localhost:9115

重要なアラート:Tangが5分以上ダウンした場合は、即座にoncallに通知してください——次のreboot時に手動でpassphraseが要求されるためです。

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

# Tang serverで:新しいkeyを生成
tangd-keygen /var/db/tang

# クライアントで:新しいkeyでre-bind(slot 1)
clevis luks regen -d /dev/sda3 -s 1

6ヶ月ごとにkeyをローテーションするスケジュールを設定し、Ansible playbookでre-bindをトリガーすることで、各サーバーへの手動SSHが不要になります。

LUKSヘッダーのバックアップ——必須

以前のCentOS 8からRocky Linuxへの移行の件で、重要な教訓を学びました:設定完了直後にLUKS headerをバックアップすること。headerを失うと = dataを永久に失います。復旧する方法はありません:

cryptsetup luksHeaderBackup /dev/sda3 \
  --header-backup-file /root/luks-header-sda3-backup.img

# このファイルを別の場所に保存する(同じサーバーではなく)
scp /root/luks-header-sda3-backup.img backup-server:/secure-backups/

dracutタイムアウトエラーの対処

ClevisがTangを見つけられずbootがタイムアウトする場合は、dracut configでタイムアウトを増やしてください:

# /etc/dracut.conf.d/clevis.conf
kernel_cmdline="rd.timeout=90 rd.neednet=1"

# 再構築する
dracut -fv --regenerate-all

NBDEのセットアップは初回に約1〜2時間かかりますが、その後はフリート全体が誰も立ち会わずに自動的に再起動できます——data-at-restが適切に暗号化されているため、セキュリティチームも安心できます。

Share: