feat(frp): frps vhost 回源端口支持 80/443,公网反代在该配置下不再适用

- 服务端表单不再硬性拒绝 vhostHTTPPort=80 / vhostHTTPSPort=443,改为保存时弹窗二次确认;
  确认后请求携带 allow_privileged_vhost_ports 提交并由后端校验放行,未携带标记的请求(含直接调 API)仍被拒绝
- frps.toml 生成改为写入风险注释、不再报错;各规则的公网反代回源地址随 80/443 自动跟随
- 「公网反代」页检测到 80/443 后显示说明卡片并隐藏统计/Nginx/规则台账/漂移区块,
  同时跳过 agent 的路由、漂移与健康请求
- 后端 DeployAgentRoute 与 RedeployAllRoutes(含漂移一键修复)拒绝下发,自动下发路径 syncAgentRoute 跳过
- 新增 internal/frp/vhost_port_test.go:校验、二次确认、frps.toml 生成、反代回源跟随与守卫对照
This commit is contained in:
2026-10-11 15:18:32 +08:00
parent 50b8cc1351
commit 0f68101503
11 changed files with 370 additions and 22 deletions
+18 -4
View File
@@ -1502,6 +1502,12 @@ func (s *Service) PushFRPSConfigToAgent(ctx context.Context, serverID int64) err
return client.PushFRPSConfig(ctx, content)
}
// errPrivilegedVhostNoPublicProxy 说明:frps 回源端口落在 80/443 时,VPS 上由 frps 直接对外,
// 没有 Nginx 的位置,公网反代(路由下发)整体不适用。
func errPrivilegedVhostNoPublicProxy() error {
return fmt.Errorf("该服务端的 frps 回源端口为 80/443:VPS 上由 frps 直接对外,公网反代(Nginx)不适用也无需部署;如需公网反代,请到「公网服务端」页把回源端口改回 8080/8443")
}
// agentUpstreamFor 返回该规则反代时的回源地址(frps vhost 端口),端口来自服务端表单自定义,
// 未配置时用默认 8080/8443,与 frps.toml 生成器同源。
func (s *Service) agentUpstreamFor(ctx context.Context, proxy Proxy) (string, error) {
@@ -1840,6 +1846,14 @@ func (s *Service) DeployAgentRoute(ctx context.Context, proxyID int64) error {
if !proxy.Enabled || (proxy.Type != "http" && proxy.Type != "https") || len(proxy.CustomDomains) == 0 {
return fmt.Errorf("只有启用的 HTTP/HTTPS 规则可以部署公网反代")
}
server, serr := s.store.GetServer(ctx, proxy.ServerID)
if serr != nil {
return serr
}
// 80/443 回源端口的服务端由 frps 直接对外,VPS 上没有 Nginx 的位置,直接拒绝部署
if privilegedVhostPorts(server.Options) {
return errPrivilegedVhostNoPublicProxy()
}
client, ok := s.agentClient(ctx, proxy.ServerID)
if !ok {
return fmt.Errorf("公网 agent 未配置或 Token 未保存")
@@ -1855,10 +1869,6 @@ func (s *Service) DeployAgentRoute(ctx context.Context, proxyID int64) error {
return uerr
}
// vhost 端口供 agent 回源自检用
server, serr := s.store.GetServer(ctx, proxy.ServerID)
if serr != nil {
return serr
}
vhostPort := server.Options.VhostHTTPPort
if vhostPort <= 0 {
vhostPort = 8080
@@ -2116,6 +2126,10 @@ func (s *Service) syncAgentRoute(ctx context.Context, proxy Proxy) {
if !proxy.Enabled || (proxy.Type != "http" && proxy.Type != "https") || len(proxy.CustomDomains) == 0 {
return
}
// 80/443 回源端口的服务端由 frps 直接对外:任何自动下发都要跳过
if server, err := s.store.GetServer(ctx, proxy.ServerID); err == nil && privilegedVhostPorts(server.Options) {
return
}
client, ok := s.agentClient(ctx, proxy.ServerID)
if !ok {
return