Skip to content

feat(annotations): ingress-nginx → synapse migration parity#25

Merged
pigri merged 2 commits into
mainfrom
feat/ingress-nginx-parity
May 25, 2026
Merged

feat(annotations): ingress-nginx → synapse migration parity#25
pigri merged 2 commits into
mainfrom
feat/ingress-nginx-parity

Conversation

@pigri
Copy link
Copy Markdown
Contributor

@pigri pigri commented May 25, 2026

Summary

Adds the ingress-nginx annotations that block cluster migration to synapse, plus the conditional v2 schema emission ssl-passthrough requires. Companion synapse PR: gen0sec/synapse#328.

Annotations added

`synapse.gen0sec.com/` + nginx-compat (`nginx.ingress.kubernetes.io/`) fallback for all of these:

Annotation Maps to
`proxy-body-size: 50m` `max_body_size` per-route (413 cap); nginx suffixes k/m/g
`server-alias: a, b` duplicate the route under each alias host (cert binding NOT inferred — use `spec.tls[].hosts` for TLS)
`ssl-passthrough: true` v2 `tls.passthrough` — SNI-routed TCP, synapse terminates nothing
`permanent-redirect: ` 301 redirect (`+ -code` to override)
`temporal-redirect: ` 302 redirect (`+ -code` to override)

Synapse-prefixed keys take precedence over nginx-compat. When both `permanent-redirect` and `temporal-redirect` are set, permanent wins.

Schema selection

When any Ingress carries `ssl-passthrough`, the operator switches the whole file to v2 (v1 has no passthrough representation, v2 parser is schema-version-strict). Otherwise emits v1 — zero behaviour change for deployments not using passthrough. Detection is automatic, no operator config flag.

v2 emission faithfully covers terminate hosts: `tls.terminate.cert`, `paths{...}` with `upstream`/`upstreams` + per-route `ssl_enabled`, `http2_enabled`, `force_https`, `disable_access_log`, `max_body_size`, `timeouts`, `headers`, `redirect`, plus boolean `healthcheck` lowered to v2's structured `health_check.type=tcp`. Depends on the companion synapse PR carrying those v1-compat fields on v2 `RawRoute`.

Out of scope

  • `rewrite-target` / `app-root` annotation. Synapse-side `RewriteConfig` machinery is already in place from the companion synapse PR but operator-side wiring is a follow-up. `app-root` explicitly out of migration scope.
  • mTLS annotations (`auth-tls-`, `proxy-ssl-`) — off the roadmap.
  • HTTP basic/digest auth (`auth-type`, `auth-secret`, *) — no synapse equivalent; flagged for the migration plan.

Test plan

  • `go build ./...` clean
  • `go test ./...` — all green
  • `size` parser: bytes, k/m/g suffix, malformed input rejects to None
  • All 5 new annotations parsed with both synapse-prefix and nginx-prefix
  • permanent-redirect wins over temporal-redirect when both set
  • server-alias duplicates the route under each alias
  • ssl-passthrough triggers v2 emission; absence keeps v1 (no-regression)
  • FIRST-WRITER-WINS on terminate-vs-passthrough collisions emits RouteConflict + bumps `synapse_operator_route_conflicts_total`
  • v2 round-trip parses back via the companion synapse PR's v2 schema

pigri added 2 commits May 25, 2026 16:43
Adds the ingress-nginx annotations that block cluster migration to
synapse, plus the conditional v2 schema emission ssl-passthrough
requires.

Annotations added (synapse.gen0sec.com/<key> + nginx-compat fallback)

  proxy-body-size    size   → max_body_size (per-route 413 cap; nginx
                              suffixes k/m/g)
  server-alias       csv    → duplicate the route under each alias host
                              with the same backend + settings (cert
                              binding NOT inferred — list aliases in
                              spec.tls[].hosts to get TLS for them)
  ssl-passthrough    bool   → v2 tls.passthrough — SNI-routed TCP
                              passthrough; synapse terminates nothing
                              for these hosts
  permanent-redirect url    → redirect { status: 301, location }
  permanent-redirect-code u → override the 301 default (e.g. 308)
  temporal-redirect  url    → redirect { status: 302, location }
  temporal-redirect-code  u → override the 302 default (e.g. 307)

Synapse-prefixed keys take precedence over nginx-compat keys
(consistent with the rest of the parser). If both permanent-redirect
and temporal-redirect are set, permanent wins (stronger commitment).

Schema selection

  When any Ingress carries ssl-passthrough, the operator switches the
  WHOLE file to v2 (v1 schema has no passthrough representation, v2
  parser is schema-version-strict). Otherwise emits v1, so deployments
  without passthrough see zero behaviour change. Detection is
  automatic — no operator config flag.

  v2 emission faithfully covers terminate hosts: tls.terminate.cert,
  paths{...} with upstream/upstreams + per-route ssl_enabled,
  http2_enabled, force_https, disable_access_log, max_body_size,
  timeouts{...}, headers{...}, redirect{...}, plus the boolean
  healthcheck lowered to v2's structured health_check.type=tcp.
  Depends on synapse's v2 RawRoute carrying those v1-compat fields
  (companion synapse PR).

FIRST-WRITER-WINS extends to passthrough

  addPassthroughHost rejects a host already claimed by a terminate
  route (and vice versa). Conflict emits the existing RouteConflict
  Warning Event + bumps synapse_operator_route_conflicts_total
  counter. Same idempotent observability as the existing terminate-
  on-terminate conflict path.

server-alias on conflict

  Each alias gets its own addRoute call after the primary; collisions
  on an alias hostname go through the same RouteConflict path.

Renamed v1 emission. Existing renderUpstreams stays the only path
when no passthrough host is present — diff is purely additive for
clusters that don't use ssl-passthrough.

Tests
  - parseSize (bytes / k/m/g / whitespace / negative / unknown suffix)
  - proxy-body-size parsing + synapse-vs-nginx precedence
  - server-alias parsing (csv, fallbacks, precedence)
  - server-alias emission (route duplicated under each alias)
  - ssl-passthrough parsing
  - addPassthroughHost FIRST-WRITER-WINS (terminate-on-passthrough
    and passthrough-on-passthrough rejection)
  - renderUpstreamsV2: scaffolding, sticky-sessions, ACME internal
    block, terminate + passthrough mixed in one render, all per-route
    v1-compat knobs threaded
  - redirect: parsing of all 4 keys + permanent-wins-over-temporal,
    emission in v1 and v2 renderers
  - max_body_size emission (v1) — present when set, absent when None

Out of scope here (not yet wired)
  - rewrite-target / app-root annotation. Synapse-side RewriteConfig
    machinery is in place from the companion synapse PR but the
    operator-side annotation wiring is a follow-up PR. app-root is
    explicitly out of migration scope.
  - mTLS annotations (auth-tls-*, proxy-ssl-*) — off the roadmap.
  - basic/digest auth (auth-type, auth-secret, *) — no synapse
    equivalent; flagged for the migration plan.
@pigri pigri marked this pull request as ready for review May 25, 2026 15:13
@pigri pigri merged commit 746f88b into main May 25, 2026
4 checks passed
@pigri pigri deleted the feat/ingress-nginx-parity branch May 25, 2026 15:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant