綾小路龍之介の素人思考

[iptables] nat 越えの ssl/tsl 接続を ftp over ssl (ftps) で行う方法

私のページは万年工事中になる予感、うそが含まれていると思うし。nat の内側にある filezilla server (ftps) へ接続できるけど、ディレクトリ一覧が取得できないという問題の解決策をあたっている最中に見つけた面白いページ。

問題のあったネットワーク図は以下。wan 側にある ftps クライアントから nat の内側にある ftps サーバに対してログインできてもディレクトリ一覧を取得するとタイムアウトする。

+-------------+
| ftps client | --- wan ---+
+-------------+            |
                 +-----------------+
                 | iptables router |
                 +-----------------+
+-------------+            |
| ftps server | --- nat ---+
+-------------+

重要なことは、ssl/tsl を使っている以上、暗号化されたパケットの中身を iptables が見ることは出来ない (たとえどんなステートフルパケットインスペクション SPI モジュールを使っても。) ということ。暗号化を伴わない ftp の場合は、制御コネクションを流れるパケットの中に IP アドレスとポート番号が含まれるが、SPI モジュールを使うことでこれを解決できる。ただし、ftp を ssl でラップした ftps は IP アドレスとポート番号を含むパケットの中身を書き換えられないことになる。問題が起こるのは、クライアントからの pasv コマンドの戻りにサーバの IP アドレスとポート番号が含まれるということだ。

クライアント: グローバル IP、サーバ: グローバル IP の場合

まずは一番シンプルなものから。クライアントマシンとサーバマシンにグローバル IP が割り振られて、双方のマシンが NAT の後にいない場合。ネットワーク図とそれぞれのマシンの IP アドレスは以下。

+-------------+
| ftps client | ------------------------ wan ---+
+-------------+                                 |
+-------------+                                 |
| ftps server | ------------------------ wan ---+
+-------------+
各マシンの IP アドレス
CCC.ggg.ggg.gggクライアントマシンの IP (グローバル)
SSS.ppp.ppp.pppサーバマシンの IP (プライベート)
SSS.ggg.ggg.gggサーバマシン側のルータ IP (グローバル)

このときのメッセージのやり取りは以下。passive モードではサーバがポートを開けて待つ。クライアントが接続試行する IP アドレスとポート (SSS.ggg.ggg.ggg:9001) はサーバマシンのグローバルアドレスになるのでデータのやり取りが出来る。パケットのヘッダとデータ部分を書いたものが以下。9000 = 35 * 256 + 40。

CCC.ggg.ggg.ggg:9000 -> SSS.ggg.ggg.ggg:990  PASV
SSS.ggg.ggg.ggg:990  -> CCC.ggg.ggg.ggg:9000 Entering Passive Mode (SSS,ggg,ggg,ggg,35,41)

また、active モードではクライアントがポートを開けて待つ。サーバが接続試行する IP アドレスとポート (CCC.ggg.ggg.ggg:9001) はクライアントマシンのグローバルアドレスになるのでデータのやり取りが出来る。

CCC.ggg.ggg.ggg:9000 -> SSS.ggg.ggg.ggg:990  PORT CCC,ggg,ggg,ggg,35,41
SSS.ggg.ggg.ggg:990  -> CCC.ggg.ggg.ggg:9000 PORT command successful.

クライアント: グローバル IP、サーバ: プライベート IP の場合

つぎに、今回問題のあったネットワークの場合。クライアントマシンにグローバル IP が割り当てられ、サーバマシンが NAT の後にあり、プライベート IP が割り振られている場合。ネットワーク図とそれぞれのマシンの IP アドレスは以下。

+-------------+
| ftps client | ------------------------ wan ---+
+-------------+                                 |
                 +-----------------+            |
                 | iptables router | --- wan ---+
                 +-----------------+
+-------------+            |
| ftps server | --- nat ---+
+-------------+
各マシンの IP アドレス
CCC.ggg.ggg.gggクライアントマシンの IP (グローバル)
SSS.ppp.ppp.pppサーバマシンの IP (プライベート)
SSS.ggg.ggg.gggサーバマシン側のルータ IP (グローバル)

IP パケットの source と distnation IP を書き換えるのが NAT。基本的にデータグラムは書きえられない。ステートフルパケットインスペクション (SPI) モジュールを組み込むことで可能になる場合もある (nf_conntrack_ftp,ip_conntrack_ftp) が、データグラムが暗号化されている場合それも無理。passive モードではサーバがポートを開けて待つ。クライアントが接続試行する IP アドレスとポート (SSS.ppp.ppp.ppp:9001) はサーバマシンのプライベートアドレスになるのでダメ。

CCC.ggg.ggg.ggg:9000 -> SSS.ggg.ggg.ggg:990  PASV
CCC.ggg.ggg.ggg:9000 -> SSS.ppp.ppp.ppp:990  PASV
SSS.ppp.ppp.ppp:990  -> CCC.ggg.ggg.ggg:9000 Entering Passive Mode (SSS,ppp,ppp,ppp,35,41)
SSS.ggg.ggg.ggg:990  -> CCC.ggg.ggg.ggg:9000 Entering Passive Mode (SSS,ppp,ppp,ppp,35,41)

active モードではクライアントがポートを開けて待つ。サーバが接続試行する IP アドレスとポート (CCC.ggg.ggg.ggg:9001) はクライアントマシンのグローバル IP なので問題なし。

CCC.ggg.ggg.ggg:9000 -> SSS.ggg.ggg.ggg:990  PORT CCC,ggg,ggg,ggg,35,41
CCC.ggg.ggg.ggg:9000 -> SSS.ppp.ppp.ppp:990  PORT CCC,ggg,ggg,ggg,35,41
SSS.ppp.ppp.ppp:990  -> CCC.ggg.ggg.ggg:9000 PORT command successful.
SSS.ggg.ggg.ggg:990  -> CCC.ggg.ggg.ggg:9000 PORT command successful.

クライアント: プライベート IP、サーバ: グローバル IP の場合

先ほどと少し違うが、クライアントマシンが NAT の後にあり、プライベート IP が割り振られ、サーバマシンにグローバル IP が割り当てられている場合。ネットワーク図とそれぞれのマシンの IP アドレスは以下。

+-------------+
| ftps client | --- nat ---+
+-------------+            |
                 +-----------------+
                 | iptables router | --- wan ---+
                 +-----------------+            |
+-------------+                                 |
| ftps server | ------------------------ wan ---+
+-------------+
各マシンの IP アドレス
CCC.ppp.ppp.pppクライアントマシンの IP (プライベート)
CCC.ggg.ggg.gggクライアントマシン側のルータ IP (グローバル)
SSS.ggg.ggg.gggサーバマシンの IP (グローバル)

passive モードではサーバがポートを開けて待つ。クライアントが接続試行する IP アドレスとポート (SSS.ggg.ggg.ggg:9001) はサーバマシンのグローバルアドレスになるので問題なし。

CCC.ppp.ppp.ppp:9000 -> SSS.ggg.ggg.ggg:990  PASV
CCC.ggg.ggg.ggg:9000 -> SSS.ggg.ggg.ggg:990  PASV
SSS.ggg.ggg.ggg:990  -> CCC.ggg.ggg.ggg:9000 Entering Passive Mode (SSS,ggg,ggg,ggg,35,41)
SSS.ggg.ggg.ggg:990  -> CCC.ppp.ppp.ppp:9000 Entering Passive Mode (SSS,ggg,ggg,ggg,35,41)

active モードではクライアントがポートを開けて待つ。サーバが接続試行する IP アドレスとポート (CCC.ppp.ppp.ppp:9001) はプライベートアドレスになるのでダメ。

CCC.ppp.ppp.ppp:9000 -> SSS.ggg.ggg.ggg:990  PORT CCC,ppp,ppp,ppp,35,41
CCC.ggg.ggg.ggg:9000 -> SSS.ggg.ggg.ggg:990  PORT CCC,ppp,ppp,ppp,35,41
SSS.ggg.ggg.ggg:990  -> CCC.ggg.ggg.ggg:9000 PORT command successful.
SSS.ggg.ggg.ggg:990  -> CCC.ppp.ppp.ppp:9000 PORT command successful.

クライアント: プライベート IP、サーバ: プライベート IP の場合

先ほどと少し違うが、クライアント、サーバマシンの双方が NAT の後にあり、プライベート IP が割り振られている場合。ネットワーク図とそれぞれのマシンの IP アドレスは以下。

+-------------+
| ftps client | --- nat ---+
+-------------+            |
                 +-----------------+
                 | iptables router | --- wan ---+
                 +-----------------+            |
                 +-----------------+            |
                 | iptables router | --- wan ---+
                 +-----------------+
+-------------+            |
| ftps server | --- nat ---+
+-------------+
各マシンの IP アドレス
CCC.ppp.ppp.pppクライアントマシンの IP (プライベート)
CCC.ggg.ggg.gggクライアントマシン側のルータ IP (グローバル)
SSS.ppp.ppp.pppサーバマシンの IP (プライベート)
SSS.ggg.ggg.gggサーバマシン側のルータ IP (グローバル)

passive モードではサーバがポートを開けて待つ。クライアントが接続試行する IP アドレスとポート (SSS.ppp.ppp.ppp:9001) はプライベートアドレスになるのでダメ。

CCC.ppp.ppp.ppp:9000 -> SSS.ggg.ggg.ggg:990  PASV
CCC.ggg.ggg.ggg:9000 -> SSS.ggg.ggg.ggg:990  PASV
CCC.ggg.ggg.ggg:9000 -> SSS.ppp.ppp.ppp:990  PASV
SSS.ppp.ppp.ppp:990  -> CCC.ggg.ggg.ggg:9000 Entering Passive Mode (SSS,ppp,ppp,ppp,35,41)
SSS.ggg.ggg.ggg:990  -> CCC.ggg.ggg.ggg:9000 Entering Passive Mode (SSS,ppp,ppp,ppp,35,41)
SSS.ggg.ggg.ggg:990  -> CCC.ppp.ppp.ppp:9000 Entering Passive Mode (SSS,ppp,ppp,ppp,35,41)

active モードではクライアントがポートを開けて待つ。サーバが接続試行する IP アドレスとポート (CCC.ppp.ppp.ppp:9001) はプライベートアドレスになるのでダメ。

CCC.ppp.ppp.ppp:9000 -> SSS.ggg.ggg.ggg:990  PORT CCC,ppp,ppp,ppp,35,41
CCC.ggg.ggg.ggg:9000 -> SSS.ggg.ggg.ggg:990  PORT CCC,ppp,ppp,ppp,35,41
CCC.ggg.ggg.ggg:9000 -> SSS.ppp.ppp.ppp:990  PORT CCC,ppp,ppp,ppp,35,41
SSS.ppp.ppp.ppp:990  -> CCC.ggg.ggg.ggg:9000 PORT command successful.
SSS.ggg.ggg.ggg:990  -> CCC.ggg.ggg.ggg:9000 PORT command successful.
SSS.ggg.ggg.ggg:990  -> CCC.ppp.ppp.ppp:9000 PORT command successful.

まとめ

つまり、ftps クライアントか ftps サーバのどちらかにグローバル IP が割り当てられている場合は、グローバル IP が割り当てられている側が接続待ちになるべき。クライアント側がグローバル IP もっていれば active モード、サーバ側がグローバル IP もっていれば passive モード。纏めると以下。

まとめ
クライアント側 IPクライアント側 NATサーバ側 NATサーバ側クライアント転送モード
グローバル無し無しグローバルactive, passive
グローバル無し有りプライベートactive
プライベート有り無しグローバルpassive
プライベート有り有りプライベート??

結局双方が NAT の内側にいる場合には PORT の引数 or PASV コマンドの戻りを書き換えるトリックが必要になるわけで、IP パケットのデータグラムを読めない限り接続は無理ということか?

リファレンス

  1. nf_conntrack_ftp|ip_conntrack_ftp passive|pasv - Google 検索
  2. vsftpdでTLSを使う - satospo
  3. iptablesでftpを通す
  4. iptablesのnatででftpのPASVモードを通す
  5. ステートフルパケットインスペクション|spi iptables - Google 検索
  6. FTPを許可する設定 iptables編
  7. リビング+:第2回:SPIと動的パケットフィルタリングの違い
  8. Linux の iptables でパッシブ FTP を許可する ( passive )
  9. FTP SSL through a NAT Firewall
  10. アクティブFTPとパッシブFTP
  11. ssl|tls nat|"ip masquerade" - Google 検索
  12. TCP/UDPポート番号一覧
  13. ssl|tls nat ftp - Google 検索
  14. IT Clashes | Crowded Thoughts » FTP behind NAT with TLS howto
  15. FTP/TLS Friendly Firewalls
  16. Entering Passive Mode - Google 検索
  17. linux netfilter module - Google 検索

ソーシャルブックマーク

  1. はてなブックマーク
  2. Google Bookmarks
  3. del.icio.us

ChangeLog

  1. Posted: 2009-10-07T19:16:59+09:00
  2. Modified: 2009-10-07T19:16:59+09:00
  3. Generated: 2017-08-01T23:09:16+09:00