Implement instance blocklist (#85)

Implement instance blocklist

Co-authored-by: Felix Ableitner <me@nutomic.com>
Reviewed-on: https://yerbamate.dev/LemmyNet/lemmy/pulls/85
This commit is contained in:
nutomic 2020-08-13 20:26:49 +00:00 committed by dessalines
parent 9a343cfe8b
commit 164a9c29fe
3 changed files with 47 additions and 25 deletions

View File

@ -62,8 +62,10 @@
enabled: false enabled: false
# whether tls is required for activitypub. only disable this for debugging, never for producion. # whether tls is required for activitypub. only disable this for debugging, never for producion.
tls_enabled: true tls_enabled: true
# comma seperated list of instances with which federation is allowed # comma separated list of instances with which federation is allowed
allowed_instances: "" allowed_instances: ""
# comma separated list of instances which are blocked from federating
blocked_instances: ""
} }
captcha: { captcha: {
enabled: true enabled: true

View File

@ -8,7 +8,7 @@ static CONFIG_FILE: &str = "config/config.hjson";
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
pub struct Settings { pub struct Settings {
pub setup: Option<Setup>, pub setup: Option<Setup>,
pub database: Database, pub database: DatabaseConfig,
pub hostname: String, pub hostname: String,
pub bind: IpAddr, pub bind: IpAddr,
pub port: u16, pub port: u16,
@ -17,7 +17,7 @@ pub struct Settings {
pub pictrs_url: String, pub pictrs_url: String,
pub rate_limit: RateLimitConfig, pub rate_limit: RateLimitConfig,
pub email: Option<EmailConfig>, pub email: Option<EmailConfig>,
pub federation: Federation, pub federation: FederationConfig,
pub captcha: CaptchaConfig, pub captcha: CaptchaConfig,
} }
@ -57,7 +57,7 @@ pub struct CaptchaConfig {
} }
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
pub struct Database { pub struct DatabaseConfig {
pub user: String, pub user: String,
pub password: String, pub password: String,
pub host: String, pub host: String,
@ -67,10 +67,11 @@ pub struct Database {
} }
#[derive(Debug, Deserialize, Clone)] #[derive(Debug, Deserialize, Clone)]
pub struct Federation { pub struct FederationConfig {
pub enabled: bool, pub enabled: bool,
pub tls_enabled: bool, pub tls_enabled: bool,
pub allowed_instances: String, pub allowed_instances: String,
pub blocked_instances: String,
} }
lazy_static! { lazy_static! {
@ -143,6 +144,20 @@ impl Settings {
allowed_instances allowed_instances
} }
pub fn get_blocked_instances(&self) -> Vec<String> {
let mut blocked_instances: Vec<String> = self
.federation
.blocked_instances
.split(',')
.map(|d| d.to_string())
.collect();
// The defaults.hjson config always returns a [""]
blocked_instances.retain(|d| !d.eq(""));
blocked_instances
}
pub fn save_config_file(data: &str) -> Result<String, Error> { pub fn save_config_file(data: &str) -> Result<String, Error> {
fs::write(CONFIG_FILE, data)?; fs::write(CONFIG_FILE, data)?;

View File

@ -76,30 +76,35 @@ fn check_is_apub_id_valid(apub_id: &Url) -> Result<(), LemmyError> {
return Err(anyhow!("invalid apub id scheme: {:?}", apub_id.scheme()).into()); return Err(anyhow!("invalid apub id scheme: {:?}", apub_id.scheme()).into());
} }
let mut allowed_instances: Vec<String> = Settings::get().get_allowed_instances(); let mut allowed_instances = Settings::get().get_allowed_instances();
let blocked_instances = Settings::get().get_blocked_instances();
// need to allow this explicitly because apub activities might contain objects from our local let domain = apub_id.domain().context(location_info!())?.to_string();
// instance. replace is needed to remove the port in our federation test setup. if !allowed_instances.is_empty() {
let settings = Settings::get(); // need to allow this explicitly because apub activities might contain objects from our local
let local_instance = settings.hostname.split(':').collect::<Vec<&str>>(); // instance. split is needed to remove the port in our federation test setup.
allowed_instances.push( let settings = Settings::get();
local_instance let local_instance = settings.hostname.split(':').collect::<Vec<&str>>();
.first() allowed_instances.push(
.context(location_info!())? local_instance
.to_string(), .first()
); .context(location_info!())?
.to_string(),
match apub_id.domain() { );
Some(d) => {
let contains = allowed_instances.contains(&d.to_owned());
if !contains {
return Err(anyhow!("{} not in federation allowlist", d).into());
}
if allowed_instances.contains(&domain) {
Ok(())
} else {
Err(anyhow!("{} not in federation allowlist", domain).into())
}
} else if !blocked_instances.is_empty() {
if blocked_instances.contains(&domain) {
Err(anyhow!("{} is in federation blocklist", domain).into())
} else {
Ok(()) Ok(())
} }
None => Err(anyhow!("federation allowlist is empty").into()), } else {
panic!("Invalid config, both allowed_instances and blocked_instances are specified");
} }
} }