Files
Havline/docs/scenario-a-multi-app-https-entry.md
T
mrlee 2a0740a60f
CI / verify (push) Canceled after 0s
Docker Release / build-and-push (amd64, linux/amd64) (push) Canceled after 0s
Docker Release / build-and-push (arm64, linux/arm64) (push) Canceled after 0s
GitHub Release / release (push) Canceled after 0s
Docker Release / create-manifest (push) Canceled after 0s
chore(release): 发布 v1.9.2
- 「公网服务端」新增 Nginx 启停控制(Agent 0.15.2,停止带二次确认)
- frps 启动失败不再误报成功,并增强启动诊断
- frps vhost 端口禁止 80/443,避免与 VPS 上 Nginx 冲突
- 旧服务端配置的 tcp_mux / login_fail_exit 默认值补齐
- 新增安全审查、页面布局方案与场景操作文档
2026-10-07 19:26:02 +08:00

12 KiB
Raw Permalink Blame History

场景 A 可行性分析与操作步骤:多 Web 应用统一入口

日期:2026-10-07 场景:家庭宽带 NAS,多个 Web 应用统一 443 对外,VPS 做公网入口中转,TLS 在 NAS 本地终止 性质:只做分析与步骤说明,不含代码改动 结论:整体可行,但场景描述里有 3 处前提需要修正,否则其中一步会走不通

一、目标链路

用户 → https://app.example.com:443
     → DNS 解析到 VPS IP
     → VPS:443(frps 监听,由 tcp 规则提供)
     → FRP TCP 隧道(remotePort 443)
     → NAS frpc → 127.0.0.1:443
     → Havline 本地 Nginx(TLS 在此终止,按 server_name 分流)
     → 内网应用

这条链路的关键特征:TLS 全程到 NAS 才解开,VPS 只做 TCP 转发,看不到明文。相比「VPS 上终止 TLS」的方案,这是更安全的一种,值得保留。

二、先修正场景描述里的三处前提

前提 1:http:// 跳转 https:// 这一步走不通

原文第 6 步写「按域名建规则(HTTPS + 强制跳转 + 选证书)」。问题在于:浏览器访问 http://app.example.com 走的是 80 端口,而本场景只把 443 通过隧道引到 NAS,80 的流量根本到不了本地 Nginx。

再看 Havline 的实现,http_redirect 的语义是「在同一个监听端口上额外生成一个 301 server 块」:

// internal/nginx/generate.go
if rule.HTTPRedirect && useHTTPS {
    b.WriteString("    return 301 https://$host:$server_port$request_uri;\n")
}

它服务于「同一端口既能收明文又能收 TLS」的场合,无法覆盖 80 端口。如果为了跳转把规则的监听端口设成 80 并开启 HTTPS,生成的会是 listen 80 ssl;,属于错误配置。

处理方式(三选一):

  1. 推荐:在 VPS 层做跳转——VPS 上已有 Nginx 时,加一个 listen 80; server_name app.example.com; return 301 https://$host$request_uri;。这与隧道无关,最简单。
  2. 额外开一条 tcp 隧道(remotePort 80 → 本地 80),再用自定义全局配置在本地 Nginx 手工加跳转。
  3. 接受「只有 https 可访问」,不做跳转。

因此在第 6 步里,HTTP 跳转 HTTPS 开关不要打开。

前提 2:443 只能作为 TCP 规则的远程端口,不能填进 frps 的 vhost 端口

场景第 1 步写「放行 bindPort 与 443」,这是对的;但要避免一个常见混淆:Havline 在生成 frps.toml 时明确禁止 vhostHTTPPort / vhostHTTPSPort 使用 80/443,会直接报错:

frps vhost 回源端口不能使用 80/443(通常由 VPS 上的 Nginx 占用);请在服务端表单改为 8080/8443 后重新生成配置

原因:vhost 端口是给「http/https 类型规则 + VPS Nginx 反代」用的,属于另一条技术路线。本场景用的是 tcp 类型规则,443 是规则的 remotePort,两者互不冲突。

前提 3:DDNS 解析到家庭宽带 IP 并不总是可行

原文最后一段写「域名也可解析到家庭宽带 IP(DDNS 留空自动检测),VPS 仅做隧道;两种都可行」。这条要加前提:

  1. 家宽必须是真正的公网 IPv4(非 CGNAT 大内网),否则外部无法直连到家庭 IP;
  2. 国内运营商普遍封锁家宽的 80/443 入站,即使有公网 IP,443 也可能不通。

只要这两条有一条不满足,「解析到家庭 IP」就等于解析到一个不可用的地址,链路会静默失败。相比之下,解析到 VPS IP 是更稳的选择,也是本场景的推荐形态。

三、项目里的功能支撑(已逐项核对)

场景需要 项目实现 位置
生成/下发 frps.toml FRPSServerConfig(脱敏预览)+ PushFRPSConfigToAgent(下发) frps_template.go、服务端「详情 → frps.toml」
DDNS 自定义 IP(指向 VPS) 自定义 IPv4/IPv6,留空则自动检测本机公网 IP DdnsView.vue
通配符证书 DNS-01 lego dns01,支持 Cloudflare / DNSPod / 阿里云 / 腾讯云 / 火山 dns_provider.go
DNS-01 凭证复用 DDNS 自动复用,无需重填 service.go ddnsSvc.ConfigForDNSZone
FRP TCP 规则 类型 tcp + 远程端口 + 内网目标 FrpView 规则表单
反代规则监听 443 + HTTPS + 出口 监听端口 / 出口多选 / 启用 HTTPS ProxyView.vue
证书自动绑定规则 按域名自动匹配,通配符优先于精确域名 generate.go findCertificate
校验并应用 frpc verify 通过才落盘 manager.go

两点值得注意的细节:

  1. 证书不需要在反代规则里逐个选。生成器按域名找证书,顺序是先试 *.example.com 再试 example.com。所以只要申请了通配符证书,之后新增子域名规则会自动套用。
  2. DNS-01 凭证确实复用 DDNS:prepareApplyByZone 先取 DDNS 配置,再由 dnsCredentialsForDomain 拿到 ddns.Credentials 交给 newDNS01Provider。前提是申请的域名必须落在 DDNS 已托管的 zone 内,否则会触发 DomainsUnderZones 校验失败。

四、操作步骤

步骤 1:VPS 侧准备

  1. 确认 443 空闲:ss -lntp | grep ':443 '。若 VPS 上已有 Nginx 占用 443,需要先停掉或改端口,否则 frps 的 tcp 规则绑定失败。
  2. 放行端口:bindPort(默认 7000)与 443。
  3. 如果 frps.toml 配了 allowPorts 白名单,务必把 443 加进去。
  4. 生成 frps 配置:Havline →「公网服务端」→ 选中该 VPS →「frps.toml」查看同源配置,用「下发 frps 配置」写入 VPS,或在 VPS 上手工放置。
  5. 启动 frps,确认控制端口可连。

步骤 2:DDNS 指向 VPS

  1. 「域名解析」页 → 新建任务 → 服务商选你的 DNS 服务商,填入凭证。
  2. 记录填写完整域名:example.com、*.example.com(通配符便于后续新增子域)。
  3. 点该任务的「自定义 IP」→ IPv4 填 VPS 的 IP(这一步很关键,不填的话 Havline 会去检测 NAS 自己的公网 IP)。
  4. 立即同步一次,确认解析生效:nslookup app.example.com 返回 VPS IP。

步骤 3:申请通配符证书

  1. 「证书」页 → 申请 → CA 选 Let's Encrypt → 校验方式为 DNS-01。
  2. 域名填 example.com 与 *.example.com(一起提交,避免重复申请)。
  3. 凭证无需填写,会自动复用步骤 2 里 DDNS 的凭证。
  4. 申请成功后确认通配符证书落在 wildcard.example.com 目录下。

步骤 4:添加 FRP 服务端

  1. 「公网服务端」→ 添加服务端。
  2. 主机地址填 VPS IP,端口 7000,Token 与 frps 一致。
  3. 若要用本页的「安装 Agent / 下发 frps 配置」能力,同时填好 Agent 地址与 SSH 信息。

步骤 5:添加 FRP TCP 规则

  1. 「公网服务端」→ 选中该服务端 → 新建规则。
  2. 类型 tcp;内网地址 127.0.0.1;内网端口 443;公网远程端口 443。
  3. 命名建议用真实用途(例如 nas-https-entry),不要沿用 ssh-tcp 这类与实际不符的名字。
  4. 启动 frpc,日志应出现该规则的 start proxy success。

步骤 6:建立反向代理规则

入口更正:这一步在「反向代理」页(路由 /proxies),不是「公网反代」页。「公网反代」(/public-proxy)管理的是部署到 VPS 上的 Nginx 反代,属于「VPS 出口」路线(TLS 在 VPS 终止);本场景要的是 NAS 本地 Nginx,必须用「反向代理」页。

页面右上角「新建规则」→ 弹窗有三个页签:基础配置 / 安全设置 / Nginx。

基础配置页签逐字段填写

字段 填写值 说明
名称 NAS 统一入口 选填,建议填,便于列表识别
前端域名 app.example.com 必填;每行一个;不要带端口
监听端口 443 必填;默认值是 80,必须改成 443
监听协议 只勾 IPv4 默认即 IPv4;除非 VPS 侧走 IPv6 否则不必勾 IPv6
目标地址 http://127.0.0.1:8080 换成实际应用地址
出口 只勾「本机 Nginx」 不要勾 Cloudflare 隧道
启用 HTTPS 打开 默认已开
HTTP 跳转 HTTPS 关闭 默认是打开的,必须手动关,原因见前提 1
启用规则 打开 默认已开

证书不需要选,生成器按域名自动匹配步骤 3 的通配符证书(优先 *.example.com)。

两个默认值陷阱

  1. 监听端口 默认是 80,不改的话会以 root 权限在 NAS 上监听 80,与预期不符。
  2. HTTP 跳转 HTTPS 默认是 开。打开后会在 443 端口上额外生成一个明文 301 server 块——对公网访问没有实际作用,反而让配置语义混乱,因此要关掉。

安全设置页签(可选)

  1. 「一键推荐」会填入:HTTPS + 请求限流(10 r/s,burst 20)+ 安全响应头。注意它会把 HTTP 跳转 HTTPS 重新打开,点完要回基础配置再关掉。
  2. 不要开启「仅中国大陆 IP」:TCP 隧道下客户端真实 IP 已丢失(见 5.1),该策略会失效或误判。
  3. 需要额外保护时可开 Basic Auth;「高级 → 仅 TLS 1.3」按需。

Nginx 页签

保持默认的生成模式即可,不需要手工编辑。

保存与生效

  1. 点保存。保存前会做「域名 + 端口」冲突检查,若提示 域名 x:443 已被规则「y」使用,说明该端口已被占用。
  2. 保存成功后后端会自动同步 Nginx 配置并重载;若返回 sync_warning,表示规则已写入但 Nginx 同步失败,需要到日志页排查。
  3. 若 NAS 本地 443 已被其他服务占用,重载会失败——先用 ss -lntp | grep ':443 ' 确认。

步骤 7:验证

  1. frpc 日志:start proxy success,无 start error。
  2. VPS 上确认 443 已被 frps 监听:ss -lntp | grep ':443 '。
  3. 外网探测 TCP:
1..5 | % { (Measure-Command { $c = New-Object Net.Sockets.TcpClient; $c.Connect('VPS_IP', 443); $c.Close() }).TotalMilliseconds }
  1. 端到端验证(这一步才说明链路通):
curl.exe -v --resolve app.example.com:443:VPS_IP https://app.example.com/
  1. 检查返回的证书是否为通配符证书(curl -v 输出中 subject 应为 *.example.com)。
  2. Havline「公网服务端 → 详情」看诊断与可用率;日志页看该服务端运行日志。

五、已知限制

5.1 客户端真实 IP 会丢失

TCP 隧道下,NAS 本地 Nginx 看到的 $remote_addr 是 frpc 来源地址(回环),不是访客真实 IP。影响两处:

  1. Nginx 访问日志里的 IP 全部失真;
  2. Havline 的「中国 IP 段」访问控制(按来源 IP 放行/拦截)会失效——所有请求看起来都来自内网。

若要保留真实 IP,需要 FRP 规则开启 transport.proxyProtocolVersion = "v2",同时本地 Nginx 的监听加 proxy_protocol 并配合 realip 模块。这属于可选增强,需要两端同时改。

5.2 NAS 本地 443 的占用

Havline 要在 NAS 上监听 443。如果 fnOS 系统面板或其他容器已经占用 443,需要先让出端口。可用 ss -lntp | grep ':443 ' 确认。

5.3 「本机出口」与「公网反代(VPS 出口)」是两条不同路线

  1. 本场景用的是本机出口:配置生成在 NAS 本地 Nginx,TLS 在 NAS 终止。
  2. 「公网反代」里另一种形态是 VPS 出口:frps 开 vhost 端口(8080/8443),VPS 上 Nginx 反代回源,TLS 在 VPS 终止。

两者不要混用同一条规则,否则会出现「证书在 NAS、TLS 却在 VPS 终止」的错配。判断方法:看规则表单里「出口」勾了哪些。

六、扩展性

这套结构的好处正如场景描述所说:新增应用只改反代,不动 FRP。

  1. 新建反代规则,监听端口仍填 443,域名换 app2.example.com,上游指向新应用;
  2. 生成器自动套用同一张通配符证书;
  3. 不需要新增 FRP 规则、不需要改 frps、不需要动 DNS(通配符记录已覆盖)。

唯一需要留意的是:所有子域名共用同一条 443 隧道,因此 NAS 本机 Nginx 是单点——它挂了所有应用一起不可用。这是该架构的固有取舍,不是配置错误。