diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index b5126404d..54effb8cf 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -4,7 +4,7 @@ on: workflow_call: inputs: component: - description: "Component to build (gateway, sandbox, cluster)" + description: "Component to build (gateway, cluster)" required: true type: string timeout-minutes: diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 6adad27b8..205ac34be 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -15,12 +15,6 @@ jobs: with: component: gateway - build-sandbox: - if: contains(github.event.pull_request.labels.*.name, 'e2e') - uses: ./.github/workflows/docker-build.yml - with: - component: sandbox - build-cluster: if: contains(github.event.pull_request.labels.*.name, 'e2e') uses: ./.github/workflows/docker-build.yml @@ -28,7 +22,7 @@ jobs: component: cluster e2e: - needs: [build-gateway, build-sandbox, build-cluster] + needs: [build-gateway, build-cluster] uses: ./.github/workflows/e2e-test.yml with: image-tag: ${{ github.sha }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 02735d6ba..57c52b165 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -19,11 +19,6 @@ jobs: with: component: gateway - build-sandbox: - uses: ./.github/workflows/docker-build.yml - with: - component: sandbox - build-cluster: uses: ./.github/workflows/docker-build.yml with: @@ -31,7 +26,7 @@ jobs: tag-ghcr-latest: name: Tag GHCR Images as Latest - needs: [build-gateway, build-sandbox, build-cluster] + needs: [build-gateway, build-cluster] runs-on: build-amd64 timeout-minutes: 10 steps: @@ -42,7 +37,7 @@ jobs: run: | set -euo pipefail REGISTRY="ghcr.io/nvidia/openshell" - for component in gateway sandbox cluster; do + for component in gateway cluster; do echo "Tagging ${REGISTRY}/${component}:${{ github.sha }} as latest..." docker buildx imagetools create \ --prefer-index=false \ @@ -52,7 +47,7 @@ jobs: build-python-wheels: name: Stage Python Wheels - needs: [build-gateway, build-sandbox, build-cluster] + needs: [build-gateway, build-cluster] runs-on: build-amd64 timeout-minutes: 120 outputs: @@ -137,7 +132,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: "3.12" + python-version: "3.13" - name: Install publish dependencies run: | diff --git a/README.md b/README.md index 2ae508599..6d47477c0 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ The sandbox container includes the following tools by default: | Category | Tools | | ---------- | -------------------------------------------------------- | | Agent | `claude`, `opencode`, `codex` | -| Language | `python` (3.12), `node` (22) | +| Language | `python` (3.13), `node` (22) | | Developer | `gh`, `git`, `vim`, `nano` | | Networking | `ping`, `dig`, `nslookup`, `nc`, `traceroute`, `netstat` | diff --git a/architecture/security-policy.md b/architecture/security-policy.md index fda0adc96..d127f8d75 100644 --- a/architecture/security-policy.md +++ b/architecture/security-policy.md @@ -606,7 +606,7 @@ network_policies: allowed_ips: - "10.86.8.223/32" binaries: - - { path: /usr/local/bin/python3.12 } + - { path: /usr/local/bin/python3.13 } ``` With this policy, both work: @@ -1090,7 +1090,7 @@ network_policies: allowed_ips: - "10.86.8.223/32" binaries: - - { path: /usr/local/bin/python3.12 } + - { path: /usr/local/bin/python3.13 } inference: allowed_routes: diff --git a/crates/navigator-sandbox/src/l7/tls.rs b/crates/navigator-sandbox/src/l7/tls.rs index f28dfd57c..eeab877c3 100644 --- a/crates/navigator-sandbox/src/l7/tls.rs +++ b/crates/navigator-sandbox/src/l7/tls.rs @@ -117,6 +117,7 @@ impl CertCache { let mut params = CertificateParams::new(vec![hostname.to_string()]).into_diagnostic()?; params.distinguished_name.push(DnType::CommonName, hostname); + params.use_authority_key_identifier_extension = true; let leaf_cert = params .signed_by(&leaf_key, &self.ca.ca_cert, &self.ca.ca_key) diff --git a/docs/tutorials/github-sandbox.md b/docs/tutorials/github-sandbox.md index bb134a812..348a25fd9 100644 --- a/docs/tutorials/github-sandbox.md +++ b/docs/tutorials/github-sandbox.md @@ -312,9 +312,7 @@ network_policies: - { path: /sandbox/.venv/bin/python } - { path: /sandbox/.venv/bin/python3 } - { path: /sandbox/.venv/bin/pip } - - { path: /app/.venv/bin/python } - - { path: /app/.venv/bin/python3 } - - { path: /app/.venv/bin/pip } + - { path: "/sandbox/.uv/python/**/python*" } - { path: /usr/local/bin/uv } - { path: "/sandbox/.uv/python/**" } diff --git a/e2e/python/test_sandbox_policy.py b/e2e/python/test_sandbox_policy.py index d3e7b1250..7fbb76e5b 100644 --- a/e2e/python/test_sandbox_policy.py +++ b/e2e/python/test_sandbox_policy.py @@ -51,7 +51,9 @@ def _policy_for_python_proxy_tests() -> sandbox_pb2.SandboxPolicy: endpoints=[ sandbox_pb2.NetworkEndpoint(host="api.openai.com", port=443) ], - binaries=[sandbox_pb2.NetworkBinary(path="/app/.venv/bin/python")], + binaries=[ + sandbox_pb2.NetworkBinary(path="/sandbox/.uv/python/**/python*") + ], ) }, ) @@ -373,7 +375,7 @@ def test_l4_binary_restricted_denies_wrong_binary( """L4-3: Policy restricted to specific binary denies others. Policy allows /usr/bin/curl -> api.anthropic.com:443. - Python (exec_python uses /app/.venv/bin/python) should be denied. + Python (exec_python uses python) should be denied. """ policy = _base_policy( network_policies={ @@ -438,7 +440,9 @@ def test_l4_cross_policy_denied( endpoints=[ sandbox_pb2.NetworkEndpoint(host="api.anthropic.com", port=443), ], - binaries=[sandbox_pb2.NetworkBinary(path="/app/.venv/bin/python")], + binaries=[ + sandbox_pb2.NetworkBinary(path="/sandbox/.uv/python/**/python*") + ], ), "other": sandbox_pb2.NetworkPolicyRule( name="other", diff --git a/e2e/python/test_sandbox_venv.py b/e2e/python/test_sandbox_venv.py index a26087e21..8f396864e 100644 --- a/e2e/python/test_sandbox_venv.py +++ b/e2e/python/test_sandbox_venv.py @@ -37,12 +37,6 @@ def test_sandbox_venv_in_path( assert "/sandbox/.venv/bin" in path_dirs, ( f"Expected /sandbox/.venv/bin in PATH, got: {result.stdout.strip()}" ) - # /sandbox/.venv/bin must come before /app/.venv/bin - sandbox_idx = path_dirs.index("/sandbox/.venv/bin") - app_idx = path_dirs.index("/app/.venv/bin") - assert sandbox_idx < app_idx, ( - "/sandbox/.venv/bin must precede /app/.venv/bin in PATH" - ) def test_pip_install_in_sandbox( diff --git a/e2e/rust/tests/cli_smoke.rs b/e2e/rust/tests/cli_smoke.rs index e54d49460..876c93ce2 100644 --- a/e2e/rust/tests/cli_smoke.rs +++ b/e2e/rust/tests/cli_smoke.rs @@ -84,8 +84,9 @@ async fn sandbox_help_shows_upload_download() { } /// `openshell sandbox create --help` must show `--upload`, `--no-git-ignore`, -/// `--bootstrap`/`--no-bootstrap`, `--editor`, and +/// `--no-bootstrap`, `--editor`, and /// `--auto-providers`/`--no-auto-providers`. +/// Note: `--bootstrap` is intentionally hidden (it's the default behaviour). #[tokio::test] async fn sandbox_create_help_shows_new_flags() { let (output, code) = run_isolated(&["sandbox", "create", "--help"]).await; @@ -95,7 +96,6 @@ async fn sandbox_create_help_shows_new_flags() { for flag in [ "--upload", "--no-git-ignore", - "--bootstrap", "--no-bootstrap", "--editor", "--auto-providers", diff --git a/e2e/rust/tests/custom_image.rs b/e2e/rust/tests/custom_image.rs index 404187dcb..30a61f85d 100644 --- a/e2e/rust/tests/custom_image.rs +++ b/e2e/rust/tests/custom_image.rs @@ -15,7 +15,7 @@ use std::io::Write; use openshell_e2e::harness::output::strip_ansi; use openshell_e2e::harness::sandbox::SandboxGuard; -const DOCKERFILE_CONTENT: &str = r#"FROM python:3.12-slim +const DOCKERFILE_CONTENT: &str = r#"FROM python:3.13-slim # iproute2 is required for sandbox network namespace isolation. RUN apt-get update && apt-get install -y --no-install-recommends iproute2 \ diff --git a/examples/bring-your-own-container/Dockerfile b/examples/bring-your-own-container/Dockerfile index d385ab3df..3b8595450 100644 --- a/examples/bring-your-own-container/Dockerfile +++ b/examples/bring-your-own-container/Dockerfile @@ -5,7 +5,7 @@ # The image exposes port 8080 with a /hello endpoint. Use port forwarding # (--forward 8080 on the CLI) to reach it from your local machine. -FROM python:3.12-slim +FROM python:3.13-slim # System tools useful for sandbox networking and debugging. RUN apt-get update && apt-get install -y --no-install-recommends \ diff --git a/examples/local-inference/sandbox-policy.yaml b/examples/local-inference/sandbox-policy.yaml index 583c736f0..79fde8ea2 100644 --- a/examples/local-inference/sandbox-policy.yaml +++ b/examples/local-inference/sandbox-policy.yaml @@ -33,7 +33,7 @@ network_policies: - { host: pypi.org, port: 443 } - { host: files.pythonhosted.org, port: 443 } binaries: - - path: /usr/bin/python3.12 + - path: /usr/bin/python3.13 # Direct access to the NVIDIA inference API (L7 TLS intercept). # Used to verify that the L7 REST relay path streams responses correctly @@ -48,4 +48,4 @@ network_policies: enforcement: enforce access: full binaries: - - path: /usr/bin/python3.12 + - path: /usr/bin/python3.13 diff --git a/examples/private-ip-routing/Dockerfile b/examples/private-ip-routing/Dockerfile index f2fd8294f..614b3b473 100644 --- a/examples/private-ip-routing/Dockerfile +++ b/examples/private-ip-routing/Dockerfile @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -FROM python:3.12-slim +FROM python:3.13-slim COPY server.py /app/server.py EXPOSE 8080 CMD ["python", "/app/server.py"] diff --git a/mise.toml b/mise.toml index d2509088b..5b6ce8353 100644 --- a/mise.toml +++ b/mise.toml @@ -12,7 +12,7 @@ redactions = ["*_TOKEN", "*_PASSWORD"] experimental = true [tools] -python = "3.12.12" +python = "3.13.12" rust = "stable" kubectl = "1.35.1" uv = "0.10.2" diff --git a/python/openshell/sandbox.py b/python/openshell/sandbox.py index 762f84d5c..81d0c409d 100644 --- a/python/openshell/sandbox.py +++ b/python/openshell/sandbox.py @@ -556,7 +556,7 @@ def exec_python( "print(result) if result is not None else None" ) -_SANDBOX_PYTHON_BIN = "/app/.venv/bin/python" +_SANDBOX_PYTHON_BIN = "python" def _serialize_python_callable( diff --git a/uv.lock b/uv.lock index 2a44c422b..687a035ae 100644 --- a/uv.lock +++ b/uv.lock @@ -512,6 +512,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5f/df/76d0321c3797b54b60fef9ec3bd6f4cfd124b9e422182156a1dd418722cf/myst_parser-4.0.1-py3-none-any.whl", hash = "sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d", size = 84579, upload-time = "2025-02-12T10:53:02.078Z" }, ] +[[package]] +name = "nvidia-sphinx-theme" +version = "0.0.9.post1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydata-sphinx-theme" }, + { name = "sphinx" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/8c/79/017fab2f7167a9a9795665f894d04f77aafceca80821b51589bb4b23ff5c/nvidia_sphinx_theme-0.0.9.post1-py3-none-any.whl", hash = "sha256:21ca60206dff2f380d7783d64bbaf71a5b9cacae53c7d0686f089c16b5a3d45a", size = 143816, upload-time = "2025-11-09T23:16:55.719Z" }, +] + [[package]] name = "openshell" source = { virtual = "." } @@ -572,18 +584,6 @@ docs = [ { name = "sphinxcontrib-mermaid" }, ] -[[package]] -name = "nvidia-sphinx-theme" -version = "0.0.9.post1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pydata-sphinx-theme" }, - { name = "sphinx" }, -] -wheels = [ - { url = "https://files.pythonhosted.org/packages/8c/79/017fab2f7167a9a9795665f894d04f77aafceca80821b51589bb4b23ff5c/nvidia_sphinx_theme-0.0.9.post1-py3-none-any.whl", hash = "sha256:21ca60206dff2f380d7783d64bbaf71a5b9cacae53c7d0686f089c16b5a3d45a", size = 143816, upload-time = "2025-11-09T23:16:55.719Z" }, -] - [[package]] name = "packaging" version = "26.0"