Home Assistant 統一遠端存取架構實作筆記

陳柏仁/

2025-09-15

/
--- views
分享如何使用 Nginx Proxy Manager 與 AdGuard Home 實現 Home Assistant 的內外網統一存取與 SSL 加密。

緣起

去年的九月,家裡的網路環境迎來了一次變動。從原本的中華電信切換到了社區網路業者「宅急網」,同時也更換了一台新的路由器。

在這次的轉換過程中,我取得了一個公共 IP 以及業者分配的特定通訊埠 (Port)。這讓我開始思考,如何重新架設家裡的網路架構,以達成幾個目標:

  1. 統一入口:無論我是在家裡使用 Wi-Fi,還是在外面使用行動網路,都能透過同一個網域名稱 (Domain Name) 連線到我的 Home Assistant 服務。
  2. 安全性:必須全程使用 SSL 加密,確保資料傳輸的安全。
  3. 擴充性:建立一個通用的反向代理 (Reverse Proxy) 機制,未來如果架設了其他服務(如 NAS、個人網站),也能輕鬆地透過同樣的方式對外開放。

這篇文章旨在紀錄我學習與架設這套網路架構的過程。這段期間,我深入研究了 NAT LoopbackSplit-Brain DNSReverse Proxy 以及 SSLCertificate 等網路概念,並將這些技術應用在實際的居家物聯網環境中。

遭遇困難:NAT Loopback 與內網存取

在初步設定時,我遇到了一個經典的網路問題:NAT Loopback (回流)

當我在家裡(內網)嘗試透過「外部網域名稱」連線到自己的服務時,封包會先送往路由器的 WAN 端,理論上路由器應該要聰明地將其轉發回內網的伺服器。然而,並非所有路由器都能良好地支援或預設開啟 NAT Loopback 功能,這導致我在內網時無法用 Domain Name 連線,只能切換回內部 IP (如 192.168.x.x)。

這種「在家用 IP,在外用 Domain」的切換方式非常不直覺,也不符合我追求「統一入口」的目標。

解決方案:Split-Brain DNS 與反向代理

為了解決上述問題並達成目標,我採用了結合 Nginx Proxy ManagerSplit-Brain DNS 的架構。

  • 外部存取 (WAN):透過 DuckDNS 更新動態 IP,路由器進行通訊埠轉發 (Port Forwarding),流量進入 Nginx Proxy Manager 進行反向代理與 SSL 解密。
  • 內部存取 (LAN):利用 Split-Brain DNS (分區 DNS) 技術。在內網架設 DNS Server (AdGuard Home),將對域名的查詢直接解析為內網 IP。這樣流量就不會繞出去外網,直接在內網完成傳輸,既解決了 NAT Loopback 問題,也提升了連線速度。

系統核心架構

  • 單一入口 URL: https://my-domain.duckdns.org:<PORT>
  • 伺服器: Raspberry Pi 4 (IP: 192.168.x.x)
  • 核心軟體 (Home Assistant Add-ons):

    • Nginx Proxy Manager: 網路總閘口,處理 HTTPS 與憑證。
    • AdGuard Home: 內網 DNS 伺服器,負責 DNS Rewrite。
    • DuckDNS: 動態域名服務 (DDNS)。

連線流程圖解

1. 外部網路 (WAN) 連線流程

當我們在外面使用 4G/5G 網路時:

graph TB
    A["1. 瀏覽器輸入 URL<br/>(使用者裝置)"]
    B["2. 公共 DNS 伺服器<br/>(網際網路)"]
    C["3. 路由器 Router<br/>(外部 IP)"]
    C1["→ 通訊埠轉發<br/>(Port Forwarding)<br/>轉發至 192.168.x.x"]
    D["4. Nginx Proxy Manager<br/>(Raspberry Pi)"]
    D1["→ 反向代理<br/>(Reverse Proxy)<br/>SSL 解密與轉發"]
    E["5. Home Assistant"]

    A -->|"① DNS 查詢"| B
    B -->|"② 回應外部 IP"| A
    A -->|"③ HTTPS 請求"| C
    C --> C1
    C1 -->|"④ 轉發請求"| D
    D --> D1
    D1 -->|"⑤ 轉發請求"| E
    E -.->|"⑥ 回應"| D1
    D1 -.->|"⑦ 加密回應"| C1
    C1 -.-> C
    C -.->|"⑧ HTTPS 回應"| A

    classDef user fill:#d4edda,stroke:#155724,stroke-width:2px;
    classDef network fill:#cce5ff,stroke:#004085,stroke-width:2px;
    classDef device fill:#f8d7da,stroke:#721c24,stroke-width:2px;
    classDef service fill:#fff3cd,stroke:#856404,stroke-width:2px;

    class A user;
    class B network;
    class C,C1 device;
    class D,D1,E service;

2. 內部網路 (LAN) 連線流程

這是最巧妙的部分。當我們連上家裡 Wi-Fi 時,DNS 查詢會被攔截:

graph TB
    A["1. 瀏覽器輸入 URL<br/>(使用者裝置)"]
    B["2. 路由器 DHCP Server<br/>(家庭網路)"]
    C["3. AdGuard Home<br/>(內部 DNS)"]
    C1["→ DNS 改寫<br/>(DNS Rewrite)<br/>解析為 192.168.x.x"]
    D["4. Nginx Proxy Manager<br/>(Raspberry Pi)"]
    D1["→ 反向代理<br/>(Reverse Proxy)<br/>SSL 處理與轉發"]
    E["5. Home Assistant"]

    A -->|"① 查詢 DNS 伺服器"| B
    B -->|"② 指定 DNS 為 AdGuard"| A
    A -->|"③ 查詢網域 IP"| C
    C --> C1
    C1 -->|"④ 回應內部 IP<br/>(192.168.x.x)"| A
    A -->|"⑤ HTTPS 請求<br/>(直接連內網 IP)"| D
    D --> D1
    D1 -->|"⑥ 轉發請求"| E
    E -.->|"⑦ 回應"| D1
    D1 -.->|"⑧ 加密回應"| A

    classDef user fill:#d4edda,stroke:#155724,stroke-width:2px;
    classDef device fill:#f8d7da,stroke:#721c24,stroke-width:2px;
    classDef service fill:#fff3cd,stroke:#856404,stroke-width:2px;

    class A user;
    class B device;
    class C,C1,D,D1,E service;

詳細實作筆記

以下紀錄關鍵的設定參數,作為日後維護的參考。

1. DuckDNS (DDNS)

僅負責更新域名對應的 IP 位址。注意這裡 不處理 SSL,將憑證管理交給 NPM 統一處理。

  • Domain: my-domain.duckdns.org
  • Token: XXXXXX
  • 設定: accept_terms: false (關閉 Let's Encrypt,避免與 NPM 衝突)

2. Nginx Proxy Manager (NPM)

系統的守門員。

  • Port Mapping: 設定將傳入的 Port 對應到容器內的 443 Port。
  • Proxy Host:

    • Forward Hostname: 192.168.x.x
    • Forward Port: 8123 (Home Assistant 的預設 Port)
    • SSL: 開啟 Force SSL 與 HTTP/2 Support,並在此申請 Let's Encrypt 憑證。

3. AdGuard Home (Split-Brain DNS 實作)

  • DNS 改寫 (DNS Rewrite):

    • 網域: my-domain.duckdns.org
    • IP 位址: 192.168.x.x
    • 效果:所有在家裡對這個域名的查詢,都會直接得到內網 IP,完全繞過外網。

4. 路由器設定

  • DHCP: 將主要 DNS 伺服器設定為 Raspberry Pi 的 IP (192.168.x.x),確保家中所有設備都自動使用 AdGuard Home 進行解析。
  • Port Forwarding: 將 ISP 提供的外部 Port 轉發到 Raspberry Pi 的 Port。

5. Home Assistant 設定

最後,需要讓 Home Assistant 信任這個架構:

  1. 網路設定:將網際網路和本地網路的 URL 都設定為 https://my-domain.duckdns.org:<PORT>
  2. configuration.yaml: 加入 trusted_proxies 設定,允許來自 Docker 網路的轉發請求,並正確讀取 X-Forwarded-For 標頭以識別真實來源 IP。
http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 172.30.xx.0/24 # NPM 所在的 Docker 網路區段

結語

透過這次的架設過程,不僅解決了實際的生活運用問題,更讓我對網路通訊協定有了更深一層的認識。看著手機無論切換 5G 還是 Wi-Fi,都能順暢且安全地連線到家中的控制中心,這種「無縫接軌」的體驗實在令人滿意。