午前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_configのAllowUsersでSSHを許可するユーザーを制限PasswordAuthenticationを無効化し、key認証のみを許可
あの午前2時のbrute-force攻撃の夜以来、private keyをディスク上に剥き出しで置くことはなくなりました。TPM 2.0はほとんどの最新ハードウェアに最初から搭載されています — 一度設定すれば永続的に使え、rootでさえkeyを持ち出すことはできません。

