はじめに
先日業務でSSHトンネルとポートフォワーディングを使用する機会がありました。
今まで使ったことがなかったので、勉強と復習を兼ねて記事にしてみました。
やること
外部から直接アクセスできない環境で、SSHトンネルを使用して安全にDBにアクセスします。
例えばこのような場合です。
- ローカルからWebサーバへSSH接続が可能
- ローカルから直接DBにアクセスすることはできない
- DBにはWebサーバーからしかアクセスできない(Webサーバーが踏み台サーバーとして機能)
SSHトンネルとは?
SSHを使って作られる、暗号化された安全な通信路です。これを利用することで、リモートサーバーへの接続等を安全に行うことができます。
リモートのDBやサーバーに接続する際の「暗号化された専用のトンネル」を作るイメージです。
ポートフォワーディングとは?
ポートフォワーディングは、SSHトンネルで使われる機能の一つです。
ローカルマシンのポートとリモートサーバーのポートを繋げて、特定のポート間の通信を転送する仕組みを提供します。これにより、ローカルのポートを通してリモートサーバーのサービスにアクセスできるようになります。
ポートフォワーディングには以下の3種類があります。
- ローカルポートフォワーディング
ローカルマシンのポートをリモートサーバのポートに転送します - リモートポートフォワーディング
リモートサーバのポートをローカルマシンのポートに転送します - ダイナミックポートフォワーディング
SOCKSプロキシを使用して動的にポートを転送します
今回はこの中のローカルポートフォワーディングを使用します。
やってみた
AWSのAmazon Lightsailというサービスを使ってこのような環境を作りました。
上記の図のWebサーバが、Lightsailのインスタンスに置き換わった形です。
勉強を兼ねて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トンネルの作成
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
実際のコマンド例
ssh -L 3307:ls-....amazonaws.com:3306 -i .ssh/LightsailDefaultKey-[リージョン].pem ubuntu@1.234.56.78
この例の場合、ローカルの3307ポートにアクセスすると、SSHトンネルを経由してリモートホストに転送されます。
このとき、転送先はリモートホストのls-....amazonaws.com
のポート3306
となります。
2. MySQLに接続
mysql -h 127.0.0.1 -P [ローカルのポート番号] -u [DBユーザー名] -p
-
127.0.0.1
SSHトンネルを通してリモートホストに接続するため、リモートのDBエンドポイントではなく、ローカルホストを指定します。 -
ローカルのポート番号
先ほどSSHトンネルの設定で指定したローカルのポート番号を指定します。
例:3307
実際のコマンド例
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での接続方法になります。
SSHトンネルで接続した場合はこのようなアイコンが表示されるので分かりやすいです
GUIツールは便利ですが、簡単にデータを変更できてしまうので、ローカル環境以外では、読み取り専用で接続するようにしています。
さいごに
記事の作成を通して、これまで知らなかったSSHトンネルやポートフォワーディングについて理解を深めることができました。
今回学んだことを今後の業務に役立てていきたいと思います。