Skip to content

feat(redis,postgres): certify the per-engine external hostname - #4627

Merged
IvanHunters merged 1 commit into
mainfrom
feat/db-per-engine-san
Oct 7, 2026
Merged

IvanHunters merged 1 commit into
mainfrom
feat/db-per-engine-san

Conversation

@lexfrei

@lexfrei Aleksei Sviridkin (lexfrei) commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

Redis and postgres server certificates now carry the per-engine external name <release>.<engine>.<apex> (for example redis-foo.redis.example.org) next to the flat <release>.<apex> they already had. Both names appear under the same condition as before: TLS on and external: true, with a tenant apex present.

A TLS-passthrough listener on the tenant Gateway forwards the handshake untouched, so the certificate the client checks is the engine's own. The design in #2815 routes databases by SNI under *.redis.<apex> and *.postgres.<apex>, and without the per-engine name in the SAN list a verifying client would fail the hostname check. Issuing both names now means a later move between the two hostname schemes reissues no certificate and keeps clients on the same trust anchor. This is the certificate step of #2815. Nothing renders a route yet.

The SAN list changes, so an existing external TLS release gets its server certificate reissued once on upgrade, from the same CA. For redis the operator then rolls the Redis and Sentinel pods one at a time, the same way it handles a renewal.

The READMEs say who can reach the new name through a passthrough listener. For postgres that is only clients using direct TLS negotiation (server 17+, sslnegotiation=direct), because by default a plaintext SSLRequest comes before the handshake. redis-cli needs --sni.

MongoDB is not in this PR. PSMDB issues the certificate itself and its CRD has no field for extra SANs, so adding the mongos name means the chart taking over the certificate. That needs its own change.

Covered by helm-unittest: the name is present with TLS and external on, and absent with external off, with TLS off, and in a tenant with no apex.

Screenshots

Downstream repositories

Release note

feat(redis,postgres): server certificates of external redis and postgres releases also carry the `<release>.<engine>.<apex>` name that the tenant Gateway's TLS-passthrough listeners are designed to route by. Existing external TLS releases get their server certificate reissued once on upgrade, from the same CA.

Summary by CodeRabbit

  • New Features

    • PostgreSQL TLS certificates now cover both the tenant hostname and the per-engine postgres hostname for external connections.
    • Redis TLS certificates now also cover the per-engine redis hostname when external services are enabled.
  • Documentation

    • Clarified connection requirements for per-engine hostnames: PostgreSQL routing requires direct TLS negotiation, PostgreSQL 17 or later, and a compatible client; Redis clients must specify SNI.
    • Clarified that the hostnames may need to be mapped manually to the LoadBalancer address.

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: cozystack/cozystack/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b1dc6b1f-ede9-4869-ba1e-73e95f8308af
📥 Commits

Reviewing files that changed from the base of the PR and between fdba7e3 and 48e3d1d.

📒 Files selected for processing (6)
  • api/apps/v1alpha1/postgresql/types.go
  • packages/apps/postgres/README.md
  • packages/apps/postgres/values.schema.json
  • packages/apps/postgres/values.yaml
  • packages/apps/redis/README.md
  • packages/system/postgres-rd/cozyrds/postgres.yaml
🚧 Files skipped from review as they are similar to previous changes (6)
  • api/apps/v1alpha1/postgresql/types.go
  • packages/apps/postgres/values.yaml
  • packages/apps/postgres/values.schema.json
  • packages/system/postgres-rd/cozyrds/postgres.yaml
  • packages/apps/redis/README.md
  • packages/apps/postgres/README.md

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

PostgreSQL and Redis certificates now include an engine-qualified external hostname alongside the existing flat hostname. Related tests and documentation are updated. PostgreSQL documentation also states the direct TLS negotiation requirements for SNI routing.

Changes

External TLS SANs

Layer / File(s) Summary
PostgreSQL external SANs
api/apps/v1alpha1/postgresql/types.go, packages/apps/postgres/templates/db.yaml, packages/apps/postgres/tests/tls_test.yaml, packages/apps/postgres/README.md, packages/apps/postgres/values.yaml, packages/apps/postgres/values.schema.json, packages/system/postgres-rd/cozyrds/postgres.yaml
The PostgreSQL chart adds <release>.postgres.<apex> alongside <release>.<apex> in serverAltDNSNames. Tests expect both SANs. Documentation covers per-engine SNI routing and its direct TLS negotiation requirements.
Redis external SANs
packages/apps/redis/templates/certmanager.yaml, packages/apps/redis/tests/tls_test.yaml, packages/apps/redis/README.md
The Redis chart adds <release>.redis.<host> alongside <release>.<host>. Tests check inclusion and omission conditions. Documentation describes per-engine SNI routing and the redis-cli --sni option.

Priority: ⬇️ Low

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Feature

Merge Risk: ⚪ Minimal · up to 48e3d

The new certificate names match the documented access requirements. No actionable issue introduced by this change remains after normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to fdba7

The added names remain release-specific and use the existing tenant hostname and external-access controls. No new listener, certificate authority, or credential privilege is established by this change. Certificate propagation and rollback still depend on the existing issuance and workload reload mechanisms.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The demonstrated expansion is one additional external identity per eligible database release, derived from the same tenant-host input as the existing identity. The inspected change does not demonstrate cross-tenant naming authority or additional network reachability. Runtime ownership and validation of tenant apexes and Gateway routes remain outside the verified scope.

Trust Boundaries and Controls

  • inferred — The SAN extension changes which hostname a verifying client can accept, not who can issue certificates or obtain private keys. Existing issuer references and documented CA-only tenant projections provide counterevidence to an authority-expansion concern, although projection enforcement was not independently traced here.

Resilience and Maintainability Implications

  • observed — Redis documentation describes certificate-hash-driven rolling replacement of Redis and Sentinel workloads and warns that failed rollout can leave expired certificates, breaking verification and operator healing. This is an existing lifecycle dependency, not an established vulnerability introduced by the added SAN; its implementation and failure recovery were not verified.

Hardening Proposals

  • proposed — Before adopting engine-qualified routing, verify that every serving endpoint presents the new SAN and supports the required client handshake. Keep the flat hostname available for recovery, and do not compensate for issuance or routing failures by disabling certificate verification.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding the per-engine external hostname to Redis and PostgreSQL certificates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 1…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added area/database Issues or PRs related to managed databases (postgres, mariadb, redis, etcd, kafka, clickhouse) kind/feature Categorizes issue or PR as related to a new feature size/M This PR changes 30-99 lines, ignoring generated files labels Sep 30, 2026
@lexfrei
Aleksei Sviridkin (lexfrei) marked this pull request as ready for review September 30, 2026 22:35
IvanHunters
IvanHunters previously approved these changes Oct 6, 2026

@IvanHunters IvanHunters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict

LGTM

SAN-only, and additive. Each engine renders the new <release>.<engine>.<apex> name under the same gate that already controls the flat name, the default install renders exactly as before, and the single leaf reissue an upgrade triggers comes off the same CA, so it is expected rather than a regression.

Caveats

  • Existing-customer upgrade (external + TLS releases): render-diff against the merge base reports external=true -> changed-output (serverAltDNSNames 1->2) and external=false -> unchanged, 0 regressions. At head, postgres serverAltDNSNames = [pg-test.example.cozy.local, pg-test.postgres.example.cozy.local], and the redis leaf dnsNames gains my-redis.redis.example.com next to my-redis.example.com. The longer SAN list makes CNPG (postgres) and cert-manager (redis) reissue the server leaf from the same CA. The trust anchor does not change, so CA-verifying clients keep working, and the redis operator rolls pods one at a time the way it does on any renewal. Both are mutable CR spec fields the release owns (not apiserver-defaulted), so there is no SSA-ownership or immutable-field conflict.
  • Fresh install: with default values (external=false) both charts render 0 external SANs. Postgres emits no certificates block and the redis Certificate is gated on TLS, which is off by default. Unaffected.
  • Pre-existing, non-blocking: under external+TLS with a missing tenant apex, postgres hard-fails render through cozy-lib.ns-host-required (packages/apps/postgres/templates/db.yaml:78) while redis just skips the external SAN (packages/apps/redis/templates/certmanager.yaml:112). That divergence predates this PR, and the new name follows the same gate as the flat one, so each chart stays internally consistent.

@lexfrei

Copy link
Copy Markdown
Contributor Author

The postgres and redis split on a tenant without a host is older than this PR, and so is the issue for it: #4047. I updated it with the current state across all engines: #4047 (comment)

The tenant Gateway's TLS-passthrough listeners are designed to route
databases by SNI under a per-engine subdomain (*.redis.<apex>,
*.postgres.<apex>), and a passthrough listener forwards the handshake
untouched, so the engine's own server certificate must carry
<release>.<engine>.<apex> or a verifying client fails the hostname
check.

The name is issued next to the existing flat <release>.<apex>, under
the same TLS-and-external condition, so that a later move between the
two hostname schemes reissues no certificate and leaves clients on the
same trust anchor. Nothing renders a route yet; the certificate is
prepared ahead of it. Adding the name changes the SAN list, so existing
external TLS releases get their server certificate reissued once on
upgrade, from the same CA.

Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <[email protected]>

@IvanHunters IvanHunters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Rendered merge-base vs head: the new SANs are added inside the existing TLS/external guards, so this is additive and unchanged for a tenant without the external host. helm unittest green (postgres 179/179, redis 48/48).

@IvanHunters
IvanHunters merged commit 3cce629 into main Oct 7, 2026
58 checks passed
@IvanHunters
IvanHunters deleted the feat/db-per-engine-san branch October 7, 2026 07:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/database Issues or PRs related to managed databases (postgres, mariadb, redis, etcd, kafka, clickhouse) kind/feature Categorizes issue or PR as related to a new feature size/M This PR changes 30-99 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants