railiance-apps/manifests/informed-decision-http-redirect.yaml
tegwick 8d3e4a62b6
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
feat(informed-decision): HTTP -> HTTPS redirect for decisions.coulomb.social
The main Ingress is websecure-only per the forgejo pattern, so port 80 was
unrouted for this host and plain http:// returned Traefik's default 404. Adds a
redirectScheme Middleware plus a web-entrypoint Ingress, following the
reuse-surface pattern.

Pins router.priority: "1" on the redirect. cert-manager solves HTTP-01 by
creating a solver Ingress on this same host and entrypoint, so a catch-all "/"
redirect competes with it directly. Traefik would normally settle that by rule
length, but reuse-surface's 2026-07-07 report is exactly a case of a specific
rule losing to a catch-all when precedence was left implicit -- and here the
symptom would not be a visible 404 but a silently failed renewal ~60 days out,
surfacing as an expired certificate on the origin backing an OIDC redirect URI.

Verified by probe twice: with a solver-shaped Ingress present the challenge path
returns 200 (solver wins) while "/" still redirects; with it absent the redirect
correctly catches both. Probe was throwaway and is not committed; the runbook
carries the table to recreate it.

GET returns 301 and HEAD 308. That split is Traefik's own behaviour on this
cluster, not a defect here -- reuse.coulomb.social does the same.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJbh7o7UWF4tQ5jxygnNGu

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2072522@bnt-lap001
Assistant-Session: 46173adf-7302-4ede-99d6-963b61359928
2026-09-10 16:57:28 +02:00

62 lines
2.5 KiB
YAML

# HTTP -> HTTPS redirect for decisions.coulomb.social.
#
# manifests/informed-decision-ingress.yaml is websecure-only (forgejo pattern),
# which leaves port 80 unrouted for this host: plain http:// returned Traefik's
# default 404 rather than redirecting. This adds the redirect, following the
# reuse-surface pattern (reuse/reuse-surface-http-redirect + a redirectScheme
# Middleware).
#
# ROUTER PRIORITY IS LOAD-BEARING. cert-manager solves HTTP-01 by creating a
# temporary solver Ingress on this same host and this same `web` entrypoint,
# serving /.well-known/acme-challenge/<token>. A catch-all `/` redirect on the
# same entrypoint competes with it. Traefik would normally resolve that by rule
# length -- the solver's Path rule is far longer than PathPrefix(`/`) -- but
# reuse-surface reported on 2026-07-07 that relying on implicit precedence is
# exactly how a specific rule ends up swallowed by a catch-all, and there the
# symptom was a 404 someone noticed. Here the symptom would be a silently failed
# renewal roughly 60 days out, i.e. an expired certificate on the origin backing
# an OIDC redirect URI.
#
# So priority is pinned explicitly and low. Traefik gives the highest-priority
# matching router the request; any cert-manager solver (which uses the default
# computed priority, an order of magnitude larger) outbids this one. Verified by
# probe on 2026-09-10 -- see docs/informed-decision-origin.md.
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: informed-decision-redirect-https
namespace: informed-decision
labels:
app.kubernetes.io/name: informed-decision
app.kubernetes.io/component: redirect
spec:
redirectScheme:
scheme: https
permanent: true
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: informed-decision-http-redirect
namespace: informed-decision
labels:
app.kubernetes.io/name: informed-decision
app.kubernetes.io/component: redirect
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: web
traefik.ingress.kubernetes.io/router.middlewares: informed-decision-informed-decision-redirect-https@kubernetescrd
# Deliberately low: ACME solver routers must win on this entrypoint.
traefik.ingress.kubernetes.io/router.priority: "1"
spec:
ingressClassName: traefik
rules:
- host: decisions.coulomb.social
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: informed-decision
port:
number: 80