From 9f1688f2be8878e249f8dd2d35d7e13024532653 Mon Sep 17 00:00:00 2001 From: Victor Bona Date: Mon, 13 Jul 2026 23:31:43 -0300 Subject: [PATCH] Restore interactive SSH PTY allocation --- .github/workflows/ci.yml | 4 ---- .github/workflows/release.yml | 3 --- CHANGELOG.md | 10 +++++++++- README.md | 4 ++-- charts/devboxes/Chart.yaml | 4 ++-- cli/Cargo.lock | 2 +- cli/Cargo.toml | 2 +- controller/pyproject.toml | 2 +- controller/src/devboxes_controller/__init__.py | 2 +- controller/src/devboxes_controller/resources.py | 2 ++ .../devboxes_controller/templates/cli_authorize.html | 4 ++-- controller/src/devboxes_controller/templates/docs.html | 6 +++--- .../src/devboxes_controller/templates/index.html | 6 +++--- .../src/devboxes_controller/templates/login.html | 4 ++-- controller/tests/test_app.py | 2 +- controller/tests/test_resources.py | 1 + controller/uv.lock | 2 +- docs/architecture.md | 2 +- docs/configuration.md | 4 ++-- docs/golden-path.md | 2 +- package-lock.json | 4 ++-- package.json | 2 +- scripts/install.sh | 2 +- 23 files changed, 40 insertions(+), 36 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 74e2fae..9220772 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -159,8 +159,4 @@ jobs: go install sigs.k8s.io/kind@v0.31.0 echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH" - name: Run full lifecycle E2E - env: - # GitHub-hosted runners cannot retain an SSH PTY through Kind's port-forward. - # The workspace image job above exercises the same shell with a real TTY. - DEVBOXES_E2E_INTERACTIVE_SSH: "0" run: scripts/kind-e2e.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6ff231a..8043e4d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -225,9 +225,6 @@ jobs: - name: Run lifecycle through released CLI, chart, and images env: DEVBOXES_VERSION: ${{ github.ref_name }} - # GitHub-hosted runners cannot retain an SSH PTY through Kind's port-forward. - # CI separately validates the workspace shell and tmux with a real TTY. - DEVBOXES_E2E_INTERACTIVE_SSH: "0" run: | DEVBOXES_VERSION="${DEVBOXES_VERSION#v}" scripts/published-e2e.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ee0528..8a6b576 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to Devboxes are documented here. The project follows [Keep a ## [Unreleased] +## [0.2.1] - 2026-07-13 + +### Fixed + +- Restored interactive SSH PTY allocation on capability-enforcing Kubernetes runtimes by granting workspace OpenSSH only `AUDIT_WRITE` in addition to the existing minimal capabilities. +- Re-enabled the real SSH PTY lifecycle in pull-request and published-release cluster gates. + ## [0.2.0] - 2026-07-13 ### Added @@ -60,7 +67,8 @@ All notable changes to Devboxes are documented here. The project follows [Keep a - Portable Helm chart with values schema, namespace-scoped RBAC, configurable storage, ingress, LoadBalancer or NodePort SSH, ServiceMonitor, and disruption budget. - macOS and Linux CLI releases, SHA-256 verification installer, GHCR images, OCI chart publishing, image provenance attestations, and clean Kind install CI. -[Unreleased]: https://github.com/vicotrbb/devboxes/compare/v0.2.0...HEAD +[Unreleased]: https://github.com/vicotrbb/devboxes/compare/v0.2.1...HEAD +[0.2.1]: https://github.com/vicotrbb/devboxes/compare/v0.2.0...v0.2.1 [0.2.0]: https://github.com/vicotrbb/devboxes/compare/v0.1.2...v0.2.0 [0.1.2]: https://github.com/vicotrbb/devboxes/compare/v0.1.1...v0.1.2 [0.1.1]: https://github.com/vicotrbb/devboxes/compare/v0.1.0...v0.1.1 diff --git a/README.md b/README.md index 9fda96d..a9a9c0e 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Devboxes is currently a single-operator system: one shared token controls every - An SSH public key. - An ingress controller and TLS certificate only if you expose the dashboard through ingress. Port-forwarding works without either. -The workspace container intentionally supports passwordless `sudo` for the trusted development user. Its pod drops all capabilities and adds back a small set needed by `sudo`, but it is not compatible with the Kubernetes `restricted` Pod Security profile. Use the `baseline` profile or an equivalent policy in the Devboxes namespace. +The workspace container intentionally supports passwordless `sudo` for the trusted development user. Its pod drops all capabilities and adds back a small set needed by `sudo` and OpenSSH PTY auditing, but it is not compatible with the Kubernetes `restricted` Pod Security profile. Use the `baseline` profile or an equivalent policy in the Devboxes namespace. ## Install @@ -84,7 +84,7 @@ kubectl -n devboxes create secret generic devboxes-workspace \ --from-file=SSH_AUTHORIZED_KEYS="$HOME/.ssh/id_ed25519.pub" helm install devboxes oci://ghcr.io/vicotrbb/charts/devboxes \ - --version 0.2.0 \ + --version 0.2.1 \ --namespace devboxes ``` diff --git a/charts/devboxes/Chart.yaml b/charts/devboxes/Chart.yaml index 93c8268..9e7153e 100644 --- a/charts/devboxes/Chart.yaml +++ b/charts/devboxes/Chart.yaml @@ -2,8 +2,8 @@ apiVersion: v2 name: devboxes description: Self-hosted, ephemeral development environments on Kubernetes type: application -version: 0.2.0 -appVersion: "0.2.0" +version: 0.2.1 +appVersion: "0.2.1" kubeVersion: ">=1.29.0-0" home: https://github.com/vicotrbb/devboxes icon: https://raw.githubusercontent.com/vicotrbb/devboxes/main/docs/assets/devboxes-mark.svg diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 0d83910..5886277 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -313,7 +313,7 @@ dependencies = [ [[package]] name = "devbox-cli" -version = "0.2.0" +version = "0.2.1" dependencies = [ "anyhow", "axum", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index d87fce2..c5bcbbc 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "devbox-cli" -version = "0.2.0" +version = "0.2.1" edition = "2024" rust-version = "1.96" description = "Terminal client for self-hosted Kubernetes development environments" diff --git a/controller/pyproject.toml b/controller/pyproject.toml index b2e0c80..24c50ca 100644 --- a/controller/pyproject.toml +++ b/controller/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "devboxes-controller" -version = "0.2.0" +version = "0.2.1" description = "Controller and dashboard for self-hosted Kubernetes development environments" readme = "README.md" requires-python = ">=3.12" diff --git a/controller/src/devboxes_controller/__init__.py b/controller/src/devboxes_controller/__init__.py index 96058f6..77e4de5 100644 --- a/controller/src/devboxes_controller/__init__.py +++ b/controller/src/devboxes_controller/__init__.py @@ -1,3 +1,3 @@ """Devboxes controller package.""" -__version__ = "0.2.0" +__version__ = "0.2.1" diff --git a/controller/src/devboxes_controller/resources.py b/controller/src/devboxes_controller/resources.py index 318013c..54cbefc 100644 --- a/controller/src/devboxes_controller/resources.py +++ b/controller/src/devboxes_controller/resources.py @@ -141,10 +141,12 @@ def build_deployment( }, "securityContext": { # The trusted dev user needs setuid sudo inside this disposable container. + # OpenSSH also writes a login audit record while allocating a PTY. "allowPrivilegeEscalation": True, "capabilities": { "drop": ["ALL"], "add": [ + "AUDIT_WRITE", "CHOWN", "DAC_OVERRIDE", "FOWNER", diff --git a/controller/src/devboxes_controller/templates/cli_authorize.html b/controller/src/devboxes_controller/templates/cli_authorize.html index 26944bc..92fac9f 100644 --- a/controller/src/devboxes_controller/templates/cli_authorize.html +++ b/controller/src/devboxes_controller/templates/cli_authorize.html @@ -5,8 +5,8 @@ Authorize Devbox CLI · Devboxes - - + +
diff --git a/controller/src/devboxes_controller/templates/docs.html b/controller/src/devboxes_controller/templates/docs.html index ec435a2..80061d0 100644 --- a/controller/src/devboxes_controller/templates/docs.html +++ b/controller/src/devboxes_controller/templates/docs.html @@ -5,9 +5,9 @@ Documentation · Devboxes - - - + + + diff --git a/controller/src/devboxes_controller/templates/index.html b/controller/src/devboxes_controller/templates/index.html index 46e39b9..33012de 100644 --- a/controller/src/devboxes_controller/templates/index.html +++ b/controller/src/devboxes_controller/templates/index.html @@ -5,9 +5,9 @@ Devboxes - - - + + + diff --git a/controller/src/devboxes_controller/templates/login.html b/controller/src/devboxes_controller/templates/login.html index e0cb262..61bc213 100644 --- a/controller/src/devboxes_controller/templates/login.html +++ b/controller/src/devboxes_controller/templates/login.html @@ -5,8 +5,8 @@ Sign in · Devboxes - - + +
diff --git a/controller/tests/test_app.py b/controller/tests/test_app.py index 463e2d9..d5d0b37 100644 --- a/controller/tests/test_app.py +++ b/controller/tests/test_app.py @@ -79,7 +79,7 @@ def test_browser_login_and_dashboard_session() -> None: assert dashboard.headers["x-content-type-options"] == "nosniff" assert "Kubernetes connected" in dashboard.text assert "cluster default storage" in dashboard.text - styles = client.get("/static/styles.css?v=0.2.0") + styles = client.get("/static/styles.css?v=0.2.1") assert "[hidden]" in styles.text assert "display: none !important" in styles.text payload = client.get("/api/v1/devboxes").json() diff --git a/controller/tests/test_resources.py b/controller/tests/test_resources.py index ef8d93c..1811ab8 100644 --- a/controller/tests/test_resources.py +++ b/controller/tests/test_resources.py @@ -39,6 +39,7 @@ def test_deployment_is_hardened_and_ready_for_tmux_workspace() -> None: assert container["resources"]["limits"] == {"memory": "8Gi"} assert "cpu" not in container["resources"]["limits"] assert container["securityContext"]["allowPrivilegeEscalation"] is True + assert "AUDIT_WRITE" in container["securityContext"]["capabilities"]["add"] assert "SYS_CHROOT" in container["securityContext"]["capabilities"]["add"] assert "SYS_ADMIN" not in container["securityContext"]["capabilities"]["add"] secret_volume = next( diff --git a/controller/uv.lock b/controller/uv.lock index 827943e..3fea7f8 100644 --- a/controller/uv.lock +++ b/controller/uv.lock @@ -382,7 +382,7 @@ wheels = [ [[package]] name = "devboxes-controller" -version = "0.2.0" +version = "0.2.1" source = { editable = "." } dependencies = [ { name = "fastapi" }, diff --git a/docs/architecture.md b/docs/architecture.md index 29e2283..9a77364 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -51,7 +51,7 @@ The workspace entrypoint refuses to start without `SSH_AUTHORIZED_KEYS`. It prep - Workspace Secrets are mounted read-only with mode `0440`, scoped to the workspace group, and are not embedded in either image. - The controller runs as a non-root user with a read-only root filesystem and all Linux capabilities dropped. - The workspace runs as root during initialization, then exposes only the unprivileged `dev` SSH user. Password login and root login are disabled. -- The trusted `dev` user has passwordless `sudo`. The pod adds only `CHOWN`, `DAC_OVERRIDE`, `FOWNER`, `SETGID`, `SETUID`, and `SYS_CHROOT`; `SYS_ADMIN` and privileged mode are not used. +- The trusted `dev` user has passwordless `sudo`. The pod adds only `AUDIT_WRITE`, `CHOWN`, `DAC_OVERRIDE`, `FOWNER`, `SETGID`, `SETUID`, and `SYS_CHROOT`; `AUDIT_WRITE` lets OpenSSH allocate audited PTYs, while `SYS_ADMIN` and privileged mode are not used. - SSH host checking uses a stable alias scoped to both the Devboxes installation and box name, preventing collisions across installations. - CLI callbacks accept only exact HTTP loopback URIs with numeric loopback hosts, explicit non-privileged ports, and `/callback`; login return targets accept only the internal authorization route. diff --git a/docs/configuration.md b/docs/configuration.md index fd386af..7d62340 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -3,9 +3,9 @@ Use a values file for durable installations: ```bash -helm show values oci://ghcr.io/vicotrbb/charts/devboxes --version 0.2.0 > values.yaml +helm show values oci://ghcr.io/vicotrbb/charts/devboxes --version 0.2.1 > values.yaml helm upgrade --install devboxes oci://ghcr.io/vicotrbb/charts/devboxes \ - --version 0.2.0 \ + --version 0.2.1 \ --namespace devboxes \ --create-namespace \ --values values.yaml diff --git a/docs/golden-path.md b/docs/golden-path.md index 8cce1d8..9e10ae0 100644 --- a/docs/golden-path.md +++ b/docs/golden-path.md @@ -38,7 +38,7 @@ kubectl create namespace devboxes # Create devboxes-auth and devboxes-workspace here, as described below. helm upgrade --install devboxes oci://ghcr.io/vicotrbb/charts/devboxes \ - --version 0.2.0 \ + --version 0.2.1 \ --namespace devboxes \ --values values.yaml ``` diff --git a/package-lock.json b/package-lock.json index ead9b8d..5ba0532 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "devboxes-repository-tooling", - "version": "0.2.0", + "version": "0.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "devboxes-repository-tooling", - "version": "0.2.0", + "version": "0.2.1", "devDependencies": { "@eslint/js": "10.0.1", "eslint": "10.7.0", diff --git a/package.json b/package.json index e95dc7f..5660b61 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "devboxes-repository-tooling", - "version": "0.2.0", + "version": "0.2.1", "private": true, "type": "module", "description": "JavaScript and documentation quality gates for Devboxes", diff --git a/scripts/install.sh b/scripts/install.sh index 7a7555c..b6267e3 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -3,7 +3,7 @@ set -eu release="${DEVBOXES_RELEASE:-devboxes}" namespace="${DEVBOXES_NAMESPACE:-devboxes}" -version="${DEVBOXES_VERSION:-0.2.0}" +version="${DEVBOXES_VERSION:-0.2.1}" repository="${DEVBOXES_CHART_REPOSITORY:-oci://ghcr.io/vicotrbb/charts/devboxes}" chart_source="${DEVBOXES_CHART_SOURCE:-auto}" controller_secret="${DEVBOXES_CONTROLLER_SECRET:-devboxes-auth}"