自宅内のアプリに、同一のドメインで、LAN内とインターネットの外からの両方に対応する(スプリットDNS)構成編

なんのこっちゃという感じだが、自宅サーバのWebサイトに対してドメインでアクセスしてしまうと、LAN内になるのにインターネット経由になってしまう。それはなんだか阿呆みたいなので、LAN内からはLANでつなげたい。

つまりこういうことです。

graph TD subgraph "インターネット (外からの道)" A["スマホ (4G/5G)"] --> B["Cloudflare (中継)"] B --> C["トンネル (暗号化)"] end subgraph "家の中 (LAN内の道)" D["PC / スマホ (Wi-Fi)"] --> E["AdGuard Home (近道の案内板)"] end C --> F["Caddy (玄関口)"] E -->|"近道 (爆速)"| F F --> G["WordPress / 自分のアプリ"] style E fill:#f96,stroke:#333,stroke-width:2px style F fill:#f9f,stroke:#333,stroke-width:2px style G fill:#bbf,stroke:#333,stroke-width:2px

同じドメインで、LANとインターネットで別々のルートにする。LAN内ではLAN内DNSを使って名前解決する、ってことだね。これによって、LAN内のアプリにhttps://app.example.comでインターネットを使わずにアクセスできるようになる。

IPアドレス直打ちやTailscaleのMagicDNSとかじゃあかんのか?という向きもあると思うが、WordPressなどはサイトドメインにリダイレクトする仕様があったりするためそうもいかないし、そもそもLANとインターネットで2つのブックマークを作るというのは、なんだかつらいものがある。

ということで、色々頑張った結果、なんかLinux Mint Xfce + Caddy(+Cloudflare) + Tailscale + AdguardHome という構成になって1日かかってしまったんだが、正直ここまで頑張らなくてももっとシンプルにやれる。

目次

構成

基本の構成

順番に考えていこう。外からの通信はみんな大好きCloudflare Tunnel から直通でOK。問題は、LAN内だね。

まずは、LAN内DNSを作って、ルータでそこに向ける。これにAdGuard Homeを使う。AdGuardHomeで特定のドメインを、リバースプロキシのCaddyに向ける。考え方の基本はこれだけ。

graph TD subgraph "インターネット (外からの道)" User_Ext["ユーザー"] Tunnel["Cloudflare Tunnel<br/>(cloudflared)"] end subgraph "LAN内 (秘密の近道)" User_Int["自分 (Wi-Fi)"] AGH["AdGuard Home<br/>(案内板: DNS書き換え)"] Caddy["Caddy<br/>(玄関: HTTPSプロキシ)"] end subgraph "アプリ群" App["WordPress / AIアプリ 等"] end %% 外のルート (Caddyをバイパス) User_Ext --> Tunnel Tunnel -->|"直接ポートを指定"| App %% 内のルート (Caddyを経由) User_Int -.->|"① 案内を聞く"| AGH AGH -.->|"② CaddyのIPを回答"| User_Int User_Int -->|"③ HTTPSでアクセス"| Caddy Caddy -->|"④ アプリへ転送"| App style AGH fill:#f96,stroke:#333 style Caddy fill:#f9f,stroke:#333 style Tunnel fill:#fff,stroke:#333 style App fill:#bbf,stroke:#333

これが基本なので、単にスプリットDNSしたいだけならこれでよい。というか、個人的にはこれで十分だと思う。なんかAIにのせられてゴチャゴチャとやってしまったが、正直それで思ったのは「いらねぇ」だった。

ここから、ちゃんとした証明書の発行と、仮想ネットワークを混ぜるとたいへんになる。色々と肉付けをしていくと複雑になる。

証明書(諸悪の根源)

ちゃんとした証明書というのは、つまりオレオレ証明書ではないということだ。これがあることで、ブラウザからは完全なるお許しを得られるので、なんかマルウェアサイト扱いされたり安全ではない云々などの小言を挟まれてイライラすることはなくなく。

証明書の発行をCaddyは本来サラッとこれをやってくれるのだが、今回はLAN内であるために、必要な80/443を使えない。

ここで、Cloudflareを利用すれば、API使ってゾーン編集して一時的にTXTレコードを作るなどして、証明書の発行ができるプラグインを活用する(DNS-01チャレンジ)。これによって、LAN内であるにも関わらずオレオレではない証明書ができるので、これにはブラウザもニッコリ。警告が消えるよ!やったね、Googleさん。

と、ブラウザはニッコリなのだが僕はかなりイライラしている。やっていることはほとんど形式的で実際的な意味はない。ついでにCloudflareに依存してしまっている…いやこれは別のプロバイダ用のプラグインもあるので、置き換えはできるんだけど。できるけどなんでこんな大掛かりなことに。

まぁ、とりあえずここまでを図示しよう。

graph TD subgraph Internet ["インターネット (外からの道)"] User_Ext["ユーザー"] Tunnel["Cloudflare Tunnel<br/>(cloudflared)"] end subgraph LAN ["LAN内 (秘密の近道)"] User_Int["自分 (Wi-Fi)"] AGH["AdGuard Home<br/>(DNS書き換え)"] Caddy["Caddy<br/>(証明書管理 & プロキシ)"] end subgraph Cloudflare_Control ["Cloudflare管理下 (DNS-01)"] CF_API["Cloudflare API"] DNS_Rec["TXTレコード (一時的な合言葉)"] end subgraph CA ["証明書発行機関 (Let's Encrypt等)"] Verify["レコードの正当性検証"] end subgraph App_Server ["アプリ"] App["WordPress / 各種アプリ"] end %% 外のルート User_Ext --> Tunnel Tunnel -->|"直接ポートを指定"| App %% 内のルート User_Int -.->|"① ドメインのIPを問い合わせ"| AGH AGH -.->|"② CaddyのLAN内IPを回答"| User_Int User_Int -->|"③ HTTPSアクセス (警告なし)"| Caddy Caddy -->|"④ アプリへ転送"| App %% 証明書発行 (DNS-01) のプロセス Caddy <-->|"⑤ APIトークンで認証"| CF_API CF_API -- "⑥ 証明用のレコードを設置" --> DNS_Rec DNS_Rec -. "⑦ 外部から合言葉を確認" .-> Verify Verify -- "⑧ 証明書を発行" --> Caddy style AGH fill:#f96,stroke:#333 style Caddy fill:#f9f,stroke:#333 style Tunnel fill:#fff,stroke:#333 style App fill:#bbf,stroke:#333 style CF_API fill:#9cf,stroke:#333

2010年代以降、インターネットは同じことをどれだけ複雑にやれるか選手権が全世界で開催されたため、その煽りを受けた格好だ。

単に複雑なだけではなく、これはむしろ脆弱になっていないかと思う。Cloudflare(等のプロバイダ)に依存するために、攻撃対象が純粋に増えている。何してるんだ俺は。俺が俺の作ったアプリを俺のLAN内で使うのに、Cloudflareという第三者をとおすほうがセキュアであるというのはどういう理屈なんだ?そんなに俺は俺が信用できないのか?どうなんだ俺?

俺ではなく今のインターネットの問題である。というか根本的にLAN内で完結することのはずなのに、少なくとも証明書の更新時にインターネット接続が必要になっており、オフラインになると維持できない仕組みだ。まぁさすがに前提条件を考えるとインターネットの維持まで疑っても仕方ないかもしれないが、しかし必要もなくインターネットの外に出ることのどこがセキュリティだというのか。

まぁ一応、なにかあったら仕組みを止めれば良いので、最低限のラインは超えていて依存とは言い切れない、くらいに考えてはいるが、いったいマジで何をやっているんだという気持ちが強い。そもそもCloudflare Tunnel使っとるやないかいと言われそうだが、これは単に玄関で、いざとなればVPS + frpなどで即座に別の玄関に変えることは可能(後述するTailscaleがあればそれも使える)だし、実際似たようなことを以前のConoHa大規模障害時にやったこともあるので、依存ではないと考えている。だがAPIを使った証明書発行と更新というのはなぁ…。

Tailscaleの追加

ここまででもかなり嫌になるが、なんか別の用途があったりしてTailscaleなど使っていると、構成はさらに複雑になる。TailscaleのDNSをAdGuard Homeに向ける必要があるからだ。また、MagicDNSを使えるのはよいのだが、それゆえのハマり要素もある。

結果、最終的に以下の構図になる。インターネット経由の経路と、Tialscaleのネットワーク経由でAdGuard HomeとCaddyを使う経路ができるわけだ。

graph TD subgraph Internet ["1. インターネット (外からの道)"] User_Ext["ユーザー"] Tunnel["Cloudflare Tunnel<br/>(cloudflared)"] end subgraph Tailscale_Mesh ["2. Tailscale Mesh (仮想ネットワーク)"] direction TB TS_DNS["Tailscale DNS / MagicDNS<br/>(Nameserver: AdGuard IP)"] User_Int["自分 (PC/スマホ)<br/>Tailscale ON"] end subgraph Linux_Server ["3. Linux Server (cimon)"] direction TB AGH["AdGuard Home<br/>(DNS書き換え実行)"] subgraph Docker ["Docker"] Caddy["Caddy<br/>(HTTPSプロキシ / 証明書管理)"] end end subgraph Mac_Mini ["4. 別マシン (mac-mini)"] App_Remote["WordPress / AIアプリ"] end subgraph Cloudflare_CA ["5. Cloudflare & CA"] CF_API["Cloudflare API"] LE["Let's Encrypt"] end %% 外のルート User_Ext --> Tunnel Tunnel -->|"直接ポート指定"| App_Remote %% 内のDNSフロー User_Int -.->|"① DNS問い合わせ"| TS_DNS TS_DNS -.->|"② 名前解決を丸投げ"| AGH AGH -.->|"③ CaddyのIPを回答"| User_Int %% 内の通信フロー User_Int -->|"④ HTTPSアクセス"| Caddy %% Caddyからの転送 (MagicDNS vs IP) Caddy -->|"⑤ Proxy via MagicDNS"| App_Remote Caddy -.->|"⑤ Proxy via Local IP<br/>(自分自身のループバック回避)"| AGH %% 証明書取得 (DNS-01) Caddy <-->|"⑥ TXTレコード操作"| CF_API CF_API <-->|"⑦ 検証"| LE style AGH fill:#f96,stroke:#333,stroke-width:2px style Caddy fill:#f9f,stroke:#333,stroke-width:2px style TS_DNS fill:#9cf,stroke:#333,stroke-width:1px style Tunnel fill:#fff,stroke:#333,stroke-width:1px

これにより以下の3つの経路が並立している。

  1. インターネット経路
  2. Tailscale経路
  3. LAN内IP直打ち経路

正直どうかと思う。

しかし、先の証明書と違い、こちらのTailscaleは仮想ネットワークの構築という明確な役割があるため、別用途で必須となっているのであれば、これはやらざるを得ない。

所感:そもそもやるべきか?

ここまで少しずつ段階を書いたのは、納得できる範囲でやるべきだからだ。全部盛りでやってしまうと、どっかぶっ壊れたときにどうしていいかわからなくなる。そもそも、本質的に必要のないことはやるべきではない

まぁしゃあないので毒を喰らわば皿までで、第三者に自宅の経路を保証してもらうという謎の苦行まではするとして、個人的にはAdGuard Home + Caddyでいいと思う。ブラウザの警告も気にしないならDNS-01チャレンジもいらんでもここまできたら全部やってもいんじゃね感。普通につかえるとストレスフリーなのはまぁそう。

が、これと別にTailscaleを導入している場合、考えるところが増える。Tailscaleが入ると、そこで名前解決があるので、*.ts.netでいいなら、実はAGHはいらない。ただ今回は外からのドメインを使うという要件があるため、AGH + Tailscale + Caddyというカオスになるのだが…。

そもそもスプリットDNSなんてやらないほうがよかった説も有力である。IPアドレス直打ちはしんどくね問題はあるが、それだけならMagicDNS使ったっていいし。警告なんて知らねぇっていってもいいし。気分の問題の側面が大きい。まぁWordPressのようにサイトドメインの概念があると、ドメインは固定になってしまうため、、同一ドメインで別経路を取るのは価値があるのだが、そこまでして…とも思える。

じゃあなぜお前はやったのか?AIに相談してたらなんかこうなってしまった。AIというやつはそうなるんだ。ベストプラクティスのツギハギをして、本来必要だったものから遠ざかっていく。途中で「絶対こんなのは必要ない」と思ったが、エラーが出ているのは腹立たしいので最後までやってしまった。

自宅サーバってレベルじゃねぇぞ、などという気もしつつ、この記事は、どこが分岐点だったのか?をまとめたものに近い。そういう意味じゃよく理解できたと言えるかもしれないが、疲れた…。AIが過剰に重視してしまう指標と、無視しがちな指標が存在するんだね。 それらの指標は、現実上の重要性とたいてい一致しない。 これは「AI」を「社会」や「組織」に置き換えても成り立つんだな。

まぁでも、僕の性格を考えるとどうせそのうち手を付けた気はする。ブラウザの警告うぜぇとなって、しかしTailscaleのドメインに依存するやり方はいやだろうし、結局スプリットDNS構成になったんじゃないかなぁ…。じゃあいいのか…。と、ここまで書いただけでなんかもうお腹いっぱいなので今回は構成のみ。具体的な手順は次の記事で。

<関連記事>

この記事をいいなと思っていただけた方、よければ高評価・チャンネル登録……はないので、コメント・SNSでシェア・ブックマーク、RSSフィード登録を、よろしくお願い致します。

コメント

コメントする

目次