Repository navigation
[Bubblewrap][Seatbelt] Reach the host's loopback services outbound, refuse blocked connections at once, and log them - #1708
Draft
Carmine Paolino (crmne) wants to merge 1 commit into
Conversation
…efuse blocked connections at once, and log them Three optional fields in network.egress (1.1.0-alpha): - hostLoopbackPorts: TCP ports of the host's own loopback the sandbox may connect to, outbound only; ingress stays as it was. On Bubblewrap they are accepted at slirp's gateway ahead of its drop, and the sandbox's own 127.0.0.1:<port> reaches them through a DNAT in its namespace (route_localnet, masquerade on tap0), so a server the sandbox starts on any other port of its loopback stays its own. On Seatbelt they are (remote tcp "localhost:<port>") rules after the host-loopback deny. - blocked: "reject" refuses a blocked connection at once instead of dropping it, so clients fail fast. Seatbelt already does. - blockedLog (Bubblewrap): an absolute path to append one JSON line to per blocked outbound connection. Each blocking verdict is preceded by an NFLOG copy, read inside the sandbox's network namespace by a netlink socket a short-lived child makes there and hands back, started before the workload is released. A blocked DNS query also carries the name it asked for. New NetworkPolicySupport bits gate each field; Bubblewrap declares all three, Seatbelt the first two, and every other backend refuses them.
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved security and correctness issues permit proxy-confinement and explicit-deny bypasses and can hang sandbox startup.
9 open issues
Prevent FIFO blockedLog paths from hanging startup · New Apply explicit denies before host-loopback port grants · New Reject new egress options with Seatbelt runtime proxy · New Regenerate the stale TypeScript contract artifact · New Include reject_blocked when selecting isolated mode · New Reject blocked=reject with runtime proxy · New Add executor-level coverage for new egress controls · New Batch host-loopback DNAT rules to avoid transaction limits · New Document Seatbelt grants access to all host addresses · New
What changed in this PR
Adds development-contract network controls to MXC for outbound access to selected host services, immediate rejection of blocked connections, and Bubblewrap blocked-traffic logging, addressing #1707.
Changes:
- Adds
hostLoopbackPorts,blocked, andblockedLogto the 1.1.0-alpha contract, adapters, schema, and capability validation. - Implements Bubblewrap firewall, loopback redirection, and logging support, plus Seatbelt host-port grants.
- Adds unit coverage and documentation while preserving defaults for older contracts.
| File | Description |
|---|---|
src/mxc-sdk/src/core/mxc_contract/dev/network.rs |
Defines the new egress controls. |
src/mxc-sdk/src/core/mxc_contract/dev/mod.rs |
Exports the blocked-action enum. |
src/mxc-sdk/src/core/mxc_common/wire.rs |
Adds internal egress fields and defaults. |
src/mxc-sdk/src/core/mxc_common/validator.rs |
Adds capability bits and unsupported-policy checks. |
src/mxc-sdk/src/core/mxc_common/sdk_input.rs |
Defaults new fields during SDK conversion. |
src/mxc-sdk/src/core/mxc_common/network_parser.rs |
Converts and validates new egress values. |
src/mxc-sdk/src/core/mxc_common/network_parser_ingress_default_tests.rs |
Updates test construction for new defaults. |
src/mxc-sdk/src/core/mxc_common/models.rs |
Extends the runtime egress policy. |
src/mxc-sdk/src/core/mxc_common/config_contract_adapters/v1_0/state_aware.rs |
Preserves lifecycle conversion defaults. |
src/mxc-sdk/src/core/mxc_common/config_contract_adapters/v1_0/common.rs |
Preserves v1.0 network defaults. |
src/mxc-sdk/src/core/mxc_common/config_contract_adapters/v0_9/state_aware.rs |
Preserves older lifecycle conversion defaults. |
src/mxc-sdk/src/core/mxc_common/config_contract_adapters/v0_9/common.rs |
Preserves v0.9 network defaults. |
src/mxc-sdk/src/core/mxc_common/config_contract_adapters/dev/state_aware.rs |
Updates lifecycle network construction. |
src/mxc-sdk/src/core/mxc_common/config_contract_adapters/dev/common.rs |
Maps development-contract egress controls. |
src/mxc-sdk/src/backends/seatbelt/common/seatbelt_runner.rs |
Declares supported network capabilities. |
src/mxc-sdk/src/backends/seatbelt/common/profile_builder.rs |
Emits and tests outbound host-port grants. |
src/mxc-sdk/src/backends/process_container/common/base_container_runner.rs |
Replaces blanket support with explicit capabilities. |
src/mxc-sdk/src/backends/nanvix/runner/mod.rs |
Updates test policy defaults. |
src/mxc-sdk/src/backends/lxc/common/network_iptables_ga_egress_spec.rs |
Updates test policy construction. |
src/mxc-sdk/src/backends/lxc/common/lxc_runner.rs |
Updates network test defaults. |
src/mxc-sdk/src/backends/isolation_session/common/policy.rs |
Updates a policy test fixture. |
src/mxc-sdk/src/backends/bubblewrap/common/proxy_network.rs |
Installs NAT rules and manages logging lifecycle. |
src/mxc-sdk/src/backends/bubblewrap/common/network_rules.rs |
Renders host-port, REJECT, NFLOG, and NAT rules. |
src/mxc-sdk/src/backends/bubblewrap/common/mod.rs |
Registers the Linux blocked-log module. |
src/mxc-sdk/src/backends/bubblewrap/common/bwrap_runner.rs |
Declares and unit-tests new capabilities. |
src/mxc-sdk/src/backends/bubblewrap/common/bwrap_command.rs |
Updates network-mode and proxy-conflict predicates. |
src/mxc-sdk/src/backends/bubblewrap/common/blocked_log.rs |
Adds namespace logging and packet parsing. |
schemas/dev/mxc-config.schema.1.1.0-alpha.json |
Adds generated schema definitions. |
docs/schema.md |
Illustrates the new egress fields. |
docs/backends/bwrap/bubblewrap-backend.md |
Documents behavior, logging, and prerequisites. |
Review effort: Balanced
Comment on lines
+79
to
+83
| .custom_flags(libc::O_NOFOLLOW) | ||
| .open(path) | ||
| .map_err(|error| { | ||
| format!("Bubblewrap: failed to open the blocked-connection log: {error}") | ||
| })?; |
Comment on lines
+817
to
+819
| for &port in &egress.host_loopback_ports { | ||
| rules.push(EgressRule { | ||
| verdict: RuleVerdict::Accept, |
| } | ||
|
|
||
| write_host_loopback_rules(out, policy, allow_outbound); | ||
| write_host_loopback_port_rules(out, policy); |
Comment on lines
+104
to
+108
| #[serde(default)] | ||
| pub host_loopback_ports: OptionalField<Vec<NonZeroU16>>, | ||
| /// Optional answer to a blocked outbound connection. Defaults to `drop`. | ||
| #[serde(default)] | ||
| pub blocked: OptionalField<NetworkBlockedAction>, |
Comment on lines
+561
to
+562
| && egress.host_loopback_ports.is_empty() | ||
| && egress.blocked_log.is_none() |
Comment on lines
+646
to
+647
| || !egress.host_loopback_ports.is_empty() | ||
| || egress.blocked_log.is_some() |
Comment on lines
+94
to
+96
| | NetworkPolicySupport::EGRESS_HOST_LOOPBACK_PORTS | ||
| | NetworkPolicySupport::EGRESS_BLOCKED_REJECT | ||
| | NetworkPolicySupport::EGRESS_BLOCKED_LOG |
Comment on lines
+875
to
+879
| let mut payload = String::from("*nat\n"); | ||
| for port in &self.host_loopback_ports { | ||
| payload.push_str(&format!( | ||
| "-A OUTPUT -d 127.0.0.1/32 -p tcp --dport {port} -j DNAT --to-destination \ | ||
| {gateway}:{port}\n" |
Comment on lines
+230
to
+232
| "hostLoopbackPorts": [5432], // 1.1.0-alpha, Bubblewrap and Seatbelt: TCP ports | ||
| // of the host's loopback the sandbox may connect to, | ||
| // outbound only (ingress is unchanged) |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



📖 Description
This adds three optional
network.egressfields to the 1.1.0-alpha contract. Each one sits behind its ownNetworkPolicySupportbit, and every backend that doesn't declare that bit refuses the field.hostLoopbackPorts: [u16]: TCP ports of the host's own loopback the sandbox may connect to, outbound only. Ingress is unchanged.10.0.2.2ahead of the host-loopback drop. The sandbox's own127.0.0.1:<port>reaches the host's through anatDNAT in the sandbox namespace, withroute_localnetand a masquerade ontap0. Only the listed ports are redirected, so a workload's own servers on other ports stay its own.(allow network-outbound (remote tcp "localhost:<port>")), after the host-loopback deny.blocked: "drop" | "reject": on Bubblewrap,"reject"turns every blocking verdict into REJECT, so clients fail at once instead of timing out. Seatbelt already fails at once, so it accepts"reject"as is.blockedLog: "<absolute path>"(Bubblewrap only):Any of the three fields selects the firewall-enforced private namespace, even with a deny default and no rules. None of them can be combined with a runtime proxy. BaseContainer now declares the features it enforces explicitly instead of
ALL.Limitations:
nattable needsnf_nat, which an unprivileged sandbox can't load. Without it, the sandbox fails to start and says why.🔗 References
Resolves #1707. Opened as a draft to agree on the approach first, as CONTRIBUTING asks for schema changes. I'm happy to reshape the fields.
🔍 Validation
cargo test -p mxc-sdk --lib -- bubblewrap validator network contract schema seatbeltpasses on Linux (1136 tests). The new tests cover:natpayloadblockedLogbeing refusedcargo clippy -p mxc-sdk --all-targets -- -D warningsandcargo fmt --all -- --checkare clean on the pinned 1.93 toolchain.schemas/dev/mxc-config.schema.1.1.0-alpha.jsonwas regenerated withmxc_schema_gen.docker0and one published on127.0.0.1:55432are both reached, the latter as the sandbox's ownlocalhost:55432.1.1.1.1:443is refused in about a second and logged.getent hosts rubygems.orgis logged with its name.✅ Checklist
Cargo.lock, thedependency-feed-checkcheck passes (see pull request builds)📋 Issue Type
Microsoft Reviewers: Open in CodeFlow