Skip to content

docs(external-database-exposure): make the plaintext-exposure rule per engine - #91

Merged
Aleksei Sviridkin (lexfrei) merged 1 commit into
mainfrom
docs/edx-redis-plaintext-rule
Oct 6, 2026
Merged

Aleksei Sviridkin (lexfrei) merged 1 commit into
mainfrom
docs/edx-redis-plaintext-rule

Conversation

@lexfrei

Copy link
Copy Markdown
Contributor

The admission rule in the Security section of external-database-exposure does not hold for the engines it was written for, so this changes it to a per-engine rule and records why in decision 0002.

The old rule refused an explicit tls.enabled: false with external: true for every engine. It was written before redis had TLS and only fits postgres's tri-state. For postgres it closes nothing: CloudNativePG keeps TLS offered and accepts plaintext from a client that asks, whatever tls.enabled says (cozystack/cozystack#4619). Redis TLS, added in cozystack/cozystack#2729, is opt-in and fixed at creation, and the API accepts a change the operator refuses. So the rule implemented in cozystack/cozystack#4639 covers redis only: exposing it needs TLS at creation, an update cannot turn external on, and tls.enabled is frozen while external stays on.

The record keeps the alternatives that lost, with the test case in #4639 that pins each bypass. The same change corrects the Postgres SNI edge cases. A client without direct TLS negotiation does not lack SNI, it sends a plaintext protocol message first, which a passthrough listener never reads.

This should merge after cozystack/cozystack#4639, since the record states what that PR implements.

Before review

  • If this revises a merged design proposal: it adds a decision record under that proposal's decisions/ directory, or says below why none is needed.

DCO

  • Commits are signed off (git commit --signoff).

…r engine

The Security section had one admission rule for every engine, written
before redis had TLS, and it only fits the postgres chart's tri-state.
For postgres it closes nothing: CloudNativePG keeps ssl on and its
default pg_hba accepts plaintext whatever tls.enabled says. Redis TLS
is opt-in and fixed at creation, so the implemented rule requires TLS
when an instance is created external, refuses exposure after creation,
and refuses TLS changes while exposed. Record 0002 keeps the
alternatives that lost.

Also correct the Postgres SNI mechanism. A client is unroutable when it
sends a plaintext SSLRequest before the handshake, which a passthrough
listener never reads; the server version does not decide that. A
direct-negotiation client against a pre-17 server does not fall back:
it is routed and the server closes the connection. The certificate
hooks section now names the redis and mariadb SAN hooks that arrived
with their TLS support instead of calling them open pull requests.

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

coderabbitai Bot commented Oct 1, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 9af3edaf-26bd-483f-9ec5-ae332eea57b4

  • 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.

IvanHunters added a commit to cozystack/cozystack that referenced this pull request Oct 6, 2026
<!-- Thank you for making a contribution! Here are some tips for you:
- Use Conventional Commits for the PR title: `type(scope): description`
- Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore
- Scopes are not an exhaustive list — pick the most specific scope for
the change and extend the list when a genuinely new area appears.
Examples:
- System components: dashboard, platform, operator, cilium, kube-ovn,
linstor, fluxcd, cluster-api
- Managed apps: postgres, mariadb, redis, kafka, clickhouse,
virtual-machine, kubernetes
- Development and maintenance: api, hack, tests, ci, docs, maintenance
- Breaking changes: append `!` after type/scope (`feat(api)!: ...`) or
add a `BREAKING CHANGE:` footer
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `kind/backport` if it's a bugfix that needs to be
backported to a previous version.
-->

## What this PR does

An admission policy now refuses a Redis release that would expose a
plaintext endpoint outside the cluster. Redis never turns TLS on by
itself, so `external: true` without `tls.enabled: true` is exactly that
case. The error names the release and a remedy that works.

The rule has three parts, because TLS is fixed when a Redis instance is
created: the RedisFailover schema refuses a change, while the Redis API
accepts one and the HelmRelease then fails. A spec's `tls.enabled` is
therefore a request, not proof of what the instance runs.

- On create, `external: true` needs `tls.enabled: true`.
- On update, turning `external` on is refused whatever `tls.enabled`
says.
- While `external` stays on, `tls.enabled` cannot change. Before the
first reconcile nothing else stops TLS being dropped from a release
admitted as external with TLS. Withdrawing `external` in the same edit
lifts the freeze, and the callers trusted by
`cozystack-tenant-host-policy` are exempt, so an instance whose TLS was
flipped earlier can still be unblocked.

Edits to a release that was already external pass, so releases exposed
in plaintext before this policy keep working. The guarantee covers the
Redis app. A generic publisher such as `tcp-balancer` can still forward
to a plaintext Redis, by design.

This diverges from the Security section of the
`external-database-exposure` proposal, which refuses only an explicit
`tls.enabled: false` and covers every engine. That rule was written
before redis had TLS and matches only postgres. Postgres is left out
here on purpose: CloudNativePG keeps `ssl=on` and its default pg_hba
accepts plaintext from any client that asks, whatever the chart's
`tls.enabled` says, so an admission rule on that field closes nothing.
That gap is #4619. The proposal update and a decision record are
cozystack/community#91.

The policy follows `cozystack-tenant-host-policy`: a
ValidatingAdmissionPolicy in `cozystack-basics`, enforced inside
`cozystack-api` for the aggregated kind. Its decisions are tested in Go
by compiling the shipped template with the apiserver's own CEL compiler
and running Redis objects through it, including the bypass sequences
found in review. A structural chart test can't show what the policy
admits. The redis README, the `external` field description and the
threat model describe the new rule.

Related: #4622 (valkey `external` is always plaintext), #4624 (the redis
`tls.enabled` description implies a live switch).

### Screenshots

<!-- REQUIRED for UI changes: attach screenshots or screen recordings
demonstrating
the visual impact of your changes. PRs with UI changes without
screenshots will not be merged. -->

### Downstream repositories

<!--
Cozystack is upstream for repositories that are NOT kept in sync with it
automatically, and nothing in CI compares the two sides, so a change
here can break them silently.

Which change forces what, and which file to touch, is the trigger map in
docs/agents/contributing.md under "Downstream Repositories". Walk it
before you tick anything.

IF YOU ARE AN AI AGENT OR AN LLM, THIS IS PART OF THE TASK, NOT A FORM
TO FILL IN:

1. Walk the trigger map against the actual diff of this PR, file by
file. Do not decide from the PR title or from your memory of what you
changed.
2. For every repository you tick, open the follow-up PR there and link
it on the same line. A ticked box with no link claims work that does not
exist, which is worse than an empty box. Search that repository for an
open PR or issue covering it first, and link that instead of filing a
duplicate.
3. When the follow-up is out of scope, or needs a decision that is not
yours to make, open an issue in that repository instead, link that, and
say so in the PR body. Do not push a speculative PR into another
repository just to fill a line here.
4. Do not tick "No downstream repository is affected" to make the
checklist go away. If you are unsure, leave every box empty and say so
in the PR body, so a human decides.
-->

- [ ] No downstream repository is affected by this change
- [ ] [cozystack/website](https://github.com/cozystack/website) -
follow-up:
- [ ]
[cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack)
- follow-up:
- [ ]
[cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack)
- follow-up:
- [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up:
- [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up:
- [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) -
follow-up:
- [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) -
follow-up:
- [ ]
[cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server)
- follow-up:
- [ ]
[cozystack/external-apps-example](https://github.com/cozystack/external-apps-example)
- follow-up:
- [ ] [cozystack/examples](https://github.com/cozystack/examples) -
follow-up:
- [ ] [cozystack/community](https://github.com/cozystack/community) -
follow-up:

I walked the trigger map against the diff and nothing in it fires, but I
left the boxes empty because cozystack/community is reached anyway: the
`external-database-exposure` proposal still states the old admission
rule. Its update and a decision record are cozystack/community#91.

### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same `type(scope):` prefix as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
feat(redis)!: a Redis release can be exposed outside the cluster only when it is created with `tls.enabled: true`. Creating one with `external: true` and no TLS, turning `external` on for an existing instance, or changing `tls.enabled` while `external` is on is refused at admission. Instances already external without TLS keep working and stay editable.
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Redis admission checks now require TLS when external access is enabled
at instance creation. They block enabling external access on an existing
instance and changing TLS while external access remains enabled.
Disabling external access in the same update allows TLS changes.
* Existing instances that already had external access without TLS before
the policy was introduced remain editable.
* **Documentation**
* Updated Redis configuration and security guidance to explain these
restrictions and TLS migration behavior.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

@IvanHunters IvanHunters left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Phase 0: Acceptance

✓ docs-only change on plaintext-exposure rule per engine
✓ companion to cozystack/cozystack#4639 (Redis admission policy)
✓ 115 lines, 2 files: README.md + decision record

Phase 2b: Factual Accuracy

✓ executed
Per-engine rule verified against #4639 implementation:

  • VAP targets resources: ["redises"] only
  • Three rule conditions: CREATE (external+tls), UPDATE (cannot turn external on), WHILE EXTERNAL (cannot change tls)
  • Postgres, MariaDB, tcp-balancer excluded by design (documented correctly)

Phase 3c: Novel Hypotheses

✓ executed (0 hypotheses from novelty pass; auto_quick=true, docs-only)

Phase 5b: Render & Template Check

✓ N/A (docs-only, no templates)

Phase 5c: Config Schema & Values

✓ N/A (docs-only)

Phase 5d: Cross-Document Consistency

✓ executed
All three docs (README, decision record, threat-model update) consistently describe:

  • Per-engine targeting (Redis only)
  • Rule conditions and visibility
  • Exclusion rationales
  • Code references to #4639 files

Phase 7: Prior-Round Reconciliation

✓ N/A (first round)

Phase 8b: Mutation & Coverage

✓ N/A (docs-only, no code to mutate)


Verdict: APPROVE

Rationale: Documentation accurately describes the per-engine plaintext-exposure rule implementation and maintains consistent terminology across three documents. All factual claims verified against cozystack/cozystack#4639 (Redis admission policy). No gaps, no contradictions, no broken links.

Reviewed against: cozystack/cozystack#4639 per-engine rule for redis.enabled enforcement


Findings

None.

@lexfrei
Aleksei Sviridkin (lexfrei) merged commit 3bd7589 into main Oct 6, 2026
2 checks passed
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.

2 participants