-
Notifications
You must be signed in to change notification settings - Fork 19
fix(security): Restrict sandbox loopback access to proxy and DNS only #95
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,8 @@ | |
|
|
||
| This module installs iptables OUTPUT rules that match on the sandbox uid: | ||
| - ALLOW the sandbox uid → 127.0.0.1:<proxy_port> (so pip etc. work) | ||
| - DROP everything else from the sandbox uid | ||
| - ALLOW the sandbox uid → 127.0.0.53:53 (DNS via systemd-resolved) | ||
| - REJECT everything else from the sandbox uid | ||
|
|
||
| The API process itself runs as root (uid 0), so the proxy's own outbound | ||
| traffic to PyPI/npm/etc. is unaffected by these rules. | ||
|
|
@@ -113,19 +114,41 @@ def install_sandbox_egress_rules(sandbox_uid: int, proxy_port: int) -> bool: | |
| "-j", | ||
| "ACCEPT", | ||
| ], | ||
| # Allow loopback traffic generally (DNS to systemd-resolved on 127.0.0.53, | ||
| # localhost-only services, etc.). The proxy enforces hostname allowlist | ||
| # for actual outbound; this just keeps the sandbox uid able to talk | ||
| # to itself if it ever needs to. | ||
| # Allow DNS to systemd-resolved on loopback (some tools resolve | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment is inaccurate for the Docker runtime: |
||
| # before handing the CONNECT to the proxy). | ||
| [ | ||
| "-A", | ||
| "OUTPUT", | ||
| "-m", | ||
| "owner", | ||
| "--uid-owner", | ||
| str(sandbox_uid), | ||
| "-o", | ||
| "lo", | ||
| "-d", | ||
| "127.0.0.53", | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrong resolver address. Verified on the live container:
Not a security regression (fails closed) and the main path is fine (the proxy resolves DNS as root for |
||
| "-p", | ||
| "udp", | ||
| "--dport", | ||
| "53", | ||
| "-m", | ||
| "comment", | ||
| "--comment", | ||
| _RULE_COMMENT, | ||
| "-j", | ||
| "ACCEPT", | ||
| ], | ||
| [ | ||
| "-A", | ||
| "OUTPUT", | ||
| "-m", | ||
| "owner", | ||
| "--uid-owner", | ||
| str(sandbox_uid), | ||
| "-d", | ||
| "127.0.0.53", | ||
|
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same wrong-address issue as the UDP rule above ( |
||
| "-p", | ||
| "tcp", | ||
| "--dport", | ||
| "53", | ||
| "-m", | ||
| "comment", | ||
| "--comment", | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IPv6 not covered (defense-in-depth gap). This
REJECT— and all rules here — areiptables(IPv4) only.ip6tablesOUTPUT policy isACCEPTwith no rules, so over IPv6 the sandbox uid is unrestricted. Not exploitable in the default deploy (Docker bridge IPv6 off, API binds0.0.0.0/IPv4-only, internal services in separate netns, seccomp blocksbindfor non-bash) — but the invariant is absent for v6. Consider mirroring these rules viaip6tables(or asserting IPv6 is disabled) so it holds if an operator ever enables Docker IPv6.