SSHのポートフォワーディングを学習して、SSH経由でMySQLサーバーに接続する

# 前置き
WorkbenchからSSHのみ接続許可されているMySQLサーバーに接続する事情がありましたので、SSHポートフォワーディングを利用しました。SSHポートフォワーディングは既に色々なところでご紹介されていますが、少しハマった箇所がありましたので記事にしたいと思います。なお、ポートフォワーディングの学習も兼ねてSSHコマンドとWorkbenchを分けましたが、Workbench単体で全てを設定させるより簡単な[方法](https://dev.classmethod.jp/cloud/aws/mysql-workbench-rds/)もあります。

# 構成
* クライアント

    | 名前           | バージョン          |
| ----------------- | ---------------- |
| macOS             | Mojave 10.14.5   |
| MySQL Workbench   | 8.0.15   |

* RDBサーバー

    | 名前           | バージョン          |
| ----------------- | ---------------- |
| AWS EC2           | Amazon Linux release 2 (Karoo)   |
| MySQL             | Ver 8.0.16 for Linux on x86_64 (MySQL Community Server - GPL)   |

今回はサンプル環境になるのでEC2にGlobal IP(Public IP)を割当てて、外部にSSHのポート22を解放しています。
MySQLのポートはデフォルトの3306を使用しており外部には解放してません。
実務で利用する環境では、既定のセキュリティポリシーと構成があると思います。

# 登場するRDBサーバーの指定方法
以下、全て同じRDBサーバーを表します。後述の注意点で説明することになりますので、予め記載します。

| 名前           | IP or 名前       | 備考          |
| -------------- | -------------- | ------------ |
| Global IP      | 18.179.xxx.xxx | 既にインスタンスを破棄しました。<br>今後別の利用者に割り当てられるかもしれないので一部伏せておきます。 |
| Private IP     | 10.0.0.176 | |
| ループバック       | 127.0.0.1  | もちろん、RDBサーバーから見た場合です。 |
| ホスト           | localhost  | 〃 |

# 今回接続に使用したコマンド
最終的に目的を実現したコマンドは以下のとおりです。

```bash
sudo ssh -fNL 33060:localhost:3306 -i "hoge.pem" ec2-user@18.179.xxx.xxx
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240974/bd828819-5935-6b13-da78-7e21ab83c683.png)
上記のイメージでSSHポートフォワーディングのコマンドを組み立てます。

1. クライアント側の転送対象のポートを選ぶ  
特に拘りがありませんでしたので、競合していない33060としました。
1. RDBサーバーから見た転送先を指定  
SSHで接続したRDBサーバー(18.179.xxx.xxx)にMySQLが稼働しています。RDBサーバーから見ると転送先は自分自身になりますので、ホストがlocalhostで、ポートは3306になります。Global IPやPrivate IPで指定しない理由は後ほど解説します。

その他、接続SSHへ接続するためのプライベートキーやユーザー、接続先の指定は通常のSSH接続と変わりありません。

# Workbenchの設定
クライアント側のWorkbench以下のように指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240974/bc25dd65-51b9-e5b3-6966-c0c6b07c4b28.png)

| 項目       | IP or 名前                |備考|
| --------- | ------------------------- | ---- |
| Hostname  | 127.0.0.1 / localhost   ||
| Port      | 33060                      ||
| Username  | 任意のユーザー                |RDBサーバー(自分自身)から接続できるユーザーを指定します。<br>例えば特定IPからのみログインを許可しているユーザーはログインできません。|
| Password  | 上記ユーザーのパスワード         ||

無事にテスト接続ができました:thumbsup:

<img width="532" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240974/d7acad47-eba7-e0a8-4b92-098adde1952f.png">

# ハマったところ
「ハマった」と言っても少しですが、躓いたところを記載します。

1. 疎通ができない
当初、転送先にRDBサーバーのGlobal IPを指定していました。

    ```bash
ssh -fNL 33060:18.179.xxx.xxx:3306 -i "web_a.pem" ec2-user@18.179.xxx.xxx
```
ネットワークの疎通については設定次第になりますが、RDBサーバーにログインして、以下のようにncコマンドを実行してみます。自身のGlobal IPとポート3306を指定して通信が疎通しない場合は、SSHポートフォワーディングもできません。

    ```bash:RDBサーバー側で疎通しない例
$ nc -w 1 -vz 18.179.xxx.xxx 3306
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Connection timed out.
```
Private IPやループバック、localhostを指定することで、疎通ができるようになります。

    ```bash:RDBサーバー側で疎通する例
$ nc -w 1 -vz 10.0.0.176 3306
Ncat: Version 7.50 ( https://nmap.org/ncat )
Ncat: Connected to 10.0.0.176:3306.
Ncat: 0 bytes sent, 0 bytes received in 0.01 seconds.
```
1. 正しいパスワードを入力しているのにログインできない
疎通ができてもログインできないケースがあります。転送先にRDBサーバーのPrivate IPを指定しているとします。

    ```bash
ssh -fNL 33060:10.0.0.176:3306 -i "web_a.pem" ec2-user@18.179.xxx.xxx
```
ログイン時のエラーメッセージは以下のとおりです。
<img width="532" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240974/71170669-c508-33a4-7207-c4890f024829.png">

ログインユーザーが`'root'@'ip-10-0-0-176.ap-northeast-1.compute.internal'`になっています。筆者の環境では、他のサーバーからのrootユーザーのログインを禁止していますが、その時の設定は以下のとおりです。

```
SELECT user, host FROM mysql.user WHERE user = 'root';
+------+-----------+
| user | host      |
+------+-----------+
| root | localhost |
+------+-----------+
1 row in set (0.00 sec)
```

そのため、`'root'@'ip-10-0-0-176.ap-northeast-1.compute.internal'`ではログインできないことになります。SSHポートフォワーディングのためにログインユーザーを作成しない場合は、転送先にRDBサーバーに`127.0.0.1`や`localhost`を指定します。

# Workbenchのみで設定する
「いままでの説明は何だったんだ:question:」と思われるかもしれませんが、前述のとおりSSHコマンドを使わずにWorkbenchのみで設定する方法があります。今までSSHコマンドで実行した内容を再現すると以下のようになります。
<img width="1012" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240974/bf24d12c-c7c4-d776-e04d-cc76eca86b46.png">


# 参考
* [踏み台の向こうにあるMySQLサーバに一発アクセスする](https://qiita.com/lighttiger2505/items/ea33291639a8656d50b4)
* [MySQL Workbenchから踏み台サーバ経由でRDSに接続する](https://dev.classmethod.jp/cloud/aws/mysql-workbench-rds/)

# 終わりに
Workbenchにポートフォワーディングの機能が含まれているので、SSHコマンドで別途実行する需要は無いかもしれません。しかし、SSHのポートフォワーディング単体を理解することで、どこかで応用ができるかもしれません。