GitHubからfetch/pullできなくなった場合の対処（2023/03/24秘密鍵公開）

日本時間で2023/03/24 14時ごろ、GitHubは、彼らのSSHホスト鍵ペアを更新しました[^github_hostkey_replacement]。SSHホスト鍵ペアの秘密鍵が一時的に公開されていたことが判明したためです。

[^github_hostkey_replacement]: [We updated our RSA SSH host key (github.blog)](https://github.blog/2023-03-23-we-updated-our-rsa-ssh-host-key/)

SSHホスト鍵ペアの更新はユーザのGitHubからSSHを使ってクローンしたリポジトリにおけるgit操作に影響を与えます。具体的には `git fetch` や `git pull`, `git push` 実行時に次のようなエラーが表示され、実行できなくなる場合があります。

```
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the RSA key sent by the remote host is
SHA256:uNiVztksCsDhcc0u9e8BujQXVUpKZIDTMczCvj3tD2s.
Please contact your system administrator.
Add correct host key in /home/foo/.ssh/known_hosts to get rid of this message.
Offending RSA key in /home/foo/.ssh/known_hosts:3
Host key for github.com has changed and you have requested strict checking.
Host key verification failed.
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
```

## 解決方法

### 古い鍵の削除

解決するには[公式ブログ](https://github.blog/2023-03-23-we-updated-our-rsa-ssh-host-key/)にて記載があるとおり、まず

```
$ ssh-keygen -R github.com
```

を実行して古い鍵を `~/.ssh/known_hosts` から削除します。

### 新しい鍵の登録

[GitHub公式ドキュメントのフィンガープリント一覧](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints) をみると次のような記載があります

```
github.com ssh-ed25519 AAAAC3NzaC1lZDI1...
github.com ecdsa-sha2-nistp256 AAAAE2Vj...
github.com ssh-rsa AAAAB3NzaC1yc2EAAAAD...
```

これらのフィンガープリントは `~/.ssh/known_hosts` にそのまま貼り付けられるフォーマットになっていますので、手元のエディタで `~/.ssh/known_hosts` を開いてこれらのフィンガープリントをコピペして保存すると登録が完了します。

コピー元は必ずGitHubの公式ドキュメントを参照するようにしてください。（上記のリンクが間違っているかもしれないことにも注意）


`HashKnownHosts` オプションを利用している場合は、保存後に `ssh-keygen -H` を実行すると今回貼り付けたエントリの接続先情報をハッシュ化できます。known_hosts ファイルの全エントリをハッシュ化するので注意してください。

### 新しい鍵の登録（別案）

何らかの事情で上記の方法での登録が難しい場合、古い鍵の削除後に以下の方法で新しい鍵を登録できます。

エラーが出ているリポジトリで `git fetch` を実行します。設定にもよりますがおそらく次のような確認ダイアログが表示されるので:

```
$ git fetch
The authenticity of host 'github.com (20.27.177.113)' can't be established.
ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])?
```

[GitHub公式ドキュメントのフィンガープリント一覧](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/githubs-ssh-key-fingerprints) から、表示されている鍵の種別(上記の例では `ED25519` )と一致する鍵のフィンガープリントをコピーしてダイアログにペーストし、リターンキーを押します。

:::note alert
公式ドキュメントの情報を使ったフィンガープリントの確認は必ず実施しましょう。特に今回は秘密鍵漏洩の可能性があるため、　**フィンガープリントの確認は絶対に省略してはいけません** 。
:::

```
Are you sure you want to continue connecting (yes/no/[fingerprint])? SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU
Warning: Permanently added 'github.com' (ED25519) to the list of known hosts.
```

すると上記のように手元の `~/.ssh/known_hosts` に新しい鍵が登録され、以後これまで通り `git fetch` 等が可能になります。

その他、自動化環境で便利な方法などは [公式ブログの記述(github.blog)](https://github.blog/2023-03-23-we-updated-our-rsa-ssh-host-key/) をご覧ください。

## 流布されている誤り等

本件に関して次のような対処法の説明を見かけることがありますが、これらは前述の操作に加えて実施しても良い操作であって、本件の解決には直接は関係ない操作です。多くの環境では害のある操作ではありませんが、これらを実施しても本件の症状は解決しません。

* 手元のSSH鍵ペアを新規作成する
* ECDSA または Ed25519 のSSHキーを再発行する

参考にする場合はこれらの操作が結局何をやっているのかを正しく理解したうえで実施しましょう。

また、次のような勘違いも多いようですのでご注意ください。

* 私は RSA の鍵ペアを使っているので　ECDSA または Ed25519 の鍵ペアに変更しなければならない
* 私は ECDSA の鍵ペアを使っているから問題ない
* 私は Ed25519 の鍵ペアを使っているから問題ない

今回の問題はあなたの鍵ペアの話ではなく、 GitHub のサーバサイドが所持しているホスト鍵ペアの話です。上記のような勘違いをしていた場合は、ぜひ次の解説をご覧ください。後半の Q&A の気になる項目だけ確認して、興味が湧いたら解説を読む、でも良いと思います。

:::note info
あなたの鍵ペアは `~/.ssh/id_rsa` や `~/.ssh/id_ed25519` 等に格納されていますが、 GitHub のホスト鍵ペアの公開鍵の情報は github.comへの接続経験があれば `~/.ssh/known_hosts` に格納されています。
:::



## 解説

本件を正確に理解するには、SSHのサーバ認証について理解する必要があります。

SSHは公開鍵暗号等の暗号技術を利用することによって、信頼できない通信経路の上に、安全な通信経路とリモートログインを提供していますが、中間者攻撃に対する防御は一部ユーザに委ねられています。

それがSSHがサーバに初めて接続するときに表示される確認ダイアログです。

```
The authenticity of host 'github.com (20.27.177.113)' can't be established.
ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])?
```

これはある意味
「あなたが今接続しようとしているサーバは、本当にそのサーバですか？」
と問われているわけです。このように接続先のサーバの真正性を確認する行為のことをサーバ認証と呼びます。

SSHにおけるサーバ認証について、もう少し詳しく見ていきます。
SSHクライアントがSSHサーバに接続したとき、SSHサーバは自身のホスト鍵ペアの公開鍵をクライアントに提示します。このときクライアント側は受領した公開鍵のフィンガープリントと、手元の `known_hosts` ファイルに記録されているフィンガープリントとを比較することで、「今接続しようとしているサーバが、過去に接続した当該サーバかどうか」を確認しようとします。もしフィンガープリントが異なっていれば、誰かが中間者攻撃をしかけているのかもしれませんので、本記事冒頭に示したようなエラーになるわけです。

しかし、初めて接続するサーバにおいては比較対象がありません。そこでSSHクライアントはユーザに対してサーバが提示してきたホスト鍵のフィンガープリントとともにダイアログを表示し、本当に接続してもよいかを確認しようとします。

ここでユーザが行うべきことは、提示されたフィンガープリントが本当に接続先の正当なホスト鍵のフィンガープリントなのかどうかを確認する、というものです。接続先が自分自身の管理するサーバであれば物理的にログインする/あるいはその他の信頼できるログイン方法でログインしてフィンガープリントを確認する等を行う必要がありますが、接続先がGitHubの場合はGitHubが公開しているフィンガープリントの情報で確認が可能です。

:::note info
厳密には、この行為は GitHub がフィンガープリントを公開しているWebサーバのHTTPS通信において、そのWebサーバが本当にGitHubのWebサーバかどうかを確認する必要があります。これはPKIという仕組みによってユーザからみると半自動的に実施されます。このときユーザが確認すべきなのは当該WebページのURLが本当にGitHubの提供するURLかどうかという点と、証明書について問題ないかどうかという点です(証明書に問題があればブラウザが警告を出します)。
:::

SSHを普段利用している場合、実際に中間者攻撃を受ける可能性はそれほど高くありませんし、接続先のサーバはユーザ自身あるいは所属組織等で管理しているサーバが多いでしょうから、このダイアログには条件反射でyesと答えている人も多いかもしれませんし、それで問題が起こることも稀でしょう。
しかし、このダイアログには本来はこのような意味があるということは押さえておくと良いと思います。

ここで今回GitHubで起きた事象に立ち戻って考えてみると、GitHubで発生した問題は「SSHホスト鍵ペアの秘密鍵が一時的に公開されていた」ということでした。GitHubの発表を見る限りでは実際に漏洩したわけではなさそうですが、前段落で書いたような普段使いのシチュエーションと比較すると、中間者攻撃を受けている可能性が若干高いということを意識しておくべきでしょう。つまり、ダイアログに条件反射でyesと答えるのはやめておきましょう。

なお、SSHクライアント側が手元に持っている自身の鍵ペアは、サーバ側においてクライアントの真正性を確認するクライアント認証に用いられるものであって、クライアント側で行われるサーバ認証には関係しません。従って、本件において手元のSSH鍵ペアを更新することは、直接的な問題の解決には寄与しません。

## Q&A

### Q: 手元のSSH鍵を更新しないといけないの？

A: いいえ、それを更新する必要はありません。

### Q: GitHubに登録してある私の公開鍵の情報を更新しないといけない？

A: いいえ、それはGitHub側があなたを認証（クライアント認証）するときの鍵ですので、更新する必要はありません。今回の問題はクライアント側がサーバ認証に用いるGitHub側の公開鍵の問題です。GitHub側の公開鍵に関する情報はあなたの `~/.ssh/known_hosts` に登録されています。

### Q: 私のSSH鍵ペアの種別は Ed25519 なので関係ないよね？

A: いいえ、あなたの鍵ペアはクライアント認証に用いられるものですが、今回の問題はサーバ認証に用いるGitHub側の公開鍵の問題です。GitHub側の公開鍵に関する情報はあなたの `~/.ssh/known_hosts` に登録されています。そのファイルにある github.com の鍵エントリが古いRSA鍵ペアの場合は影響を受ける可能性があります。


### Q: 私の鍵ペアは ECDSA なので関係ないよね？

A: いいえ。一つ上のQ&Aを確認してください。


### Q: 私の鍵ペアは RSA だから ECDSA などに変えないとだめ？

A: いいえ、あなたの鍵ペアはクライアント認証に用いられるものですから、今回の事象には関係ありません。よって変更も必要ありません。


### Q: そんな警告でなかったんだけど？

A: 設定次第ではサーバ認証にRSA以外のアルゴリズムを優先的に用いることがありますので、そのような環境では警告がでず、安全に利用継続できる場合があります。

サーバ認証に用いるアルゴリズムはSSHクライアント/サーバの設定によって優先度があらかじめ決められており、さらにサーバ側が所持しているホスト鍵の種別を加味したうえで、最終的には接続時にSSHサーバとのネゴシエーションによって決定されます。優先度については、最近のバージョンのOpenSSHであれば、優先度が高い方からざっくりと Ed25519 -> ECDSA -> RSA の順になっていると思います。ただし、 `known_hosts` に接続サーバの情報が格納されている場合、そちらを優先するようです[^HostKeyAlgorithms]。

[^HostKeyAlgorithms]: [ssh_config(5)のHostKeyAlgorithms](https://man7.org/linux/man-pages/man5/ssh_config.5.html) には「If hostkeys are known for the destination host then this default is modified to prefer their algorithms.」との記述があります

今回差し替えがあったのはRSAのホスト鍵なので、例えば Ed25519 などの種別の github.com のホスト鍵を `known_hosts` に格納済みの場合はエラーは発生しません。冒頭で紹介した公式ブログエントリにある「If you are using **our** ECDSA or Ed25519 keys, you will not notice any change and no action is needed.」というのは、そのようなシチュエーションのことを指しています。これはクライアント側で用意している鍵ペアが ECDSA または Ed25519 であることとは **全く異なります** 。

また、github.com のホスト鍵について ECDSA/Ed25519鍵が追加されたのは 2021-09-24 だそうです( @ttdoda さんコメントありがとうございます)。従って、それ以降に初めて github.com に接続したような環境であれば今回のエラーは発生していない可能性が高いです。一方、 github.comのSSHホスト鍵がRSA(とDSA)しかなかった頃に接続し、その後 known_hosts の github.com のエントリを更新していない（問題がおこらない限り普通はしないでしょう）みたいな環境だと fetch/pull ができなくなっているはずです。

なお、エラーが出なかったとしても秘密鍵が漏洩した可能性のある鍵ペアのフィンガープリントが `known_hosts` に依然として残っている可能性はあります。これを放置するのはあまり良いことではありませんので、可能なら古い鍵の情報は `known_hosts` から削除しておきましょう。（本記事冒頭に示している手順で一旦 github.com の鍵情報はすべて削除したうえで新規に最新の鍵情報を追加するのが作業手順としては簡単です）


### Q: 中間者攻撃って論理的に可能かもしれないけど、実際に可能なの？

A: 特定のアカウントをピンポイントで狙った攻撃は難しいと思いますが、例えばユーザがカフェなどの店舗で提供されているWiFiを利用する場合で、カフェのWiFi機器の運用者が悪意を持っていた場合などには攻撃が容易に成立することがあります。

### Q: 私の鍵ペアは Ed25519 にしておくべき？

A: クライアント認証に用いる鍵ペアのタイプを何にするべきかは、本事象とは直接関係しません。

が、一般論として回答すると、 Ed25519 は短い鍵長で、より長い鍵長の RSA 鍵と同程度の強度があると言われていますので、2023年現在においてはRSAよりもより良い選択肢であるといえます。(鍵長が短いほうが利用時のパフォーマンスが良いことが多い）

ただし、サーバサイドのサポート状況としてはRSAの方がより広く普及していますので、普段使いには Ed25519 を利用し, それをサポートしないサーバへの接続用に RSA 鍵も持っておく（あるいは必要になったら生成する）、という運用が現実的かと思います。