sozu_command_lib/
config.rs

1//! # Sōzu's configuration
2//!
3//! This module is responsible for parsing the `config.toml` provided by the flag `--config`
4//! when starting Sōzu.
5//!
6//! Here is the workflow for generating a working config:
7//!
8//! ```text
9//!     config.toml   ->   FileConfig    ->  ConfigBuilder   ->  Config
10//! ```
11//!
12//! `config.toml` is parsed to `FileConfig`, a structure that itself contains a lot of substructures
13//! whose names start with `File-` and end with `-Config`, like `FileHttpFrontendConfig` for instance.
14//!
15//! The instance of `FileConfig` is then passed to a `ConfigBuilder` that populates a final `Config`
16//! with listeners and clusters.
17//!
18//! To illustrate:
19//!
20//! ```ignore
21//! use sozu_command_lib::config::{FileConfig, ConfigBuilder};
22//!
23//! let file_config = FileConfig::load_from_path("../config.toml")
24//!     .expect("Could not load config.toml");
25//!
26//! let config = ConfigBuilder::new(file_config, "../assets/config.toml")
27//!     .into_config()
28//!     .expect("Could not build config");
29//! ```
30//!
31//! Note that the path to `config.toml` is used twice: the first time, to parse the file,
32//! the second time, to keep the path in the config for later use.
33//!
34//! However, there is a simpler way that combines all this:
35//!
36//! ```ignore
37//! use sozu_command_lib::config::Config;
38//!
39//! let config = Config::load_from_path("../assets/config.toml")
40//!     .expect("Could not build config from the path");
41//! ```
42//!
43//! ## How values are chosen
44//!
45//! Values are chosen in this order of priority:
46//!
47//! 1. values defined in a section of the TOML file, for instance, timeouts for a specific listener
48//! 2. values defined globally in the TOML file, like timeouts or buffer size
49//! 3. if a variable has not been set in the TOML file, it will be set to a default defined here
50use std::{
51    collections::{BTreeMap, HashMap, HashSet},
52    env, fmt,
53    fs::{File, create_dir_all, metadata},
54    io::{ErrorKind, Read},
55    net::SocketAddr,
56    ops::Range,
57    path::PathBuf,
58};
59
60use crate::{
61    ObjectKind,
62    certificate::split_certificate_chain,
63    logging::AccessLogFormat,
64    proto::command::{
65        ActivateListener, AddBackend, AddCertificate, CertificateAndKey, Cluster,
66        CustomHttpAnswers, HttpListenerConfig, HttpsListenerConfig, ListenerType,
67        LoadBalancingAlgorithms, LoadBalancingParams, LoadMetric, MetricsConfiguration, PathRule,
68        ProtobufAccessLogFormat, ProxyProtocolConfig, Request, RequestHttpFrontend,
69        RequestTcpFrontend, RulePosition, ServerConfig, ServerMetricsConfig, SocketAddress,
70        TcpListenerConfig, TlsVersion, WorkerRequest, request::RequestType,
71    },
72};
73
74/// provides all supported cipher suites exported by Rustls TLS
75/// provider as it support only strongly secure ones.
76///
77/// See the [documentation](https://docs.rs/rustls/latest/rustls/static.ALL_CIPHER_SUITES.html)
78pub const DEFAULT_RUSTLS_CIPHER_LIST: [&str; 9] = [
79    // TLS 1.3 cipher suites
80    "TLS13_AES_256_GCM_SHA384",
81    "TLS13_AES_128_GCM_SHA256",
82    "TLS13_CHACHA20_POLY1305_SHA256",
83    // TLS 1.2 cipher suites
84    "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
85    "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
86    "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
87    "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
88    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
89    "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
90];
91
92pub const DEFAULT_CIPHER_SUITES: [&str; 4] = [
93    "TLS_AES_256_GCM_SHA384",
94    "TLS_AES_128_GCM_SHA256",
95    "TLS_AES_128_CCM_SHA256",
96    "TLS_CHACHA20_POLY1305_SHA256",
97];
98
99pub const DEFAULT_SIGNATURE_ALGORITHMS: [&str; 9] = [
100    "ECDSA+SHA256",
101    "ECDSA+SHA384",
102    "ECDSA+SHA512",
103    "RSA+SHA256",
104    "RSA+SHA384",
105    "RSA+SHA512",
106    "RSA-PSS+SHA256",
107    "RSA-PSS+SHA384",
108    "RSA-PSS+SHA512",
109];
110
111pub const DEFAULT_GROUPS_LIST: [&str; 4] = ["P-521", "P-384", "P-256", "x25519"];
112
113/// maximum time of inactivity for a frontend socket (60 seconds)
114pub const DEFAULT_FRONT_TIMEOUT: u32 = 60;
115
116/// maximum time of inactivity for a backend socket (30 seconds)
117pub const DEFAULT_BACK_TIMEOUT: u32 = 30;
118
119/// maximum time to connect to a backend server (3 seconds)
120pub const DEFAULT_CONNECT_TIMEOUT: u32 = 3;
121
122/// maximum time to receive a request since the connection started (10 seconds)
123pub const DEFAULT_REQUEST_TIMEOUT: u32 = 10;
124
125/// maximum time to wait for a worker to respond, until it is deemed NotAnswering (10 seconds)
126pub const DEFAULT_WORKER_TIMEOUT: u32 = 10;
127
128/// a name applied to sticky sessions ("SOZUBALANCEID")
129pub const DEFAULT_STICKY_NAME: &str = "SOZUBALANCEID";
130
131/// Interval between checking for zombie sessions, (30 minutes)
132pub const DEFAULT_ZOMBIE_CHECK_INTERVAL: u32 = 1_800;
133
134/// timeout to accept connection events in the accept queue (60 seconds)
135pub const DEFAULT_ACCEPT_QUEUE_TIMEOUT: u32 = 60;
136
137/// number of workers, i.e. Sōzu processes that scale horizontally (2)
138pub const DEFAULT_WORKER_COUNT: u16 = 2;
139
140/// wether a worker is automatically restarted when it crashes (true)
141pub const DEFAULT_WORKER_AUTOMATIC_RESTART: bool = true;
142
143/// wether to save the state automatically (false)
144pub const DEFAULT_AUTOMATIC_STATE_SAVE: bool = false;
145
146/// minimum number of buffers (1)
147pub const DEFAULT_MIN_BUFFERS: u64 = 1;
148
149/// maximum number of buffers (1 000)
150pub const DEFAULT_MAX_BUFFERS: u64 = 1_000;
151
152/// size of the buffers, in bytes (16 KB)
153pub const DEFAULT_BUFFER_SIZE: u64 = 16_393;
154
155/// maximum number of simultaneous connections (10 000)
156pub const DEFAULT_MAX_CONNECTIONS: usize = 10_000;
157
158/// size of the buffer for the channels, in bytes. Must be bigger than the size of the data received. (1 MB)
159pub const DEFAULT_COMMAND_BUFFER_SIZE: u64 = 1_000_000;
160
161/// maximum size of the buffer for the channels, in bytes. (2 MB)
162pub const DEFAULT_MAX_COMMAND_BUFFER_SIZE: u64 = 2_000_000;
163
164/// wether to avoid register cluster metrics in the local drain
165pub const DEFAULT_DISABLE_CLUSTER_METRICS: bool = false;
166
167pub const MAX_LOOP_ITERATIONS: usize = 100000;
168
169/// Number of TLS 1.3 tickets to send to a client when establishing a connection.
170/// The tickets allow the client to resume a session. This protects the client
171/// agains session tracking. Increases the number of getrandom syscalls,
172/// with little influence on performance. Defaults to 4.
173pub const DEFAULT_SEND_TLS_13_TICKETS: u64 = 4;
174
175/// for both logs and access logs
176pub const DEFAULT_LOG_TARGET: &str = "stdout";
177
178#[derive(Debug)]
179pub enum IncompatibilityKind {
180    PublicAddress,
181    ProxyProtocol,
182}
183
184#[derive(Debug)]
185pub enum MissingKind {
186    Field(String),
187    Protocol,
188    SavedState,
189}
190
191#[derive(thiserror::Error, Debug)]
192pub enum ConfigError {
193    #[error("env path not found: {0}")]
194    Env(String),
195    #[error("Could not open file {path_to_open}: {io_error}")]
196    FileOpen {
197        path_to_open: String,
198        io_error: std::io::Error,
199    },
200    #[error("Could not read file {path_to_read}: {io_error}")]
201    FileRead {
202        path_to_read: String,
203        io_error: std::io::Error,
204    },
205    #[error(
206        "the field {kind:?} of {object:?} with id or address {id} is incompatible with the rest of the options"
207    )]
208    Incompatible {
209        kind: IncompatibilityKind,
210        object: ObjectKind,
211        id: String,
212    },
213    #[error("Invalid '{0}' field for a TCP frontend")]
214    InvalidFrontendConfig(String),
215    #[error("invalid path {0:?}")]
216    InvalidPath(PathBuf),
217    #[error("listening address {0:?} is already used in the configuration")]
218    ListenerAddressAlreadyInUse(SocketAddr),
219    #[error("missing {0:?}")]
220    Missing(MissingKind),
221    #[error("could not get parent directory for file {0}")]
222    NoFileParent(String),
223    #[error("Could not get the path of the saved state")]
224    SaveStatePath(String),
225    #[error("Can not determine path to sozu socket: {0}")]
226    SocketPathError(String),
227    #[error("toml decoding error: {0}")]
228    DeserializeToml(String),
229    #[error("Can not set this frontend on a {0:?} listener")]
230    WrongFrontendProtocol(ListenerProtocol),
231    #[error("Can not build a {expected:?} listener from a {found:?} config")]
232    WrongListenerProtocol {
233        expected: ListenerProtocol,
234        found: Option<ListenerProtocol>,
235    },
236}
237
238/// An HTTP, HTTPS or TCP listener as parsed from the `Listeners` section in the toml
239#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
240#[serde(deny_unknown_fields)]
241pub struct ListenerBuilder {
242    pub address: SocketAddr,
243    pub protocol: Option<ListenerProtocol>,
244    pub public_address: Option<SocketAddr>,
245    pub answer_301: Option<String>,
246    pub answer_400: Option<String>,
247    pub answer_401: Option<String>,
248    pub answer_404: Option<String>,
249    pub answer_408: Option<String>,
250    pub answer_413: Option<String>,
251    pub answer_502: Option<String>,
252    pub answer_503: Option<String>,
253    pub answer_504: Option<String>,
254    pub answer_507: Option<String>,
255    pub tls_versions: Option<Vec<TlsVersion>>,
256    pub cipher_list: Option<Vec<String>>,
257    pub cipher_suites: Option<Vec<String>>,
258    pub expect_proxy: Option<bool>,
259    #[serde(default = "default_sticky_name")]
260    pub sticky_name: String,
261    pub certificate: Option<String>,
262    pub certificate_chain: Option<String>,
263    pub key: Option<String>,
264    /// maximum time of inactivity for a frontend socket
265    pub front_timeout: Option<u32>,
266    /// maximum time of inactivity for a backend socket
267    pub back_timeout: Option<u32>,
268    /// maximum time to connect to a backend server
269    pub connect_timeout: Option<u32>,
270    /// maximum time to receive a request since the connection started
271    pub request_timeout: Option<u32>,
272    /// A [Config] to pull defaults from
273    pub config: Option<Config>,
274    /// Number of TLS 1.3 tickets to send to a client when establishing a connection.
275    /// The ticket allow the client to resume a session. This protects the client
276    /// agains session tracking. Defaults to 4.
277    pub send_tls13_tickets: Option<u64>,
278}
279
280pub fn default_sticky_name() -> String {
281    DEFAULT_STICKY_NAME.to_string()
282}
283
284impl ListenerBuilder {
285    /// starts building an HTTP Listener with config values for timeouts,
286    /// or defaults if no config is provided
287    pub fn new_http(address: SocketAddress) -> ListenerBuilder {
288        Self::new(address, ListenerProtocol::Http)
289    }
290
291    /// starts building an HTTPS Listener with config values for timeouts,
292    /// or defaults if no config is provided
293    pub fn new_tcp(address: SocketAddress) -> ListenerBuilder {
294        Self::new(address, ListenerProtocol::Tcp)
295    }
296
297    /// starts building a TCP Listener with config values for timeouts,
298    /// or defaults if no config is provided
299    pub fn new_https(address: SocketAddress) -> ListenerBuilder {
300        Self::new(address, ListenerProtocol::Https)
301    }
302
303    /// starts building a Listener
304    fn new(address: SocketAddress, protocol: ListenerProtocol) -> ListenerBuilder {
305        ListenerBuilder {
306            address: address.into(),
307            answer_301: None,
308            answer_401: None,
309            answer_400: None,
310            answer_404: None,
311            answer_408: None,
312            answer_413: None,
313            answer_502: None,
314            answer_503: None,
315            answer_504: None,
316            answer_507: None,
317            back_timeout: None,
318            certificate_chain: None,
319            certificate: None,
320            cipher_list: None,
321            cipher_suites: None,
322            config: None,
323            connect_timeout: None,
324            expect_proxy: None,
325            front_timeout: None,
326            key: None,
327            protocol: Some(protocol),
328            public_address: None,
329            request_timeout: None,
330            send_tls13_tickets: None,
331            sticky_name: DEFAULT_STICKY_NAME.to_string(),
332            tls_versions: None,
333        }
334    }
335
336    pub fn with_public_address(&mut self, public_address: Option<SocketAddr>) -> &mut Self {
337        if let Some(address) = public_address {
338            self.public_address = Some(address);
339        }
340        self
341    }
342
343    pub fn with_answer_404_path<S>(&mut self, answer_404_path: Option<S>) -> &mut Self
344    where
345        S: ToString,
346    {
347        if let Some(path) = answer_404_path {
348            self.answer_404 = Some(path.to_string());
349        }
350        self
351    }
352
353    pub fn with_answer_503_path<S>(&mut self, answer_503_path: Option<S>) -> &mut Self
354    where
355        S: ToString,
356    {
357        if let Some(path) = answer_503_path {
358            self.answer_503 = Some(path.to_string());
359        }
360        self
361    }
362
363    pub fn with_tls_versions(&mut self, tls_versions: Vec<TlsVersion>) -> &mut Self {
364        self.tls_versions = Some(tls_versions);
365        self
366    }
367
368    pub fn with_cipher_list(&mut self, cipher_list: Option<Vec<String>>) -> &mut Self {
369        self.cipher_list = cipher_list;
370        self
371    }
372
373    pub fn with_cipher_suites(&mut self, cipher_suites: Option<Vec<String>>) -> &mut Self {
374        self.cipher_suites = cipher_suites;
375        self
376    }
377
378    pub fn with_expect_proxy(&mut self, expect_proxy: bool) -> &mut Self {
379        self.expect_proxy = Some(expect_proxy);
380        self
381    }
382
383    pub fn with_sticky_name<S>(&mut self, sticky_name: Option<S>) -> &mut Self
384    where
385        S: ToString,
386    {
387        if let Some(name) = sticky_name {
388            self.sticky_name = name.to_string();
389        }
390        self
391    }
392
393    pub fn with_certificate<S>(&mut self, certificate: S) -> &mut Self
394    where
395        S: ToString,
396    {
397        self.certificate = Some(certificate.to_string());
398        self
399    }
400
401    pub fn with_certificate_chain(&mut self, certificate_chain: String) -> &mut Self {
402        self.certificate = Some(certificate_chain);
403        self
404    }
405
406    pub fn with_key<S>(&mut self, key: String) -> &mut Self
407    where
408        S: ToString,
409    {
410        self.key = Some(key);
411        self
412    }
413
414    pub fn with_front_timeout(&mut self, front_timeout: Option<u32>) -> &mut Self {
415        self.front_timeout = front_timeout;
416        self
417    }
418
419    pub fn with_back_timeout(&mut self, back_timeout: Option<u32>) -> &mut Self {
420        self.back_timeout = back_timeout;
421        self
422    }
423
424    pub fn with_connect_timeout(&mut self, connect_timeout: Option<u32>) -> &mut Self {
425        self.connect_timeout = connect_timeout;
426        self
427    }
428
429    pub fn with_request_timeout(&mut self, request_timeout: Option<u32>) -> &mut Self {
430        self.request_timeout = request_timeout;
431        self
432    }
433
434    /// Get the custom HTTP answers from the file system using the provided paths
435    fn get_http_answers(&self) -> Result<Option<CustomHttpAnswers>, ConfigError> {
436        let http_answers = CustomHttpAnswers {
437            answer_301: read_http_answer_file(&self.answer_301)?,
438            answer_400: read_http_answer_file(&self.answer_400)?,
439            answer_401: read_http_answer_file(&self.answer_401)?,
440            answer_404: read_http_answer_file(&self.answer_404)?,
441            answer_408: read_http_answer_file(&self.answer_408)?,
442            answer_413: read_http_answer_file(&self.answer_413)?,
443            answer_502: read_http_answer_file(&self.answer_502)?,
444            answer_503: read_http_answer_file(&self.answer_503)?,
445            answer_504: read_http_answer_file(&self.answer_504)?,
446            answer_507: read_http_answer_file(&self.answer_507)?,
447        };
448        Ok(Some(http_answers))
449    }
450
451    /// Assign the timeouts of the config to this listener, only if timeouts did not exist
452    fn assign_config_timeouts(&mut self, config: &Config) {
453        self.front_timeout = Some(self.front_timeout.unwrap_or(config.front_timeout));
454        self.back_timeout = Some(self.back_timeout.unwrap_or(config.back_timeout));
455        self.connect_timeout = Some(self.connect_timeout.unwrap_or(config.connect_timeout));
456        self.request_timeout = Some(self.request_timeout.unwrap_or(config.request_timeout));
457    }
458
459    /// build an HTTP listener with config timeouts, using defaults if no config is provided
460    pub fn to_http(&mut self, config: Option<&Config>) -> Result<HttpListenerConfig, ConfigError> {
461        if self.protocol != Some(ListenerProtocol::Http) {
462            return Err(ConfigError::WrongListenerProtocol {
463                expected: ListenerProtocol::Http,
464                found: self.protocol.to_owned(),
465            });
466        }
467
468        if let Some(config) = config {
469            self.assign_config_timeouts(config);
470        }
471
472        let http_answers = self.get_http_answers()?;
473
474        let configuration = HttpListenerConfig {
475            address: self.address.into(),
476            public_address: self.public_address.map(|a| a.into()),
477            expect_proxy: self.expect_proxy.unwrap_or(false),
478            sticky_name: self.sticky_name.clone(),
479            front_timeout: self.front_timeout.unwrap_or(DEFAULT_FRONT_TIMEOUT),
480            back_timeout: self.back_timeout.unwrap_or(DEFAULT_BACK_TIMEOUT),
481            connect_timeout: self.connect_timeout.unwrap_or(DEFAULT_CONNECT_TIMEOUT),
482            request_timeout: self.request_timeout.unwrap_or(DEFAULT_REQUEST_TIMEOUT),
483            http_answers,
484            ..Default::default()
485        };
486
487        Ok(configuration)
488    }
489
490    /// build an HTTPS listener using defaults if no config or values were provided upstream
491    pub fn to_tls(&mut self, config: Option<&Config>) -> Result<HttpsListenerConfig, ConfigError> {
492        if self.protocol != Some(ListenerProtocol::Https) {
493            return Err(ConfigError::WrongListenerProtocol {
494                expected: ListenerProtocol::Https,
495                found: self.protocol.to_owned(),
496            });
497        }
498
499        let default_cipher_list = DEFAULT_RUSTLS_CIPHER_LIST
500            .into_iter()
501            .map(String::from)
502            .collect();
503
504        let cipher_list = self.cipher_list.clone().unwrap_or(default_cipher_list);
505
506        let default_cipher_suites = DEFAULT_CIPHER_SUITES
507            .into_iter()
508            .map(String::from)
509            .collect();
510
511        let cipher_suites = self.cipher_suites.clone().unwrap_or(default_cipher_suites);
512
513        let signature_algorithms: Vec<String> = DEFAULT_SIGNATURE_ALGORITHMS
514            .into_iter()
515            .map(String::from)
516            .collect();
517
518        let groups_list: Vec<String> = DEFAULT_GROUPS_LIST.into_iter().map(String::from).collect();
519
520        let versions = match self.tls_versions {
521            None => vec![TlsVersion::TlsV12 as i32, TlsVersion::TlsV13 as i32],
522            Some(ref v) => v.iter().map(|v| *v as i32).collect(),
523        };
524
525        let key = self.key.as_ref().and_then(|path| {
526            Config::load_file(path)
527                .map_err(|e| {
528                    error!("cannot load key at path '{}': {:?}", path, e);
529                    e
530                })
531                .ok()
532        });
533        let certificate = self.certificate.as_ref().and_then(|path| {
534            Config::load_file(path)
535                .map_err(|e| {
536                    error!("cannot load certificate at path '{}': {:?}", path, e);
537                    e
538                })
539                .ok()
540        });
541        let certificate_chain = self
542            .certificate_chain
543            .as_ref()
544            .and_then(|path| {
545                Config::load_file(path)
546                    .map_err(|e| {
547                        error!("cannot load certificate chain at path '{}': {:?}", path, e);
548                        e
549                    })
550                    .ok()
551            })
552            .map(split_certificate_chain)
553            .unwrap_or_default();
554
555        let http_answers = self.get_http_answers()?;
556
557        if let Some(config) = config {
558            self.assign_config_timeouts(config);
559        }
560
561        let https_listener_config = HttpsListenerConfig {
562            address: self.address.into(),
563            sticky_name: self.sticky_name.clone(),
564            public_address: self.public_address.map(|a| a.into()),
565            cipher_list,
566            versions,
567            expect_proxy: self.expect_proxy.unwrap_or(false),
568            key,
569            certificate,
570            certificate_chain,
571            front_timeout: self.front_timeout.unwrap_or(DEFAULT_FRONT_TIMEOUT),
572            back_timeout: self.back_timeout.unwrap_or(DEFAULT_BACK_TIMEOUT),
573            connect_timeout: self.connect_timeout.unwrap_or(DEFAULT_CONNECT_TIMEOUT),
574            request_timeout: self.request_timeout.unwrap_or(DEFAULT_REQUEST_TIMEOUT),
575            cipher_suites,
576            signature_algorithms,
577            groups_list,
578            active: false,
579            send_tls13_tickets: self
580                .send_tls13_tickets
581                .unwrap_or(DEFAULT_SEND_TLS_13_TICKETS),
582            http_answers,
583        };
584
585        Ok(https_listener_config)
586    }
587
588    /// build an HTTPS listener using defaults if no config or values were provided upstream
589    pub fn to_tcp(&mut self, config: Option<&Config>) -> Result<TcpListenerConfig, ConfigError> {
590        if self.protocol != Some(ListenerProtocol::Tcp) {
591            return Err(ConfigError::WrongListenerProtocol {
592                expected: ListenerProtocol::Tcp,
593                found: self.protocol.to_owned(),
594            });
595        }
596
597        if let Some(config) = config {
598            self.assign_config_timeouts(config);
599        }
600
601        Ok(TcpListenerConfig {
602            address: self.address.into(),
603            public_address: self.public_address.map(|a| a.into()),
604            expect_proxy: self.expect_proxy.unwrap_or(false),
605            front_timeout: self.front_timeout.unwrap_or(DEFAULT_FRONT_TIMEOUT),
606            back_timeout: self.back_timeout.unwrap_or(DEFAULT_BACK_TIMEOUT),
607            connect_timeout: self.connect_timeout.unwrap_or(DEFAULT_CONNECT_TIMEOUT),
608            active: false,
609        })
610    }
611}
612
613/// read a custom HTTP answer from a file
614fn read_http_answer_file(path: &Option<String>) -> Result<Option<String>, ConfigError> {
615    match path {
616        Some(path) => {
617            let mut content = String::new();
618            let mut file = File::open(path).map_err(|io_error| ConfigError::FileOpen {
619                path_to_open: path.to_owned(),
620                io_error,
621            })?;
622
623            file.read_to_string(&mut content)
624                .map_err(|io_error| ConfigError::FileRead {
625                    path_to_read: path.to_owned(),
626                    io_error,
627                })?;
628
629            Ok(Some(content))
630        }
631        None => Ok(None),
632    }
633}
634
635#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
636#[serde(deny_unknown_fields)]
637pub struct MetricsConfig {
638    pub address: SocketAddr,
639    #[serde(default)]
640    pub tagged_metrics: bool,
641    #[serde(default)]
642    pub prefix: Option<String>,
643}
644
645#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
646#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
647#[serde(deny_unknown_fields)]
648pub enum PathRuleType {
649    Prefix,
650    Regex,
651    Equals,
652}
653
654#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
655#[serde(deny_unknown_fields)]
656pub struct FileClusterFrontendConfig {
657    pub address: SocketAddr,
658    pub hostname: Option<String>,
659    /// creates a path routing rule where the request URL path has to match this
660    pub path: Option<String>,
661    /// declares whether the path rule is Prefix (default), Regex, or Equals
662    pub path_type: Option<PathRuleType>,
663    pub method: Option<String>,
664    pub certificate: Option<String>,
665    pub key: Option<String>,
666    pub certificate_chain: Option<String>,
667    #[serde(default)]
668    pub tls_versions: Vec<TlsVersion>,
669    #[serde(default)]
670    pub position: RulePosition,
671    pub tags: Option<BTreeMap<String, String>>,
672}
673
674impl FileClusterFrontendConfig {
675    pub fn to_tcp_front(&self) -> Result<TcpFrontendConfig, ConfigError> {
676        if self.hostname.is_some() {
677            return Err(ConfigError::InvalidFrontendConfig("hostname".to_string()));
678        }
679        if self.path.is_some() {
680            return Err(ConfigError::InvalidFrontendConfig(
681                "path_prefix".to_string(),
682            ));
683        }
684        if self.certificate.is_some() {
685            return Err(ConfigError::InvalidFrontendConfig(
686                "certificate".to_string(),
687            ));
688        }
689        if self.hostname.is_some() {
690            return Err(ConfigError::InvalidFrontendConfig("hostname".to_string()));
691        }
692        if self.certificate_chain.is_some() {
693            return Err(ConfigError::InvalidFrontendConfig(
694                "certificate_chain".to_string(),
695            ));
696        }
697
698        Ok(TcpFrontendConfig {
699            address: self.address,
700            tags: self.tags.clone(),
701        })
702    }
703
704    pub fn to_http_front(&self, _cluster_id: &str) -> Result<HttpFrontendConfig, ConfigError> {
705        let hostname = match &self.hostname {
706            Some(hostname) => hostname.to_owned(),
707            None => {
708                return Err(ConfigError::Missing(MissingKind::Field(
709                    "hostname".to_string(),
710                )));
711            }
712        };
713
714        let key_opt = match self.key.as_ref() {
715            None => None,
716            Some(path) => {
717                let key = Config::load_file(path)?;
718                Some(key)
719            }
720        };
721
722        let certificate_opt = match self.certificate.as_ref() {
723            None => None,
724            Some(path) => {
725                let certificate = Config::load_file(path)?;
726                Some(certificate)
727            }
728        };
729
730        let certificate_chain = match self.certificate_chain.as_ref() {
731            None => None,
732            Some(path) => {
733                let certificate_chain = Config::load_file(path)?;
734                Some(split_certificate_chain(certificate_chain))
735            }
736        };
737
738        let path = match (self.path.as_ref(), self.path_type.as_ref()) {
739            (None, _) => PathRule::prefix("".to_string()),
740            (Some(s), Some(PathRuleType::Prefix)) => PathRule::prefix(s.to_string()),
741            (Some(s), Some(PathRuleType::Regex)) => PathRule::regex(s.to_string()),
742            (Some(s), Some(PathRuleType::Equals)) => PathRule::equals(s.to_string()),
743            (Some(s), None) => PathRule::prefix(s.clone()),
744        };
745
746        Ok(HttpFrontendConfig {
747            address: self.address,
748            hostname,
749            certificate: certificate_opt,
750            key: key_opt,
751            certificate_chain,
752            tls_versions: self.tls_versions.clone(),
753            position: self.position,
754            path,
755            method: self.method.clone(),
756            tags: self.tags.clone(),
757        })
758    }
759}
760
761#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
762#[serde(deny_unknown_fields, rename_all = "lowercase")]
763pub enum ListenerProtocol {
764    Http,
765    Https,
766    Tcp,
767}
768
769#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
770#[serde(deny_unknown_fields, rename_all = "lowercase")]
771pub enum FileClusterProtocolConfig {
772    Http,
773    Tcp,
774}
775
776#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
777#[serde(deny_unknown_fields)]
778pub struct FileClusterConfig {
779    pub frontends: Vec<FileClusterFrontendConfig>,
780    pub backends: Vec<BackendConfig>,
781    pub protocol: FileClusterProtocolConfig,
782    pub sticky_session: Option<bool>,
783    pub https_redirect: Option<bool>,
784    #[serde(default)]
785    pub send_proxy: Option<bool>,
786    #[serde(default)]
787    pub load_balancing: LoadBalancingAlgorithms,
788    pub answer_503: Option<String>,
789    #[serde(default)]
790    pub load_metric: Option<LoadMetric>,
791}
792
793#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
794#[serde(deny_unknown_fields)]
795pub struct BackendConfig {
796    pub address: SocketAddr,
797    pub weight: Option<u8>,
798    pub sticky_id: Option<String>,
799    pub backup: Option<bool>,
800    pub backend_id: Option<String>,
801}
802
803impl FileClusterConfig {
804    pub fn to_cluster_config(
805        self,
806        cluster_id: &str,
807        expect_proxy: &HashSet<SocketAddr>,
808    ) -> Result<ClusterConfig, ConfigError> {
809        match self.protocol {
810            FileClusterProtocolConfig::Tcp => {
811                let mut has_expect_proxy = None;
812                let mut frontends = Vec::new();
813                for f in self.frontends {
814                    if expect_proxy.contains(&f.address) {
815                        match has_expect_proxy {
816                            Some(true) => {}
817                            Some(false) => {
818                                return Err(ConfigError::Incompatible {
819                                    object: ObjectKind::Cluster,
820                                    id: cluster_id.to_owned(),
821                                    kind: IncompatibilityKind::ProxyProtocol,
822                                });
823                            }
824                            None => has_expect_proxy = Some(true),
825                        }
826                    } else {
827                        match has_expect_proxy {
828                            Some(false) => {}
829                            Some(true) => {
830                                return Err(ConfigError::Incompatible {
831                                    object: ObjectKind::Cluster,
832                                    id: cluster_id.to_owned(),
833                                    kind: IncompatibilityKind::ProxyProtocol,
834                                });
835                            }
836                            None => has_expect_proxy = Some(false),
837                        }
838                    }
839                    let tcp_frontend = f.to_tcp_front()?;
840                    frontends.push(tcp_frontend);
841                }
842
843                let send_proxy = self.send_proxy.unwrap_or(false);
844                let expect_proxy = has_expect_proxy.unwrap_or(false);
845                let proxy_protocol = match (send_proxy, expect_proxy) {
846                    (true, true) => Some(ProxyProtocolConfig::RelayHeader),
847                    (true, false) => Some(ProxyProtocolConfig::SendHeader),
848                    (false, true) => Some(ProxyProtocolConfig::ExpectHeader),
849                    _ => None,
850                };
851
852                Ok(ClusterConfig::Tcp(TcpClusterConfig {
853                    cluster_id: cluster_id.to_string(),
854                    frontends,
855                    backends: self.backends,
856                    proxy_protocol,
857                    load_balancing: self.load_balancing,
858                    load_metric: self.load_metric,
859                }))
860            }
861            FileClusterProtocolConfig::Http => {
862                let mut frontends = Vec::new();
863                for frontend in self.frontends {
864                    let http_frontend = frontend.to_http_front(cluster_id)?;
865                    frontends.push(http_frontend);
866                }
867
868                let answer_503 = self.answer_503.as_ref().and_then(|path| {
869                    Config::load_file(path)
870                        .map_err(|e| {
871                            error!("cannot load 503 error page at path '{}': {:?}", path, e);
872                            e
873                        })
874                        .ok()
875                });
876
877                Ok(ClusterConfig::Http(HttpClusterConfig {
878                    cluster_id: cluster_id.to_string(),
879                    frontends,
880                    backends: self.backends,
881                    sticky_session: self.sticky_session.unwrap_or(false),
882                    https_redirect: self.https_redirect.unwrap_or(false),
883                    load_balancing: self.load_balancing,
884                    load_metric: self.load_metric,
885                    answer_503,
886                }))
887            }
888        }
889    }
890}
891
892#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
893#[serde(deny_unknown_fields)]
894pub struct HttpFrontendConfig {
895    pub address: SocketAddr,
896    pub hostname: String,
897    pub path: PathRule,
898    pub method: Option<String>,
899    pub certificate: Option<String>,
900    pub key: Option<String>,
901    pub certificate_chain: Option<Vec<String>>,
902    #[serde(default)]
903    pub tls_versions: Vec<TlsVersion>,
904    #[serde(default)]
905    pub position: RulePosition,
906    pub tags: Option<BTreeMap<String, String>>,
907}
908
909impl HttpFrontendConfig {
910    pub fn generate_requests(&self, cluster_id: &str) -> Vec<Request> {
911        let mut v = Vec::new();
912
913        let tags = self.tags.clone().unwrap_or_default();
914
915        if self.key.is_some() && self.certificate.is_some() {
916            v.push(
917                RequestType::AddCertificate(AddCertificate {
918                    address: self.address.into(),
919                    certificate: CertificateAndKey {
920                        key: self.key.clone().unwrap(),
921                        certificate: self.certificate.clone().unwrap(),
922                        certificate_chain: self.certificate_chain.clone().unwrap_or_default(),
923                        versions: self.tls_versions.iter().map(|v| *v as i32).collect(),
924                        // This field is used to override the certificate subject and san, we should not set it when
925                        // loading the configuration, as we may provide a wildcard certificate for a specific domain.
926                        // As a result, we will reject legit traffic for others domains as the certificate resolver will
927                        // not load twice the same certificate and then do not register the certificate for others domains.
928                        names: vec![],
929                    },
930                    expired_at: None,
931                })
932                .into(),
933            );
934
935            v.push(
936                RequestType::AddHttpsFrontend(RequestHttpFrontend {
937                    cluster_id: Some(cluster_id.to_string()),
938                    address: self.address.into(),
939                    hostname: self.hostname.clone(),
940                    path: self.path.clone(),
941                    method: self.method.clone(),
942                    position: self.position.into(),
943                    tags,
944                })
945                .into(),
946            );
947        } else {
948            //create the front both for HTTP and HTTPS if possible
949            v.push(
950                RequestType::AddHttpFrontend(RequestHttpFrontend {
951                    cluster_id: Some(cluster_id.to_string()),
952                    address: self.address.into(),
953                    hostname: self.hostname.clone(),
954                    path: self.path.clone(),
955                    method: self.method.clone(),
956                    position: self.position.into(),
957                    tags,
958                })
959                .into(),
960            );
961        }
962
963        v
964    }
965}
966
967#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
968#[serde(deny_unknown_fields)]
969pub struct HttpClusterConfig {
970    pub cluster_id: String,
971    pub frontends: Vec<HttpFrontendConfig>,
972    pub backends: Vec<BackendConfig>,
973    pub sticky_session: bool,
974    pub https_redirect: bool,
975    pub load_balancing: LoadBalancingAlgorithms,
976    pub load_metric: Option<LoadMetric>,
977    pub answer_503: Option<String>,
978}
979
980impl HttpClusterConfig {
981    pub fn generate_requests(&self) -> Result<Vec<Request>, ConfigError> {
982        let mut v = vec![
983            RequestType::AddCluster(Cluster {
984                cluster_id: self.cluster_id.clone(),
985                sticky_session: self.sticky_session,
986                https_redirect: self.https_redirect,
987                proxy_protocol: None,
988                load_balancing: self.load_balancing as i32,
989                answer_503: self.answer_503.clone(),
990                load_metric: self.load_metric.map(|s| s as i32),
991            })
992            .into(),
993        ];
994
995        for frontend in &self.frontends {
996            let mut orders = frontend.generate_requests(&self.cluster_id);
997            v.append(&mut orders);
998        }
999
1000        for (backend_count, backend) in self.backends.iter().enumerate() {
1001            let load_balancing_parameters = Some(LoadBalancingParams {
1002                weight: backend.weight.unwrap_or(100) as i32,
1003            });
1004
1005            v.push(
1006                RequestType::AddBackend(AddBackend {
1007                    cluster_id: self.cluster_id.clone(),
1008                    backend_id: backend.backend_id.clone().unwrap_or_else(|| {
1009                        format!("{}-{}-{}", self.cluster_id, backend_count, backend.address)
1010                    }),
1011                    address: backend.address.into(),
1012                    load_balancing_parameters,
1013                    sticky_id: backend.sticky_id.clone(),
1014                    backup: backend.backup,
1015                })
1016                .into(),
1017            );
1018        }
1019
1020        Ok(v)
1021    }
1022}
1023
1024#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
1025pub struct TcpFrontendConfig {
1026    pub address: SocketAddr,
1027    pub tags: Option<BTreeMap<String, String>>,
1028}
1029
1030#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
1031pub struct TcpClusterConfig {
1032    pub cluster_id: String,
1033    pub frontends: Vec<TcpFrontendConfig>,
1034    pub backends: Vec<BackendConfig>,
1035    #[serde(default)]
1036    pub proxy_protocol: Option<ProxyProtocolConfig>,
1037    pub load_balancing: LoadBalancingAlgorithms,
1038    pub load_metric: Option<LoadMetric>,
1039}
1040
1041impl TcpClusterConfig {
1042    pub fn generate_requests(&self) -> Result<Vec<Request>, ConfigError> {
1043        let mut v = vec![
1044            RequestType::AddCluster(Cluster {
1045                cluster_id: self.cluster_id.clone(),
1046                sticky_session: false,
1047                https_redirect: false,
1048                proxy_protocol: self.proxy_protocol.map(|s| s as i32),
1049                load_balancing: self.load_balancing as i32,
1050                load_metric: self.load_metric.map(|s| s as i32),
1051                answer_503: None,
1052            })
1053            .into(),
1054        ];
1055
1056        for frontend in &self.frontends {
1057            v.push(
1058                RequestType::AddTcpFrontend(RequestTcpFrontend {
1059                    cluster_id: self.cluster_id.clone(),
1060                    address: frontend.address.into(),
1061                    tags: frontend.tags.clone().unwrap_or(BTreeMap::new()),
1062                })
1063                .into(),
1064            );
1065        }
1066
1067        for (backend_count, backend) in self.backends.iter().enumerate() {
1068            let load_balancing_parameters = Some(LoadBalancingParams {
1069                weight: backend.weight.unwrap_or(100) as i32,
1070            });
1071
1072            v.push(
1073                RequestType::AddBackend(AddBackend {
1074                    cluster_id: self.cluster_id.clone(),
1075                    backend_id: backend.backend_id.clone().unwrap_or_else(|| {
1076                        format!("{}-{}-{}", self.cluster_id, backend_count, backend.address)
1077                    }),
1078                    address: backend.address.into(),
1079                    load_balancing_parameters,
1080                    sticky_id: backend.sticky_id.clone(),
1081                    backup: backend.backup,
1082                })
1083                .into(),
1084            );
1085        }
1086
1087        Ok(v)
1088    }
1089}
1090
1091#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
1092pub enum ClusterConfig {
1093    Http(HttpClusterConfig),
1094    Tcp(TcpClusterConfig),
1095}
1096
1097impl ClusterConfig {
1098    pub fn generate_requests(&self) -> Result<Vec<Request>, ConfigError> {
1099        match *self {
1100            ClusterConfig::Http(ref http) => http.generate_requests(),
1101            ClusterConfig::Tcp(ref tcp) => tcp.generate_requests(),
1102        }
1103    }
1104}
1105
1106/// Parsed from the TOML config provided by the user.
1107#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default, Deserialize)]
1108pub struct FileConfig {
1109    pub command_socket: Option<String>,
1110    pub command_buffer_size: Option<u64>,
1111    pub max_command_buffer_size: Option<u64>,
1112    pub max_connections: Option<usize>,
1113    pub min_buffers: Option<u64>,
1114    pub max_buffers: Option<u64>,
1115    pub buffer_size: Option<u64>,
1116    pub saved_state: Option<String>,
1117    #[serde(default)]
1118    pub automatic_state_save: Option<bool>,
1119    pub log_level: Option<String>,
1120    pub log_target: Option<String>,
1121    #[serde(default)]
1122    pub log_colored: bool,
1123    #[serde(default)]
1124    pub access_logs_target: Option<String>,
1125    #[serde(default)]
1126    pub access_logs_format: Option<AccessLogFormat>,
1127    #[serde(default)]
1128    pub access_logs_colored: Option<bool>,
1129    pub worker_count: Option<u16>,
1130    pub worker_automatic_restart: Option<bool>,
1131    pub metrics: Option<MetricsConfig>,
1132    pub disable_cluster_metrics: Option<bool>,
1133    pub listeners: Option<Vec<ListenerBuilder>>,
1134    pub clusters: Option<HashMap<String, FileClusterConfig>>,
1135    pub handle_process_affinity: Option<bool>,
1136    pub ctl_command_timeout: Option<u64>,
1137    pub pid_file_path: Option<String>,
1138    pub activate_listeners: Option<bool>,
1139    #[serde(default)]
1140    pub front_timeout: Option<u32>,
1141    #[serde(default)]
1142    pub back_timeout: Option<u32>,
1143    #[serde(default)]
1144    pub connect_timeout: Option<u32>,
1145    #[serde(default)]
1146    pub zombie_check_interval: Option<u32>,
1147    #[serde(default)]
1148    pub accept_queue_timeout: Option<u32>,
1149    #[serde(default)]
1150    pub request_timeout: Option<u32>,
1151    #[serde(default)]
1152    pub worker_timeout: Option<u32>,
1153}
1154
1155impl FileConfig {
1156    pub fn load_from_path(path: &str) -> Result<FileConfig, ConfigError> {
1157        let data = Config::load_file(path)?;
1158
1159        let config: FileConfig = match toml::from_str(&data) {
1160            Ok(config) => config,
1161            Err(e) => {
1162                display_toml_error(&data, &e);
1163                return Err(ConfigError::DeserializeToml(e.to_string()));
1164            }
1165        };
1166
1167        let mut reserved_address: HashSet<SocketAddr> = HashSet::new();
1168
1169        if let Some(listeners) = config.listeners.as_ref() {
1170            for listener in listeners.iter() {
1171                if reserved_address.contains(&listener.address) {
1172                    return Err(ConfigError::ListenerAddressAlreadyInUse(listener.address));
1173                }
1174                reserved_address.insert(listener.address);
1175            }
1176        }
1177
1178        //FIXME: verify how clusters and listeners share addresses
1179        /*
1180        if let Some(ref clusters) = config.clusters {
1181          for (key, cluster) in clusters.iter() {
1182            if let (Some(address), Some(port)) = (cluster.ip_address.clone(), cluster.port) {
1183              let addr = (address, port);
1184              if reserved_address.contains(&addr) {
1185                println!("TCP cluster '{}' listening address ( {}:{} ) is already used in the configuration",
1186                  key, addr.0, addr.1);
1187                return Err(Error::new(
1188                  ErrorKind::InvalidData,
1189                  format!("TCP cluster '{}' listening address ( {}:{} ) is already used in the configuration",
1190                    key, addr.0, addr.1)));
1191              } else {
1192                reserved_address.insert(addr.clone());
1193              }
1194            }
1195          }
1196        }
1197        */
1198
1199        Ok(config)
1200    }
1201}
1202
1203/// A builder that converts [FileConfig] to [Config]
1204pub struct ConfigBuilder {
1205    file: FileConfig,
1206    known_addresses: HashMap<SocketAddr, ListenerProtocol>,
1207    expect_proxy_addresses: HashSet<SocketAddr>,
1208    built: Config,
1209}
1210
1211impl ConfigBuilder {
1212    /// starts building a [Config] with values from a [FileConfig], or defaults.
1213    ///
1214    /// please provide a config path, usefull for rebuilding the config later.
1215    pub fn new<S>(file_config: FileConfig, config_path: S) -> Self
1216    where
1217        S: ToString,
1218    {
1219        let built = Config {
1220            accept_queue_timeout: file_config
1221                .accept_queue_timeout
1222                .unwrap_or(DEFAULT_ACCEPT_QUEUE_TIMEOUT),
1223            activate_listeners: file_config.activate_listeners.unwrap_or(true),
1224            automatic_state_save: file_config
1225                .automatic_state_save
1226                .unwrap_or(DEFAULT_AUTOMATIC_STATE_SAVE),
1227            back_timeout: file_config.back_timeout.unwrap_or(DEFAULT_BACK_TIMEOUT),
1228            buffer_size: file_config.buffer_size.unwrap_or(DEFAULT_BUFFER_SIZE),
1229            command_buffer_size: file_config
1230                .command_buffer_size
1231                .unwrap_or(DEFAULT_COMMAND_BUFFER_SIZE),
1232            config_path: config_path.to_string(),
1233            connect_timeout: file_config
1234                .connect_timeout
1235                .unwrap_or(DEFAULT_CONNECT_TIMEOUT),
1236            ctl_command_timeout: file_config.ctl_command_timeout.unwrap_or(1_000),
1237            front_timeout: file_config.front_timeout.unwrap_or(DEFAULT_FRONT_TIMEOUT),
1238            handle_process_affinity: file_config.handle_process_affinity.unwrap_or(false),
1239            access_logs_target: file_config.access_logs_target.clone(),
1240            access_logs_format: file_config.access_logs_format.clone(),
1241            access_logs_colored: file_config.access_logs_colored,
1242            log_level: file_config
1243                .log_level
1244                .clone()
1245                .unwrap_or_else(|| String::from("info")),
1246            log_target: file_config
1247                .log_target
1248                .clone()
1249                .unwrap_or_else(|| String::from("stdout")),
1250            log_colored: file_config.log_colored,
1251            max_buffers: file_config.max_buffers.unwrap_or(DEFAULT_MAX_BUFFERS),
1252            max_command_buffer_size: file_config
1253                .max_command_buffer_size
1254                .unwrap_or(DEFAULT_MAX_COMMAND_BUFFER_SIZE),
1255            max_connections: file_config
1256                .max_connections
1257                .unwrap_or(DEFAULT_MAX_CONNECTIONS),
1258            metrics: file_config.metrics.clone(),
1259            disable_cluster_metrics: file_config
1260                .disable_cluster_metrics
1261                .unwrap_or(DEFAULT_DISABLE_CLUSTER_METRICS),
1262            min_buffers: std::cmp::min(
1263                file_config.min_buffers.unwrap_or(DEFAULT_MIN_BUFFERS),
1264                file_config.max_buffers.unwrap_or(DEFAULT_MAX_BUFFERS),
1265            ),
1266            pid_file_path: file_config.pid_file_path.clone(),
1267            request_timeout: file_config
1268                .request_timeout
1269                .unwrap_or(DEFAULT_REQUEST_TIMEOUT),
1270            saved_state: file_config.saved_state.clone(),
1271            worker_automatic_restart: file_config
1272                .worker_automatic_restart
1273                .unwrap_or(DEFAULT_WORKER_AUTOMATIC_RESTART),
1274            worker_count: file_config.worker_count.unwrap_or(DEFAULT_WORKER_COUNT),
1275            zombie_check_interval: file_config
1276                .zombie_check_interval
1277                .unwrap_or(DEFAULT_ZOMBIE_CHECK_INTERVAL),
1278            worker_timeout: file_config.worker_timeout.unwrap_or(DEFAULT_WORKER_TIMEOUT),
1279            ..Default::default()
1280        };
1281
1282        Self {
1283            file: file_config,
1284            known_addresses: HashMap::new(),
1285            expect_proxy_addresses: HashSet::new(),
1286            built,
1287        }
1288    }
1289
1290    fn push_tls_listener(&mut self, mut listener: ListenerBuilder) -> Result<(), ConfigError> {
1291        let listener = listener.to_tls(Some(&self.built))?;
1292        self.built.https_listeners.push(listener);
1293        Ok(())
1294    }
1295
1296    fn push_http_listener(&mut self, mut listener: ListenerBuilder) -> Result<(), ConfigError> {
1297        let listener = listener.to_http(Some(&self.built))?;
1298        self.built.http_listeners.push(listener);
1299        Ok(())
1300    }
1301
1302    fn push_tcp_listener(&mut self, mut listener: ListenerBuilder) -> Result<(), ConfigError> {
1303        let listener = listener.to_tcp(Some(&self.built))?;
1304        self.built.tcp_listeners.push(listener);
1305        Ok(())
1306    }
1307
1308    fn populate_listeners(&mut self, listeners: Vec<ListenerBuilder>) -> Result<(), ConfigError> {
1309        for listener in listeners.iter() {
1310            if self.known_addresses.contains_key(&listener.address) {
1311                return Err(ConfigError::ListenerAddressAlreadyInUse(listener.address));
1312            }
1313
1314            let protocol = listener
1315                .protocol
1316                .ok_or(ConfigError::Missing(MissingKind::Protocol))?;
1317
1318            self.known_addresses.insert(listener.address, protocol);
1319            if listener.expect_proxy == Some(true) {
1320                self.expect_proxy_addresses.insert(listener.address);
1321            }
1322
1323            if listener.public_address.is_some() && listener.expect_proxy == Some(true) {
1324                return Err(ConfigError::Incompatible {
1325                    object: ObjectKind::Listener,
1326                    id: listener.address.to_string(),
1327                    kind: IncompatibilityKind::PublicAddress,
1328                });
1329            }
1330
1331            match protocol {
1332                ListenerProtocol::Https => self.push_tls_listener(listener.clone())?,
1333                ListenerProtocol::Http => self.push_http_listener(listener.clone())?,
1334                ListenerProtocol::Tcp => self.push_tcp_listener(listener.clone())?,
1335            }
1336        }
1337        Ok(())
1338    }
1339
1340    fn populate_clusters(
1341        &mut self,
1342        mut file_cluster_configs: HashMap<String, FileClusterConfig>,
1343    ) -> Result<(), ConfigError> {
1344        for (id, file_cluster_config) in file_cluster_configs.drain() {
1345            let mut cluster_config =
1346                file_cluster_config.to_cluster_config(id.as_str(), &self.expect_proxy_addresses)?;
1347
1348            match cluster_config {
1349                ClusterConfig::Http(ref mut http) => {
1350                    for frontend in http.frontends.iter_mut() {
1351                        match self.known_addresses.get(&frontend.address) {
1352                            Some(ListenerProtocol::Tcp) => {
1353                                return Err(ConfigError::WrongFrontendProtocol(
1354                                    ListenerProtocol::Tcp,
1355                                ));
1356                            }
1357                            Some(ListenerProtocol::Http) => {
1358                                if frontend.certificate.is_some() {
1359                                    return Err(ConfigError::WrongFrontendProtocol(
1360                                        ListenerProtocol::Http,
1361                                    ));
1362                                }
1363                            }
1364                            Some(ListenerProtocol::Https) => {
1365                                if frontend.certificate.is_none() {
1366                                    if let Some(https_listener) =
1367                                        self.built.https_listeners.iter().find(|listener| {
1368                                            listener.address == frontend.address.into()
1369                                                && listener.certificate.is_some()
1370                                        })
1371                                    {
1372                                        //println!("using listener certificate for {:}", frontend.address);
1373                                        frontend
1374                                            .certificate
1375                                            .clone_from(&https_listener.certificate);
1376                                        frontend.certificate_chain =
1377                                            Some(https_listener.certificate_chain.clone());
1378                                        frontend.key.clone_from(&https_listener.key);
1379                                    }
1380                                    if frontend.certificate.is_none() {
1381                                        debug!("known addresses: {:?}", self.known_addresses);
1382                                        debug!("frontend: {:?}", frontend);
1383                                        return Err(ConfigError::WrongFrontendProtocol(
1384                                            ListenerProtocol::Https,
1385                                        ));
1386                                    }
1387                                }
1388                            }
1389                            None => {
1390                                // create a default listener for that front
1391                                let file_listener_protocol = if frontend.certificate.is_some() {
1392                                    self.push_tls_listener(ListenerBuilder::new(
1393                                        frontend.address.into(),
1394                                        ListenerProtocol::Https,
1395                                    ))?;
1396
1397                                    ListenerProtocol::Https
1398                                } else {
1399                                    self.push_http_listener(ListenerBuilder::new(
1400                                        frontend.address.into(),
1401                                        ListenerProtocol::Http,
1402                                    ))?;
1403
1404                                    ListenerProtocol::Http
1405                                };
1406                                self.known_addresses
1407                                    .insert(frontend.address, file_listener_protocol);
1408                            }
1409                        }
1410                    }
1411                }
1412                ClusterConfig::Tcp(ref tcp) => {
1413                    //FIXME: verify that different TCP clusters do not request the same address
1414                    for frontend in &tcp.frontends {
1415                        match self.known_addresses.get(&frontend.address) {
1416                            Some(ListenerProtocol::Http) | Some(ListenerProtocol::Https) => {
1417                                return Err(ConfigError::WrongFrontendProtocol(
1418                                    ListenerProtocol::Http,
1419                                ));
1420                            }
1421                            Some(ListenerProtocol::Tcp) => {}
1422                            None => {
1423                                // create a default listener for that front
1424                                self.push_tcp_listener(ListenerBuilder::new(
1425                                    frontend.address.into(),
1426                                    ListenerProtocol::Tcp,
1427                                ))?;
1428                                self.known_addresses
1429                                    .insert(frontend.address, ListenerProtocol::Tcp);
1430                            }
1431                        }
1432                    }
1433                }
1434            }
1435
1436            self.built.clusters.insert(id, cluster_config);
1437        }
1438        Ok(())
1439    }
1440
1441    /// Builds a [`Config`], populated with listeners and clusters
1442    pub fn into_config(&mut self) -> Result<Config, ConfigError> {
1443        if let Some(listeners) = &self.file.listeners {
1444            self.populate_listeners(listeners.clone())?;
1445        }
1446
1447        if let Some(file_cluster_configs) = &self.file.clusters {
1448            self.populate_clusters(file_cluster_configs.clone())?;
1449        }
1450
1451        let command_socket_path = self.file.command_socket.clone().unwrap_or({
1452            let mut path = env::current_dir().map_err(|e| ConfigError::Env(e.to_string()))?;
1453            path.push("sozu.sock");
1454            let verified_path = path
1455                .to_str()
1456                .ok_or(ConfigError::InvalidPath(path.clone()))?;
1457            verified_path.to_owned()
1458        });
1459
1460        if let (None, Some(true)) = (&self.file.saved_state, &self.file.automatic_state_save) {
1461            return Err(ConfigError::Missing(MissingKind::SavedState));
1462        }
1463
1464        Ok(Config {
1465            command_socket: command_socket_path,
1466            ..self.built.clone()
1467        })
1468    }
1469}
1470
1471/// Sōzu configuration, populated with clusters and listeners.
1472///
1473/// This struct is used on startup to generate `WorkerRequest`s
1474#[derive(Clone, PartialEq, Eq, Serialize, Default, Deserialize)]
1475pub struct Config {
1476    pub config_path: String,
1477    pub command_socket: String,
1478    pub command_buffer_size: u64,
1479    pub max_command_buffer_size: u64,
1480    pub max_connections: usize,
1481    pub min_buffers: u64,
1482    pub max_buffers: u64,
1483    pub buffer_size: u64,
1484    pub saved_state: Option<String>,
1485    #[serde(default)]
1486    pub automatic_state_save: bool,
1487    pub log_level: String,
1488    pub log_target: String,
1489    pub log_colored: bool,
1490    #[serde(default)]
1491    pub access_logs_target: Option<String>,
1492    pub access_logs_format: Option<AccessLogFormat>,
1493    pub access_logs_colored: Option<bool>,
1494    pub worker_count: u16,
1495    pub worker_automatic_restart: bool,
1496    pub metrics: Option<MetricsConfig>,
1497    #[serde(default = "default_disable_cluster_metrics")]
1498    pub disable_cluster_metrics: bool,
1499    pub http_listeners: Vec<HttpListenerConfig>,
1500    pub https_listeners: Vec<HttpsListenerConfig>,
1501    pub tcp_listeners: Vec<TcpListenerConfig>,
1502    pub clusters: HashMap<String, ClusterConfig>,
1503    pub handle_process_affinity: bool,
1504    pub ctl_command_timeout: u64,
1505    pub pid_file_path: Option<String>,
1506    pub activate_listeners: bool,
1507    #[serde(default = "default_front_timeout")]
1508    pub front_timeout: u32,
1509    #[serde(default = "default_back_timeout")]
1510    pub back_timeout: u32,
1511    #[serde(default = "default_connect_timeout")]
1512    pub connect_timeout: u32,
1513    #[serde(default = "default_zombie_check_interval")]
1514    pub zombie_check_interval: u32,
1515    #[serde(default = "default_accept_queue_timeout")]
1516    pub accept_queue_timeout: u32,
1517    #[serde(default = "default_request_timeout")]
1518    pub request_timeout: u32,
1519    #[serde(default = "default_worker_timeout")]
1520    pub worker_timeout: u32,
1521}
1522
1523fn default_front_timeout() -> u32 {
1524    DEFAULT_FRONT_TIMEOUT
1525}
1526
1527fn default_back_timeout() -> u32 {
1528    DEFAULT_BACK_TIMEOUT
1529}
1530
1531fn default_connect_timeout() -> u32 {
1532    DEFAULT_CONNECT_TIMEOUT
1533}
1534
1535fn default_request_timeout() -> u32 {
1536    DEFAULT_REQUEST_TIMEOUT
1537}
1538
1539fn default_zombie_check_interval() -> u32 {
1540    DEFAULT_ZOMBIE_CHECK_INTERVAL
1541}
1542
1543fn default_accept_queue_timeout() -> u32 {
1544    DEFAULT_ACCEPT_QUEUE_TIMEOUT
1545}
1546
1547fn default_disable_cluster_metrics() -> bool {
1548    DEFAULT_DISABLE_CLUSTER_METRICS
1549}
1550
1551fn default_worker_timeout() -> u32 {
1552    DEFAULT_WORKER_TIMEOUT
1553}
1554
1555impl Config {
1556    /// Parse a TOML file and build a config out of it
1557    pub fn load_from_path(path: &str) -> Result<Config, ConfigError> {
1558        let file_config = FileConfig::load_from_path(path)?;
1559
1560        let mut config = ConfigBuilder::new(file_config, path).into_config()?;
1561
1562        // replace saved_state with a verified path
1563        config.saved_state = config.saved_state_path()?;
1564
1565        Ok(config)
1566    }
1567
1568    /// yields requests intended to recreate a proxy that match the config
1569    pub fn generate_config_messages(&self) -> Result<Vec<WorkerRequest>, ConfigError> {
1570        let mut v = Vec::new();
1571        let mut count = 0u8;
1572
1573        for listener in &self.http_listeners {
1574            v.push(WorkerRequest {
1575                id: format!("CONFIG-{count}"),
1576                content: RequestType::AddHttpListener(listener.clone()).into(),
1577            });
1578            count += 1;
1579        }
1580
1581        for listener in &self.https_listeners {
1582            v.push(WorkerRequest {
1583                id: format!("CONFIG-{count}"),
1584                content: RequestType::AddHttpsListener(listener.clone()).into(),
1585            });
1586            count += 1;
1587        }
1588
1589        for listener in &self.tcp_listeners {
1590            v.push(WorkerRequest {
1591                id: format!("CONFIG-{count}"),
1592                content: RequestType::AddTcpListener(*listener).into(),
1593            });
1594            count += 1;
1595        }
1596
1597        for cluster in self.clusters.values() {
1598            let mut orders = cluster.generate_requests()?;
1599            for content in orders.drain(..) {
1600                v.push(WorkerRequest {
1601                    id: format!("CONFIG-{count}"),
1602                    content,
1603                });
1604                count += 1;
1605            }
1606        }
1607
1608        if self.activate_listeners {
1609            for listener in &self.http_listeners {
1610                v.push(WorkerRequest {
1611                    id: format!("CONFIG-{count}"),
1612                    content: RequestType::ActivateListener(ActivateListener {
1613                        address: listener.address,
1614                        proxy: ListenerType::Http.into(),
1615                        from_scm: false,
1616                    })
1617                    .into(),
1618                });
1619                count += 1;
1620            }
1621
1622            for listener in &self.https_listeners {
1623                v.push(WorkerRequest {
1624                    id: format!("CONFIG-{count}"),
1625                    content: RequestType::ActivateListener(ActivateListener {
1626                        address: listener.address,
1627                        proxy: ListenerType::Https.into(),
1628                        from_scm: false,
1629                    })
1630                    .into(),
1631                });
1632                count += 1;
1633            }
1634
1635            for listener in &self.tcp_listeners {
1636                v.push(WorkerRequest {
1637                    id: format!("CONFIG-{count}"),
1638                    content: RequestType::ActivateListener(ActivateListener {
1639                        address: listener.address,
1640                        proxy: ListenerType::Tcp.into(),
1641                        from_scm: false,
1642                    })
1643                    .into(),
1644                });
1645                count += 1;
1646            }
1647        }
1648
1649        if self.disable_cluster_metrics {
1650            v.push(WorkerRequest {
1651                id: format!("CONFIG-{count}"),
1652                content: RequestType::ConfigureMetrics(MetricsConfiguration::Disabled.into())
1653                    .into(),
1654            });
1655            // count += 1; // uncomment if code is added below
1656        }
1657
1658        Ok(v)
1659    }
1660
1661    /// Get the path of the UNIX socket used to communicate with Sōzu
1662    pub fn command_socket_path(&self) -> Result<String, ConfigError> {
1663        let config_path_buf = PathBuf::from(self.config_path.clone());
1664        let mut config_dir = config_path_buf
1665            .parent()
1666            .ok_or(ConfigError::NoFileParent(
1667                config_path_buf.to_string_lossy().to_string(),
1668            ))?
1669            .to_path_buf();
1670
1671        let socket_path = PathBuf::from(self.command_socket.clone());
1672
1673        let mut socket_parent_dir = match socket_path.parent() {
1674            // if the socket path is of the form "./sozu.sock",
1675            // then the parent is the directory where config.toml is situated
1676            None => config_dir,
1677            Some(path) => {
1678                // concatenate the config directory and the relative path of the socket
1679                config_dir.push(path);
1680                // canonicalize to remove double dots like /path/to/config/directory/../../path/to/socket/directory/
1681                config_dir.canonicalize().map_err(|io_error| {
1682                    ConfigError::SocketPathError(format!(
1683                        "Could not canonicalize path {config_dir:?}: {io_error}"
1684                    ))
1685                })?
1686            }
1687        };
1688
1689        let socket_name = socket_path
1690            .file_name()
1691            .ok_or(ConfigError::SocketPathError(format!(
1692                "could not get command socket file name from {socket_path:?}"
1693            )))?;
1694
1695        // concatenate parent directory and socket file name
1696        socket_parent_dir.push(socket_name);
1697
1698        let command_socket_path = socket_parent_dir
1699            .to_str()
1700            .ok_or(ConfigError::SocketPathError(format!(
1701                "Invalid socket path {socket_parent_dir:?}"
1702            )))?
1703            .to_string();
1704
1705        Ok(command_socket_path)
1706    }
1707
1708    /// Get the path of where the state will be saved
1709    fn saved_state_path(&self) -> Result<Option<String>, ConfigError> {
1710        let path = match self.saved_state.as_ref() {
1711            Some(path) => path,
1712            None => return Ok(None),
1713        };
1714
1715        debug!("saved_stated path in the config: {}", path);
1716        let config_path = PathBuf::from(self.config_path.clone());
1717
1718        debug!("Config path buffer: {:?}", config_path);
1719        let config_dir = config_path
1720            .parent()
1721            .ok_or(ConfigError::SaveStatePath(format!(
1722                "Could get parent directory of config file {config_path:?}"
1723            )))?;
1724
1725        debug!("Config folder: {:?}", config_dir);
1726        if !config_dir.exists() {
1727            create_dir_all(config_dir).map_err(|io_error| {
1728                ConfigError::SaveStatePath(format!(
1729                    "failed to create state parent directory '{config_dir:?}': {io_error}"
1730                ))
1731            })?;
1732        }
1733
1734        let mut saved_state_path_raw = config_dir.to_path_buf();
1735        saved_state_path_raw.push(path);
1736        debug!(
1737            "Looking for saved state on the path {:?}",
1738            saved_state_path_raw
1739        );
1740
1741        match metadata(path) {
1742            Err(err) if matches!(err.kind(), ErrorKind::NotFound) => {
1743                info!("Create an empty state file at '{}'", path);
1744                File::create(path).map_err(|io_error| {
1745                    ConfigError::SaveStatePath(format!(
1746                        "failed to create state file '{path:?}': {io_error}"
1747                    ))
1748                })?;
1749            }
1750            _ => {}
1751        }
1752
1753        saved_state_path_raw.canonicalize().map_err(|io_error| {
1754            ConfigError::SaveStatePath(format!(
1755                "could not get saved state path from config file input {path:?}: {io_error}"
1756            ))
1757        })?;
1758
1759        let stringified_path = saved_state_path_raw
1760            .to_str()
1761            .ok_or(ConfigError::SaveStatePath(format!(
1762                "Invalid path {saved_state_path_raw:?}"
1763            )))?
1764            .to_string();
1765
1766        Ok(Some(stringified_path))
1767    }
1768
1769    /// read any file to a string
1770    pub fn load_file(path: &str) -> Result<String, ConfigError> {
1771        std::fs::read_to_string(path).map_err(|io_error| ConfigError::FileRead {
1772            path_to_read: path.to_owned(),
1773            io_error,
1774        })
1775    }
1776
1777    /// read any file to bytes
1778    pub fn load_file_bytes(path: &str) -> Result<Vec<u8>, ConfigError> {
1779        std::fs::read(path).map_err(|io_error| ConfigError::FileRead {
1780            path_to_read: path.to_owned(),
1781            io_error,
1782        })
1783    }
1784}
1785
1786impl fmt::Debug for Config {
1787    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1788        f.debug_struct("Config")
1789            .field("config_path", &self.config_path)
1790            .field("command_socket", &self.command_socket)
1791            .field("command_buffer_size", &self.command_buffer_size)
1792            .field("max_command_buffer_size", &self.max_command_buffer_size)
1793            .field("max_connections", &self.max_connections)
1794            .field("min_buffers", &self.min_buffers)
1795            .field("max_buffers", &self.max_buffers)
1796            .field("buffer_size", &self.buffer_size)
1797            .field("saved_state", &self.saved_state)
1798            .field("automatic_state_save", &self.automatic_state_save)
1799            .field("log_level", &self.log_level)
1800            .field("log_target", &self.log_target)
1801            .field("access_logs_target", &self.access_logs_target)
1802            .field("access_logs_format", &self.access_logs_format)
1803            .field("worker_count", &self.worker_count)
1804            .field("worker_automatic_restart", &self.worker_automatic_restart)
1805            .field("metrics", &self.metrics)
1806            .field("disable_cluster_metrics", &self.disable_cluster_metrics)
1807            .field("handle_process_affinity", &self.handle_process_affinity)
1808            .field("ctl_command_timeout", &self.ctl_command_timeout)
1809            .field("pid_file_path", &self.pid_file_path)
1810            .field("activate_listeners", &self.activate_listeners)
1811            .field("front_timeout", &self.front_timeout)
1812            .field("back_timeout", &self.back_timeout)
1813            .field("connect_timeout", &self.connect_timeout)
1814            .field("zombie_check_interval", &self.zombie_check_interval)
1815            .field("accept_queue_timeout", &self.accept_queue_timeout)
1816            .field("request_timeout", &self.request_timeout)
1817            .field("worker_timeout", &self.worker_timeout)
1818            .finish()
1819    }
1820}
1821
1822fn display_toml_error(file: &str, error: &toml::de::Error) {
1823    println!("error parsing the configuration file '{file}': {error}");
1824    if let Some(Range { start, end }) = error.span() {
1825        print!("error parsing the configuration file '{file}' at position: {start}, {end}");
1826    }
1827}
1828
1829impl ServerConfig {
1830    /// size of the slab for the Session manager
1831    pub fn slab_capacity(&self) -> u64 {
1832        10 + 2 * self.max_connections
1833    }
1834}
1835
1836/// reduce the config to the bare minimum needed by a worker
1837impl From<&Config> for ServerConfig {
1838    fn from(config: &Config) -> Self {
1839        let metrics = config.metrics.clone().map(|m| ServerMetricsConfig {
1840            address: m.address.to_string(),
1841            tagged_metrics: m.tagged_metrics,
1842            prefix: m.prefix,
1843        });
1844        Self {
1845            max_connections: config.max_connections as u64,
1846            front_timeout: config.front_timeout,
1847            back_timeout: config.back_timeout,
1848            connect_timeout: config.connect_timeout,
1849            zombie_check_interval: config.zombie_check_interval,
1850            accept_queue_timeout: config.accept_queue_timeout,
1851            min_buffers: config.min_buffers,
1852            max_buffers: config.max_buffers,
1853            buffer_size: config.buffer_size,
1854            log_level: config.log_level.clone(),
1855            log_target: config.log_target.clone(),
1856            access_logs_target: config.access_logs_target.clone(),
1857            command_buffer_size: config.command_buffer_size,
1858            max_command_buffer_size: config.max_command_buffer_size,
1859            metrics,
1860            access_log_format: ProtobufAccessLogFormat::from(&config.access_logs_format) as i32,
1861            log_colored: config.log_colored,
1862        }
1863    }
1864}
1865
1866#[cfg(test)]
1867mod tests {
1868    use toml::to_string;
1869
1870    use super::*;
1871
1872    #[test]
1873    fn serialize() {
1874        let http = ListenerBuilder::new(
1875            SocketAddress::new_v4(127, 0, 0, 1, 8080),
1876            ListenerProtocol::Http,
1877        )
1878        .with_answer_404_path(Some("404.html"))
1879        .to_owned();
1880        println!("http: {:?}", to_string(&http));
1881
1882        let https = ListenerBuilder::new(
1883            SocketAddress::new_v4(127, 0, 0, 1, 8443),
1884            ListenerProtocol::Https,
1885        )
1886        .with_answer_404_path(Some("404.html"))
1887        .to_owned();
1888        println!("https: {:?}", to_string(&https));
1889
1890        let listeners = vec![http, https];
1891        let config = FileConfig {
1892            command_socket: Some(String::from("./command_folder/sock")),
1893            worker_count: Some(2),
1894            worker_automatic_restart: Some(true),
1895            max_connections: Some(500),
1896            min_buffers: Some(1),
1897            max_buffers: Some(500),
1898            buffer_size: Some(16393),
1899            metrics: Some(MetricsConfig {
1900                address: "127.0.0.1:8125".parse().unwrap(),
1901                tagged_metrics: false,
1902                prefix: Some(String::from("sozu-metrics")),
1903            }),
1904            listeners: Some(listeners),
1905            ..Default::default()
1906        };
1907
1908        println!("config: {:?}", to_string(&config));
1909        let encoded = to_string(&config).unwrap();
1910        println!("conf:\n{encoded}");
1911    }
1912
1913    #[test]
1914    fn parse() {
1915        let path = "assets/config.toml";
1916        let config = Config::load_from_path(path).unwrap_or_else(|load_error| {
1917            panic!("Cannot load config from path {path}: {load_error:?}")
1918        });
1919        println!("config: {config:#?}");
1920        //panic!();
1921    }
1922}