ヘアピンNAT(NATループバック)非対応ルーターで自宅サーバーにローカルネットワークに接続できなくなった件
こんにちは、クリエイティブSecの長谷川です。
私の家では、個人ブログなどを運用するために自宅サーバーを稼働させています。
先日、家のインターネット回線をNURO光からSoftBank光に変えたのですが
その際に自宅サーバーにアクセスできなくなってしまいました。
今回は、そのときに解決した方法について書いていこうかと思います。
そもそも何が起きたのか
「自宅サーバーにアクセスできなくなってしまった」と先ほど書きましたが
正確には「自宅サーバーと同じネットワーク内からドメイン経由でアクセスできなくなった」です。
つまり、手持ちのスマートフォンでアクセスする場合
Wi-Fiを無効にして、キャリアの回線に切り替えればアクセスできるという状態です。
アクセスできなくなった原因は
今回インターネット回線を切り替えたために問題が発生しましたが
正確にはインターネット回線を切り替えたことではなく、ルーターが変わったことが原因です。
新しく導入したルーターが ヘアピンNAT(NATループバック)に非対応 だったため、
LAN内から自分のグローバルIP宛にアクセスする通信を正しく処理できなかったのです。
ドメイン名やグローバルIPを指定して自宅サーバーへアクセスした場合、
ルーターはそれを 「外部宛の通信」 として扱います。
しかしその宛先が 自分自身(ルーターのWAN側IP) になるケースは、
ヘアピンNAT非対応のルーターでは想定されておらず、
その結果、通信が途中で破綻してアクセスできなくなります。
本来このような通信を成立させるには、
一度外に出た通信を内部に折り返す処理(ヘアピンNAT)が必要になります。
どうやって解決するか
ヘアピンNAT非対応ルーターによる問題には、いくつかの解決方法があります。
環境や目的によって最適な選択肢は異なります。
1. ヘアピンNAT対応ルーターを使用する
最もシンプルな解決方法は、
ヘアピンNAT(NATループバック)に対応したルーターを使用することです。
この方法ではネットワーク構成を変更せずに済み、
LAN内・外部のどちらからも同じドメイン名でアクセスできます。
ただし、ルーターの買い替えが必要になる点がデメリットです。
2. hostsファイルなどで個別対応する
検証用途などでは、各端末のhostsファイルを編集する方法もあります。
ただし、この方法だと例えばノートPCを外に持っていって使う場合には再度編集しなければいけないのと
iPhoneではそもそもhostsファイルを変更することが通常手段ではできないために、私の場合は現実的ではありませんでした。
3. 内部DNSサーバーを構築する(DNSスプリット)
もう一つの方法は、LAN内専用のDNSサーバーを用意し、内部向けに名前解決を分ける方法です。
内部DNSではドメイン名をグローバルIPではなく、
直接LAN内のサーバーIPに解決させることで、
ルーターを経由せずに通信できるようになります。
この方法は、
- ルーターの機種に依存しない
- 柔軟な構成が可能
というメリットがあります。
今回、私はこの方法を採用しました。
DNSサーバーを構築する
私の場合、もともと自宅サーバーを立てていたので
そこにDNSサーバーの機能を追加することにしました。
今回はDnsmasqをインストールすることにしました。
DNSサーバーには、他にも「BIND」や「Unbound」などがありますが
私の場合、あくまでも自分だけのための用途なので軽量かつ簡易なものということで選択しました。
ということで、インストールおよび設定の手順です。
Dnsmasqのインストール
インストールはコマンド一発ですね。
$ sudo apt install dnsmasq設定の変更
次に設定を変更します。
$ vi /etc/dnsmasq.confDnsmasq自体はDHCPサーバーの機能も持っていますが
今回はDNSサーバーとしてだけ使いたいので、私の場合は下記の内容を変更しました。
#port=5353
port=53
~~~~~
#server=/localnet/192.168.0.1
server=1.1.1.1 ← 自分で設定しないドメインの名前解決先
server=1.0.0.1
~~~~~
#listen-address=
listen-address=127.0.0.1,192.168.0.100 ← 192.168.0.100はサーバー自体のアドレス
~~~~~
#no-dhcp-interface=
no-dhcp-interface= ← コメントアウト。DHCP機能を無効化
~~~~~
#bind-interfaces
bind-interfaces ← コメントアウト。listen-addressで指定したアドレス宛のDNSパケットだけ受け取る
~~~~~~
#no-hosts
no-hosts ← コメントアウト。/etc/hostsは参照しない設定例では bind-interfaces をコメントアウトしていますが
これは dnsmasq が全インターフェースに bind しても listen-address の指定で受け付けるアドレスを制限しているためです。
必要に応じて bind-interfaces を有効化しても構いませんが、その場合は動的IP変更時の再起動などの注意が必要です。
次に、実際に解決したいドメインとアドレスの設定を追加します。
$ vi /etc/dnsmasq.d/local.confaddress=/sample.xxx.com/192.168.0.100
address=/www.xxx.com/192.168.0.100systemd-resolvedの停止と無効化
次にsystemd-resolvedを停止・無効化します。
systemd-resolvedとは、ローカルのDNSスタブリゾルバで、今回のdnsmasqと衝突してしまいます。
そのため、下記コマンドで停止・無効化します。
sudo systemctl disable --now systemd-resolvedDnsmasqの起動
最後にDnsmasqを起動します。
systemctl start dnsmasq
systemctl enable dnsmasq各種端末のDNSサーバーの設定
先程までの手順でDNSサーバーが構築できたので
各種端末が構築したDNSサーバーを参照するように変更します。
Windowsの場合は、Wi-FiやLANなどネットワークアダプター単位でDNSを変更することもできますが
Wi-Fiの場合、家のWi-Fiだけ指定のDNSを使って、外では別のDNSを使いたいということもあるので
今回はWi-Fiのアクセスポイント単位で設定する方法を紹介します。
「設定」の「ネットワークとインターネット」から「Wi-Fi」を選択すると
接続しているアクセスポイントのプロパティがでてくるので、それを選択します。

「DNSサーバーの割り当て」という項目があるので、その「編集」を押下します。

一番上の選択肢がデフォルトは「自動」だと思いますが
これを「手動」に変更すると、さらに設定項目が下に表示されます。
IPv4のチェックを有効状態にして、優先DNSに今回DNSサーバーを構築した
サーバー機のIPアドレスを入力します。

Windowsの場合はこれで完了です。
iPhoneやAndroidについては下記の記事を参考にしてみてください。
さいごに
実は昔も一度、ヘアピンNAT非対応の問題に直面したことがあり、
そのときは市販のヘアピンNAT対応ルーターに交換して対応していました。
あれから10年ほど経っているため、すっかりこの問題の存在を忘れていました。
ヘアピンNAT対応は、まだ当たり前の機能にはなっていないのだと改めて感じました。
そもそも自宅サーバーを運用している人自体が少なく、
需要があまりないという事情もあるのかもしれません。
実際、多くの家庭用ルーターでは機能名や設定項目が明示されておらず、
スペック表にも「対応」と記載されていないケースがあります。
対応機種を選ぶ際は、必ずメーカーの仕様を確認することをおすすめします。




