SSH接続のやり方をめちゃくちゃ詳しく解説する for Mac

# はじめに
この記事には以下の内容が含まれています。
- SSH接続の方法
- `ssh-agent`を使用したパスフレーズ入力の省略方法
- Configファイル（`~/.ssh/config`）の説明と書き方
- SSH接続に使える便利なVSCodeの拡張機能を使った接続方法
- GitHub / BitbucketにSSH接続するための追加設定（参照のみ）

SSHの仕組みについては以下の記事が参考になります。

https://qiita.com/angel_p_57/items/2e3f3f8661de32a0d432

# SSH接続の方法
GitHubにSSH接続したいだけの人もこの項目をやってから次の「GitHubにSSH接続するための追加設定」に移ってください。

## 1. 公開鍵と秘密鍵の生成
### RSA方式はもう古い
RSAは歴史のある公開鍵暗号方式なのでいまだに広く使われていますが、徐々によりセキュアなEdDSAが普及してきています。
> Mody氏は最終的に、実装や互換性を重視する場合は公開鍵の鍵長が2048bitもしくは4096bitのRSAを、パフォーマンスやセキュリティを重視する場合はEdDSAを推奨。DSAやECDSAは使わないように呼びかけています。 - [SSHの公開鍵暗号には「RSA」「DSA」「ECDSA」「EdDSA」のどれを使えばよいのか？](https://gigazine.net/news/20200828-ssh-encryption-algorithm/)

以下のように[GitHubの公式ドキュメント](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent#generating-a-new-ssh-key)では、RSAではなく、**Ed25519**（EdDSAの一種）を使うように推奨されています。
> Paste the text below, substituting in your GitHub email address.
> $ ssh-keygen -t ed25519 -C "your_email@example.com"
> 
> Note: If you are using a legacy system that doesn't support the Ed25519 algorithm, use:
> $ ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

ちなみにEd448というEd25519より暗号強度が高いEdDSAもあるようです[^1]。
[^1]: [ed25519のすすめ](https://dnsops.jp/event/20180627/20180627-ed25519.pdf)

:::note info
ただEd25519はサポートされていない場合もあるので、ここではEd25519とRSAの両方の鍵生成方法を載せておきます。
:::

### 1-1. 鍵ペア生成コマンドを実行
`-C`は鍵にコメントをつけるものなので、必要なければつけなくてもいいが、GitHubにSSH接続するための鍵ペアを生成する場合はGitHubに登録しているメアドを指定した方がいいようです（[GitHubの公式ドキュメント](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent#generating-a-new-ssh-key)では、このメアドをラベルとして、新しいSSH鍵を作成すると言ってます。GitHub側でユーザーによって登録された公開鍵とは別の鍵ペアを作成しているということなんですかね？）。

#### Ed25519（推奨）
```zsh
% ssh-keygen -t ed25519 -C "your_email@example.com"  # GitHubに接続するわけでなければ「-C "your_email@example.com"」は指定しなくていい
```

#### RSA（非推奨）
Ed25519が使えずRSAを使用する場合は、前述のとおり、4096bitの鍵長を指定しましょう。
```zsh
% ssh-keygen -t rsa -b 4096 -C "your_email@example.com"  # GitHubに接続するわけでなければ「-C "your_email@example.com"」は指定しなくていい
```

### 1-2. 鍵の保存場所を指定
以下のように入力を求められるが、基本的には`~/.ssh`下に入れておく形で大丈夫（もちろんどこに置いてもパス指定さえできれば使えるが、一箇所に集約した方が管理しやすいのと、慣習的に`~/.ssh`下に入れることが多いためにそれを前提にしたサービスなどがたまにあるので）。

ただ、鍵の名前はわかりやすい方がいいので`id_rsa`や`id_ed25519`のままにしておくのは避けた方がいい。わかりやすい名前をつけとこう（ここで指定するのは秘密鍵のファイル名。公開鍵のファイル名は秘密鍵のファイル名に`.pub`がついたものになる）。
```zsh
> Enter file in which to save the key (/Users/yourname/.ssh/id_ed25519): /Users/yourname/.ssh/<わかりやすい名前>
```

### 1-3. パスフレーズを入力
最後にパスフレーズの入力を求められる。パスフレーズは省略されがちだが、セキュリティ上、入力することが推奨される。
```zsh
> Enter passphrase (empty for no passphrase): 
> Enter same passphrase again: 
```

[GitHubの公式ドキュメント](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/working-with-ssh-key-passphrases)でも言及されているように、パスフレーズを指定しなかった場合、もしも秘密鍵が流出した場合に、そのまま秘密鍵を使用してSSH接続されてしまう。パスフレーズを指定しておけば、たとえ秘密鍵が流出したとしても、SSH接続する際にパスフレーズの入力を求められるので、より安全になる。

ただ、秘密鍵ファイルが流出したということは、攻撃者は自身のローカルでじっくりとパスフレーズを調べられるということなので、8文字とかそういった短いパスフレーズだと簡単に突破されてしまう。できれば300文字以上のパスフレーズが欲しいところである。今の時代はパスワードマネージャーもあるし、後述するssh-agentを使用すれば毎回パスフレーズを入力しなくても済むようになるのでそれぐらい大仰にやったところでそこまで運用コストが増えるものでもないだろう。パスワードはパスワードマネージャー等で生成できる。

パスフレーズを入力し終わったら以下のメッセージが表示されて、鍵ペアの生成が終了する。
```zshsh
> Your identification has been saved in /Users/yourname/.ssh/<ファイル名>
Your public key has been saved in /Users/yourname/.ssh/<ファイル名>.pub
The key fingerprint is:
SHA256:<ランダムな英数字> rihitobannai@Rihitos-MacBook-Pro.local
The key's randomart image is:
+--[ED25519 256]--+
|       ......... |
|  ........       |
|       ......... |
| ..............  |
|        ......   |
|            ...  |
|       ......... |
|      ....       |
|   ......        |
+----[SHA256]-----+
```
鍵ペアを保管場所を表示して生成した鍵ペアが存在していればOKだ。
```zsh
% ls ~/.ssh
> <生成したファイル名>		<生成したファイル名>.pub
```

#### パスフレーズの変更
もしもパスフレーズを入力し忘れたり、パスフレーズを変更したい場合は、以下のコマンドで新しいパスフレーズを指定できる。[^2]
[^2]: [Adding or changing a passphrase - GitHub Docs](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/working-with-ssh-key-passphrases#adding-or-changing-a-passphrase)
```zsh
% ssh-keygen -p -f ~/.ssh/<秘密鍵ファイル名>
> Enter old passphrase: [Type old passphrase]
> Key has comment 'your_email@example.com'
> Enter new passphrase (empty for no passphrase): [Type new passphrase]
> Enter same passphrase again: [Repeat the new passphrase]
> Your identification has been saved with the new passphrase.
```

#### SSH接続時にパスフレーズの入力を省略したいとき
キーチェーンにパスフレーズを保存すれば、初回以降はSSH接続時にパスフレーズの入力を求められなくなる。OSの種類に応じて以下の２通りの方法がある。

Mac OSの調べ方と、Mac OSの順番に関しては以下を参照。

https://support.apple.com/ja-jp/HT201260

##### Mac OS X LeopardからOS X El Capitanの場合
Mac OS X LeopardからOS X El Capitanでは、`.ssh/id_rsa`や`.ssh/identity`という名前の秘密鍵ファイルの場合、初回のSSH接続時にパスフレーズの入力とともに、パスフレーズをキーチェーンに保存することを選択すればいい。[^3]
[^3]: [Saving your passphrase in the keychain - GitHub Docs](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/working-with-ssh-key-passphrases#saving-your-passphrase-in-the-keychain)

##### それ以外のMac OSの場合
そうでない場合は、以下の手順で鍵を`ssh-agent`に追加することで、パスフレーズをキーチェーンに保存することができる。[^4]

1. バックグラウンドで`ssh-agent`のプロセスをスタートさせる
    ```zsh
    % eval "$(ssh-agent -s)"
    > Agent pid 59566
    ```
    または環境によっては上記のコマンドの代わりに以下のどれかを実行する必要があるかもしれない。
    
    ```zsh
    % sudo -s -H 
    % eval "$(ssh-agent -s)"
    ```
    ```zsh
    % exec ssh-agent bash
    ```
    ```zsh
    % exec ssh-agent zsh
    ```

1. macOS Sierra 10.12.2かそれ以降のMac OSを使用している場合は、Configファイルを修正して、鍵を自動的に`ssh-agent`にロードし、パスフレーズをキーチェーンに保存するようにする

    まずはConfigファイルを開く。VSCodeやVim等で開いても問題ない。
    ```zsh
    % open ~/.ssh/config  # Configファイルが存在していれば、編集可能な状態で開かれる
    # もし`> The file /Users/YOU/.ssh/config does not exist.`と表示されたら、
    # Configファイルが存在していないので、
    # `% touch ~/.ssh/config`を実行し、再度`% open ~/.ssh/config`を実行
    ```
    
    そしてConfigファイルに以下を追記して保存。もしパスフレーズを使用していない場合は「UseKeychain yes」の行を消す。
    ```plaintext
    Host <任意の名前(自由に付けていい。自分が識別するためのものなので)>
        AddKeysToAgent yes
        UseKeychain yes
        IdentityFile ~/.ssh/<生成した秘密鍵ファイル名>
    ```

1. 秘密鍵を`ssh-agent`に追加し、パスフレーズをキーチェーンに保存する
    ```zsh
    % ssh-add --apple-use-keychain ~/.ssh/<生成した秘密鍵ファイル名>
    > Enter passphrase for /Users/rihitobannai/.ssh/<生成した秘密鍵ファイル名>: 
    > Identity added: /Users/yourname/.ssh/<生成した秘密鍵ファイル名> (rihitobannai@Rihitos-MacBook-Pro.local)
    ```

[^4]: [Adding your SSH key to the ssh-agent - GitHub Docs](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent#adding-your-ssh-key-to-the-ssh-agent)

## 2. 生成した鍵ペアを用いてSSH接続する
### 2-1. 秘密鍵ファイルのパーミッションを確認する
秘密鍵ファイルに作業者以外のパーミッションがついていると接続時に`Permission denied`というエラーが出てしまうため、パーミッションが正しく設定できているか確認する。`600`が表示されたら大丈夫である。今までの方法で作成していればおそらく問題ないはず。
```zsh
% stat -f %A ~/.ssh/<秘密鍵ファイル名>
> 600
```
もし、`600`以外が表示された場合は以下を実行する。

```zsh
% chmod 600 ~/.ssh/<秘密鍵ファイル名>
```

パーミッションの数字について知りたい人は以下の記事が参考になります。

https://kazmax.zpp.jp/linux_beginner/permission_numerical.html

### 2-2. 生成した公開鍵を接続先に保存する
#### 直接SSH接続先にアクセスして保存する場合
接続先にアクセスし、`~/.ssh/authorized_keys`の末尾に、生成した公開鍵を追記して保存。
```bash
$ vi ~/.ssh/authorized_keys
# 末尾に、生成した公開鍵を追記して保存
```

#### `ssh-copy-id`を使う場合
下記のコマンドを実行することで、SSH接続元から接続先に公開鍵を保存することができます。
```zsh
% ssh-copy-id -i ~/.ssh/<生成した公開鍵ファイル名> <接続先のユーザー名>@<接続先のIPアドレス>
```
ただし、この方法が使えるのは、パスワード認証（公開鍵認証以外の認証手段）が使える場合に限ります。そりゃそうですよね、知らない人に勝手に自分のマシンに公開鍵を送り込まれたら接続されちゃいますからね。AWS EC2などはデフォルトでパスワード認証は使えないようになってます。

ちなみにセキュリティの観点からパスワード認証は無効にしておきましょう。公開鍵認証は自動生成されるので高い強度を保証できますが、パスワードはユーザーが作成するので保証できる強度の程度が低いためです。わざわざ懸念を増やす必要もないですからパスワード認証は無効化しとくのが吉です。無効化方法については後述します。

### 2-3. パスワード認証を禁止する
1. 接続先で以下の`/etc/ssh/sshd_config`ファイルに書かれている`PasswordAuthentication yes`を`PasswordAuthentication no`に変更して保存
    ```bash
    $ vi /etc/ssh/sshd_config
    # `PasswordAuthentication yes`部分を`PasswordAuthentication no`に変更して保存
    ```
1. 編集後の`/etc/ssh/sshd_config`ファイルの構文をチェック
    ```bash
    $ sshd -t  # 正常なら何も表示されないが、異常があるとエラー文が表示される
    ```
1. sshdを再起動してSSHの設定の変更を反映させる
    正直OSによってコマンドが違うので調べてもらうのが確実だが、一応Ubuntuの場合のコマンドを載せておく。
    ```bash
    $ sudo /etc/init.d/ssh restart
    ```
    他のOSは以下の記事が参考になるかもしれません（試してないので保証はしませんが）。
    
https://orebibou.com/ja/home/201410/20141031_001/

### 2-4. 接続元IPアドレスを制限する
接続元のIPアドレスを制限できるんだったら制限しときましょう。よくあるユーザー名を使って22番ポートを無差別攻撃してくる輩がいるようです。ログは簡単に見ることができますので興味がある人は以下の記事とか参考になるんじゃないでしょうか。

https://helpcenter.gmocloud.com/altus/s/article/ch-4270

IPアドレス制限のやり方は以下の記事が参考になります。

https://blog.katsubemakito.net/linux/sshd-ipaddress-check

ちなみにグローバルIPアドレスは以下のコマンドで表示できます。間違ってプライベートIPアドレス（`192.168`で始まるやつとか）を指定しないようにしてくださいね。

```zsh
% curl ipecho.net/plain; echo
```

### 2-5. 接続先のユーザー名を変更する
デフォルトのユーザー名は絶対に変更してください。`ubuntu`とかそういうよくあるユーザー名を使って攻撃してくるので。変更方法は自分で調べてみてください。

### 2-6. ポート番号を変更する
できるならポート番号も22番から変更しときましょう。以下の記事が参考になります。

https://iwazawa-ryo0829.hatenablog.com/entry/2021/05/13/172453

### 2-7. その他のセキュリティ対策
あとはrootユーザーのパスワードをちゃんと設定しておくとか、接続する際はちゃんと権限を制限したユーザーを使用するようにしてrootユーザーを使わないようにするとか。万一、総当たり攻撃などで突破されてSSH接続されてしまったとしても権限を制限したユーザーで接続されるようになっていれば被害を小さくできますから。

他にも色々あると思うんですけど、この記事はあくまでSSH接続についての記事なので、ここらでやめておきます。

### 2-8. SSH接続する
#### ターミナルからそのまま接続する場合
```zsh
$ ssh -i ~/.ssh/<生成した秘密鍵ファイル名> <接続先のユーザー名>@<接続先のIPアドレス>
```

#### Configファイルに接続情報を記述して接続する場合
何回もSSH接続する場合、毎回秘密鍵ファイルのパスやユーザ名、IPアドレスを入力するのは面倒です。なのでConfigファイルにそれらの情報を前もって記述しておくことでもっと簡単にSSH接続することができるようになります。

1. まずはConfigファイルを開く。VSCodeやVim等で開いても問題ない。
    ```zsh
    % open ~/.ssh/config  # Configファイルが存在していれば、編集可能な状態で開かれる
    # もし`> The file /Users/YOU/.ssh/config does not exist.`と表示されたら、
    # Configファイルが存在していないので、
    # `% touch ~/.ssh/config`を実行し、再度`% open ~/.ssh/config`を実行
    ```
    
2. そしてConfigファイルに以下を追記して保存
    - `ssh-agent`の項目で追加した`AddKeysToAgent yes`や`UseKeychain yes`がある場合はその行は残しつつ、`HostName`や`User`を追記する。
    - ちなみにAWS EC2 Linuxインスタンスのデフォルトのユーザーネームは[ここ](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/managing-users.html)を参照

    ```plaintext
    Host <任意の名前(自由に付けていい。自分が識別するためのものなので)>
        HostName <接続先のIPアドレスまたはドメイン名>
        User <接続先のユーザー名>
        IdentityFile ~/.ssh/<生成した秘密鍵ファイル名>
    ```

3. SSH接続する
    ターミナルを使う方法とVSCodeの拡張機能を使う方法の２通りを説明します。
    ##### ターミナルを使う場合
    ```zsh
    % ssh <Configファイルの`Host`に指定した名前>
    ```
    
    ##### VSCodeの拡張機能を用いて接続する場合
    VSCodeは既にインストール済みという前提で話を進めます。「Remote Development」というVSCodeの拡張機能をインストールしてください。そして左側に表示されるメニューバーから「リモートエクスプローラー」のアイコンをクリックすると、Configファイルに記述した`Host`名が表示されていると思うのでそれをクリックすれば接続できます。

# GitHub / BitbucketにSSH接続するための追加設定
## GitHub
公式ドキュメントに書いてあるので、この通りにやってください。

https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account

## Bitbucket

https://support.atlassian.com/bitbucket-cloud/docs/set-up-an-ssh-key/

1. Bitbucketの右上のユーザーアイコンをクリックして、「Personal settings」をクリックします。
1. 左側のメニューバーにある「SSH鍵」をクリックします。
1. 「鍵を追加」をクリックして、「Label」には任意のラベル名を入力し、「Key」にはコピーした公開鍵の値を貼り付け、「鍵を追加」をクリックします。
1. ターミナルで以下のコマンドを実行して以下のような結果が返ってきたら成功です。
    ```zsh
    % ssh -T git@bitbucket.org
    The authenticity of host 'bitbucket.org (243:da00f5)' can't be established.
    RSA key fingerprint is SHA256:zzXQOXG1A.
    This key is not known by any other names
    Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
    Warning: Permanently added 'bitbucket.org' (RSA) to the list of known hosts.
    authenticated via ssh key.
    
    You can use git to connect to Bitbucket. Shell access is disabled
    ```
1. 左側のメニューバーにある「アプリ　パスワード」をクリックします。
1. 「アプリ　パスワードの作成」をクリックします。
1. 「Label」に任意のアプリパスワードの名前を入力し、必要な権限にチェックを入れます。
    - リポジトリをpushしたりcloneするのに必要な最低限の権限だけ与えたい場合は「リポジトリ」の「読み取り」と「書き込み」にだけチェックを入れればOKです。
1. 「作成」をクリックし、表示されたパスワードをパスワードマネージャー等に保存します。この時だけしかパスワードを見ることはできないのでしっかりとメモしておきましょう。
    - Bitbucketにpushする際にパスワードの入力を求められると思いますので、このパスワードを入力すればOKです。