世界を動かす技術を、日本語で。

SSH証明書: より良いSSH体験

概要

  • SSHの初回接続時 にはサーバの真正性確認が必要
  • 公開鍵認証 によりパスワード入力を省略可能
  • ホスト鍵の変更 時には警告や手動対応が必要
  • SSH CA(認証局)と証明書 を使えば運用が大幅に簡素化
  • 証明書運用のメリット と基本的な導入手順を解説

SSH証明書:より良いSSH体験

  • サーバへ初めて SSH接続 する際、「このサーバが本物か?」という確認ダイアログの表示
    • 例:ssh -l jane 192.0.2.65 実行時の警告表示
  • Trust on First Use(TOFU) による初回接続時の信頼確立
    • サーバ管理者から フィンガープリント を事前に取得・照合する運用推奨
    • 例:ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key でフィンガープリント確認
  • ssh-keyscan などで公開鍵取得可能だが、 別経路での検証 が必要

SSH鍵ペアによる認証

  • 自分の 公開鍵 をサーバの$HOME/.ssh/authorized_keysに設置
    • サーバ接続時に 秘密鍵 とパスフレーズで認証
  • 鍵作成例ssh-keygen -t ecdsa -C "JP's demo key" -f demokey
    • 公開鍵のサーバ登録:ssh-copy-id -i demokey.pub jane@192.0.2.65
  • SSHエージェント 利用でパスフレーズ入力の省略
    • eval $(ssh-agent)ssh-add demokeyで管理

公開鍵認証の課題

  • 各ユーザーごとに公開鍵設置 が必要
  • TOFU による known_hostsファイル へのホスト鍵保存
  • ホスト鍵のロール(再生成) 時に警告発生
    • サーバのホスト鍵変更時、ssh-keygen -Rknown_hosts から削除し再登録が必要
  • 大規模環境 ではこれらの運用が煩雑

SSH CA(認証局)とSSH証明書

  • SSH CA はシンプルな SSH鍵ペア で運用可能
    • OpenSSH 5.4以降 で証明書運用対応
  • SSH証明書 の特徴
    • 公開鍵ファイルに見えるが、 証明書情報 を含む
    • 例:ecdsa-sha2-nistp256-cert-v01@openssh.com ... Jane's key
  • CA鍵ペア を使い、ユーザー鍵やホスト鍵を署名
  • CA導入のメリット
    • サーバへの公開鍵配布(authorized_keys編集)が不要
    • 不要な鍵の登録リスク低減
    • ホスト鍵ロール 時もクライアント側警告なし
    • TOFU不要、ユーザー・サーバ間の相互信頼が自動化
    • 証明書の有効期間や利用ユーザー(プリンシパル)を柔軟に制御
    • IP制限 やリモートコマンド制限なども証明書側で設定可能
    • known_hosts にCA公開鍵を1行追加するだけで全ユーザー適用

SSH CA運用の基本手順

  • CA用ディレクトリ作成 と鍵生成
    • 例:umask 077; mkdir CA; ssh-keygen -t ecdsa -C "JP's SSH CA" -f CA/ssh-ca
  • ユーザー鍵ペア生成 (既存鍵でも可)
    • 例:ssh-keygen -t ecdsa -C "Jane's key" -f jane
  • ユーザー公開鍵をCAで署名し証明書発行
    • 証明書発行コマンド例や詳細は割愛(続きで解説可能)

SSH証明書運用のまとめ

  • 大規模サーバ運用セキュリティ要件の高い現場 でのSSH運用最適化
  • 鍵管理・配布・信頼性向上 のための現代的アプローチ
  • OpenSSH標準機能 で導入可能、追加ソフト不要

ご要望があれば、 証明書署名コマンド例やサーバ・クライアントの設定手順 も詳述可能です。

Hackerたちの意見

数ヶ月ごとに誰かがSSH証明書を再発見して、ブログに書くんだよね。俺もその一人だ。15年前の俺のブログ記事はOPの投稿には全然及ばないけど、15年前の自分が今の基準に達してたら、かなりがっかりするだろうな。: https://blog.habets.se/2011/07/OpenSSH-certificates.html

SSH証明書のことは前から知ってたけど、キーから移行する努力はしてなかったんだ。でも、いろんなサーバーやデバイスでSSHキーを手動で管理するのはすごくイライラする。15年の間にたくさんの考えをまとめたと思うけど、切り替えるべきかな?

SSH証明書のもう一つの便利な機能は、ユーザーの公開鍵に署名して、特定のリモートユーザーとしてリモートマシンへのアクセスを限られた時間だけ許可できることだよ。

怖い現実は、多くの人が「鍵」と「証明書」を混同していることだと思う。セキュリティエンジニアと一緒に働いていると、SSH証明書は使わないってことを思い出させないといけないことがあるんだよね。彼らはそれを理解するのにちょっと考えないといけないみたい。

ああ、やばい、10年くらい前に$jobのためにSSH証明書認証局を書いたとき、あなたのブログ記事を参考にしたよ… 書いてくれてありがとう!

開発/ステージング環境では、毎朝半分のマシンを再インストールしてるんだ(主にマシンセットアップの自動化をテストするため)。SSHホスト証明書があると、ホストキーを保持したり、known_hostsから削除・置き換えたりする必要がなくて、すごく楽になるよ。めっちゃおすすめ!

著者はCA証明書の利点をすべて挙げてるけど、欠点は挙げてないね。一方で、設定に必要な多くのステップがあるから、欠点はかなり明らかだよ。TOFUのせいでセキュリティ問題が起きたことはないけど、君はどう?

TOFUは便利だけど、必須ではないよ。TOFUを使う選択は、SSHで生成されたキーを使う選択とは別のものなんだ。もしTOFUを使いたくないなら、追加のセキュリティのために、対応する公開鍵を安全なチャネル(例えばUSBメモリを使って)でコピーして、コンピュータ同士をペアリングすればいい。証明書を使っても、簡略化や追加のセキュリティは得られないよ。本当のセキュリティを得るには、通信するコンピュータ同士を、やっぱり安全なチャネルで証明書をコピーしてペアリングする必要がある。HTTPSでブラウザに付属している証明書を使うときは、そのインストーラーパッケージが証明書を作成した権威から安全なチャネルでそのコンピュータに届いたと信じることになる。これは、君が管理しているコンピュータ間でTOFUを信頼できるという仮定よりも、はるかに非現実的な仮定だよ。証明書は大きな組織では役立つかもしれないけど、単に安全な通信チャネルを確立する以上の機能が必要な場合、例えば証明書の失効を使いたいときにね。親記事で挙げられている「利点」のリストの半分以上は間違ってる。なぜなら、証明書が正しく実装されていれば、TOFUなしのSSHキーを使うときと証明書を使うときで、完全に同じアクションを実行しなければならないから。著者は「利点」のいくつかを書くことで、証明書を使うときに必要なくなるアクションはユーザーではなく管理者が行うものだと言いたかったのかもしれない。でも、それはSSHにも当てはまる。管理者が証明書をインストールすれば、ユーザーからのアクションは不要になるけど、管理者がSSH公開鍵をインストールすれば、ユーザーからのTOFUも不要になる。証明書を使うには、SSHで生成されたキーを使うのとまったく同じステップが必要なんだ(つまり、証明書を生成して、安全なチャネルでコンピュータ間でコピーして、サーバーと認可されたユーザーをペアリングすること)。でも、証明書は追加の機能を提供するから、追加のステップが必要になることもある。

それに、TOFUのせいでセキュリティ問題が起きたことはないけど、君はどう? これは、車の事故に遭ったことがないからシートベルトは考慮する価値がないと言ってるようなものだね。設定に関する明らかで文書化された作業を超えて、SSH証明書を使うことに欠点があると思う?

ボックスに接続する前にCA設定を行うためのアクセス手段があるなら、その同じアクセスチャネルを使ってTOFUに頼らずに鍵アクセスを設定できるよ。インストールスクリプトの一部からカスタマイズされたデプロイメントイメージ、仮想化シナリオでのホストへの物理アクセスまで、何でもあり。TOFUは、ボックスがすでに設定されていて、SSHで接続して物をボックスにロードする以外に方法がないときに本当に関係してくる。でも、証明書アプローチを取るつもりなら、同じ話になるよ。

それに、TOFUのせいでセキュリティの問題が起きたことはないけど、君はどう? TOFUのせいでセキュリティの問題が起きたことがないのは君だけじゃないよ - それ、直したよ。

最近のnpmハックでプライベートキーが盗まれる事件があったから、キーの有効期限を制限したいと思ってる。管理しているホストにSSH CAとしていくつかのYubiKeyを設定したよ。これを使って、1日の始まりに短期間の証明書(例えば24時間)を作成してる。この方法だと、YubiKeyのPINを1日に1回だけ入力すればいいから楽なんだ。OpenSSHで最大証明書の有効期限を簡単に制限する方法が見つからなかったけど、AuthorizedPrincipalCommandを使うのが唯一の方法で、ちょっと脆弱に感じる。似たようなセットアップをしている人はいる?証明書の最大有効期限をどうやって制限してる?

CAを使う代わりに、鍵のPINポリシーを「一度」に設定して、アクティブセッションを保持するエージェント(例:https://github.com/FiloSottile/yubikey-agent/)を使ってみたらどう?エージェントを一日の始まりに起動して、一度PINを入力して、終わりにエージェントを停止する。

SSH証明書は、プロダクション環境で静かに問題を引き起こす。短命のクレデンシャルと中央集権的なCAは、根本的な問題であるユーザー管理を解決せずに複雑さを増すだけだ。システムは多くの小さなローカル状態から、一つの高度に結合された制御ポイントに移行する。その制御ポイントは常に正確でアクセス可能でなければならない。そうでないと、失敗が広がってしまう。例えば、いくつかのボックスがハッキングされてCAを攻撃し始める。そうなったらどうする?アクセスが一気に壊れる。一般的な摩擦ポイントは以下の通り:1. 常に稼働して正確でなければならないサイナー 2. どこにでもある(そして漂っている)信頼のルート 3. TTL調整の無駄(短すぎるとランダムなロックアウト、長すぎると何の意味があるのか) 4. 限られたボックス内の状態がデバッグを難しくする 5. 失敗が広がりやすく、収束しない。取り消しも一種の嘘だ。期限切れを待って、それで十分だと期待しているだけ。実際には、人々はサイドカーやキャッシュ、エージェントなどで状態を再導入する。必要だからね。私たちは逆の方向に進んだ:1. ノードはアウトバウンドHTTPSでプルする 2. ローカルのauthorized_keysがローカルの真実の源 3. ユーザー/ロールがボックス上で見える 4. 漂流がすぐに修正される 5. インバウンドポートなし、CA署名なし(厳密にはそうではないけど!)中央制御はまだあるけど、運用と失敗のモードはローカルで、「みんなが今すぐロックアウトされている」状態ではない。これが基本的にUserifyでやっていることだ(https://userify.com)。証明書よりは洗練されてないけど、午前2時でも生き残れる。認証だけでなく、認可もちゃんと扱ってるし。SSH CAでよく軽視される部分:1. ユーザーアカウントの作成 2. sudoロールの管理 3. 削除時のホームディレクトリの扱い 4. コンプライアンス/フォレンジックのためのクリーンアップと保持。これらは消えないけど、証明書ソリューションの一部ではない。*(TLSはここに存在していて、システムの信頼ストアを使ってトランスポート層で動いている。そのチャネルはユーザー、鍵、ロールを提供する。残りは暗黙的ではなく明示的に処理される。)

TOFUはどう解決するの?

この議論は「安全な」SSHのための支離滅裂な解決策で溢れていて、ほとんどが実用的な意味を持たないか、技術的な根拠がない。信頼できる権威からの明確なベストプラクティスガイドが必要だよ。

僕の考えでは、ソフトウェアキーを使うのをやめることがもっと重要だから、ホストとユーザーの両方にsk(fido)を使った方がいいと思う。そこからCAが次のステップになるかな。ドキュメントや例のセットアップが驚くほど貧弱で、どの機能のステップ2を見てもそう。つまり、SKキーはユーザーキーとしては理解されてるけど、ホストキーとしての設定は曖昧で、本当に機能するかテストが必要だよ。

企業環境で働いていて、ZscalerとそのSSL検査のせいで無駄にしたお金と時間は、あなたの想像を超えているよ。 "SSL証明書の問題:証明書チェーン内の自己署名証明書"というエラーを見るたびに、トラブルだってわかる。

Zscalerが友達の職場のWindows 11マシンに導入されたんだけど、ソフトウェアを評価したら、その挙動がまるでマルウェアみたいに悪質だった。知っての通り、TLS/HTTPS接続を監視するために、自分のルート証明書をOSに注入して中間者攻撃を仕掛けるんだよね。さらに、ウェブブラウザの設定をロックして、ZscalerのMITMを回避するためにプロキシサーバーを使えなくしてる。Mozilla Firefoxでこの挙動を見たけど、プロキシオプションが「プロキシなし」に設定されてて、他のオプションは無効化されてグレーアウトしてた。Google Chromeでも同じことしてると思う。ブラウザのプロキシ設定の.iniファイルを変更しようとすると、Zscalerがすぐに上書きしてくる。Zscalerはユーザーを監視するために設定を強制するためにすごく頑張ってるよね。そして、当然だけど、システムトレイのZscaler GUIを開くと、ITパスワードがあればソフトを無効にするオプションが出てくる。でも、もちろん、そんなパスワードは持ってない。まあ、Cybereasonのアンチウイルスソフトよりは少しマシかも。あれはシステムトレイアイコンしかなくて、終了オプションもないし、タスクマネージャーでもローカル管理者でも終了できないし、毎秒何百もの小さなテキストファイルを開くと重くなるから。

僕が働いてるところのCisco Umbrellaについても同じ気持ちだ。最悪なのはプロトコルの破損で、基本的にHTTPを基にした他のプロトコルが頻繁に壊れる。実装してる人たちは全然気づいてないみたい。彼らは「透明だ」と言ってるベンダーの主張を信じてるけど、実際には「検査/トレース専用」モードでもいろんなものが壊れることが多い。Umbrellaが壊したものを挙げると、- Git - RubyGems - go mod - OrbStack - Matrix - Cargo - すべてのJDK - Nix - Pkgsrc - すべてのVM、他にも忘れてるものがあるかも。こういう破損が報告されると、最初の反応は「企業管理されたブラウザでそのドメインにアクセスしたけど、ブロックされてない」っていう無意味で関係のないテスト。多くの場合、破損を完全に診断するのに数時間かかるし、そのツールのせいだと自信を持って指摘するのは難しい。これを買って導入してる人たちが、どれだけのものが壊れるか知らないのか、ただ気にしてないのかはわからないけど、壊れ方はどこにでもあって、誰もそれに備えてないみたい。

SSH証明書はX.509証明書じゃないよ。

TOFUの草の根的な性質には欠点もあるけど、結構役立つよね。自分のマシンでサーバーのホストキーがSSHクライアントで見えるものと一致してるか確認できるし。TOFUが問題なさそうなら、そのホストには安心して接続できる。だって、キーを変えないからね。シンプルな企業のUnix環境では、内部サーバーの公開鍵をSSL経由でアクセスできる内部サイトにリストアップしておけば十分。これでTOFUを実行するための確認が一度だけで済むから、あとは未来の接続を信頼できる。大量のマシンがある環境や、新しいマシンが常に追加されるようなダイナミックな環境では、証明書を使った方が楽かもしれない。もちろん、証明書には手間がかかるし、機能も増えるから、メリットはケースバイケースだけど。この規模だとTOFUは色々と問題が出てくるから、証明書に対して強い意見を持つのは難しいよね。ウェブブラウザもサーバーのTLSホストキーを簡単に記憶してくれたらいいのに、変わった時には通知してくれるとかさ。新しいキーを受け入れるのはいいけど、信頼できるCA経由で。

1996年からSSHを使ってるけど、そんな風に公的キーを手動で確認してる人を見たことがないよ。もし彼らがあなたのSSHサーバーをMITMできるなら、なんで内部のウェブサイトもMITMできないの?一般的には、TOFUは「はい」と言ってYOLOって感じだね。

Kerberos SSO、つまりOpenSSHのGSSAPI認証を使えると、生活が楽になるよね。(証明書の話をしてるなら、たぶんOpenSSHのことだよね?それとも他に実装してるものがあるの?)

なんでSSHを本物のIdPに証明書で接続するんじゃなくて、そうするの?

SSHキーを使わずにパスワードでSSHする人がいるのは、いつも驚きだよ。特に大企業では、いろんな環境があって、パスワードも違うし、パスワードの有効期限やローテーション、複雑さのルールもあるのに。助けを求めたり、一緒に作業したりするときに「じゃあ、devfoo1234にSSHしよう」と言うと、彼らはそれをやって、パスワードを入力するんだけど、間違えることもあって、リセットしなきゃならなかったりする。そうすると、ホストにSSHするのに30秒以上かかることもある。複数のホストが関わることもあって、すごく時間がかかる。僕は「実はSSHのパスワードは使わないんだ。devfoo1234のパスワードも知らないし、ssh-keygenでググって設定してみたらどう?」って言うと、「ああ、それいいね。後でやってみるよ!」って言うんだけど、結局やらないでずっとパスワードに悩まされてる。全然理解できない。

規制されていない/分散型のSSHキーの使い方(つまり、ssh-copy-idを許可すること)は、ハッカーがネットワーク内で横移動するための夢みたいなものだよね。だから、多くの組織はそれを無効にしてるし、ちゃんとした中央集権的なCAや認証サーバーを整備するためにリソースを投資してないんだ。

キーは個人利用にはすごくいいけど、会社で使うなら中央集権的なキー管理が必要だね(ユーザーごとに1つ以上のキーを発行する)。よくあるのは、全マシンで1つの「開発用SSHキー」を使っちゃう(これは良くない)か、みんながキーを共有したり、マシンに識別できないキーがある状態になること。パスワードは少なくとも「簡単」だから、みんな扱いやすいよね。

ここ数年、私はHSMの後ろに安全に隠されたSSHプライベートキーしか使ってない。攻撃面は超小さいよ。Yubikeyが私には合ってる(他のベンダーでも大丈夫だけど)。私のSSHキーにはパスワードはないけど、SSHでログインする時は、物理的にYubikeyに触れる必要があるんだ(私のYubikeyのうちの1つね)。