SSH ポート転送(Port Forwarding)の実行時、プロトコル的に何が起こっているか気になったので調べた。
いわゆるリモートポート転送(-R
オプション)は調べていません!
RFC 4254 SSH Connection Protocolとは
SSH Transport Layerの上で動作するプロトコルで、インタラクティブなセッション、コマンド実行、ポート転送などを提供します。
今回、注目するのはTCP/IP Port Forwarding
の部分です。
ポート転送時、このプロトコルがどのように使われているのか確認してみます。
確認方法
OpenSSHを使います。
- ローカルサーバ
192.168.122.80
- CentOS 7.8.2003 / OpenSSH 7.4p1
- リモートサーバ
192.168.122.61
- CentOS 7.8.2003 / OpenSSH 7.4p1
- 接続先サーバ
192.168.122.111
ローカルサーバからリモートサーバにSSHで接続し、
ポート転送で接続先サーバへTCP通信してみます。
本来はSSHのメッセージをキャプチャしたいのですが、SSHを復号する方法がわからない…ため、
リモートサーバのsshdログからメッセージのやりとりを探ってみます。
# 抜粋。デバッグログを出力。
# LogLevel INFO
LogLevel DEBUG3
同様にローカルサーバのSSHクライアントも-vvv
オプションをつけてみます。
$ ssh -vvv -N -L 10022:192.168.122.111:22 192.168.122.61
また、ローカルサーバには確認のためlsofをインストールしました。
-L
ローカルポート転送
ローカルサーバのTCPポートへの通信を接続先サーバのポートへ転送します。
# -Nはログイン先で何もしない
[testing@local ~]$ ssh -vvv -N -L 10022:192.168.122.111:22 192.168.122.61
SSH開始時
上のコマンドを実行したタイミングで、sshdのログ(/var/log/secure)を見てみます。
$ sudo tailf /var/log/secure | grep packet:
<略>
Sep 6 12:03:18 remote sshd[8365]: debug3: send packet: type 20 [preauth]
Sep 6 12:03:18 remote sshd[8365]: debug3: receive packet: type 20 [preauth]
Sep 6 12:03:18 remote sshd[8365]: debug3: receive packet: type 30 [preauth]
Sep 6 12:03:18 remote sshd[8365]: debug3: send packet: type 31 [preauth]
Sep 6 12:03:18 remote sshd[8365]: debug3: send packet: type 21 [preauth]
Sep 6 12:03:18 remote sshd[8365]: debug3: send packet: type 7 [preauth]
Sep 6 12:03:18 remote sshd[8365]: debug3: receive packet: type 21 [preauth]
Sep 6 12:03:18 remote sshd[8365]: debug3: receive packet: type 5 [preauth]
Sep 6 12:03:18 remote sshd[8365]: debug3: send packet: type 6 [preauth]
Sep 6 12:03:18 remote sshd[8365]: debug3: receive packet: type 50 [preauth]
Sep 6 12:03:18 remote sshd[8365]: debug3: send packet: type 51 [preauth]
Sep 6 12:03:18 remote sshd[8365]: debug3: receive packet: type 50 [preauth]
Sep 6 12:03:18 remote sshd[8365]: debug3: send packet: type 60 [preauth]
Sep 6 12:03:18 remote sshd[8365]: debug3: receive packet: type 50 [preauth]
Sep 6 12:03:18 remote sshd[8365]: debug3: send packet: type 52 [preauth]
Sep 6 12:03:18 remote sshd[8368]: debug3: send packet: type 80
Sep 6 12:03:18 remote sshd[8368]: debug3: receive packet: type 80
receiveしたpacketのtypeに注目すると、type 90 (SSH_MSG_CHANNEL_OPEN)
がありません。
(typeの一覧はrfc4250に記載があります。)
また、最後のtype 80 (SSH_MSG_GLOBAL_REQUEST)
は、
ログを確認すると、どちらもOpenSSHの拡張機能であることがわかります。
(こちら。hostkeys-00@openssh.com
とno-more-sessions@openssh.com
)
Sep 6 12:03:18 remote sshd[8368]: debug3: notify_hostkeys: sent 3 hostkeys
Sep 6 12:03:18 remote sshd[8368]: debug3: send packet: type 80
<略>
Sep 6 12:03:18 remote sshd[8368]: debug3: receive packet: type 80
Sep 6 12:03:18 remote sshd[8368]: debug1: server_input_global_request: rtype no-more-sessions@openssh.com want_reply 0
debug1: Requesting no-more-sessions@openssh.com
debug3: send packet: type 80
debug1: Entering interactive session.
debug1: pledge: network
debug3: receive packet: type 80
debug1: client_input_global_request: rtype hostkeys-00@openssh.com want_reply 0
このように、この段階ではポート転送に関わるメッセージは送信されていません。
転送元ポート接続時
次に、ローカルサーバで転送元ポートに接続してみます。
[testing@local ~]$ ssh -p 10022 127.0.0.1
testing@127.0.0.1's password:
ここでsshdのログを確認すると、type 90 (SSH_MSG_CHANNEL_OPEN)
を受信し、type 91 (SSH_MSG_CHANNEL_OPEN_CONFIRMATION)
を送信しています。
[testing@remote ~]$ sudo tailf /var/log/secure | grep packet:
<略>
Sep 6 12:03:45 remote sshd[8368]: debug3: receive packet: type 90
Sep 6 12:03:45 remote sshd[8368]: debug3: send packet: type 91
grepを外して詳しく確認してみます。
Sep 6 12:03:45 remote sshd[8368]: debug3: receive packet: type 90
Sep 6 12:03:45 remote sshd[8368]: debug1: server_input_channel_open: ctype direct-tcpip rchan 2 win 2097152 max 32768
Sep 6 12:03:45 remote sshd[8368]: debug1: server_request_direct_tcpip: originator 127.0.0.1 port 53988, target 192.168.122.111 port 22
Sep 6 12:03:45 remote sshd[8368]: debug2: fd 8 setting O_NONBLOCK
Sep 6 12:03:45 remote sshd[8368]: debug2: fd 8 setting TCP_NODELAY
Sep 6 12:03:45 remote sshd[8368]: debug1: connect_next: host 192.168.122.111 ([192.168.122.111]:22) in progress, fd=8
Sep 6 12:03:45 remote sshd[8368]: debug3: fd 8 is O_NONBLOCK
Sep 6 12:03:45 remote sshd[8368]: debug3: fd 8 is O_NONBLOCK
Sep 6 12:03:45 remote sshd[8368]: debug1: channel 0: new [direct-tcpip]
Sep 6 12:03:45 remote sshd[8368]: debug1: server_input_channel_open: confirm direct-tcpip
Sep 6 12:03:45 remote sshd[8368]: debug3: channel 0: waiting for connection
Sep 6 12:03:45 remote sshd[8368]: debug1: channel 0: connected to 192.168.122.111 port 22
Sep 6 12:03:45 remote sshd[8368]: debug3: send packet: type 91
唐突ですが、今回受信したメッセージのフォーマットは以下だと考えられます。
(rfcより抜粋)
byte SSH_MSG_CHANNEL_OPEN
string "direct-tcpip"
uint32 sender channel
uint32 initial window size
uint32 maximum packet size
string host to connect
uint32 port to connect
string originator IP address
uint32 originator port
ログの2行目と3行目に注目して、受信したメッセージを推測してみます。
2行目、rchan 2 win 2097152 max 32768
部分は、sender channel
、initial window size
、maximum packet size
に対応していそうです。
(別の値を受信して、同意できるものを表示している可能性もありますが…)
3行目、originator 127.0.0.1 port 53988
はそのままoriginator IP address
とoriginator port
に、
残ったtarget 192.168.122.111 port 22
はhost to connect
とport to connect
に対応していると思われます。
大体のメッセージがわかったところで各パラメータについて考えます。
-
sender channel
はクライアントが割り当てるチャンネル番号と一致します。- クライアントのログで確認できます。
-
initial window size
、maximum packet size
はわかりません。クライアントのデフォルト? -
host to connect
、port to connect
は転送先のアドレス、ポートと一致します。
最後にoriginator IP address
、originator port
です。
転送元ポートへの接続時、接続元ポートに何を使っているか確認します。
[testing@local ~]$ lsof -i
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
<略>
ssh 4585 testing 6u IPv4 37972 0t0 TCP localhost:10022->localhost:53988 (ESTABLISHED)
ssh 4586 testing 3u IPv4 37971 0t0 TCP localhost:53988->localhost:10022 (ESTABLISHED)
53988を使っています。これはログのoriginator port
部分と一致します。
せっかくなので、さらに転送元ポートへ接続してみます。
sshdログで新しいメッセージの受信を確認できます。
Sep 6 12:09:57 remote sshd[8368]: debug3: receive packet: type 90
Sep 6 12:09:57 remote sshd[8368]: debug1: server_input_channel_open: ctype direct-tcpip rchan 3 win 2097152 max 3
2768
Sep 6 12:09:57 remote sshd[8368]: debug1: server_request_direct_tcpip: originator 127.0.0.1 port 53990, target 19
2.168.122.111 port 22
前回と比べると、sender channel
が3
に、originator port
が53900
になったようです。
やはりoriginator port
は接続元ポートと一致します。
$ lsof -i
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
<略>
ssh 4585 testing 7u IPv4 38063 0t0 TCP localhost:10022->localhost:53990 (ESTABLISHED)
ssh 4591 testing 3u IPv4 38062 0t0 TCP localhost:53990->localhost:10022 (ESTABLISHED)
切断時
転送元ポートへのTCP接続を切断してみます。
SSHをCtrl+C
で中断します
すると、type 96 (SSH_MSG_CHANNEL_EOF)
とtype 97 (SSH_MSG_CHANNEL_CLOSE)
が送受信されます。
[testing@remote ~]$ sudo tailf /var/log/secure | grep packet:
<略>
Sep 6 12:29:29 remote sshd[8368]: debug3: receive packet: type 96
Sep 6 12:29:29 remote sshd[8368]: debug3: send packet: type 96
Sep 6 12:29:29 remote sshd[8368]: debug3: send packet: type 97
Sep 6 12:29:29 remote sshd[8368]: debug3: receive packet: type 97
ローカルポート転送まとめ
-
SSH_MSG_CHANNEL_OPEN
が送信されるのは、SSH接続時ではなく、転送元ポートへのTCP接続時です。 - メッセージのパラメータは以下になります。
-
sender channel
はクライアントのチャンネル番号です。 -
host to connect
、port to connect
は転送先ホスト、ポートです。 -
originator IP address
、originator port
は転送元ポートへの接続元アドレス、ポートです。
-
- TCP接続が終了すると、
SSH_MSG_CHANNEL_EOF
、SSH_MSG_CHANNEL_CLOSE
が送受信されます。
-D
Dynamicポート転送
ローカルサーバのTCPポートを指定すると、そのポートへの接続時SOCKSサーバとして動作します。
[testing@local ~]$ ssh -vvv -N -D 1080 192.168.122.61
SSH開始時
ローカルポート転送と同様、ポート転送に関わる通信は発生しないようです。
(最後のtype 80
についても同様です。)
Sep 6 13:06:29 remote sshd[9061]: debug3: send packet: type 20 [preauth]
Sep 6 13:06:29 remote sshd[9061]: debug3: receive packet: type 20 [preauth]
Sep 6 13:06:30 remote sshd[9061]: debug3: receive packet: type 30 [preauth]
Sep 6 13:06:30 remote sshd[9061]: debug3: send packet: type 31 [preauth]
Sep 6 13:06:30 remote sshd[9061]: debug3: send packet: type 21 [preauth]
Sep 6 13:06:30 remote sshd[9061]: debug3: send packet: type 7 [preauth]
Sep 6 13:06:30 remote sshd[9061]: debug3: receive packet: type 21 [preauth]
Sep 6 13:06:30 remote sshd[9061]: debug3: receive packet: type 5 [preauth]
Sep 6 13:06:30 remote sshd[9061]: debug3: send packet: type 6 [preauth]
Sep 6 13:06:30 remote sshd[9061]: debug3: receive packet: type 50 [preauth]
Sep 6 13:06:30 remote sshd[9061]: debug3: send packet: type 51 [preauth]
Sep 6 13:06:30 remote sshd[9061]: debug3: receive packet: type 50 [preauth]
Sep 6 13:06:30 remote sshd[9061]: debug3: send packet: type 60 [preauth]
Sep 6 13:06:30 remote sshd[9061]: debug3: receive packet: type 50 [preauth]
Sep 6 13:06:30 remote sshd[9061]: debug3: send packet: type 52 [preauth]
Sep 6 13:06:30 remote sshd[9064]: debug3: send packet: type 80
Sep 6 13:06:30 remote sshd[9064]: debug3: receive packet: type 80
SOCKS接続時
SOCKS経由でのHTTP接続をやってみます。
[testing@local ~]$ curl -i --SOCKS5 127.0.0.1:1080 http://192.168.122.111:80
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.8.2
Date: Sun, 06 Sep 2020 17:07:46 GMT
Content-type: text/html; charset=utf-8
Content-Length: 843
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
SOCKSの認証についてやり取りした後、接続元からConnect
が送信されます。
ここでRemote Address
は192.168.122.111
、Port
は80
です。
また、SOCKSの通信と後に続くHTTP通信は、どちらも接続元ポート39884
を使っています。
ここで、sshdのログを確認します。
[testing@remote ~]$ sudo tailf /var/log/secure | grep packet:
<略>
Sep 6 13:07:46 remote sshd[9064]: debug3: receive packet: type 90
Sep 6 13:07:46 remote sshd[9064]: debug3: send packet: type 91
Sep 6 13:07:46 remote sshd[9064]: debug3: send packet: type 96
Sep 6 13:07:46 remote sshd[9064]: debug3: receive packet: type 96
Sep 6 13:07:46 remote sshd[9064]: debug3: receive packet: type 97
Sep 6 13:07:46 remote sshd[9064]: debug3: send packet: type 97
type 90
について詳しく確認します。
Sep 6 13:07:46 remote sshd[9064]: debug3: receive packet: type 90
Sep 6 13:07:46 remote sshd[9064]: debug1: server_input_channel_open: ctype direct-tcpip rchan 2 win 2097152 max 32768
Sep 6 13:07:46 remote sshd[9064]: debug1: server_request_direct_tcpip: originator 127.0.0.1 port 39884, target 192.168.122.111 port 80
見たところ、
-
host to connect
、port to connect
はSOCKS ConnectのRemote Address
、Port
-
originator IP address
、originator port
はSOCKSの接続元アドレス、ポート
なっていそうです。
今度は別のポートで待ち受けるHTTPサーバに接続してみます。
[testing@local ~]$ curl -i --SOCKS5 127.0.0.1:1080 http://192.168.122.111:8000
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.8.2
Date: Sun, 06 Sep 2020 17:13:53 GMT
Content-type: text/html; charset=utf-8
Content-Length: 843
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
SOCKSのConnectでRemote Address
は192.168.122.111
、Port
は8000
です。
接続元ポートは39886
を使っています。
同様に確認します。
Sep 6 13:13:53 remote sshd[9064]: debug3: receive packet: type 90
Sep 6 13:13:53 remote sshd[9064]: debug3: send packet: type 91
Sep 6 13:13:53 remote sshd[9064]: debug3: send packet: type 96
Sep 6 13:13:53 remote sshd[9064]: debug3: receive packet: type 96
Sep 6 13:13:53 remote sshd[9064]: debug3: receive packet: type 97
Sep 6 13:13:53 remote sshd[9064]: debug3: send packet: type 97
Sep 6 13:13:53 remote sshd[9064]: debug3: receive packet: type 90
Sep 6 13:13:53 remote sshd[9064]: debug1: server_input_channel_open: ctype direct-tcpip rchan 2 win 2097152 max 3
2768
Sep 6 13:13:53 remote sshd[9064]: debug1: server_request_direct_tcpip: originator 127.0.0.1 port 39886, target 19
2.168.122.111 port 8000
やはり一回目と同様です。
Dynamicポート転送まとめ
- SOCKS Connectの受信後に、以下パラメータで
SSH_MSG_CHANNEL_OPEN
が送信されます。-
host to connect
、port to connect
はSOCKS Connectで指定したリモートアドレス、ポートです。 -
originator IP address
、originator port
はSOCKS接続時の接続元アドレス、ポートです。
-
終わりに
SSHトンネル、なんて言い方をするので恒久的なイメージだったが、
TCP接続に合わせてChannelを作っていることがわかった。