2017/05/25(Thu)EdgeRouter X(ER-X)で802.1q VLAN + Hairpin NATを実現する方法

2017/05/24 3:21 Hardware::NetworkSwitch
EdgeRouter X (ER-X)というUbiquiti Networkが販売するEdgeRouterシリーズのルータでの設定方法例についてまとめる。このエントリは、802.1q VLANとHairpin NATを組み合わせて動作させる方法について。

諸注意

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を有効にすることが出来るのだが、
er-x-hairpin-nat.png

実はこれには罠があり、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 20rule 6020を参照してほしい。

この設定で、以下のようなiptablesコマンドと同様の設定がされる。
hairpin-nat.png

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を定義しているはず。
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
            }
        }
        :
    }
}

*1 : 中身はLinux箱なので/sbin/tcpdumpが使える!超便利!!

OK キャンセル 確認 その他