add support for reading a registration token from a file

Signed-off-by: strawberry <strawberry@puppygock.gay>
This commit is contained in:
strawberry 2024-09-29 01:54:07 -04:00
parent ee1580e480
commit 7a59add8f1
10 changed files with 78 additions and 21 deletions

View File

@ -195,11 +195,14 @@ allow_guests_auto_join_rooms = false
# Enables registration. If set to false, no users can register on this # Enables registration. If set to false, no users can register on this
# server. # server.
#
# If set to true without a token configured, users can register with no form of 2nd- # If set to true without a token configured, users can register with no form of 2nd-
# step only if you set # step only if you set
# `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse` to # `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse` to
# true in your config. If you would like # true in your config.
# registration only via token reg, please configure the `registration_token` key. #
# If you would like registration only via token reg, please configure
# `registration_token` or `registration_token_file`.
allow_registration = false allow_registration = false
# Please note that an open registration homeserver with no second-step verification # Please note that an open registration homeserver with no second-step verification
# is highly prone to abuse and potential defederation by homeservers, including # 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 # 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 # an account. If unset and `allow_registration` is true, registration is open
# without any condition. YOU NEED TO EDIT THIS. # 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 # controls whether federation is allowed or not
# defaults to true # 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) # 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 # 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 # 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. # will be publicly visible to the room, originating from the sender.

View File

@ -16,7 +16,7 @@ services:
CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit
CONDUWUIT_DATABASE_BACKEND: rocksdb CONDUWUIT_DATABASE_BACKEND: rocksdb
CONDUWUIT_PORT: 6167 # should match the loadbalancer traefik label 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_REGISTRATION: 'true'
CONDUWUIT_ALLOW_FEDERATION: 'true' CONDUWUIT_ALLOW_FEDERATION: 'true'
CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true' CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true'

View File

@ -32,7 +32,7 @@ services:
CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit
CONDUWUIT_DATABASE_BACKEND: rocksdb CONDUWUIT_DATABASE_BACKEND: rocksdb
CONDUWUIT_PORT: 6167 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_REGISTRATION: 'true'
CONDUWUIT_ALLOW_FEDERATION: 'true' CONDUWUIT_ALLOW_FEDERATION: 'true'
CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true' CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true'

View File

@ -15,7 +15,8 @@ services:
CONDUWUIT_SERVER_NAME: your.server.name.example # EDIT THIS CONDUWUIT_SERVER_NAME: your.server.name.example # EDIT THIS
CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]' CONDUWUIT_TRUSTED_SERVERS: '["matrix.org"]'
CONDUWUIT_ALLOW_REGISTRATION: 'false' # After setting a secure registration token, you can enable this 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_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_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 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 ### 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 # 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_LOG: info # default is: "warn,state_res=warn"
# CONDUWUIT_ALLOW_JAEGER: 'false'
# CONDUWUIT_ALLOW_ENCRYPTION: 'true' # CONDUWUIT_ALLOW_ENCRYPTION: 'true'
# CONDUWUIT_ALLOW_FEDERATION: 'true' # CONDUWUIT_ALLOW_FEDERATION: 'true'
# CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true' # CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true'
@ -31,7 +31,7 @@ services:
# CONDUWUIT_ALLOW_OUTGOING_PRESENCE: true # CONDUWUIT_ALLOW_OUTGOING_PRESENCE: true
# CONDUWUIT_ALLOW_LOCAL_PRESENCE: true # CONDUWUIT_ALLOW_LOCAL_PRESENCE: true
# CONDUWUIT_WORKERS: 10 # 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>⚧" # 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 # We need some way to serve the client and server .well-known json. The simplest way is via the CONDUWUIT_WELL_KNOWN

View File

@ -16,7 +16,7 @@ services:
CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit CONDUWUIT_DATABASE_PATH: /var/lib/conduwuit
CONDUWUIT_DATABASE_BACKEND: rocksdb CONDUWUIT_DATABASE_BACKEND: rocksdb
CONDUWUIT_PORT: 6167 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_REGISTRATION: 'true'
CONDUWUIT_ALLOW_FEDERATION: 'true' CONDUWUIT_ALLOW_FEDERATION: 'true'
CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true' CONDUWUIT_ALLOW_CHECK_FOR_UPDATES: 'true'

View File

@ -111,7 +111,7 @@ pub(crate) async fn register_route(
if is_guest if is_guest
&& (!services.globals.allow_guest_registration() && (!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!( info!(
"Guest registration disabled / registration enabled with token configured, rejecting guest registration \ "Guest registration disabled / registration enabled with token configured, rejecting guest registration \
@ -183,7 +183,7 @@ pub(crate) async fn register_route(
// UIAA // UIAA
let mut uiaainfo; 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 // Registration token required
uiaainfo = UiaaInfo { uiaainfo = UiaaInfo {
flows: vec![AuthFlow { 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( pub(crate) async fn check_registration_token_validity(
State(services): State<crate::State>, body: Ruma<check_registration_token_validity::v1::Request>, State(services): State<crate::State>, body: Ruma<check_registration_token_validity::v1::Request>,
) -> Result<check_registration_token_validity::v1::Response> { ) -> Result<check_registration_token_validity::v1::Response> {
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( return Err(Error::BadRequest(
ErrorKind::forbidden(), ErrorKind::forbidden(),
"Server does not allow token registration.", "Server does not allow token registration.",

View File

@ -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 { if config.max_request_size < 5_120_000 {
return Err!(Config( return Err!(Config(
"max_request_size", "max_request_size",
@ -111,12 +127,13 @@ pub fn check(config: &Config) -> Result<()> {
if config.allow_registration if config.allow_registration
&& !config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse && !config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
&& config.registration_token.is_none() && config.registration_token.is_none()
&& config.registration_token_file.is_none()
{ {
return Err!(Config( return Err!(Config(
"registration_token", "registration_token",
"!! You have `allow_registration` enabled without a token configured in your config which means you are \ "!! 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 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 \ 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: want, please set the following config option to true:
`yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`" `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 if config.allow_registration
&& config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse && config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
&& config.registration_token.is_none() && config.registration_token.is_none()
&& config.registration_token_file.is_none()
{ {
warn!( warn!(
"Open registration is enabled via setting \ "Open registration is enabled via setting \

View File

@ -139,6 +139,7 @@ pub struct Config {
#[serde(default)] #[serde(default)]
pub yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse: bool, pub yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse: bool,
pub registration_token: Option<String>, pub registration_token: Option<String>,
pub registration_token_file: Option<PathBuf>,
#[serde(default = "true_fn")] #[serde(default = "true_fn")]
pub allow_encryption: bool, pub allow_encryption: bool,
#[serde(default = "true_fn")] #[serde(default = "true_fn")]
@ -572,12 +573,20 @@ impl fmt::Display for Config {
line("Allow registration", &self.allow_registration.to_string()); line("Allow registration", &self.allow_registration.to_string());
line( line(
"Registration token", "Registration token",
if self.registration_token.is_some() { if self.registration_token.is_none() && self.registration_token_file.is_none() && self.allow_registration {
"set" "not set (⚠️ open registration!)"
} else if self.registration_token.is_none() && self.registration_token_file.is_none() {
"not set"
} else { } 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( line(
"Allow guest registration (inherently false if allow registration is false)", "Allow guest registration (inherently false if allow registration is false)",
&self.allow_guest_registration.to_string(), &self.allow_guest_registration.to_string(),

View File

@ -41,6 +41,7 @@ pub struct Service {
pub server_user: OwnedUserId, pub server_user: OwnedUserId,
pub admin_alias: OwnedRoomAliasId, pub admin_alias: OwnedRoomAliasId,
pub turn_secret: String, pub turn_secret: String,
pub registration_token: Option<String>,
} }
type RateLimitState = (Instant, u32); // Time if last failed try, number of failed tries 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 { let mut s = Self {
db, db,
config: config.clone(), 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) server_user: UserId::parse_with_server_name(String::from("conduit"), &config.server_name)
.expect("@conduit:server_name is valid"), .expect("@conduit:server_name is valid"),
turn_secret, turn_secret,
registration_token,
}; };
if !s if !s

View File

@ -6,7 +6,7 @@ use std::{
use conduit::{ use conduit::{
err, error, implement, utils, err, error, implement, utils,
utils::{hash, string::EMPTY}, utils::{hash, string::EMPTY},
Error, Result, Server, Error, Result,
}; };
use database::{Deserialized, Map}; use database::{Deserialized, Map};
use ruma::{ use ruma::{
@ -26,7 +26,6 @@ pub struct Service {
} }
struct Services { struct Services {
server: Arc<Server>,
globals: Dep<globals::Service>, globals: Dep<globals::Service>,
users: Dep<users::Service>, users: Dep<users::Service>,
} }
@ -48,7 +47,6 @@ impl crate::Service for Service {
userdevicesessionid_uiaainfo: args.db["userdevicesessionid_uiaainfo"].clone(), userdevicesessionid_uiaainfo: args.db["userdevicesessionid_uiaainfo"].clone(),
}, },
services: Services { services: Services {
server: args.server.clone(),
globals: args.depend::<globals::Service>("globals"), globals: args.depend::<globals::Service>("globals"),
users: args.depend::<users::Service>("users"), users: args.depend::<users::Service>("users"),
}, },
@ -135,7 +133,13 @@ pub async fn try_auth(
uiaainfo.completed.push(AuthType::Password); uiaainfo.completed.push(AuthType::Password);
}, },
AuthData::RegistrationToken(t) => { 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); uiaainfo.completed.push(AuthType::RegistrationToken);
} else { } else {
uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody { uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody {