ローカルの NATed FTP サーバーを公開するための nft 構成

ローカルの NATed FTP サーバーを公開するための nft 構成

すべてが分離されたネットワーク上にあるため、セキュリティは問題になりません。eth0
は「パブリック」ネットワークに接続されています。アドレスは DHCP によって割り当てられます。eth1 は、
ssh、telnet、「その他」、および ftp を提供する「プライベート ネットワーク」サーバーに接続されています。
この例では、このサーバーには固定 IP (192.168.1.2) が設定されます。

ゲートウェイはDebian BusterとLinuxカーネル4.19.94を実行しています。

nft は NAT で使用されます。
これはこれまでの私の「ゲートウェイ」nft 構成です。

table ip my_nat {
    chain my_prerouting { type nat hook prerouting priority 0;
    tcp dport { 2222 } dnat to :22 # 2222 backdoor for ssh to the gateway
    tcp dport { 1-1023 } dnat to 192.168.1.2
  }
  chain my_postrouting {
    type nat hook postrouting priority 100;
    ip daddr 192.168.1.2  masquerade
  }
}

FTPを動作させるには何を追加する必要がありますか

答え1

要約

# nft add ct helper ip my_nat ftp-incoming '{ type "ftp" protocol tcp; }'
# nft add chain ip my_nat my_helpers '{ type filter hook prerouting priority 10; }'
# nft add rule ip my_nat my_helpers iif eth0 ip daddr 192.168.1.2 tcp dport 21 ct helper set ftp-incoming
# modprobe nf_nat_ftp

詳細は下記をご覧ください...

問題のあるプロトコル用のプロトコルヘルパーモジュール

FTP は古いプロトコルであり、ファイアウォールとの相性があまり良くありません。FTP コマンド チャネル (21/TCP) のコマンドは、次の転送コマンドで使用する一時的なポートをネゴシエートします。このため、ステートフル ファイアウォールは、これらのコマンドと応答をスヌープして、使用しようとしている適切なポートを一時的に事前に許可する必要があります。

Linuxでは、これはプロトコル固有のヘルパーモジュールによって提供され、接続トラック、NetfilterサブシステムはNATとステートフルファイアウォールの接続を追跡します。FTPでは、次の転送のためのポートネゴシエーション(主にPORT、EPRT、PASV、またはEPSV)がFTPコマンドポートで確認されると、ヘルパーは特別なエントリに短命のエントリを追加します。接続トラックテーブル(予想接続テーブル) は、次の関連する予想されるデータ接続を待機します。

私の回答では、最新の「安全な」処理を使用しています。このブログについてiptables一般論として、nftablesウィキそしてman nft取り扱いについてnftablesこれはiptables

ヘルパーとnftablesルールの安全な使用

Linuxカーネル4.7以降(4.19を含む)は、デフォルトで安全なアプローチを使用しています。(ここではFTP)ヘルパーモジュールをロードすると、特定のポートがTCP送信元または宛先ポート21であるすべてのパケットをスヌープすることはなくなります。nftables(またはiptables) ステートメントは、どのような (制限された) 場合にスヌープを行うかを指定します。これにより、不要な CPU 使用が回避され、いくつかのルール (またはセット) を変更するだけで、いつでもスヌープする FTP ポートを変更できます。

最初の部分は、スヌーピングをトリガーできるフローを宣言することです。これは、nftablesよりもiptablesここでは、ct helper、それをアクティブにするフィルターは接続トラック(その間iptables事前にアクションを実行する必要があります。man nft伝える:

iptables とは異なり、ヘルパーの割り当ては、たとえばデフォルトのフック優先度 0 を使用して、conntrack ルックアップが完了した後に実行する必要があります。

nft add ct helper ip my_nat ftp-incoming '{ type "ftp" protocol tcp; }'

nft add chain ip my_nat my_helpers '{ type filter hook prerouting priority 10; }'
nft add rule ip my_nat my_helpers iif eth0 ip daddr 192.168.1.2 tcp dport 21 ct helper set ftp-incoming

同じテーブルを選択しましたが、ステートフル オブジェクトの宣言とそれを参照するルールが同じテーブルにある限り、別のテーブルに作成することもできます。

もちろん、より制限の少ないルールを選択することもできます。最後のルールを次のルールに置き換えると、レガシー モードと同じ効果が得られます。

nft add rule ip my_nat my_helpers tcp dport 21 ct helper set ftp-incoming

参考までに、今後は使用しないレガシー モードでは、上記のルールは必要なく、次の切り替え (および関連するカーネル モジュールの手動読み込み) のみが必要です。

sysctl -w net.netfilter.nf_conntrack_helper=1

nf_nat_ftpロードされていることを確認する

カーネル モジュールは、nf_conntrack_ftpによって作成された依存関係とともに自動的にロードされますct helper ... type "ftp"。 の場合はそうではありませんnf_nat_ftpが、データ フロー ポートで NAT が実行されている場合は、コマンド ポートでパケット マングリングも有効にする必要があります。

たとえば、モジュールがロードされるnf_nat_ftpたびにプルされるようにするにはnf_conntrack_ftp、次の内容のファイル/etc/modprobe.d/local-nat-ftp.confを追加できます。

install nf_conntrack_ftp /sbin/modprobe --ignore-install nf_conntrack_ftp; /sbin/modprobe --ignore-install nf_nat_ftp

または、代わりに、たとえば次のように追加します/etc/modules-load.d/local-nat-ftp.conf

nf_nat_ftp

とにかく今は、ロードされていることを確認するために次のコマンドを実行する必要があります。

modprobe nf_nat_ftp

ファイアウォールについて

ファイアウォールに関する追加情報です。新しいフローをタグ付きで許可するのではなく、いくつかの制限を課したファイアウォールルールもあります。関連しているによる接続トラック

たとえば、FTP ヘルパー モジュールはパッシブ モードとアクティブ モードの両方を処理しますが、何らかの理由でパッシブ モード (クライアントからサーバーへのデータ接続) のみを許可し、「アクティブ」 FTP (サーバー ソース ポート 20 からクライアントへのデータ接続) を許可しない場合は、通常のルールの代わりに、たとえばルールセットのファイアウォール部分で次のルールを使用できますct state established,related accept

ct state established accept
ct state related ct helper "ftp" iif eth0 oif eth1 tcp sport 1024-65535 accept
ct state related ct helper "ftp" drop
ct state related accept 

その他の種類の関連しているFTP に関連しないフローは受け入れられたままになります (または、さらに個別に分割することもできます)


ヘルパーによる対応例

ここ(シミュレーション環境)には2つの接続トラック測定されたイベントのリスト期待テーブルと接続トラックOP のルールと上記の追加ルールを含む表。インターネット クライアント 203.0.113.101 がルーターのパブリック IP アドレス 192.0.2.2 を使用してパッシブ モードで FTP を実行し、ログイン後に LIST コマンドを使用します。

# conntrack -E expect
    [NEW] 300 proto=6 src=203.0.113.101 dst=192.0.2.2 sport=0 dport=37157 mask-src=0.0.0.0 mask-dst=0.0.0.0 sport=0 dport=65535 master-src=203.0.113.101 master-dst=192.0.2.2 sport=50774 dport=21 class=0 helper=ftp
[DESTROY] 300 proto=6 src=203.0.113.101 dst=192.0.2.2 sport=0 dport=37157 mask-src=0.0.0.0 mask-dst=0.0.0.0 sport=0 dport=65535 master-src=203.0.113.101 master-dst=192.0.2.2 sport=50774 dport=21 class=0 helper=ftp

同時に:

# conntrack -E
    [NEW] tcp      6 120 SYN_SENT src=203.0.113.101 dst=192.0.2.2 sport=50774 dport=21 [UNREPLIED] src=192.168.1.2 dst=192.168.1.1 sport=21 dport=50774 helper=ftp
 [UPDATE] tcp      6 60 SYN_RECV src=203.0.113.101 dst=192.0.2.2 sport=50774 dport=21 src=192.168.1.2 dst=192.168.1.1 sport=21 dport=50774 helper=ftp
 [UPDATE] tcp      6 432000 ESTABLISHED src=203.0.113.101 dst=192.0.2.2 sport=50774 dport=21 src=192.168.1.2 dst=192.168.1.1 sport=21 dport=50774 [ASSURED] helper=ftp
    [NEW] tcp      6 120 SYN_SENT src=203.0.113.101 dst=192.0.2.2 sport=55835 dport=37157 [UNREPLIED] src=192.168.1.2 dst=192.168.1.1 sport=37157 dport=55835
 [UPDATE] tcp      6 60 SYN_RECV src=203.0.113.101 dst=192.0.2.2 sport=55835 dport=37157 src=192.168.1.2 dst=192.168.1.1 sport=37157 dport=55835
 [UPDATE] tcp      6 432000 ESTABLISHED src=203.0.113.101 dst=192.0.2.2 sport=55835 dport=37157 src=192.168.1.2 dst=192.168.1.1 sport=37157 dport=55835 [ASSURED]
 [UPDATE] tcp      6 120 FIN_WAIT src=203.0.113.101 dst=192.0.2.2 sport=55835 dport=37157 src=192.168.1.2 dst=192.168.1.1 sport=37157 dport=55835 [ASSURED]
 [UPDATE] tcp      6 30 LAST_ACK src=203.0.113.101 dst=192.0.2.2 sport=55835 dport=37157 src=192.168.1.2 dst=192.168.1.1 sport=37157 dport=55835 [ASSURED]
 [UPDATE] tcp      6 120 TIME_WAIT src=203.0.113.101 dst=192.0.2.2 sport=55835 dport=37157 src=192.168.1.2 dst=192.168.1.1 sport=37157 dport=55835 [ASSURED]

期待値の始まりはproto=6 src=203.0.113.101 dst=192.0.2.2 sport=0 dport=37157、203.0.113.101:*から192.0.2.2:37157への次のTCP接続が関連付けられることを示しています(状態関連している) が FTP 接続で使用されます。直接は見えませんが、NAT FTP ヘルパー モジュールもロードされているため、PASV/EPSV コマンドに応答してサーバーから送信された実際のデータが傍受され、変換されます。そのため、クライアントは 192.168.1.2 ではなく 192.0.2.2 に接続します。当然、クライアント側では失敗します。

2番目の流れにもかかわらず(2番目の新しい明確なルールがない私の事前ルーティングルーターの背後にあるサーバーに DNAT が正常に送信されました。


ノート

  • FTP 制御ポートが暗号化されている場合 ( AUTH TLS...)、ヘルパー モジュールはネゴシエートされたポートをスヌープできなくなり、この方法は機能しません。FTP サーバー構成とファイアウォール/NAT ルーターの両方でポートの予約範囲を構成し、ネゴシエート時にサーバーが自身の IP アドレスではなく正しい (パブリック) IP アドレスを送信するようにサーバーを構成する必要があります。また、サーバーにインターネットへのルートがない場合 (この場合はそのようです)、アクティブ FTP モードは使用できません。

  • 細かいことを言うと、プリルーティングの優先度が 10 であれば、カーネル 4.18 未満でも、新しいフローに対して NAT が既に行われていることが保証されます (OP は、通常の -100 ではなくフック プリルーティングの優先度 0 を選択しました。これは、nftables ではほとんど問題にならないためです)。したがって、の使用がdaddr 192.168.1.2可能です。優先度が 0 (または 0 未満) の場合、ルールは、最初のパケットがパブリック IP 宛先アドレスで NAT されていないと見なしますが、同じフローの後続のパケットは直接処理されるため、キャッチする可能性があります (未検証)。接続トラック優先度-200。安全のために10を使用する方が良いでしょう。実際、これはカーネル4.18以降では関係ありません(専念参照先これ保留中のパッチ) では、NAT 優先度は複数の NAT チェーン間の比較にのみ関連します (また、iptables レガシーと nftables の NAT を混在させることができます)。

答え2

試行錯誤の末、次の nftables.conf を作成しました。これは意図したとおりに動作し、双方向の NAT をサポートし、サーバー上の 1 つのポートを除くすべてのポートを「パブリック」ネットワークにエクスポートします。ポート 2222 は、ゲートウェイへの「バックドア」として引き続き使用され、再度アクセスする必要がある場合に使用されます :-)

table ip my_nat {
        ct helper ftp-incoming {
                type "ftp" protocol tcp
                l3proto ip
        }

        chain my_prerouting {
                type nat hook prerouting priority 0; policy accept;
                iifname "eth0" tcp dport { 2222 } dnat to :ssh
                iifname "eth0" tcp dport { 1-2221, 2223-65535 } dnat to 192.168.0.2
        }

        chain my_postrouting {
                type nat hook postrouting priority 100; policy accept;
                ip daddr 192.168.0.2 masquerade
                oifname "eth0" masquerade
        }

        chain my_helpers {
                type filter hook prerouting priority 10; policy accept;
                iif "eth0" ip daddr 192.168.0.2 tcp dport ftp ct helper set "ftp-incoming"
        }

}

関連情報