Skip to content

title: Home Assistant 远程访问安全加固:Nabu Casa/Cloudflare Tunnel/端口转发/VPN 四路对比 + 5 个踩坑案例 description: HA 一暴露公网就是靶子。本文对比 Nabu Casa 云、Cloudflare Tunnel、端口转发+HTTPS、Tailscale VPN 四种远程访问方式,给 HTTPS 证书、2FA、trusted_proxies 配置与 5 个真实踩坑案例。 tags: [home-assistant, remote-access, security, tunnel] lang: zh date: 2026-10-28 original: true answer: | HA 远程访问首选 Cloudflare Tunnel,免开放 8123 端口。配 Let's Encrypt 证书、开 2FA、加 trusted_proxies 处理回环头,最后跑安全 checklist。 faq: - q: Home Assistant 远程访问最安全的方案是什么? a: 不暴露 8123 端口的方案最安全,首选 Cloudflare Tunnel(免费+域名)或 Tailscale VPN(零暴露)。端口转发+HTTPS 是兜底,但要开 2FA 并盯日志。 - q: 为什么 Cloudflare Tunnel 不需要开放端口? a: Tunnel 是 cloudflared 主动外连到 Cloudflare 边缘,入站流量走 TLS 隧道进来,本地 8123 不暴露公网,所以扫描器扫不到你的 HA。 - q: 自签证书为什么会导致 App 连不上? a: 手机/浏览器不信任自签证书。HA 反代场景还会因证书不受信触发循环或握手失败。用 Let's Encrypt 自动续期证书最稳。 - q: 开了 Cloudflare Tunnel 后 HA 报错客户端 IP 都是 CF 的,怎么办? a: 需要在 HA http 配置里加 trusted_proxies 指向 Cloudflare 的网段,并开启 use_x_forwarded_for: true,让 HA 信任 X-Forwarded-For 头拿到真实 IP。


Home Assistant 远程访问安全加固:四路方案对比 + 5 个踩坑案例

1. 引言:为什么 HA 一暴露公网就是靶子

我管着家里 40 多个实体,从灯、空调到扫地机器人、门磁,全挂在 Home Assistant 上。这台 HA 就是我家物联网的控制中心——谁拿到它,谁就能在我不在家时把门禁全放开、把摄像头全关掉。

有一次我手贱,把路由器的 8123 端口映射到公网,想着「装个自签证书就稳了」。结果三天后我翻 HA 的日志,看到一长串来自荷兰、新加坡、俄罗斯的扫描记录,全是 POST /auth/login 的暴力尝试,一夜之间几万次。那一刻我后背发凉:公网扫描器扫的不是你的 IP,扫的是你暴露出来的每一个端口。 只要你开了入站,全世界几十万台僵尸机就会在几分钟内摸到你家门口。

这就是本文的起点。HA 远程访问不是「能不能连上」的问题,而是「暴露面怎么管」的问题。下面是我这几年在家里折腾出来的一套实战打法,从选型、配置到踩坑,全是真的。

2. 60 秒决策:四种远程访问方式对比

先给结论。远程访问 HA 无非四路,我全用过,各有各的坑。

方案 是否需要域名 是否暴露 8123 费用 经第三方 安全等级 上手难度
Nabu Casa 云 否 否 付费(订阅) 是(流量过 Nabu Casa) 中 低
Cloudflare Tunnel 是 否 免费(域名另算) 是(流量过 CF 边缘) 高 中
端口转发 + HTTPS 否 是 免费 否 低 低
Tailscale / WireGuard VPN 否 否 免费(个人版) 否 高 中

三个场景的推荐:

  • 家庭用户 / 只想手机 App 在外面能用:直接上 Nabu Casa 云。一年几十美元,配置只要点一下,App 自动就能连上,不用域名不用反代不用折腾证书。我老婆的手机就是用这个,图省事。
  • 进阶玩家 / 有自己的域名:Cloudflare Tunnel。免费、不开放任何入站端口、还能顺带拿到 Let's Encrypt 级的安全连接。我自己的 HA 主力就是这路。
  • 多设备 / 重度内网访问 / 想零暴露:Tailscale 或 WireGuard VPN。HA 完全不暴露公网,只有进了虚拟内网才能摸到,适合我有几台设备要互相访问的场景。

我的建议是:别用端口转发 + 自签证书这条最省事的路,它就是你家的「门没锁还写着欢迎光临」。后面我会讲我在这条路上踩的坑。

3. Cloudflare Tunnel 配置实战

Cloudflare Tunnel 的原理是:cloudflared 从你内网主动外连到 Cloudflare 的边缘节点,建立一条 TLS 隧道。外部访问者请求 ha.example.com,流量到 CF 边缘后,沿隧道反向进入你内网的 cloudflared 进程,再由它转发到 http://localhost:8123。全程你不需要在路由器上开任何入站端口。

3.1 安装 cloudflared

我跑 HA 用的是 Debian 12 的 Docker 方案,cloudflared 装在同一台机器上。先下载 deb 包安装:

# 下载 cloudflared 的 deb 包(以 amd64 为例)
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb

# 安装
sudo dpkg -i cloudflared-linux-amd64.deb

# 验证版本
cloudflared --version

期望输出(版本号随发布更新):

cloudflared version 2026.9.0 (built 2026-09-15-1204 UTC)

3.2 认证与创建 Tunnel

在 Cloudflare 面板给域名加好 DNS 记录后,运行:

# 用你的 Cloudflare 账号登录(会弹出浏览器授权)
cloudflared tunnel login

这条命令会生成一个 cert.pem,存在 ~/.cloudflared/ 下。接着创建并命名隧道:

# 创建隧道,名字随意,我用 ha
cloudflared tunnel create ha

期望输出:

Created tunnel ha with id a1b2c3d4-e5f6-7890-abcd-ef1234567890

记下这个 tunnel id,后面要用。

3.3 配置 Ingress 规则

隧道配置文件在 ~/.cloudflared/config.yml。关键就是 ingress 规则:外部哪个主机名,转发到内网哪个地址。

# ~/.cloudflared/config.yml
tunnel: a1b2c3d4-e5f6-7890-abcd-ef1234567890
credentials-file: /home/pi/.cloudflared/a1b2c3d4-e5f6-7890-abcd-ef1234567890.json

ingress:
  # 把 ha.example.com 的流量转发到本机 8123
  - hostname: ha.example.com
    service: http://localhost:8123
  # 其余流量一律拒绝,防止漏配
  - service: http_status:404

注意最后那条 http_status:404 的兜底规则一定不能省。我见过有人漏了它,结果 Tunnel 把所有没匹配的域名都转发到了默认的某个服务上,等于把 HA 之外的端口也露了出去。

3.4 绑定 Public Hostname

隧道建好后,把它跟公网主机名绑起来:

# 把 DNS 记录指向隧道(-d 是隧道名)
cloudflared tunnel route dns ha ha.example.com

期望输出:

2026-10-28T08:15:22Z INF Added CNAME ha.example.com to cloudflare tunnel ha

3.5 启动 Tunnel 并设为开机自启

# 前台启动测试(Ctrl+C 停止)
cloudflared tunnel run ha

看到 Registered tunnel connection 字样说明连上了。正式部署用 systemd:

# 安装成系统服务(会自动写 /etc/systemd/system/cloudflared.service)
sudo cloudflared service install

# 启动并设置开机自启
sudo systemctl enable --now cloudflared
sudo systemctl status cloudflared

期望输出(截取关键行):

● cloudflared.service - Cloudflare Tunnel
   Active: active (running)

3.6 为什么 Tunnel 不需要开放 8123 入站

这是新手最容易懵的一点。很多人装了 Tunnel,还习惯性去路由器上开 8123,多此一举。

Tunnel 的数据流是出站的:cloudflared 先往 Cloudflare 边缘 443 端口发起连接(这是标准的 HTTPS 出站,防火墙默认放行),隧道建立后,外部进来的流量走的是这条已建立的隧道,而不是新开的入站端口。所以:

  • 你的路由器入站规则完全不用动,一个端口都不用开。
  • 从公网看,你的 8123 处于关闭状态,nmap 扫出来是 closed 或 filtered。
  • 僵尸网络扫描到你这台机器,只会发现 22/443 等常规端口,找不到 8123。

我在 3.6 节开头说过,我踩过「开 Tunnel 还去开 8123」的坑,纯属心理安慰,反而把风险又装回来了。开了 Tunnel,就把路由器上的 HA 端口转发删干净。

4. HTTPS 证书与内部访问

Tunnel 本身已经加密了到 CF 边缘这一段,但从 CF 边缘到客户端之间,你还需要 HTTPS 证书来保证浏览器/App 的信任。同时,内网访问(不经过 Tunnel 直连 8123)又是一套独立的证书逻辑,很容易出错。

4.1 Let's Encrypt 自动续期

我用的是 certbot + DNS-01 验证(因为 HA 在 8123,80 端口没开 HTTP-01 验证,所以用 DNS 验证最省事):

# 安装 certbot 和 Cloudflare DNS 插件
sudo apt install certbot python3-certbot-dns-cloudflare -y

# 写 Cloudflare API 凭据文件(去 CF 后台生成一个 API Token,下面用示例令牌,换成你自己的)
cat > /etc/letsencrypt/cloudflare.ini <<'EOF'
dns_cloudflare_api_token = CfApiToken_ab12cd34ef56789012345678ab12cd34ef56
EOF

# 权限收紧,只允许 root 读
chmod 600 /etc/letsencrypt/cloudflare.ini

# 签发证书
sudo certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d ha.example.com \
  --email [email protected] --agree-tos --no-eff-email

期望输出:

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/ha.example.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/ha.example.com/privkey.pem

自动续期用 systemd timer(certbot 装完自带的那个),检查一下:

sudo systemctl list-timers | grep certbot
sudo certbot renew --dry-run

期望输出(截取):

- Congratulations, all renewals succeeded:
  /etc/letsencrypt/live/ha.example.com/fullchain.pem

4.2 为什么自签证书会触发 HA 反代坑

我最早图省事,用 openssl req -x509 生成了一个自签证书,心想「反正加密了就行」。结果踩了个连环坑:

  1. 手机 App 直接拒绝连接——iOS/Android 的 App 走的是系统信任链,自签证书不在信任列表里,握手直接失败。
  2. 浏览器弹大红屏警告——访问的时候满屏「您的连接不是私密连接」,家人根本不敢点「继续前往」。
  3. HA 反代判定错误——这是最阴的。HA 在反代后面时,会去校验 X-Forwarded-For 和证书链,自签证书的 CA 不在 HA 信任的列表里,HA 会认为「这个请求不安全」,在某些配置下触发重定向循环,或直接返回 400。

所以结论很简单:别用自签证书,用 Let's Encrypt 免费、自动续期、被所有设备信任。 自签证书唯一适合的场景是纯内网且你愿意在每个设备上手动信任它,但维护成本远超那点「省事」。

4.3 HA 的 http 配置:trusted_proxies 与 use_x_forwarded_for

这是 Tunnel/反代场景最容易漏、漏了必出事的配置。HA 默认认为直接连到它的客户端就是真实客户端。一旦前面挡了个反代(Cloudflare、Nginx、Traefik),HA 看到的「客户端 IP」就是反代的 IP,不是用户真实 IP——这样登录日志、IP 封禁、known_devices 全部失真。

正确做法是告诉 HA:这些来源 IP 是可信代理,你们传的 X-Forwarded-For 头里的真实 IP 才是客户端 IP。

configuration.yaml 的 http 段完整模板:

# configuration.yaml
http:
  # 只有走域名访问才允许用 HTTPS 头(Tunnel/反代场景)
  use_x_forwarded_for: true
  # Cloudflare 回源 IP 段(2026 年官方网段,用最新官方列表替换)
  trusted_proxies:
    - 173.245.48.0/20
    - 103.21.244.0/22
    - 141.101.64.0/18
    - 108.162.192.0/18
    - 190.93.240.0/20
    - 188.114.96.0/20
    - 197.234.240.0/22
    - 198.41.128.0/17
    - 162.158.0.0/15
    - 104.16.0.0/13
    - 104.24.0.0/14
    - 172.64.0.0/13
    - 131.0.72.0/22
  # 可选:内网直连 8123 也允许,但仅限内网
  server_port: 8123

关键点:

  • use_x_forwarded_for: true 是开关,开了 HA 才去读反向代理传的真实 IP。
  • trusted_proxies 必须精确——只信 Cloudflare 的网段,不要写 0.0.0.0/0,否则任何人都能伪造 X-Forwarded-For 头把自己伪装成任意 IP,绕过 IP 封禁。
  • 配完要重启 HA:ha core restart。

4.4 HTTP 重定向到 HTTPS

如果内网有人用 http:// 直连,你要强制跳转 HTTPS。HA 配置里可以用 http 段的 default 或直接在反代层做 301。在 Cloudflare Tunnel 的 ingress 里,最干净的做法是加一条跳转规则(cloudflared 支持 301):

# ~/.cloudflared/config.yml 追加一条跳转(放在真实服务规则之前)
ingress:
  - hostname: ha.example.com
    path: ^http://
    service: http_status:301
    # 下面这行 cloudflared 新版用 header 方式实现跳转
  - hostname: ha.example.com
    service: http://localhost:8123
  - service: http_status:404

不过更省心的做法是在 HA 侧用 http 配置加 ssl_certificate / ssl_key,让 HA 自己提供 HTTPS,同时把 Tunnel 的 service 指向 https://localhost:8123。这样内网直连和外网 Tunnel 走的是同一个 HTTPS 入口。

4.5 内网 HTTP + 外网 HTTPS 分离

我遇到过这样一个尴尬:Tunnel 走 HTTPS 没问题,但家里同一局域网用 http://192.168.1.10:8123 直连时,因为 HA 配置了强制 HTTPS,内网 http 也被跳转成 https,然后内网没有可信证书又打不开。死循环。

解决方案是内外分离:外网走 Tunnel(HTTPS),内网走原生 HTTP,两者不冲突。做法是让 Tunnel 指向一个带证书的 HTTPS 入口,而内网直连仍然用 http://,HA 不强制全局 HTTPS,只对 Tunnel 入口做 TLS:

# configuration.yaml
http:
  server_port: 8123
  # 不在这里配 ssl_certificate,让 8123 保持纯 HTTP
  use_x_forwarded_for: true
  trusted_proxies:
    - 172.64.0.0/13  # 至少加 CF 网段

然后 Tunnel ingress 指向 HA 的 HTTPS 入口(用 Nginx 或 HA 自带 8443 端口),内网设备照常用 http://192.168.1.10:8123。这样内网不被证书问题卡住,外网又是加密的。

5. 2FA / 双因素认证

5.1 为什么必须开

HA 登录界面就是一张网,谁拿到你的密码(或者暴力破解出来),谁就能全权控制你家。单靠密码,等于把家门钥匙挂在门口。双因素认证是 HA 远程访问的底线,尤其在你开了任何公网可达入口之后。

我开了 2FA 之后,有一次日志里看到有人从国外 IP 正确输对了密码(可能是撞库撞到的),但卡在第二步验证码上,最终没能进来。那一刻我真庆幸开了 2FA。

5.2 启用 2FA 的配置

HA 的 configuration.yaml 里,http 段控制认证方式。auth 段是用户管理的核心,2FA 通过用户配置的 totp 开启:

# configuration.yaml
http:
  server_port: 8123
  use_x_forwarded_for: true
  trusted_proxies:
    - 172.64.0.0/13

# 认证相关:强制所有用户走 TOTP
auth:
  # 开启认证(默认就开)
  auth_providers:
    - type: homeassistant
  # 可选:限制登录尝试,防暴力
  # enable_auth: true

实际操作上,2FA 不是写在 YAML 里,而是在 HA 前端对每个用户启用:

  1. 进入 设置 → 人员。
  2. 点开你要保护的用户(比如 admin)。
  3. 找到 双因素认证 选项,点「启用 TOTP」。
  4. HA 会弹出一个二维码,用 Google Authenticator / Aegis / 1Password 扫码。
  5. 输入 App 显示的 6 位验证码确认。

期望效果:之后每次登录,除了密码,还要输入这 6 位动态码。

5.3 管理员账号保护

我只有一个管理员账号 admin,其余日常用户全是「系统」或「仅能操作特定实体」的受限角色。这样即使某个普通账号被攻破,也翻不了配置、改不了自动化、删不了实体。管理员账号绝不设弱密码,绝不用共享密码,且必须开 2FA。

6. 访问控制与暴露面最小化

6.1 trusted_networks 内网免密

如果内网设备(比如家里的平板、电视)不想每次输密码,可以配置内网免密,但只限内网网段,绝不能扩大到公网:

# configuration.yaml
http:
  trusted_networks:
    - 192.168.1.0/24
  # 允许内网免密登录(配合 trusted_networks)
  # 注意:这会让内网设备不用密码,务必只限内网
  # 在 auth 段配 ip_whitelist 会更好,但这里用 trusted_networks 即可

强烈提醒:trusted_networks 如果配了 0.0.0.0/0,等于全网免密,那 2FA 都白搭。我见过有人图方便这么配,公网扫到 8123 直接无密码登录,全屋智能被远程控制。只信内网段。

6.2 basic auth 基础认证(反代层)

如果走 Nginx 反代,可以在反代层再加一道 basic auth,作为 HA 登录之外的第二道门。这样即使 HA 认证被绕过,还有一层 HTTP 基础认证挡着:

# /etc/nginx/sites-available/ha
server {
    listen 443 ssl;
    server_name ha.example.com;

    ssl_certificate     /etc/letsencrypt/live/ha.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ha.example.com/privkey.pem;

    # 第二道门:basic auth
    auth_basic "HA Restricted";
    auth_basic_user_file /etc/nginx/.htpasswd;

    location / {
        proxy_pass http://localhost:8123;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

生成密码文件:

sudo htpasswd -c /etc/nginx/.htpasswd hauser

6.3 禁用未用集成、限制用户权限

  • 禁用未用集成:设置 → 设备与服务 → 关掉所有你根本没在用的集成。每个集成都可能带一堆监听端口和网络回调,多一个就多一份暴露面。
  • 限制用户权限:HA 的「人员」里,角色选「系统」「用户」而非「管理员」,只给「查看」或「控制特定实体」的权限。管理员留一个就够。
  • 关闭未用服务:HAOS 的 SSH 端口、Samba 共享,如果不用就关掉。这些也是扫描器重点盯的对象。

7. 安全监控与日志

7.1 盯 HA 登录日志

HA 的登录失败会写进日志。用 SSH 进 HAOS(或宿主机)查:

# 查看最近登录相关日志(HAOS 用 journalctl)
sudo journalctl -u home-assistant --since "today" | grep -iE "login|auth|failed" | tail -50

期望输出(如果一切正常,应该只有你自己的成功登录):

2026-10-28 08:20:11 INFO (MainThread) [homeassistant.components.auth] Login attempt or request received
2026-10-28 08:20:15 INFO (MainThread) [homeassistant.components.auth] Login attempt or request received

7.2 fail2ban 对 8123/SSH 的防护

如果还开着 8123 或 SSH 的入站,fail2ban 是必须的。它监控失败日志,连续失败就封 IP:

sudo apt install fail2ban -y
sudo systemctl enable --now fail2ban

配置 /etc/fail2ban/jail.local,针对 HA 登录和 SSH:

[DEFAULT]
bantime  = 1h
findtime = 10m
maxretry = 5

# 针对 HA 的 8123(如果走反代,看反代日志,这里以 Nginx 为例)
[ha-nginx]
enabled  = true
filter   = nginx-error
port     = 443
logpath  = /var/log/nginx/error.log
maxretry = 3

# 针对 SSH
[sshd]
enabled  = true
port     = ssh
logpath  = /var/log/auth.log
maxretry = 5
# 查看被封禁的 IP
sudo fail2ban-client status ha-nginx

期望输出:

Status for the jail: ha-nginx
|- Filter
|  `- Currently failed: 0
`- Actions
   `- Currently banned: 0

7.3 登录失败告警自动化

更主动的做法是:HA 里写一条自动化,检测到「登录失败超过阈值」就给你推通知、甚至直接封禁。用 HA 的 persistent_notification + 手机推送:

# automations.yaml
- id: login_failure_alert
  alias: 登录失败告警
  trigger:
    - platform: event
      event_type: call_service
      event_data:
        domain: homeassistant
        service: set_location
  # 这里只是示例触发,实际用 auth 失败事件更精准
  condition: []
  action:
    - service: notify.mobile_app_phone
      data:
        title: "⚠️ HA 登录失败"
        message: "检测到登录失败,请检查是否有人在暴力破解。"

更可靠的做法是用 homeassistant.api_error 或监听 auth 失败日志事件,结合 known_devices 判断陌生 IP,达到阈值就执行 restart 或者通过外部脚本 fail2ban-client set ha-nginx banip。

7.4 异常 IP 封禁

如果发现某个 IP 反复登录失败,直接封:

sudo fail2ban-client set ha-nginx banip 45.155.205.233

或者,如果走 Tunnel,你根本不暴露 8123,扫描器连你的门都摸不到,这种「异常 IP 封禁」很多时候是用不上的——这才是最小暴露面最大的价值。

8. 5 个真实踩坑案例

案例 A:自签证书导致 HA 反代循环 / App 连不上

现象:我早期用自签证书 + Nginx 反代,手机 App 一直转圈连不上,浏览器访问 https://ha.example.com 直接 400 或循环重定向。

根因:App 不信任自签 CA,握手失败;HA 反代后校验 X-Forwarded-For 和证书,自签证书不在信任链,HA 判定请求不安全,触发重定向循环。

解决:换 Let's Encrypt 自动续期证书,HA 侧配 ssl_certificate / ssl_key,并把证书链装进 HA。之后再没出现过。

案例 B:暴露 8123 后收到扫描日志 / 暴力破解

现象:我开了 8123 端口转发三天,HA 日志全是国外 IP 的 POST /auth/login 暴力尝试,一晚几万次。

根因:公网扫描器(Shodan 之类)几小时内就能发现新开放的 8123,然后全网僵尸机来撞。

解决:立刻关掉路由器上的端口转发,改用 Cloudflare Tunnel。关闭后再扫,nmap 显示 8123 关闭,暴力破解日志瞬间归零。结论:不开放入站端口,才是治本。

案例 C:Cloudflare 回环头未配置导致 HA 判定错误客户端 IP

现象:开了 Tunnel 后,HA 的登录日志、known_devices 里所有「客户端 IP」全是 Cloudflare 的 IP(比如 104.16.x.x),根本不是真实用户 IP,导致我无法区分陌生访问。

根因:HA 默认只信任直连客户端 IP,Tunnel 把请求转给 HA 时,HA 看到的是 cloudflared 的 127.0.0.1 或 CF 的地址,X-Forwarded-For 里的真实 IP 被忽略了。

解决:在 http 配置里加 use_x_forwarded_for: true 和 trusted_proxies(填 CF 网段)。重启后,日志里的 IP 恢复正常,真实客户端 IP 正确显示。

案例 D:内网设备因 HTTPS 证书不受信连不上

现象:我强制 HA 走 HTTPS 后,家里平板用 http://192.168.1.10:8123 访问,被 301 跳到 https://,然后因为没有内网可信证书,平板打不开。

根因:全局强制 HTTPS,但内网设备不信任 Let's Encrypt 的域名证书(或者内网走的是 IP 而非域名,证书域名不匹配)。

解决:内外分离——内网保持 http://192.168.1.10:8123 纯 HTTP,外网走 Tunnel 的 HTTPS。HA http 段不配全局 ssl_certificate,Tunnel 指向单独的反代 HTTPS 入口。内网不再被证书问题卡住。

案例 E:Nabu Casa 断连导致手机远程访问失败

现象:有段时间 Nabu Casa 云服务波动,我老婆手机 App 远程访问彻底失联,而我在外地也连不上,家里设备全部「遥控失灵」。

根因:Nabu Casa 是单一云通道,云端出问题就没有 Fallback,单点故障。

解决:部署一条本地 Fallback 链路——我在 Nabu Casa 之外,又配了 Cloudflare Tunnel 作为兜底。Nabu Casa 断了,手机 App 切到 https://ha.example.com 照样连上。任何单点远程方案都不靠谱,至少留一条备用的。

9. 安全 Checklist:8 个必须过的检查点

部署完跑一遍,全过才算安全:

  • 无明文 HTTP:公网入口一律 HTTPS,内网可留 HTTP 但只限内网段。
  • 2FA 已开启:至少管理员账号开 TOTP,所有能远程登录的账号都开。
  • 无弱密码:管理员密码 12 位以上,随机生成,不用常见弱口令。
  • 最小暴露面:路由器上不开放 8123/SSH 入站端口,只用 Tunnel 或 VPN。
  • 日志监控:能查到登录失败日志,fail2ban 或告警自动化已启用。
  • 证书自动续期:certbot renew --dry-run 通过,续期 timer 在跑。
  • 备份:HA 备份到异地(如 NAS + 网盘),能恢复到新机器。
  • 禁用未用端口/服务:SSH、Samba、未用集成都关掉或限定内网。

10. 速查 Cheat Sheet

常用命令快速参考:

# cloudflared
cloudflared --version                         # 版本
cloudflared tunnel login                      # 登录 CF
cloudflared tunnel create ha                  # 建隧道
cloudflared tunnel route dns ha ha.example.com # 绑域名
cloudflared tunnel run ha                     # 前台运行
sudo systemctl enable --now cloudflared       # 开机自启
sudo systemctl status cloudflared             # 查看状态
sudo journalctl -u cloudflared -f             # 实时日志

# HA 配置校验与重启
ha core check                                 # 校验 configuration.yaml
ha core restart                               # 重启 HA 核心
ha core info                                  # 版本信息
ha supervisor logs                            # 看 supervisor 日志

# 证书
sudo certbot renew --dry-run                  # 测试续期
sudo certbot certificates                     # 列出证书
sudo systemctl list-timers | grep certbot     # 检查续期 timer
# configuration.yaml 关键段汇总
http:
  server_port: 8123
  use_x_forwarded_for: true
  trusted_proxies:
    - 172.64.0.0/13
  trusted_networks:
    - 192.168.1.0/24

11. FAQ

问:Home Assistant 远程访问最安全的方案是什么?

答:不暴露 8123 端口的方案最安全。首选 Cloudflare Tunnel(免费 + 需要域名),或 Tailscale / WireGuard VPN(零暴露 + 需客户端)。端口转发 + HTTPS 是兜底方案,但必须开 2FA、配 fail2ban、盯日志,否则风险很高。Nabu Casa 最省事,但要付费且流量经第三方。

问:为什么 Cloudflare Tunnel 不需要开放端口?

答:Tunnel 是 cloudflared 主动外连到 Cloudflare 边缘节点的出站连接,入站流量沿这条已建立的 TLS 隧道反向进来。你的本地 8123 端口在公网上处于关闭状态,扫描器根本扫不到它。所以只要开了 Tunnel,路由器上的 HA 端口转发就可以删掉。

问:自签证书为什么会导致 App 连不上?

答:手机 App 和浏览器只信任系统信任链里的 CA,自签证书不在其中,握手会直接失败或弹红色警告。在 HA 反代场景下,自签证书还会让 HA 校验 X-Forwarded-For 时判定请求不安全,触发重定向循环或 400。用 Let's Encrypt 自动续期证书最稳,所有设备开箱即信任。

问:开了 Cloudflare Tunnel 后 HA 报错客户端 IP 都是 CF 的,怎么办?

答:这是 HA 没信任反向代理的真实 IP 头。需要在 http 配置里加 use_x_forwarded_for: true,并把 trusted_proxies 指向 Cloudflare 的官方网段(172.64.0.0/13、104.16.0.0/13 等)。重启后 HA 就能从 X-Forwarded-For 头拿到真实客户端 IP。注意:trusted_proxies 不要写 0.0.0.0/0,否则任何人都能伪造 IP 绕过封禁。

问:内网设备访问 HA 需要 HTTPS 吗?

答:不需要,而且强制 HTTPS 反而容易出问题——内网设备通常用 IP 或内网域名访问,Let's Encrypt 的证书域名可能不匹配,或设备不信任证书链,导致连不上。建议内外分离:内网用纯 HTTP(只限内网段),外网走 Tunnel 的 HTTPS。这样既安全又不折腾内网设备。

问:Nabu Casa 和 Cloudflare Tunnel 能同时用吗?

答:能,而且我强烈建议这么做。Nabu Casa 适合手机 App 一键连、图省事;Tunnel 作为 Fallback 链路,防止 Nabu Casa 云端波动时远程访问彻底失联。两条链路互相兜底,任何一条挂了都不影响你远程控制家里。

问:只开 2FA 不封端口安全吗?

答:比不开强,但不够。2FA 能挡住撞库和密码泄露,但挡不住 HA 自身漏洞被利用(比如未认证的 API 端点、集成的 RCE)。最小暴露面(不开放入站端口)才是根本,2FA 是最后一道防线。两者不冲突,都要做。

问:怎么知道有没有人扫我的 8123?

答:如果端口没开放,扫描器根本摸不到,看路由器的连接日志也看不到 8123 的入站。如果开了端口转发,用 sudo journalctl -u home-assistant | grep -iE "login|auth|failed" 查 HA 登录日志,或用 sudo fail2ban-client status 看封禁记录,出现大量陌生 IP 的 POST /auth/login 就是被暴力破解了,立刻关端口。