2017/05/25(Thu)EdgeRouter X(ER-X)で802.1q VLAN + Hairpin NATを実現する方法
2017/05/24 3:21
諸注意
config内に日本語で補足を入れているが、文字化けの原因になる可能性があるので注意。コメントは入れるなら英語で入れた方が良いかも。(自分はそうしている)また下記のconfigは、実環境を参考に、説明用の設定値に変更しながら手打ちしたものなので、syntax error等があるかもしれない。その場合は適宜修正して、できれば本記事にコメントを頂けるとありがたい。
何故Hairpin NATが必要か
一つのISPを家庭内LANと自宅サーバで共有するネットワークを構築し、かつ、ルータとサーバが同一ではない場合、家庭内LANから自宅サーバに設定したドメインでアクセスすると、ルータの管理画面やsshにアクセスしてしまうという問題が起こる。これは、LAN内のPCからの通信がルータを経由する際、宛先のグローバルアドレスがルータ自身のアドレスであるため、ルータが応答を返してしまうからである。このようなパケットも、インターネット側からアクセスされたものと想定して、NAT変換等を自動的に行う仕様のルータ(Hairpin NAT, NAT Loopback, NAT Reflection対応ルータなどと呼ばれる)もあるが、殆どの家庭用ルータは非対応で、EdgeRouterもデフォルトはオフとなっている。
簡易設定は簡単だが…
EdgeRouterは結構作り込まれているので、Web管理画面からチェックを1つ入れるだけでHairpin NATを有効にすることが出来るのだが、実はこれには罠があり、ethポート(ルータの物理ポート)をuntaggedで利用している場合向けの機能となっているようだ。また、この機能はport-forwardというNAT設定を簡略的に行う設定項目となってしまうため、firewallが自動設定に纏められたりして、細かな制御や確認がし辛いという難点がある。
port-forward { auto-firewall enable hairpin-nat enable lan-interface switch0 rule 1 { description myserver-http forward-to { address 192.168.11.10 port 80 } original-port 80 protocol tcp } wan-interface eth1 }
natルールを使って記述する方法
nat設定方法で指定する場合、グローバルアドレスが固定IPでなければconfigが書けないという制約があるものの、EdgeRouter - NAT Hairpin (Nat Inside-to-Inside / Loopback / Reflection)にも紹介されている方法でよいはず(untagged環境では未検証)。以下のrule 20とrule 6020を参照してほしい。この設定で、以下のようなiptablesコマンドと同様の設定がされる。
nat { /* inbound-interfaceがpppoe0(on eth1)向けのNAT設定 */ rule 10 { description myserver-http destination { port 80 } inbound-interface pppoe0 inside-address { address 192.168.11.10 port 80 } protocol tcp type destination } /* inbound-interfaceがeth0向けのNAT設定 */ rule 20 { description myserver-http-hairpin destination { address 192.51.100.123 port 80 } inbound-interface eth0 inside-address { address 192.168.11.10 port 80 } protocol tcp type destination } /* DNATはrule 1~4999、SNAT/Masqueradeはrule 5000~9999と決まっていることに注意 */ /* Internetの通常NAT設定 */ rule 6000 { description internet-share outbound-interface pppoe0 protocol all source { address 192.168.11.0/24 } type masquerade } /* Hairpin NAT設定 */ rule 6020 { description myserver-hairpin-nat destination { address 192.168.11.10 } outbound-interface eth0 source { address 192.168.11.0/24 } type masquerade } }
802.1q VLAN上でHairpin NATを行う
802.1q VLANを使う設定をしている場合は、おそらくこんな感じでbridgeを定義しているはず。(2021/07/25 追記) brdigeを使ってVLANを組むのは推奨されていないっぽいので、次項を参照して下さい。
interfaces { bridge { br10 { address 192.168.11.1/24 description myhome-lan } br20 { description onu-zone } : } ethernet { /* eth0: br10をuntagged */ eth0 { bridge-group bridge br10 } /* eth1: br20をuntagged */ eth1 { bridge-group bridge br20 } /* eth2: 以下はbr10をVLAN ID=10で、br20をVLAN ID=20でtaggedする */ eth2 { vif 10 { bridge-group br10 } vif 20 { bridge-group br20 } } : } }Hairpin NATを行う設定でinbound-interface eth0やoutbound-interface eth0の部分をbr10に直せばよいのかというと、それだけでは動かない。
しばらく頭を悩ませたが、パケットキャプチャなどをしているうちに*1、パケットがbridgeに行った後に破棄されている感じであることが分かった。
で、そんな折に見つけたのがコレ。bridgeをpromiscuous modeにすると動くという解決方法。
Port Forwarding working externally / Hairpin doesn't work when accessing via LAN (EdgeOS v1.4.0)
VLAN環境下では、eth側の処理とbridge側の処理をそれぞれ経由するが、bridge内のsoftware処理では「パケットの宛先が(bridgeの192.168.11.0/24でない)グローバルアドレスが付いているのでdrop」と動作しているようだ。なので、br10をpromiscuousにしてやれば、この動作を防ぐ事が出来、Hairpin NATを有効に出来る。
設定としてはこうなる。(上記で書いていたrule 10, 6000は省略)
natのinterface名をbr10に変更し、set bridge br10 promiscuous enableを追加してやればよい。
nat { : /* inbound-interfaceがeth0向けのNAT設定 */ rule 20 { description myserver-http-hairpin destination { address 192.51.100.123 port 80 } inbound-interface br10 inside-address { address 192.168.11.10 port 80 } protocol tcp type destination } : /* Hairpin NAT設定 */ rule 6020 { description myserver-hairpin-nat destination { address 192.168.11.10 } outbound-interface br10 source { address 192.168.11.0/24 } type masquerade } } : interfaces { bridge { br10 { address 192.168.11.1/24 description myhome-lan promiscuous enable } br20 { description onu-zone } : } ethernet { /* eth0: br10をuntagged */ eth0 { bridge-group bridge br10 } /* eth1: br20をuntagged */ eth1 { bridge-group bridge br20 } /* eth2: 以下はbr10をVLAN ID=10で、br20をVLAN ID=20でtaggedする */ eth2 { vif 10 { bridge-group br10 } vif 20 { bridge-group br20 } } : } }
802.1q VLAN上でHairpin NATを行う(改良版)
前項では、bridgeを使ってVLANを作っていたが、本来はswitch-portを使って定義するものらしい。(公式のドキュメントをよく読んでいなかったので、switch-portはnon VLAN専用だと思い込んでいた)
まず最初に、set interfaces switch switch0 switch-port vlan-aware enableしておけば、switch-portがVLANで分離可能になり、bridge同様、「物理ポート定義(interfaces ethernet ethX)と切り離してVLANを管理できる」
よって、最初はこんな感じで2つのVLANが切ってあるはず。(pvidはUntagged port, vidはTagged port)
interfaces { switch switch0 { switch-port { /* eth0: VLAN 10をuntagged */ interface eth0 { vlan { pvid 10 } } /* eth1: VLAN 20をuntagged */ interface eth1 { vlan { pvid 20 } } /* eth2: VLAN 10と20をtaggedで、untaggedは設定なし */ interface eth2 { vlan { vid 10 vid 20 } } : vlan-aware enable } vif 10 { address 192.168.11.1/24 description myhome-lan } vif 20 { description onu-zone } : } ethernet { /* こちら側にVLANの設定は一切無し */ eth0 { duplex auto speed auto } eth1 { duplex auto speed auto } eth2 { duplex auto speed auto } : } }bridgeではHairpin NATの設定を行っても、プロミスキャスにしないと動作しない*2が、以下のように同様のNAT設定を入れるだけでswitchの場合はうまく動作した。(本来こちらが期待する動作)
また前項ではNATルールにポート番号も指定していたが、基本的にグローバルアドレス向けの通信は全部Hairpinに吸い込んで良いはずなので、指定を外した。(protocolもallで良いかもしれないが、とりあえずtcpだけで事足りる)
nat { : /* inbound-interfaceがVLAN 10(eth0)向けのNAT設定 */ rule 20 { description myserver-http-hairpin destination { address 192.51.100.123 } inbound-interface switch0.10 inside-address { address 192.168.11.10 } protocol tcp type destination } : /* Hairpin NAT設定 */ rule 6020 { description myserver-hairpin-nat destination { address 192.168.11.10 } outbound-interface switch0.10 source { address 192.168.11.0/24 } type masquerade } } : interfaces { switch switch0 { switch-port { /* eth0: VLAN 10をuntagged */ interface eth0 { vlan { pvid 10 } } /* eth1: VLAN 20をuntagged */ interface eth1 { vlan { pvid 20 } } /* eth2: VLAN 10と20をtaggedで、untaggedは設定なし */ interface eth2 { vlan { vid 10 vid 20 } } : vlan-aware enable } vif 10 { address 192.168.11.1/24 description myhome-lan } vif 20 { description onu-zone } : } ethernet { /* こちら側にVLANの設定は一切無し */ eth0 { duplex auto speed auto } eth1 { duplex auto speed auto } eth2 { duplex auto speed auto } : } }こうすることで、VLAN 10配下のPC(192.168.11.20/24)が198.51.100.123:80にアクセスすると、ルータが192.168.11.1をSrcIPに置き換えて、LAN内のサーバ192.168.11.10にアクセスし、サーバはルータに応答を返却(SrcIP: 192.168.11.0, DstIP: 192.168.11.1)、Masquerade設定により戻りパケットのSrcIPが198.51.100.123に、DstIPが192.168.11.20に変更され通信が完結する。
クライアント
SrcIP: 192.168.11.20 (自分自身)
DstIP: 198.51.100.123 (自宅内サーバのグローバルIPアドレス)
↓
ルータ(以下のように置換)
SrcIP: 192.168.11.1 (ルータがサーバに対して直接リーチできるアドレスに変換)
DstIP: 192.168.11.10
↓
サーバの応答
SrcIP: 192.168.11.10
DstIP: 192.168.11.1
↓
ルータ(以下のように置換)
SrcIP: 198.51.100.123
DstIP: 192.168.11.20
というわけで、サーバから見たアクセス元が全てルータになる点は仕様。
ちなみに、VLANが複数ある場合、NATのrule 20とrule 6020のセットをVLANの数だけ増やせば対応可能。inbound-interfaceおよびoutband-interfaceを該当のVLAN用のswitch0.xに設定し、そのVLANで扱っているローカルアドレスにinside-address addressおよびsource addressを置き換えればうまくいく。