Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@ Nothing has been released yet. Everything below is on `main` and unversioned.
first start and printed once. The default bind address is loopback.
- **Per-sandbox network mode:** A sandbox is created with network mode `full`
(the default, with normal outbound access) or `none`, which denies it a route
to the internet for its whole lifetime, including every fork or restore made
from it. The mode is fixed at creation and cannot be changed afterward.
to the internet. The mode is fixed at creation and cannot be changed
afterward. Restoring a sandbox always keeps its own mode. Forking a sandbox
from a snapshot inherits the snapshot's mode by default, but an explicit
`network` on the fork request overrides it.
- **Optional gVisor runtime:** An operator can configure `orcald` to run every
sandbox under gVisor instead of the default container runtime, trading some
compatibility and performance for a smaller kernel attack surface. The choice
Expand Down
21 changes: 18 additions & 3 deletions docs/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,13 @@ on scopes to partition a shared one.
## Network modes

A sandbox is created with a network mode that cannot be changed afterward — there is no
endpoint to move a running sandbox between modes, and forking or restoring a sandbox preserves
the network mode it had.
endpoint to move a running sandbox between modes. Restoring a sandbox always preserves that
sandbox's own mode: restore never changes it. Forking a sandbox from a snapshot is different —
the new sandbox inherits the snapshot's mode by default, but a `network` field on the fork
request overrides that default explicitly. A caller with `sandboxes:write` can therefore fork a
`none` snapshot with `"network": "full"` and get an internet-connected sandbox holding the
`none` sandbox's filesystem; that is deliberate, not a gap, but it means "created with `none`"
does not imply "every descendant stays `none`" unless the caller forking it says so.

`full`, the default, attaches the sandbox to a bridge network with a normal route to the
internet. `none` attaches the sandbox to a second, Docker-internal bridge network that carries
Expand Down Expand Up @@ -119,7 +124,7 @@ construction, not by redaction. Events can be listed and filtered through `GET /
gated behind the `audit:read` scope, and are pruned on both an age and a count basis according
to `ORCAL_AUDIT_RETENTION_DAYS` and `ORCAL_AUDIT_MAX_EVENTS`.

Two gaps are worth naming explicitly:
Four gaps are worth naming explicitly:

- **`stat` and `list` are not audited.** Every other file operation is, but checking whether a
path exists or listing a directory's contents leaves no trace in the audit log. An attacker
Expand All @@ -130,6 +135,16 @@ Two gaps are worth naming explicitly:
deliberate trade-off — an audit store outage should not become an availability outage for the
product — but it means the audit log's completeness is not guaranteed under a failing audit
store, only best-effort.
- **A panicking handler leaves no audit event at all.** Recovery from a panic happens outside
the audit middleware, so a request that crashes its handler unwinds past the code that would
have written the event. This is distinct from the fail-open case above, which covers a
failed *insert*; here nothing is ever attempted.
- **An unauthenticated caller can crowd out real history.** Every rejected request is audited,
including ones from a caller with no valid token at all, and there is no rate limiting on the
API. Since pruning by count evicts the oldest events first, anyone who can reach `ORCAL_ADDR`
can flood it with denied requests and push genuine history out of the retention window. In
practice this is mitigated by the default loopback bind: it only matters once an operator
exposes `orcald` beyond `127.0.0.1`.

## Container hardening

Expand Down
42 changes: 25 additions & 17 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,48 +5,56 @@ go 1.25.0
require (
github.com/containerd/errdefs v1.0.0
github.com/docker/docker v28.5.2+incompatible
github.com/getkin/kin-openapi v0.135.0
github.com/getkin/kin-openapi v0.146.0
github.com/google/uuid v1.6.0
github.com/opencontainers/image-spec v1.1.1
github.com/spf13/cobra v1.9.1
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.39.0
)

require (
github.com/Microsoft/go-winio v0.4.14 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/logr v1.4.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-openapi/jsonpointer v0.22.5 // indirect
github.com/go-openapi/swag/jsonname v0.25.5 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/moby/sys/atomicwriter v0.1.0 // indirect
github.com/moby/term v0.5.2 // indirect
github.com/morikuni/aec v1.1.0 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/oasdiff/yaml v0.0.9 // indirect
github.com/oasdiff/yaml3 v0.0.9 // indirect
github.com/oasdiff/yaml v0.1.1 // indirect
github.com/oasdiff/yaml3 v0.0.14 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/woodsbury/decimal128 v1.3.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.opentelemetry.io/otel v1.45.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.45.0 // indirect
go.opentelemetry.io/otel/metric v1.45.0 // indirect
go.opentelemetry.io/otel/sdk v1.45.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.45.0 // indirect
go.opentelemetry.io/otel/trace v1.45.0 // indirect
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.40.0 // indirect
golang.org/x/time v0.15.0 // indirect
gotest.tools/v3 v3.5.2 // indirect
modernc.org/libc v1.66.3 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
Expand Down
Loading
Loading