トンネルエンドポイントの先にあるサブネットへは三つの方法で到達でき、事実確認を一分もすればたいてい二つは除外されます。確認すべき事実は、向こう側のサーバーがユーザーを区別する必要があるか、向こう側の誰かがあなたの範囲へ接続しに来ることがあるか、そして遠端のルーターを編集できるのは誰かです。この三つに答えれば、設計は自然に決まります。以下では、あなたがエンドポイントを運用し、そのファイアウォールを編集できることを前提とします。

三つの選択肢と、それぞれが実際に変えるもの

プレフィックスのマッピング、ホスト単位の DNAT、ルーティングされたプレフィックスはいずれも同じ二つの範囲の間でパケットを移動させますが、変えるものは異なります。マッピングは別名の範囲を作り出し、転送中にアドレスを算術的に書き換えます。ホスト単位のルールも書き換えますが、宛先は一つずつ手で指定します。ルーティングされたプレフィックスは何も書き換えません。パケットは実際のアドレスを保ち、両端がそのアドレスの所在について合意します。

What the far side needsPrefix mappingPer-host DNATRouted prefix
Users visible to its logsOnly without source NATSame conditionYes, unchanged
To start a connection toward youNo, alias is localNo, same reasonYes, both ends route
To absorb a new hostYes, arithmetic onlyNo, one rule per addressYes, it simply routes
To require no config changeYes, with source NATYes, same conditionNo, it needs a route back

除外は遠端から始めます。そこが最も変えにくい制約だからです。向こうのサーバーがクライアントのアドレスをログに書き、許可リストと照合し、あるいは別のシステムへ渡しているなら、送信元を変換するとその唯一の識別子が壊れます。遠端がこちらへ接続しに来る必要もあるなら、変換は完全に不可能です。向こう側が別名の存在を知らないからです。遠端が要求に応答するだけのときに限り、安価な選択肢が安全になります。

プレフィックスの組と、サイズが一致しなければならない理由

NETMAP は nat テーブルのターゲットで、同じ長さのプレフィックスの組に対して動作します。内部にアドレスごとの表は持たず、プレフィックスを剥がしてホストビットを保持し、新しいプレフィックスに付け直します。だから /24 は /24 にマッピングでき、/25 には決してなりません。位置が方向を決めます。PREROUTING の宛先一致はパケットの行き先を書き換え、POSTROUTING の送信元一致はその来歴を書き換えます。

192.168.2.17 のホストは、あなたのクライアントには 10.50.1.17 として見えます。最後のオクテットは飾りではなく、両側で同じ数です。明日 192.168.2.40 にホストを追加すれば、その別名は 10.50.1.40 になり、編集すべきルールはありません。

クライアントには別名の範囲をあなたのボックスへ向けるルートが依然として必要ですが、別名がすでに到達できるサブネット内にある場合は不要です。宛先変換はルート検索より前の PREROUTING で実行されるので、ゲートウェイに別名へのルートは不要です。転送も有効でなければならず、そうでなければ書き換えの後にカーネルがパケットを破棄します。以下の三つのコマンドが、クライアントが br0、別名が 10.50.1.0/24、トンネルが tun0 にある Debian 風ゲートウェイでの設定のすべてです。

sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A PREROUTING -i br0 -d 10.50.1.0/24 -j NETMAP --to 192.168.2.0/24
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE

応答は自力で戻る道を見つけなければならない

宛先を書き換えることは、パケットがどこへ配達されるかだけを決めます。遠端のサーバーは、自分が到達できるアドレスに応答しなければならず、その手配には二つの方法があります。送信元を変換して応答をあなたのボックスへ戻すか、送信元をそのままにして遠端へクライアント範囲のルートを与えるかです。

外向きのマスカレードは近道です。POSTROUTING のルール一つで、遠端にはあなたのトンネルアドレスが見えるので、向こう側に独自のルートは要りません。代償は、すべてのユーザーが一つのアドレスに潰れ、ユーザー単位のログが意味を失うことです。送信元をそのまま残せばその情報は保たれますが、遠端のサイトはクライアント範囲への応答をトンネル経由で返さなければならなくなります。OpenVPN サーバーでは、それがプッシュされる push "route 10.50.9.0 255.255.255.0"、さらに route 192.168.2.0 255.255.255.0 行です。

高くつく失敗は、それぞれ半分ずつやることです。一部のトラフィックをマスカレードしながら、遠端が別のトラフィック用の戻りルートも持っていると、応答はコネクション追跡エントリが一度も見られなかったインターフェースに届きます。クライアントは、サーバーが処理済みとして記録した要求に対してタイムアウトを報告します。フローの両方向は同じ netfilter インスタンスを通らなければならない:一つのゲートウェイが変換するのであって、二つのファイアウォールを直列に置くのではない。

conntrack -L -d 10.50.1.17
iptables -t nat -L PREROUTING -v -n

エントリを両方向から読みます。元のタプルは宛先にあなたの別名を保ち、応答タプルは実際のアドレスを運びます。応答タプルがなければ、問題はルールではなく戻り経路です。PREROUTING のカウンターが一度も動いていなければ、パケットはそのフックに到達していません。

衝突と、先に走る検索

二つの異なる範囲は衝突し得ます。どちらの故障も、悪いルールというより壊れたトンネルに見えます。あなたが作る別名は経路全体で未使用でなければならず、実際の遠端の範囲があなたのネットワークにすでに存在してはなりません。重複は断続的な誤ったホストへのトラフィックを生み、接続拒否より診断が難しくなります。ルールを追加する前にルーティングテーブルに尋ねてください。

ゲートウェイでは、ip route get 192.168.2.17 がトンネルデバイスを指すはずです。ローカルブリッジが返るなら遠端の範囲は隠されており、nat ルールでは直りません。あなたのネットワークがすでに 192.168.2.0/24 を使っているなら、衝突の上にマッピングを重ねるのではなく、遠端のサイトを採番し直してください。

実際のクライアントから試し、それから保持する

別名を実際に使うマシンから試してください。ゲートウェイからではいけません。ローカルで生成されたトラフィックは PREROUTING を同じようには通らないからです。別名のホストへ接続を開き、カウンターと conntrack エントリが一緒に現れるのを見て、閉じてからエントリが消えることを確認します。古いエントリは半開きの接続を指し、たいていは応答が戻ってこなかったことを意味します。

curl -sv http://10.50.1.17/ -o /dev/null
ss -tn dst 10.50.1.17
conntrack -L -d 10.50.1.17

二、三の成功した接続は算術を証明しますが、永続性や次の技術者については何も語りません。ルールは、起動プロセスが読み込む場所に、Debian と Ubuntu なら netfilter-persistent を有効にしたうえで iptables-save > /etc/iptables/rules.v4 として保存してください。再起動で消えるマッピングは、責めるべき最近の変更もなく障害が訪れるのですから、何もないより悪いのです。

ルールのそばにメモを残してください。別名の範囲、実際の範囲、その組にした理由、どちら側が開始できるかです。そのメモが、設計と、誰も消そうとしないルールの山とを分けます。後の疑問も片づきます。リモートサイトがホストを追加し、誰も設定していないアドレスがすでに応答するときです。メモに遠端が戻りルートを必要とすると書いてあれば、次の人はマスカレードのルールを外すと接続が切れることを知っています。ここでは文書化は安く、午前三時に NAT 依存を再発見するのは安くありません。

関連記事