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