LinuxでTPM 2.0によるSSH Keyの保護:攻撃者がroot権限を持っていてもPrivate Keyは盗めない

Security tutorial - IT technology blog
Security tutorial - IT technology blog

午前2時の出来事

約6ヶ月前、Fail2banから大量のアラートが届きました。サーバーがかつてないほどの頻度でSSHブルートフォース攻撃を受けていたのです。午前2時にラップトップを開いてリモート接続し、対処してIPをブロックし、不正侵入の痕跡がないかログを一行一行確認しました。幸い誰も侵入できていませんでしたが、真夜中にファイルを一つずつ確認するあの感覚…言葉にできないほど不快でした。その経験から高い授業料を払って学んだこと:何かが起きてからではなく、最初からセキュリティを設定しておくこと。

その後、改めて考えてみました。もし攻撃者がroot権限で侵入できたとしたら?private keyは~/.ssh/id_rsaにあります。passphraseを設定していてもファイルはディスク上にあり、いつでもコピーできます。key fileが一度持ち出されたら、攻撃者はオフラインで時間無制限にクラックでき、自分のサーバーへの接続はもう必要ありません。

核心的な問題:ディスクに保存されたSSH Keyは致命的な弱点

通常のSSH key保存方法には根本的な脆弱性があります:private keyはディスク上のファイルです。パーミッション600を設定し、強力なpassphraseがあっても、root権限があれば攻撃者は以下のことができます:

  • ~/.ssh/id_rsaファイルをコピーしてhashcatでオフラインクラック — RTX 4090 GPUを使えば、弱いpassphraseなら数分で突破される
  • keyがロードされているときにssh-agentプロセスのメモリをdump(keyはRAMにplaintextで存在する)
  • OpenSSHの共有ライブラリにhookして署名時にkeyをintercept
  • root権限で/proc/<pid>/mem経由でkeyを読み取る

passphraseが弱いからでも、パーミッションが間違っているからでもありません。致命的な点はprivate keyをサーバーの外に持ち出せることです。ファイルはコピーでき、メモリはdumpできる — どんなpassphraseもこの脆弱性は塞げません。

解決策

方法1:ハードウェアセキュリティキー(YubiKey、Nitrokey)

YubiKeyはprivate keyをチップ内に保存し、extractできません。SSHのたびに物理的に挿入する必要があります。YubiKey 5 Seriesは$50〜60程度、Nitrokeyは少し安く(約$35〜45)。個人ラップトップには適していますが、24時間365日稼働するヘッドレスサーバーには現実的ではありません。データセンターのVPSにYubiKeyを挿すことはできないからです。

方法2:短いTTLのSSH Certificate Authority

CAを使って短い有効期限(例:8時間)のcertificateに署名する方法です。keyが漏洩しても、その期間内しか使用できません。デメリットは、かなり複雑なインフラが必要なこと — HashiCorp Vault SSH Secrets Engineを使うか、自前でCAを構築する必要があり、その期間中はkeyがファイル形式で存在し続けます。

方法3:TPM 2.0 — 内蔵チップ、追加ハードウェア不要

TPM(Trusted Platform Module)は、2016年以降に発売されたほとんどのサーバーやラップトップに内蔵されているセキュリティチップです。このチップはhardware内でkey pairを生成・保存します — 攻撃者がroot権限を持っていても、RAM全体やディスクをdumpしても、private keyはチップから外に出ることはありません。

最善の方法:tpm2-pkcs11でTPM 2.0を使ったSSH Key

ステップ1:サーバーにTPM 2.0があるか確認する

# TPMデバイスの存在を確認
ls /dev/tpm*
# またはバージョンを確認
cat /sys/class/tpm/tpm0/tpm_version_major
# 出力が"2"ならTPM 2.0

# 必要なパッケージをインストール(Ubuntu/Debian)
sudo apt install tpm2-tools tpm2-abrmd tpm2-pkcs11 tpm2-pkcs11-tools

# TPM resource manager daemonを起動
sudo systemctl enable --now tpm2-abrmd

/dev/tpm0または/dev/tpmrm0が確認できればサーバーにTPMが搭載されています。一般的なVPSのほとんどにはTPMハードウェアがありませんが、一部のクラウドプロバイダーはvTPMをサポートしています — AWS Nitro、Google Cloud Confidential VMs、Azure Gen 2 VMs。

ステップ2:TPM内にPKCS#11 token storeを初期化する

tpm2-pkcs11ライブラリにより、OpenSSHがPKCS#11を通じてTPMと通信できます — PKCS#11はハードウェアセキュリティモジュールの標準インターフェースです。

# storeを初期化
tpm2_ptool init

# ラベル名と2種類のPINでtokenを作成:
# sopin = Security Officer PIN(userpinのリセットに使用)
# userpin = SSH時に日常的に使用するPIN
tpm2_ptool addtoken \
  --pid=1 \
  --label=ssh-keys \
  --sopin=SuperAdminPin123 \
  --userpin=MyUserPin456

ステップ3:TPM内にSSH Keyを作成する

# EC key P-256を作成(推奨 — 小さく、高速、TPM 2.0が良好にサポート)
tpm2_ptool addkey \
  --label=ssh-keys \
  --userpin=MyUserPin456 \
  --algorithm=ecc256

# 古いシステムとの互換性が必要な場合はRSA 2048
tpm2_ptool addkey \
  --label=ssh-keys \
  --userpin=MyUserPin456 \
  --algorithm=rsa2048

# TPM内に作成されたkeyを確認
tpm2_ptool listobjects --label=ssh-keys

ステップ4:Public Keyを取得してサーバーに配置する

# TPMからpublic keyを取得して対象サーバーにコピー
ssh-keygen -D /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so

# 出力形式:
# ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAI...

# 直接対象サーバーにコピー
ssh-keygen -D /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so | \
  ssh user@remote-server "cat >> ~/.ssh/authorized_keys"

~/.ssh/configを設定してSSHが自動的にTPM keyを使用するようにします:

Host production-server
    HostName your-server.com
    User ubuntu
    PKCS11Provider /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so

ステップ5:TPM経由でSSH接続する

# SSHはpassphraseの代わりにPINを要求する
ssh production-server
# Enter PIN for 'ssh-keys': ****

# またはPKCS11 providerを直接指定
ssh -I /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so user@remote-server

検証:Keyは本当にコピーできないのか?

自分で確認のためにテストしました。root権限で、private key materialを見つけるあらゆる方法を試しました:

# システム全体でprivate key fileを検索
sudo find / -name "*.pem" -o -name "id_rsa" -o -name "id_ecdsa" 2>/dev/null
# → TPM keyに関連するprivate key fileは見つからない

# TPM内のobjectを確認
tpm2_ptool listobjects --label=ssh-keys
# メタデータのみ表示:CKA_ID、CKA_CLASS、CKA_LABEL...
# raw private key materialは存在しない

# tpm2_ptoolにprivate keyをexportするコマンドは存在しない
# 権限不足ではなく — TPMハードウェアが設計上
# このinterfaceを提供しないのです

実際のメカニズム:private keyをRAMにロードしてから署名する代わりに、OpenSSHはTPMチップに「このデータに署名してください」というリクエストを送ります。TPMはチップ内で署名処理を行い、署名を返します。private keyはplaintextの形でチップから外に出ることは一切ありません — カーネルでさえ読み取ることができません。

6ヶ月の実際の使用で学んだこと

  • PINを即座にバックアップ:作成直後にuserpinとsopinの両方をpassword managerに保存してください。両方を忘れるとkeyは永久に失われ、recoverする方法はありません。
  • OSの再インストールでtoken storeが失われる:一部のディストリビューションを再インストールするとTPMがクリアされます。key pairを再作成し、すべてのサーバーのauthorized_keysを更新する必要があります。手順を記録しておき、素早く対応できるようにしておきましょう。
  • ライブラリのPathはディストリビューションによって異なる:Ubuntuは/usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so、Arch Linuxは/usr/lib/libtpm2_pkcs11.so、Fedoraは/usr/lib64/pkcs11/libtpm2_pkcs11.soを使用します。検索コマンド:find /usr -name "libtpm2_pkcs11.so"
  • パフォーマンスは問題なし:TPM経由の署名はソフトウェアkeyに比べて約50〜100ms遅くなります。実際にはSSH接続のセットアップに少し時間がかかりますが、体感できるほどではありません。
  • VPSにTPMがない場合:一般的なVPSのほとんどにはTPMハードウェアがありません。この場合、短いTTLのSSH Certificateや、個人マシンからSSHする場合はYubiKeyの使用を検討してください。

セキュリティレイヤーの組み合わせ

TPM keyは特定のattack vectorを塞ぎます:攻撃者がroot権限を持っていても、infrastructure内の他のサーバーへのlateral movementに使うprivate keyを取得できません。現在並行して運用しているセキュリティ対策は以下のとおりです:

  • SSH keyをTPM内に保存 — rootでもextractできない
  • Fail2banでIPごとのbrute-force攻撃をブロック
  • SSHポートを22から変更してログのノイズを削減
  • /etc/ssh/sshd_configAllowUsersでSSHを許可するユーザーを制限
  • PasswordAuthenticationを無効化し、key認証のみを許可

あの午前2時のbrute-force攻撃の夜以来、private keyをディスク上に剥き出しで置くことはなくなりました。TPM 2.0はほとんどの最新ハードウェアに最初から搭載されています — 一度設定すれば永続的に使え、rootでさえkeyを持ち出すことはできません。

Share: