From 7a59add8f1bc8d4697580823e8651f4b72e4b9d5 Mon Sep 17 00:00:00 2001 From: strawberry Date: Sun, 29 Sep 2024 01:54:07 -0400 Subject: [PATCH] add support for reading a registration token from a file Signed-off-by: strawberry --- conduwuit-example.toml | 18 +++++++++++++---- docs/deploying/docker-compose.for-traefik.yml | 2 +- docs/deploying/docker-compose.with-caddy.yml | 2 +- .../deploying/docker-compose.with-traefik.yml | 6 +++--- docs/deploying/docker-compose.yml | 2 +- src/api/client/account.rs | 6 +++--- src/core/config/check.rs | 20 ++++++++++++++++++- src/core/config/mod.rs | 15 +++++++++++--- src/service/globals/mod.rs | 16 +++++++++++++++ src/service/uiaa/mod.rs | 12 +++++++---- 10 files changed, 78 insertions(+), 21 deletions(-) diff --git a/conduwuit-example.toml b/conduwuit-example.toml index b532d381..11735616 100644 --- a/conduwuit-example.toml +++ b/conduwuit-example.toml @@ -195,11 +195,14 @@ allow_guests_auto_join_rooms = false # Enables registration. If set to false, no users can register on this # server. +# # If set to true without a token configured, users can register with no form of 2nd- # step only if you set # `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse` to -# true in your config. If you would like -# registration only via token reg, please configure the `registration_token` key. +# true in your config. +# +# If you would like registration only via token reg, please configure +# `registration_token` or `registration_token_file`. allow_registration = false # Please note that an open registration homeserver with no second-step verification # is highly prone to abuse and potential defederation by homeservers, including @@ -208,7 +211,14 @@ allow_registration = false # A static registration token that new users will have to provide when creating # an account. If unset and `allow_registration` is true, registration is open # without any condition. YOU NEED TO EDIT THIS. -registration_token = "change this token for something specific to your server" +registration_token = "change this token/string here or set registration_token_file" + +# Path to a file on the system that gets read for the registration token +# +# conduwuit must be able to access the file, and it must not be empty +# +# no default +#registration_token_file = "/etc/conduwuit/.reg_token" # controls whether federation is allowed or not # defaults to true @@ -344,7 +354,7 @@ allow_profile_lookup_federation_requests = true # Controls the max log level for admin command log captures (logs generated from running admin commands) # # Defaults to "info" on release builds, else "debug" on debug builds -#admin_log_capture = info +#admin_log_capture = "info" # Allows admins to enter commands in rooms other than #admins by prefixing with \!admin. The reply # will be publicly visible to the room, originating from the sender. diff --git a/docs/deploying/docker-compose.for-traefik.yml b/docs/deploying/docker-compose.for-traefik.yml index 1c615673..ae93d52f 100644 --- a/docs/deploying/docker-compose.for-traefik.yml +++ b/docs/deploying/docker-compose.for-traefik.yml @@ -16,7 +16,7 @@ services: CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit CONDUWUIT_DATABASE_BACKEND: rocksdb CONDUWUIT_PORT: 6167 # should match the loadbalancer traefik label - CONDUWUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB + CONDUWUIT_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB CONDUWUIT_ALLOW_REGISTRATION: 'true' CONDUWUIT_ALLOW_FEDERATION: 'true' CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true' diff --git a/docs/deploying/docker-compose.with-caddy.yml b/docs/deploying/docker-compose.with-caddy.yml index 899f4d67..36924212 100644 --- a/docs/deploying/docker-compose.with-caddy.yml +++ b/docs/deploying/docker-compose.with-caddy.yml @@ -32,7 +32,7 @@ services: CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit CONDUWUIT_DATABASE_BACKEND: rocksdb CONDUWUIT_PORT: 6167 - CONDUWUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB + CONDUWUIT_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB CONDUWUIT_ALLOW_REGISTRATION: 'true' CONDUWUIT_ALLOW_FEDERATION: 'true' CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true' diff --git a/docs/deploying/docker-compose.with-traefik.yml b/docs/deploying/docker-compose.with-traefik.yml index f05006a5..89118c74 100644 --- a/docs/deploying/docker-compose.with-traefik.yml +++ b/docs/deploying/docker-compose.with-traefik.yml @@ -15,7 +15,8 @@ services: CONDUWUIT_SERVER_NAME: your.server.name.example # EDIT THIS CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]' CONDUWUIT_ALLOW_REGISTRATION: 'false' # After setting a secure registration token, you can enable this - CONDUWUIT_REGISTRATION_TOKEN: # This is a token you can use to register on the server + CONDUWUIT_REGISTRATION_TOKEN: "" # This is a token you can use to register on the server + #CONDUWUIT_REGISTRATION_TOKEN_FILE: "" # Alternatively you can configure a path to a token file to read CONDUWUIT_ADDRESS: 0.0.0.0 CONDUWUIT_PORT: 6167 # you need to match this with the traefik load balancer label if you're want to change it CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit @@ -23,7 +24,6 @@ services: ### Uncomment and change values as desired, note that conduwuit has plenty of config options, so you should check out the example example config too # Available levels are: error, warn, info, debug, trace - more info at: https://docs.rs/env_logger/*/env_logger/#enabling-logging # CONDUWUIT_LOG: info # default is: "warn,state_res=warn" - # CONDUWUIT_ALLOW_JAEGER: 'false' # CONDUWUIT_ALLOW_ENCRYPTION: 'true' # CONDUWUIT_ALLOW_FEDERATION: 'true' # CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true' @@ -31,7 +31,7 @@ services: # CONDUWUIT_ALLOW_OUTGOING_PRESENCE: true # CONDUWUIT_ALLOW_LOCAL_PRESENCE: true # CONDUWUIT_WORKERS: 10 - # CONDUWUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB + # CONDUWUIT_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB # CONDUWUIT_NEW_USER_DISPLAYNAME_SUFFIX = "🏳<200d>⚧" # We need some way to serve the client and server .well-known json. The simplest way is via the CONDUWUIT_WELL_KNOWN diff --git a/docs/deploying/docker-compose.yml b/docs/deploying/docker-compose.yml index bc9f2477..26145c5a 100644 --- a/docs/deploying/docker-compose.yml +++ b/docs/deploying/docker-compose.yml @@ -16,7 +16,7 @@ services: CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit CONDUWUIT_DATABASE_BACKEND: rocksdb CONDUWUIT_PORT: 6167 - CONDUWUIT_MAX_REQUEST_SIZE: 20_000_000 # in bytes, ~20 MB + CONDUWUIT_MAX_REQUEST_SIZE: 20000000 # in bytes, ~20 MB CONDUWUIT_ALLOW_REGISTRATION: 'true' CONDUWUIT_ALLOW_FEDERATION: 'true' CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true' diff --git a/src/api/client/account.rs b/src/api/client/account.rs index 63d02f8f..1ededa36 100644 --- a/src/api/client/account.rs +++ b/src/api/client/account.rs @@ -111,7 +111,7 @@ pub(crate) async fn register_route( if is_guest && (!services.globals.allow_guest_registration() - || (services.globals.allow_registration() && services.globals.config.registration_token.is_some())) + || (services.globals.allow_registration() && services.globals.registration_token.is_some())) { info!( "Guest registration disabled / registration enabled with token configured, rejecting guest registration \ @@ -183,7 +183,7 @@ pub(crate) async fn register_route( // UIAA let mut uiaainfo; - let skip_auth = if services.globals.config.registration_token.is_some() { + let skip_auth = if services.globals.registration_token.is_some() { // Registration token required uiaainfo = UiaaInfo { flows: vec![AuthFlow { @@ -685,7 +685,7 @@ pub(crate) async fn request_3pid_management_token_via_msisdn_route( pub(crate) async fn check_registration_token_validity( State(services): State, body: Ruma, ) -> Result { - let Some(reg_token) = services.globals.config.registration_token.clone() else { + let Some(reg_token) = services.globals.registration_token.clone() else { return Err(Error::BadRequest( ErrorKind::forbidden(), "Server does not allow token registration.", diff --git a/src/core/config/check.rs b/src/core/config/check.rs index 8dea55d8..c0d05533 100644 --- a/src/core/config/check.rs +++ b/src/core/config/check.rs @@ -94,6 +94,22 @@ pub fn check(config: &Config) -> Result<()> { )); } + // check if we can read the token file path, and check if the file is empty + if config.registration_token_file.as_ref().is_some_and(|path| { + let Ok(token) = std::fs::read_to_string(path).inspect_err(|e| { + error!("Failed to read the registration token file: {e}"); + }) else { + return true; + }; + + token == String::new() + }) { + return Err!(Config( + "registration_token_file", + "Registration token file was specified but is empty or failed to be read" + )); + } + if config.max_request_size < 5_120_000 { return Err!(Config( "max_request_size", @@ -111,12 +127,13 @@ pub fn check(config: &Config) -> Result<()> { 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() + && config.registration_token_file.is_none() { return Err!(Config( "registration_token", "!! 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 +If this is not the intended behaviour, please set a registration token.\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`" @@ -126,6 +143,7 @@ For security and safety reasons, conduwuit will shut down. If you are extra sure 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() + && config.registration_token_file.is_none() { warn!( "Open registration is enabled via setting \ diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index d8e1c7d9..126b3123 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -139,6 +139,7 @@ pub struct Config { #[serde(default)] pub yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse: bool, pub registration_token: Option, + pub registration_token_file: Option, #[serde(default = "true_fn")] pub allow_encryption: bool, #[serde(default = "true_fn")] @@ -572,12 +573,20 @@ impl fmt::Display for Config { line("Allow registration", &self.allow_registration.to_string()); line( "Registration token", - if self.registration_token.is_some() { - "set" + if self.registration_token.is_none() && self.registration_token_file.is_none() && self.allow_registration { + "not set (⚠️ open registration!)" + } else if self.registration_token.is_none() && self.registration_token_file.is_none() { + "not set" } else { - "not set (open registration!)" + "set" }, ); + line( + "Registration token file path", + self.registration_token_file + .as_ref() + .map_or("", |path| path.to_str().unwrap_or_default()), + ); line( "Allow guest registration (inherently false if allow registration is false)", &self.allow_guest_registration.to_string(), diff --git a/src/service/globals/mod.rs b/src/service/globals/mod.rs index f24e8a27..fb970f07 100644 --- a/src/service/globals/mod.rs +++ b/src/service/globals/mod.rs @@ -41,6 +41,7 @@ pub struct Service { pub server_user: OwnedUserId, pub admin_alias: OwnedRoomAliasId, pub turn_secret: String, + pub registration_token: Option, } type RateLimitState = (Instant, u32); // Time if last failed try, number of failed tries @@ -96,6 +97,20 @@ impl crate::Service for Service { }) }); + let registration_token = + config + .registration_token_file + .as_ref() + .map_or(config.registration_token.clone(), |path| { + let Ok(token) = std::fs::read_to_string(path).inspect_err(|e| { + error!("Failed to read the registration token file: {e}"); + }) else { + return config.registration_token.clone(); + }; + + Some(token) + }); + let mut s = Self { db, config: config.clone(), @@ -112,6 +127,7 @@ impl crate::Service for Service { server_user: UserId::parse_with_server_name(String::from("conduit"), &config.server_name) .expect("@conduit:server_name is valid"), turn_secret, + registration_token, }; if !s diff --git a/src/service/uiaa/mod.rs b/src/service/uiaa/mod.rs index 0415bfc2..f75f1bcd 100644 --- a/src/service/uiaa/mod.rs +++ b/src/service/uiaa/mod.rs @@ -6,7 +6,7 @@ use std::{ use conduit::{ err, error, implement, utils, utils::{hash, string::EMPTY}, - Error, Result, Server, + Error, Result, }; use database::{Deserialized, Map}; use ruma::{ @@ -26,7 +26,6 @@ pub struct Service { } struct Services { - server: Arc, globals: Dep, users: Dep, } @@ -48,7 +47,6 @@ impl crate::Service for Service { userdevicesessionid_uiaainfo: args.db["userdevicesessionid_uiaainfo"].clone(), }, services: Services { - server: args.server.clone(), globals: args.depend::("globals"), users: args.depend::("users"), }, @@ -135,7 +133,13 @@ pub async fn try_auth( uiaainfo.completed.push(AuthType::Password); }, AuthData::RegistrationToken(t) => { - if Some(t.token.trim()) == self.services.server.config.registration_token.as_deref() { + if self + .services + .globals + .registration_token + .as_ref() + .is_some_and(|reg_token| t.token.trim() == reg_token) + { uiaainfo.completed.push(AuthType::RegistrationToken); } else { uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody {