Skip to content

Block loopback egress#216

Closed
luca-iachini wants to merge 29 commits into
mainfrom
fir-383-block-loopback
Closed

Block loopback egress#216
luca-iachini wants to merge 29 commits into
mainfrom
fir-383-block-loopback

Conversation

@luca-iachini

Copy link
Copy Markdown
Contributor

Problem

A wrapped agent's connection to 127.0.0.1/::1/loopback bypasses HTTP_PROXY, never reaching the Sidecar. Where the agent shares the host netns (proxy-only, macOS sandbox-exec), it can reach local admin ports, daemons, MCP servers with no policy eval and no audit.

Approach

Block loopback at the sandbox boundary — agent process only, never Firma's own components (proxy bridge + DNS stub use loopback) — and audit each block.

  • Linux (bwrap, structural): seccomp user-notify traps connect(2); host supervisor reads sockaddr via /proc/<pid>/mem, blocks non-sanctioned loopback with EACCES. In-sandbox installer hands the notify fd over SCM_RIGHTS, then execves the agent.
  • macOS (sandbox-exec): SBPL loopback re-allow port-scoped to bridge + DNS (was: all loopback). Structural, but no per-attempt audit (sandbox-exec denials not delivered to Sidecar).
  • Proxy-only (vz default, wsl2): no guard.

Audit

New DenyReason::LoopbackBlocked + audit-only network.loopback. Blocks flow over the generic firma run audit channel (FIRMA_RUN_AUDIT_SOCK): RunAuditMessage { session_id, agent_id, event } with tagged RunAuditEvent. Sidecar owns the mapping (impl From<&RunAuditMessage> for AuditPayload) and signs it. LoopbackBlocked is the first kind; future producers reuse the channel.

Limitations

  • Linux structural path only (non-structural not yet wired). seccomp allow-path TOCTOU caveat. macOS no per-attempt audit. WSL2/Firecracker not wired.

Verification

Draft — full build/nextest/clippy/dprint pending (tooling outage during dev).

@luca-iachini luca-iachini marked this pull request as ready for review July 2, 2026 13:53
@falcucci

falcucci commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

thanks for working on this.

question: have you tested manually on both OS as well?

I think we could have an OS targeted stacked change here to help @therandomsecurityguy or others to review and test on linux as well instead of one targeting everything.

If it's possible it would be great to have something like this to speed up the process and isolated reviews:

run-audit

  • add loopback deny event plumbing

linux maybe could be one pr?

  • add seccomp user-notify loopback guard
  • wire loopback guard into bwrap runtime
  • add loopback and raw TCP e2e coverage

macos:

  • scope sandbox-exec loopback access to Firma ports

docs

  • document proxy-only loopback guard gap on windows
  • document loopback egress enforcement model

I think we can add more tests based on the test coverage report as well.

NOTE: this is just a suggestion to take it or leave it.

@luca-iachini

Copy link
Copy Markdown
Contributor Author

Superseded — split into 4 focused PRs:

Same content, reviewable per-platform. Closing this monolith.

luca-iachini added a commit that referenced this pull request Jul 7, 2026
## What

Adds the **firma run audit channel**: an out-of-band control socket
(`FIRMA_RUN_AUDIT_SOCK`) carrying newline-delimited
`firma_core::RunAuditMessage`. The Sidecar owns the mapping from each
`RunAuditEvent` variant to `(action_class, decision, deny_reason,
resource)` and signs the result.

`RunAuditEvent::LoopbackBlocked` is the first variant, mapping to the
audit-only `network.loopback` action class (not in
`ActionClassRegistry::v0_1`, never Cedar-evaluated). The channel is
reusable by future out-of-band producers rather than adding new sockets.

## Why

Foundation for the loopback egress guard (Linux) and future out-of-band
enforcement facts to reach signed audit without touching the request
pipeline.

## Stack

Base of the fir-383 split. Replaces #216.
- this -> #(linux) -> stacked
- this -> #(docs) -> stacked
luca-iachini added a commit that referenced this pull request Jul 7, 2026
## What

Linux `bwrap` seccomp `user-notify` filter that traps the agent's
`connect(2)` and **blocks** direct connections to any loopback address
(`127.0.0.0/8`, `::1`, IPv4-mapped) except the proxy bridge and DNS stub
ports, returning `EACCES`. Catches direct sockets that bypass
`HTTP_PROXY`.

Each block is reported to the Sidecar over the audit channel (PR #231)
and recorded as a signed `DENY` / `network.loopback` / `loopback
blocked` event, visible in `firma monitor`. Deny path is race-free;
allow path uses `SECCOMP_USER_NOTIF_FLAG_CONTINUE`. Fails open with a
warning if user-notify is unavailable — the private netns already
isolates the agent, so the guard is defense-in-depth plus a
direct-socket audit trail.

## Stack

Based on #231 (audit channel). Part of the fir-383 split replacing #216.
luca-iachini added a commit that referenced this pull request Jul 8, 2026
## What

Documents the loopback egress enforcement model across backends:

- `docs-site/src/content/docs/concepts/sandbox.md` — new **Loopback
blocking** section (Linux seccomp guard, macOS sandbox-exec port-scope,
proxy-only backends), updated network-boundary / proxy-bridge prose, and
structural/invariant tables.
- `crates/firma-run/src/backend/windows_wsl2.rs` — comment documenting
the WSL2/Windows gap: the seccomp guard is Linux-host-only and not wired
across the Windows->guest boundary; WSL2 stays proxy-only.

`llms.txt` audit-channel + `network.loopback` action-class registry
entries land in the branches that own them (#231 base; #232 linux; #233
macOS).

## Stack

Based on #231 (audit channel, for the `network.loopback` model). Part of
the fir-383 split replacing #216.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants