佐藤です。

Hiroo Ono (小野寛生) <hiroo.ono+free...@gmail.com> wrote
  in <cantk6sgbfq8abvpazrlm_kptk_hcyvm-n0waad7jtfco4_2...@mail.gmail.com>:

hi> 2015年3月31日 1:30  <zen-freebsd-us...@suzuki.que.ne.jp>:
hi> > 鈴木@葛飾区です。
hi> > なんか、最初のご質問を私読み違えてますね。。。リモートへの転送と大変な
hi> > 勘違いで。。
hi> >
hi> > fwd 127.0.0.1,2222 なのでローカルへの転送なのでnatを使うまでもなくルー
hi> > ルにマッチすれば2222に転送されるはずですね。。。
hi>
hi> 私もそう思ってはじめはコメントするのを控えたのですが、「BSDカーネルの
hi> 設計と実装」13.4節に
hi> “ホストが複数のアドレスを持っている場合、パケットはその送信先がこれら
hi> のどれかに一致すれば受け入れられる。”
hi> とありますので、127.0.0.1:2222 に届いたけれども、パケットの宛先アドレ
hi> スとポート番号を見て
hi> 192.168.11.3:22 宛のパケットという処理がされているのではないかと思いま
hi> す。
hi> ということで、パケットを書き換える NAPT が必要という指摘は正しいはずで
hi> す。
hi> よくわかっていませんが。

 このケースはローカルへの転送ですので、NAPT は必要ありません。

 次の条件

 - 127.0.0.1:2222 で listen している sshd と、
   192.168.11.3:22 で listen している sshd が、それぞれ
   同一のホスト (以降ホスト B と呼びます) に存在している

 - ホスト B の IPFW の fwd ルールで、192.168.11.3:22 宛のパケットが
   127.0.0.1:2222 に転送される

 が満たされる場合、別のホスト 192.168.11.2 (以降ホスト A と
 呼びます) からの TCP 接続要求は、次のような動作になります。

  1. 始点 192.168.11.2:E, 終点 192.168.11.3:22 の
     TCP SYN が A から B に送られる。(E は ephemeral port)

  2. IPFW がホスト B 上で 1. のパケットを 127.0.0.1:2222 に転送する。

     この時、fwd ルールは、パケットの始点・終点アドレスを書き換えないため、
     実際には 127.0.0.1:2222 に送られるにも関わらず、
     ホスト B のネットワークスタックが socket レベルで認識する
     終点アドレスは 192.168.11.3:22 のままです。

  3. 2222 ポートで listen している sshd は、パケットを受け取って
     accept(2) をコールし、接続用の新しい socket を生成する。

     この受け取ったパケットは、192.168.11.2:E -> 192.168.11.3:22 という
     始点・終点アドレスとして解釈されるため、ホスト B からの
     TCP SYN+ACK の返送は、逆転して 192.168.11.3:22 -> 192.168.11.2:E に
     なります。

     つまり、接続用の新しい socket には、ピアの始点として
     127.0.0.1:2222 ではなく、192.168.11.3:22 が設定されます。

 したがって、実際にパケットは 127.0.0.1:2222 に転送されているにも関わらず、

  - A->B 方向の通信は 192.168.11.2:E  -> 192.168.11.3:22

  - B->A 方向の通信は 192.168.11.3:22 -> 192.168.11.2:E

 というアドレスにパケットが流れているように見えることになり、
 アプリケーションからは、転送の事実は見えません。
 sshd などのデーモンは accept した時点で接続用の socket から
 通信相手のアドレス・ポート番号を取得しますが、
 得られるピアの始点・終点には、127.0.0.1:2222 が現れないということです。

 なので、2222/tcp と 22/tcp で待ち受けている 2 つの sshd がホスト B に
 あって、始点アドレスを条件に IPFW fwd で振り分けた場合、
 外からは、どちらも 22/tcp に接続しているように見えます。

 127.0.0.1:2222 で待ち受けているのに、192.168.11.3:22 から通信が
 行なわれているように動作するのは奇妙な話のように思えますが、
 listen しているアドレスと、accept の時の終点アドレスが
 異なっているという状態は、特別なものではありません。
 たとえばデーモンが INADDR_ANY (0.0.0.0) で listen している場合、
 accept して生成される通信用 socket のピア始点アドレスは
 当然ながら 0.0.0.0 ではなく、接続してきた相手のアドレスが使われます。
 それとまったく同じです。

Tetsuya Ito <chalt...@agate.plala.or.jp> wrote
  in <20150331212326.2783.a7d5a...@agate.plala.or.jp>:

ch> 結果を纏めますと、
ch>
ch> add 1001 pass tcp from any to any established
ch> add 1002 fwd 127.0.0.1,2222 tcp from not <特定IP> to me 22
ch>
ch> ⇒ 先にestablishedがある場合はNG (22/tcpに接続される)
ch>
ch> add 1001 fwd 127.0.0.1,2222 tcp from not <特定IP> to me 22
ch> add 1002 pass tcp from any to any established
ch>
ch> ⇒ 想定通り特定IP以外は2222/tcpに接続される。
ch>
ch> という結果となりました。
ch>
ch> ただ、ローカルネットの接続まで拒否されてしまうので、ちょっと困ってます。
ch> ローカルネット接続のpassルールをfwdの前に設定するしかないですかね...。

 established が先にあると、TCP の最初のパケットだけ 127.0.0.1:2222 に
 転送されて、それ以降は 192.168.11.3:22 に送られてしまうため、
 動作しないのだと思います。

 ローカルネットの接続が拒否される、というのが具体的に
 何を指しているのかがよく分からないのですが、
 たとえばホスト B 上で "ssh 127.0.0.1" とやった時に
 22/tcp ではなく 2222/tcp に接続されてしまうのが困るという話であれば、
 to me ではなく to 192.168.11.3 と fwd を限定すれば良いように思います。

-- Hiroki

Attachment: pgp0rZx7A7ZIM.pgp
Description: PGP signature

メールによる返信