Repository navigation
feat(rabbitmq): add TLS support for AMQPS and management HTTPS - #2683
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds tri-state TLS for RabbitMQ: API ChangesRabbitMQ TLS Configuration Feature
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces TLS support for the RabbitMQ managed service, allowing secure communication for AMQPS and the management interface. It leverages cert-manager to handle certificate generation and rotation, while providing a flexible configuration schema that defaults to secure settings when external access is enabled. The changes ensure that existing plaintext traffic remains unaffected while enabling encrypted alternatives. Highlights
New Features🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Ignored Files
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
packages/apps/rabbitmq/tests/rabbitmq_tls_test.yaml (2)
136-169: ⚡ Quick winAdd tri-state coverage for LoadBalancer port overrides.
The unset-
tls.enabledpath is validated forspec.tls.secretNameand management SSL, but not forspec.override.service.spec.ports. A regression where LB overrides require explicittls.enabled: truewould slip through.Suggested additional test
+ - it: renders LoadBalancer override ports when tls.enabled is unset (default) and external is true + release: + name: myrabbitmq + namespace: tenant-test + values: + - fixtures/tls_default_values.yaml + set: + external: true + _namespace.host: example.com + documentSelector: + path: kind + value: RabbitmqCluster + asserts: + - contains: + path: spec.override.service.spec.ports + content: + name: management-tls + port: 15671 + targetPort: 15671 + protocol: TCPAlso applies to: 253-293
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/apps/rabbitmq/tests/rabbitmq_tls_test.yaml` around lines 136 - 169, Add tri-state coverage for LoadBalancer port overrides: extend the existing test blocks (the ones using fixtures/tls_default_values.yaml, release name myrabbitmq, documentSelector kind: RabbitmqCluster) to also assert spec.override.service.spec.ports when tls.enabled is unset (default) and external: true; specifically add an asserts block that checks spec.override.service.spec.ports contains the expected LB port entries (the same check you have for the other two tests that validate spec.tls.secretName and management.ssl.port) and mirror the same additional test for the other range (lines 253-293) so the unset/true/false matrix is fully covered.
320-330: ⚡ Quick winAvoid index-coupled baseline assertion.
Line 330 uses
documentIndex: 0, which is brittle if another manifest is rendered first. Select by kind like the other tests to keep this baseline stable.Stable selector-based variant
- it: always renders RabbitmqCluster release: name: myrabbitmq namespace: tenant-test set: tls.enabled: false external: false + documentSelector: + path: kind + value: RabbitmqCluster asserts: - isKind: of: RabbitmqCluster - documentIndex: 0🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/apps/rabbitmq/tests/rabbitmq_tls_test.yaml` around lines 320 - 330, The baseline test currently asserts the rendered RabbitmqCluster using a documentIndex (documentIndex: 0), which is brittle; update the assertion to select the RabbitmqCluster by kind instead of by index so the test doesn't break if other manifests are rendered first — replace the assertion that includes "documentIndex: 0" with a selector-style isKind check that targets "RabbitmqCluster" (matching how other tests select by kind) so the test locates the resource by its kind rather than its position.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/apps/rabbitmq/templates/certmanager.yaml`:
- Around line 54-57: The SAN list creation in the $dnsNames template hardcodes
"cluster.local" which breaks clusters with custom DNS; change the third entry to
use a configurable cluster domain (e.g. .Values.clusterDomain) instead of the
literal "cluster.local" so the SAN becomes (printf "%s.%s.svc.%s" .Release.Name
.Release.Namespace .Values.clusterDomain), and add a default clusterDomain:
"cluster.local" to values.yaml so .Values.clusterDomain is available.
- Around line 5-10: The current logic only checks hasKey and then assigns
$tlsEnabled = .Values.tls.enabled which treats a null value as false and
prevents falling back to .Values.external; change the branch to explicitly treat
null as "inherit" by checking for nil (e.g., use hasKey and ne
.Values.tls.enabled nil) or use Helm's default semantics to set $tlsEnabled to
.Values.tls.enabled when it is non-null, otherwise to .Values.external; update
the assignment that sets $tlsEnabled (referencing $tlsEnabled,
.Values.tls.enabled, .Values.external, and hasKey) so that a null
.Values.tls.enabled falls back to .Values.external.
In `@packages/apps/rabbitmq/templates/rabbitmq.yaml`:
- Around line 4-9: The template treats a present-but-null .Values.tls.enabled as
an explicit value because it uses hasKey, which causes the external fallback
(.Values.external) to be skipped; replace the tri-state check with a
default-based expression (e.g. set $tlsEnabled using default .Values.external
.Values.tls.enabled or the Helm "or" idiom) so that nil/absent tls.enabled falls
back to .Values.external and downstream logic that emits spec.tls.secretName
uses the correct value; update the block that defines $tlsEnabled (currently
using hasKey .Values.tls "enabled" and .Values.tls.enabled) to the single
default/or expression.
---
Nitpick comments:
In `@packages/apps/rabbitmq/tests/rabbitmq_tls_test.yaml`:
- Around line 136-169: Add tri-state coverage for LoadBalancer port overrides:
extend the existing test blocks (the ones using
fixtures/tls_default_values.yaml, release name myrabbitmq, documentSelector
kind: RabbitmqCluster) to also assert spec.override.service.spec.ports when
tls.enabled is unset (default) and external: true; specifically add an asserts
block that checks spec.override.service.spec.ports contains the expected LB port
entries (the same check you have for the other two tests that validate
spec.tls.secretName and management.ssl.port) and mirror the same additional test
for the other range (lines 253-293) so the unset/true/false matrix is fully
covered.
- Around line 320-330: The baseline test currently asserts the rendered
RabbitmqCluster using a documentIndex (documentIndex: 0), which is brittle;
update the assertion to select the RabbitmqCluster by kind instead of by index
so the test doesn't break if other manifests are rendered first — replace the
assertion that includes "documentIndex: 0" with a selector-style isKind check
that targets "RabbitmqCluster" (matching how other tests select by kind) so the
test locates the resource by its kind rather than its position.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 3980558a-e1ed-4ebc-9242-c75c97984d18
📒 Files selected for processing (12)
api/apps/v1alpha1/rabbitmq/types.goapi/apps/v1alpha1/rabbitmq/zz_generated.deepcopy.gopackages/apps/rabbitmq/Makefilepackages/apps/rabbitmq/README.mdpackages/apps/rabbitmq/templates/certmanager.yamlpackages/apps/rabbitmq/templates/rabbitmq.yamlpackages/apps/rabbitmq/tests/certmanager_test.yamlpackages/apps/rabbitmq/tests/fixtures/tls_default_values.yamlpackages/apps/rabbitmq/tests/rabbitmq_tls_test.yamlpackages/apps/rabbitmq/values.schema.jsonpackages/apps/rabbitmq/values.yamlpackages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml
There was a problem hiding this comment.
Code Review
This pull request introduces TLS support for the RabbitMQ application, including the necessary API changes, Helm templates for cert-manager integration, and updated schema definitions. It also adds comprehensive unit tests to verify the TLS configuration and service port overrides. Regarding the implementation, consider parameterizing the hardcoded .cluster.local domain in the cert-manager templates to better support environments with custom cluster domains.
676576e to
4f857f1
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (3)
packages/apps/rabbitmq/templates/certmanager.yaml (2)
5-10:⚠️ Potential issue | 🟠 Major | ⚡ Quick winTreat
nullas “inherit”, not as disabled.A present-but-null
tls.enabledstill takes thehasKeybranch, sotls.enabled: nullresolves to falsey here instead of falling back to.Values.external. That breaks the advertised tri-state inheritance for external releases.Proposed fix
-{{- $tlsEnabled := false }} -{{- if hasKey .Values.tls "enabled" }} - {{- $tlsEnabled = .Values.tls.enabled }} -{{- else }} - {{- $tlsEnabled = .Values.external }} -{{- end }} +{{- $tlsEnabled := .Values.external }} +{{- $tlsExplicit := dig "tls" "enabled" nil .Values }} +{{- if ne $tlsExplicit nil }} + {{- $tlsEnabled = $tlsExplicit }} +{{- end }}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/apps/rabbitmq/templates/certmanager.yaml` around lines 5 - 10, The template treats a present-but-null .Values.tls.enabled as false because hasKey alone is used; change the logic so null means "inherit" by checking for presence AND non-null. Replace the if that sets $tlsEnabled to use: if hasKey .Values.tls "enabled" and not (eq .Values.tls.enabled nil) then set $tlsEnabled = .Values.tls.enabled else set $tlsEnabled = .Values.external; reference the variables $tlsEnabled, .Values.tls.enabled and .Values.external and the hasKey check when making the change.
54-57:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDon’t hardcode
cluster.localinto the certificate SANs.The RabbitMQ TLS contract here is
svc.<cluster-domain>, not a fixed suffix. On clusters with a custom domain, the fully qualified service name will no longer match the certificate. (rabbitmq.com)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/apps/rabbitmq/templates/certmanager.yaml` around lines 54 - 57, The template currently hardcodes "cluster.local" in the SANs (inside the $dnsNames list), which breaks clusters with custom cluster domains; update the $dnsNames construction to use a configurable cluster domain instead (e.g., use default "cluster.local" for backward compatibility via a chart value like .Values.clusterDomain) and build the FQDN with printf "%s.svc.%s" .Release.Name (default .Values.clusterDomain) so the SANs reflect the actual cluster domain; adjust any values.yaml to document .Values.clusterDomain default.packages/apps/rabbitmq/templates/rabbitmq.yaml (1)
4-9:⚠️ Potential issue | 🟠 Major | ⚡ Quick winTreat
nullas “inherit” here too.This has the same tri-state bug as the cert-manager template:
tls.enabled: nullis treated as an explicit value and skips the.Values.externalfallback, so external deployments can silently lose TLS wiring.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/apps/rabbitmq/templates/rabbitmq.yaml` around lines 4 - 9, The template treats tls.enabled=null as an explicit value and skips the external fallback; change the logic that computes $tlsEnabled so null is treated like "not set" — i.e., if .Values.tls.enabled is missing or nil use .Values.external, otherwise use .Values.tls.enabled; you can implement this by replacing the hasKey branch with a check that .Values.tls.enabled is not nil (or by using Helm's default helper: default .Values.external .Values.tls.enabled) to assign $tlsEnabled.
🧹 Nitpick comments (1)
packages/apps/rabbitmq/tests/rabbitmq_tls_test.yaml (1)
25-38: ⚡ Quick winScope TLS-off assertions to management SSL keys, not full
additionalConfigabsence.These checks can fail if unrelated RabbitMQ config is added later. Prefer asserting that
management.ssl.*entries are absent instead of assertingspec.rabbitmq.additionalConfigis entirely missing.Proposed test change
- - notExists: - path: spec.rabbitmq.additionalConfig + - notMatchRegex: + path: spec.rabbitmq.additionalConfig + pattern: "management\\.ssl\\." @@ - - notExists: - path: spec.rabbitmq.additionalConfig + - notMatchRegex: + path: spec.rabbitmq.additionalConfig + pattern: "management\\.ssl\\."Also applies to: 174-190
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/apps/rabbitmq/tests/rabbitmq_tls_test.yaml` around lines 25 - 38, The test currently asserts that spec.rabbitmq.additionalConfig does not exist when tls.enabled is false; instead, change the assertions to only scope absence to the management SSL keys (e.g., assert notExists for paths like spec.rabbitmq.additionalConfig.management.ssl and/or spec.rabbitmq.additionalConfig.management.ssl.*) so unrelated additionalConfig entries won't break the test; update the two test cases (the one shown and the one at lines 174-190) to replace the top-level notExists on spec.rabbitmq.additionalConfig with notExists checks targeting spec.rabbitmq.additionalConfig.management.ssl (or the specific keys under management.ssl) while leaving other parts of additionalConfig allowed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/apps/rabbitmq/templates/certmanager.yaml`:
- Around line 54-58: The DNS SAN list variable $dnsNames in certmanager.yaml
currently only includes the client service names; update it to also include the
headless nodes service SANs required by RabbitMQ Cluster Operator by adding
entries for the "-nodes" service variants (e.g., add (printf "%s-nodes"
.Release.Name), (printf "%s-nodes.%s.svc" .Release.Name .Release.Namespace) and
(printf "%s-nodes.%s.svc.cluster.local" .Release.Name .Release.Namespace) to the
list) so node-to-node TLS hostname verification succeeds.
---
Duplicate comments:
In `@packages/apps/rabbitmq/templates/certmanager.yaml`:
- Around line 5-10: The template treats a present-but-null .Values.tls.enabled
as false because hasKey alone is used; change the logic so null means "inherit"
by checking for presence AND non-null. Replace the if that sets $tlsEnabled to
use: if hasKey .Values.tls "enabled" and not (eq .Values.tls.enabled nil) then
set $tlsEnabled = .Values.tls.enabled else set $tlsEnabled = .Values.external;
reference the variables $tlsEnabled, .Values.tls.enabled and .Values.external
and the hasKey check when making the change.
- Around line 54-57: The template currently hardcodes "cluster.local" in the
SANs (inside the $dnsNames list), which breaks clusters with custom cluster
domains; update the $dnsNames construction to use a configurable cluster domain
instead (e.g., use default "cluster.local" for backward compatibility via a
chart value like .Values.clusterDomain) and build the FQDN with printf
"%s.svc.%s" .Release.Name (default .Values.clusterDomain) so the SANs reflect
the actual cluster domain; adjust any values.yaml to document
.Values.clusterDomain default.
In `@packages/apps/rabbitmq/templates/rabbitmq.yaml`:
- Around line 4-9: The template treats tls.enabled=null as an explicit value and
skips the external fallback; change the logic that computes $tlsEnabled so null
is treated like "not set" — i.e., if .Values.tls.enabled is missing or nil use
.Values.external, otherwise use .Values.tls.enabled; you can implement this by
replacing the hasKey branch with a check that .Values.tls.enabled is not nil (or
by using Helm's default helper: default .Values.external .Values.tls.enabled) to
assign $tlsEnabled.
---
Nitpick comments:
In `@packages/apps/rabbitmq/tests/rabbitmq_tls_test.yaml`:
- Around line 25-38: The test currently asserts that
spec.rabbitmq.additionalConfig does not exist when tls.enabled is false;
instead, change the assertions to only scope absence to the management SSL keys
(e.g., assert notExists for paths like
spec.rabbitmq.additionalConfig.management.ssl and/or
spec.rabbitmq.additionalConfig.management.ssl.*) so unrelated additionalConfig
entries won't break the test; update the two test cases (the one shown and the
one at lines 174-190) to replace the top-level notExists on
spec.rabbitmq.additionalConfig with notExists checks targeting
spec.rabbitmq.additionalConfig.management.ssl (or the specific keys under
management.ssl) while leaving other parts of additionalConfig allowed.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 1514db0b-2ffb-49d0-9cc6-8db8e053645d
📒 Files selected for processing (12)
api/apps/v1alpha1/rabbitmq/types.goapi/apps/v1alpha1/rabbitmq/zz_generated.deepcopy.gopackages/apps/rabbitmq/Makefilepackages/apps/rabbitmq/README.mdpackages/apps/rabbitmq/templates/certmanager.yamlpackages/apps/rabbitmq/templates/rabbitmq.yamlpackages/apps/rabbitmq/tests/certmanager_test.yamlpackages/apps/rabbitmq/tests/fixtures/tls_default_values.yamlpackages/apps/rabbitmq/tests/rabbitmq_tls_test.yamlpackages/apps/rabbitmq/values.schema.jsonpackages/apps/rabbitmq/values.yamlpackages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml
✅ Files skipped from review due to trivial changes (1)
- packages/apps/rabbitmq/README.md
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
NOT LGTM — cert chain is clean (correct CA usages [cert sign, crl sign], correct leaf [digital signature, key encipherment, server auth], SAN list covers both client-facing service and inter-node headless wildcard), and the tri-state tls.enabled resolution was tightened in a3acd2d2 to handle explicit null correctly. Two blockers: when TLS lands on an externally-published instance, the LoadBalancer exposes BOTH the plaintext and TLS ports, and the chart-managed CA's ca.crt is not reachable by tenant clients who need it to verify the TLS handshake.
Business context: Add TLS for the managed RabbitMQ service — AMQPS (5671) and Management HTTPS (15671) — via a self-contained cert-manager chain, with tls.enabled falling back to external when unset. Plaintext listeners are intentionally preserved for backward compatibility per the PR description.
Blockers
B1: External LoadBalancer exposes plaintext AMQP (5672) and plaintext Management HTTP (15672) alongside TLS ports
File: packages/apps/rabbitmq/templates/rabbitmq.yaml:36-56
Issue: When external: true AND TLS is enabled (either explicitly or via the auto-on tri-state), the override.service.spec.ports block unconditionally declares all four ports — amqp, amqps, management, management-tls — on the type: LoadBalancer Service. The PR description acknowledges plaintext "coexists with the TLS ports" for backward compatibility; what's not acknowledged is that this coexistence is published over the public LoadBalancer interface, not just inside the cluster.
Evidence:
{{- if and $tlsEnabled .Values.external }}
service:
spec:
ports:
- name: amqp
port: 5672
targetPort: 5672
- name: amqps
port: 5671
targetPort: 5671
- name: management
port: 15672
targetPort: 15672
- name: management-tls
port: 15671
targetPort: 15671
{{- end }}spec.tls.disableNonTLSListeners is also not set — that upstream RabbitMQ-operator flag would have closed 5672/15672 at the broker level even if the Service exposed them. With this PR, an attacker on the public network can pick the plaintext AMQP port and send PLAIN SASL credentials in cleartext, or hit the management UI over HTTP and submit cookies/sessions in cleartext.
Impact: Silent security regression for the precise cohort the TLS auto-on logic targets — externally-published instances. Operators who toggle external: true (and therefore implicitly opt in to TLS via the tri-state) reasonably expect the public-facing surface to be TLS-only; today they get TLS plus full plaintext as a bonus.
Fix — pick one (not mutually exclusive):
-
Set
spec.tls.disableNonTLSListeners: trueon the RabbitmqCluster when TLS is on. This closes plaintext at the broker level. Add atls.required: falseescape hatch (mirroring the mariadb pattern in #2680) for tenants in mid-migration that still need plaintext. -
Drop the plaintext ports from the LoadBalancer override when TLS is on:
{{- if and $tlsEnabled .Values.external }} service: spec: ports: - name: amqps port: 5671 targetPort: 5671 - name: management-tls port: 15671 targetPort: 15671 {{- end }}
Leaves plaintext accessible only over ClusterIP (in-cluster clients can still use it during migration), TLS-only externally.
Either fix the PR description's "preserved for backward compatibility" claim by scoping it explicitly to in-cluster traffic, with TLS-only external. The current state silently widens the attack surface.
B2: Tenant clients cannot verify the TLS handshake — the chart-managed CA ca.crt is not surfaced
File: packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml (the dashboard resource map) + packages/apps/rabbitmq/templates/dashboard-resourcemap.yaml (the RBAC)
Issue: This chart issues a brand-new self-signed CA in the tenant namespace and uses it to sign the leaf serving cert. For any client to verify the AMQPS/HTTPS handshake against that CA — outside the tenant cluster's default cluster-trust roots — they need the CA's ca.crt. The dashboard resource map exposes <release>-default-user and <release>-<user>-credentials only; the CA secret (<release>-ca) and the leaf secret (<release>-tls) are not in the map. The dashboard role binds at the use access level, so even tenant admins (without raw kubectl get secret access on the namespace) hit the same gap.
Evidence: cozyrds/rabbitmq.yaml:31-37 only lists rabbitmq-{{ .name }}-default-user and user-credential secrets under secrets.include. dashboard-resourcemap.yaml RBAC matches. cert-manager produces <release>-ca (type kubernetes.io/tls, contains the CA private key + cert) and <release>-tls (type kubernetes.io/tls, contains the server private key + cert + ca.crt). Both have tls.key alongside tls.crt; Kubernetes RBAC at the Secret level cannot scope to individual keys.
Impact: An external client that wants to do verify-peer on the AMQPS handshake (the security default for production) has no path to fetch the CA cert via the supported tenant interface. Workarounds available today (cluster admin pulls the Secret out-of-band, or tenant disables peer verification) defeat the security gain of this PR.
The cleanest fix is to publish only ca.crt — not the private keys — as a separate, tenant-visible object. cert-manager's additionalOutputFormats: [{type: "CombinedPEM"}] on the leaf Certificate doesn't help here (it adds more material to the same Secret, not a separate one). Two viable patterns:
-
Post-install Helm hook Job that reads
<release>-tls'sca.crtand writes it into a<release>-ca-bundleConfigMap. Add that ConfigMap tocozyrds/rabbitmq.yamlunderconfigMaps.include. The ConfigMap has no private key material, so the use-tier RBAC is safe. -
Use cert-manager's
trust-manager(if available cluster-side) to distribute the CA cert into a ConfigMap declaratively.
The same gap is observed (and called out) in #2680 mariadb B3 — but there it was the inverse problem (RBAC TOO permissive). Here the RBAC is correctly restrictive but the chart never publishes the public-only material. Solving B3 from #2680 and B2 here with the same ConfigMap pattern aligns the whole TLS batch.
Non-blocking follow-ups
-
PR description acknowledges Erlang inter-node distribution (port 25672) stays plaintext. Worth tracking as an explicit follow-up issue (referenced by-number) so it doesn't drift.
spec.tls.caSecretName+ a separate per-node cert is the canonical upstream path; the chart's current single-cert model needs to be extended for that. -
certmanager.yaml:37-42: CA private key only setsrotationPolicy: Never, leaving algorithm/size at cert-manager defaults (RSA-2048). Non-uniform across the batch (mariadb uses RSA-4096 explicitly). Acceptable but worth a project-level convention. -
certmanager.yaml:59-68: SAN list includes*.<release>-nodes.<ns>.svc(without cluster domain). The RabbitMQ operator's inter-node communication usesrabbit@<pod>.<release>-nodes.<ns>.svc.<cluster-domain>(FQDN form), which IS covered by the wildcard on line 67. Inter-node Erlang distribution is plaintext (per follow-up #1), so today the inter-node SANs aren't exercised over TLS — they're future-proofing for when #1 lands.
Comparison to the sibling TLS PRs reviewed: B2 from #2680 (missing digital signature in usages) does NOT recur — present here on the leaf. B3 from #2680 (dashboard RBAC over TLS private keys) does NOT recur — <release>-ca and <release>-tls are correctly excluded from the dashboard role. B1 from #2684/#2685 (short-form peer hostnames not in SAN) does NOT recur for RabbitMQ either — inter-node uses FQDN by upstream chart's default and is plaintext today regardless.
|
Addressed your review. Blockers:
Non-blocking follow-ups:
Series-wide cleanups applied here too:
Ready for re-review. |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/apps/rabbitmq/templates/rabbitmq.yaml (1)
37-54:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftFix LB “TLS-only” behavior:
spec.override.service.spec.portsomission won’t prune plaintext portsFor
packages/apps/rabbitmq/templates/rabbitmq.yaml, settingspec.override.service.spec.portsto only 5671/15671 via strategic merge does not delete the operator-generated Service ports for omitted entries (e.g., 5672/15672); strategic merge patches leave list items you don’t specify unchanged. This means the external LoadBalancer may still expose plaintext AMQP/management unless the operator override uses explicit delete semantics for theportslist items (e.g.,$patch: delete/deleteDirective keyed byport).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/apps/rabbitmq/templates/rabbitmq.yaml` around lines 37 - 54, The override using spec.override.service.spec.ports only adds ports and does not remove operator-generated plaintext ports (5672/15672); update the Helm/Kustomize override for the Service to explicitly remove those ports by using delete semantics (e.g., add a $patch: delete or use the deleteDirective keyed by port) so that only the TLS ports (5671/15671) remain exposed; target the Service ports list (spec.override.service.spec.ports) and identify entries to remove by their port numbers (5672 and 15672) when applying the patch.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/apps/rabbitmq/templates/dashboard-resourcemap.yaml`:
- Around line 20-24: The RBAC entry currently grants tenants "get" on the full
TLS Secret ({{ .Release.Name }}-tls) which exposes tls.key; instead, create a
dedicated CA-only Secret (e.g., {{ .Release.Name }}-ca) containing only ca.crt
and change the RBAC grant in dashboard-resourcemap.yaml to reference that CA
secret instead of {{ .Release.Name }}-tls; implement the extraction in
certmanager.yaml (or the certificate controller) to copy/extract ca.crt from the
primary TLS Secret into the new {{ .Release.Name }}-ca Secret at
issuance/rotation time, and update any template references that expect the CA
secret name.
- Around line 20-24: The RBAC rule in dashboard-resourcemap.yaml grants read
access to the entire TLS Secret ({{ .Release.Name }}-tls) when $tlsEnabled is
true, which exposes tls.key; instead, stop granting get/list/watch on {{
.Release.Name }}-tls and grant access only to a new minimal Secret that contains
the CA cert only (e.g., {{ .Release.Name }}-ca). Update
dashboard-resourcemap.yaml to reference {{ .Release.Name }}-ca when $tlsEnabled,
and change certificates provisioning
(packages/apps/rabbitmq/templates/certmanager.yaml) or add a small post-install
controller/hook to copy only the ca.crt from spec.secretName: {{ .Release.Name
}}-tls into the new {{ .Release.Name }}-ca Secret so tenants can read ca.crt
without access to tls.key.
---
Outside diff comments:
In `@packages/apps/rabbitmq/templates/rabbitmq.yaml`:
- Around line 37-54: The override using spec.override.service.spec.ports only
adds ports and does not remove operator-generated plaintext ports (5672/15672);
update the Helm/Kustomize override for the Service to explicitly remove those
ports by using delete semantics (e.g., add a $patch: delete or use the
deleteDirective keyed by port) so that only the TLS ports (5671/15671) remain
exposed; target the Service ports list (spec.override.service.spec.ports) and
identify entries to remove by their port numbers (5672 and 15672) when applying
the patch.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 09b835a4-9410-47ba-9c63-8fb32e43f349
📒 Files selected for processing (10)
packages/apps/rabbitmq/Makefilepackages/apps/rabbitmq/templates/certmanager.yamlpackages/apps/rabbitmq/templates/dashboard-resourcemap.yamlpackages/apps/rabbitmq/templates/rabbitmq.yamlpackages/apps/rabbitmq/tests/certmanager_test.yamlpackages/apps/rabbitmq/tests/dashboard_resourcemap_test.yamlpackages/apps/rabbitmq/tests/rabbitmq_tls_test.yamlpackages/apps/rabbitmq/values.schema.jsonpackages/apps/rabbitmq/values.yamlpackages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml
💤 Files with no reviewable changes (1)
- packages/apps/rabbitmq/Makefile
✅ Files skipped from review due to trivial changes (1)
- packages/apps/rabbitmq/tests/dashboard_resourcemap_test.yaml
There was a problem hiding this comment.
NOT LGTM — re-review on head 6d5f940e. Two security blockers. The first reopens a point I previously marked resolved: the "drop plaintext ports" approach does not actually remove them.
Business context: adds TLS (AMQPS 5671 + Management HTTPS 15671) to the managed RabbitMQ app via a self-contained cert-manager chain, with a tri-state tls.enabled that falls back to external when unset.
Blockers
B1 (reopened): omitting plaintext ports from the override does not remove them — plaintext AMQP/management stay on the public LoadBalancer
File: packages/apps/rabbitmq/templates/rabbitmq.yaml:36-55
Issue: when external + TLS, the override emits only amqps:5671 and management-tls:15671, relying on omission to keep plaintext amqp:5672 / management:15672 off the LoadBalancer. But the RabbitMQ Cluster Operator applies spec.override.service.spec.ports as a strategic merge keyed by port and independently regenerates its default ports — omitting a port from the override never deletes it.
Evidence: the PR's own test comment states this verbatim (tests/rabbitmq_tls_test.yaml:235-237): "This MERGES by port number rather than replacing the whole list; operator-generated defaults (amqp:5672, amqps:5671, management:15672) are unaffected." The notContains assertions only validate the rendered CR override block, not the final Service the operator produces, so the suite is green while plaintext stays exposed.
Impact: silent public plaintext exposure for exactly the external-published cohort the TLS auto-on targets — cleartext SASL PLAIN credentials over AMQP 5672 and cleartext management sessions over HTTP 15672 on a public IP. The PR's security goal is not met.
Fix: close plaintext at the broker with spec.tls.disableNonTLSListeners: true when TLS is on (the operator then also moves the readiness probe to amqps automatically). The current template comment explicitly avoids this to protect in-cluster clients during migration — so pair it with an opt-out (tls.allowPlaintext) for the migration window rather than leaving plaintext on a public LB. The omit-from-override approach cannot work against this operator.
B2: dashboard RBAC grants the server private key to every tenant use member
File: packages/apps/rabbitmq/templates/dashboard-resourcemap.yaml:20-24
Issue: with TLS on, the Role adds <release>-tls to resourceNames for the tenant use access level (RoleBinding at :39). The comment intends ca.crt retrieval, but <release>-tls is the cert-manager kubernetes.io/tls leaf Secret carrying tls.key — the broker's serving private key. RBAC is per-Secret, so get exposes the key, not just ca.crt.
Evidence: certmanager.yaml leaf secret (<release>-tls, type kubernetes.io/tls); Role grant at dashboard-resourcemap.yaml:23 bound to "use".
Impact: a use-level tenant can extract the broker private key, stand up an impersonating TLS terminator on 5671, MITM other clients' AMQP traffic, and decrypt captured non-PFS sessions.
Fix: expose only the public ca.crt (separate ConfigMap via trust-manager, or a ca.crt-only Secret), not the leaf Secret. Same defect as the sibling mariadb/mongodb TLS PRs — a single shared fix would close it across the series.
Non-blocking follow-ups
- The test comment at
tests/rabbitmq_tls_test.yaml:242("we include amqp, amqps, management, management-tls") is stale after the override change and will need rewriting once B1 is fixed viadisableNonTLSListeners. - Erlang inter-node distribution (25672) stays plaintext (acknowledged in the README); inter-node
-nodesSANs are already wired as future-proofing. Fine as a tracked follow-up. - README "TLS scope" inherits the framing that plaintext "coexists for backward compat" without disclosing that, when
external: true, that coexistence is on the public LoadBalancer (B1). Fix the framing alongside B1.
1c41333 to
10e2b93
Compare
The annotation block restated the README's TLS section nearly verbatim. Only the @PARAM and @field lines reach the generated schema and README table; the rest was a second copy that would drift against the first. Keeps the three facts that change what a caller sets — neither field is inferred from external, enabling TLS moves the binding port, and disableNonTLSListeners is not external-only — and points at the README. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
The dashboard Role already refuses to grant the key-bearing TLS Secrets, but nothing in the chart told the CA-extraction controller to publish the key-free trust anchor those Secrets feed: no TenantProjection sentinel referenced the CA Certificate's Secret as a source. Without that sentinel the controller has nothing to act on, so rabbitmq-<name>.tenant-ca is never created and a tenant has no path to the CA at all, contrary to what the README and the Role's own comments describe. Add templates/tenant-projection.yaml, gated on the same TLS condition as the certificate chain and mirroring the nats and qdrant charts: it names the CA Certificate's Secret as the projection source. Drop the internal.cozystack.io/publish-ca-cert labels from certmanager.yaml, since the controller resolves its source by name through the sentinel rather than by a label on the Secret, and correct the comments and README passage that described that label as the actual publication mechanism. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
Clearing users and vhosts and setting tls.disableNonTLSListeners in the same edit rendered cleanly, because the existing refusal reads values and both were already empty by then. Helm applies every object in the target release before pruning the ones the release dropped, so the broker closed plaintext and lost the operator-connection-uri annotation while the User, Vhost and Permission objects still held their deletion finalizers. messaging-topology-operator then had to reach the management API over a certificate it verifies against the system trust pool, which cannot hold a per-release CA, so the delete never completed and the upgrade reported success over objects left in Terminating. Read the topology objects actually present in the namespace instead, and only while the flag is being introduced, so a release that already closed its listeners keeps rendering and an object created afterwards does not block it. Both refusals and the README now carry the two-upgrade order, the command that confirms the namespace is clear, and the way back out. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
Two comment blocks in certmanager.yaml used `#`, which the renderer does not strip, so fourteen lines of prose about the sentinel wiring and the CA rotation policy were copied verbatim into every Certificate the chart applies. The rest of the file already explains itself with Go-template comments; use those here too, and cut the parts the README carries. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
…anism Both the cozyrds comment and the bats header said the chart labels its CA Secret for publication. Nothing labels that Secret: the chart names it in a TenantProjection sentinel, and the CA-extraction controller stamps internal.cozystack.io/tenant-ca on the key-free projection it writes. A reader who went looking for the missing label on the CA Certificate would have found nothing, and the obvious repair — a secretTemplate stamping the label onto the CA Secret — would offer a tenant the CA private key. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
…dered cozyvalues-gen propagates the @field line and nothing under it, so the description reaching values.schema.json, the README table and the dashboard form said only that TLS turns on, while the continuation lines holding the breaking part of the change stayed in values.yaml. Fold the listener, binding-port and metrics-port moves into the @field line, and leave the continuation with the part that is genuinely about the struct. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
Every existing case with external: false also left _namespace.host unset, so the platform host stayed out of the SAN list because no host was supplied, not because the gate held. Replacing the gate with a constant left the suite green. Supply a host with external: false so the gate is the only thing deciding. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
A tenant namespace holds every RabbitMQ application the tenant runs, and the listing enumerated all of them. A second instance with users kept the first from ever closing its listeners, and the refusal named the other application's objects and told the operator to delete them. The mechanism behind the refusal does not reach that far either: a sibling's topology objects are reconciled through their own broker, whose listeners this flag leaves alone. Read spec.rabbitmqClusterReference.name and keep only the objects that name this release. The hand-written fixture gained the reference it was missing, which is what hid the gap, and a sibling release now has a case of its own. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
The checks grepped the ResourceDefinition source for forbidden names, a
shape the nats sibling documents as broken: quoting the scalar, spelling
the name through a helper, or reaching the Secret through a label all
leave the grep green. Adding a quoted rabbitmq-{{ .name }}-tls to the
include passed every test.
Compare whole values instead, the way the converged siblings do: the
include and exclude lists as parsed YAML, and the dashboard Role's rules
and kinds against rendered output, so the spelling used in the template
cannot smuggle a name past. The Role's secrets rule is the one part that
grows from values, so a declared user gets its own case.
Assisted-by: LLM
Signed-off-by: Aleksei Sviridkin <[email protected]>
The file header and the block at the secrets rule both said no TLS Secret is granted in either TLS state. The one at the rule is where a reader editing the grant will be looking. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
The paragraph closed by saying TLS moves clients before there is a key-free way to hand them the CA. This chart ships that way, and the section two headings down documents it. The reason the default holds is that the move lands the instant the flag is set, before any client has had a chance to pick the trust anchor up. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
Both messages ran 700-800 characters. They surface in a HelmRelease condition read through kubectl describe, which truncates, so the part an operator needs was the part most likely to be cut. Keep the objects that block, the two-upgrade order, the command that confirms the namespace is clear and a pointer to the README section; the mechanism is already there in full. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
The README and the TLS suite called spec.tls.caSecretName the inter-node mTLS knob and said the chart leaves it unwired. The field has nothing to do with Erlang distribution: under it the operator writes ssl_options.cacertfile, ssl_options.verify = verify_peer, and a cacertfile for the management and metrics listeners, every one of them client-facing. The cluster-operator this platform ships carries no distribution TLS under any field, so there was no inter-node path to leave unwired either. Say what the field does at each place it is named, and give the client-verification section the operator field it was missing, along with the reason verify_peer on its own does not make a client certificate mandatory. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
f60b145 to
2d6a188
Compare
|
Pushed 2d6a188. The one-step migration is refused at render time now: the guard reads the live RabbitmqCluster and the User, Vhost and Permission objects that reference it (filtered by rabbitmqClusterReference.name, so a neighbouring release in the same namespace cannot lock this one), and fails the flip while any of them still exist, naming the two-step order. A new suite pins it, thirteen mutations red. The topology operator here runs without --connect-using-plain-http, so the annotation is load-bearing exactly as you traced. The cozyrd comment and the bats header no longer describe a label the chart never applies; the tls.enabled description carries the port and metrics consequences; the certificate prose moved into template comments; the external SAN gate has its isolating case. Body rewritten: the controller is on main and the chart uses a sentinel. One correction on the way: caSecretName does not configure inter-node TLS on v2.9.0, it feeds the client listeners, and the README says so now. Not done: an e2e for the wedge itself, since there is no rabbitmq chainsaw suite; that is a separate change. |
There was a problem hiding this comment.
Superseded by #2683 (review), which carries the same findings as inline comments on the lines they are about. The original text is removed rather than left as a second copy of the same review.
IvanHunters
left a comment
There was a problem hiding this comment.
Verdict
NOT LGTM
The guard added here to prevent a wedged teardown enumerates three of the twelve topology kinds that wedge identically, and the runbook step meant to confirm the namespace is clear checks the same three.
Findings
- [MAJOR]
packages/apps/rabbitmq/templates/rabbitmq.yaml:39, the topology-object refusal covers 3 of the 12 kinds that share the wedging deletion path
Claim mismatches
- "Turning TLS back off leaves the published anchor behind" is contradicted by this PR's own
README.md:110, and the README matches the code:reconciler.go:1101-1103builds a real owner reference, so deleting the sentinel garbage-collects the projection. What survives is the raw CA material, not the anchor. - Three paragraphs describe deltas against earlier revisions of this branch rather than against
main: thespec.override.serviceblock, the<release>-tlsgrant and theadditionalPropertiesguard are not in this diff at all. Worth a line saying they answer review rounds.
Caveats
- Not affected on upgrade, and that is rendered rather than reasoned:
tlsdefaults to{}and nothing infers it fromexternal, so three corners come out unchanged against merge-base. - Certificate renewal reload cannot be verified statically and rests on Erlang re-reading the PEM by mtime. The merged qdrant and nats charts carry identical durations, so it is a property of the series rather than of this PR.
| {{- $live := lookup "rabbitmq.com/v1beta1" "RabbitmqCluster" .Release.Namespace .Release.Name | default dict }} | ||
| {{- if not (dig "spec" "tls" "disableNonTLSListeners" false $live) }} | ||
| {{- $pending := list }} | ||
| {{- range $kind := list "User" "Vhost" "Permission" }} |
There was a problem hiding this comment.
[MAJOR] the topology-object refusal covers 3 of the 12 kinds that share the wedging deletion path
The guard iterates a fixed list, and the runbook checks the same three:
39: {{- range $kind := list "User" "Vhost" "Permission" }}
86: kubectl get users.rabbitmq.com,vhosts.rabbitmq.com,permissions.rabbitmq.com --namespace <tenant-namespace>
For chart-created objects that list is exactly right. The problem is the scope claimed beyond it: the guard's own test is titled "refuses on a hand-written Vhost the chart never declared", and README step 1 says to remove any hand-written topology object. For that scope it is short by nine. In messaging-topology-operator v1.14.2, main.go:180-360 wires Queue, Exchange, Binding, User, Vhost, Policy, OperatorPolicy, Permission, SchemaReplication, Federation, Shovel and TopicPermission through one TopologyReconciler, and controllers/topology_controller.go:64-77 is the single deletion path for all of them.
So the failure is still reachable through the documented procedure. A release with tls.enabled: true and a Queue bound to it: step 1 clears users and vhosts, step 2's command returns nothing because it does not list queues, step 3 sets the flag, the render passes, the broker closes plaintext, and the chart withdraws the operator-connection-uri annotation, so cluster_reference.go:119 falls through to useTLSForConnection = true. The operator then verifies a per-release CA against the system pool, DeleteFunc never succeeds, the finalizer never comes off, and kubectl delete namespace hangs.
Reachability is narrower than the chart-created case: no cozy:tenant:* ClusterRole grants the rabbitmq.com group, so the actor is platform-tier. That is why this is MAJOR and not CRITICAL.
Fix: extend the kind list and both kubectl get strings to the twelve kinds, and add a case seeding a Queue with rabbitmqClusterReference.name equal to the release, asserting the refusal names it. A smaller gap in the same predicate: rabbitmq.yaml:45 matches only on rabbitmqClusterReference.name, while cluster_reference.go:44 shows an object can bind through connectionSecret instead, which no kind list will catch.
If the intent is that the guard only ever covers what the chart creates, the list is right and the fix is documentation: drop the hand-written-object language from README step 1 and retitle that test.
There was a problem hiding this comment.
Fixed in 3c5d9f4. The guard reads a single list in templates/_topology.tpl: the twelve kinds the operator routes through its TopologyReconciler (SuperStream is the thirteenth CRD and has no finalizer path of its own, its Queue, Exchange and Binding children are in the twelve). The lookup loop, the kubectl command the refusal prints and the README runbook all read that list, and hack/check-rabbitmq-topology-kinds.bats pins it against the operator's own CRDs so a make update that adds a kind goes red. Two things fell out on the way: the runbook demanded an empty namespace while the guard ignores objects bound to another release, both now say bound to this release; and the README's open question about closing AMQP through additionalConfig is answered, statefulset.go pins the readiness probe to the amqp port unless the flag is set, so that path leaves every pod not ready.
Superseded by the inline review linked above.
The refusal that stops tls.disableNonTLSListeners from closing the plaintext listeners under live topology objects iterated User, Vhost and Permission, the three kinds the chart itself creates. All twelve kinds messaging-topology-operator drives through TopologyReconciler carry the same deletion.finalizers.<plural>.rabbitmq.com finalizer and share one deletion path, so a Queue or a Shovel bound to the release wedges identically while the render passes and the runbook step reports the namespace clear. Derive the list once in a template helper, read it from both the lookup loop and the confirmation command the refusal prints, and widen the README runbook to the same set. SuperStream stays out: its own reconciler sets no finalizer, and the Queue, Exchange and Binding objects it owns are already on the list. An object that binds through connectionSecret rather than by cluster name is still outside the guard, which the comment now says. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
The list the migration guard iterates can drift two ways. A `make update` in packages/system/rabbitmq-operator can add a CRD the chart never learns about, and the README runbook is a hand-written copy of the command the refusal prints, which an operator follows instead of reading the template. Check both against the vendored CRDs: every kind the operator declares bar SuperStream has to be on the list, and the README command has to name the operator's own plurals in the order the helper yields them. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
The helper comment says a HelmRelease override carrying `tls: null` reaches the chart unpruned and is accepted by values.schema.json. Helm's coalescing drops the key instead, so the schema never sees a null and `.Values.tls` is nil rather than an empty dict. The `dig` on a defaulted dict is still what stops the panic; only the account of how the value arrives was wrong. The README says the flag forces the topology operator onto TLS regardless of how it is configured. The connection-uri annotation short-circuits that calculation before it is reached, so the reason the flag wins is that the port the annotation names is the port the flag closes, which is why the chart withdraws it. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
`var=$(a | b | c) || exit 1` takes the status of the last element only, and the runner sets no pipefail, so an unreadable manifest arrives as an empty string with a zero status. The comparisons that follow still fail closed, but the guard read as protection that was not there. Check the values are non-empty where the pipeline hides the status, drop the clause where it never applied, and read the operator's CRDs once instead of once per kind. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
…t question The confirmation step told the operator to wait for an empty namespace, while the guard it confirms deliberately ignores objects naming another release. A tenant running two brokers in one namespace would wait for something that never happens. Say the check is release-scoped, in the README and in the refusal. Closing AMQP through additionalConfig while keeping the operator on plaintext management was recorded as an open question. It is answerable: cluster-operator pins the readiness probe to the amqp port unless disableNonTLSListeners is set, so that configuration leaves every pod failing readiness. Stated instead of left open. Also repoint one template reference at the heading that exists, and name the runbook test after what it compares. Assisted-by: LLM Signed-off-by: Aleksei Sviridkin <[email protected]>
|
Pushed 3c5d9f4, answer in the thread. The refusal now covers every kind the topology operator routes through its shared reconciler, read from one list that the printed command, the README runbook and a bats guard against the operator's CRDs share. Also corrected on the way: the claim that turning TLS off leaves the published anchor behind (the sentinel is TLS-gated and owns the projection, so GC takes it; what survives is the raw CA material because enableCertificateOwnerRef is false), and three body paragraphs that described deltas against earlier revisions rather than main. Not done: an e2e for the wedge itself, there is still no rabbitmq chainsaw suite. |
IvanHunters
left a comment
There was a problem hiding this comment.
Verdict
LGTM with non-blocking notes
Reviewed at 3c5d9f49e510dd6870c84a331c91ec199aaf18ac against merge-base 3a70f91423b17cafa27e59a5d40fe7c3076416bf. The default path renders byte-identically to main, every schema-valid TLS corner was rendered, and fourteen mutations of the new guards and SAN entries all turn the suite red. One MINOR below, plus the live-cluster surfaces this static review cannot reach.
Findings
- [MINOR]
packages/apps/rabbitmq/README.md:110, the private-key Secrets survive an application delete and only the disable path says so
Caveats
- Upgrade path: rendering the merge-base and head over the
external,replicasandstorageClasscorners produces no difference for an instance that does not settls, which is every instance in the field today. - Not executed, needs a live cluster: enabling TLS on a running instance (issuance, the operator reconciling against a Secret that does not exist yet, the pod roll), the broker picking up a renewed leaf, and
<release>.tenant-careachingtenantsecretsfor this chart. All three sit behind an explicittls.enabled: true, so no unattended upgrade reaches them. See follow-ups. review-helper render-matrixcame backinconclusiveon every corner, so the corner set was driven byhelm templateover the chart's own values instead. A corner on an axis I did not name could have been missed.- Enabling TLS does not reduce what an
external: truebroker exposes. Plaintext 5672 and 15672 stay on the LoadBalancer, andtls.disableNonTLSListeners, the only switch that closes them, refuses to render alongsideusersorvhosts— the only way this chart declares users. A published broker with chart-managed users therefore has no setting that closes its public plaintext ports. The README says so; noted because it is the shape of the feature rather than a defect in it. tests/tls_resolution_test.yaml:154pins a Helm JSON-schema validator message verbatim. It matches on Helm 3.19.0 and 4.0.4, but thechecksjob pins no Helm version, so the assertion rides on whatever the runner carries.- Envelope warnings, unrelated to this diff: 56 CRD files declared a definition the schema pass could not use, 26 group/kind/version pairs are defined twice with differing schemas, and the hot-hunks file holds 100 of 131 matches.
Recommended follow-ups
- A
cozystack-pr-testrun on a disposable dev cluster for the opt-in path: enable TLS on a running instance and watch it converge, read<release>.tenant-caas a tenant, then exercise the two-upgradedisableNonTLSListenersorder and confirm theUserobjects are gone rather than Terminating. The author lists this as known limitation 2; it is the right shape for a separate run. - The trust-anchor leg has an e2e precedent in
hack/e2e-chainsaw/mongodb/chainsaw-test.yaml. Mirroring it for rabbitmq would cover the projection end to end without needing the broker's TLS path. - Pre-existing and outside this change: chart-declared user passwords are rendered into
<release>-<user>-credentialsasstringData, so they land in the stored Helm release manifest and stay there for the retained revisions. Same class as #4078, unrelated to the TLS work, but this chart is the one holding it.
|
|
||
| ### Turning TLS off again | ||
|
|
||
| Disabling TLS on a release that had it needs one manual step. Helm removes the `TenantProjection` sentinel along with the `Certificate` objects, and Kubernetes garbage-collects `rabbitmq-<name>.tenant-ca` with it, so the trust anchor stops being projected as soon as TLS goes off — no stale anchor is left for an endpoint that has gone back to plaintext. What Helm cannot clean up is the raw CA material: cert-manager does not delete the Secrets it produced (`enableCertificateOwnerRef` is off), so `rabbitmq-<name>-ca` and `rabbitmq-<name>-tls` stick around, unused, holding private keys nobody references any more. Delete them by hand after disabling TLS. |
There was a problem hiding this comment.
[MINOR] the private-key Secrets survive an application delete and only the disable path says so
This section covers one of the two ways a release stops using TLS. Deleting the RabbitMQ application altogether hits the same cert-manager behaviour and is not mentioned anywhere.
The chart renders Certificate and Issuer objects and never the Secrets, so Helm's uninstall removes nothing; cert-manager ships enableCertificateOwnerRef: false (packages/system/cert-manager/charts/cert-manager/values.yaml:268, and packages/system/cert-manager/values.yaml does not override it), so it does not delete what it issued either. rabbitmq-<name>-ca, holding the CA private key, and rabbitmq-<name>-tls, holding the server private key, are left in the tenant namespace with nothing referencing them and nothing that will ever collect them. The warning below about copied trust bundles going stale makes the CA's lifetime an operator concern, which is exactly the reader who needs to know the key outlives the application.
Extend the same paragraph to the delete case: after removing a RabbitMQ that had TLS on, kubectl delete secret rabbitmq-<name>-ca rabbitmq-<name>-tls --namespace <tenant-namespace>. Worth confirming what cert-manager does when an application of the same name is created again while the old rabbitmq-<name>-ca Secret is still present and its certificate still valid, because reusing the previous CA key is the plausible outcome and it is not what "a per-release CA" reads like.
There was a problem hiding this comment.
Agreed, the delete path was missing. The paragraph is in #4143 rather than here, since this PR merged on the approvals it had.
What this PR does
Adds TLS for AMQP and the management API, and hands the tenant a key-free trust anchor instead of a private key.
TenantProjectionsentinel; the CA-extraction controller readsca.crtfrom that Secret and writes a key-free projection, stampinginternal.cozystack.io/tenant-caon the projection it owns. That label is what the ResourceDefinition selects. Nothing labels the CA Secret itself, which holds the CA private key and is excluded by name.<release>-tlscarries the broker's private key and reaches no tenant. Neither the dashboard Role nor the ResourceDefinition grants it, and the ResourceDefinition excludes it by name as well, so a label mis-stamped onto it later still does not get through.*.<release>-nodeswildcard that the operator uses forRABBITMQ_NODENAME.TLS is opt-in here, unlike the sibling charts
tls.enableddefaults tofalseand is not inferred fromexternal. That diverges from nats, qdrant and kafka, which infer it, and the divergence is deliberate for a reason specific to this operator.Setting
spec.tls.secretNamemakes the cluster operator rewrite theportkey in the<release>-default-userSecret from 5672 to 5671, on the mere fact of TLS and regardless of whether the plaintext listener is still open. That Secret is the Service Binding the chart hands the tenant. A client with a hardcoded:5672keeps working. A client that reads the endpoint from the binding, which is the documented way, gets moved to the TLS port and must then speak TLS and be given a CA to verify against, neither of which it was configured for. Inferring TLS fromexternalwould fire all of that on an ordinary upgrade, with no configuration change, against exactly the clients that followed the documentation.Whether the other engines should follow is an epic-level question rather than this PR's to answer; for nats it is raised in #3348.
The plaintext ports are still published, and this PR says so
There is no operator-supported way to publish the TLS ports alone.
spec.override.service.spec.portsis a strategic merge keyed onportagainst defaults the operator rebuilds every reconcile, so it adds and mutates entries but never subtracts one, and a TLS-only port list there would read as a guarantee while changing nothing. The chart renders no service override at all and says why at the callsite.tls.disableNonTLSListenersis offered as an explicit opt-in, and it refuses to render rather than failing silently in three situations.usersorvhosts. The flag forces messaging-topology-operator onto TLS regardless of its own configuration, after which it verifies the broker against the system certificate pool, where a per-release CA cannot be. Every topology object bound to the release would stop reconciling, silently: already-created users keep working while password rotation and new users simply never apply. There is no working route left for it, so the chart refuses instead. Managing those objects as hand-written CRs is not a way out, because the operator takes TLS from theRabbitmqClusterand not from whoever created the CR, so manual objects break identically and without the render-time refusal.rabbitmqctlor a definitions import are.Erlang distribution (25672) and epmd (4369) stay plaintext under every setting; the flag does not touch them, so
disableNonTLSListeners: trueis not "TLS everywhere".A third route, closing AMQP at the broker through
spec.rabbitmq.additionalConfigwhile leaving the operator on plaintext management, has been suggested and is plausible, but the operator's sources are not vendored here and it was not verified. It is recorded as an open question rather than promised.Closing the listeners takes two upgrades, and the chart enforces it
Clearing
usersandvhostsand settingtls.disableNonTLSListeners: truein one edit is the natural thing to try, and it wedges the objects being removed. The values-level refusal cannot catch it, because by then both maps are empty. Helm applies every object in the target release before pruning the ones the release dropped, so the broker closes plaintext and loses therabbitmq.com/operator-connection-uriannotation while the topology objects still holddeletion.finalizers.<plural>.rabbitmq.com. Their operator then has to reach a management API it can only get to over a certificate it verifies against the system trust pool. The delete never completes, and the upgrade reports success over objects left inTerminating, with the users still live in the broker andkubectl delete namespaceon that tenant hanging.The chart now reads the namespace rather than the values. When the flag is being introduced and any topology object is still there, the render refuses and names them, gives the two-upgrade order and the
kubectl getthat confirms nothing bound to this release is left, and says how to unwedge a release that is already stuck. The check keys on the liveRabbitmqClusteras well, so it applies only to the transition: once the listeners are closed, an object a tenant creates afterwards is theirs to clean up rather than something that stops the release rendering. Eleven unit cases cover the firing and the silent paths.The set is wider than the three kinds this chart creates. All twelve kinds
messaging-topology-operatordrives through its sharedTopologyReconcilercarry that finalizer and share one deletion path, so aQueueor aShovelwritten by hand against the release wedges exactly the same way, and a runbook step that checks only users, vhosts and permissions says the namespace is clear when it is not. The list is derived in one place and read from both the lookup and thekubectl getthe refusal prints, and a unit test holds it against the operator's own CRDs so a bump cannot introduce a kind the guard has never heard of.SuperStreamis the one CRD left out: it runs on its own reconciler with no finalizer, and the objects it owns are already on the list. An object that binds throughconnectionSecretinstead of naming the cluster stays outside the guard, which the comment says.Breaking for metrics scrapers
Enabling TLS moves the metrics endpoint from 15692 to 15691. A ServiceMonitor selecting the port by the name
prometheussilently stops matching and the instance's metrics disappear with no error anywhere. Nothing in this repository scrapes RabbitMQ, so only user-defined monitoring is affected, silently, which is why it is stated here.Known limitations
TenantProjectionsentinel, and Helm deletes the sentinel along with the rest of the chain. What survives is<release>-caand<release>-tls, because cert-manager runs withenableCertificateOwnerRef: falseand does not delete the Secrets it issued, so the README says to remove them by hand. Disabling TLS leaves the published tenant trust anchor behind — the contract has no withdrawal signal #3349 asks the same question of the label-driven leg of the contract, which this chart does not use.Useris actually gone rather than merely marked for deletion. A template test cannot reach that.On certificate renewal, the leaf carries
duration: 8760handrenewBefore: 720h, so cert-manager rewrites the Secret around day 335. The cluster operator mounts it as a projected volume with nosubPath, so the kubelet refreshes the files in place, and the operator does not watch a user-supplied TLS Secret, so nothing rolls the pods. The last hop, the broker picking the new file up, rests on Erlang'sssl_pem_cachere-reading by mtime and has only been read from sources rather than exercised against a live broker. The merged qdrant chart ships the same duration androtationPolicy, so this is a platform-wide property rather than something this PR introduces.Downstream repositories
packages/apps/rabbitmq/values.schema.jsongains thetlsobject, socozystack/terraform-provider-cozystackneeds the field modelled forcozystack_rabbitmq; tracked in cozystack/terraform-provider-cozystack#42.Release note