12 KiB
公网反代「部署设置」对齐本地反代 —— 方案设计
目标:把本地反向代理(
ProxyView.vue规则弹窗)中真正有价值的安全与运维设置,按公网反代的语义迁移到 「公网反代」页(/public-proxy,ProxyRemoteView.vue)的「部署设置」中,由 havline-agent 生成对应的 Nginx vhost 配置。原则:公网反代只管「公网入口」这一层。凡是会破坏 frps vhost 路由、或本质属于「本机内网分流」的选项, 一律不迁移(见「明确不做」一节)。
更新日期:2026-09-22
建议
「部署设置」弹窗从现在的单页改为三个 Tab(基础 / 安全 / 高级),与本地反代弹窗结构对齐:
基础:启用 HTTPS、强制跳转、WebSocket、上传大小、代理超时
安全:IP 白名单 / 黑名单、Basic Auth(用户名 + 可选密码)
高级:仅 TLS 1.3、安全响应头、限流、连接数限制
第一梯队 7 项都是 agent 模板内改动 + Havline 参数透传,风险低、见效快;第二梯队 3 项需要 agent 多写一个 http 上下文文件。说一声我从第一梯队开始做,或者指定要哪几项。
一、现状对照
本地反代(ProxyView.vue)规则弹窗现有设置项
| 分组 | 设置项 |
|---|---|
| 基础 | 名称、前端域名、目标地址、启用 HTTPS、HTTP 跳转 HTTPS、启用规则 |
| 安全 · IP 访问控制 | 仅中国大陆 IP、黑名单模式 + 列表、白名单模式 + 列表 |
| 安全 · 认证 | Basic Auth(用户名 + 自定义密码) |
| 安全 · 流量控制 | 请求限流(速率 / 突发)、连接数限制(每 IP 并发) |
| 安全 · 高级 | 忽略后端 TLS 证书、使用目标 Host 头、仅 TLS 1.3、安全响应头 |
| Nginx | auto / custom 自定义配置 |
公网反代(agent nginxRoute 模板)现有能力
internal/agent/server.go 的 routeRequest 与 nginxRoute 已支持:
domain/upstream(127.0.0.1:<frps vhost 端口>)/tls+cert_dir;websocket(Upgrade / Connection 头);redirect_https(80 → 443 的 301 + 443 TLS server 两块);allow_ips(location 内 allow + deny all);basic_auth_user(密码由 agent 自动生成并落盘 htpasswd);- 默认头:Host / X-Real-IP / X-Forwarded-For。
Havline 侧对应链路:ProxyOptions(internal/frp/model.go)→ DeployAgentRoute → AgentClient.SyncRouteEx
→ PUT /api/v1/routes。
二、迁移清单
第一梯队:纯模板级改动,无 Nginx 上下文限制(建议先做)
| # | 功能 | 对应本地项 | 生成的 Nginx 指令 |
|---|---|---|---|
| 1 | 启用 HTTPS 开关 | 启用 HTTPS | 关闭时仅 listen 80;开启且证书已推送时 listen 443 ssl |
| 2 | 安全响应头 | 安全响应头 | add_header Strict-Transport-Security / X-Frame-Options / X-Content-Type-Options / Referrer-Policy |
| 3 | 仅 TLS 1.3 | 仅 TLS 1.3 | ssl_protocols TLSv1.3; |
| 4 | 客户端最大上传大小 | (本地为全局默认) | client_max_body_size 50m;(server 级,按规则可覆盖) |
| 5 | 代理超时 | (本地固定 3600s) | proxy_read_timeout 3600s;(长轮询 / 流媒体 / 大文件必需) |
| 6 | IP 黑名单 | 黑名单模式 | location 内 deny x.x.x.x;(与现有白名单对称) |
| 7 | Basic Auth 自定义密码 | Basic Auth 密码 | 用表单密码写 htpasswd;留空则保持「首次自动生成」 |
第二梯队:需要 Nginx http 上下文或额外数据下发(成本中等)
| # | 功能 | 关键约束 |
|---|---|---|
| 8 | 请求限流 | limit_req_zone 只能出现在 http 块。agent 需额外生成 havline-limits.conf(宝塔将 vhost 目录 include 在 http{} 内,因此该文件顶层指令即 http 上下文),各域名 server 内 limit_req zone=havline_<域名>_req burst=N nodelay; |
| 9 | 连接数限制 | 同上,limit_conn_zone + limit_conn havline_<域名>_conn N; |
| 10 | 仅中国大陆 IP | 需 geo 模块 + 中国 IP 段下发到 VPS(Havline 已有 china cidr 模块,7456 条 v4),下发体积最大、成本最高,建议最后做 |
明确不做(字段 / 语义不匹配)
| 功能 | 原因 |
|---|---|
| 使用目标 Host 头 | 公网回源目标是 frps 的 vhost 端口,frps 靠 Host 头匹配隧道。改成上游 Host 会让 vhost 无法路由,直接 404。必须保持 $host |
| 忽略后端 TLS 证书 | 公网 upstream 是 http://127.0.0.1:<vhost端口>,不涉及上游 TLS 校验,无作用 |
| 自定义 upstream 覆盖 | 技术上可行,但会让流量绕过 frps 隧道(健康检查「隧道」必然 ✗),语义属于「本机内网分流」,只在本地反代页做 |
三、字段契约
两侧 JSON key 必须严格一致(历史教训:字段未逐项对照导致多轮返工)。
| 显示名 | ProxyOptions(Go,internal/frp/model.go) |
JSON key | TS(web/src/api/types.ts) |
routeRequest(agent) |
Nginx 指令 |
|---|---|---|---|---|---|
| 启用 HTTPS | HTTPSDisabled bool |
https_disabled |
https_disabled?: boolean |
HTTPSDisabled bool |
关闭时不出 listen 443 |
| 上传大小 | ClientMaxBodySize string |
client_max_body_size |
client_max_body_size?: string |
ClientMaxBodySize string |
client_max_body_size 50m; |
| 代理超时 | ProxyReadTimeout string |
proxy_read_timeout |
proxy_read_timeout?: string |
ProxyReadTimeout string |
proxy_read_timeout 3600s; |
| 安全响应头 | SecurityHeaders bool |
security_headers |
security_headers?: boolean |
SecurityHeaders bool |
add_header ... |
| 仅 TLS 1.3 | TLS13Only bool |
tls13_only |
tls13_only?: boolean |
TLS13Only bool |
ssl_protocols TLSv1.3; |
| IP 黑名单 | DenyIPs []string |
deny_ips |
deny_ips?: string[] |
DenyIPs []string |
deny x; |
| Basic Auth 密码 | BasicAuthPassword string |
basic_auth_password |
basic_auth_password?: string |
BasicAuthPassword string |
写入 htpasswd |
| 请求限流(速率) | RateLimitRate int |
rate_limit_rate |
rate_limit_rate?: number |
RateLimitRate int |
limit_req_zone ... rate=Nr/s; |
| 请求限流(突发) | RateLimitBurst int |
rate_limit_burst |
rate_limit_burst?: number |
RateLimitBurst int |
limit_req ... burst=N |
| 连接数限制 | ConnLimitMax int |
conn_limit_max |
conn_limit_max?: number |
ConnLimitMax int |
limit_conn_zone / limit_conn |
| 仅中国大陆 | ChinaOnly bool |
china_only |
china_only?: boolean |
ChinaOnly bool |
geo + allow/deny |
约束:
- 这些字段只作用于公网 Nginx 模板,绝不写入 frpc.toml(与现有
websocket/redirect_https等一致,在ProxyOptions上以注释标明); - 「启用 HTTPS」采用反向字段
https_disabled:字段缺省时保持现有行为(证书推送成功即启用 TLS), 保证新 Havline + 旧 agent、旧数据不出现行为突变。
四、实施步骤
阶段 1:第一梯队后端(agent + Havline)
internal/agent/server.gorouteRequest增加第一梯队字段;nginxRoute模板按字段输出指令:listen 443受HTTPSDisabled控制;redirect_https仅在 TLS 实际启用时生成;client_max_body_size/proxy_read_timeout/ 安全响应头 /ssl_protocols/deny列表;- Basic Auth:传入密码时
htpasswd -nbB写入,未传则沿用现有「首次自动生成 + 复用」。
internal/frp/model.go:ProxyOptions增加同名 Go 字段(含注释)。internal/frp/agent_client.go:SyncRouteEx参数改为传结构体(当前 10 个位置参数已到上限,继续追加易错):保留type RouteSpec struct { Domain, Upstream, CertDir string TLS, WebSocket, RedirectHTTPS, HTTPSDisabled bool VhostPort int AllowIPs, DenyIPs []string BasicAuthUser, BasicAuthPassword string ClientMaxBodySize, ProxyReadTimeout string SecurityHeaders, TLS13Only bool } func (c *AgentClient) SyncRouteSpec(ctx, spec RouteSpec) (map[string]any, error)SyncRoute/SyncRouteEx作为兼容包装。internal/frp/service.go:DeployAgentRoute从proxy.Options组装RouteSpec透传。
阶段 2:第一梯队前端
web/src/api/types.ts补FrpProxyOptions字段;ProxyRemoteView.vue「部署设置」弹窗改为三个 Tab(与本地反代结构对齐):- 基础:启用 HTTPS、HTTP 强制跳转、WebSocket、上传大小、代理超时;
- 安全:IP 白名单、IP 黑名单、Basic Auth(用户名 + 可选密码);
- 高级:仅 TLS 1.3、安全响应头(第二梯队上线后再加限流/连接数/中国 IP)。
阶段 3:第二梯队(按需)
- agent 增加
havline-limits.conf生成器(http 上下文 zone 定义)+ 每域名 server 引用; - 中国 IP:Havline 将 china cidr 列表推送到 agent(
/var/lib/havline-agent/geo/china.conf),agent 生成geo块。
五、验证方案
每次改动后全量跑(项目既有闭环):
cd f:\Havline-main\Havline-1.0.2
gofmt -w internal/agent/server.go internal/frp/model.go internal/frp/agent_client.go internal/frp/service.go
go build ./... ; go vet ./internal/frp ./internal/agent ./internal/api
go test ./internal/frp ./internal/api
cd web ; npx vue-tsc --noEmit ; npx vite build
agent 二进制需按既有流程交叉编译(amd64 + arm64)并升级:
$env:GOOS='linux'; $env:GOARCH='amd64'; $env:CGO_ENABLED='0'
go build -trimpath -o internal/frp/agentbin/havline-agent-linux-amd64.new ./cmd/havline-agent
# arm64 同理;Move-Item 覆盖后,在「公网 Agent」页执行「升级 Agent」
VPS 侧实测(部署 7023 规则后):
nginx -t # 语法
cat /www/server/panel/vhost/nginx/havline-7023.36800.cc.conf # 配置内容(或页面「查看配置」)
curl -I http://7023.36800.cc # 强制跳转 → 301
curl -sI -H 'Host: 7023.36800.cc' http://127.0.0.1:9080 # 回源自检
curl -k -sI https://7023.36800.cc # 响应头(HSTS / X-Frame-Options)
openssl s_client -connect 7023.36800.cc:443 -tls1_2 </dev/null | head -3 # 仅 TLS1.3 时应握手失败
curl -sI -H 'Host: 7023.36800.cc' -d @bigfile http://127.0.0.1:9080 # 上传大小限制
for i in $(seq 1 50); do curl -s -o /dev/null -w '%{http_code} ' http://7023.36800.cc/; done # 限流 → 429(第二梯队)
排查口径沿用现有约定:健康检查五段徽标(DNS / 证书 / 隧道 / 服务 / 反代生效态)任一 ✗, 悬停看详情定位断点,不再靠猜。
六、兼容性与回滚
| 场景 | 期望行为 |
|---|---|
| 新 Havline + 旧 agent | 旧 agent 忽略未知 JSON 字段(Go 默认行为),新选项静默不生效 → 部署前必须先「升级 Agent」,页面提示 |
| 旧 Havline + 新 agent | 新字段缺省,行为与现状一致(https_disabled 缺省 = 启用 TLS) |
| frps v0.68.0 / v0.71.0 | 与本方案无关(作用于 Nginx vhost,不依赖 dashboard API) |
| 回滚 | 关闭对应开关后重新部署即可恢复;agent 侧每域名配置为整文件覆盖写,无残留 |
七、附:与既有文档的关系
docs/agent-proxy-roadmap.md:功能梯队总览(本方案是其「第二批」在部署设置维度的细化);docs/frps-features.md:frps 服务端配置能力对照(frps.toml 层面,与本文的 Nginx 层面互补)。