sbd_server/
config.rs

1#[cfg(feature = "unstable")]
2const DEF_IP_DENY_DIR: &str = ".";
3#[cfg(feature = "unstable")]
4const DEF_IP_DENY_S: i32 = 600;
5const DEF_LIMIT_CLIENTS: i32 = 32768;
6const DEF_LIMIT_IP_KBPS: i32 = 1000;
7const DEF_LIMIT_IP_BYTE_BURST: i32 = 16 * 16 * 1024;
8const DEF_LIMIT_IDLE_MILLIS: i32 = 10_000;
9
10/// Configure and execute an SBD server.
11#[derive(clap::Parser, Debug, Clone)]
12#[command(version, styles=get_styles())]
13pub struct Config {
14    /// TLS certificate path (pem).
15    /// If specified, `--priv-key-pem-file` must also be specified.
16    /// It is recommended to run acme service on port 80 and only
17    /// bind SBD to port 443.
18    #[arg(long)]
19    pub cert_pem_file: Option<std::path::PathBuf>,
20
21    /// TLS private key path (pem).
22    /// If specified, `--cert-pem-file` must also be specified.
23    /// It is recommended to run acme service on port 80 and only
24    /// bind SBD to port 443.
25    #[arg(long)]
26    pub priv_key_pem_file: Option<std::path::PathBuf>,
27
28    /// Bind to this interface and port. If multiple bindings specify port
29    /// zero, the server will attempt to bind the same port to each interface.
30    /// If it cannot, it will allow all the ports to be different.
31    /// Can be specified more than once.
32    /// E.g. `--bind 127.0.0.1:0 --bind [::1]:0 --bind 192.168.0.10:443`.
33    #[arg(long)]
34    pub bind: Vec<String>,
35
36    #[cfg(feature = "unstable")]
37    /// Watch this directory, and reload TLS certificates 10s after any
38    /// files change within it. Must be an exact match to the parent directory
39    /// of both `--cert-pem-file` and `--priv-key-pem-file`.
40    #[arg(long)]
41    pub watch_reload_tls_dir: Option<std::path::PathBuf>,
42
43    /// Use this http header to determine IP address instead of the raw
44    /// TCP connection details.
45    #[arg(long)]
46    pub trusted_ip_header: Option<String>,
47
48    #[cfg(feature = "unstable")]
49    /// The directory in which to store the blocked ip addresses.
50    /// Note v4 addresses will be mapped to v6 addresses per
51    /// <https://datatracker.ietf.org/doc/html/rfc4291#section-2.5.5.2>.
52    #[arg(long, default_value = DEF_IP_DENY_DIR)]
53    pub ip_deny_dir: std::path::PathBuf,
54
55    #[cfg(feature = "unstable")]
56    /// How long to block ip addresses in seconds. Set to zero to block
57    /// forever (or until the file is manually deleted).
58    #[arg(long, default_value_t = DEF_IP_DENY_S)]
59    pub ip_deny_s: i32,
60
61    #[cfg(feature = "unstable")]
62    /// Bind to this backchannel interface and port.
63    /// Can be specified more than once.
64    /// Note, this should be a local only or virtual private interface.
65    #[arg(long)]
66    pub back_bind: Vec<String>,
67
68    #[cfg(feature = "unstable")]
69    /// Allow incoming backchannel connections only
70    /// from the following explicit addresses. Note, this is expecting direct
71    /// connections, not through a proxy, so only the raw TCP address will
72    /// be validated. (This ignores the --trusted-ip-header parameter).
73    /// Can be specified more than once.
74    /// E.g. `--back-allow-ip 192.168.0.2 --back-allow-ip 192.168.0.3`.
75    #[arg(long)]
76    pub back_allow_ip: Vec<String>,
77
78    #[cfg(feature = "unstable")]
79    /// Try to establish outgoing backchannel connections
80    /// to the following ip+port addresses.
81    /// Can be specified more than once.
82    /// E.g. `--back-open 192.168.0.3:1443`
83    #[arg(long)]
84    pub back_open: Vec<String>,
85
86    #[cfg(feature = "unstable")]
87    /// Bind to this interface and port to provide prometheus metrics.
88    /// Note, this should be a local only or virtual private interface.
89    #[arg(long)]
90    pub bind_prometheus: Option<String>,
91
92    /// Limit client connections.
93    #[arg(long, default_value_t = DEF_LIMIT_CLIENTS)]
94    pub limit_clients: i32,
95
96    /// If set, rate-limiting will be disabled on the server,
97    /// and clients will be informed they have an 8gbps rate limit.
98    #[arg(long)]
99    pub disable_rate_limiting: bool,
100
101    /// Rate limit connections to this kilobits per second.
102    /// The default value of 1000 obviously limits connections to 1 mbps.
103    /// If the default of 32768 connections were all sending this amount
104    /// at the same time, the server would need a ~33 gbps connection.
105    /// The rate limit passed to clients will be divided by the number
106    /// of open connections for a given ip address.
107    #[arg(long, default_value_t = DEF_LIMIT_IP_KBPS)]
108    pub limit_ip_kbps: i32,
109
110    /// Allow IPs to burst by this byte count.
111    /// If the max message size is 16K, this value must be at least 16K.
112    /// The default value provides 16 * 16K to allow for multiple connections
113    /// from a single ip address sending full messages at the same time.
114    #[arg(long, default_value_t = DEF_LIMIT_IP_BYTE_BURST)]
115    pub limit_ip_byte_burst: i32,
116
117    /// How long in milliseconds connections can remain idle before being
118    /// closed. Clients must send either a message or a keepalive before
119    /// this time expires to keep the connection alive.
120    #[arg(long, default_value_t = DEF_LIMIT_IDLE_MILLIS)]
121    pub limit_idle_millis: i32,
122
123    /// The authentication "Hook Server" as defined by
124    /// <https://github.com/holochain/sbd/blob/main/spec-auth.md>
125    #[arg(long)]
126    pub authentication_hook_server: Option<String>,
127
128    /// If specified, this will enable exporting metrics to an OpenTelemetry endpoint.
129    #[arg(long)]
130    pub otlp_endpoint: Option<String>,
131}
132
133impl Default for Config {
134    /// Construct a new config with some defaults set.
135    fn default() -> Self {
136        Self {
137            cert_pem_file: None,
138            priv_key_pem_file: None,
139            bind: Vec::new(),
140            #[cfg(feature = "unstable")]
141            watch_reload_tls_dir: None,
142            trusted_ip_header: None,
143            #[cfg(feature = "unstable")]
144            ip_deny_dir: std::path::PathBuf::from(DEF_IP_DENY_DIR),
145            #[cfg(feature = "unstable")]
146            ip_deny_s: DEF_IP_DENY_S,
147            #[cfg(feature = "unstable")]
148            back_bind: Vec::new(),
149            #[cfg(feature = "unstable")]
150            back_allow_ip: Vec::new(),
151            #[cfg(feature = "unstable")]
152            back_open: Vec::new(),
153            #[cfg(feature = "unstable")]
154            bind_prometheus: None,
155            limit_clients: DEF_LIMIT_CLIENTS,
156            disable_rate_limiting: false,
157            limit_ip_kbps: DEF_LIMIT_IP_KBPS,
158            limit_ip_byte_burst: DEF_LIMIT_IP_BYTE_BURST,
159            limit_idle_millis: DEF_LIMIT_IDLE_MILLIS,
160            authentication_hook_server: None,
161            otlp_endpoint: None,
162        }
163    }
164}
165
166impl Config {
167    pub(crate) fn idle_dur(&self) -> std::time::Duration {
168        std::time::Duration::from_millis(self.limit_idle_millis as u64)
169    }
170
171    /// convert kbps into the nanosecond weight of each byte
172    /// (easier to rate limit with this value)
173    pub(crate) fn limit_ip_byte_nanos(&self) -> i32 {
174        8_000_000 / self.limit_ip_kbps
175    }
176}
177
178fn get_styles() -> clap::builder::Styles {
179    clap::builder::Styles::styled()
180        .usage(
181            anstyle::Style::new()
182                .bold()
183                .fg_color(Some(anstyle::Color::Ansi(
184                    anstyle::AnsiColor::Yellow,
185                ))),
186        )
187        .header(
188            anstyle::Style::new()
189                .bold()
190                .fg_color(Some(anstyle::Color::Ansi(
191                    anstyle::AnsiColor::Yellow,
192                ))),
193        )
194        .literal(
195            anstyle::Style::new().fg_color(Some(anstyle::Color::Ansi(
196                anstyle::AnsiColor::Green,
197            ))),
198        )
199        .invalid(
200            anstyle::Style::new()
201                .bold()
202                .fg_color(Some(anstyle::Color::Ansi(anstyle::AnsiColor::Red))),
203        )
204        .error(
205            anstyle::Style::new()
206                .bold()
207                .fg_color(Some(anstyle::Color::Ansi(anstyle::AnsiColor::Red))),
208        )
209        .valid(
210            anstyle::Style::new()
211                .bold()
212                .fg_color(Some(anstyle::Color::Ansi(
213                    anstyle::AnsiColor::Green,
214                ))),
215        )
216        .placeholder(
217            anstyle::Style::new().fg_color(Some(anstyle::Color::Ansi(
218                anstyle::AnsiColor::White,
219            ))),
220        )
221}