feat: added basic ACL functionality

This commit is contained in:
NinekoTheCat 2023-12-24 11:03:02 +01:00
parent 6a9f8dfa6f
commit 7562925aeb
No known key found for this signature in database
GPG key ID: 700DB3F678A4AB66
13 changed files with 183 additions and 4 deletions

25
src/service/acl/data.rs Normal file
View file

@ -0,0 +1,25 @@
use serde::{Serialize, Deserialize};
use url::Host;
pub trait Data: Send + Sync {
/// check if given host exists in Acls, if so return it
fn check_acl(&self,host: &Host<String> ) -> crate::Result<Option<AclMode>>;
/// add a given Acl entry to the database
fn add_acl(&self, acl: AclDatabaseEntry) -> crate::Result<()>;
/// remove a given Acl entry from the database
fn remove_acl(&self,host: Host<String>) -> crate::Result<()>;
}
#[derive(Serialize,Deserialize, Debug, Clone, Copy)]
pub enum AclMode{
Block,
Allow
}
#[derive(Serialize,Deserialize, Debug, Clone)]
pub struct AclDatabaseEntry {
pub(crate) mode: AclMode,
pub(crate) hostname: Host
}

72
src/service/acl/mod.rs Normal file
View file

@ -0,0 +1,72 @@
use std::sync::Arc;
use ruma::ServerName;
use tracing::{warn, debug, error};
use url::Host;
use crate::{config::acl::AccessControlListConfig, api::server_server::FedDest};
pub use self::data::*;
mod data;
pub struct Service {
pub db: &'static dyn Data,
pub acl_config: Arc<AccessControlListConfig>
}
impl Service {
/// same as federation_with_allowed however it can work with the fedi_dest type
pub fn is_federation_with_allowed_fedi_dest(&self,fedi_dest: &FedDest) -> bool {
let hostname = if let Ok(name) = Host::parse(&fedi_dest.hostname()) {
name
} else {
warn!("cannot deserialise hostname for server with name {:?}",fedi_dest);
return false;
};
return self.is_federation_with_allowed(hostname);
}
/// same as federation_with_allowed however it can work with the fedi_dest type
pub fn is_federation_with_allowed_server_name(&self,srv: &ServerName) -> bool {
let hostname = if let Ok(name) = Host::parse(srv.host()) {
name
} else {
warn!("cannot deserialise hostname for server with name {:?}",srv);
return false;
};
return self.is_federation_with_allowed(hostname);
}
/// is federation allowed with this particular server?
pub fn is_federation_with_allowed(&self,server_host_name: Host<String>) -> bool {
debug!("checking federation allowance for {}", server_host_name);
// check blocklist first
if self.acl_config.block_list.contains(&server_host_name) {
return false;
}
let mut allow_list_enabled = false;
// check allowlist
if let Some(list) = &self.acl_config.allow_list {
if list.contains(&server_host_name) {
return true;
}
allow_list_enabled = true;
}
//check database
match self.db.check_acl(&server_host_name) {
Err(error) => {
error!("database failed with {}",error);
false
}
Ok(None) => false,
Ok(Some(data::AclMode::Block)) => false,
Ok(Some(data::AclMode::Allow)) if allow_list_enabled => true,
Ok(Some(data::AclMode::Allow)) => {
warn!("allowlist value found in database for {} but allow list is not enabled, denied request", server_host_name);
false
}
}
}
}

View file

@ -6,7 +6,7 @@ use std::{
use lru_cache::LruCache;
use crate::{Config, Result};
pub mod acl;
pub mod account_data;
pub mod admin;
pub mod appservice;
@ -34,6 +34,7 @@ pub struct Services {
pub key_backups: key_backups::Service,
pub media: media::Service,
pub sending: Arc<sending::Service>,
pub acl: acl::Service
}
impl Services {
@ -49,11 +50,13 @@ impl Services {
+ key_backups::Data
+ media::Data
+ sending::Data
+ acl::Data
+ 'static,
>(
db: &'static D,
config: Config,
) -> Result<Self> {
let acl_conf = config.acl.clone();
Ok(Self {
appservice: appservice::Service { db },
pusher: pusher::Service { db },
@ -118,6 +121,7 @@ impl Services {
sending: sending::Service::build(db, &config),
globals: globals::Service::load(db, config)?,
acl: acl::Service { db: db, acl_config: acl_conf },
})
}
fn memory_usage(&self) -> String {

View file

@ -1645,6 +1645,17 @@ impl Service {
/// Returns Ok if the acl allows the server
pub fn acl_check(&self, server_name: &ServerName, room_id: &RoomId) -> Result<()> {
if !services().acl.is_federation_with_allowed_server_name(server_name) {
info!(
"Server {} was denied by server ACL in {}",
server_name, room_id
);
return Err(Error::BadRequest(
ErrorKind::Forbidden,
"Server was denied by Server ACL",
));
}
let acl_event = match services().rooms.state_accessor.room_state_get(
room_id,
&StateEventType::RoomServerAcl,