【Git】WSL2環境でGitを使う場合のGitHub SSHキー設定（SSHキーを複数登録する場合も併せて）

## 概要
WSL2環境でGit操作を行うために必要な、SSH キーの生成と設定方法を記載します。この手順を完了すると、WSL2からGitHubリポジトリに対してclone、push、pullなどの操作が可能になります。

また、Remote Developmentを使う場合に、コンテナ内からGitHubリポジトリにアクセスするために必要な、SSH Agentの設定も記載します。

特別な事情が無い限り、SSHキーの生成は1台のPCにつき1度でよいです。WSL2のディストリビューションが1PC中に複数ある場合でも、使いまわしが効くはず。

ただし、複数のGitHubアカウントへ1台のPCでアクセスしたい場合は、SSHキーを複数登録する場合がありますね。適宜、登録するSSHキーが同一WSL内で2つ目以降である場合の調整事項も記載しておきます。

| 項目 | 内容 |
| ---- | ---- |
| **取り扱う内容** | • WSL2環境でのGitHub SSH接続設定<br>• Remote Development環境でのSSH Agent設定（keychainを使う）<br>• （既にSSHキーをマウントする設定を書いている）docker-compose.ymlでの認証設定の調整<br>• 同一WSL内で2つ目以降のSSHキー登録する場合の手順 |
| **想定読者** | • WSL2でGit操作を行う開発者<br>• Remote Developmentを使用してコンテナ内で開発を行う人<br>• GitHubリポジトリへのSSH接続設定が必要な人<br>• SSHキーをコンテナへマウントしているのをやめたい人 |
| **ゴール** | • WSL2からGitHubリポジトリへのアクセスを実現<br>• コンテナ内からのGit操作を行えるようにする<br>• 同一WSL2内から複数のGitHubアカウントへGitアクセスできるようになる |

注意点：
SSHキーをバインドマウントする設定が既にある場合、SSH Agent設定と競合しエラーの原因となる可能性があります。プロジェクトの認証方式を変更する際は、必ずプロジェクトリーダーに確認してください。

## 手順
### 新しくSSHキーを生成する
GitHubに登録するSSHキーを生成します。
```bash
# -t 鍵のタイプとしてRSAアルゴリズムを指定、メールアドレスはGithubに紐付く自分のものを。
# -b 鍵のビット長　現代的な水準に対して十分
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

```
キー生成を行うと、ファイル名を聞かれます。複数のSSHキーを同一端末で扱う場合など、変更してもよいです。変更不要ならそのままEnterキーを押して操作を進めましょう。
```bash
Generating public/private rsa key pair.
Enter file in which to save the key (/home/your_username/.ssh/id_rsa):
# ファイル名を変更する際は、以下のようにフルパスで入力する　ファイル名だけを入力するとカレントに作成される
# Enter file in which to save the key (/home/ubuntu/.ssh/id_rsa): /home/ubuntu/.ssh/id_rsa_private
```

パスフレーズの入力を求められます。これは、SSH認証を通して操作を行う場合に聞かれるパスワードのこと。設定不要ならそのままEnterキーを押します。（セキュリティの観点では、なるべく設定しておくべき）
```bash
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
```

キーの生成完了
```bash
Your identification has been saved in /home/your_username/.ssh/id_rsa.
Your public key has been saved in /home/your_username/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:...
The key's randomart image is:
+---[RSA 4096]----+
| .o=+..          |
|  ..o.o.         |
|     Eo .        |
+----[SHA256]-----+

```
### SSHキーをGitHubに登録する
[GitHubのSSHキー設定ページ](https://github.com/settings/keys)にて、SSH公開鍵（`id_rsa.pub`の内容）を登録します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3906608/06b7d850-aa56-4e23-6aa3-10eef13cd146.png)

SSHキー生成操作によって作られた`id_rsa.pub`の内容。`.ssh/`のディレクトリにあるはず。
```text
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC3N8h8K3NQhJlkj3... user@hostname
```

キー追加画面のKey欄に上記をコピペ・登録して、対応付けられたSSHキーがGitHubのリストに上がればOK。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3906608/f5a78f53-4bcb-4f3b-a4f0-cc015226294b.png)

### （同一WSL2内で2つ目以降のSSHキー登録である場合）configファイルを編集する
SSHキーを同一WSL内で複数作成・登録した場合、`.ssh/config`に書き込まれている設定情報を確認します。
```config
Host github.com
  HostName github.com
  User git
  Port 22
  IdentityFile ~/.ssh/id_rsa
  TCPKeepAlive yes
  IdentitiesOnly yes
Host github.com.private ←ここの情報が無い or 食い違っていたら修正する
  HostName github.com
  User git
  Port 22
  IdentityFile ~/.ssh/id_rsa_private ←作成したSSHキー名称と合致しているか確認する　違っていたら書き換える
  TCPKeepAlive yes
  IdentitiesOnly yes
```

### GitHub接続確認
以下コマンドでGitHubに接続が通ることを確認します。
```bash
ssh -T git@github.com
# 今回のSSHキーに対応する`.ssh/config`のHostを書くこと
# ssh -T git@github.com.private
```
以下メッセージには`yes`を入力します。
> The authenticity of host 'github.com (IP ADDRESS)' can't be established.
ECDSA key fingerprint is SHA256:p2QAM------.
Are you sure you want to continue connecting (yes/no/[fingerprint])?

以下のようなメッセージが表示されればOK。
> Hi username! You've successfully authenticated, but GitHub does not provide shell access.

### git cloneする
ここまでの手順で、WSL2のターミナルから直接git cloneすれば処理が通ってリポジトリの内容が落ちてくるはず。
```bash
git clone git@github.com:your-account/sample.git
# 今回のSSHキーに対応する`.ssh/config`のHostに変えること
git clone git@github.com.private:your-account/sample.git
```

### SSH Agent設定（Remote Developmentのコンテナ内でGit操作を可能にする）
WSL2から直接Git操作を行う場合はここまでの設定で問題ないけれど、Remote Developmentを用いて起動したDockerコンテナ内からGit操作を行うためには、SSH AgentにSSHキーを登録する必要があります。

#### 前提：SSH Agent（SSHキーをメモリに保持し、認証情報をキャッシュする）を用いる利点
* SSH AgentがRemote Development経由で起動したコンテナ内にSSHキーを持ち込んで、コンテナ内からのGit操作を可能にする
* SSH Agentはメモリ内でキーを管理するため、ディスク上に平文で保存されるリスクを減らす
* WSL2のセッション（= ターミナル）が生きている間は、操作の度にパスフレーズの入力をする必要がない
* WSL2側の認証ファイルをdocker-compose.ymlでバインドマウントする手法でも実現可能だが、以下の通りデメリットが多い
  * 認証ファイルをコンテナ内に残したままではセキュリティ面で課題がある。SSH Agentならメモリ経由でSSHキーが共有されるので、ファイルはコンテナ内に残らない
  * ymlファイルの設定が、個々のWSL2ディレクトリ構成に依存するのは望ましくない。人によって動きが違う事態を招きかねない
  * コンテナ内で認証ファイルをうまく読めなくて権限いじり倒す操作が、WSL2ホスト側のファイルにも反映されてしまう。なにか別のリポジトリを開く度に権限エラーになって書き換える、といったことを繰り返しがち

#### 設定手順
##### keychainをインストールする
WSL2では、SSH Agentのセッション・登録情報が新しいターミナルやコンテナ内で持続しないため、別途対策が必要。SSH Agentセッションを持続させるために、`keychain`をインストールします。
```bash
sudo apt-get install keychain
```
このとき、`ubuntu is not in the sudoers file. This incident will be reported.`表示されて、sudo権限が無い場合は、以下記事の手順を実行してsudo権限を得ましょう。
[【WSL2】Ubuntuディストリビューションのユーザーがsudoできるようにする](https://qiita.com/Shimooo_mutenka/items/cf7eaa79d5cb2562db05)

また、`Package keychain is not available, but is referred to by another package.`が表示される場合、パッケージの入手先リポジトリの参照設定が古い可能性があります。`sudo apt update`と`sudo apt upgrade`を実行、WSL2を再起動して改めて試しましょう。

##### 新しいターミナルを開いた際にkeychainを実行するよう設定する
`~/.bashrc`に、以下を追記します。、以後はSSH Agentが維持され、Remote Developmentを用いてコンテナにアタッチした際にもSSHキーを共有、Git操作が可能になります。
```bash
/usr/bin/keychain -q --nogui $HOME/.ssh/id_rsa # id_rsaのファイル名は生成した秘密鍵名に合わせて調整すること
source $HOME/.keychain/$(hostname)-sh
```
新しいターミナルを開く度にパスフレーズ入力が要求されるようになるが、これはSSH Agent起動時の挙動として正しい動作です。

##### PCスリープから復帰時の面倒なこと
端末や環境によるのかもしれないですが、`Reopen in Container`したままPCをスリープ→復帰すると、その後のgit操作が以下エラーによって通らなくなることがありました。再度`Reopen in Container`すると解消します。コンテナの再ビルドまでは不要で、入り直しさえすればOK。
```bash
# 上記該当時のエラー
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.
```

#### 既にdocker-compose.ymlで認証ファイルをバインドマウントしているリポジトリの調整
以下のように、バインドマウントでDockerコンテナへ認証ファイルをバインドマウントしているコンテナがある場合、SSH Agentが起動している状態でReopen in Containerするとエラーになってコンテナが起動しません。バインドマウント設定を削除することで、該当リポジトリのコードをバインドマウント→SSH Agent利用するよう変更できます。（リポジトリ全体の環境に関わる変更のため、勝手にやらずに、プロジェクトリーダーの指示を仰いでください）

1. `docker-compose.yml`の修正
WSL2側でマウント部分をコメントアウトしてからReopen in Containerする。

    <span class="label label-blue">WSL2側の操作</span>
    `docker-compose.yml`（多くの場合、`.devcontainer`のymlファイル）
    ```yml
    version: '3'
    services:
      angular:
        volumes:
          - ..:/workspaces:cached
          - ~/.gitconfig:/root/.gitconfig # SSH関連のバインドマウント。これをコメントアウトする
          - ~/.ssh:/root/.ssh # SSH関連のバインドマウント。これをコメントアウトする
    ```

2. コンテナ内でgit操作を行う
Reopen in Containerした後、コンテナ内からgit pull/pushが動作することを確認します。

3. （リモートリポジトリ側でymlの変更があった場合）コンテナ内で`docker-compose.yml`の変更を取り消す
リモートリポジトリ側で`docker-compose.yml`に変更が加えられていた場合、1で変更したymlの変更差分にてコンフリクトが起こります。一旦コンテナ内でymlのバインドマウント削除を取り消してgit pull or git mergeし、リモートの変更内容をローカルに反映させてから、改めてバインドマウント部分の削除とcommit/pullを行います。