fix(haproxy): check-alpn http/1.1 für HTTPS-Backends mit Healthcheck

L7TOUT-Bug: server-Stmt setzt `alpn h2,http/1.1` → Server handelt h2
aus → `option httpchk` sendet HTTP/1.x → Server antwortet nicht →
HAProxy markiert Backend DOWN → 503 für alle Requests. Fix: explizit
`check-alpn http/1.1` an die Server-Direktive wenn Scheme=https UND
Healthcheck aktiv. HTTP-only-Backends bleiben unverändert.

Bonus 1: Inter-Font lokal in public/fonts/ (DSGVO, Performance, Offline-
Dev) — Pattern 1:1 aus netcell-webpanel. Kein Google-CDN-Roundtrip mehr.

Test: TestRender_HTTPSHealthcheckPinsAlpnHTTP1 stellt sicher dass der
Pin gesetzt wird und HTTP-Backends KEIN check-alpn bekommen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Debian
2026-05-12 13:45:47 +02:00
parent 305a3ce992
commit 1bb13e8107
23 changed files with 306 additions and 9 deletions

View File

@@ -102,6 +102,48 @@ func TestRender_HealthCheckPathAddsCheckInter(t *testing.T) {
}
}
func TestRender_HTTPSHealthcheckPinsAlpnHTTP1(t *testing.T) {
// L7TOUT-Bug: ohne `check-alpn http/1.1` handelt der Check h2
// aus (vom server-Stmt geerbt) und hängt, weil option httpchk
// HTTP/1.x sendet. Test stellt sicher dass HTTPS+Healthcheck
// das ALPN für den Check pinnt.
hcp := "/"
v := View{
Backends: []BackendView{
{
Backend: models.Backend{ID: 9, Name: "tls-app", Scheme: "https",
LBAlgorithm: "roundrobin", HealthCheckPath: &hcp, Active: true},
Servers: []models.BackendServer{
{BackendID: 9, Name: "tls-1", Address: "10.0.0.30", Port: 8443, Weight: 100, Active: true},
},
},
{
// Gegenprobe: HTTP-Backend mit Healthcheck darf KEIN
// check-alpn bekommen (ALPN gibt's nur bei SSL).
Backend: models.Backend{ID: 10, Name: "plain-app", Scheme: "http",
LBAlgorithm: "roundrobin", HealthCheckPath: &hcp, Active: true},
Servers: []models.BackendServer{
{BackendID: 10, Name: "plain-1", Address: "10.0.0.31", Port: 80, Weight: 100, Active: true},
},
},
},
}
out := renderView(t, v)
idxTLS := strings.Index(out, "backend eg_backend_9")
idxPlain := strings.Index(out, "backend eg_backend_10")
if idxTLS < 0 || idxPlain < 0 {
t.Fatalf("backend sections missing:\n%s", out)
}
tlsBlock := out[idxTLS:idxPlain]
plainBlock := out[idxPlain:]
if !strings.Contains(tlsBlock, "check-alpn http/1.1") {
t.Errorf("HTTPS+healthcheck soll check-alpn http/1.1 pinnen:\n%s", tlsBlock)
}
if strings.Contains(plainBlock, "check-alpn") {
t.Errorf("HTTP-Backend darf KEIN check-alpn bekommen:\n%s", plainBlock)
}
}
func TestRender_WebSocketEmitsTunnelTimeout(t *testing.T) {
v := View{
Backends: []BackendView{