grapsus_config/server.rs
1//! Server and listener configuration types
2//!
3//! This module contains configuration types for the proxy server itself
4//! and its listeners (ports/addresses it binds to).
5
6use serde::{Deserialize, Serialize};
7use std::path::PathBuf;
8use validator::Validate;
9
10use grapsus_common::types::{TlsVersion, TraceIdFormat};
11
12// ============================================================================
13// Server Configuration
14// ============================================================================
15
16/// Server-wide configuration
17#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
18pub struct ServerConfig {
19 /// Number of worker threads (0 = number of CPU cores)
20 #[serde(default = "default_worker_threads")]
21 pub worker_threads: usize,
22
23 /// Maximum number of connections
24 #[serde(default = "default_max_connections")]
25 pub max_connections: usize,
26
27 /// Graceful shutdown timeout
28 #[serde(default = "default_graceful_shutdown_timeout")]
29 pub graceful_shutdown_timeout_secs: u64,
30
31 /// Enable daemon mode
32 #[serde(default)]
33 pub daemon: bool,
34
35 /// PID file path
36 pub pid_file: Option<PathBuf>,
37
38 /// User to switch to after binding
39 pub user: Option<String>,
40
41 /// Group to switch to after binding
42 pub group: Option<String>,
43
44 /// Working directory
45 pub working_directory: Option<PathBuf>,
46
47 /// Trace ID format for request tracing
48 ///
49 /// - `tinyflake` (default): 11-char Base58, operator-friendly
50 /// - `uuid`: 36-char UUID v4, guaranteed unique
51 #[serde(default)]
52 pub trace_id_format: TraceIdFormat,
53
54 /// Enable automatic configuration reload on file changes
55 ///
56 /// When enabled, the proxy will watch the configuration file for changes
57 /// and automatically reload when modifications are detected.
58 #[serde(default)]
59 pub auto_reload: bool,
60}
61
62// ============================================================================
63// Listener Configuration
64// ============================================================================
65
66/// Listener configuration (port binding)
67#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
68pub struct ListenerConfig {
69 /// Unique identifier for this listener
70 pub id: String,
71
72 /// Socket address to bind to
73 #[validate(custom(function = "crate::validation::validate_socket_addr"))]
74 pub address: String,
75
76 /// Protocol (http, https)
77 pub protocol: ListenerProtocol,
78
79 /// TLS configuration (required for https)
80 pub tls: Option<TlsConfig>,
81
82 /// Default route if no other matches
83 pub default_route: Option<String>,
84
85 /// Request timeout
86 #[serde(default = "default_request_timeout")]
87 pub request_timeout_secs: u64,
88
89 /// Keep-alive timeout
90 #[serde(default = "default_keepalive_timeout")]
91 pub keepalive_timeout_secs: u64,
92
93 /// Maximum concurrent streams (HTTP/2)
94 #[serde(default = "default_max_concurrent_streams")]
95 pub max_concurrent_streams: u32,
96}
97
98/// Listener protocol
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
100#[serde(rename_all = "lowercase")]
101pub enum ListenerProtocol {
102 Http,
103 Https,
104 #[serde(rename = "h2")]
105 Http2,
106 #[serde(rename = "h3")]
107 Http3,
108}
109
110// ============================================================================
111// TLS Configuration
112// ============================================================================
113
114/// TLS configuration
115#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
116pub struct TlsConfig {
117 /// Default certificate file path (used when no SNI match)
118 /// Optional when ACME is configured
119 pub cert_file: Option<PathBuf>,
120
121 /// Default private key file path
122 /// Optional when ACME is configured
123 pub key_file: Option<PathBuf>,
124
125 /// Additional certificates for SNI support
126 /// Maps hostname patterns to certificate configurations
127 #[serde(default)]
128 pub additional_certs: Vec<SniCertificate>,
129
130 /// CA certificate file path for client verification (mTLS)
131 pub ca_file: Option<PathBuf>,
132
133 /// Minimum TLS version
134 #[serde(default = "default_min_tls_version")]
135 pub min_version: TlsVersion,
136
137 /// Maximum TLS version
138 pub max_version: Option<TlsVersion>,
139
140 /// Cipher suites (empty = use defaults)
141 #[serde(default)]
142 pub cipher_suites: Vec<String>,
143
144 /// Require client certificates (mTLS)
145 #[serde(default)]
146 pub client_auth: bool,
147
148 /// OCSP stapling
149 #[serde(default = "default_ocsp_stapling")]
150 pub ocsp_stapling: bool,
151
152 /// Session resumption
153 #[serde(default = "default_session_resumption")]
154 pub session_resumption: bool,
155
156 /// ACME automatic certificate management
157 /// When configured, cert_file and key_file become optional
158 pub acme: Option<AcmeConfig>,
159}
160
161/// ACME automatic certificate configuration
162///
163/// Enables zero-config TLS via Let's Encrypt and compatible CAs.
164/// When configured, Grapsus will automatically obtain, renew, and
165/// manage TLS certificates for the specified domains.
166///
167/// # Example
168///
169/// ```kdl
170/// tls {
171/// acme {
172/// email "admin@example.com"
173/// domains "example.com" "www.example.com"
174/// staging false
175/// storage "/var/lib/grapsus/acme"
176/// renew-before-days 30
177/// challenge-type "http-01" // or "dns-01" for wildcards
178///
179/// // Required for DNS-01 challenges
180/// dns-provider {
181/// type "hetzner"
182/// credentials-file "/etc/grapsus/secrets/hetzner-dns.json"
183/// api-timeout-secs 30
184///
185/// propagation {
186/// initial-delay-secs 10
187/// check-interval-secs 5
188/// timeout-secs 120
189/// }
190/// }
191/// }
192/// }
193/// ```
194#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
195pub struct AcmeConfig {
196 /// Contact email for Let's Encrypt account
197 /// Required for account registration and recovery
198 #[validate(email)]
199 pub email: String,
200
201 /// Domain names to obtain certificates for
202 /// At least one domain is required
203 #[validate(length(min = 1, message = "at least one domain is required"))]
204 pub domains: Vec<String>,
205
206 /// Use Let's Encrypt staging environment
207 /// Set to true for testing to avoid rate limits
208 #[serde(default)]
209 pub staging: bool,
210
211 /// Directory for storing certificates and account keys
212 /// Defaults to /var/lib/grapsus/acme
213 #[serde(default = "default_acme_storage")]
214 pub storage: PathBuf,
215
216 /// Days before expiry to trigger renewal
217 /// Let's Encrypt certificates are valid for 90 days
218 /// Default is 30 days before expiry
219 #[serde(default = "default_renewal_days")]
220 pub renew_before_days: u32,
221
222 /// Challenge type to use for domain validation
223 /// Defaults to HTTP-01, use DNS-01 for wildcard certificates
224 #[serde(default)]
225 pub challenge_type: AcmeChallengeType,
226
227 /// DNS provider configuration (required for DNS-01 challenges)
228 pub dns_provider: Option<DnsProviderConfig>,
229}
230
231/// ACME challenge type
232#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
233#[serde(rename_all = "kebab-case")]
234pub enum AcmeChallengeType {
235 /// HTTP-01 challenge (default)
236 /// Requires HTTP access on port 80
237 #[default]
238 Http01,
239
240 /// DNS-01 challenge
241 /// Required for wildcard certificates
242 /// Requires DNS provider configuration
243 Dns01,
244}
245
246impl AcmeChallengeType {
247 /// Check if this is DNS-01 challenge type
248 pub fn is_dns01(&self) -> bool {
249 matches!(self, Self::Dns01)
250 }
251
252 /// Check if this is HTTP-01 challenge type
253 pub fn is_http01(&self) -> bool {
254 matches!(self, Self::Http01)
255 }
256}
257
258/// DNS provider configuration for DNS-01 challenges
259#[derive(Debug, Clone, Serialize, Deserialize)]
260pub struct DnsProviderConfig {
261 /// DNS provider type
262 pub provider: DnsProviderType,
263
264 /// Path to credentials file
265 /// File should contain JSON: {"token": "..."} or {"api_key": "...", "api_secret": "..."}
266 pub credentials_file: Option<PathBuf>,
267
268 /// Environment variable containing credentials
269 pub credentials_env: Option<String>,
270
271 /// API request timeout in seconds
272 #[serde(default = "default_dns_api_timeout")]
273 pub api_timeout_secs: u64,
274
275 /// Propagation check configuration
276 #[serde(default)]
277 pub propagation: PropagationCheckConfig,
278}
279
280/// DNS provider type
281#[derive(Debug, Clone, Serialize, Deserialize)]
282#[serde(tag = "type", rename_all = "lowercase")]
283pub enum DnsProviderType {
284 /// Hetzner DNS API
285 Hetzner,
286
287 /// Generic webhook provider
288 Webhook {
289 /// Webhook URL
290 url: String,
291 /// Optional custom auth header name
292 auth_header: Option<String>,
293 },
294}
295
296/// Configuration for DNS propagation checking
297#[derive(Debug, Clone, Serialize, Deserialize)]
298pub struct PropagationCheckConfig {
299 /// Initial delay before first check (seconds)
300 #[serde(default = "default_propagation_initial_delay")]
301 pub initial_delay_secs: u64,
302
303 /// Interval between propagation checks (seconds)
304 #[serde(default = "default_propagation_check_interval")]
305 pub check_interval_secs: u64,
306
307 /// Maximum time to wait for propagation (seconds)
308 #[serde(default = "default_propagation_timeout")]
309 pub timeout_secs: u64,
310
311 /// Custom nameservers to query (optional)
312 /// Defaults to Google (8.8.8.8), Cloudflare (1.1.1.1), Quad9 (9.9.9.9)
313 #[serde(default)]
314 pub nameservers: Vec<String>,
315}
316
317impl Default for PropagationCheckConfig {
318 fn default() -> Self {
319 Self {
320 initial_delay_secs: default_propagation_initial_delay(),
321 check_interval_secs: default_propagation_check_interval(),
322 timeout_secs: default_propagation_timeout(),
323 nameservers: Vec::new(),
324 }
325 }
326}
327
328/// SNI certificate configuration
329#[derive(Debug, Clone, Serialize, Deserialize)]
330pub struct SniCertificate {
331 /// Hostname patterns to match (e.g., "example.com", "*.example.com")
332 pub hostnames: Vec<String>,
333
334 /// Certificate file path
335 pub cert_file: PathBuf,
336
337 /// Private key file path
338 pub key_file: PathBuf,
339}
340
341// ============================================================================
342// Default Value Functions
343// ============================================================================
344
345pub(crate) fn default_worker_threads() -> usize {
346 0
347}
348
349pub(crate) fn default_max_connections() -> usize {
350 10000
351}
352
353pub(crate) fn default_graceful_shutdown_timeout() -> u64 {
354 30
355}
356
357pub(crate) fn default_request_timeout() -> u64 {
358 60
359}
360
361pub(crate) fn default_keepalive_timeout() -> u64 {
362 75
363}
364
365pub(crate) fn default_max_concurrent_streams() -> u32 {
366 100
367}
368
369fn default_min_tls_version() -> TlsVersion {
370 TlsVersion::Tls12
371}
372
373fn default_ocsp_stapling() -> bool {
374 true
375}
376
377fn default_session_resumption() -> bool {
378 true
379}
380
381pub(crate) fn default_acme_storage() -> PathBuf {
382 PathBuf::from("/var/lib/grapsus/acme")
383}
384
385pub(crate) fn default_renewal_days() -> u32 {
386 30
387}
388
389fn default_dns_api_timeout() -> u64 {
390 30
391}
392
393fn default_propagation_initial_delay() -> u64 {
394 10
395}
396
397fn default_propagation_check_interval() -> u64 {
398 5
399}
400
401fn default_propagation_timeout() -> u64 {
402 120
403}