Skip to content

feat(kubernetes): support corporate upstream proxy - #2633

Merged
johntmyers merged 3 commits into
NVIDIA:mainfrom
loveRhythm1990:2624-kubernetes-corporate-proxy/lr
Aug 14, 2026
Merged

johntmyers merged 3 commits into
NVIDIA:mainfrom
loveRhythm1990:2624-kubernetes-corporate-proxy/lr

Conversation

@loveRhythm1990

@loveRhythm1990 loveRhythm1990 commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Add Kubernetes support for the existing supervisor corporate HTTP forward-proxy path. This lets sandboxes in proxy-only clusters reach policy-approved TLS destinations through an operator-managed proxy without allowing workloads to choose, bypass, or observe the upstream route.

The Kubernetes driver previously had no configuration or Secret-projection path for the shared supervisor feature that Podman already uses. This PR adds that driver-level plumbing while preserving direct egress as the default when no proxy is configured.

Related Issue

Closes #2624

Changes

  • Add Kubernetes driver configuration for https_proxy, no_proxy, proxy credential Secret name/key, insecure Basic-auth acknowledgement, and CONNECT-by-hostname mode. The URL remains intentionally limited to http:// forward proxies; HTTPS-to-proxy support is out of scope.
  • Expose the configuration through gateway CLI/environment options and Helm upstreamProxy values, while keeping it deployment-owned rather than allowing per-sandbox driver_config overrides.
  • Reuse the shared URL validation and enforce coherent settings at gateway startup: invalid URLs, unpaired Secret fields, invalid Secret names/keys, and unsafe credential settings fail closed.
  • Project credentials as a read-only Kubernetes Secret volume, never as an environment value, annotation, or command-line value. The gateway validates the reference syntax; kubelet resolves Secret existence/key availability when the sandbox Pod starts; the supervisor validates the credential content and rejects malformed files.
  • Require topology = \"sidecar\" whenever proxy credentials are configured. Combined topology shares a container filesystem with the workload, and workspace fsGroup can make an otherwise root-only Secret mount group-readable; rejecting this combination avoids a credential disclosure path. Credential-free proxy configuration can still use Combined topology.
  • Pass upstream arguments only to the network-supervising container. In sidecar mode this is the network sidecar; network-init never receives proxy configuration and the agent container never receives the credential mount.
  • Update the gateway reference, Kubernetes setup guide, compute-driver reference, architecture overview, Helm README, values, and rendering tests.

Testing

  • mise run pre-commit passes
  • Kubernetes driver unit tests and gateway TOML/Helm rendering tests updated
  • Kubernetes mock-proxy e2e: authenticated CONNECT reaches a TLS upstream and policy-denied traffic never reaches the proxy
  • Kubernetes mock-proxy e2e: NO_PROXY bypasses only the corporate proxy; policy-denied traffic remains denied
  • Kubernetes e2e: missing credential Secret fails sandbox creation closed
  • Kubernetes e2e: malformed credential content fails sandbox creation closed
  • mise run test has one unrelated environment-dependent DNS failure: test_forward_public_ip_allowed_without_allowed_ips resolved dns.google to 198.18.1.240, which the SSRF guard correctly rejects. All Kubernetes proxy-specific checks above passed.

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)

@copy-pr-bot

copy-pr-bot Bot commented Aug 6, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

All contributors have signed the DCO ✍️ ✅
Posted by the DCO Assistant Lite bot.

@loveRhythm1990
loveRhythm1990 marked this pull request as ready for review August 6, 2026 13:48
@loveRhythm1990
loveRhythm1990 force-pushed the 2624-kubernetes-corporate-proxy/lr branch 2 times, most recently from 26493d1 to 78250e4 Compare August 6, 2026 13:52

@johntmyers johntmyers left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gator-agent

PR Review Status

Validation: Project-valid as a focused Kubernetes-driver extension of the corporate upstream-proxy support merged in #2245, with the Kubernetes-specific operator path and motivation documented in #2624.
Head SHA: 78250e45a3ade0622f167be52f1499e190ecc917
Base SHA: 0c7e59a95355cabc15ccaddb86fcbe6a1d30eaaa
Merge base SHA: d2c44b0e5393e3746eae5783aa99ac31be08daae
Patch ID: 5db52dea1e5bdc062384aa8edc68b640bfa32b9d
Gator payload: 4
Review mode: initial
Previous reviewed SHA: none
Review budget exhausted: no
Maintainer decision required: no

Blocking findings:

  • GATOR-78250e45-01: Kubernetes Secret keys longer than 253 bytes pass gateway validation even though Kubernetes cannot create them, shifting an invalid deployment setting into repeated sandbox Pod-provisioning failures.

Carried findings:

  • None

Non-blocking suggestions:

  • Consider adding corporate-proxy checks to debug-openshell-cluster for rendered configuration, Secret-volume events, supervisor arguments/mounts, and proxy reachability.

Docs: Fern documentation covers the new Kubernetes setup and gateway/compute-driver configuration; existing navigation already exposes those pages.

Next state: gator:in-review

Comment thread crates/openshell-driver-kubernetes/src/config.rs
@johntmyers johntmyers added the gator:in-review Gator is reviewing or awaiting PR review feedback label Aug 13, 2026
@johntmyers

Copy link
Copy Markdown
Collaborator

Thanks @loveRhythm1990 - started our review agent. I'm also soliciting some reviews from other maintainers.

@russellb

Copy link
Copy Markdown
Contributor

I took a pass on this and wasn't able to identify any issues beyond the one already highlighted by John's agent. Nice work

Signed-off-by: loveRhythm1990 <qiuweimin@126.com>
…t create

Gateway validation accepted proxy_auth_secret_key values that Kubernetes
rejects when creating the Secret (keys longer than 253 bytes, or the
reserved "."/".." names), turning an invalid deployment setting into
repeated sandbox Pod-provisioning failures instead of a startup error.
Reject them in validate_upstream_proxy_config so they fail closed at
gateway startup.

Signed-off-by: loveRhythm1990 <qiuweimin@126.com>
…luster

Add a Kubernetes corporate upstream proxy troubleshooting section covering
rendered [openshell.drivers.kubernetes] configuration, credential Secret
volume events, supervisor arguments and mounts confined to the network-
supervising container, and proxy reachability.

Signed-off-by: loveRhythm1990 <qiuweimin@126.com>
@loveRhythm1990
loveRhythm1990 force-pushed the 2624-kubernetes-corporate-proxy/lr branch from 78250e4 to f727a8d Compare August 14, 2026 04:51
@loveRhythm1990

Copy link
Copy Markdown
Contributor Author

recheck

@johntmyers johntmyers added the test:e2e Requires end-to-end coverage label Aug 14, 2026
@github-actions

Copy link
Copy Markdown

Label test:e2e applied, but pull-request/2633 does not exist yet. A maintainer needs to comment /ok to test f727a8d7128ff46cd8222482fdc1cf9893887bb7 to mirror this PR. Once the mirror exists, re-apply the label or re-run Branch E2E Checks from the Actions tab.

@johntmyers johntmyers left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gator-agent

PR Review Status

Thanks @loveRhythm1990. I checked your update that rejects Kubernetes Secret keys over 253 bytes and the reserved ./.. names, including the 253-byte accepted and 254-byte rejected boundary tests. That resolves GATOR-78250e45-01. I also checked the author-only troubleshooting-skill update; it addresses the earlier non-blocking suggestion.

Validation: Project-valid as a focused Kubernetes-driver extension of the corporate upstream-proxy support, with the Kubernetes-specific operator path and motivation documented in accepted issue #2624.
Head SHA: f727a8d7128ff46cd8222482fdc1cf9893887bb7
Base SHA: c4b500a7de64d0b66e3ee8098f58d14299092162
Merge base SHA: c4b500a7de64d0b66e3ee8098f58d14299092162
Patch ID: d090894f5d8ca10c405cc12c83dc9c82038f991a
Gator payload: 4
Review mode: follow_up
Previous reviewed SHA: 78250e45a3ade0622f167be52f1499e190ecc917
Review budget exhausted: no
Maintainer decision required: no

Blocking findings:

  • No blocking findings remain.

Carried findings:

  • GATOR-78250e45-01: resolved by the latest author delta.

Docs: Fern documentation covers the user-visible Kubernetes proxy configuration, and the debug-cluster skill now covers rendered configuration, Secret-volume events, supervisor arguments and mounts, and proxy reachability.

Next state: gator:in-review pending confirmation that the newly requested current-head E2E workflow has been queued, started, or completed.

@johntmyers

Copy link
Copy Markdown
Collaborator

/ok to test f727a8d

@johntmyers johntmyers added gator:watch-pipeline Gator is monitoring PR CI/CD status gator:approval-needed Gator completed review; maintainer approval needed and removed gator:in-review Gator is reviewing or awaiting PR review feedback gator:watch-pipeline Gator is monitoring PR CI/CD status labels Aug 14, 2026
@johntmyers
johntmyers added this pull request to the merge queue Aug 14, 2026
@johntmyers johntmyers added gator:merge-ready and removed gator:approval-needed Gator completed review; maintainer approval needed labels Aug 14, 2026
Merged via the queue into NVIDIA:main with commit d0c6dc3 Aug 14, 2026
67 of 71 checks passed
@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

Monitoring Complete

Monitoring is complete because this PR has merged.

Head SHA: f727a8d7128ff46cd8222482fdc1cf9893887bb7
Gator payload: 4

Final status: The PR reached gator:merge-ready after the Kubernetes corporate-upstream-proxy review finding was resolved, current-head E2E completed, required checks passed, and maintainer approval was recorded.

I removed the active gator:* label because there is nothing left for gator to monitor on this PR.

johntmyers pushed a commit to feloy/OpenShell that referenced this pull request Sep 2, 2026
…dboxes

The corporate forward proxy machinery from NVIDIA#1792 is driver-agnostic and
already merged: openshell-supervisor-network implements CONNECT chaining,
NO_PROXY matching, credentials, https:// proxies and corporate CA trust, and
openshell-sandbox exposes it as six argv-only flags. Podman gained the driver
half in NVIDIA#2245/NVIDIA#2512 and Kubernetes in NVIDIA#2633; the VM driver had none of it, so
VM sandboxes on proxy-only networks could not reach any destination requiring
the proxy even when policy allowed it.

The blocking piece was not proxy logic but delivery: the VM guest init script
runs as PID 1 and execs a fixed supervisor command line, and libkrun's
krun_set_exec receives an empty argv, so there was no channel for driver-owned
supervisor arguments. The supervisor's proxy flags deliberately have no
environment fallback, and build_guest_environment merges user-supplied
environment, so the guest env is not a safe transport either.

Add a driver-authored argument file, mirroring the existing init.d manifest:
the driver writes /opt/openshell/supervisor-args into the overlay upperdir on
every launch and the guest reads it verbatim, one argument per line, appending
it to every supervisor exec. It is written even when empty, which is what makes
the channel unforgeable -- the upperdir always shadows the read-only image
layer, so an image can neither supply its own arguments nor disable the
operator's by omitting the file. Because both launch backends exec the same
init script, this covers libkrun and QEMU without touching either.

A microVM has no bind mounts or container secrets, so the credential and CA
bundle are staged into the per-sandbox overlay the way the gateway JWT already
is: credential root-only at 0600, CA at 0644, both rewritten every launch so a
removed setting clears prior material, and both deleted with the sandbox state
directory. This places the credential at rest in the overlay image on the
gateway host, which differs from the Podman secret model and is documented as
an explicit security consideration.

Validation is fail-closed and shared: a new
openshell_core::driver_utils::validate_upstream_proxy_settings holds the
pairing rules the Podman driver established, and both the gateway and the
driver call it so an invalid table names the offending key instead of
surfacing as an opaque driver-readiness timeout.

Guest egress leaves through gvproxy, so a proxy on the gateway host's loopback
is reachable only through host.openshell.internal; the guest to gateway
callback is unaffected.

Closes NVIDIA#3088

Signed-off-by: Philippe Martin <phmartin@redhat.com>
feloy added a commit to feloy/OpenShell that referenced this pull request Sep 2, 2026
…dboxes

The corporate forward proxy machinery from NVIDIA#1792 is driver-agnostic and
already merged: openshell-supervisor-network implements CONNECT chaining,
NO_PROXY matching, credentials, https:// proxies and corporate CA trust, and
openshell-sandbox exposes it as six argv-only flags. Podman gained the driver
half in NVIDIA#2245/NVIDIA#2512 and Kubernetes in NVIDIA#2633; the VM driver had none of it, so
VM sandboxes on proxy-only networks could not reach any destination requiring
the proxy even when policy allowed it.

The blocking piece was not proxy logic but delivery: the VM guest init script
runs as PID 1 and execs a fixed supervisor command line, and libkrun's
krun_set_exec receives an empty argv, so there was no channel for driver-owned
supervisor arguments. The supervisor's proxy flags deliberately have no
environment fallback, and build_guest_environment merges user-supplied
environment, so the guest env is not a safe transport either.

Add a driver-authored argument file, mirroring the existing init.d manifest:
the driver writes /opt/openshell/supervisor-args into the overlay upperdir on
every launch and the guest reads it verbatim, one argument per line, appending
it to every supervisor exec. It is written even when empty, which is what makes
the channel unforgeable -- the upperdir always shadows the read-only image
layer, so an image can neither supply its own arguments nor disable the
operator's by omitting the file. Because both launch backends exec the same
init script, this covers libkrun and QEMU without touching either.

A microVM has no bind mounts or container secrets, so the credential and CA
bundle are staged into the per-sandbox overlay the way the gateway JWT already
is: credential root-only at 0600, CA at 0644, both rewritten every launch so a
removed setting clears prior material, and both deleted with the sandbox state
directory. This places the credential at rest in the overlay image on the
gateway host, which differs from the Podman secret model and is documented as
an explicit security consideration.

Validation is fail-closed and shared: a new
openshell_core::driver_utils::validate_upstream_proxy_settings holds the
pairing rules the Podman driver established, and both the gateway and the
driver call it so an invalid table names the offending key instead of
surfacing as an opaque driver-readiness timeout.

Guest egress leaves through gvproxy, so a proxy on the gateway host's loopback
is reachable only through host.openshell.internal; the guest to gateway
callback is unaffected.

Closes NVIDIA#3088

Signed-off-by: Philippe Martin <phmartin@redhat.com>
pull Bot pushed a commit to rozsazoltan-forks/OpenShell that referenced this pull request Sep 2, 2026
…dboxes (NVIDIA#3090)

* feat(vm): support corporate HTTP forward proxy egress for microVM sandboxes

The corporate forward proxy machinery from NVIDIA#1792 is driver-agnostic and
already merged: openshell-supervisor-network implements CONNECT chaining,
NO_PROXY matching, credentials, https:// proxies and corporate CA trust, and
openshell-sandbox exposes it as six argv-only flags. Podman gained the driver
half in NVIDIA#2245/NVIDIA#2512 and Kubernetes in NVIDIA#2633; the VM driver had none of it, so
VM sandboxes on proxy-only networks could not reach any destination requiring
the proxy even when policy allowed it.

The blocking piece was not proxy logic but delivery: the VM guest init script
runs as PID 1 and execs a fixed supervisor command line, and libkrun's
krun_set_exec receives an empty argv, so there was no channel for driver-owned
supervisor arguments. The supervisor's proxy flags deliberately have no
environment fallback, and build_guest_environment merges user-supplied
environment, so the guest env is not a safe transport either.

Add a driver-authored argument file, mirroring the existing init.d manifest:
the driver writes /opt/openshell/supervisor-args into the overlay upperdir on
every launch and the guest reads it verbatim, one argument per line, appending
it to every supervisor exec. It is written even when empty, which is what makes
the channel unforgeable -- the upperdir always shadows the read-only image
layer, so an image can neither supply its own arguments nor disable the
operator's by omitting the file. Because both launch backends exec the same
init script, this covers libkrun and QEMU without touching either.

A microVM has no bind mounts or container secrets, so the credential and CA
bundle are staged into the per-sandbox overlay the way the gateway JWT already
is: credential root-only at 0600, CA at 0644, both rewritten every launch so a
removed setting clears prior material, and both deleted with the sandbox state
directory. This places the credential at rest in the overlay image on the
gateway host, which differs from the Podman secret model and is documented as
an explicit security consideration.

Validation is fail-closed and shared: a new
openshell_core::driver_utils::validate_upstream_proxy_settings holds the
pairing rules the Podman driver established, and both the gateway and the
driver call it so an invalid table names the offending key instead of
surfacing as an opaque driver-readiness timeout.

Guest egress leaves through gvproxy, so a proxy on the gateway host's loopback
is reachable only through host.openshell.internal; the guest to gateway
callback is unaffected.

Closes NVIDIA#3088

Signed-off-by: Philippe Martin <phmartin@redhat.com>

* fix(vm): bound the proxy CA read and scope the host-loopback recipe

Two review findings on the corporate forward proxy support for microVM
sandboxes.

The driver read the operator's proxy_ca_bundle with an unbounded fs::read and
accepted it on a substring match for the PEM BEGIN CERTIFICATE marker. A
special file such as /dev/zero therefore grew driver memory without bound on
every authorized sandbox create, and a PEM block holding invalid DER passed
the host check but contributes no trust anchor in the guest, so every
supervisor would fail after boot with an error attributed to the sandbox
rather than to the setting.

Move the read into openshell-core as read_upstream_proxy_ca_bundle_file: it
reuses the credential reader's bounded-read path (non-regular files rejected
on fstat, size capped, read bounded even if the file grows), then requires at
least one anchor that RootCertStore::add_parsable_certificates accepts. The
supervisor's own reader now delegates to it, so host acceptance and guest
acceptance are the same function and cannot drift.

The published host-loopback recipe was written for libkrun only. gvproxy NATs
host.openshell.internal to the gateway host's 127.0.0.1, but GPU sandboxes run
on the QEMU/TAP backend where that name resolves to the TAP host address and
the driver's own nftables input chain accepts only the gateway port from the
guest — no proxy on the gateway host is reachable there at any bind address,
so an operator following the generic recipe lost all proxy-required egress
while configuration validation succeeded.

Scope the recipe to libkrun in every reference and reject a gateway-host proxy
URL when a launch plan resolves to QEMU, naming the reason, instead of booting
a sandbox whose policy-approved CONNECTs all time out.

Signed-off-by: Philippe Martin <phmartin@redhat.com>

* fix(vm): match the QEMU proxy preflight to the selected TAP host

The gateway-host proxy guard added for the QEMU/TAP backend classified the
wrong set of addresses in both directions.

It ran at the top of configure_qemu_launch_plan, before the subnet allocation
that settles plan.host_ip, so it could not compare against the address the
guest actually reaches the host on. An operator pointing https_proxy at the
sandbox's own TAP host address, such as 10.0.128.1, passed the check, and the
driver's nftables input chain — which accepts only the gateway port from the
guest — then dropped every policy-approved CONNECT, which is exactly the
silent timeout the guard exists to prevent.

In the other direction it rejected 192.168.127.254 unconditionally. That
address is special only to libkrun/gvproxy; on QEMU/TAP it is an ordinary
address that may be routable through the guest's masqueraded egress, so the
guard refused a working configuration.

Run the check after the launch plan's network allocation, on both the
freshly-allocated and already-complete paths, and compare IP literals with
that sandbox's selected TAP host. Loopback literals, localhost, and the
documented host aliases that write_host_gateway_aliases seeds to the TAP host
still classify as the gateway host, and the failure names the address. The
gvproxy host-loopback constant returns to being a documentation anchor.

Signed-off-by: Philippe Martin <phmartin@redhat.com>

---------

Signed-off-by: Philippe Martin <phmartin@redhat.com>
purp pushed a commit to purp/OpenShell that referenced this pull request Sep 2, 2026
…dboxes (NVIDIA#3090)

* feat(vm): support corporate HTTP forward proxy egress for microVM sandboxes

The corporate forward proxy machinery from NVIDIA#1792 is driver-agnostic and
already merged: openshell-supervisor-network implements CONNECT chaining,
NO_PROXY matching, credentials, https:// proxies and corporate CA trust, and
openshell-sandbox exposes it as six argv-only flags. Podman gained the driver
half in NVIDIA#2245/NVIDIA#2512 and Kubernetes in NVIDIA#2633; the VM driver had none of it, so
VM sandboxes on proxy-only networks could not reach any destination requiring
the proxy even when policy allowed it.

The blocking piece was not proxy logic but delivery: the VM guest init script
runs as PID 1 and execs a fixed supervisor command line, and libkrun's
krun_set_exec receives an empty argv, so there was no channel for driver-owned
supervisor arguments. The supervisor's proxy flags deliberately have no
environment fallback, and build_guest_environment merges user-supplied
environment, so the guest env is not a safe transport either.

Add a driver-authored argument file, mirroring the existing init.d manifest:
the driver writes /opt/openshell/supervisor-args into the overlay upperdir on
every launch and the guest reads it verbatim, one argument per line, appending
it to every supervisor exec. It is written even when empty, which is what makes
the channel unforgeable -- the upperdir always shadows the read-only image
layer, so an image can neither supply its own arguments nor disable the
operator's by omitting the file. Because both launch backends exec the same
init script, this covers libkrun and QEMU without touching either.

A microVM has no bind mounts or container secrets, so the credential and CA
bundle are staged into the per-sandbox overlay the way the gateway JWT already
is: credential root-only at 0600, CA at 0644, both rewritten every launch so a
removed setting clears prior material, and both deleted with the sandbox state
directory. This places the credential at rest in the overlay image on the
gateway host, which differs from the Podman secret model and is documented as
an explicit security consideration.

Validation is fail-closed and shared: a new
openshell_core::driver_utils::validate_upstream_proxy_settings holds the
pairing rules the Podman driver established, and both the gateway and the
driver call it so an invalid table names the offending key instead of
surfacing as an opaque driver-readiness timeout.

Guest egress leaves through gvproxy, so a proxy on the gateway host's loopback
is reachable only through host.openshell.internal; the guest to gateway
callback is unaffected.

Closes NVIDIA#3088

Signed-off-by: Philippe Martin <phmartin@redhat.com>

* fix(vm): bound the proxy CA read and scope the host-loopback recipe

Two review findings on the corporate forward proxy support for microVM
sandboxes.

The driver read the operator's proxy_ca_bundle with an unbounded fs::read and
accepted it on a substring match for the PEM BEGIN CERTIFICATE marker. A
special file such as /dev/zero therefore grew driver memory without bound on
every authorized sandbox create, and a PEM block holding invalid DER passed
the host check but contributes no trust anchor in the guest, so every
supervisor would fail after boot with an error attributed to the sandbox
rather than to the setting.

Move the read into openshell-core as read_upstream_proxy_ca_bundle_file: it
reuses the credential reader's bounded-read path (non-regular files rejected
on fstat, size capped, read bounded even if the file grows), then requires at
least one anchor that RootCertStore::add_parsable_certificates accepts. The
supervisor's own reader now delegates to it, so host acceptance and guest
acceptance are the same function and cannot drift.

The published host-loopback recipe was written for libkrun only. gvproxy NATs
host.openshell.internal to the gateway host's 127.0.0.1, but GPU sandboxes run
on the QEMU/TAP backend where that name resolves to the TAP host address and
the driver's own nftables input chain accepts only the gateway port from the
guest — no proxy on the gateway host is reachable there at any bind address,
so an operator following the generic recipe lost all proxy-required egress
while configuration validation succeeded.

Scope the recipe to libkrun in every reference and reject a gateway-host proxy
URL when a launch plan resolves to QEMU, naming the reason, instead of booting
a sandbox whose policy-approved CONNECTs all time out.

Signed-off-by: Philippe Martin <phmartin@redhat.com>

* fix(vm): match the QEMU proxy preflight to the selected TAP host

The gateway-host proxy guard added for the QEMU/TAP backend classified the
wrong set of addresses in both directions.

It ran at the top of configure_qemu_launch_plan, before the subnet allocation
that settles plan.host_ip, so it could not compare against the address the
guest actually reaches the host on. An operator pointing https_proxy at the
sandbox's own TAP host address, such as 10.0.128.1, passed the check, and the
driver's nftables input chain — which accepts only the gateway port from the
guest — then dropped every policy-approved CONNECT, which is exactly the
silent timeout the guard exists to prevent.

In the other direction it rejected 192.168.127.254 unconditionally. That
address is special only to libkrun/gvproxy; on QEMU/TAP it is an ordinary
address that may be routable through the guest's masqueraded egress, so the
guard refused a working configuration.

Run the check after the launch plan's network allocation, on both the
freshly-allocated and already-complete paths, and compare IP literals with
that sandbox's selected TAP host. Loopback literals, localhost, and the
documented host aliases that write_host_gateway_aliases seeds to the TAP host
still classify as the gateway host, and the failure names the address. The
gvproxy host-loopback constant returns to being a documentation anchor.

Signed-off-by: Philippe Martin <phmartin@redhat.com>

---------

Signed-off-by: Philippe Martin <phmartin@redhat.com>
craig-kindo added a commit to UsableMachines/OpenShell that referenced this pull request Sep 9, 2026
…roxy

Ported from 8a55af9 and 2364978 onto upstream main. Not cherry-picked:
both sat on config plumbing upstream has since landed itself (NVIDIA#2633), so a
pick conflicts in 13 files while re-adding fields that already exist.

Three new flags. Everything else those commits carried is now upstream's:
- --upstream-proxy-auth-sandbox-identity sends the resolved sandbox id as the
  Proxy-Authorization Basic username with an empty password, so a proxy can
  attribute and ACL egress per sandbox instead of seeing one client
- --upstream-proxy-client-cert / --upstream-proxy-client-key present a client
  certificate on the TLS hop to a proxy that authenticates its callers
- --upstream-proxy-ca from the source commit is NOT added; it duplicated
  upstream's existing --upstream-proxy-ca-bundle, which is used instead

This is what backend/sandbox in the mono repo requires, and both halves are
needed: egress/identity.go parses exactly
`Proxy-Authorization: Basic base64("<sandbox-id>:")` for the caller's identity,
and egress/main.go sets ClientAuth: RequireAndVerifyClientCert, so the
supervisor must also present a certificate. Identity comes from the header,
authentication from the cert.

Fail-closed, each with a test:
- an unresolved sandbox identity refuses to start rather than egressing
  unattributed, which would silently defeat a proxy ACL keyed on the username
- mutually exclusive with the auth file, because one request carries a single
  Proxy-Authorization header, enforced at the driver and the supervisor
- it rejects rather than requires the cleartext-credential acknowledgement,
  whose text would be false when nothing confidential is sent
- half a client identity, or either half against an http:// proxy where it
  would be silently ignored, is fatal at startup
- the client cert requires supervisor topology "sidecar", keeping the private
  key mount out of the workload container

Verified: cargo check --workspace --all-targets clean; 1268 supervisor-network,
253 driver-kubernetes and 461 core tests pass, 0 failed; helm lint clean and
the chart renders.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test:e2e Requires end-to-end coverage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(kubernetes): support corporate HTTP forward proxy egress for sandboxes

3 participants