ssh port forwarding

ssh でのポートフォワーディングを勉強したのでまとめます。正確には TCP ポートフォワーディングと言うらしい。以下、単にポートフォワーディングと記載します。

記述に間違い等ありましたらご指摘頂ければ有難いです。

※以降、文体変わります

概要

ポートフォワーディングとは、確立している ssh 接続をトンネルとして利用し、任意の通信をトンネルを経由させて転送すること。転送先ネットワークやサーバとは、透過的な通信が可能となる。

基礎概念

概念的には、マシンAのポートをトンネルの入り口とし、出口をマシンBのポートにするという絵をイメージするとよい。まずは難しく考えず、この絵だけを想像しておく。トンネルの中を通信が通過する。

マシンAとマシンBに ssh で接続を作っておく。これがトンネルになる。トンネルには、予め入り口と出口、ならびに出口の先の終着点をを設定する。これはトンネル構築時に行う(基本的には。応用は後述)。

出口はマシンBのポートなので、マシンBの環境から接続できる先が終着点となる。仮にマシンCとする。終着点には当然ながらポートも指定する。

マシンAのポート 9999 を入り口とし、マシンBが出口。マシンCのポート10000 が終着点としてトンネルを構築。するとマシンAの9999に接続するとマシンCのポートにつながることになる。

マシンAとマシンBの間は ssh の機能により暗号化され安全になる。ただし、マシンBとマシンCの間は暗号化されないし、マシンAに到達するまでの通信も暗号化されない。

以下では OpenSSH 4.3p2 での利用法をを主に示す。

利用方法

のっけから余談だが、以下の例では一部に -C オプションをつけた状態でコマンド例を記載する。-Cオプションの意味は後述。例を読む上では無視してよい。

例1 ローカルからのポートフォワーディング
  ssh -C -L [bind_address:]in_port:goal_host:goal_port [user@]out_host

-L オプションがローカルからのポートフォワーディングの指定。

このコマンドを実行したホストが入り口となる。先の例ではマシンA。

in_port が入り口ポート。bind_addressを指定した場合、そのアドレスで listenする。デフォルトは127.0.0.1

out_host がトンネルの出口になるマシン。先の例ではマシンBに相当する。

goal_host と goal_port が終着点。先の例では マシンC。

よって、この例では bind_address:in_port に接続すると、その通信は暗号化された状態で out_host へ転送され、復号されたうえで out_host からgoal_host:goal_port に渡される。bind_address:in_port に接続した側から見れば、最初から goal_host:goal_port に接続したのと同等の効果を得られる。

例2 リモートからのポートフォワーディング
  ssh -C -R [bind_address:]in_port:goal_host:goal_port [user@]in_host

-R オプションがリモートからのポートフォワーディングの指定。

このコマンドを実行したホストが出口となる。

少々ややこしいが、このコマンドを実行する側から見れば in_host がリモートホストである。in_host で in_port が listen される。つまり、リモートホストが入り口 ― ホストA になる。

出口であるホストBはこのコマンドを打ったマシンとなる。

終着点は変わらず goal_host:goal_port である。

この場合の bind_address がどのような挙動を示すかは確認できなかった。

例3 ローカルからのダイナミックポートフォワーディング
  ssh -D [bind_address:]port [user@]remote_host

goal_port:goal_host を指定しない形式。このコマンドを打ったマシンが入り口となる。

終着点を指定せず、アプリケーションレベルで終着点を動的に変化させられる。プロトコルは SOCKS4、SOCKS5 を利用できる。

挙動は未検証。

制限方法

(2008/09/02 追記:現在のところ、決め手となる対策が見当たらないようだ)

sshd_config の AllowTcpForwarding

ポートフォワーディングされる事を制限するには /etc/ssh/sshd_config のAllowTcpForwarding パラメータを no に設定する。

  AllowTcpForwarding no

検証した結果、この設定の挙動には幾つかのパターンがある。尚、手元に環境がなかったため、入り口側ホストと出口側ホストの OpenSSH のバージョンに差異がある状態での確認となっている。

- 入口側ホストのAllowTcpForwarding 出口側ホストのAllowTcpForwarding 結果
ローカルからのポートフォワーディング yes no 出口から終着点への接続が拒否される
no yes 制限されない
リモートからのポートフォワーディング yes no 制限されない
no yes ポートの Listen が制限される

確認した限り直感的な挙動ではないし、統一的に制限できる訳でもないようだ。

さらに man にも書いてあるが、シェルアクセスできる以上、そのユーザは転送のためのプログラムを配置できるので完全な制約とはならない。

要塞化

結局はこの対策になる(2008/09/02 追記:これも決め手ではない)。具体的には、(1)余分なポートを Listen しない、(2)不要な新規コネクションの作成を許さない、である。

iptables 等のポリシーを INPUT、OUTPUT 共に DROPREJECT にして、必要な接続のみ許可するように構成する。 --state モジュールなどを利用すると設定しやすい。

その他

  • -C オプションを併用すれば通信が圧縮されるのでトラフィックを節約できる。