【SSH】ポートフォワード(トンネル)の使い方|-Lでリモートのデータベースに接続

【SSH】ポートフォワード(トンネル)の使い方|-Lでリモートのデータベースに接続 SSH

SSHポートフォワード(トンネル)は、SSHの暗号化された接続の中に「別の通信を通す」機能です。もっともよくある用途が、外部からは直接つなげないリモートのデータベースに、手元のツールから接続することです。多くのレンタルサーバーやクラウドでは、セキュリティのためMySQLなどのデータベースを外部公開していません。しかしSSHでログインできるなら、SSH接続をトンネルにして、そのデータベースへ安全にアクセスできます。

この記事では、実際にレンタルサーバー(エックスサーバー)のリモートMySQLへ、SSHトンネル経由で手元から接続する例を動かしながら、ポートフォワードの使い方を整理します。-L(ローカルフォワード)を中心に、つまずきやすい「転送先はどこから見た宛先か」という点や、-N-fの使い方まで解説します。

先に結論

  • 基本はssh -L 手元のポート:宛先ホスト:宛先ポート ユーザー@サーバーです。
  • 手元のlocalhost:ポートへつなぐと、サーバー経由で宛先に届きます
  • 宛先ホストは「サーバー側から見た宛先」として解決されます(ここが最重要)。
  • 外部非公開のリモートDBに、手元のDBツールから接続できるのが定番用途です。
  • シェルを使わず接続だけ張るなら-N、背後で動かすなら-fを付けます。
  • SOCKSプロキシは-D、逆方向(リモート→手元)は-Rです。

接続設定の集約は~/.ssh/config、鍵認証の準備はssh-keygen、ポート変更済みサーバーの扱いはSSHポート番号の変更もあわせて参考になります。

スポンサーリンク

ローカルフォワード(-L)の基本

もっとも使うのが-L(ローカルフォワード)です。書式は-L 手元のポート:宛先ホスト:宛先ポート。「手元の指定ポートへの通信を、SSHサーバーを経由して宛先ホスト:宛先ポートへ届ける」という意味です。

-L でリモートMySQLにつなぐ
# 手元の 13306 → サーバー経由 → リモートDB(3306) へトンネルを張る
ssh -L 13306:db-host.example.com:3306 user@server.example.com

#   13306               … 手元PCで開くポート(好きな番号でよい)
#   db-host.example.com … 「サーバーから見た」DBのホスト名
#   3306                … DBのポート(MySQLの既定)

# 別のターミナルで、手元の 13306 に接続すると
# サーバー経由でリモートDBにつながる
mysql -h 127.0.0.1 -P 13306 -u dbuser -p
外部非公開のDBにトンネルで接続できた(実サーバーで実証)

実際にレンタルサーバーで試しました。ssh -L 13306:(DBホスト):3306 (ユーザー)@(サーバー)でトンネルを張り、別のターミナルから手元の127.0.0.1:13306に対してMySQLクライアントで接続したところ、リモートのMySQLに正しくつながり、SELECT VERSION()5.7.27、データベース名まで取得できました。ポイントは、このDBホストが外部には公開されておらず、手元から直接は接続できないことです。それでも、SSHでログインできる権限さえあれば、SSH接続をトンネルにしてDBへ安全に到達できます。ローカル開発環境のツール(phpMyAdminやDBクライアント)から本番DBを覗く、といった作業がこれで実現できます。通信はSSHで暗号化されるため、安全性の面でも安心です。

【最重要】転送先は「サーバー側から見た宛先」

ポートフォワードで最も誤解しやすいのが、-L 手元:宛先ホスト:宛先ポートの「宛先ホスト」は、どこから見た名前かという点です。答えは「SSHでログインするサーバーから見た宛先」です。手元のPCから見た名前ではありません。

宛先ホストの解決の考え方
# パターン1: DBがSSHサーバー自身にある場合 → localhost
ssh -L 13306:localhost:3306 user@server.example.com
#   「サーバーにとっての localhost:3306」= サーバー上のMySQL

# パターン2: DBが別のホストにある場合 → そのホスト名/IP
ssh -L 13306:db-internal.example.com:3306 user@server.example.com
#   db-internal は「サーバーから見て」到達できる名前である必要がある

# 手元からは db-internal に直接つながらなくてよい
# (サーバーが中継してくれるため)
宛先の名前解決はサーバー側で行われる

実サーバーでの検証でも、トンネルの宛先に指定したDBホストは「サーバー側のネットワークから到達できる名前」でした。手元のPCからそのDBホストに直接つなごうとしても、外部非公開なので届きません。しかしトンネルでは宛先への接続をサーバーが代わりに行うため、手元から届かない相手にもアクセスできるのです。よくある間違いが、-L 13306:localhost:3306localhostを「手元のPC」と勘違いすることです。ここでのlocalhost「SSHでログインするサーバー自身」を指します。「宛先ホストは、SSHサーバーがそこへ接続しに行く先」と理解すれば、間違えません。DBがSSHサーバー自身で動いているならlocalhost、別のDBサーバーならサーバーから見たそのホスト名を書きます。

-N・-f(接続だけを背後で張る)

トンネルを張るとき、サーバー上でシェル操作をする必要はありません。-N(コマンドを実行しない)-f(バックグラウンドに回る)を組み合わせると、トンネル専用の接続をすっきり張れます。

-N -f でトンネル専用接続
# -N: リモートでコマンドを実行しない(トンネルだけ)
# -f: 認証後にバックグラウンドへ回る
ssh -f -N -L 13306:db-host:3306 user@server.example.com

# これでプロンプトが戻り、裏でトンネルが張られたまま
# 手元の 13306 を使う作業ができる

# ポートが22以外なら -p も(configに書けば省ける)
ssh -f -N -p 10022 -L 13306:db-host:3306 user@server.example.com

実サーバーでのトンネルも、-f -N -L ...の形でバックグラウンドに接続だけを張り、別ターミナルからDB接続するという流れで動作しました。-Nを付けないと通常のログインシェルも開いてしまい、-fを付けないとそのターミナルがトンネルに占有されます。「トンネルを張りっぱなしにして作業したい」ときは-f -Nがセットです。毎回オプションを書くのが面倒なら、~/.ssh/configLocalForward 13306 db-host:3306と書いておく方法もあります。

-D(SOCKSプロキシ)と-R(リモートフォワード)

-L以外にも、方向や用途の違う転送があります。-DはSOCKSプロキシ(ブラウザ等の通信をまるごとサーバー経由にする)、-Rはリモートフォワード-Lと逆に、サーバー側のポートを手元へ転送)です。

-D と -R
# -D: 動的フォワード(SOCKSプロキシ)
# 手元の 1080 を SOCKS プロキシにして、通信をサーバー経由にする
ssh -D 1080 user@server.example.com
#   ブラウザのプロキシ設定を SOCKS5 127.0.0.1:1080 にすると
#   すべての通信がサーバーを経由する(社内網アクセス等)

# -R: リモートフォワード(-L と逆方向)
# サーバーの 8080 への接続を、手元の 3000 へ転送する
ssh -R 8080:localhost:3000 user@server.example.com
#   手元で動かしている開発サーバー(3000)を、
#   リモート側から一時的に見せたいときに使う

-Dは、指定した手元のポートをSOCKSプロキシとして動かし、ブラウザなどの通信をまとめてサーバー経由にできます(社内ネットワークへのアクセスや、接続元をサーバーに見せたいときに便利です)。-R-L向きが逆で、サーバー側のポートへの接続を手元へ転送します。「手元で動かしている開発中のサーバーを、リモート側から一時的にアクセスできるようにする」といった使い方があります。日常でよく使うのは圧倒的に-Lなので、まずは-Lを確実に押さえ、-D-Rは「そういう方向もある」と覚えておけば十分です。

トンネルの閉じ方

-f -Nでバックグラウンドに張ったトンネルは、作業が終わったらそのSSHプロセスを終了して閉じます。開いているポートから、対応するプロセスを見つけて終了します。

トンネルを閉じる
# Linux/Mac: ポートを使っているプロセスを探して終了
lsof -i :13306          # トンネルの ssh プロセスを確認
kill <表示されたPID>

# もっと手軽に(ポートを直接指定して終了)
# pkill -f "13306:"     # フォワード指定で絞って終了

# Windows(PowerShell): ポートからPIDを調べて終了
# netstat -ano | findstr 13306
# taskkill /PID <PID> /F

# -f を付けず前面で張っている場合は Ctrl+C で終了

バックグラウンドのトンネルは、明示的に終了しないと張られたまま残るため、使い終わったら閉じる習慣をつけましょう。ps・killの記事で解説したように、lsof -i :ポート(またはss/netstat)でトンネルのSSHプロセスを特定し、killで終了します。前面で張っている(-fなし)場合は、そのターミナルでCtrl+Cを押せば閉じられます。

主なオプション一覧

ポートフォワードで使うオプションをまとめます。

オプション 働き
-L 手元:宛先:ポート ローカルフォワード(手元→サーバー経由→宛先)
-R サーバー:宛先:ポート リモートフォワード(-Lと逆方向)
-D ポート 動的フォワード(SOCKSプロキシ)
-N リモートでコマンドを実行しない(トンネル専用)
-f 認証後にバックグラウンドへ回る
-p 番号 SSHのポート(22以外のとき)

よくある失敗

宛先のlocalhostを「手元」と勘違いする

-L 13306:localhost:3306localhostSSHサーバー自身です。手元PCではありません。

手元のポートが既に使われている

13306など空いている番号を選びます。使用中だと「Address already in use」になります。

接続先を127.0.0.1にし忘れる

ツール側はサーバー名ではなく手元の127.0.0.1:手元ポートに接続します。

トンネルを閉じ忘れる

-fで背後に張ったトンネルは残り続けます。lsof/netstatで見つけて終了します。

-Nを付けず余計なシェルが開く

トンネル専用なら-Nを付けます。-fと合わせて-f -Nが定番です。

よくある質問

QSSHポートフォワードは何に使いますか?
Aもっとも多いのは、外部に公開されていないリモートのデータベースへ、手元のツールから接続することです。ssh -L 13306:DBホスト:3306 user@serverでトンネルを張り、手元の127.0.0.1:13306に接続すると、SSHサーバー経由でDBにつながります。実サーバーでも、外部非公開のMySQLにこの方法で接続できることを確認しています。通信はSSHで暗号化されるので安全です。
Q-L 13306:localhost:3306 のlocalhostはどこを指しますか?
ASSHでログインするサーバー自身を指します。手元のPCではありません。ポートフォワードの宛先ホストは「サーバー側から見た宛先」として解決されるためです。DBがSSHサーバー上で動いているならlocalhost、別のDBサーバーにあるなら「サーバーから到達できる」そのホスト名を書きます。
Q-Nと-fは何のために付けますか?
A-Nはリモートでコマンドを実行せず、トンネルだけを張るためです。-fは認証後にバックグラウンドへ回り、プロンプトを返します。トンネルを張りっぱなしで別の作業をしたいときは、ssh -f -N -L ...のように両方を付けるのが定番です。
Q-Lと-Rの違いは何ですか?
A-L(ローカルフォワード)は「手元→サーバー経由→宛先」で、リモートのサービスに手元からアクセスします。-R(リモートフォワード)はその逆で、「サーバー側のポートへの接続を手元へ転送」します。手元で動かしている開発サーバーをリモートから見せたいときなどに使います。日常でよく使うのは-Lです。
Q張ったトンネルはどうやって閉じますか?
A-fでバックグラウンドに張った場合は、そのSSHプロセスを終了します。lsof -i :ポート番号(またはWindowsはnetstat -ano | findstr ポート)でプロセスを特定し、killtaskkillで終了してください。-fを付けず前面で張っている場合は、そのターミナルでCtrl+Cを押せば閉じられます。

まとめ

  • 基本はssh -L 手元ポート:宛先ホスト:宛先ポート user@server
  • 手元の127.0.0.1:手元ポートにつなぐと、サーバー経由で宛先に届きます
  • 宛先ホストは「サーバー側から見た宛先」localhostはサーバー自身です。
  • トンネル専用は-f -N、SOCKSは-D、逆方向は-R
  • 使い終わったトンネルはlsof/netstatで見つけて閉じます。

SSHポートフォワードは、「外部非公開のサービスに、SSHの権限だけで安全に到達する」ための強力な仕組みです。まずは-LでリモートDBに接続する使い方を押さえれば、本番データベースの確認や管理がぐっと楽になります。~/.ssh/configLocalForwardを書いておけば、毎回の長いコマンドも不要になります。