feat(nats): add TLS support via cert-manager - #2684
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (11)
💤 Files with no reviewable changes (9)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds a tri-state TLS option for NATS (defaults follow ChangesNATS TLS Configuration Support
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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 integrates TLS support into the NATS managed service via cert-manager. It introduces a tri-state configuration for TLS, enabling it automatically for external services while remaining configurable for internal ones. The changes include the generation of a self-signed CA chain, proper wiring into the NATS HelmRelease, and comprehensive unit tests to ensure correct behavior across different configuration scenarios. 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: 1
🤖 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/nats/Makefile`:
- Around line 7-8: Before performing the jq patch that sets
.properties.tls.properties.enabled.type in values.schema.json, first run a
precondition check that the path .properties.tls.properties.enabled exists (fail
with an error and exit if it does not) so the patch cannot create missing paths
and mask generator drift; after the precondition succeeds, apply the existing jq
patch to set the type and keep the existing post-check that validates the type
equals ["boolean","null"]. Use the same values.schema.json target and the same
jq-based validation/exit pattern so the new check sits immediately before the jq
patch line.
🪄 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: 586baa32-7685-471c-bb7a-863534a7230e
📒 Files selected for processing (12)
api/apps/v1alpha1/nats/types.goapi/apps/v1alpha1/nats/zz_generated.deepcopy.gopackages/apps/nats/Makefilepackages/apps/nats/README.mdpackages/apps/nats/templates/_tls.tplpackages/apps/nats/templates/certmanager.yamlpackages/apps/nats/templates/nats.yamlpackages/apps/nats/tests/certmanager_test.yamlpackages/apps/nats/tests/nats_tls_test.yamlpackages/apps/nats/values.schema.jsonpackages/apps/nats/values.yamlpackages/system/nats-rd/cozyrds/nats.yaml
There was a problem hiding this comment.
Code Review
This pull request introduces TLS support for the NATS application, implementing a tri-state logic for the tls.enabled flag that defaults to the external access setting if omitted. The implementation includes new cert-manager templates for automated certificate generation, updates to the NATS HelmRelease configuration, and extensive unit tests. Review feedback recommends using the dig function in Helm templates when accessing nested values like _namespace and _cluster to ensure defensive handling of potentially missing keys.
025c7f7 to
cd7b067
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/apps/nats/tests/certmanager_test.yaml (1)
271-287: ⚡ Quick winAdd an explicit
tls.enabled: nulltri-state test case.You validate “unset” behavior via omission; adding
enabled: nullwill guard the schema-supported null path explicitly and prevent regressions in helper logic.Suggested test addition
+ - it: renders cert-manager chain when tls.enabled is null and external is true + set: + tls: + enabled: null + external: true + asserts: + - hasDocuments: + count: 4 + + - it: renders no documents when tls.enabled is null and external is false + set: + tls: + enabled: null + external: false + asserts: + - hasDocuments: + count: 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/nats/tests/certmanager_test.yaml` around lines 271 - 287, Add an explicit tri-state test that sets tls.enabled: null to ensure the null path is covered: mirror the existing "renders cert-manager chain when tls.enabled is not set and external is true" test by adding a new test entry named e.g. "renders cert-manager chain when tls.enabled is null and external is true" with set: { external: true, tls: { enabled: null } } and asserts: hasDocuments: count: 4 (you can also add the equivalent null+external: false case expecting 0 if desired); place it alongside the existing tests so the helper logic is validated for schema-supported null values.
🤖 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.
Nitpick comments:
In `@packages/apps/nats/tests/certmanager_test.yaml`:
- Around line 271-287: Add an explicit tri-state test that sets tls.enabled:
null to ensure the null path is covered: mirror the existing "renders
cert-manager chain when tls.enabled is not set and external is true" test by
adding a new test entry named e.g. "renders cert-manager chain when tls.enabled
is null and external is true" with set: { external: true, tls: { enabled: null }
} and asserts: hasDocuments: count: 4 (you can also add the equivalent
null+external: false case expecting 0 if desired); place it alongside the
existing tests so the helper logic is validated for schema-supported null
values.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 35132ae4-7778-4fbe-9e90-c0bcaf906b12
📒 Files selected for processing (12)
api/apps/v1alpha1/nats/types.goapi/apps/v1alpha1/nats/zz_generated.deepcopy.gopackages/apps/nats/Makefilepackages/apps/nats/README.mdpackages/apps/nats/templates/_tls.tplpackages/apps/nats/templates/certmanager.yamlpackages/apps/nats/templates/nats.yamlpackages/apps/nats/tests/certmanager_test.yamlpackages/apps/nats/tests/nats_tls_test.yamlpackages/apps/nats/values.schema.jsonpackages/apps/nats/values.yamlpackages/system/nats-rd/cozyrds/nats.yaml
✅ Files skipped from review due to trivial changes (1)
- packages/apps/nats/README.md
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
NOT LGTM — clean cert chain with correct usages, the upstream chart's config-reloader sidecar handles cert rotation cleanly via SIGHUP (verified through nats.reloaderConfig extracting cert/key/CA paths), and the dashboard RBAC only exposes <release>-credentials (no TLS-secret leak like #2680). One blocker: the cluster-route URLs don't match the SAN list, and replicas: 2 is the default for this chart so this hits on first install.
Business context: Add TLS for NATS managed service — client connections on :4222 and inter-pod cluster routes on :6222 — via a self-contained cert-manager chain, with tls.enabled tri-state falling back to external when unset.
Blockers
B1: Cluster TLS handshake fails because route URLs use short-form pod hostnames not in the SAN list
File: packages/apps/nats/templates/nats.yaml:93-102 + packages/apps/nats/templates/certmanager.yaml:60-64
Issue: The upstream NATS chart at packages/system/nats/charts/nats/files/config/cluster.yaml:12-17 constructs each cluster route URL as:
{{ proto }}://{{ release }}-{{ i }}.{{ release }}-headless:{{ port }}
…unless routeURLs.useFQDN: true, in which case it appends .{{ ns }}.svc.{{ clusterDomain }}. The upstream default is useFQDN: false (verified at packages/system/nats/charts/nats/values.yaml:60), and this PR's HelmRelease values don't override it — only routeURLs.k8sClusterDomain is set, which has no effect when useFQDN is off.
The cert SAN list (certmanager.yaml:60-64) covers <release>-headless{,.<ns>.svc{,.<cluster>}} plus the wildcard *.<release>-headless.<ns>.svc.<cluster>. X.509 wildcards match exactly one DNS label at the leftmost position — so the wildcard matches <release>-0.<release>-headless.<ns>.svc.<cluster> (FQDN form) but does NOT match <release>-0.<release>-headless (short form). The short form has fewer labels than the wildcard pattern.
Evidence: packages/system/nats/charts/nats/files/config/cluster.yaml:12-17 — the $domain variable stays at $.Values.headlessService.name (i.e. <release>-headless) when useFQDN is false; the route URL template printf "%s://%s%s-%d.%s:%d" then yields tls://<release>-N.<release>-headless:6222. NATS Go client TLS verification (used internally by the server for cluster route dialing) compares the SAN list against the literal URL hostname, not the post-resolution FQDN. No insecure option is set in the cluster TLS block.
Impact: Default install (this chart ships replicas: 2, see values.yaml:59) + TLS auto-on (whenever external: true is set, which also triggers TLS via the tri-state fallback) → cluster won't form. Pods log x509: certificate is valid for <list>, not <release>-0.<release>-headless and reroute attempts loop. Single-replica deployments happen to work because no peer route is dialed.
Fix: Force FQDN route URLs in the HelmRelease values whenever TLS is enabled:
cluster:
enabled: true
routeURLs:
useFQDN: true
k8sClusterDomain: {{ $clusterDomain }}
replicas: {{ .Values.replicas }}This swaps the route URL to <release>-N.<release>-headless.<ns>.svc.<cluster> which the existing wildcard SAN covers. No SAN changes needed.
Non-blocking follow-ups
-
certmanager.yaml:25-26: CAprivateKeyonly setsrotationPolicy: Never, leaving algorithm/size at cert-manager defaults (RSA-2048). The leaf certificate has noprivateKeyblock at all. Non-uniform across the sibling TLS PRs (mariadb uses RSA-4096 explicitly); RSA-2048 is still sound for a tenant-scoped CA but worth a brief look for a project-level convention. -
_tls.tpl:11-17—kindIs "invalid"correctly handlestls.enabled == nil(the Makefile patches the schema to["boolean","null"]). One edge case: if a future codegen drift unknowingly changes the schema to["boolean"]only, the Makefile's precondition check (commit68264383) will catch the schema rebuild but not a manually-passednull— though Helm itself will rejectnullagainst"type":"boolean", so the failure mode is at install time, not template time. Acceptable; the Makefile precondition is a good safeguard. -
nats.yaml:62-66:tlsCA.enabled: truewithsecretName: <release>-camounts the CA secret cluster-wide for verification. cert-manager's CA secret (<release>-ca) is akubernetes.io/tlssecret that also containstls.key(the CA private key). The NATS upstream chart'stlsCAVolumemounts the whole secret. Tenant pods don't have direct read access to that secret (no separate RBAC change exposes it), but it's worth noting that NATS pods themselves can read the CA private key from the mounted volume. Strictly speaking the pod doesn't need the CA private key — onlyca.crt— but the chart mounts everything from the Secret. Not a blocker (this is on the NATS pod, not on tenant users), but a defense-in-depth improvement would be a separate CA-cert-only ConfigMap.
|
Addressed your review. Blockers:
Non-blocking follow-ups:
Series-wide cleanups applied here too:
Ready for re-review. |
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
LGTM. Both findings from my prior review on this branch are addressed in this push, plus the schema-array regression and the missing digital signature are folded in.
a62225796 fix(nats): force cluster route useFQDN when TLS enabled to match cert SAN wildcard— closes the SAN-vs-route-URL mismatch I flagged. Thenats.useFQDN: trueoverride under$tlsEnabledaligns the route URL (<release>-N.<release>-headless.<ns>.svc.<cluster-domain>) with the cert's wildcard*.<release>-headless.<ns>.svc.<cluster-domain>SAN.b00b17448 refactor(nats): switch CA and leaf private keys to ECDSA P-256+ leaf usages[digital signature, key encipherment, server auth, client auth]—digital signaturewas present already, but the explicit ECDSA + the keep of the same usages survives the rewrite.682643831 fix(nats): add precondition check before schema patch in Makefile+cd7b06797 chore(nats): regenerate codegen artifacts+1f830806d refactor(nats): drop null from tls.enabled schema, use kindIs invalid pattern— same schema-shape fix as the rest of the batch (apiextv1.JSONSchemaProps can't unmarshal["boolean","null"]).
dashboard RBAC exposes only <release>-credentials — the TLS Secret isn't granted to tenant use, so the B3 pattern that's blocking the chart-side cert-manager peer PRs doesn't apply here. NATS config-reloader sidecar handles SIGHUP-driven cert rotation in the chart already, so no Reloader annotation needed.
- Extend values.yaml and JSON schema with tls.enabled (*bool tri-state) and tls.secretName fields - Update Go types and regenerate README for TLS field - Enable TLS toggle in nats-rd cozyrds config - Wire helm-unittest into Makefile as test target Signed-off-by: Arsolitt <arsolitt@gmail.com>
- Add _tls.tpl helper with tri-state resolver (nil auto-detects, true/false explicit) - Add certmanager.yaml to provision Certificate and Issuer resources for client, server, and leaf certs - Mount CA and certs in nats.yaml with correct EKUs for cluster mTLS and external client auth Signed-off-by: Arsolitt <arsolitt@gmail.com>
- certmanager_test.yaml: verify Certificate and Issuer resources are rendered correctly for all TLS scenarios - nats_tls_test.yaml: verify nats.yaml mounts, config blocks, and tri-state TLS logic across enabled/disabled/auto modes Signed-off-by: Arsolitt <arsolitt@gmail.com>
Signed-off-by: Arsolitt <arsolitt@gmail.com>
Guard the tls.enabled nullable patch with an existence check so that generator drift (cozyvalues-gen changing its output shape) is detected immediately rather than being masked by jq silently creating the path. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Arsolitt <arsolitt@gmail.com>
… SAN wildcard The upstream NATS chart defaults routeURLs.useFQDN to false, which causes peer routes to resolve as tls://<release>-N.<release>-headless:6222 (short form). The cert SAN wildcard *.<release>-headless.<ns>.svc.<cluster> matches FQDN forms but not the short form, so inter-pod cluster traffic runs in encryption-only mode without hostname verification. Setting useFQDN: true when TLS is enabled makes peer routes use the FQDN form that matches the wildcard SAN, restoring authenticated TLS for cluster routing. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Arsolitt <arsolitt@gmail.com>
RSA is slower and produces larger keys and certificates than ECDSA. P-256 provides equivalent security with smaller cert size and faster TLS handshakes, which matters for frequent cluster-internal reconnections. CA retains rotationPolicy: Never; leaf retains rotationPolicy: Always. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Arsolitt <arsolitt@gmail.com>
… pattern Remove the jq post-processing step from the Makefile that was patching tls.enabled to ["boolean","null"]. The plain "type":"boolean" form that cozyvalues-gen emits is correct: cozystack-api unmarshals openAPISchema into apiextv1.JSONSchemaProps where Type is a single string, so an array type silently zeroes out specSchema and disables server-side defaulting. Tri-state semantics (nil / true / false) are preserved entirely on the Helm side via kindIs "invalid" guards in the templates. Signed-off-by: Arsolitt <arsolitt@gmail.com>
1f83080 to
d9a11a2
Compare
Aleksei Sviridkin (lexfrei)
left a comment
There was a problem hiding this comment.
LGTM — TLS via cert-manager is correctly wired for both client (4222) and cluster (6222) traffic, the cluster-route SAN mismatch from the earlier review is fixed and test-covered, generated artifacts are in sync, and all 51 helm-unittest cases pass.
Business context: adds TLS to the managed NATS app via a self-contained cert-manager chain (SelfSigned issuer → CA → leaf), with a tri-state tls.enabled that falls back to external when unset.
Non-blocking follow-ups
- The CA uses ECDSA P-256 with
rotationPolicy: Neverand the leaf ECDSA P-256 withrotationPolicy: Always+renewBefore: 720h— consistent and sound. Worth confirming the sibling TLS PRs converge on the same algorithm/curve for cross-app uniformity (packages/apps/nats/templates/certmanager.yaml:25-28,51-54). tlsCAmounts the whole<release>-casecret (including the CA private key) into NATS pods, though onlyca.crtis needed for trust. Not a tenant-facing leak (no RBAC surfaces it; the dashboard exposes only<release>-credentials) and the pod is the trust anchor anyway — a CA-cert-only ConfigMap would be defence-in-depth, not a requirement (packages/apps/nats/templates/nats.yaml:62-66).- Tri-state default-on means an existing install with
external: trueand notlsset flips TLS on upon upgrade, breaking plaintext external clients. This matches the secure-by-default stance and is documented — worth calling out as intended breaking behaviour in the release note.
What this PR does
Adds TLS support to the NATS managed service via cert-manager.
tls.enabledfield with tri-state semantics: when unset, defaults to the value ofexternal(auto-on for externally published services, off for cluster-internal). Explicittls.enabledalways wins..ns.svc,.ns.svc.<cluster-domain>) plus the external hostname whenexternal: true.Verified end-to-end on a sandbox cluster: cert chain reaches Ready, TLS handshake against the tenant service succeeds with the chart CA bundle.
Release note
Summary by CodeRabbit
New Features
Documentation
Tests