Repository navigation
feat: secure the NATS control plane - #194
Merged
Merged
Conversation
Every message on the NATS bus is trusted, so connecting without authentication must be an explicit choice. In production mode, components now fail to start when nats.auth.method is unset or "none" unless nats.auth.allowAnonymous is set to true. Development and debug modes keep allowing anonymous connections. Tests that run the production launchers against an unsecured broker opt in via the new flag. Refs: S1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q43ySpqd18vfAt3S6pk7UR
Connections to the broker could not be encrypted because nats.Connect was called without any TLS option. Components now read nats.tls.enabled, nats.tls.ca, nats.tls.cert and nats.tls.key: setting any of them enables TLS, a CA file replaces the system trust store, and a client certificate and key enable mutual TLS. File paths are checked at startup so misconfiguration fails fast. The NATS test fixture gains SetupNatsContainerWithConfig to start a broker with arbitrary server configuration and files, which the new integration tests use to verify mTLS against a real server. Refs: S1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q43ySpqd18vfAt3S6pk7UR
The Tenant Manager applied any orchestration response whose correlationId matched a participant profile, so anyone on the bus could mark deployments active, overwrite VPA state data, or replay old responses. TM now records the manifest ID and type of the orchestration it starts on the participant profile (cfm.orchestration.pending) and ignores responses that do not match it. A caller-supplied value for that property is overwritten on deploy. The record is cleared once a successful response is applied, so duplicates and replays are dropped. Auto-compensation orchestrations are started by the Provision Manager, not TM, so PM now records which orchestration a compensation compensates and includes it in the response as compensatedManifestId. TM accepts a dispose response that compensates its pending deploy. The manifest field is not deserialized, so external callers cannot claim to compensate an orchestration. Refs: S1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q43ySpqd18vfAt3S6pk7UR
Authentication alone still lets every component publish anything on the bus. Adds docs/developer/nats with a guide to securing the control plane (authentication, TLS, per-component users and known limitations) and a sample NATS authorization block giving the Tenant Manager, Provision Manager, an orchestration agent, the key management agent and IdentityHub only the subjects they need. Only TM may start orchestrations and only IdentityHub may publish key pair events. Two e2e tests run the deploy, dispose and key event flows against the sample file and check that forbidden publishes are rejected, so the documentation stays in sync with the subjects the code uses. Refs: S1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q43ySpqd18vfAt3S6pk7UR
paullatzelsperger
force-pushed
the
feature/s1-secure-nats-control-plane
branch
from
October 30, 2026 11:26
21ecabe to
56c4318
Compare
Test_VerifyAutoCompensation only waited for PM and then called TM, which fails with "connection refused" when TM's HTTP server is not listening yet. Wait for TM as well in the compensation tests and in verifyDeployAndDispose. waitTManager shadowed err, so it never reported a timeout. Fix that and pause between attempts in both wait helpers instead of spinning. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
wolf4ood
approved these changes
Oct 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this PR changes
Every message on the NATS bus is trusted by CFM components, yet the bus was unauthenticated and unencrypted by default. This PR hardens the control plane, one commit per change:
nats.auth.methodis unset ornoneunlessnats.auth.allowAnonymous=true. Development and debug modes are unchanged.nats.tls.enabled,nats.tls.ca,nats.tls.certandnats.tls.keyoptions, integration-tested against a broker that requires mTLS.compensatedManifestId) so TM can accept them.docs/developer/nats/documents securing the bus and ships a sample NATS authorization block with one user per component. Two e2e tests run the deploy, dispose and key-event flows against that file.Notes for reviewers
$KV.<bucket>.>messages, which sends+ACKto the KV writer's reply inbox. The sample permissions deny it; it should go away when the watcher moves to a JetStream consumer.🤖 Generated with Claude Code