openblox exists to contain hostile code, so security reports take priority over all other work.
Do not open a public issue, pull request or discussion.
Report privately through GitHub private vulnerability reporting, or email security@openblox.sh.
Include what you can of: the affected version or commit, the deployment mode
(library or openbloxd), the gVisor version (runsc --version), reproduction
steps, and the impact you believe it has. A failing property in the style of
pkg/conformance is the most useful form a report can take, but is not
required.
In scope: anything that lets code inside a sandbox reach something
THREAT_MODEL.md says it cannot; anything that lets an openbloxd
caller weaken a profile's policy; forging or misrouting preview tokens; and any
claim in the documentation that the code does not uphold. Vulnerabilities in gVisor,
Docker or the Linux kernel themselves belong with those projects, but tell us too
if openblox's defaults make one reachable.
- Acknowledgement within 72 hours.
- An initial assessment — whether we can reproduce it, and how severe we think it is — within 7 days.
- A fix or a documented mitigation as fast as severity warrants. We aim for 90 days at most and will agree a disclosure date with you.
- Credit in the advisory and release notes, unless you prefer otherwise.
Please give us a reasonable chance to ship a fix before disclosing publicly, and do not test against systems you do not own.
- A GitHub Security Advisory,
with a CVE where one applies. This also feeds
govulncheckand Dependabot. - A patch release. Its notes and the
Securitysection of CHANGELOG.md say what was fixed and who is affected. - For a vulnerability in the reference sandbox image, a new image version. Published image versions are never overwritten, so an image pinned by digest does not change underneath you; move the pin to pick up the fix.
openblox is pre-1.0. Only the latest release receives security fixes. Upgrade to it rather than expecting a backport.
| Version | Supported |
|---|---|
latest v0.x release |
yes |
| older releases | no |
openblox assumes the code in a sandbox is actively hostile. It trusts the host, its
kernel, Docker, gVisor, the sandbox image, openbloxd, and whoever configures
them. The full model — assets, trust boundaries, each attack with its defence and
test, and the residual risks — is in THREAT_MODEL.md.
openblox narrows what untrusted code can reach. It does not guarantee that a sandbox cannot be escaped: its isolation is gVisor's, and gVisor has had vulnerabilities.
application ──Unix socket──► openbloxd ──Docker API──► Docker + gVisor ──► sandbox
(no Docker access) (holds the socket;
policy in its config)
- Run
openbloxdon the host and have applications usepkg/brokerclient. Importingpkg/dockerdirectly means your application holds the Docker socket, which is equivalent to root on the host. - Install
openbloxdfrom a release, verified as described in RELEASING.md, and pin a version, notlatest. - Pin the sandbox image by digest in every profile.
- Keep
runsccurrent; its security fixes are yours to apply. - Serve preview URLs from an origin that shares no cookies with your application.
The runtime decides which kernel a guest syscall reaches, and that is the property the rest of this document rests on. It is an ordering, not a switch:
| Runtime | Kernel surface reached by the guest | Verdict |
|---|---|---|
runc (the host default) |
the host kernel, in full | Unsafe for untrusted code. A shared-kernel container is not a boundary against attacker-controlled native code. |
runsc (gVisor) — default |
the Sentry, a user-space kernel; the host kernel only past B1 and B2 | What openblox is built and tested against. |
| a microVM runtime, e.g. Kata | a separate guest kernel | Stronger than the default, at a higher cost per sandbox. Kata on amd64 is measured, with one layer lost: /dev/shm is not noexec,nosuid in its guest, and Kata discards the options openblox could set on it. One lifecycle claim — that Exec on a crashed sandbox fails promptly and Create recovers it — is unmeasured under Kata. See THREAT_MODEL.md. |
openblox does not rank runtimes at create time. Create requires only that the
named runtime is registered with Docker and fails with ErrRuntimeUnavailable
when it is not; it never falls back to the host default. Failing closed is a
property of "is it registered", and is independent of how strong the runtime
is — so choosing a stronger one is supported, and choosing runc is refused by
this document rather than by the code.
Two caveats on going stronger. THREAT_MODEL.md §B1/§B2 is
written specifically against gVisor's Sentry, so under a microVM runtime those
two rows describe a boundary you are no longer relying on and the residual risks
differ; and every merge-gating test behind the claims below runs against
runsc in CI. Kata on amd64 is also measured by openblox — the conformance
suite, in a workflow that does not gate merges, with results in
THREAT_MODEL.md; on anything else you are trusting
the runtime's own evidence, not openblox's. The containment openblox configures
— no network interface, dropped capabilities, read-only root, non-root user,
resource caps — is set identically either way. What openblox leaves to Docker's
defaults is not honoured identically: under Kata, /dev/shm is not noexec or
nosuid, and cannot be made so from openblox — Kata replaces that mount and
discards its options.
| Setting | Safe value | Why it matters |
|---|---|---|
runtime / WithRuntime |
runsc (default), or a microVM runtime |
runc, the host default, runs untrusted code on the host kernel. See The isolation runtime. |
egress / WithEgress |
none (default) |
unrestricted gives the sandbox the host's network, including the LAN and cloud metadata. |
user / WithUser |
numeric, non-zero uid:gid (default 1000:1000) |
Root, group 0, user names and a bare uid are refused: names and a bare uid resolve inside the untrusted image. |
image / WithImage |
name@sha256:… |
A tag can be repointed by whoever controls the registry. |
socket_group |
a group holding only trusted callers | Every member controls every sandbox. |
max_sandboxes, memory_mb, cpus |
sized to the host | Per-sandbox caps do not bound the total. |
idle_timeout, max_age |
positive (defaults apply when omitted) | Negative values are refused by openbloxd; in the library they disable the bound. |
Preview key (WithPreviews) |
≥ 32 random bytes, kept secret | Anyone with the key can mint a preview token for any sandbox port. |
- Do not mount the Docker socket — or any host path — into a sandbox.
- Do not run
openbloxdin a container with the Docker socket mounted; it defeats the reason the daemon exists. - Do not mount the Docker socket into an application container to use the library;
use
openbloxd. - Do not set
egress: unrestrictedfor untrusted code without an external firewall. - Do not run untrusted code under
runc, the host default, or any other shared-kernel runtime. Going the other way — a microVM runtime such as Kata — is a stronger boundary and is supported. - Do not put secrets into a sandbox's environment, files or command arguments.
- Do not trust sandbox output: escape it before rendering, bound it before parsing, and treat it as potential prompt injection before handing it to a model.
- Do not rely on
Revokeacross replicas; rely on short preview TTLs. - Do not treat openblox as a tenant boundary between callers of the same
openbloxd(see THREAT_MODEL.md §7).