opensshでフォワーディングを用いる際、それぞれのポートをlistenするインターフェースをどのように選択できるか調べた。

ローカル/リモートフォワードの listen対象

ローカルフォワードでのlisten対象インターフェース

~/.ssh/configのLocalForward(あるいは -L オプション)でソース側のアドレスを用いて インターフェースを指定でき、に対応するインターフェースがlistenされる。

LocalForward [bind_address:] port:host:hostport

例: RDPを飛ばす際、lo とeth2(家庭内LAN用、192.168.0.1)だけをリスンし、インターネット接続のeth1は無視したい

LocalForward    127.0.0.1:3389   <リモート>:3389
LocalForward    192.168.0.1:3389   <リモート>:3389

例:すべてのインターフェースを使う: LocalForward *:3389 <リモート>:3389

また~/.ssh/configのGatewayPorts=yes か、コマンドラインオプション -g で、全てのローカルフォワードについて全てのインターフェースをListenする。

リモートフォワードでのlisten対象

サーバーの設定(sshd_config)でのGatewayPorts={yes|no|clientspecified}は、 リモートフォワードでlistenするインターフェースの選択方法 を制御する。 yes/noは、とにかく全てのインターフェースをlisten/非listen する。clientspecifiedでは、クライアントがインターフェースを選択できる。

様々な指定方法でのlistenテスト

設定を色々変えて、リモート側のポートlisten状態を調べた結果。チェックには ss -tln を使用した。

変更した項目

  • /etc/sshd_configでの GatewayPorts= 指定 (yes , no , clientspecified)
  • sshコマンドラインオプションや .ssh/configでの、リモートのバインドアドレス指定

    RemoteForward [:] :

結果

サーバーGatewayPorts リモートフォワードのリモート部分書式 リモートでlistenされるインターフェース
yes *:<port> (全インターフェース)
yes <addr>:<port> (全インターフェース)
yes <port> (全インターフェース)
no(デフォルト) *:<port> lo
no(デフォルト) <addr>:<port> lo
no(デフォルト) <port> lo
clientspecified *:<port> * (全インターフェース)
clientspecified <addr>:<port> <addrに対応するインターフェース>
clientspecified <port> lo
  • サーバー設定の GatewayPorts =yes と no は、クライアントのリモートポート指定に関わらず、かつエラーも出さず、とにかく全インターフェースをlistenするか、又はloのみのlistenを強制的に行う。

  • 接続やポート毎に異なる扱いをするためには、GatewayPorts=clientspecified を用いる必要がある。

    • 例えば接続先の自宅サーバーからのリモートフォワードの場合、サーバーの lo とLAN側インターフェースの2つだけlistenし、インターネット接続のインターフェース(グローバルIP)は無視させたい場合、loとLAN用インターフェースの2つを書く。 RemoteForward 192.168.0.1:3389 <手元LAN上のWinマシン>:3389 RemoteForward 127.0.0.1:3389 <手元LAN上のWinマシン>:3389

注意点

  • 上のテストでは1つのリモートフォワードだけ調べたが、実際使用するとき、サーバー設定でGatewayPorts=yesにすると、全接続の全フォワーディングが、全インターフェースlistenになることに注意。大胆な使い方だ。
  • うまくいかないときは、以下をチェックしてみる。
    • そのインターフェース:ポートが、既にバインドされている。→ ssh接続時にエラーが表示されるはず。
    • ファイヤウォールなどでブロックされている。