v0.312.0: every public name answers only over https; HSTS one day (R-1033, P1)
gates / gates (push) Successful in 48s

forwarded.yml: felhom-forwarded is now a chain [felhom-https-only (redirectScheme https, 301 when the trusted tunnel
says X-Forwarded-Proto: http), felhom-forwarded-headers (the clean-up + HSTS 86400, no subdomains, no preload)] plus a
priority-1 catch-all router for unknown names over http. traefik.yml unchanged; hot-loaded.
Tests red-proofed. Evidence: felhom.eu documentation/audits/https-only-2026-10-14/.
This commit is contained in:
2026-10-14 22:39:58 +02:00
parent 9b95e8e09b
commit 8bc7ec54cd
7 changed files with 158 additions and 25 deletions
@@ -302,3 +302,27 @@ func TestRenderCloudflared_HasAReadinessHealthcheck(t *testing.T) {
}
}
}
// R-1033 (P1): a box that carries the pre-v0.312.0 forwarded.yml (headers only, no https-only redirect), or a hand-edited
// one, gets the current file back on the next bring-up — so a released template reaches every box and drift does not
// survive a tick. The file is hot-loaded by traefik's file provider (watch: true): no traefik restart.
func TestEnsureForwardedHeaders_R1033_RewritesAnOldFile(t *testing.T) {
dir := t.TempDir()
dyn := filepath.Join(dir, "dynamic")
if err := os.MkdirAll(dyn, 0o755); err != nil {
t.Fatal(err)
}
old := "http:\n middlewares:\n " + infra.ForwardedMiddleware + ":\n headers:\n customRequestHeaders:\n X-Forwarded-Port: \"443\"\n"
touch(t, filepath.Join(dyn, "forwarded.yml"), old)
m := &Manager{logger: log.New(io.Discard, "", 0)}
if err := m.ensureForwardedHeaders(dir); err != nil {
t.Fatal(err)
}
got, _ := os.ReadFile(filepath.Join(dyn, "forwarded.yml"))
if string(got) != infra.RenderForwardedHeaders() {
t.Fatalf("an old forwarded.yml must be replaced by the current render (https-only chain); got:\n%s", got)
}
if !strings.Contains(string(got), infra.HTTPSOnlyMiddleware) {
t.Fatal("the rewritten file must carry the https-only redirect")
}
}