問題
自宅でサービスをセルフホストしているなら、この苛立たしい問題に遭遇したことがあるはずだ。サービスは外部からは完璧に動作するのに、自宅にいてドメイン名でアクセスしようとすると、まったく動かない。
これはヘアピンNAT問題(NATループバックとも呼ばれる)と呼ばれるもので、これはそれを最終的に解決した方法だ。
ヘアピンNATとは何か?
humfurie.orgに外部からアクセスすると、リクエストはグローバルIPに送られ、内部サーバーに転送され、すべて問題なく動作する。しかし、自分自身のネットワーク内にいて同じことを試すと、ルーターが混乱し、トラフィックは外へ出ていき、戻ってこようとして、壊れる。
「正しい」修正方法は、ルーター上でのヘアピンNATルールだ。しかしこれは信頼性に欠けることが判明した。特にMikroTikルーターでは、fasttrackという機能が確立済み接続に対してNATルールをバイパスしてしまい、正しく設定していてもヘアピンNATが静かに失敗してしまう。
より良い修正方法:AdGuard HomeによるDNSオーバーライド
NATルールと格闘する代わりに、よりクリーンな解決策は、ローカルネットワークにドメインをサーバーの内部IPに直接解決させることだ。ヘアピンは不要になる。
ステップ1: AdGuard Homeをインストールする
Ubuntuサーバー上でdocker-compose.ymlを作成する:
services:
adguardhome:
image: adguard/adguardhome
container_name: adguardhome
restart: unless-stopped
ports:
- "53:53/tcp"
- "53:53/udp"
- "3000:3000/tcp"
volumes:
- ./adguard/work:/opt/adguardhome/work
- ./adguard/conf:/opt/adguardhome/conf
labels:
- "traefik.enable=true"
- "traefik.http.routers.adguard.rule=Host(`adguard.yourdomain.com`)"
- "traefik.http.routers.adguard.entrypoints=websecure"
- "traefik.http.services.adguard.loadbalancer.server.port=80"
ステップ2: ポート53を空ける
Ubuntuはデフォルトでポート53にsystemd-resolvedを使用している。まずこれを無効化する:
sudo systemctl disable systemd-resolved
sudo systemctl stop systemd-resolved
sudo rm /etc/resolv.conf
echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf
次にAdGuardを起動する:
docker compose up -d
ステップ3: AdGuardのセットアップを完了する
http://your-server-ip:3000にアクセスし、セットアップウィザードを完了する。セットアップ後、Traefikのラベルをポート3000ではなくポート80を使うように更新し、コンテナを再起動する。
ステップ4: MikroTikのDHCPをAdGuardに向ける
MikroTikでIP、DHCP Server、Networksに移動し、DNSサーバーをUbuntuサーバーのIP(例: 192.168.88.109)に設定する。これにより、ネットワーク上のすべてのデバイスが自動的にAdGuardを使うようになる。
ステップ5: AdGuardにDNS書き換えを追加する
Filters、DNS Rewritesに移動し、各ドメインごとにエントリを追加する。ルートドメインとサブドメイン用のワイルドカードの両方が必要だ:
| ドメイン | 回答 |
|---|---|
| humfurie.org | 192.168.88.109 |
| *.humfurie.org | 192.168.88.109 |
| yourdomain.com | 192.168.88.109 |
| *.yourdomain.com | 192.168.88.109 |
注意: *.humfurie.orgはサブドメインをカバーするが、ルートドメイン自体はカバーしない。そのため両方のエントリが必要だ。
ステップ6: 確認する
DHCPリースを更新し、テストする:
nslookup humfurie.org
レスポンスにグローバルIPではなく内部IPが表示されるはずだ。
注意点
Tailscale: Tailscaleを使用している場合、デバイス上のDNSを上書きしてしまう。Tailscaleの設定で「Use Tailscale DNS settings」を無効にすること。そうしないとデバイスがAdGuardを完全に無視してしまう。
MikroTikのDNSキャッシュ: MikroTikはDNSレスポンスをキャッシュする。変更後にうまく動作しない場合は、ターミナルでキャッシュをフラッシュすること: /ip dns cache flush
Dockerとポート53: AdGuardの管理UIは初期セットアップ中はポート3000で動作し、その後ポート80に切り替わる。セットアップ後は、それに応じてTraefikの設定を更新すること。
結果
これで、すべてのセルフホスト型サービスに、ネットワークの内側からでも外側からでも、同じドメイン名でアクセスできるようになった。AdGuard Homeはおまけとして、ネットワーク全体の広告ブロックとDNSクエリのログ記録も提供してくれる。