1use std::net::IpAddr;
6use std::path::PathBuf;
7
8use crate::config::{DnsConfig, InterfaceOverrides, NetworkConfig, PortProtocol, PublishedPort};
9use crate::dns::Nameserver;
10use crate::policy::{BuildError, NetworkPolicy};
11use crate::secrets::config::{HostPattern, SecretEntry, SecretInjection, ViolationAction};
12use crate::tls::TlsConfig;
13
14#[derive(Clone)]
20pub struct NetworkBuilder {
21 config: NetworkConfig,
22 errors: Vec<BuildError>,
23}
24
25pub struct DnsBuilder {
27 config: DnsConfig,
28}
29
30pub struct TlsBuilder {
32 config: TlsConfig,
33}
34
35pub struct SecretBuilder {
45 env_var: Option<String>,
46 value: Option<String>,
47 placeholder: Option<String>,
48 allowed_hosts: Vec<HostPattern>,
49 injection: SecretInjection,
50 require_tls_identity: bool,
51}
52
53impl NetworkBuilder {
58 pub fn new() -> Self {
60 Self {
61 config: NetworkConfig::default(),
62 errors: Vec::new(),
63 }
64 }
65
66 pub fn from_config(config: NetworkConfig) -> Self {
68 Self {
69 config,
70 errors: Vec::new(),
71 }
72 }
73
74 pub fn enabled(mut self, enabled: bool) -> Self {
76 self.config.enabled = enabled;
77 self
78 }
79
80 pub fn port(self, host_port: u16, guest_port: u16) -> Self {
82 self.add_port(host_port, guest_port, PortProtocol::Tcp)
83 }
84
85 pub fn port_udp(self, host_port: u16, guest_port: u16) -> Self {
87 self.add_port(host_port, guest_port, PortProtocol::Udp)
88 }
89
90 fn add_port(mut self, host_port: u16, guest_port: u16, protocol: PortProtocol) -> Self {
91 self.config.ports.push(PublishedPort {
92 host_port,
93 guest_port,
94 protocol,
95 host_bind: IpAddr::V4(std::net::Ipv4Addr::LOCALHOST),
96 });
97 self
98 }
99
100 pub fn policy(mut self, policy: NetworkPolicy) -> Self {
102 self.config.policy = policy;
103 self
104 }
105
106 pub fn dns(mut self, f: impl FnOnce(DnsBuilder) -> DnsBuilder) -> Self {
115 self.config.dns = f(DnsBuilder::new()).build();
116 self
117 }
118
119 pub fn tls(mut self, f: impl FnOnce(TlsBuilder) -> TlsBuilder) -> Self {
121 self.config.tls = f(TlsBuilder::new()).build();
122 self
123 }
124
125 pub fn secret(mut self, f: impl FnOnce(SecretBuilder) -> SecretBuilder) -> Self {
135 self.config
136 .secrets
137 .secrets
138 .push(f(SecretBuilder::new()).build());
139 self
140 }
141
142 pub fn secret_env(
144 mut self,
145 env_var: impl Into<String>,
146 value: impl Into<String>,
147 placeholder: impl Into<String>,
148 allowed_host: impl Into<String>,
149 ) -> Self {
150 self.config.secrets.secrets.push(SecretEntry {
151 env_var: env_var.into(),
152 value: value.into(),
153 placeholder: placeholder.into(),
154 allowed_hosts: vec![HostPattern::Exact(allowed_host.into())],
155 injection: SecretInjection::default(),
156 require_tls_identity: true,
157 });
158 self
159 }
160
161 pub fn on_secret_violation(mut self, action: ViolationAction) -> Self {
163 self.config.secrets.on_violation = action;
164 self
165 }
166
167 pub fn max_connections(mut self, max: usize) -> Self {
169 self.config.max_connections = Some(max);
170 self
171 }
172
173 pub fn interface(mut self, overrides: InterfaceOverrides) -> Self {
175 self.config.interface = overrides;
176 self
177 }
178
179 pub fn trust_host_cas(mut self, enabled: bool) -> Self {
185 self.config.trust_host_cas = enabled;
186 self
187 }
188
189 pub fn build(mut self) -> Result<NetworkConfig, BuildError> {
195 if let Some(err) = self.errors.drain(..).next() {
196 return Err(err);
197 }
198 Ok(self.config)
199 }
200}
201
202impl DnsBuilder {
203 pub fn new() -> Self {
205 Self {
206 config: DnsConfig::default(),
207 }
208 }
209
210 pub fn rebind_protection(mut self, enabled: bool) -> Self {
212 self.config.rebind_protection = enabled;
213 self
214 }
215
216 pub fn nameservers<I>(mut self, nameservers: I) -> Self
223 where
224 I: IntoIterator,
225 I::Item: Into<Nameserver>,
226 {
227 self.config.nameservers = nameservers.into_iter().map(Into::into).collect();
228 self
229 }
230
231 pub fn query_timeout_ms(mut self, ms: u64) -> Self {
233 self.config.query_timeout_ms = ms;
234 self
235 }
236
237 pub fn build(self) -> DnsConfig {
239 self.config
240 }
241}
242
243impl Default for DnsBuilder {
244 fn default() -> Self {
245 Self::new()
246 }
247}
248
249impl TlsBuilder {
250 pub fn new() -> Self {
252 Self {
253 config: TlsConfig {
254 enabled: true,
255 ..TlsConfig::default()
256 },
257 }
258 }
259
260 pub fn bypass(mut self, pattern: impl Into<String>) -> Self {
262 self.config.bypass.push(pattern.into());
263 self
264 }
265
266 pub fn verify_upstream(mut self, verify: bool) -> Self {
268 self.config.verify_upstream = verify;
269 self
270 }
271
272 pub fn intercepted_ports(mut self, ports: Vec<u16>) -> Self {
274 self.config.intercepted_ports = ports;
275 self
276 }
277
278 pub fn block_quic(mut self, block: bool) -> Self {
280 self.config.block_quic_on_intercept = block;
281 self
282 }
283
284 pub fn upstream_ca_cert(mut self, path: impl Into<PathBuf>) -> Self {
289 self.config.upstream_ca_cert.push(path.into());
290 self
291 }
292
293 pub fn intercept_ca_cert(mut self, path: impl Into<PathBuf>) -> Self {
295 self.config.intercept_ca.cert_path = Some(path.into());
296 self
297 }
298
299 pub fn intercept_ca_key(mut self, path: impl Into<PathBuf>) -> Self {
301 self.config.intercept_ca.key_path = Some(path.into());
302 self
303 }
304
305 pub fn build(self) -> TlsConfig {
307 self.config
308 }
309}
310
311impl SecretBuilder {
312 pub fn new() -> Self {
314 Self {
315 env_var: None,
316 value: None,
317 placeholder: None,
318 allowed_hosts: Vec::new(),
319 injection: SecretInjection::default(),
320 require_tls_identity: true,
321 }
322 }
323
324 pub fn env(mut self, var: impl Into<String>) -> Self {
326 self.env_var = Some(var.into());
327 self
328 }
329
330 pub fn value(mut self, value: impl Into<String>) -> Self {
332 self.value = Some(value.into());
333 self
334 }
335
336 pub fn placeholder(mut self, placeholder: impl Into<String>) -> Self {
339 self.placeholder = Some(placeholder.into());
340 self
341 }
342
343 pub fn allow_host(mut self, host: impl Into<String>) -> Self {
345 self.allowed_hosts.push(HostPattern::Exact(host.into()));
346 self
347 }
348
349 pub fn allow_host_pattern(mut self, pattern: impl Into<String>) -> Self {
351 self.allowed_hosts
352 .push(HostPattern::Wildcard(pattern.into()));
353 self
354 }
355
356 pub fn allow_any_host_dangerous(mut self, i_understand_the_risk: bool) -> Self {
359 if i_understand_the_risk {
360 self.allowed_hosts.push(HostPattern::Any);
361 }
362 self
363 }
364
365 pub fn require_tls_identity(mut self, enabled: bool) -> Self {
367 self.require_tls_identity = enabled;
368 self
369 }
370
371 pub fn inject_headers(mut self, enabled: bool) -> Self {
373 self.injection.headers = enabled;
374 self
375 }
376
377 pub fn inject_basic_auth(mut self, enabled: bool) -> Self {
379 self.injection.basic_auth = enabled;
380 self
381 }
382
383 pub fn inject_query(mut self, enabled: bool) -> Self {
385 self.injection.query_params = enabled;
386 self
387 }
388
389 pub fn inject_body(mut self, enabled: bool) -> Self {
391 self.injection.body = enabled;
392 self
393 }
394
395 pub fn build(self) -> SecretEntry {
400 let env_var = self.env_var.expect("SecretBuilder: .env() is required");
401 let value = self.value.expect("SecretBuilder: .value() is required");
402 let placeholder = self
403 .placeholder
404 .unwrap_or_else(|| format!("$MSB_{env_var}"));
405
406 SecretEntry {
407 env_var,
408 value,
409 placeholder,
410 allowed_hosts: self.allowed_hosts,
411 injection: self.injection,
412 require_tls_identity: self.require_tls_identity,
413 }
414 }
415}
416
417impl Default for NetworkBuilder {
422 fn default() -> Self {
423 Self::new()
424 }
425}
426
427impl Default for TlsBuilder {
428 fn default() -> Self {
429 Self::new()
430 }
431}
432
433impl Default for SecretBuilder {
434 fn default() -> Self {
435 Self::new()
436 }
437}
438
439#[cfg(test)]
444mod tests {
445 use super::*;
446
447 #[test]
449 fn network_builder_happy_path_returns_config() {
450 let cfg = NetworkBuilder::new()
451 .dns(|d| d.rebind_protection(false))
452 .build()
453 .unwrap();
454 assert!(!cfg.dns.rebind_protection);
455 }
456}