Skip to content

fix(fleet): waive the visualiser's plain-HTTP listener, which is loopback-only - #433

Merged
Yambr merged 2 commits into
docs/demo-walkthroughfrom
fix/g7-visualizer-tls-waiver
Aug 11, 2026
Merged

fix(fleet): waive the visualiser's plain-HTTP listener, which is loopback-only#433
Yambr merged 2 commits into
docs/demo-walkthroughfrom
fix/g7-visualizer-tls-waiver

Conversation

@Yambr

@Yambr Yambr commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Clears the last red gate on #346.

The finding

SAST — semgrep blocks on exactly one: go.lang.security.audit.net.use-tls against http.ListenAndServe in deploy/fleet/g7-visualizer/proxy.go:236.

Why a waiver and not a fix

The rule cannot see the deployment, and the deployment is what makes it safe. The compose service publishes the port as:

ports:
  - "127.0.0.1:8099:8099"

Loopback only — never the fleet network, never off-box. That is the sole exposure: no second compose file or override, no nginx or ingress forwarding to it, no k8s manifest, no Helm value, no docker run in a stand script. Terminating TLS on a loopback port means shipping a certificate nobody can validate.

What sits behind the port

Worth stating plainly, because the first version of this waiver got it wrong. The service is not a read-only visualiser: the handler POSTs to the gateway over mTLS (TLS 1.3, client cert from /pki), so this plain-HTTP port is an unauthenticated front to an authenticated channel.

That does not make the waiver false — reaching a loopback port already means host access — but it is the fact a reviewer needs, so the comment states it rather than the softer claim. Two conditions invalidate the waiver, and both are written at the line: the published port losing its 127.0.0.1 prefix, and any route here that is not safe for whoever holds a shell on the host.

Per-line, not a rule disable

A second plain-HTTP listener added later fires fresh instead of being silently pre-exempted. Verified: the finding goes 1 → 0 with the waiver and returns to 1 when it is removed.

One note: gofmt reports this file as unformatted, from a stray blank line at 294 that predates this change. Left alone — it is not this diff's line.

…back-only

`SAST — semgrep` blocks on one finding: `use-tls` against the g7 visualiser's
`http.ListenAndServe`. The rule cannot see the deployment, and the deployment
is what makes it safe — the compose service publishes the port as
`127.0.0.1:8099:8099`, so the listener is reachable from the host loopback
alone, never from the fleet network and never off-box.

It serves a read-only visualiser over data the operator already has locally,
and terminating TLS on a loopback port means shipping a cert nobody can
validate. The waiver states that reason at the line, and says what would
invalidate it: the published port losing its 127.0.0.1 prefix.

Per-line, not a rule disable — a second plain listener added later fires fresh.
Verified: the finding goes 1 -> 0 with the waiver and returns when it is removed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (2)
  • main
  • next/v1

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7ae3c583-7704-4d55-8d29-6416adcdb064

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The waiver called this a read-only visualiser. It is not: the handler POSTs to
the gateway over mTLS with a client cert from /pki, so the plain-HTTP port is
an unauthenticated front to an authenticated channel.

That does not make the waiver false — loopback is still loopback, and reaching
the port already means host access — but the reason a reader checks has to be
the true one. The comment now states it, and names the second condition that
would invalidate it: a route here that is not safe for whoever holds a shell on
the host.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Yambr
Yambr merged commit a8bc021 into docs/demo-walkthrough Aug 11, 2026
11 checks passed
@Yambr
Yambr deleted the fix/g7-visualizer-tls-waiver branch August 11, 2026 09:43
Yambr added a commit that referenced this pull request Aug 11, 2026
…tend bridge (#434)

* fix(fleet): take the unauthenticated exec surface off the shared frontend 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>

* test(fleet): allowlist g7's co-tenants instead of denylisting the web 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>

---------

Co-authored-by: Nick <developer@widemoat.ai>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

2 participants