fix(fleet): take the unauthenticated exec surface off the shared frontend bridge - #434
Merged
Merged
Conversation
…tend bridge The waiver merged in #433 rested on a false premise. It said the visualiser was "reachable from the host loopback alone — never from the fleet network". The `ports:` publish is loopback, but the service sat on `ocu-frontend`, a plain bridge it shared with open-webui, webui, embed-portal, admin and mcp-gateway. A published port says nothing about in-network callers, so every one of those could reach it. What they could reach matters: `/api/create`, `/api/exec`, `/api/tool` and `/api/destroy` take NO inbound credential, and the process holds the gateway mTLS client cert. Any co-tenant could POST an arbitrary argv in cleartext and have it executed in a live guest under that cert — with open-webui, which processes untrusted input, among the co-tenants. g7 now shares a dedicated bridge with control alone, which is the only hop it needs. The waiver stays, because the rule still cannot see a deployment, but it now states the containment that is real instead of the one that was not, and says that the exposure here is authorisation rather than eavesdropping. `deploy/tests/test_fleet_g7_isolation.py` turns all of it into properties: the loopback prefix, no shared bridge with the web tier, and — so the isolation cannot pass by breaking the service — that g7 still shares a network with control. Mutation-checked: each of the three reds on its own violation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. 🗂️ Base branches to auto review (2)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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 |
… tier
A hard-coded list of web-tier names passes silently the moment a service is
renamed or a new one is added — the assertion would still be green while the
containment was gone. The test now derives the actual co-tenant set from the
compose file and requires it to be exactly `{control}`, so any unlisted service
joining that bridge reds and is named in the failure.
Mutation-checked: adding an arbitrary service to `ocu-g7` reds and prints which
one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Corrects #433, which I merged on a false premise.
What was wrong
That waiver said the visualiser is "reachable from the host loopback alone — never from the fleet network". The first clause is true; the second was false. The
ports:publish is127.0.0.1:8099:8099, but the service sat onocu-frontend— a plain bridge (driver: bridge, nointernal: true) shared withopen-webui,webui,embed-portal,adminandmcp-gateway. A published port governs host exposure, not in-network reachability, so every co-tenant could reach it.Why that mattered
/api/create/api/exec/api/tool/api/destroyThere is no
Authorizationcheck, no token, no header check anywhere on the inbound side — I grepped for all of them. The process holds the gateway mTLS client cert (/pki/client.pem), so any co-tenant could POST an arbitrary argv in cleartext and have it executed under that cert with no credential of its own.open-webuiprocesses untrusted agent and user input and was one of those co-tenants.The fix
g7 moves to a dedicated
ocu-g7bridge shared withcontrolalone — the only hop it needs (GATEWAY_URLdefaults tohttps://control:9466). The web tier can no longer address it at all.The
use-tlswaiver stays, because the rule still cannot see a deployment, but its stated reason is replaced: the containment is the network isolation, and the exposure on this hop is authorisation, not eavesdropping. TLS here would encrypt a channel whose problem was never confidentiality.Enforced, not asserted
deploy/tests/test_fleet_g7_isolation.pymakes all three properties mechanical, which is what #433 was missing — it asserted a precondition nothing checked:127.0.0.1:prefixMutation-checked: each reds on its own violation (put g7 back on
ocu-frontend; drop the loopback prefix; remove control fromocu-g7).Not addressed here
The
/api/*surface is still unauthenticated. Network isolation contains it for this deployment; it is not a substitute for an inbound credential. That is a larger change than this correction, and it belongs to whoever owns the demo surface.