Typical flow
NetworkPolicy and the builders live in microsandbox_network; NetworkPolicy is also re-exported from the crate root as microsandbox::NetworkPolicy.
NetworkPolicy static methods
ANetworkPolicy is an ordered rule list plus two per-direction defaults, evaluated first-match-wins. The presets below construct common shapes directly; for anything custom, start from builder().
NetworkPolicy::builder()
Example
Example
NetworkPolicyBuilder. The primary construction path: string inputs (.ip, .cidr, .domain, .domain_suffix) are stored raw and parsed at build(), so the chain stays clean and the first parse or validation failure surfaces as BuildError.
Returns
NetworkPolicy::none()
SandboxBuilder::disable_network().
NetworkPolicy::allow_all()
NetworkPolicy::public_only()
Default policy. Egress defaults to deny but allows DNS to the gateway forwarder and any Public destination; private, loopback, link-local, and metadata are denied. Ingress defaults to allow, preserving unfiltered published-port behavior.
NetworkPolicy::non_local()
public_only() but also allows egress to Private / LAN ranges. Loopback, link-local, and metadata stay denied; ingress defaults to allow.
NetworkPolicy instance methods
These methods consumeself and return a modified policy, so they chain off a preset or a built policy. Each prepends its rules, so a later deny outranks a catch-all allow like allow public under first-match-wins. All return Result<NetworkPolicy, DomainNameError> because the names are parsed eagerly.
policy.allow_domain()
Example
Example
Domain egress rule. Single-name sugar over allow_domains().
policy.deny_domain()
Domain egress rule. Single-name sugar over deny_domains().
policy.allow_domains()
Example
Example
Domain egress rule per name.
Parameters
namesIntoIterator<Item = AsRef<str>>policy.deny_domains()
Example
Example
Domain egress rule per name. Prepending lets the denies outrank catch-all allows.
policy.allow_domain_suffix()
DomainSuffix egress rule. Single-suffix sugar over allow_domain_suffixes().
policy.deny_domain_suffix()
DomainSuffix egress rule. Single-suffix sugar over deny_domain_suffixes().
policy.allow_domain_suffixes()
DomainSuffix egress rule per suffix. Suffixes match the apex domain and every subdomain (label-aligned).
policy.deny_domain_suffixes()
Example
Example
DomainSuffix egress rule per suffix.
NetworkPolicyBuilder
Fluent builder forNetworkPolicy, obtained via NetworkPolicy::builder(). Defaults and rule-batch closures interleave; the build is deferred. The closure signature for rule() / egress() / ingress() / any() is FnOnce(&mut RuleBuilder) -> &mut RuleBuilder. A chain ending in any rule-adder (.allow_public(), .deny().ip(...), etc.) returns the builder reference and satisfies the bound; multi-statement bodies end with an explicit r return.
State setters inside a closure (.tcp(), .port()) accumulate eagerly and are not reset between rule-adders, so a single closure can fan one state into several rules. Use separate closures for rules that need different state. See State accumulation for the rationale.
.default_deny()
default_egress and default_ingress to Deny.
.default_allow()
default_egress and default_ingress to Allow.
.default_egress()
Parameters
actionAction.default_ingress()
Parameters
actionAction.egress()
Example
Example
rule() with direction pre-set to Egress.
.ingress()
rule() with direction pre-set to Ingress.
.any()
rule() with direction pre-set to Any. Rules committed inside apply in both directions.
.rule()
Example
Example
.egress(), .ingress(), or .any() before any rule-adder, otherwise build() returns BuildError::DirectionNotSet.
.build()
NetworkPolicy. Lazy-parses every .ip() / .cidr() / .domain() / .domain_suffix() input, validates the direction-set and ICMP-egress-only invariants, and emits a tracing::warn! for each shadowed rule pair (a rule fully covered by an earlier one in the same direction; only Ip / Cidr / Group destinations are checked). Builds still succeed when a shadow is detected. Returns the first BuildError encountered.
Returns
RuleBuilder
The mutable builder handed to aNetworkPolicyBuilder rule-batch closure. Direction, protocol, and port setters return &mut Self and accumulate eagerly; rule-adders commit one rule each using the current state. Protocols and ports have set semantics, so duplicates dedupe.
.egress()
Egress for subsequent rule-adders. Last-write-wins.
.ingress()
Ingress for subsequent rule-adders. Last-write-wins.
.any()
Any for subsequent rule-adders. Rules committed after this apply in both directions. Last-write-wins.
.tcp()
Tcp to the protocols set.
.udp()
Udp to the protocols set.
.icmpv4()
Icmpv4 to the protocols set. Egress-only: an ICMP protocol on an Ingress or Any rule fails build with BuildError::IngressDoesNotSupportIcmp.
.icmpv6()
Icmpv6 to the protocols set. Egress-only; same rule as icmpv4().
.port()
Parameters
portu16.port_range()
Parameters
lou16hiu16lo > hi records BuildError::InvalidPortRange..ports()
port() once per element.
.allow_public()
Public group: every IP not in another named category. A matching deny_public() exists for each allow_* group adder below.
.allow_private()
Private group (RFC1918 + ULA + CGN).
.allow_loopback()
Loopback group (127.0.0.0/8, ::1): the guest’s own loopback, not the host. To reach a service on the host’s localhost use allow_host() instead. See the loopback-vs-host trap.
.allow_link_local()
LinkLocal group (169.254.0.0/16, fe80::/10). Excludes the metadata IP 169.254.169.254.
.allow_meta()
Metadata group (169.254.169.254). Dangerous on cloud hosts: exposes IAM credentials.
.allow_multicast()
Multicast group (224.0.0.0/4, ff00::/8).
.allow_host()
Host group: per-sandbox gateway IPs that back host.microsandbox.internal. This is the right shortcut for “let the sandbox reach my host’s localhost”, not allow_loopback().
.deny_public()
Public group. Per-group deny_* adders mirror the allow_* set: deny_private(), deny_loopback(), deny_link_local(), deny_meta(), deny_multicast(), and deny_host().
.allow_local()
Loopback + LinkLocal + Host. Each uses the closure’s current state. Metadata is intentionally excluded; opt in via allow_meta() separately.
.deny_local()
Loopback + LinkLocal + Host. Metadata is intentionally excluded.
.allow_domains()
Example
Example
Domain rule per name, inheriting the closure’s current direction / protocol / port state. Lazy-parse: invalid names surface as BuildError::InvalidDomain from build().
.deny_domains()
Domain rule per name.
.allow_domain_suffixes()
DomainSuffix rule per suffix.
.deny_domain_suffixes()
DomainSuffix rule per suffix.
.allow()
Example
Example
Allow. The returned RuleDestinationBuilder requires exactly one destination call to commit; dropping it without one adds no rule.
.deny()
Deny.
RuleDestinationBuilder
Returned byRuleBuilder::allow() / RuleBuilder::deny(). Requires exactly one destination method call to commit the rule, then returns the &mut RuleBuilder so the chain continues. The type is #[must_use]: dropping it without a destination call adds no rule.
.ip()
Destination::Cidr of the IP as /32 (v4) or /128 (v6). The string is parsed at build(); invalid values surface as BuildError::InvalidIp.
.cidr()
Destination::Cidr. Invalid values surface as BuildError::InvalidCidr.
.domain()
Destination::Domain. Matches only when a cached hostname for the remote IP equals this name (after canonicalization).
.domain_suffix()
Destination::DomainSuffix. Matches the apex domain itself and any subdomain. A single-label suffix (e.g. com) is rejected at build as BuildError::InvalidDomain.
.group()
Destination::Group for callers who already hold a DestinationGroup value.
.any()
Destination::Any: matches every remote.
NetworkBuilder
Builder for the sandbox’s network stack, used inSandboxBuilder::network(|n| n...). Every setter returns Self, so calls chain. Errors accumulated by nested builders cascade up: the outermost SandboxBuilder::build() surfaces them as MicrosandboxError::NetworkBuilder(BuildError).
.policy()
Example
Example
NetworkPolicy.
Parameters
policyNetworkPolicy.port()
127.0.0.1. Equivalent to SandboxBuilder::port().
Parameters
host_portu16guest_portu16.port_udp()
127.0.0.1.
.port_bind()
0.0.0.0.
Parameters
host_bindIpAddrhost_portu16guest_portu16.port_udp_bind()
.dns()
Example
Example
DnsBuilder.
.tls()
TlsBuilder.
.trust_host_cas()
false. Opt in when egress HTTPS inside the sandbox needs to work behind corporate MITM proxies (Cloudflare Warp Zero Trust, Zscaler, Netskope, etc.): those proxies install a gateway CA on the host that’s unknown to the guest’s stock Mozilla bundle.
.max_connections()
256.
Parameters
maxusize.ipv4_pool()
/30 guest subnets. Defaults to 172.16.0.0/12. A pool with a prefix longer than /30 records BuildError::InvalidIpv4Pool.
Parameters
poolIpv4Network/30 or shorter..ipv6_pool()
/64 guest prefixes. Defaults to fd42:6d73:62::/48. A pool with a prefix longer than /64 records BuildError::InvalidIpv6Pool.
.interface()
ipv4_pool() and ipv6_pool(), which validate the prefix. Unset fields fall back to values derived deterministically from the sandbox slot. See InterfaceOverrides.
Parameters
overridesInterfaceOverrides.enabled()
true. To fully turn networking off, prefer SandboxBuilder::disable_network(), which also sets the policy to NetworkPolicy::none().
.on_secret_violation()
Example
Example
ViolationActionBuilder.
Passthrough hosts receive the placeholder unchanged. They do not receive real secret values.
.secret()
SandboxBuilder::secret(). See SecretBuilder for the full API. A companion secret_env(env_var, value, placeholder, allowed_host) shorthand and secret_entry(SecretEntry) are also available on NetworkBuilder.
Parameters
DnsBuilder
Builder for DNS interception, used inNetworkBuilder::dns(|d| d...). Owns rebind protection, nameserver pinning, and the per-query timeout. Every setter returns Self.
.nameservers()
/etc/resolv.conf (or, on macOS, the SystemConfiguration dynamic store). Each element converts into Nameserver: a SocketAddr, an IpAddr, or a parsed string via "dns.google:53".parse::<Nameserver>()?.
Parameters
nameserversIntoIterator<Item = Into<Nameserver>>.query_timeout_ms()
5000.
.rebind_protection()
true.
TlsBuilder
Builder for TLS interception, used inNetworkBuilder::tls(|t| t...). Creating it enables interception. Every setter returns Self.
.bypass()
"*.internal.corp"). Use for domains with certificate pinning. Can be called multiple times.
Parameters
patternimpl Into<String>*.suffix wildcards..intercepted_ports()
[443].
.verify_upstream()
true. Set to false only for self-signed servers.
.block_quic()
true.
.intercept_ca_cert()
intercept_ca_key() to provide a stable CA across sandbox restarts. If unset, a CA is auto-generated and persisted.
.intercept_ca_key()
.upstream_ca_cert()
.upstream_ca_cert_for()
pattern. Pattern syntax matches bypass(): exact hosts and *.suffix wildcards are supported.
.verify_upstream_for()
pattern. Pattern syntax matches bypass(): exact hosts and *.suffix wildcards are supported. Setting verify to false is the proxy-side equivalent of curl -k for matching hosts; TLS interception still runs.
ViolationActionBuilder
Builder for secret-violation behavior, used byNetworkBuilder::on_secret_violation() and SecretBuilder::on_violation(). A blocking call (block, block_and_log, block_and_terminate) replaces any accumulated passthrough hosts; passthrough calls accumulate. When passthrough hosts are configured, non-matching hosts use the default action. Every setter returns Self. Produces a ViolationAction.
.block()
.block_and_log()
ViolationAction default.
.block_and_terminate()
.passthrough_host()
Parameters
hostimpl Into<String>.passthrough_host_pattern()
*.example.com) to receive placeholders unchanged.
.passthrough_all_hosts()
i_understand_the_risk is true.
Parameters
i_understand_the_riskbooltrue to take effect.Types
NetworkPolicy
Built by NetworkPolicy::builder() · used by policy()
An ordered rule list plus two per-direction defaults, evaluated first-match-wins. Egress evaluation considers rules wheredirection ∈ {Egress, Any}; ingress considers {Ingress, Any}. If no rule matches, the direction-specific default applies. Default is public_only().
Rule
Held by NetworkPolicy
A single policy rule. Thedestination interpretation is direction-dependent: egress destination, or ingress peer/source. ports is always the guest-side port (egress destination port / ingress listening port).
| Field | Type | Description |
|---|---|---|
| direction | Direction | Which evaluator considers this rule |
| destination | Destination | Target filter |
| protocols | Vec<Protocol> | Set semantics; empty = any protocol |
| ports | Vec<PortRange> | Set semantics; empty = any port |
| action | Action | What to do on match |
| Method | Description |
|---|---|
Rule::allow_egress(destination) | Allow rule, direction Egress |
Rule::deny_egress(destination) | Deny rule, direction Egress |
Rule::allow_ingress(destination) | Allow rule, direction Ingress |
Rule::deny_ingress(destination) | Deny rule, direction Ingress |
Rule::allow_any(destination) | Allow rule, direction Any |
Rule::deny_any(destination) | Deny rule, direction Any |
Rule::allow_dns() | Allow plain DNS (UDP/53 + TCP/53) to the gateway forwarder (Group::Host); the one-liner for opening DNS under deny-by-default. See DNS as egress |
Action
Used by Rule · NetworkPolicy · default_egress()
| Value | Wire format | Description |
|---|---|---|
Allow | "allow" | Permit the traffic |
Deny | "deny" | Drop the traffic silently |
Direction
Used by Rule
| Value | Wire format | Description |
|---|---|---|
Egress | "egress" | Traffic leaving the sandbox |
Ingress | "ingress" | Traffic entering the sandbox (via published ports) |
Any | "any" | Rule applies in either direction |
Destination
Held by Rule · committed by RuleDestinationBuilder
| Variant | Description |
|---|---|
Any | Match any address |
Cidr(IpNetwork) | Match a CIDR range (e.g. 10.0.0.0/8); single IPs are stored as /32 or /128 |
Domain(DomainName) | Match an exact domain (e.g. example.com) when a cached hostname for the remote IP equals it |
DomainSuffix(DomainName) | Match the apex domain and every subdomain (e.g. example.com and api.example.com) |
Group(DestinationGroup) | Match a predefined address group |
DestinationGroup
Held by Destination · committed by RuleBuilder group adders
Groups are disjoint with one carve-out:Metadata takes precedence over LinkLocal for 169.254.169.254, and Host over Private when the gateway IPs sit in CGN/ULA ranges.
| Value | Wire format | Matches |
|---|---|---|
Public | "public" | Complement of the other categories: every address not in any other group |
Loopback | "loopback" | 127.0.0.0/8, ::1 (the guest’s own loopback, not the host) |
Private | "private" | RFC1918 (10/8, 172.16/12, 192.168/16) + CGN (100.64/10) + ULA (fc00::/7) |
LinkLocal | "link_local" | 169.254.0.0/16, fe80::/10 (excludes metadata) |
Metadata | "metadata" | Cloud metadata endpoint (169.254.169.254) |
Multicast | "multicast" | 224.0.0.0/4, ff00::/8 |
Host | "host" | Per-sandbox gateway IPs that back host.microsandbox.internal |
Protocol
Held by Rule · set by RuleBuilder protocol setters
| Value | Wire format |
|---|---|
Tcp | "tcp" |
Udp | "udp" |
Icmpv4 | "icmpv4" |
Icmpv6 | "icmpv6" |
Ingress or Any carrying an ICMP protocol fails build with BuildError::IngressDoesNotSupportIcmp.
PortRange
Held by Rule · added by port() · port_range()
An inclusive port range.| Field | Type | Description |
|---|---|---|
| start | u16 | Start port (inclusive) |
| end | u16 | End port (inclusive) |
| Method | Description |
|---|---|
PortRange::single(port) | Match a single port |
PortRange::range(start, end) | Match an inclusive range |
contains(port) | Whether port falls within the range |
DomainName
Held by Destination::Domain / DomainSuffix
A validated DNS name. Construction goes throughstr::parse (or TryFrom<String>), which delegates to hickory_proto::rr::Name and canonicalizes the input (lowercased ASCII, leading and trailing dots stripped) so rule matching is a byte-wise compare against the DNS cache. Invalid inputs return a DomainNameError.
_service._tcp.example.com are accepted. The builder methods (.domain(&str), .domain_suffix(&str)) take strings and parse them lazily at build(), so callers rarely construct DomainName directly.
| Method | Description |
|---|---|
as_str() | Borrow the canonical string form |
try_into_suffix() | Validate for use as a DomainSuffix; single-label names (e.g. com) are rejected |
Nameserver
Used by nameservers()
An upstream DNS server, either a literal address or a hostname resolved at interceptor startup via the host’s OS resolver. Serializes as a single string. Construct viaFrom<SocketAddr>, From<IpAddr>, or str::parse (errors with ParseNameserverError).
| Variant | Description |
|---|---|
Addr(SocketAddr) | Literal socket address, ready to use |
Host { host: String, port: u16 } | Hostname + port resolved at startup |
1.1.1.1, 1.1.1.1:5353, 2606:4700:4700::1111, [2606:4700:4700::1111]:53, dns.google, dns.google:53. A bare IP or hostname defaults to port 53.
InterfaceOverrides
Used by interface()
Per-sandbox guest interface overrides. Every field is optional; an omitted field is derived deterministically from the sandbox slot. Most callers only touch the pools viaipv4_pool() / ipv6_pool() rather than constructing this directly.
| Field | Type | Description |
|---|---|---|
| mac | Option<[u8; 6]> | Guest MAC address. Default: derived from slot |
| mtu | Option<u16> | Interface MTU. Default: 1500 |
| ipv4_address | Option<Ipv4Addr> | Guest IPv4 address. Default: derived from slot within ipv4_pool |
| ipv4_pool | Option<Ipv4Network> | IPv4 pool guest subnets are derived from. Default: 172.16.0.0/12 |
| ipv6_address | Option<Ipv6Addr> | Guest IPv6 address. Default: derived from slot within ipv6_pool |
| ipv6_pool | Option<Ipv6Network> | IPv6 pool guest prefixes are derived from. Default: fd42:6d73:62::/48 |
BuildError
Returned by NetworkPolicyBuilder::build() · wrapped by NetworkBuilder
Errors surfaced by the builders’build() methods. The same enum covers NetworkPolicy::builder(), DnsBuilder, and NetworkBuilder; the network and DNS builders accumulate lazily, so the first failure surfaces from the outermost build() in the chain.
| Variant | Cause |
|---|---|
DirectionNotSet { rule_index } | A rule was committed without .egress() / .ingress() / .any() |
MissingDestination { rule_index } | .allow() or .deny() was called but no destination method followed |
InvalidIp { rule_index, raw } | .ip(&str) got an unparseable value |
InvalidCidr { rule_index, raw } | .cidr(&str) got an unparseable value |
InvalidIpv4Pool { raw } | ipv4_pool() got a pool that can’t hold a /30 sandbox subnet |
InvalidIpv6Pool { raw } | ipv6_pool() got a pool that can’t hold a /64 sandbox prefix |
InvalidDomain { rule_index, raw, source } | .domain / .domain_suffix got a value that failed DomainName parse |
InvalidPortRange { rule_index, lo, hi } | .port_range(lo, hi) had lo > hi |
IngressDoesNotSupportIcmp { rule_index } | ICMP protocol on a non-egress rule |
InvalidSecretConfig { source } | A secret entry failed validation |
SandboxBuilder::build(), BuildError is wrapped as MicrosandboxError::NetworkBuilder(BuildError).
ViolationAction
Built by ViolationActionBuilder · used by on_secret_violation()
Action taken when a secret placeholder is sent to a disallowed host. Also documented on the Secrets page, where it pairs withSecretBuilder.
| Value | Wire format | Description |
|---|---|---|
Block | "block" | Silently drop the request |
BlockAndLog | "block-and-log" | Drop the request and emit a warning log (the default) |
BlockAndTerminate | "block-and-terminate" | Drop the request, log an error, and shut down the sandbox |
Passthrough(Vec<HostPattern>) | "passthrough" | Forward matching hosts with the placeholder unchanged; non-matching hosts use the default action |