Skip to content

fix for clone/stop cross namespace#515

Merged
jordan-rash merged 5 commits intomainfrom
fix/clone-stop-cross-namespace
Apr 9, 2026
Merged

fix for clone/stop cross namespace#515
jordan-rash merged 5 commits intomainfrom
fix/clone-stop-cross-namespace

Conversation

@jordan-rash
Copy link
Copy Markdown
Contributor

@jordan-rash jordan-rash commented Apr 9, 2026

fix: honor owning namespace on clone, stop, and credential mint

Operations initiated by the system namespace must act on behalf of the workload's owning namespace. The client now publishes on the owning namespace's NATS control subjects (masquerading at the subject layer while authenticated as system) and discovers the target namespace via ListWorkloads when the system user stops a workload by id. The node mints workload credentials against the request body's namespace so log
permissions line up with where the workload actually lives. handleCloneWorkload also gains a guard that prevents non-system callers from reading workload definitions belonging to other namespaces.

  • client.StartWorkload now takes a *models.StartWorkloadRequest so the caller controls the target namespace directly; Auction takes an explicit namespace argument
  • client.StopWorkload detects system callers and resolves the owning namespace via ListWorkloads before publishing the stop; errors out clearly when the namespace cannot be determined, suggesting the --namespace flag as the operator escape hatch
  • client.CloneWorkload uses cloneResp.Namespace for both the auction and the deploy, so clones land in the owning namespace instead of being re-homed into system
  • handlers.go handleCloneWorkload silently drops responses whose workload namespace does not match the caller (unless caller is system), closing a pre-existing cross-namespace info disclosure

Operations initiated by the system namespace must act on behalf of the
workload's owning namespace. The client now publishes on the owning
namespace's NATS control subjects (masquerading at the subject layer
while authenticated as system) and discovers the target namespace via
ListWorkloads when the system user stops a workload by id. The node
mints workload credentials against the request body's namespace so log
permissions line up with where the workload actually lives.
handleCloneWorkload also gains a guard that prevents non-system callers
from reading workload definitions belonging to other namespaces.

- client.StartWorkload now takes a *models.StartWorkloadRequest so the
  caller controls the target namespace directly; Auction takes an
  explicit namespace argument
- client.StopWorkload detects system callers and resolves the owning
  namespace via ListWorkloads before publishing the stop; errors out
  clearly when the namespace cannot be determined, suggesting the
  --namespace flag as the operator escape hatch
- client.CloneWorkload uses cloneResp.Namespace for both the auction
  and the deploy, so clones land in the owning namespace instead of
  being re-homed into system
- handlers.go handleAuctionDeploy mints WorkloadCred with req.Namespace
  so \$NEX.FEED.<ns>.logs.> permission lines up with the workload's
  actual namespace
- handlers.go handleCloneWorkload silently drops responses whose
  workload namespace does not match the caller (unless caller is
  system), closing a pre-existing cross-namespace info disclosure
- nex workload copy --stop prints clone success and stop failure as
  distinct lines so partial failures are not silent
- Error messages on the system-user stop discovery path do not echo
  the resolved namespace names, to avoid leaking namespace inventory
  through NATS micro response headers and logs
- Tests: cross-namespace clone, cross-namespace clone+stop, system-user
  stop discovery, not-found, and a WorkloadClaims unit test locking in
  the log-subject scope invariant

Signed-off-by: Jordan Rash <jordan@synadia.com>
- StartWorkload shallow-copies the incoming request before defaulting
  Tags so call sites that pass a struct they still hold (notably
  CloneWorkload passing cloneResp verbatim) do not see silent mutation
- CloneWorkload fails fast with a clear error when cloneResp.Namespace
  is empty, instead of publishing on a malformed auction subject and
  surfacing as a misleading "no nodes available for placement"
- TestNexClient_CloneWorkload_CrossNamespace replaces a trivially-true
  subtraction assertion with explicit count checks against both the
  user and system clients

Signed-off-by: Jordan Rash <jordan@synadia.com>
@jordan-rash jordan-rash requested a review from a team as a code owner April 9, 2026 17:44
@jordan-rash jordan-rash changed the title for for clone/stop cross namespace fix for clone/stop cross namespace Apr 9, 2026
Signed-off-by: Jordan Rash <jordan@synadia.com>
Signed-off-by: Jordan Rash <jordan@synadia.com>
Signed-off-by: Jordan Rash <jordan@synadia.com>
@jordan-rash jordan-rash merged commit 7735cd3 into main Apr 9, 2026
4 checks passed
@jordan-rash jordan-rash deleted the fix/clone-stop-cross-namespace branch April 9, 2026 23:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant