fix(gateway): 被代理站点的 favicon 免登录直通上游(而非门户静态文件/登录墙)
承接上一提交:把门户 favicon 从代理块里去掉后暴露两个问题:
- requireLogin 的站点 /favicon.ico 变成 302(浏览器标签与图标抓取都取不到);
- 而 favicon 本来就不该要求登录。
改为:代理块显式写
location = /favicon.ico { auth_request off; proxy_pass <上游>; ... }
location = /favicon.svg { auth_request off; proxy_pass <上游>; ... }
(https/自签上游附带 proxy_ssl_verify off)。
效果:浏览器标签显示各站点自己的图标、无需登录;抓取端也因此能在未登录时取到真实图标。
门户页/登录页/默认 404 块仍提供门户自己的 favicon。测试断言同步改为"直通上游"语义。
This commit is contained in:
@ -306,6 +306,24 @@ func writeRouteServer(b *strings.Builder, cfg *GatewayConfig, r Route) error {
|
||||
for _, p := range r.BypassPaths {
|
||||
writeProxyLocation(b, r, p, true)
|
||||
}
|
||||
// favicon 免登录直通上游。
|
||||
// 理由:① 图标不是敏感资源,未登录时浏览器标签也该显示站点自己的图标;
|
||||
// ② 导航项/历史条目的图标抓取依赖它在未登录时可取。
|
||||
// 之前这里是"在门户 root 里 try_files",等于把每个被代理站点的图标都换成门户图标
|
||||
// (实测 pi / ittools / vikunja 存下了同一张门户图标)。
|
||||
if up := strings.TrimSpace(r.Upstream); up != "" {
|
||||
for _, fa := range []string{"/favicon.ico", "/favicon.svg"} {
|
||||
fmt.Fprintf(b, " location = %s {\n auth_request off;\n proxy_pass %s;\n", fa, up)
|
||||
b.WriteString(" proxy_set_header Host $host;\n")
|
||||
b.WriteString(" proxy_set_header X-Real-IP $remote_addr;\n")
|
||||
b.WriteString(" proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n")
|
||||
b.WriteString(" proxy_set_header X-Forwarded-Proto $scheme;\n")
|
||||
if r.InsecureTLS || strings.HasPrefix(strings.ToLower(up), "https://") {
|
||||
b.WriteString(" proxy_ssl_verify off;\n")
|
||||
}
|
||||
b.WriteString(" proxy_redirect off;\n }\n")
|
||||
}
|
||||
}
|
||||
writeProxyLocation(b, r, "", false)
|
||||
b.WriteString("}\n\n")
|
||||
return nil
|
||||
|
||||
@ -494,21 +494,23 @@ func TestBeianInjectionSanitized(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// 代理到上游的 server 块**不得**包含门户自己的 favicon location:
|
||||
// try_files 是在门户 root 里找文件,会把被代理站点的 /favicon.ico 顶成门户图标
|
||||
// (实测 pi / ittools / vikunja 三个站点的导航图标因此变成同一张门户图标)。
|
||||
func TestProxyRouteDoesNotShadowUpstreamFavicon(t *testing.T) {
|
||||
out, err := RenderGatewayConf(&GatewayConfig{ListenPort: 3081, Routes: []Route{
|
||||
{Host: "proxy.example", Upstream: "http://127.0.0.1:8080"},
|
||||
{Host: "portal.example", Kind: "portal"},
|
||||
}})
|
||||
// 代理到上游的 server 块:favicon 必须"免登录直通上游",而不是在门户 root 里 try_files。
|
||||
// try_files 会把被代理站点的 /favicon.ico 顶成门户图标(实测 pi / ittools / vikunja 三站同图)。
|
||||
func TestProxyRoutePassesFaviconThrough(t *testing.T) {
|
||||
out, err := RenderGatewayConf(&GatewayConfig{
|
||||
ListenPort: 3081,
|
||||
AuthURL: "http://127.0.0.1:3005/auth/verify",
|
||||
LoginURL: "https://login.example/login",
|
||||
Routes: []Route{
|
||||
{Host: "proxy.example", Upstream: "http://127.0.0.1:8080", RequireLogin: true, Websocket: true},
|
||||
{Host: "portal.example", Kind: "portal"},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// 切出 proxy.example 的 server 块单独断言,避免被门户块的正常输出干扰
|
||||
blocks := strings.Split(out, "server {")
|
||||
var proxyBlock, portalBlock string
|
||||
for _, b := range blocks {
|
||||
for _, b := range strings.Split(out, "server {") {
|
||||
if strings.Contains(b, "server_name proxy.example;") {
|
||||
proxyBlock = b
|
||||
}
|
||||
@ -519,13 +521,16 @@ func TestProxyRouteDoesNotShadowUpstreamFavicon(t *testing.T) {
|
||||
if proxyBlock == "" || portalBlock == "" {
|
||||
t.Fatalf("server blocks not found\n%s", out)
|
||||
}
|
||||
if strings.Contains(proxyBlock, "location = /favicon.ico") {
|
||||
t.Errorf("代理块不应提供门户 favicon(会顶掉上游图标):\n%s", proxyBlock)
|
||||
if !strings.Contains(proxyBlock, "location = /favicon.ico") {
|
||||
t.Errorf("代理块应有 favicon 直通 location:\n%s", proxyBlock)
|
||||
}
|
||||
if strings.Contains(proxyBlock, "location = /favicon.svg") {
|
||||
t.Errorf("代理块不应提供门户 favicon.svg:\n%s", proxyBlock)
|
||||
if strings.Contains(proxyBlock, "try_files $uri =404;\n }") && strings.Contains(proxyBlock, "location = /favicon.ico { auth_request off; try_files") {
|
||||
t.Errorf("代理块的 favicon 不应走门户静态文件(会顶掉上游图标):\n%s", proxyBlock)
|
||||
}
|
||||
if !strings.Contains(portalBlock, "location = /favicon.ico") {
|
||||
t.Errorf("门户块应保留门户 favicon:\n%s", portalBlock)
|
||||
if !strings.Contains(proxyBlock, "proxy_pass http://127.0.0.1:8080;") {
|
||||
t.Errorf("代理块 favicon 应 proxy_pass 到上游:\n%s", proxyBlock)
|
||||
}
|
||||
if !strings.Contains(portalBlock, "location = /favicon.ico { auth_request off; try_files $uri =404; }") {
|
||||
t.Errorf("门户块应保留门户自己的 favicon:\n%s", portalBlock)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user