4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SSH ポート転送時のSSH Connection Protocolについて

Posted at

SSH ポート転送(Port Forwarding)の実行時、プロトコル的に何が起こっているか気になったので調べた。

いわゆるリモートポート転送(-Rオプション)は調べていません!

RFC 4254 SSH Connection Protocolとは

こちら

SSH Transport Layerの上で動作するプロトコルで、インタラクティブなセッション、コマンド実行、ポート転送などを提供します。

今回、注目するのはTCP/IP Port Forwardingの部分です。

ポート転送時、このプロトコルがどのように使われているのか確認してみます。

確認方法

OpenSSHを使います。

以下の環境を用意しました。
image.png

  • ローカルサーバ 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ログからメッセージのやりとりを探ってみます。

/etc/ssh/sshd_config
# 抜粋。デバッグログを出力。
# LogLevel INFO
LogLevel DEBUG3

同様にローカルサーバのSSHクライアントも-vvvオプションをつけてみます。

$ ssh -vvv -N -L 10022:192.168.122.111:22 192.168.122.61

また、ローカルサーバには確認のためlsofをインストールしました。

-L ローカルポート転送

ローカルサーバのTCPポートへの通信を接続先サーバのポートへ転送します。

-L
# -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.comno-more-sessions@openssh.com

/var/log/secure(リモートサーバ)
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を外して詳しく確認してみます。

/var/log/secure
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 channelinitial window sizemaximum packet size に対応していそうです。
(別の値を受信して、同意できるものを表示している可能性もありますが…)

3行目、originator 127.0.0.1 port 53988はそのままoriginator IP addressoriginator portに、
残ったtarget 192.168.122.111 port 22host to connectport to connectに対応していると思われます。

大体のメッセージがわかったところで各パラメータについて考えます。

  • sender channelはクライアントが割り当てるチャンネル番号と一致します。
    • クライアントのログで確認できます。
  • initial window sizemaximum packet sizeはわかりません。クライアントのデフォルト?
  • host to connectport to connectは転送先のアドレス、ポートと一致します。

最後にoriginator IP addressoriginator 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 channel3に、originator port53900になったようです。

やはり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 connectport to connectは転送先ホスト、ポートです。
    • originator IP addressoriginator portは転送元ポートへの接続元アドレス、ポートです。
  • TCP接続が終了すると、SSH_MSG_CHANNEL_EOFSSH_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の通信は以下のようになっています。
image.png

SOCKSの認証についてやり取りした後、接続元からConnectが送信されます。
ここでRemote Address192.168.122.111Port80です。
また、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について詳しく確認します。

/var/log/secure
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 connectport to connectはSOCKS ConnectのRemote AddressPort
  • originator IP addressoriginator 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>

image.png
SOCKSのConnectでRemote Address192.168.122.111Port8000です。
接続元ポートは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 connectport to connectはSOCKS Connectで指定したリモートアドレス、ポートです。
    • originator IP addressoriginator portはSOCKS接続時の接続元アドレス、ポートです。

終わりに

SSHトンネル、なんて言い方をするので恒久的なイメージだったが、
TCP接続に合わせてChannelを作っていることがわかった。

4
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?