OutRig is pre-1.0 and ships from a single trunk line. Security fixes land on the latest
0.2.x release; there are no separate maintenance branches for older patch releases yet.
| Version | Supported |
|---|---|
| 0.2.x | yes |
| < 0.2 | no |
Please report security issues privately -- do not open a public GitHub issue for a suspected vulnerability.
- Preferred: GitHub private vulnerability reporting. On the repository, go to Security -> Advisories -> Report a vulnerability. This opens a private channel with the maintainer.
- Alternatively, email travis@gockelhut.com with details and, ideally, a reproduction.
This is a small, single-maintainer project. Expect an initial acknowledgement within about a
week. Once a fix is ready it ships in the next 0.2.x release, with credit in the CHANGELOG
unless you ask to remain anonymous.
OutRig's security property is the container boundary. MCP servers and the tools the agent
runs execute inside a podman-managed container; the host stays outside that boundary except
for the workspace mount(s) and the runtime services OutRig explicitly connects. See
doc/concepts/mcp-trust-model.md and
doc/concepts/containers.md for the full model.
Issues that bear on host integrity are in scope, for example:
- A path by which a container escapes the rootless-podman boundary to reach host files or processes outside the configured workspace mount(s).
- OutRig leaking host secrets, credentials, or environment into the container when not
configured to, including through a podman default OutRig leaves in place, such as
--http-proxy. - The network interceptor failing to enforce a configured host:port allow/deny policy.
- The network interceptor attaching to a container that holds
CAP_NET_ADMIN--cap-addofNET_ADMIN, or ofALLwithoutNET_ADMINincap-drop-- since whatever it runs as root could delete the nftables table that is the interception. Attaching refuses such a container. - The network interceptor dialing, from the host's network namespace, an address that means something only inside the container's -- its loopback, or the interceptor's own listener -- so that a container reaches a host service the policy never named.
- A name the container merely asserts -- a TLS
ClientHelloSNI or an HTTPHost:header -- satisfying a hostnameallowentry. A hostname rule grants only against a destination the interceptor itself resolved to that name. - A repo config's
[network]block reaching the egress policy. A repo may choose[network].mode;default,allow, anddenybelong to the operator's global config, and a repo value carrying them must not widen or replace what the global config set. - A repo config binding a host secret: a
[providers.<name>]entry that carries anapi-keybelongs to the operator's global config, and a repo config -- committed, or edited by the agent through the read-write workspace -- that declares one must be refused rather than handed the operator's key to send to abase-urlof its choosing. - A repo config reading a host variable it was not allowed: a
${VAR}inbuild-argsor in an MCP server'senvhands the variable's value to code the repo controls, so a repo config file -- committed, or edited by the agent through the read-write workspace -- may name only the variables the global config lists in[repo] env-allow, and one that names another must be refused rather than resolved. So must a server an image'sorg.outrig.mcplabel declares: the label is the image author's text -- a Dockerfile in the repo, or an image the repo names -- and not a config file, but a reference in itsenvis read from the host and handed to that server all the same. - Capability handling that is more permissive than the selected capability profile
(
default/no-net-raw/drop-all). - A container launched more permissively than its
[images.<name>.security]block asks for ----security-opt=no-new-privilegesmissing whenno-new-privilegeswas not set tofalse, or a device reaching a container that did not list it indevices.
- The agent has arbitrary code execution inside the container. That is the point -- there are no command allowlists within a normal OutRig container. Anything reachable through the configured workspace mounts and network policy is reachable by the agent.
- OutRig relies on rootless podman for isolation. Beyond
--userns=keep-id,--security-opt=no-new-privileges, and the selected capability profile, it does not add seccomp, AppArmor, or SELinux policy, a read-only root filesystem, or network egress policy in the container launch path. - An image-config may opt out of
--security-opt=no-new-privilegesand may pass host device nodes through withdevices. Both default to off, and both weaken the boundary when set: a container withoutno_new_privscan use a setuid-root binary in its own image to reach namespace-local root, and a passed-through device is real hardware access. Configuring either is a deliberate choice by whoever owns the config, not a vulnerability. - A sidecar with
view = "primary"is inside the primary's trust boundary, not beside it. It joins the primary's mount namespace withCAP_SYS_ADMIN/CAP_SYS_PTRACE(scoped to the rootless user namespace, not host root) and can read the primary's entire filesystem -- so its image is as trusted as the primary image. It is opt-in, defaults to"none", and remains a container (cgroups, seccomp, network policy, andno-new-privilegesstill apply; only the mount namespace is joined). See MCP Trust Model. - A proxy handed to an MCP server through its
envis the agent's to read, credentials included, and a filter policy that allows it allows whatever it will reach. See Containers. - A variable listed in
[repo] env-allowis the repo's to route. The repo picks which build-arg or MCP server environment receives it -- in its config, or through an image label it controls -- and whatever runs there can use it as far as the container allows. List only variables you would hand to everyone who can change that repo's config or the images it builds or names, the agent included. See Config reference. - Network filtering is host:port allow/deny plus DNS and audit logging, not TLS interception. HTTPS MITM is explicitly deferred to a later release.
- Hostname deny rules are best-effort against a hostile client. A container that resolves a
name through the interceptor is bound to it and cannot shed that binding, but one that
connects by address and asserts no name at all offers nothing to match against. Hostname deny
rules describe traffic, not a containment boundary; bound a hostile container with address,
CIDR, and port rules, or with
default = "deny". - A hostname allow rule grants the addresses its name resolved to, not the site: on shared
hosting or a CDN, a container that resolved the allowed name can reach any other site served
from that address. Name a host in
allowonly where you would trust everything served there. - Under
default = "deny"a container resolves only the names anallowhostname entry could match, and every lookup is recorded; address and CIDRallowentries open no lookups. An allowed name can still carry data in its labels to that zone's name servers, so a wildcard such as*.example.comis only as tight as the zone is trusted. Underdefault = "allow"lookups are open apart from names a hostnamedenyentry matches.
Treat the container as you would any environment where you grant an agent real tools: put only what the agent should be able to use inside the boundary.