microsandbox_network/config.rs
1//! Serializable network configuration types.
2//!
3//! These types represent the user-facing declarative network configuration
4//! for sandbox networking. Designed for the smoltcp in-process engine.
5
6use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
7
8use ipnetwork::{Ipv4Network, Ipv6Network};
9use serde::{Deserialize, Serialize};
10
11use crate::dns::Nameserver;
12
13use crate::policy::NetworkPolicy;
14use crate::secrets::config::SecretsConfig;
15use crate::tls::TlsConfig;
16
17//--------------------------------------------------------------------------------------------------
18// Types
19//--------------------------------------------------------------------------------------------------
20
21/// Complete network configuration for a sandbox.
22///
23/// Narrowed for the smoltcp in-process engine. Gateway, prefix length, and
24/// other host-backend details are engine internals derived from the sandbox
25/// slot — the user only specifies what matters: interface overrides, ports,
26/// policy, DNS, TLS, and connection limits.
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct NetworkConfig {
29 /// Whether networking is enabled for this sandbox.
30 #[serde(default = "default_true")]
31 pub enabled: bool,
32
33 /// Guest interface overrides. Unset fields derived from sandbox slot.
34 #[serde(default)]
35 pub interface: InterfaceOverrides,
36
37 /// Host → guest port mappings.
38 #[serde(default)]
39 pub ports: Vec<PublishedPort>,
40
41 /// Egress/ingress policy rules.
42 #[serde(default)]
43 pub policy: NetworkPolicy,
44
45 /// DNS interception and filtering settings.
46 #[serde(default)]
47 pub dns: DnsConfig,
48
49 /// TLS interception settings.
50 #[serde(default)]
51 pub tls: TlsConfig,
52
53 /// Secret injection settings.
54 #[serde(default)]
55 pub secrets: SecretsConfig,
56
57 /// Max concurrent guest connections. Default: 256.
58 #[serde(default)]
59 pub max_connections: Option<usize>,
60
61 /// Ship the host's trusted root CAs into the guest at boot so outbound
62 /// TLS works behind corporate MITM proxies (Cloudflare Warp Zero
63 /// Trust, Zscaler, Netskope, etc.) whose gateway CA is installed on
64 /// the host but not shipped in the Mozilla root bundle the guest OS
65 /// uses. Opt-in: host trust is not copied into the guest unless
66 /// this is explicitly enabled. Default: false.
67 #[serde(default)]
68 pub trust_host_cas: bool,
69}
70
71/// Optional overrides for the guest interface.
72///
73/// If omitted, values are derived deterministically from the sandbox slot.
74#[derive(Debug, Clone, Default, Serialize, Deserialize)]
75pub struct InterfaceOverrides {
76 /// Guest MAC address. Default: derived from slot.
77 #[serde(default)]
78 pub mac: Option<[u8; 6]>,
79
80 /// Interface MTU. Default: 1500.
81 #[serde(default)]
82 pub mtu: Option<u16>,
83
84 /// Guest IPv4 address. Default: derived from slot within `ipv4_pool`.
85 #[serde(default)]
86 pub ipv4_address: Option<Ipv4Addr>,
87
88 /// Guest IPv4 pool. Default: derived from slot (172.16.0.0/12 pool).
89 #[serde(default)]
90 pub ipv4_pool: Option<Ipv4Network>,
91
92 /// Guest IPv6 address. Default: derived from slot within `ipv6_pool`.
93 #[serde(default)]
94 pub ipv6_address: Option<Ipv6Addr>,
95
96 /// Guest IPv6 pool. Default: derived from slot (fd42:6d73:62::/48 pool).
97 #[serde(default)]
98 pub ipv6_pool: Option<Ipv6Network>,
99}
100
101/// DNS interception settings for the sandbox.
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct DnsConfig {
104 /// Whether DNS rebinding protection is enabled.
105 #[serde(default = "default_true")]
106 pub rebind_protection: bool,
107
108 /// Nameservers to forward DNS queries to. When empty, fall back to
109 /// the `nameserver` entries in the host's `/etc/resolv.conf`. Set
110 /// this to pin specific resolvers (e.g. `1.1.1.1:53`, `dns.google`)
111 /// or to work around split-DNS / VPN setups where the host's
112 /// resolv.conf is incomplete. Accepts IPs, `IP:PORT`, or hostnames
113 /// (resolved once at startup via the host's OS resolver).
114 #[serde(default)]
115 pub nameservers: Vec<Nameserver>,
116
117 /// Per-query timeout in milliseconds. Default: 5000.
118 #[serde(default = "default_query_timeout_ms")]
119 pub query_timeout_ms: u64,
120}
121
122/// A published port mapping between host and guest.
123#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct PublishedPort {
125 /// Host-side port to bind.
126 pub host_port: u16,
127
128 /// Guest-side port to forward to.
129 pub guest_port: u16,
130
131 /// Protocol (TCP or UDP).
132 #[serde(default)]
133 pub protocol: PortProtocol,
134
135 /// Host address to bind. Defaults to loopback.
136 #[serde(default = "default_host_bind")]
137 pub host_bind: IpAddr,
138}
139
140/// Protocol for a published port.
141#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
142pub enum PortProtocol {
143 /// TCP (default).
144 #[default]
145 #[serde(rename = "tcp", alias = "Tcp")]
146 Tcp,
147
148 /// UDP.
149 #[serde(rename = "udp", alias = "Udp")]
150 Udp,
151}
152
153//--------------------------------------------------------------------------------------------------
154// Trait Implementations
155//--------------------------------------------------------------------------------------------------
156
157impl Default for NetworkConfig {
158 fn default() -> Self {
159 Self {
160 enabled: true,
161 interface: InterfaceOverrides::default(),
162 ports: Vec::new(),
163 policy: NetworkPolicy::default(),
164 dns: DnsConfig::default(),
165 tls: TlsConfig::default(),
166 secrets: SecretsConfig::default(),
167 max_connections: None,
168 trust_host_cas: false,
169 }
170 }
171}
172
173impl Default for DnsConfig {
174 fn default() -> Self {
175 Self {
176 rebind_protection: true,
177 nameservers: Vec::new(),
178 query_timeout_ms: default_query_timeout_ms(),
179 }
180 }
181}
182
183//--------------------------------------------------------------------------------------------------
184// Functions
185//--------------------------------------------------------------------------------------------------
186
187fn default_true() -> bool {
188 true
189}
190
191fn default_host_bind() -> IpAddr {
192 IpAddr::V4(Ipv4Addr::LOCALHOST)
193}
194
195fn default_query_timeout_ms() -> u64 {
196 5000
197}
198
199#[cfg(test)]
200mod tests {
201 use super::PortProtocol;
202
203 #[test]
204 fn port_protocol_serializes_lowercase_and_accepts_legacy_case() {
205 assert_eq!(
206 serde_json::to_string(&PortProtocol::Tcp).unwrap(),
207 "\"tcp\""
208 );
209 assert_eq!(
210 serde_json::to_string(&PortProtocol::Udp).unwrap(),
211 "\"udp\""
212 );
213 assert_eq!(
214 serde_json::from_str::<PortProtocol>("\"Tcp\"").unwrap(),
215 PortProtocol::Tcp
216 );
217 assert_eq!(
218 serde_json::from_str::<PortProtocol>("\"Udp\"").unwrap(),
219 PortProtocol::Udp
220 );
221 }
222}