SSHのホスト鍵設定

ホスト鍵とは

SSHの鍵はデジタル署名用の秘密鍵であり、本人確認に使われる。いわゆるid_rsaは、ユーザー側の本人確認のために使われる。

一方、SSHサーバー側も専用の秘密鍵を所有している。これは /etc/ssh/ssh_host_{dsa,ecdsa,ed25519,rsa}_key のような場所に保管されている。SSH接続の際には、これを使った公開鍵認証も行われる。クライアント側の .ssh/known_hosts と照らし合わせてチェックする。

ホスト鍵がないと何が問題か

もし悪意ある第三者が通信を盗聴・改竄できる場合、偽のサーバーと通信させることができてしまう。すると、適切な暗号を用いて全くの別人と会話しているという本末転倒な状態になる。偽のサーバー上と気付かずにsudoパスワードを入れたら困ったことになるかもしれない。

HashKnownHostsをオフにする

筆者は、ホスト鍵を安全に運用するために、HashKnownHostsオフにするべきだと考えている。

そもそも HashKnownHosts とは、 .ssh/known_hosts においてホスト名をハッシュ化して保存するオプションである。ハッシュ化をしない場合、 known_hosts の各行は以下のようになっている。

example.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHayNTYAAAAIbmlzdHAyNTYAAABBBAHOh5LY0tU5hZGZn4iFvUQ9EJSGW7n9KbTXj5WK5AEIQNB5ShhNPwJcXqtc5hxwEmBX2VSdjUFkIT6U2Otur7w=

HashKnownHosts を使うと、「ホスト名またはIPアドレス」「署名方式」「公開鍵」のうち、「ホスト名またはIPアドレス」の部分がハッシュ化され、

|1|tbdGjw+HE9Clw2hC7ezBLOMGFGI=|xOtpgqDyfDlT/PB7cYm442R1+zY= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHayNTYAAAAIbmlzdHAyNTYAAABBBAHOh5LY0tU5hZGZn4iFvUQ9EJSGW7n9KbTXj5WK5AEIQNB5ShhNPwJcXqtc5hxwEmBX2VSdjUFkIT6U2Otur7w=

のようになる(|1|ソルト|ハッシュ|)。ハッシュ化することで、

  • 特定のホスト名が、この行に該当するかどうかは判定できる。
  • しかし、この行だけ見て、ホスト名を復元することは困難である。

となる。つまり、

  • HashKnownHostsメリット: known_hosts が漏洩しても、接続先ホストの情報が復元できない。
  • HashKnownHostsデメリット: known_hosts の管理が困難になる。どのホストを信任しているかわからないため、偽のホストを信頼してしまう危険性が増える。

と考えられる。接続先ホストの情報は仮に漏れても大きな影響はなさそうだし、むしろ known_hosts が漏洩するような状況では秘密鍵など他の重要な情報も漏れている状況だから、メリットがデメリットに釣り合わないと思う。

known_hosts を管理する

ここでは、ホスト名のハッシュ化を止めた上で、 known_hosts をきちんと管理することを考えたい。前述のとおり、 known_hosts の基本フォーマットは

ホスト名またはIPアドレス 署名方式 公開鍵 (コメント)

である。また、 # で始まる行もコメントである。

「ホスト名またはIPアドレス」の部分の詳細なフォーマットは以下の通り。

  • 基本的には、 gitlab.com とか 52.167.219.168 のようにホスト名またはIPアドレスがそのまま使える。
  • 22番以外のポートのときは、 [example.com]:60022 とか [192.0.2.23]:60022 のように[]づけで表記する。
  • , で複数のホスト名またはIPアドレスを並べることができる。ホスト名とIPアドレスを並べてもよい。 (ハッシュ化していないときのみ)
  • * は0文字以上のワイルドカードとして使える。 (ハッシュ化していないときのみ)
  • ? は1文字のワイルドカードとして使える。 (ハッシュ化していないときのみ)

例えば、GitHubとGitLabのための known_hosts は以下のように書ける。

# GitHub -- marked CheckHostIP no
github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==

# GitLab
gitlab.com,52.167.219.168 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFSMqzJeV9rUzU4kWitGjeR4PWSa29SPqJ1fVkhtj3Hw9xjLVXVYrU9QlYWrOLXBpQ6KWjbjTDTdDkoohFzgbEY=
gitlab.com,52.167.219.168 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCsj2bNKTBSpIYDEGk9KxsGh3mySTRgMtXL583qmBpzeQ+jqCMRgBqB98u3z++J1sKlXHWfM9dyhSevkMwSbhoR8XIq/U0tCNyokEi/ueaBMCvbcTHhO7FcwzY92WK4Yt0aGROY5qX2UKSeOvuP4D6TPqKF1onrSzH9bx9XUf2lEdWT/ia1NEKjunUqu1xOB/StKDHMoX4/OKyIzuS0q/T1zOATthvasJFoPrAjkohTyaDUz2LN5JoH839hViyEG82yB+MjcFV5MU3N1l1QL3cVUCh93xSaua1N85qivl+siMkPGbO5xR/En4iEY6K2XPASUEMaieWVNTRCtJ4S8H+9
gitlab.com,52.167.219.168 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAfuCHKVTjquxvt6CM6tdG4SLp1Btn/nOeHHE5UOzRdf

CheckHostIP をオフにする

ホスト鍵は基本的に、接続しようとしたDNS名と照合される。しかし、 CheckHostIP が有効の場合、IPアドレスに対しても照合が行われる。

普通、DNSの対応付けが変わっていても、接続先のサーバーが想定通りのホスト鍵を返せば信頼してもよいはずなので、 CheckHostIP は予防的な意味合いが強いと思われる。特に、GitHubのようにDNSラウンドロビンを使っている場合は面倒なので、上記のワイルドカードを使うか、そもそも CheckHostIP を外してしまうのがよいと思う。 (GitHubのIPアドレス一覧)

StrictHostKeyChecking の有効化

想定していないホスト鍵が送られてきたとき、SSHは以下のように振る舞う。

  • 別のホスト鍵を既に知っている場合、問答無用で接続拒否になる。オレオレ詐欺で言うところの「母ちゃん俺だよ、携帯番号変えたからさ」というやつである。どうしても接続するならknown_hostsを手動かコマンドでいじる必要がある。
  • 未知のホストの場合
    • StrictHostKeyChecking yes の場合: 接続拒否する。
    • StrictHostKeyChecking no の場合: known_hostsに追加して続行する。
    • StrictHostKeyChecking ask の場合(デフォルト): ユーザーの答えに応じて上のどちらかの処理をする。

.ssh/config に入っているホストにしか接続しないような生活であれば、どれもそれほど変わらないと思う。筆者は StrictHostKeyChecking yes にしておき、新規ホストを追加したとき、そのホストに対して一時的に StrictHostKeyChecking ask を付与している。

EC2への対処

EC2のように、同じIPアドレスのマシンを消したりまた立ち上げたりしていると、既知のホスト鍵との衝突でどうしても接続拒否になってしまう。せっかく known_hosts をちゃんと管理しているので、テキストエディタで消してしまうとよいと思うが、ホスト鍵のチェックを強制的に省略する方法もあるにはある

UpdateHostKeys の有効化

UpdateHostKeys ask とすると、ホスト鍵が更新されたときに、新しいホスト鍵を known_hosts に追加することができる。(以前のホスト鍵で認証したあとで送られてくるので、問題はない。) サーバー側をうまく設定すれば、キーローテーションをすることもできる。

まとめと宣伝

known_hosts はちゃんと管理することもできる。ちなみに known_hostsauthorized_keys を正しくハイライトするVimプラグインを作ったのでぜひ。