文章

Nginx 反代内网 Home Assistant 的踩坑与排查记录

Nginx 反代内网 Home Assistant 的踩坑与排查记录

背景与网络拓扑

最近在折腾 Home Assistant (HA) 的外网访问。为了安全和方便,我没有选择直接进行端口映射,而是采用了如下的网络拓扑:

  • 云服务器(公网节点):安装了 1Panel 面板,使用 OpenResty(Nginx)作为统一的反向代理入口。
  • 组网方式:通过 WireGuard 将云服务器与家里的局域网打通。
  • 后端服务:家里的 Home Assistant 运行在局域网(192.168.6.1:8123)。

外网用户访问云服务器域名 https://ha.example.com 后,Nginx 通过 WireGuard 隧道将请求转发到家里的 HA。

排查时遇到了两个现象:外部请求报 SSL_ERROR_SYSCALL,以及 HA 手机 App 在认证阶段断连。期间调整过 Nginx 配置,但这些调整都不是最终的解决办法。最后换用一个已在国内备案的域名,访问才恢复正常。下面按排查顺序记录。


现象一:外部请求报 SSL_ERROR_SYSCALL

为了进一步提升安全性,我在 Nginx 中配置了 IP 白名单,仅允许我所在省份(如湖北省 IP 段)以及 WireGuard 内网 IP 访问。

起初,我将 allow 和 deny all; 规则直接写在了 Nginx 的 server { ... } 块的最外层。 配置后,使用外部 IP 跑 curl -I https://ha.example.com 测试拦截效果时,并没有像预期那样返回 403 Forbidden,而是报了底层的 SSL 错误:

1
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to ha.example.com:443

当时的排查

当时怀疑 IP 白名单的放置位置影响了请求,因此检查了 allow、deny 所在的配置块。不过,仅凭这条 curl 报错,无法确定断连发生在 Nginx 的哪个处理阶段,也无法证明白名单就是原因。

我尝试将规则移到 location ^~ / { ... } 中。这是排查时做过的配置调整,不能作为这次访问问题的最终解决方案。


现象二:手机 App 登录断连 Received close message during auth phase

调整白名单配置后,我用安卓端 Home Assistant App 登录,输入账号密码后一直转圈,最终登录失败。

查看家里的 HA 后台日志,发现了大量如下报错:

[140570145081920] from 61.242.135.xx (Mozilla/5.0 ... Android 16 ... Home Assistant ...): Disconnected: Received close message during auth phase

日志记录了客户端公网 IP,也记录了连接在认证阶段关闭;它本身不足以证明白名单、转发头或 WebSocket 配置都正确。

排查过程

1. 检查 WebSocket 请求头 检查 Nginx 反代配置时,我看到 1Panel 生成的配置和手动添加的配置都设置了 WebSocket 相关请求头:

1
2
3
4
5
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
...
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;

我清理了重复的请求头配置,固定使用 Connection "upgrade",但手机 App 登录问题仍然存在。原先将请求头冲突判定为断连原因,缺少足够的验证。

2. 尝试关闭 Nginx 代理缓冲

考虑到请求经过 WireGuard 隧道,我还尝试在反代的 location 中添加:

1
proxy_buffering off;

这项改动也没有解决本次问题。此前关于代理缓冲、WireGuard MTU 导致认证帧滞留的分析没有得到验证,不能把它当成根因。


排查时使用过的 Nginx 配置

下面是排查时整理过的 Nginx location 配置,保留作记录。其中的白名单、请求头和缓冲设置都不代表这次问题的最终解决办法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
location ^~ / {
    proxy_pass http://192.168.6.1:8123;

    # -----------------------------------
    # 1. IP 访问控制 (白名单模式)
    # -----------------------------------
    allow 127.0.0.1;
    allow 192.168.0.0/16;   # 家里内网
    allow 10.0.0.0/8;       # WireGuard 等 VPN 网段
    include /www/sites/ha.example.com/hubei_ips.conf; # 引入指定省份的 IP 库
    deny all;

    # -----------------------------------
    # 2. 排查时保留的 WebSocket 相关设置
    # -----------------------------------
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 86400; # 读取上游响应的超时设置

    # 排查时尝试关闭代理缓冲;未证实能解决本次断连
    proxy_buffering off;

    # -----------------------------------
    # 3. 真实 IP 透传
    # -----------------------------------
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

排查时,Home Assistant 的 configuration.yaml 中也配置了反代服务器 IP:

1
2
3
4
http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 192.168.6.x  # 你的 WireGuard Nginx 入口 IP

实际解决办法与总结

最后真正让访问恢复的,是换了一个在国内已备案的域名。换完之后,外网能正常打开 Home Assistant,手机 App 也能登录了。

回头看,前面调整白名单位置、清理 WebSocket 请求头、关闭代理缓冲,都是我排查时做过的尝试,但都没解决这次的问题。至于原来的域名具体在哪个环节出了问题,我手头的记录还不足以判断。先把这个结果记下来,免得以后再遇到类似现象时,又只盯着 Nginx 和 WireGuard 的配置看。

本文由作者按照 CC BY 4.0 进行授权