From 87a7c8d9e88ddb589dc25eda223ab11e30abf6a4 Mon Sep 17 00:00:00 2001 From: strawberry Date: Fri, 29 Mar 2024 18:21:17 -0400 Subject: [PATCH] add opt-in sentry logging, improve main function Signed-off-by: strawberry --- Cargo.lock | 312 +++++++++++++++++++++++++++++++ Cargo.toml | 7 +- conduwuit-example.toml | 10 + src/config/mod.rs | 5 + src/main.rs | 415 +++++++++++++++++++++-------------------- 5 files changed, 544 insertions(+), 205 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 238776a1..82a7c32a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -461,6 +461,7 @@ dependencies = [ "rusqlite", "rust-rocksdb", "sd-notify", + "sentry", "serde", "serde_html_form", "serde_json", @@ -593,6 +594,16 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "serde", + "uuid", +] + [[package]] name = "der" version = "0.7.8" @@ -684,6 +695,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "fallible-iterator" version = "0.3.0" @@ -696,6 +717,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + [[package]] name = "fdeflate" version = "0.3.4" @@ -725,6 +752,18 @@ dependencies = [ "version_check", ] +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + [[package]] name = "flate2" version = "1.0.28" @@ -741,6 +780,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -822,9 +876,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", + "futures-io", "futures-macro", "futures-sink", "futures-task", + "memchr", "pin-project-lite", "pin-utils", "slab", @@ -1118,6 +1174,19 @@ dependencies = [ "tokio-rustls", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "hyperlocal" version = "0.8.0" @@ -1381,6 +1450,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + [[package]] name = "lock_api" version = "0.4.11" @@ -1514,6 +1589,24 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "new_debug_unreachable" version = "1.0.6" @@ -1658,12 +1751,50 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.55", +] + [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "opentelemetry" version = "0.21.0" @@ -1745,6 +1876,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "os_info" +version = "3.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" +dependencies = [ + "log", + "serde", + "windows-sys 0.52.0", +] + [[package]] name = "overload" version = "0.1.1" @@ -2100,10 +2242,12 @@ dependencies = [ "http-body", "hyper", "hyper-rustls", + "hyper-tls", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", @@ -2116,6 +2260,7 @@ dependencies = [ "sync_wrapper", "system-configuration", "tokio", + "tokio-native-tls", "tokio-rustls", "tokio-socks", "tower-service", @@ -2401,6 +2546,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustls" version = "0.21.10" @@ -2516,6 +2674,114 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +[[package]] +name = "sentry" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "766448f12e44d68e675d5789a261515c46ac6ccd240abdd451a9c46c84a49523" +dependencies = [ + "httpdate", + "native-tls", + "reqwest", + "sentry-backtrace", + "sentry-contexts", + "sentry-core", + "sentry-debug-images", + "sentry-panic", + "sentry-tracing", + "tokio", + "ureq", +] + +[[package]] +name = "sentry-backtrace" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32701cad8b3c78101e1cd33039303154791b0ff22e7802ed8cc23212ef478b45" +dependencies = [ + "backtrace", + "once_cell", + "regex", + "sentry-core", +] + +[[package]] +name = "sentry-contexts" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ddd2a91a13805bd8dab4ebf47323426f758c35f7bf24eacc1aded9668f3824" +dependencies = [ + "hostname", + "libc", + "os_info", + "rustc_version", + "sentry-core", + "uname", +] + +[[package]] +name = "sentry-core" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1189f68d7e7e102ef7171adf75f83a59607fafd1a5eecc9dc06c026ff3bdec4" +dependencies = [ + "once_cell", + "rand", + "sentry-types", + "serde", + "serde_json", +] + +[[package]] +name = "sentry-debug-images" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4d0a615e5eeca5699030620c119a094e04c14cf6b486ea1030460a544111a7" +dependencies = [ + "findshlibs", + "once_cell", + "sentry-core", +] + +[[package]] +name = "sentry-panic" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1c18d0b5fba195a4950f2f4c31023725c76f00aabb5840b7950479ece21b5ca" +dependencies = [ + "sentry-backtrace", + "sentry-core", +] + +[[package]] +name = "sentry-tracing" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3012699a9957d7f97047fd75d116e22d120668327db6e7c59824582e16e791b2" +dependencies = [ + "sentry-backtrace", + "sentry-core", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sentry-types" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7173fd594569091f68a7c37a886e202f4d0c1db1e1fa1d18a051ba695b2e2ec" +dependencies = [ + "debugid", + "hex", + "rand", + "serde", + "serde_json", + "thiserror", + "time", + "url", + "uuid", +] + [[package]] name = "serde" version = "1.0.197" @@ -2835,6 +3101,18 @@ dependencies = [ "libc", ] +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "tendril" version = "0.4.3" @@ -3004,6 +3282,16 @@ dependencies = [ "syn 2.0.55", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.24.1" @@ -3253,6 +3541,15 @@ version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e36a83ea2b3c704935a01b4642946aadd445cea40b10935e3f8bd8052b8193d6" +[[package]] +name = "uname" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" +dependencies = [ + "libc", +] + [[package]] name = "uncased" version = "0.9.10" @@ -3295,6 +3592,19 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "ureq" +version = "2.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11f214ce18d8b2cbe84ed3aa6486ed3f5b285cf8d8fbdbce9f3f767a724adc35" +dependencies = [ + "base64 0.21.7", + "log", + "native-tls", + "once_cell", + "url", +] + [[package]] name = "url" version = "2.5.0" @@ -3304,6 +3614,7 @@ dependencies = [ "form_urlencoded", "idna 0.5.0", "percent-encoding", + "serde", ] [[package]] @@ -3325,6 +3636,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ "getrandom", + "serde", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d758bbfe..350d8704 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -169,6 +169,11 @@ version = "0.20.0" optional = true features = ["rt-tokio"] +# optional sentry metrics for crash/panic reporting +[dependencies.sentry] +version = "0.32.2" +optional = true + # optional jemalloc usage [dependencies.tikv-jemallocator] version = "0.5.4" @@ -282,7 +287,7 @@ hyperlocal = { git = "https://github.com/softprops/hyperlocal", rev = "2ee4d1496 [features] -default = ["conduit_bin", "backend_rocksdb", "systemd", "element_hacks"] +default = ["conduit_bin", "backend_rocksdb", "systemd", "element_hacks", "sentry"] conduit_bin = ["axum"] backend_sqlite = ["sqlite"] backend_rocksdb = ["rocksdb"] diff --git a/conduwuit-example.toml b/conduwuit-example.toml index fed77160..577c3353 100644 --- a/conduwuit-example.toml +++ b/conduwuit-example.toml @@ -32,6 +32,16 @@ # Defaults to `matrix.org` # trusted_servers = ["matrix.org"] +# Sentry.io crash/panic reporting, performance monitoring/metrics, etc. +# +# Defaults to false +#sentry = false + +# Report your Conduwuit server_name in Sentry.io crash reports and metrics +# +# Defaults to false +#sentry_send_server_name = false + ### Database configuration diff --git a/src/config/mod.rs b/src/config/mod.rs index 67aa037a..a07fa5da 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -232,6 +232,11 @@ pub struct Config { #[serde(default)] pub block_non_admin_invites: bool, + #[serde(default)] + pub sentry: bool, + #[serde(default)] + pub sentry_send_server_name: bool, + #[serde(flatten)] #[allow(clippy::zero_sized_map_values)] // this is a catchall, the map shouldn't be zero at runtime pub catchall: BTreeMap, diff --git a/src/main.rs b/src/main.rs index 149b1157..d42b0892 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,30 +55,17 @@ use tracing_subscriber::{prelude::*, EnvFilter}; #[global_allocator] static GLOBAL: Jemalloc = Jemalloc; -#[tokio::main] -async fn main() { +fn main() { let args = clap::parse(); // Initialize config - let raw_config = if Env::var("CONDUIT_CONFIG").is_some() { + let raw_config = if let Some(config_file_env) = Env::var("CONDUIT_CONFIG") { Figment::new() - .merge( - Toml::file(Env::var("CONDUIT_CONFIG").expect( - "The CONDUIT_CONFIG environment variable was set but appears to be invalid. This should be set to \ - the path to a valid TOML file, an empty string (for compatibility), or removed/unset entirely.", - )) - .nested(), - ) + .merge(Toml::file(config_file_env).nested()) .merge(Env::prefixed("CONDUIT_").global()) - } else if args.config.is_some() { + } else if let Some(config_file_arg) = args.config { Figment::new() - .merge( - Toml::file(args.config.expect( - "conduwuit config commandline argument was specified, but appears to be invalid. This should be \ - set to the path of a valid TOML file.", - )) - .nested(), - ) + .merge(Toml::file(config_file_arg).nested()) .merge(Env::prefixed("CONDUIT_").global()) } else { Figment::new().merge(Env::prefixed("CONDUIT_").global()) @@ -92,13 +79,18 @@ async fn main() { }, }; + // don't start if we're listening on both UNIX sockets and TCP at same time + if config.is_dual_listening(&raw_config) { + return; + }; + if config.allow_jaeger { #[cfg(feature = "perf_measurements")] { opentelemetry::global::set_text_map_propagator(opentelemetry_jaeger::Propagator::new()); let tracer = opentelemetry_jaeger::new_agent_pipeline() .with_auto_split_batch(true) - .with_service_name("conduit") + .with_service_name("conduwuit") .install_batch(opentelemetry_sdk::runtime::Tokio) .unwrap(); let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); @@ -143,6 +135,29 @@ async fn main() { tracing::subscriber::set_global_default(subscriber).unwrap(); } + #[cfg(feature = "sentry")] + if config.sentry { + info!("Sentry.io crash reporting and telemetry is enabled, initialising guard"); + + let _guard = sentry::init(( + "https://fe2eb4536aa04949e28eff3128d64757@o4506996327251968.ingest.us.sentry.io/4506996334657536", + sentry::ClientOptions { + release: sentry::release_name!(), + server_name: if config.sentry_send_server_name { + Some(config.server_name.to_string().into()) + } else { + None + }, + ..Default::default() + }, + )); + } + + if let Err(e) = check_config(&config) { + error!("Config check failed: {e}"); + return; + } + // This is needed for opening lots of file descriptors, which tends to // happen more often when using RocksDB and making lots of federation // connections at startup. The soft limit is usually 1024, and the hard @@ -153,192 +168,23 @@ async fn main() { #[cfg(unix)] maximize_fd_limit().expect("Unable to increase maximum soft and hard file descriptor limit"); - config.warn_deprecated(); - config.warn_unknown_key(); + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + .block_on(async { + info!("Loading database"); + let db_load_time = std::time::Instant::now(); + if let Err(error) = KeyValueDatabase::load_or_create(config).await { + error!(?error, "The database couldn't be loaded or created"); + return; + }; + info!("Database took {:?} to load, now starting server", db_load_time.elapsed()); - // don't start if we're listening on both UNIX sockets and TCP at same time - if config.is_dual_listening(&raw_config) { - return; - }; - - info!("Loading database"); - let db_load_time = std::time::Instant::now(); - if let Err(error) = KeyValueDatabase::load_or_create(config).await { - error!(?error, "The database couldn't be loaded or created"); - return; - }; - info!("Database took {:?} to load", db_load_time.elapsed()); - - let config = &services().globals.config; - - /* ad-hoc config validation/checks */ - - if config.unix_socket_path.is_some() && !cfg!(unix) { - error!( - "UNIX socket support is only available on *nix platforms. Please remove \"unix_socket_path\" from your \ - config." - ); - return; - } - - if config.address.is_loopback() && cfg!(unix) { - debug!( - "Found loopback listening address {}, running checks if we're in a container.", - config.address - ); - - #[cfg(unix)] - if Path::new("/proc/vz").exists() /* Guest */ && !Path::new("/proc/bz").exists() - /* Host */ - { - error!( - "You are detected using OpenVZ with a loopback/localhost listening address of {}. If you are using \ - OpenVZ for containers and you use NAT-based networking to communicate with the host and guest, this \ - will NOT work. Please change this to \"0.0.0.0\". If this is expected, you can ignore.", - config.address - ); - } - - #[cfg(unix)] - if Path::new("/.dockerenv").exists() { - error!( - "You are detected using Docker with a loopback/localhost listening address of {}. If you are using a \ - reverse proxy on the host and require communication to conduwuit in the Docker container via \ - NAT-based networking, this will NOT work. Please change this to \"0.0.0.0\". If this is expected, \ - you can ignore.", - config.address - ); - } - - #[cfg(unix)] - if Path::new("/run/.containerenv").exists() { - error!( - "You are detected using Podman with a loopback/localhost listening address of {}. If you are using a \ - reverse proxy on the host and require communication to conduwuit in the Podman container via \ - NAT-based networking, this will NOT work. Please change this to \"0.0.0.0\". If this is expected, \ - you can ignore.", - config.address - ); - } - } - - // rocksdb does not allow max_log_files to be 0 - if config.rocksdb_max_log_files == 0 && cfg!(feature = "rocksdb") { - error!("When using RocksDB, rocksdb_max_log_files cannot be 0. Please set a value at least 1."); - return; - } - - // yeah, unless the user built a debug build hopefully for local testing only - if config.server_name == "your.server.name" && !cfg!(debug_assertions) { - error!("You must specify a valid server name for production usage of conduwuit."); - return; - } - - if cfg!(debug_assertions) { - info!("Note: conduwuit was built without optimisations (i.e. debug build)"); - } - - // check if the user specified a registration token as `""` - if config.registration_token == Some(String::new()) { - error!("Registration token was specified but is empty (\"\")"); - return; - } - - if config.max_request_size < 4096 { - error!(?config.max_request_size, "Max request size is less than 4KB. Please increase it."); - } - - // check if user specified valid IP CIDR ranges on startup - for cidr in services().globals.ip_range_denylist() { - _ = ipaddress::IPAddress::parse(cidr).map_err(|e| error!("Error parsing specified IP CIDR range: {e}")); - } - - if config.allow_registration - && !config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse - && config.registration_token.is_none() - { - error!( - "!! You have `allow_registration` enabled without a token configured in your config which means you are \ - allowing ANYONE to register on your conduwuit instance without any 2nd-step (e.g. registration token).\n - If this is not the intended behaviour, please set a registration token with the `registration_token` config \ - option.\n - For security and safety reasons, conduwuit will shut down. If you are extra sure this is the desired behaviour \ - you want, please set the following config option to true: - `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`" - ); - return; - } - - if config.allow_registration - && config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse - && config.registration_token.is_none() - { - warn!( - "Open registration is enabled via setting \ - `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse` and `allow_registration` to \ - true without a registration token configured. You are expected to be aware of the risks now.\n - If this is not the desired behaviour, please set a registration token." - ); - } - - if config.allow_outgoing_presence && !config.allow_local_presence { - error!("Outgoing presence requires allowing local presence. Please enable \"allow_local_presence\"."); - return; - } - - if config.allow_outgoing_presence { - warn!( - "! Outgoing federated presence is not spec compliant due to relying on PDUs and EDUs combined.\nOutgoing \ - presence will not be very reliable due to this and any issues with federated outgoing presence are very \ - likely attributed to this issue.\nIncoming presence and local presence are unaffected." - ); - } - - if config - .url_preview_domain_contains_allowlist - .contains(&"*".to_owned()) - { - warn!( - "All URLs are allowed for URL previews via setting \"url_preview_domain_contains_allowlist\" to \"*\". \ - This opens up significant attack surface to your server. You are expected to be aware of the risks by \ - doing this." - ); - } - if config - .url_preview_domain_explicit_allowlist - .contains(&"*".to_owned()) - { - warn!( - "All URLs are allowed for URL previews via setting \"url_preview_domain_explicit_allowlist\" to \"*\". \ - This opens up significant attack surface to your server. You are expected to be aware of the risks by \ - doing this." - ); - } - if config - .url_preview_url_contains_allowlist - .contains(&"*".to_owned()) - { - warn!( - "All URLs are allowed for URL previews via setting \"url_preview_url_contains_allowlist\" to \"*\". This \ - opens up significant attack surface to your server. You are expected to be aware of the risks by doing \ - this." - ); - } - - /* end ad-hoc config validation/checks */ - - info!("Starting server"); - if let Err(e) = run_server().await { - error!("Critical error starting server: {}", e); - }; - - // if server runs into critical error and shuts down, shut down the tracer - // provider if jaegar is used. awaiting run_server() is a blocking call so - // putting this after is fine, but not the other options above. - #[cfg(feature = "perf_measurements")] - if config.allow_jaeger { - opentelemetry::global::shutdown_tracer_provider(); - } + if let Err(e) = run_server().await { + error!("Critical error starting server: {e}"); + }; + }); } async fn run_server() -> io::Result<()> { @@ -962,6 +808,167 @@ fn maximize_fd_limit() -> Result<(), nix::errno::Errno> { Ok(()) } +fn check_config(config: &Config) -> Result<()> { + config.warn_deprecated(); + config.warn_unknown_key(); + + if config.unix_socket_path.is_some() && !cfg!(unix) { + return Err(Error::bad_config( + "UNIX socket support is only available on *nix platforms. Please remove \"unix_socket_path\" from your \ + config.", + )); + } + + if config.address.is_loopback() && cfg!(unix) { + debug!( + "Found loopback listening address {}, running checks if we're in a container.", + config.address + ); + + #[cfg(unix)] + if Path::new("/proc/vz").exists() /* Guest */ && !Path::new("/proc/bz").exists() + /* Host */ + { + error!( + "You are detected using OpenVZ with a loopback/localhost listening address of {}. If you are using \ + OpenVZ for containers and you use NAT-based networking to communicate with the host and guest, this \ + will NOT work. Please change this to \"0.0.0.0\". If this is expected, you can ignore.", + config.address + ); + } + + #[cfg(unix)] + if Path::new("/.dockerenv").exists() { + error!( + "You are detected using Docker with a loopback/localhost listening address of {}. If you are using a \ + reverse proxy on the host and require communication to conduwuit in the Docker container via \ + NAT-based networking, this will NOT work. Please change this to \"0.0.0.0\". If this is expected, \ + you can ignore.", + config.address + ); + } + + #[cfg(unix)] + if Path::new("/run/.containerenv").exists() { + error!( + "You are detected using Podman with a loopback/localhost listening address of {}. If you are using a \ + reverse proxy on the host and require communication to conduwuit in the Podman container via \ + NAT-based networking, this will NOT work. Please change this to \"0.0.0.0\". If this is expected, \ + you can ignore.", + config.address + ); + } + } + + // rocksdb does not allow max_log_files to be 0 + if config.rocksdb_max_log_files == 0 && cfg!(feature = "rocksdb") { + return Err(Error::bad_config( + "When using RocksDB, rocksdb_max_log_files cannot be 0. Please set a value at least 1.", + )); + } + + // yeah, unless the user built a debug build hopefully for local testing only + if config.server_name == "your.server.name" && !cfg!(debug_assertions) { + return Err(Error::bad_config( + "You must specify a valid server name for production usage of conduwuit.", + )); + } + + if cfg!(debug_assertions) { + info!("Note: conduwuit was built without optimisations (i.e. debug build)"); + } + + // check if the user specified a registration token as `""` + if config.registration_token == Some(String::new()) { + return Err(Error::bad_config("Registration token was specified but is empty (\"\")")); + } + + if config.max_request_size < 16384 { + return Err(Error::bad_config("Max request size is less than 16KB. Please increase it.")); + } + + // check if user specified valid IP CIDR ranges on startup + for cidr in &config.ip_range_denylist { + if let Err(e) = ipaddress::IPAddress::parse(cidr) { + error!("Error parsing specified IP CIDR range from string: {e}"); + return Err(Error::bad_config("Error parsing specified IP CIDR ranges from strings")); + } + } + + if config.allow_registration + && !config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse + && config.registration_token.is_none() + { + return Err(Error::bad_config( + "!! You have `allow_registration` enabled without a token configured in your config which means you are \ + allowing ANYONE to register on your conduwuit instance without any 2nd-step (e.g. registration token).\n +If this is not the intended behaviour, please set a registration token with the `registration_token` config option.\n +For security and safety reasons, conduwuit will shut down. If you are extra sure this is the desired behaviour you \ + want, please set the following config option to true: +`yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`", + )); + } + + if config.allow_registration + && config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse + && config.registration_token.is_none() + { + warn!( + "Open registration is enabled via setting \ + `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse` and `allow_registration` to \ + true without a registration token configured. You are expected to be aware of the risks now.\n + If this is not the desired behaviour, please set a registration token." + ); + } + + if config.allow_outgoing_presence && !config.allow_local_presence { + return Err(Error::bad_config( + "Outgoing presence requires allowing local presence. Please enable \"allow_local_presence\".", + )); + } + + if config.allow_outgoing_presence { + warn!( + "! Outgoing federated presence is not spec compliant due to relying on PDUs and EDUs combined.\nOutgoing \ + presence will not be very reliable due to this and any issues with federated outgoing presence are very \ + likely attributed to this issue.\nIncoming presence and local presence are unaffected." + ); + } + + if config + .url_preview_domain_contains_allowlist + .contains(&"*".to_owned()) + { + warn!( + "All URLs are allowed for URL previews via setting \"url_preview_domain_contains_allowlist\" to \"*\". \ + This opens up significant attack surface to your server. You are expected to be aware of the risks by \ + doing this." + ); + } + if config + .url_preview_domain_explicit_allowlist + .contains(&"*".to_owned()) + { + warn!( + "All URLs are allowed for URL previews via setting \"url_preview_domain_explicit_allowlist\" to \"*\". \ + This opens up significant attack surface to your server. You are expected to be aware of the risks by \ + doing this." + ); + } + if config + .url_preview_url_contains_allowlist + .contains(&"*".to_owned()) + { + warn!( + "All URLs are allowed for URL previews via setting \"url_preview_url_contains_allowlist\" to \"*\". This \ + opens up significant attack surface to your server. You are expected to be aware of the risks by doing \ + this." + ); + } + + Ok(()) +} + #[cfg(test)] mod test { use super::*;