SSHトンネル使ってMySQLに接続してみた

## はじめに
先日業務でSSHトンネルとポートフォワーディングを使用する機会がありました。
今まで使ったことがなかったので、勉強と復習を兼ねて記事にしてみました。

## やること
外部から直接アクセスできない環境で、SSHトンネルを使用して安全にDBにアクセスします。

例えばこのような場合です。
- ローカルからWebサーバへSSH接続が可能
- ローカルから直接DBにアクセスすることはできない
- DBにはWebサーバーからしかアクセスできない（Webサーバーが踏み台サーバーとして機能）

![名称未設定ファイル.drawio (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748887/395604af-1d54-cdf3-e775-5299b1ecdb86.png)

## SSHトンネルとは？
SSHを使って作られる、暗号化された安全な通信路です。これを利用することで、リモートサーバーへの接続等を安全に行うことができます。
リモートのDBやサーバーに接続する際の「暗号化された専用のトンネル」を作るイメージです。

## ポートフォワーディングとは？
ポートフォワーディングは、SSHトンネルで使われる機能の一つです。
ローカルマシンのポートとリモートサーバーのポートを繋げて、特定のポート間の通信を転送する仕組みを提供します。これにより、ローカルのポートを通してリモートサーバーのサービスにアクセスできるようになります。

ポートフォワーディングには以下の3種類があります。
1. ローカルポートフォワーディング
ローカルマシンのポートをリモートサーバのポートに転送します
2. リモートポートフォワーディング
リモートサーバのポートをローカルマシンのポートに転送します
3. ダイナミックポートフォワーディング
SOCKSプロキシを使用して動的にポートを転送します

今回はこの中のローカルポートフォワーディングを使用します。

## やってみた
AWSのAmazon Lightsailというサービスを使ってこのような環境を作りました。
上記の図のWebサーバが、Lightsailのインスタンスに置き換わった形です。
勉強を兼ねてTerraformで構築してみました。(ほぼ[ドキュメント](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lightsail_instance)通りですが)

![名称未設定ファイル.drawio (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748887/f2c7d8fb-f24b-21b7-cab9-75918ec2dbe6.png)

```terraform
# インスタンス
resource "aws_lightsail_instance" "test" {
  name              = "test"
  availability_zone = var.availability_zone
  blueprint_id      = "ubuntu_22_04"
  bundle_id         = "nano_3_0"
  ip_address_type   = "ipv4"
}

# ポートの設定
resource "aws_lightsail_instance_public_ports" "test" {
  instance_name = aws_lightsail_instance.test.name

  # SSHしか使わないので22番ポートだけ
  port_info {
    protocol  = "tcp"
    from_port = 22
    to_port   = 22
    # 一応IPアドレス制限
    cidrs     = [var.ip]
  }
}

# DB
resource "aws_lightsail_database" "test-db" {
  relational_database_name = "testdb"
  availability_zone        = var.availability_zone
  master_database_name     = var.master_database_name
  master_password          = var.master_password
  master_username          = var.master_username
  blueprint_id             = "mysql_8_0"
  bundle_id                = "micro_2_0"
}
```

### SSHトンネルの作成とMySQLへの接続
Lightsailの環境構築が完了したら、SSHトンネルを使ってローカルからMySQLに接続します。

#### 1. SSHトンネルの作成

```bash
ssh -L [ローカルのポート番号]:[DBエンドポイント]:[DBポート番号] -i [インスタンスSSH鍵] [インスタンスユーザー名]@[インスタンスパブリックIP]
```
#### コマンドのオプション説明
- **`-L`オプション**  
ローカルポートフォワーディングを行うオプションです。

- **ローカルのポート番号**
ローカルで空いている任意のポート番号を指定します。このポートにアクセスすると、SSHトンネル経由でリモートホストに転送されます。
**例**：`3307`

- **DBエンドポイント**
DBサーバのエンドポイントです。
**例**（Lightsailの場合）：`ls-....amazonaws.com`

- **DBポート番号**  
DBサーバがリッスンしているポート番号です。  
**例**：`3306`

- **インスタンスSSH鍵**  
LightsailインスタンスにSSH接続する際の秘密鍵ファイルのパスを指定します。  
**例**：`.ssh/LightsailDefaultKey-[リージョン].pem`

- **インスタンスユーザー名**  
SSH接続時のユーザー名です。
**例**：`ubuntu`

- **インスタンスパブリックIP**  
SSHで接続するLightsailインスタンスのパブリックIPアドレスです。
**例**：`1.234.56.78`

#### 実際のコマンド例
```bash
ssh -L 3307:ls-....amazonaws.com:3306 -i .ssh/LightsailDefaultKey-[リージョン].pem ubuntu@1.234.56.78
```
この例の場合、ローカルの3307ポートにアクセスすると、SSHトンネルを経由してリモートホストに転送されます。
このとき、転送先はリモートホストの`ls-....amazonaws.com`のポート`3306`となります。


#### 2. MySQLに接続
```bash
mysql -h 127.0.0.1 -P [ローカルのポート番号] -u [DBユーザー名] -p
```

- **127.0.0.1**  
SSHトンネルを通してリモートホストに接続するため、リモートのDBエンドポイントではなく、ローカルホストを指定します。

- **ローカルのポート番号**  
先ほどSSHトンネルの設定で指定したローカルのポート番号を指定します。
**例**：`3307`

#### 実際のコマンド例
```bash
mysql -h 127.0.0.1 -P 3307 -u user -p
```
このコマンドを実行すると、127.0.0.1（ローカル）のポート3307を通じて、SSHトンネル経由でリモートのMySQL（ls-....amazonaws.com:3306）に接続することができます。

## GUIツールを使う場合
(GUIは甘え。という声が聞こえてきそうですが。。)
私が普段使用しているDBeaverでの接続方法になります。

1. データベース > 新しい接続を選択
![スクリーンショット 2024-09-30 22.31.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748887/28293131-3688-434c-749d-05f58710c0d2.png)

2. DBを選択
![スクリーンショット 2024-09-30 22.31.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748887/7731bcfb-2615-439d-a5c5-52a7c4090b1f.png)

3. 接続情報を入力
先程の設定を例に入力していきます。
- 一般
![スクリーンショット 2024-09-30 22.46.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748887/ad19f297-0203-4e7b-d7f3-b2ada83790cb.png)

- SSH
`SSH Tunnelを使用する`にチェックを入れると入力できるようなります
![スクリーンショット 2024-09-30 22.47.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748887/000190ba-0cc1-eba4-f2be-75e7be970362.png)


テスト接続でこうなれば成功です
![スクリーンショット 2024-09-30 22.48.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748887/624268d3-81b9-3b43-44f7-c8244b1b2b0a.png)

SSHトンネルで接続した場合はこのようなアイコンが表示されるので分かりやすいです
![スクリーンショット 2024-09-30 22.51.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748887/0c33ac31-c683-f8d7-09d5-5ddf965def43.png)

GUIツールは便利ですが、簡単にデータを変更できてしまうので、ローカル環境以外では、読み取り専用で接続するようにしています。

## さいごに
記事の作成を通して、これまで知らなかったSSHトンネルやポートフォワーディングについて理解を深めることができました。
今回学んだことを今後の業務に役立てていきたいと思います。