This release includes 3 security fixes for security teams reviewing exposed deployments.
Topics
Affected surfaces
Summary
AI summaryUpdates π¦ Misc, π Bug Fixes, and β¨ Features across a mixed release.
Full changelog
What's New
β οΈ Breaking Changes
-
Credential Vault requires
dns+nftenforcement β DNS-only egress can no longer carry credential-bound traffic. Migrate to[egress] mode = "dns+nft";defaultAction: allowstill works but is deprecated and warns. #1136 -
X-Forwarded-Protono longer trusted by default β the server proxy strips client-supplied forwarded headers and rebuilds them from the connection. Deployments that terminate TLS in front of the egress sidecar must list their proxy addresses in the newOPENSANDBOX_EGRESS_CREDENTIAL_VAULT_TRUSTED_PROXY_CIDRS. #1138 -
OPENSANDBOX_EGRESS_MITMPROXY_SCRIPTremoved from the env allowlist β the variable let a sandbox point the egress sidecar'smitmdump -sat any file under the shared/opt/opensandboxemptyDir, enabling code execution in the sidecar and Credential Vault plaintext access. Requests carrying it are now rejected. Ship custom addons in a purpose-built egress image viaENV. #1194
π Security
-
Secure-access token now enforced on the server proxy β the lifecycle proxy at
/sandboxes/{id}/proxy/{port}/...previously skipped API-key checks and never validated theOpenSandbox-Secure-Accessheader (CVSSv3.1 ~7.5). Both HTTP and WebSocket paths now require a matching header (constant-time compare) and strip the token before forwarding. #1191 -
Sandbox pods no longer mount a service-account token β the
kubernetes.service_accountconfig is removed andautomountServiceAccountToken: falseis set on both providers. Nothing in the sandbox consumed the token. Operators depending on the SA'simagePullSecretsshould moveserviceAccountNameinto their agent-sandbox template. #1227 -
21 CVE-remediating dependency bumps β
pydantic-settings,python-multipart,starlette,cryptography,pyjwt,undici,js-yaml. Direct deps pinned inpyproject.toml, transitives via[tool.uv] constraint-dependenciesandpnpm.overrides. #1145
β¨ Features
-
Multi-tenancy provider with file and HTTP backends β sandboxes and snapshots can be isolated per tenant via K8s namespaces. Ships a file provider (TOML with mtime hot-reload) and an HTTP provider (per-key TTL cache + singleflight). Tenant context is propagated per request through auth middleware; unavailable providers return 503. Closes #972 #497. #1184
-
sandbox.createlatency reported via metrics events β newPOST /v1/metrics/eventsaccepts fire-and-forgetMetricsEventpayloads and optionally records them as an OTEL histogram (opensandbox.sandbox.create.duration). Python/Go/JS SDKs already hook this on every create. Configure via[otel]. #1307 -
Sandbox extensions returned in lifecycle responses β
CreateSandboxResponse,getSandbox, andlistSandboxesnow surfaceopensandbox.extensions.*back to callers, matching the values set at create time. K8s reads from pod annotations; Docker persists them as container labels. Closes #1060. #1112 #1167 -
Snapshot list supports exact-name filtering β
GET /snapshotsaccepts an optionalnamequery parameter, honored through the API, service, and SQLite repository while preserving pagination. Exposed in the Python, JavaScript, Go, C#, and Kotlin SDKs. Closes #1205. #1301 -
Configurable Docker port range for bridge-mode sandboxes β new
[docker] port_range_min/port_range_maxfields (default40000-60000) narrow host-port allocation so multiple bridge-mode sandboxes can expose distinct execd ports (host mode always binds 44772). #1182 -
OPENSANDBOX_EGRESS_POLICY_FILEandOPENSANDBOX_EGRESS_SANDBOX_IDnow allowlisted β sandbox creates can opt into egress policy-file persistence and per-sandbox OTLP attribution (OSEP-0010) via the standardenvfield. #1190 #1335 -
JDK trust store hardened for MITM CA β
bootstrap.shnow discovers every JDK on the system and imports the egress MITM CA into eachcacertsviakeytool. Fixes PKIX failures on tarball / Alpine / distroless JDKs when using the credential proxy. Idempotent and best-effort. Closes #1201. #1229
π Bug Fixes
-
Docker
listSandboxesno longer flakes on concurrent deletion β docker-py's high-levelcontainers.list()does a follow-up inspect per match, so a sibling cleanup mid-listing failed the whole call with 500. The service now uses the low-leveldocker_client.api.containers(...)summary endpoint and mapsNotFoundfrom single-container lookups to 404. List view dropsentrypoint, exit code, andFinishedAtfidelity (callers needing those must usegetSandbox). #1342 -
K8s informer honors
informer_resync_secondsβ periodic full resync was silently disabled while watch mode was active. Watch streams are now capped by the remaining resync interval. Fixes #1322. #1323 -
networkPolicyrejected in pool mode β combiningextensions.poolRefwith anetworkPolicyis unsupported; requests are now rejected at schema validation with a defense-in-depth guard inBatchSandboxProvider. Closes #1315. #1319 -
Diagnostics scoped to the tenant namespace β K8s pod/log/event lookups use the request-scoped tenant namespace instead of the server default. Non-tenant requests unchanged. Closes #1304. #1305
-
use_server_proxypreserves the API mount prefix β the API is mounted both bare and under/v1, but proxied endpoints were derived fromrequest.base_urland dropped the prefix, breaking ingress deployments that path-split/v1/*. The rewritten endpoint now keeps the prefix;server.eipis still respected verbatim. #1297 -
Renewed Docker sandbox expirations survive restart β timers were rebuilt from immutable container labels, so a renewed sandbox reverted to its original TTL. Renewals now write an override into the file-backed metadata store; old callbacks that observe a renewal rearm cleanly. Closes #1200. #1267
-
PVC
readOnlypropagated to the volume source β the flag was only applied to the mount, not topersistentVolumeClaim.readOnlyon the pod spec. Both are now aligned. Closes #545. #1246 -
Fail fast for missing
poolRefpools βextensions.poolRefis validated against the Pool CRD before creating aBatchSandbox, returningKUBERNETES::POOL_NOT_FOUNDinstead of failing downstream. Fixes #1175. #1176 -
Docker egress no longer crashes on hosts without IPv6 procfs entries β
egress.disable_ipv6=trueunconditionally injectednet.ipv6.conf.*.disable_ipv6sysctls. The injection is now skipped when the required procfs knobs are missing. Fixes #1168. #1172 -
Event loop no longer blocked during Docker image pull β provisioning runs on a daemon thread with an
asyncio.Futureso health checks stay responsive. Newtimeout_graceful_shutdownconfig (default 5s) makes SIGINT actually exit. #1171 -
K8s diagnostics logs endpoint works on multi-container pods β
read_namespaced_pod_logwas called without acontainerargument, so every standard sandbox (withegresssidecar +execd-installerinit) returned 500. The endpoint now defaults to thesandboxcontainer, accepts?container=for sidecar/init logs, and maps K8s errors to structured 400/403/404/502. Fixes #1150. #1151
π¦ Misc
-
renew_intentmarked stable β OSEP-0009 is out of experimental; drops[EXPERIMENTAL]/ π§ͺ markers from config docstrings, example TOML, startup logs, andconfiguration.md. No behavior change. #1350 -
Removed unused
PendingSandboxmachinery and fixed the spec β Docker and K8s both provision synchronously; the async worker, related helpers, and their tests were dead code (β357 lines). ThePOST /sandboxes202 response description inspecs/sandbox-lifecycle.ymlalso described aPending β Runningpolling flow that was never implemented β now corrected. #1178 -
GHCR image publishing β component images (server, k8s, execd, egress, ingress) are now published to GitHub Container Registry alongside Docker Hub and Aliyun. Refs #1160. #1161
-
Package metadata and docs point at
opensandbox-group/OpenSandboxβ user-facing links across README, docs, Helm chart, examples, andserver/clipyproject.tomlupdated. No package rename. #1197 #1140 -
Auto-bumps β
execdv1.0.20 β v1.0.21 (#1144 #1279);egressv1.1.3 β v1.1.4 (#1142 #1278).
π₯ Contributors
Thanks to these contributors β€οΈ
- @1363653611
- @bcho
- @FAUST-BENCHOU
- @ferponse
- @GreenShadeZhang
- @Gujiassh
- @hellomypastor
- @jianpingpei
- @jwx0925
- @Pangjiping
- @ruirui6946
- @shenshouer
- @tea-artist
- PyPI: opensandbox-server==0.2.2
- Docker Hub: opensandbox/server:v0.2.2
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/server:v0.2.2
- GHCR: ghcr.io/opensandbox-group/server:v0.2.2
Breaking Changes
- Credential Vault requires `dns+nft` enforcement; DNSβonly egress is no longer allowed (migrate to `[egress] mode = "dns+nft"`).
- `X-Forwarded-Proto` header is stripped by default and must be rebuilt; deployments terminating TLS in front of the sidecar need to add proxy CIDRs to `OPENSANDBOX_EGRESS_CREDENTIAL_VAULT_TRUSTED_PROXY_CIDRS`.
- Removed env var `OPENSANDBOX_EGRESS_MITMPROXY_SCRIPT` from allowlist; requests carrying it are now rejected.
Security Fixes
- Secureβaccess token enforcement added to server proxy on HTTP and WebSocket paths (CVE not listed, CVSSv3.1 ~7.5).
- Sandbox pods no longer mount a Kubernetes serviceβaccount token; operators must move `serviceAccountName` into their agent template.
- 21 dependency bumps remediating CVEs: pydantic-settings, python-multipart, starlette, cryptography, pyjwt, undici, js-yaml.
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
Related context
Earlier breaking changes
- vpython/sandbox/v0.1.14 Removes `CredentialMatch.ports`; port now derived from scheme.
- vjava/sandbox/v1.0.16 Removes CredentialMatch.ports; port derived from scheme instead.
- vdocker/egress/v1.1.4 `Match.Ports` field removed from Credential Vault bindings; ports derived from scheme.
- vdocker/egress/v1.1.4 'X-Forwarded-Proto' is trusted only from configured proxy CIDRs.
- vdocker/egress/v1.1.4 Credential Vault requires `dns+nft` enforcement for egress connections.
Beta — feedback welcome: [email protected]