fix(gateway): http→https 跳转是否带端口改为显式开关(避免生产回归)
练兵发现:跳转写成 return 302 https://$host$request_uri(不带端口), 非 443 端口的新装会跳到 443 打不开。 但直接用'端口 != 443 就带端口'推导会把生产弄坏:生产容器监听 3080, 对外经 frp 映射为 443,带端口会把公网用户导向 https://host:3080。 因此新增 gateway.yml 字段 redirectWithPort(默认 false,行为不变): - 默认:跳 https://host(前置 frp/443 的部署,含现生产) - 全新安装且对外端口=入口端口:install-host.sh 自动置 true 测试补两条:开关为真时带端口、默认(生产形态)不带端口。
This commit is contained in:
@ -44,10 +44,14 @@ type GatewayConfig struct {
|
||||
AuthURL string `yaml:"authUrl,omitempty" json:"authUrl,omitempty"`
|
||||
LoginURL string `yaml:"loginUrl,omitempty" json:"loginUrl,omitempty"`
|
||||
|
||||
ListenPort int `yaml:"listenPort,omitempty" json:"listenPort,omitempty"`
|
||||
ListenTLSPort int `yaml:"listenTlsPort,omitempty" json:"listenTlsPort,omitempty"`
|
||||
RedirectPort int `yaml:"redirectPort,omitempty" json:"redirectPort,omitempty"`
|
||||
Routes []Route `yaml:"routes" json:"routes"`
|
||||
ListenPort int `yaml:"listenPort,omitempty" json:"listenPort,omitempty"`
|
||||
ListenTLSPort int `yaml:"listenTlsPort,omitempty" json:"listenTlsPort,omitempty"`
|
||||
RedirectPort int `yaml:"redirectPort,omitempty" json:"redirectPort,omitempty"`
|
||||
// RedirectWithPort:http→https 跳转时是否带上入口端口。
|
||||
// 默认 false(跳 https://host,适用于前置 frp/443 的场景);
|
||||
// 全新安装且对外端口=入口端口时置 true。
|
||||
RedirectWithPort bool `yaml:"redirectWithPort,omitempty" json:"redirectWithPort,omitempty"`
|
||||
Routes []Route `yaml:"routes" json:"routes"`
|
||||
}
|
||||
|
||||
func defaultGatewayConfig() *GatewayConfig {
|
||||
@ -319,7 +323,15 @@ func RenderGatewayConf(cfg *GatewayConfig) (string, error) {
|
||||
b.WriteString("# 由 HomeDash 后台生成,请勿手改。源: " + gatewayFile + "\n\n")
|
||||
|
||||
if cfg.ListenHTTPS && cfg.RedirectPort > 0 {
|
||||
fmt.Fprintf(&b, "server {\n listen %d default_server;\n server_name _;\n return 302 https://$host$request_uri;\n}\n\n", cfg.RedirectPort)
|
||||
// 跳转目标是否带上入口端口:由 redirectWithPort 显式决定。
|
||||
// 不能用"端口 != 443"推导——生产容器监听 3080,但对外经 frp 映射为 443,
|
||||
// 那种情况下带端口反而把公网用户导向 https://host:3080(打不开)。
|
||||
// 只有在"对外端口就等于入口端口"的部署(如全新安装 13080)才应开启。
|
||||
redirectHost := "$host"
|
||||
if cfg.RedirectWithPort && cfg.ListenPort != 443 {
|
||||
redirectHost = fmt.Sprintf("$host:%d", cfg.ListenPort)
|
||||
}
|
||||
fmt.Fprintf(&b, "server {\n listen %d default_server;\n server_name _;\n return 302 https://%s$request_uri;\n}\n\n", cfg.RedirectPort, redirectHost)
|
||||
}
|
||||
|
||||
// 默认服务器:保留原统一代理的自定义错误页设计。
|
||||
|
||||
@ -389,3 +389,52 @@ func TestPortalKindNoDuplicateRoot(t *testing.T) {
|
||||
t.Fatalf("门户段内 root 指令应恰好 1 次,实际 %d 次:\n%s", n, block)
|
||||
}
|
||||
}
|
||||
|
||||
// redirectWithPort=true 时跳转目标带入口端口(对外端口=入口端口的部署)。
|
||||
func TestRedirectTargetIncludesEntryPort(t *testing.T) {
|
||||
out, err := RenderGatewayConf(&GatewayConfig{
|
||||
ListenPort: 3080, ListenTLSPort: 8443, RedirectPort: 3081, ListenHTTPS: true, RedirectWithPort: true,
|
||||
CertFile: "/etc/nginx/certs/fullchain.pem", KeyFile: "/etc/nginx/certs/privkey.pem",
|
||||
AuthURL: "http://127.0.0.1:3005/auth/verify", LoginURL: "https://login.x.com/login",
|
||||
Routes: []Route{{Host: "x.com", Kind: "portal"}},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("渲染失败: %v", err)
|
||||
}
|
||||
if !strings.Contains(out, "return 302 https://$host:3080$request_uri;") {
|
||||
t.Fatalf("跳转目标未带端口:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
// 标准 443 部署不应出现 :443 后缀。
|
||||
func TestRedirectTargetOmitsPortOn443(t *testing.T) {
|
||||
out, err := RenderGatewayConf(&GatewayConfig{
|
||||
ListenPort: 443, RedirectPort: 3081, ListenHTTPS: true,
|
||||
CertFile: "/etc/nginx/certs/fullchain.pem", KeyFile: "/etc/nginx/certs/privkey.pem",
|
||||
AuthURL: "http://127.0.0.1:3005/auth/verify", LoginURL: "https://login.x.com/login",
|
||||
Routes: []Route{{Host: "x.com", Kind: "portal"}},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("渲染失败: %v", err)
|
||||
}
|
||||
if !strings.Contains(out, "return 302 https://$host$request_uri;") || strings.Contains(out, "$host:443") {
|
||||
t.Fatalf("443 部署跳转目标不正确:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
// 默认(redirectWithPort=false)即使入口不是 443 也不带端口——
|
||||
// 生产就是这种:监听 3080,对外经 frp 是 443,带端口会把公网用户导向 :3080。
|
||||
func TestRedirectDefaultsNoPortOnNonStandardPort(t *testing.T) {
|
||||
out, err := RenderGatewayConf(&GatewayConfig{
|
||||
ListenPort: 3080, RedirectPort: 3081, ListenHTTPS: true, RedirectWithPort: false,
|
||||
CertFile: "/etc/nginx/certs/fullchain.pem", KeyFile: "/etc/nginx/certs/privkey.pem",
|
||||
AuthURL: "http://127.0.0.1:3005/auth/verify", LoginURL: "https://login.x.com/login",
|
||||
Routes: []Route{{Host: "x.com", Kind: "portal"}},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("渲染失败: %v", err)
|
||||
}
|
||||
if !strings.Contains(out, "return 302 https://$host$request_uri;") || strings.Contains(out, "$host:3080") {
|
||||
t.Fatalf("默认不应带端口:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user