Skip to main content

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}