SSHのポートフォワーディング(SSHポート転送)機能を利用すると、ローカルホストの任意のポートに送信したデータを、リモートホストの特定のポートへ転送することができます。
POPのようなプレーンテキストのプロトコルに使用して、SSHの通信経路は使った安全な通信を行うことに利用できます。
SSHポートフォワーディングは、主に次のようなケースで利用します。
- SSHの暗号化された通信経路を使った安全な通信を行う
- ファイアーウォールを超えてサーバーと通信する
- 複数ホストを経由する必要がある場合、最終ホストと直接通信しているようにアクセスする
この記事ではSSHポートフォワーディング(SSHポート転送)について、使い方や動作を図を使ってわかりやすく説明していきます。
コマンドの構文と説明に使用するケース
最初にポートフォワーディングの構文とこの記事で説明するケースを見てみましょう。
SSHポートフォワーディングの構文
SSHポートフォワーディングを行うためのsshコマンドの構文は次の通りです。
ssh -f -N -L ローカルポート番号:転送先ホスト名:転送先ポート番号 ユーザ名@SSH接続ホスト名
-fオプションはSSHの認証が完了し、リモートコマンドを実行した後にSSHをバックグランドへ移行するためのオプションです。
-Nオプションはリモートコマンドを実行しないよう指示するオプションです。上記の構文ではリモートで実行するコマンドの指定がないので、-fオプションを指定するときは-Nオプションが必要です(指定しないとエラーになる)。
-Lオプションはポートフォワーディングを指示するオプションです。「ローカルポート番号」には、ローカルマシンで使用していない任意のポート番号を指定します。
システムポート(ウェルノウンポート)の「0番〜1023番」はrootユーザーでしか使用できません。一般にシステムポートの使用は避けた方が良いでしょう。
「転送先ホスト名:転送先ポート番号」には、通信を転送する先のホスト名とそのポート番号を指定します。
残りの「ユーザ名@SSH接続ホスト名」には、SSHサーバーのホスト名とそのユーザ名を指定します。
なお、ホスト名の部分はIPアドレスでも構いません。
記事で説明するケース
この記事ではリモートのWebサーバー(80番ポート)にアクセスする次の2つのケースを例として、ポートフォワーディングの使い方とその動作を説明します。
- ケース1:SSHサーバー経由でWebサーバーへアクセスする
-
ケース1はローカルマシンからWebサーバへ直接アクセスできません。しかし、SSHサーバー(ポート22番)にログインはでき、そのSSHサーバーからWebサーバー(ポート80番)にはアクセスできます。
例えば、SSHサーバーとWebサーバーが社内LANのネットワーク上にあり、外部からはSSHサーバー(踏み台サーバー)を経由しなければならない場合がこれに該当します。
このケースでポートフォワーディングを使うと、ローカルマシンがWebサーバーに直接アクセスしているかのように通信できます。
- ケース2:ファイアーウォールを超えてアクセスする
-
ケース2はファイアーウォールでポート80番が遮断されているため、Webサーバー(ポート80番)に直接アクセスできません。しかし、SSHサーバー(ポート22番)にログインはできるケースです。
このときポートフォワーディングを使うと、ローカルマシンからサーバーで稼働するWebサーバー(ポート80番)に直接アクセスしているかのように通信ができます。
なお、ここではSSH接続にパスワード認証を使っているものと想定しています。
それではそれぞれのケースについてポートフォワーディングの使い方を見ていきましょう。
ケース1:
SSHサーバー経由でWebサーバーへアクセスする
ケース1はローカルマシンから直接Webサーバーへアクセスできないが、SSHサーバーにログインすることができ、かつSSHサーバーからはWebサーバーへアクセスできる場合です。
言い換えれば、Webサーバーへアクセスするには必ずSSHサーバーを経由する必要があるということです。
上図のような構成のとき、SSHポートフォワーディングを使ってローカルマシンからWebサーバーへアクセスできるようにするには次のコマンドを実行します。
ssh -L 50080:www.example.com:80 taro@ssh.example.com
コマンドを実行するとパスワードを尋ねられますので、通常のSSH接続と同じようにパスワードを入力してSSHサーバーにログインします。
このコマンドのそれぞれの意味は次の通りです。
- 「50080」はローカルマシンで使用するポート番号です。ローカルマシンで使用していない任意のポート番号を指定します。
- 「www.example.com:80」の部分には、SSHの通信を転送する先のリモートホスト名とポート番号を指定します。ここではWebサーバーの80番ポートを指定しています。
- コマンドの残りの部分は、SSHサーバーにログインするために必要なものを指定します。ここではユーザーtaroがパスワード認証でログインできることを想定しています。
これでローカルマシンの50080番ポートへの通信がWebサーバー(ポート80番)へ送られるようになります。このとき、SSH接続の経路の通信は暗号化されます。
Webブラウザでアクセスする
Webブラウザを使って実際にWebサーバーにアクセスしてみましょう。例えば、通常は以下のURLでWebページにアクセスできるとします。
http://a.example.com/page-a.html
ポートフォワーディングを使ったときはコマンドで指定したローカルマシンのポートにアクセスしますので、URLは次のようになります。
http://localhost:50080/page-a.html
URLのホスト名はローカルマシンを表すlocalhost(ループバックアドレス)、ポート番号はコマンドで指定した50080番です。
ブラウザを起動して上記のURLにアクセスするとWebサーバーからpage-aを取得してブラウザに表示されます。
ケース1の通信の詳細
このときの通信の流れを詳しく見てみましょう。
まずSSHコマンドを実行すると、ローカルマシンの任意のポート(any)とSSHサーバーの22番ポートでSSH接続が確立されます。
次にブラウザでローカルマシンの50080番ポートにアクセスすると、その通信はSSH接続の通信路を通ってSSHサーバーの22番ポートへ送られます。さらにSSHサーバの任意のポート(any)とWebサーバーの80番ポートの接続が確立され、確立された経路を通ってWebサーバーの80番ポートへ送られます。
この動作を図で表すと次のようになります。
SSH接続のパイプ(トンネル)は、ここを通る通信が暗号化されていることを表しています。
SSHコマンドをバックグラウンドで実行する
先程はWebブラウザを使いましたが、curlコマンドなどでWebサーバーにアクセスしたい場合もあるでしょう。
前述のコマンドを実行するとターミナルはSSHサーバーにログインした状態なのでローカルマシンのコマンドを実行できません。新しくターミナルを立ち上げて、そこでコマンドを実行しても良いのですが、SSHコマンドをバックグラウンドで実行すれば同じターミナルがそのまま使用できます。
それには先ほどのコマンドに-fと-Nオプションを追加します。
ssh -f -N -L 50080:www.example.com:80 taro@ssh.example.com
-fオプションは認証が完了してリモートでコマンド実行した後にSSHをバックグランドへ移行するためのオプションです。ここではリモートで実行するコマンドを指定していないので、リモートコマンドを実行しないよう指示する-Nオプションも指定します。
このようにするとSSHコマンドはバックグラウンドで実行されますので、そのままターミナルからローカルマシンのコマンドを実行できます。
例えば先ほどと同じWebページをcurlコマンドで取得するには、ターミナルで次のコマンドを実行します。URLはブラウザでアクセスする場合と同じです。
curl http://localhost:50080/page-a.html
ただしSSHコマンドはバックグラウンドで実行され続けますので、ポートフォワーディングが不要になったらプロセスをキルするといいでしょう。
それには次のようにpsコマンドでバックグラウンドで実行されているSSHコマンドのプロセスID(PID)を確認し、killコマンドでプロセスを終了します。
# ps -ef | grep ssh ... root 3200 ... ssh -f -N -L 50080:www.example.com:80 taro@ssh.example.com ... # kill 3200
ケース2:
ファイアーウォールを超えて接続する
ケース2はサーバーにSSH(ポート22番)接続はできるが、そのサーバーで稼働するWebサーバー(ポート80番)には直接アクセスできないケースです。
このケースでもSSHポートフォワーディングを使うと、ローカルマシンからWebサーバーに直接アクセスしているかのように通信できます。
このときはターミナルから次のコマンドをいずれかを実行します。
ssh -L 50080:localhost:80 taro@a.example.com ssh -L 50080:server.example.com:80 taro@a.example.com
どちらのコマンドでもやりたいことができますが、サーバー内部での動作が少し異なります。1つ目のコマンドをおすすめします。
コマンドを実行したら、通常のSSH接続と同じようにサーバーにログインします。
ケース1との違いは転送先のホスト名の指定だけです。ケース1はSSHサーバーとは異なるサーバー(Webサーバー)のホスト名を指定していましたが、今回は自分自身のホスト名(localhostあるいはserver.example.com)を指定しています。
つまり、SSH接続の経路を通ってきた通信を自身のポート80番に転送しています。
Webブラウザでアクセスする
WebブラウザでアクセスするURLもケース1と同じで、ローカルホスト自身(localhost)のポート50080番を指定します。
http://localhost:50080/page-a.html
WebブラウザでこのURLにアクセスするとpage-a.htmlを取得して表示します。
ケース2の通信の詳細
この動作を図で表すと次のようになります。
SSHコマンドを実行するとローカルマシンの任意のポート(any)とサーバーの22番ポートの間でSSH接続が確立されます。
ブラウザからローカルマシンの50080番ポートにアクセスすると、通信はSSHの通信路を通りサーバーに送られ、その通信はさらにサーバー自身のポート80番に転送されます。
ローカルマシンとサーバーAの間はSSHのトンネルを通りますので、その通信が暗号化されているのもケース1と同じです。
SSHコマンドをバックグラウンドで実行する
SSHコマンドをバックグランドで実行する方法もケース1と同様で、先ほどのコマンドに-fと-Nオプションを追加します。
ssh -f -N -L 50080:localhost:80 taro@a.example.com
curlコマンドでWebページを取得する方法もケース1と同じです。
curl http://localhost:50080/page-a.html
バックグラウンドで実行されているコマンドは不要になったらkillするようにしましょう。
localhostと実際のホスト名を指定したときの違い
先ほど次の2つのコマンドを紹介しましたが、この違いについて簡単に説明します。
ssh -L 50080:localhost:80 taro@a.example.com ssh -L 50080:server.example.com:80 taro@a.example.com
「localhost」と「server.example.com」の指定はどちらもSSHサーバー自身を表すことになりますが、それぞれ名前解決されるとlocalhostは「127.0.0.1(ループバックアドレス)」に、server.example.comはサーバーの実際のIPアドレスに解決されます。
ループバックアドレスは論理インタフェースに紐づけられ、実際のIPアドレスは物理インタフェースに紐づけられますので、ポートフォワーディングの際、SSHサーバーのポート20番に届いた通信が論理インタフェースに紐づけられたループバックアドレスに転送されるか、物理インタフェースに紐づけらた実際のIPアドレスに転送されるかが異なることになります。
つまりSSHサーバーのポート20番に届いた通信が自身のポート80番へ転送される経路が多少異なるということです。
この経路の違いがどのように影響するかはOSの詳細に立ち入ることになり、またポートフォワーディングの使い方という本質からだいぶ外れてしまうのでこのへんにしておきますが、localhostとの通信は完全な内部通信となりますので、こちらを使った方が何かと問題が出ないと思います。
なお、私の持っている本で3冊にポートフォワードの記載がありましたが、いずれも実際のホスト名を使っていました。しかしmanコマンドで参照できるSSHコマンドのマニュアルではlocalhostを使用していました。
まとめ
SSHポートフォワーディングは、目的のサーバーにアクセスするためにSSHサーバーを経由しなければならないときに非常に便利です。
この記事ではWebサーバー(ポート80番)を例にしましたが、もちろん任意のリモートホストの任意のポートにポートフォワーディングすることが可能です。
私はファイル転送にもよくポートフォワーディングを使っていますので、最後にこの事例を簡単に説明して終わりにしたいと思います。
ポートフォワーディングを使ってファイルを転送する
このケースは次のような構成であるとします。
クライアント --- リモートホスト1 --- リモートホスト2
クライアントからリモートホスト1にSSH接続ができ、リモートホスト1からリモートホスト2にもSSH接続ができるが、クライアントからリモートホスト2には直接SSH接続できないケースです。このときクライアントでリモートホスト2からファイルを取得したいとします。
ポートフォワーディングを使わないで手順は大体次のようになるでしょう。
- クライアントからリモートホスト1にSSHログインする
- リモートホスト1からリモートホスト2のファイルをSCPで取得する
- クライアントからリモートホスト1のファイルをSCPやWinSCPなどのアプリケーションで取得する
- リモートホスト1に残っているファイルを削除する
これをポートフォワーディングを使って行うと次のようになります。
- クライアントでポートフォワーディング(転送先ホストはリモートホスト2のポート22番)を実行する
- クライアントからSCPやWinSCPなどを使って自身のポートにアクセスし、リモートホスト2のファイルを取得する
- ポートフォワーディングのプロセスを終了する
この説明だとあまり楽になってないように思えるかもしれませんが、実際はかなり楽できると思います。特にリモートホスト1に一時ファイルができないのが良いところです。
このほかにも便利な使い方がありますので、皆さんもSSHポートフォワーディングを是非使ってみてください。
ここから先は主に検証した内容の覚え書きです。SSHポートフォワードを使うにはここまでの記事で十分ですので、この先は読まなくても良いでしょう。興味のある方のみ先に進んでいただければと思います。
【参考】動作検証
この記事を書くにあたり仮想環境でサーバーを建てて検証を実施しました。
Webサーバへのアクセスは一瞬で終わってしまうので、この検証ではPOPサーバーにTelnetで接続して確認を行います。
ケース1の検証
仮想環境に3台のLinuxマシンを用意します。
- ローカルマシン用のLinuxマシン
-
- ホスト名:client
- IPアドレス:192.168.60.10
- 用途:SSHクライアント用
- SSHサーバー用のLinuxマシン
-
- ホスト名:Server1
- IPアドレス:192.168.60.20
- 用途:SSHサーバーが稼働。Server2のPOPサーバへもアクセス可能
- POPサーバー用のLinuxマシン
-
- ホスト名:Server2
- IPアドレス:192.168.60.30
- 用途:POPサーバ(dovecot)が稼働
clientでSSHポートフォワーディングのコマンドを実行します。Server1にSSH接続し、その通信はServer2のPOPサーバーへ転送します。
[root@client ~]# ssh -f -N -L 50110:192.168.60.30:110 taro@192.168.60.20
この状態でのclientのTCPソケットを確認します。
[root@client ~]# ss -atn State Recv-Q Send-Q Local Address:Port Peer Address:Port Process ... LISTEN 0 128 127.0.0.1:50110 0.0.0.0:* ESTAB 0 0 192.168.60.10:56474 192.168.60.20:22 ...
SSHの接続が確立され、ループバックアドレス(127.0.0.1)のポート50110番がLISTENです。
clientのループバックアドレス(127.0.0.1)のポート50110番にTelnetで接続します。これでServer2のPOPサーバーに接続されました。
[root@client ~]# telnet 127.0.0.1 50110 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. +OK Dovecot ready.
このときのclientのTCPソケットを別のターミナルを立ち上げて見てみます。
[root@client ~]# ss -atn State Recv-Q Send-Q Local Address:Port Peer Address:Port Process ... LISTEN 0 128 127.0.0.1:50110 0.0.0.0:* ESTAB 0 0 127.0.0.1:50110 127.0.0.1:39152 ESTAB 0 0 127.0.0.1:39152 127.0.0.1:50110 ESTAB 0 0 192.168.60.10:56474 192.168.60.20:22 ...
「127.0.0.1:50110」と「127.0.0.1:39152」との間で接続が確立されています。
Telnet接続したときの通信をclentでキャプチャした結果は次の通りです。複数のインタフェース(loとeth1)をキャプチャしたかったので、tcpdumpではなくtsharkを使いました。
[root@client ~]# tshark -i lo -i eth1 -n ... ... 1 0.000000000 127.0.0.1 → 127.0.0.1 TCP 74 39152 → 50110 [SYN] Seq=0 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=1165547724 TSecr=0 WS=128 2 0.000007518 127.0.0.1 → 127.0.0.1 TCP 74 50110 → 39152 [SYN, ACK] Seq=0 Ack=1 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=1165547724 TSecr=1165547724 WS=128 3 0.000014242 127.0.0.1 → 127.0.0.1 TCP 66 39152 → 50110 [ACK] Seq=1 Ack=1 Win=43776 Len=0 TSval=1165547724 TSecr=1165547724 4 0.010067703 127.0.0.1 → 127.0.0.1 TCP 86 50110 → 39152 [PSH, ACK] Seq=1 Ack=1 Win=43776 Len=20 TSval=1165547734 TSecr=1165547724 5 0.010095015 127.0.0.1 → 127.0.0.1 TCP 66 39152 → 50110 [ACK] Seq=1 Ack=21 Win=43776 Len=0 TSval=1165547734 TSecr=1165547734 6 0.000115681 192.168.60.10 → 192.168.60.20 SSH 166 Client: Encrypted packet (len=100) 7 0.001023164 192.168.60.20 → 192.168.60.10 SSH 118 Server: Encrypted packet (len=52) 8 0.001038095 192.168.60.10 → 192.168.60.20 TCP 66 56474 → 22 [ACK] Seq=101 Ack=53 Win=278 Len=0 TSval=1792724337 TSecr=3104266180 9 0.009896408 192.168.60.20 → 192.168.60.10 SSH 134 Server: Encrypted packet (len=68) 10 0.009914526 192.168.60.10 → 192.168.60.20 TCP 66 56474 → 22 [ACK] Seq=101 Ack=121 Win=278 Len=0 TSval=1792724346 TSecr=3104266188 ^C14 packets captured
「127.0.0.1:39152」と「127.0.0.1:50110」で接続が確立され、client(192.168.60.10)とServer1(192.168.60.20)間の通信はSSH接続が使われていることがわかります。
Server1のTCPソケットの状態は次の通りです。
[root@Serve1 ~]# ss -atn State Recv-Q Send-Q Local Address:Port Peer Address:Port Process ... ESTAB 0 0 192.168.60.20:37244 192.168.60.30:110 ESTAB 0 0 192.168.60.20:22 192.168.60.10:56474 ...
SSH接続とServer2のPOPサーバー(192.168.60.30:110)との接続が確立されていることがわかります。
Server1でTelnet接続したときのキャプチャは次の通りです。
[root@Serve1 ~]# tshark -i lo -i eth1 -n ... 1 0.000000000 192.168.60.10 → 192.168.60.20 SSH 166 Client: Encrypted packet (len=100) 2 0.000146179 192.168.60.20 → 192.168.60.30 TCP 74 37244 → 110 [SYN] Seq=0 Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=33210704 TSecr=0 WS=128 3 0.000428286 192.168.60.30 → 192.168.60.20 TCP 74 110 → 37244 [SYN, ACK] Seq=0 Ack=1 Win=28960 Len=0 MSS=1460 SACK_PERM=1 TSval=1011476475 TSecr=33210704 WS=128 4 0.000444294 192.168.60.20 → 192.168.60.30 TCP 66 37244 → 110 [ACK] Seq=1 Ack=1 Win=29312 Len=0 TSval=33210705 TSecr=1011476475 5 0.000540260 192.168.60.20 → 192.168.60.10 SSH 118 Server: Encrypted packet (len=52) 6 0.000791571 192.168.60.10 → 192.168.60.20 TCP 66 56474 → 22 [ACK] Seq=101 Ack=53 Win=278 Len=0 TSval=1792724337 TSecr=3104266180 7 0.009096094 192.168.60.30 → 192.168.60.20 POP 86 S: +OK Dovecot ready. 8 0.009122311 192.168.60.20 → 192.168.60.30 TCP 66 37244 → 110 [ACK] Seq=1 Ack=21 Win=29312 Len=0 TSval=33210713 TSecr=1011476484 9 0.009349972 192.168.60.20 → 192.168.60.10 SSH 134 Server: Encrypted packet (len=68) 10 0.009725835 192.168.60.10 → 192.168.60.20 TCP 66 56474 → 22 [ACK] Seq=101 Ack=121 Win=278 Len=0 TSval=1792724346 TSecr=3104266188 ^C10 packets captured
Server1(192.168.60.20のポート37224番)とServer2(192.168.60.30のポート110番)の接続が確立され、この接続を使って通信が行われていること、およびclientとServer1の間ではSSH接続を使って通信していることがわかります。
Server2でも同じようにみてみます。TCPソケットの状態は次の通りです。
[root@Server2 ~]# ss -atn State Recv-Q Send-Q Local Address:Port Peer Address:Port Process LISTEN 0 100 0.0.0.0:110 0.0.0.0:* LISTEN 0 128 0.0.0.0:22 0.0.0.0:* ... ESTAB 0 0 192.168.60.30:110 192.168.60.20:37244 ...
Server2でキャプチャした内容は次の通りです。
[root@Server2 ~]# tshark -i lo -i eth1 -n ... 1 0.000000000 192.168.60.20 → 192.168.60.30 TCP 74 37244 → 110 [SYN] Seq=0 Win=29200 Len=0 MSS=1460 SACK_PERM=1 TSval=33210704 TSecr=0 WS=128 2 0.000029528 192.168.60.30 → 192.168.60.20 TCP 74 110 → 37244 [SYN, ACK] Seq=0 Ack=1 Win=28960 Len=0 MSS=1460 SACK_PERM=1 TSval=1011476475 TSecr=33210704 WS=128 3 0.000227995 192.168.60.20 → 192.168.60.30 TCP 66 37244 → 110 [ACK] Seq=1 Ack=1 Win=29312 Len=0 TSval=33210705 TSecr=1011476475 4 0.008548826 192.168.60.30 → 192.168.60.20 POP 86 S: +OK Dovecot ready. 5 0.009014309 192.168.60.20 → 192.168.60.30 TCP 66 37244 → 110 [ACK] Seq=1 Ack=21 Win=29312 Len=0 TSval=33210713 TSecr=1011476484 ^C5 packets captured
Server1(192.168.60.20のポート37224番)とServer2(192.168.60.30のポート110番)の接続が確立され、この接続を使ってServer1と通信していることがわかります。
ケース2の検証
ケース2では仮想環境に2台のLinuxマシンを用意します。
- ローカルマシン用のLinuxマシン
-
- ホスト名:client
- IPアドレス:192.168.60.10
- 用途:SSHクライアント用
- SSHサーバー用のLinuxマシン
-
- ホスト名:Server1
- IPアドレス:192.168.60.20
- 用途:SSHサーバーとPOPサーバが稼働するサーバー
SSHポートフォワーディングのため以下のコマンドを実行します。
# ssh -f -N -L 50110:localhost:110 taro@192.168.60.20
このときのclientのTCPソケットの状態を確認します。
[root@client ~]# ss -atn State Recv-Q Send-Q Local Address:Port Peer Address:Port Process ... LISTEN 0 128 127.0.0.1:50110 0.0.0.0:* ESTAB 0 0 192.168.60.10:56424 192.168.60.20:22 ...
「127.0.0.1:50110」がLISTENで、Server1(192.168.60.20)とSSH接続が確立されています。
clientのポート50110番にTelnetで接続します。Server1のPOPサーバーと接続されました。
[root@client ~]# telnet 127.0.0.1 50110 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. +OK Dovecot ready.
POPサーバーと接続された状態のclientのソケットを確認します。
[root@client ~]# ss -atn State Recv-Q Send-Q Local Address:Port Peer Address:Port Process ... LISTEN 0 128 127.0.0.1:50110 0.0.0.0:* ESTAB 0 0 127.0.0.1:39118 127.0.0.1:50110 ESTAB 0 0 192.168.60.10:56424 192.168.60.20:22 ESTAB 0 0 127.0.0.1:50110 127.0.0.1:39118 ...
「127.0.0.1:39118」と「127.0.0.1:50110」の間の接続と、SSH接続が確立されています。
Telnet接続したときの通信をclientでキャプチャした結果は次の通りです。
[root@client ~]# tshark -i lo -i eth1 -n ... 1 0.000000000 192.168.60.10 → 192.168.60.20 SSH 166 Client: Encrypted packet (len=100) 2 0.000614701 192.168.60.20 → 192.168.60.10 SSH 118 Server: Encrypted packet (len=52) 3 0.000624958 192.168.60.10 → 192.168.60.20 TCP 66 56424 → 22 [ACK] Seq=101 Ack=53 Win=278 Len=0 TSval=1789555944 TSecr=3101097788 4 -0.000129537 127.0.0.1 → 127.0.0.1 TCP 74 39118 → 50110 [SYN] Seq=0 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=1162379331 TSecr=0 WS=128 5 -0.000122340 127.0.0.1 → 127.0.0.1 TCP 74 50110 → 39118 [SYN, ACK] Seq=0 Ack=1 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=1162379331 TSecr=1162379331 WS=128 6 -0.000114875 127.0.0.1 → 127.0.0.1 TCP 66 39118 → 50110 [ACK] Seq=1 Ack=1 Win=43776 Len=0 TSval=1162379331 TSecr=1162379331 7 0.009258142 127.0.0.1 → 127.0.0.1 TCP 86 50110 → 39118 [PSH, ACK] Seq=1 Ack=1 Win=43776 Len=20 TSval=1162379341 TSecr=1162379331 8 0.009267739 127.0.0.1 → 127.0.0.1 TCP 66 39118 → 50110 [ACK] Seq=1 Ack=21 Win=43776 Len=0 TSval=1162379341 TSecr=1162379341 9 0.009147352 192.168.60.20 → 192.168.60.10 SSH 134 Server: Encrypted packet (len=68) 10 0.009165047 192.168.60.10 → 192.168.60.20 TCP 66 56424 → 22 [ACK] Seq=101 Ack=121 Win=278 Len=0 TSval=1789555952 TSecr=3101097796 ^C10 packets captured
「127.0.0.1:39118」と「127.0.0.1:50110」で接続が確立され、client(192.168.60.10)とServer1(192.168.60.20)の間ではSSH接続でパケットがやり取りされていることが見てとれます。
このときServer1のTCPソケットは次の通りです。
[root@Serve1 ~]# ss -atn State Recv-Q Send-Q Local Address:Port Peer Address:Port Process LISTEN 0 100 0.0.0.0:110 0.0.0.0:* LISTEN 0 128 0.0.0.0:22 0.0.0.0:* ... ESTAB 0 0 127.0.0.1:110 127.0.0.1:32800 ESTAB 0 0 192.168.60.20:22 192.168.60.10:56424 ESTAB 0 0 127.0.0.1:32800 127.0.0.1:110 ...
Server1の内部(127.0.0.1)でポート32800番とポート110番ポートの接続が確立されたことがわかります。
Server1でキャプチャした結果は次の通り。
[root@Serve1 ~]# tshark -i lo -i eth1 -n ... 1 0.000000000 127.0.0.1 → 127.0.0.1 TCP 74 32800 → 110 [SYN] Seq=0 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=1648130125 TSecr=0 WS=128 2 0.000034110 127.0.0.1 → 127.0.0.1 TCP 74 110 → 32800 [SYN, ACK] Seq=0 Ack=1 Win=43690 Len=0 MSS=65495 SACK_PERM=1 TSval=1648130125 TSecr=1648130125 WS=128 3 0.000047073 127.0.0.1 → 127.0.0.1 TCP 66 32800 → 110 [ACK] Seq=1 Ack=1 Win=43776 Len=0 TSval=1648130125 TSecr=1648130125 4 -0.000166854 192.168.60.10 → 192.168.60.20 SSH 166 Client: Encrypted packet (len=100) 5 0.000081507 192.168.60.20 → 192.168.60.10 SSH 118 Server: Encrypted packet (len=52) 6 0.000445111 192.168.60.10 → 192.168.60.20 TCP 66 56424 → 22 [ACK] Seq=101 Ack=53 Win=278 Len=0 TSval=1789555944 TSecr=3101097788 7 0.008540671 127.0.0.1 → 127.0.0.1 POP 86 S: +OK Dovecot ready. 8 0.008632949 192.168.60.20 → 192.168.60.10 SSH 134 Server: Encrypted packet (len=68) 9 0.008855826 192.168.60.10 → 192.168.60.20 TCP 66 56424 → 22 [ACK] Seq=101 Ack=121 Win=278 Len=0 TSval=1789555952 TSecr=3101097796 10 0.008555632 127.0.0.1 → 127.0.0.1 TCP 66 32800 → 110 [ACK] Seq=1 Ack=21 Win=43776 Len=0 TSval=1648130133 TSecr=1648130133 ^C10 packets captured
「127.0.0.1:32800」と「127.0.0.1:110」で接続が確立され、SSH接続でもパケットがやり取りされていることが見てとれます。
ループバックアドレスを指定と自身のIPアドレスを指定の違い
先程は転送先とホスト名としてlocalhostを指定しました。次のように転送先に実際のIPアドレス(192.168.60.20)を使ったときとの違いも見てみましょう。
[root@client ~]# ssh -f -N -L 50110:192.168.60.20:110 taro@192.168.60.20
結論としてはServer1内部のPOPサーバーとの接続に使用されるIPが異なります。
Telnetで接続したときのServer1のTCPソケットを見てみましょう。
[root@Serve1 ~]# ss -atn State Recv-Q Send-Q Local Address:Port Peer Address:Port Process ... ESTAB 0 0 192.168.60.20:22 192.168.60.10:56470 ESTAB 0 0 192.168.60.20:110 192.168.60.20:43804 ESTAB 0 0 192.168.60.20:43804 192.168.60.20:110 ...
localhost(127.0.0.1)を指定した場合、「127.0.0.1:32800」と「127.0.0.1:110」のようにループバックアドレス間で接続が確立されましたが、実IPアドレスを指定した場合は「192.168.60.20:43804」と「192.168.60.20:110のように実IPアドレス間で接続が確立されています。
このように接続に使われるIPアドレスが異なるのでServer1内部での経路が全く同じになるというわけではありません。
以上、検証はここまでとなります。ここが本当の最後になります。最後まで読んでいただきありがとうございます。