2026年5月29日
2026年5月29日
SSHローカルポートフォワーディング(ssh -L)の使い方|DB・内部画面への接続と常駐化
SSH のローカルポートフォワーディング(ssh -L)は、手元の PC のポートに来た接続を、SSH の接続を通してサーバー側の宛先に届ける機能です。インターネットに公開していない MySQL や、社内ネットワークの中にある管理画面に、SSH でログインできるサーバーを経由して接続できます。
この記事のコマンドは OpenSSH 9.6 で動作を確認しています(2026年10月時点)。Windows 10(バージョン 1809 以降)・11 の OpenSSH クライアントでも、PowerShell から同じ書き方で使えます。
書式と読み方
ssh -L [待ち受けアドレス:]待ち受けポート:宛先ホスト:宛先ポート ユーザー@SSHサーバー
| 部分 | 意味 |
|---|---|
| 待ち受けアドレス | 手元の PC で待ち受けるアドレス。省略すると 127.0.0.1(と ::1)だけで待ち受ける |
| 待ち受けポート | 手元の PC で待ち受けるポート番号 |
| 宛先ホスト:宛先ポート | SSH サーバーから見た接続先。127.0.0.1 と書くと SSH サーバー自身を指す |
| ユーザー@SSHサーバー | SSH でログインするサーバー |
間違えやすいのは宛先ホストです。宛先は手元の PC ではなく SSH サーバーが名前を解決して接続するため、127.0.0.1 は「SSH サーバー自身」、10.0.0.20 は「SSH サーバーから届く内部ネットワークのホスト」を意味します。
暗号化されるのは手元の PC と SSH サーバーの間だけです。SSH サーバーから宛先ホストまでは通常の通信になるため、宛先が別のホストの場合は、その間のネットワークが信頼できることを確かめてください。
例1: サーバーの MySQL に手元から接続する
データベースサーバーの MySQL が 127.0.0.1:3306 だけで待ち受けていて外から接続できない場合、手元の 3307 番ポートを転送します。
ssh -N -L 3307:127.0.0.1:3306 user@db.example.com
-N はリモートでコマンドを実行しない(転送だけを行う)オプションです。このターミナルを開いたまま、別のターミナルから接続します。
mysql -h 127.0.0.1 -P 3307 -u wpuser -p
MySQL のクライアントは -h localhost と書くとソケットファイルで接続しようとするため、転送を使うときは 127.0.0.1 と書きます。手元でも MySQL が 3306 番を使っていることが多いので、待ち受けポートには別の番号(ここでは 3307)を使っています。
例2: 踏み台を経由して内部の管理画面を開く
踏み台サーバー(bastion.example.com)からしか届かない内部のサーバー 10.0.0.20 の 8080 番で動く管理画面を、手元のブラウザーで開きます。
ssh -N -L 8080:10.0.0.20:8080 user@bastion.example.com
ブラウザーで http://127.0.0.1:8080/ を開きます。
宛先が HTTPS で、ホスト名で振り分けている(名前ベースのバーチャルホスト)場合、https://127.0.0.1:8443/ で開くと証明書の名前が一致せず警告が出たり、別のサイトが表示されたりします。この場合は、ホスト名を 127.0.0.1 に向けて開きます。curl なら --resolve で指定できます。
ssh -N -L 8443:10.0.0.20:443 user@bastion.example.com
# 別のターミナルで(intranet.example.com を 127.0.0.1 として扱う)
curl --resolve intranet.example.com:8443:127.0.0.1 https://intranet.example.com:8443/
ブラウザーで開く場合は、手元の hosts ファイルで intranet.example.com を 127.0.0.1 にして https://intranet.example.com:8443/ を開きます。確認が終わったら hosts ファイルの行を消してください。
複数のポートをまとめて転送する
-L は1回の接続で何度でも指定できます。
ssh -N -L 3307:127.0.0.1:3306 -L 6380:127.0.0.1:6379 user@db.example.com
バックグラウンドで動かす・転送の失敗を見逃さない
ターミナルを占有したくない場合は -f を付けると、認証が終わった後にバックグラウンドに移ります。あわせて ExitOnForwardFailure=yes を指定します。
ssh -f -N -o ExitOnForwardFailure=yes -L 3307:127.0.0.1:3306 user@db.example.com
ExitOnForwardFailure を指定しないと、待ち受けポートがすでに使われていても、次の警告を出すだけで SSH の接続は続きます。転送ができていないのに接続が残るため、気付きにくくなります。指定すると、転送を作れなかったときに ssh が終了します(終了コード 255)。
bind [127.0.0.1]:3307: Address already in use
channel_setup_fwd_listener_tcpip: cannot listen to port: 3307
Could not request local forwarding.
待ち受けているかどうかは、手元の PC(Linux)で次のように確かめます。
ss -ltnp | grep 3307
よくあるエラー
| 表示 | 原因 |
|---|---|
bind [127.0.0.1]:3307: Address already in use |
手元の待ち受けポートを別のプログラムや前の ssh が使っている |
channel 2: open failed: connect failed: Connection refused |
SSH サーバーから宛先ホスト・ポートに接続できない(宛先のサービスが止まっている、ポートが違うなど) |
channel 2: open failed: administratively prohibited: open failed |
SSH サーバーの設定(AllowTcpForwarding・PermitOpen)で転送が禁止されている |
後の2つは、転送ポートに接続したときに ssh を実行しているターミナルに表示されます。-f でバックグラウンドにしていると見えにくいため、うまく行かないときは -f を外して試します。
~/.ssh/config に書いておく
毎回同じ転送を使うなら、~/.ssh/config に書いておくと短いコマンドで済みます。
Host db-tunnel
HostName db.example.com
User user
LocalForward 127.0.0.1:3307 127.0.0.1:3306
ExitOnForwardFailure yes
ServerAliveInterval 30
ServerAliveCountMax 3
ssh -f -N db-tunnel
ServerAliveInterval 30 と ServerAliveCountMax 3 は、30秒ごとに生存確認を送り、3回続けて応答がなければ接続を切る設定です。通信が途切れたまま ssh が残り続けるのを防ぎます。
転送を止める
フォアグラウンドで動かしている場合は Ctrl+C で止まります。-f でバックグラウンドにした場合は、プロセスを探して止めます。
pgrep -af 'ssh .*-L 3307'
kill プロセス番号
止める予定があるなら、制御用のソケットを作っておくと、プロセス番号を探さずに止められます。
# -M で制御用のマスター接続にし、-S でソケットの場所を決める
ssh -f -N -M -S ~/.ssh/tunnel-db.sock -L 3307:127.0.0.1:3306 user@db.example.com
# 状態の確認と停止
ssh -S ~/.ssh/tunnel-db.sock -O check user@db.example.com
ssh -S ~/.ssh/tunnel-db.sock -O exit user@db.example.com
切れても自動でつなぎ直す(常駐化)
while true; do ssh -L ... & sleep 1200; done のように ssh をバックグラウンドで繰り返し起動する方法は使わないでください。前の ssh が動いたまま次の ssh が起動するため、同じポートで待ち受けようとして失敗する ssh や、不要な接続が増えていきます。また、接続が切れたこと自体は検知できません。
つなぎ直しは、ssh が終了したら起動し直す仕組みに任せます。どちらの方法でも、パスフレーズなしの鍵か ssh-agent で、パスワードを入力せずにログインできる状態にしておきます(最初に1回手動で接続して、ホスト鍵を known_hosts に登録しておきます)。
systemd のサービスにする(Linux)
/etc/systemd/system/ssh-tunnel-db.service を作ります。deploy はトンネルを張るユーザー(その鍵と known_hosts を持つユーザー)に置き換えます。
[Unit]
Description=SSH tunnel 127.0.0.1:3307 to db.example.com:3306
After=network-online.target
Wants=network-online.target
[Service]
User=deploy
ExecStart=/usr/bin/ssh -N -o BatchMode=yes -o ExitOnForwardFailure=yes -o ServerAliveInterval=30 -o ServerAliveCountMax=3 -L 127.0.0.1:3307:127.0.0.1:3306 user@db.example.com
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now ssh-tunnel-db.service
systemctl status ssh-tunnel-db.service
journalctl -u ssh-tunnel-db.service -n 20
-f は付けません(systemd がプロセスを管理するため)。BatchMode=yes は、パスワードの入力などを求められたときに待たずに失敗させる設定です。ServerAliveInterval で切断を検知して ssh が終了すると、Restart=always により10秒後に起動し直されます。
autossh を使う
autossh は、ssh を起動して監視し、終了したら起動し直すプログラムです。Debian・Ubuntu では sudo apt install autossh で入ります。
autossh -M 0 -f -N -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -o "ExitOnForwardFailure yes" -L 3307:127.0.0.1:3306 user@db.example.com
-M 0 は autossh 独自の監視用ポートを使わず、ssh の ServerAliveInterval で切断を検知して終了したら起動し直す指定です。
待ち受けアドレスに注意する
待ち受けアドレスを省略すると、手元の PC 自身(127.0.0.1 と ::1)からしか転送ポートに接続できません。-L 0.0.0.0:3307:127.0.0.1:3306 のように書いたり -g を付けたりすると、同じネットワークの他の機器からも転送ポートに接続でき、データベースなどを意図せず公開することになります。必要がなければ待ち受けアドレスは省略するか 127.0.0.1 と明示します。
1024 番より小さいポートで待ち受けるには、通常は管理者権限が必要です。手元では 1024 以上の番号を使います。
サーバー側で転送を制限する
SSH サーバー(OpenSSH)の AllowTcpForwarding は既定で yes で、ログインできるユーザーは転送を使えます。転送専用のユーザーを作る場合は、/etc/ssh/sshd_config の末尾で、許可する宛先を絞れます。
Match User tunnel
AllowTcpForwarding local
PermitOpen 127.0.0.1:3306
X11Forwarding no
PermitTTY no
AllowTcpForwarding local はローカルポートフォワーディング(-L)だけを許可し、PermitOpen は転送先を指定したホストとポートに限ります。それ以外の宛先への転送は administratively prohibited で拒否されます。変更したら sudo sshd -t で文法を確かめてから SSH サービスを再読み込みします(サービス名は Ubuntu・Debian では ssh、RHEL 系では sshd)。設定を誤るとログインできなくなることがあるため、今の SSH 接続は切らずに、別の接続でログインできることを確かめてください。
まとめ
ssh -L 待ち受けポート:宛先ホスト:宛先ポート ユーザー@サーバーの宛先は、SSH サーバーから見たアドレスで書く- 転送だけなら
-N、バックグラウンドなら-f、転送の失敗で終了させるならExitOnForwardFailure=yesを付ける - よく使う転送は
~/.ssh/configのLocalForwardに書き、ServerAliveIntervalで切断を検知する - 常駐させるなら、ssh を繰り返し起動するループではなく、systemd の
Restart=alwaysか autossh を使う - 待ち受けアドレスは
127.0.0.1のままにし、サーバー側はAllowTcpForwardingとPermitOpenで転送先を絞る