mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-12 15:34:00 +00:00
Implement instance whitelist
This commit is contained in:
parent
c5ced6fa5e
commit
b1b97db11a
8 changed files with 46 additions and 20 deletions
2
docker/federation/docker-compose.yml
vendored
2
docker/federation/docker-compose.yml
vendored
|
@ -25,6 +25,7 @@ services:
|
||||||
- LEMMY_FRONT_END_DIR=/app/dist
|
- LEMMY_FRONT_END_DIR=/app/dist
|
||||||
- LEMMY_FEDERATION__ENABLED=true
|
- LEMMY_FEDERATION__ENABLED=true
|
||||||
- LEMMY_FEDERATION__TLS_ENABLED=false
|
- LEMMY_FEDERATION__TLS_ENABLED=false
|
||||||
|
- LEMMY_FEDERATION__INSTANCE_WHITELIST=lemmy_beta
|
||||||
- LEMMY_PORT=8540
|
- LEMMY_PORT=8540
|
||||||
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha
|
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_alpha
|
||||||
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
|
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
|
||||||
|
@ -58,6 +59,7 @@ services:
|
||||||
- LEMMY_FRONT_END_DIR=/app/dist
|
- LEMMY_FRONT_END_DIR=/app/dist
|
||||||
- LEMMY_FEDERATION__ENABLED=true
|
- LEMMY_FEDERATION__ENABLED=true
|
||||||
- LEMMY_FEDERATION__TLS_ENABLED=false
|
- LEMMY_FEDERATION__TLS_ENABLED=false
|
||||||
|
- LEMMY_FEDERATION__INSTANCE_WHITELIST=lemmy_alpha
|
||||||
- LEMMY_PORT=8550
|
- LEMMY_PORT=8550
|
||||||
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta
|
- LEMMY_SETUP__ADMIN_USERNAME=lemmy_beta
|
||||||
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
|
- LEMMY_SETUP__ADMIN_PASSWORD=lemmy
|
||||||
|
|
2
server/config/defaults.hjson
vendored
2
server/config/defaults.hjson
vendored
|
@ -56,6 +56,8 @@
|
||||||
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
|
||||||
|
instance_whitelist: ""
|
||||||
}
|
}
|
||||||
# # email sending configuration
|
# # email sending configuration
|
||||||
# email: {
|
# email: {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::apub::is_apub_id_valid;
|
||||||
use crate::db::community::Community;
|
use crate::db::community::Community;
|
||||||
use crate::db::community_view::CommunityFollowerView;
|
use crate::db::community_view::CommunityFollowerView;
|
||||||
use crate::db::post::Post;
|
use crate::db::post::Post;
|
||||||
|
@ -36,9 +37,12 @@ where
|
||||||
A: Serialize + Debug,
|
A: Serialize + Debug,
|
||||||
{
|
{
|
||||||
let json = serde_json::to_string(&activity)?;
|
let json = serde_json::to_string(&activity)?;
|
||||||
debug!("Sending activitypub activity {}", json);
|
debug!("Sending activitypub activity {} to {:?}", json, to);
|
||||||
for t in to {
|
for t in to {
|
||||||
debug!("Sending activity to: {}", t);
|
if is_apub_id_valid(&t) {
|
||||||
|
debug!("Not sending activity to {} (invalid or blacklisted)", t);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let res = Request::post(t)
|
let res = Request::post(t)
|
||||||
.header("Content-Type", "application/json")
|
.header("Content-Type", "application/json")
|
||||||
.body(json.to_owned())?
|
.body(json.to_owned())?
|
||||||
|
|
|
@ -17,22 +17,18 @@ pub enum CommunityAcceptedObjects {
|
||||||
Follow(Follow),
|
Follow(Follow),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct Params {
|
|
||||||
community_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handler for all incoming activities to community inboxes.
|
/// Handler for all incoming activities to community inboxes.
|
||||||
pub async fn community_inbox(
|
pub async fn community_inbox(
|
||||||
input: web::Json<CommunityAcceptedObjects>,
|
input: web::Json<CommunityAcceptedObjects>,
|
||||||
params: web::Query<Params>,
|
path: web::Path<String>,
|
||||||
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
|
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let input = input.into_inner();
|
let input = input.into_inner();
|
||||||
let conn = &db.get().unwrap();
|
let conn = &db.get().unwrap();
|
||||||
debug!(
|
debug!(
|
||||||
"Community {} received activity {:?}",
|
"Community {} received activity {:?}",
|
||||||
¶ms.community_name, &input
|
&path.into_inner(),
|
||||||
|
&input
|
||||||
);
|
);
|
||||||
match input {
|
match input {
|
||||||
CommunityAcceptedObjects::Follow(f) => handle_follow(&f, conn),
|
CommunityAcceptedObjects::Follow(f) => handle_follow(&f, conn),
|
||||||
|
|
|
@ -8,7 +8,6 @@ use crate::db::user::{UserForm, User_};
|
||||||
use crate::db::user_view::UserView;
|
use crate::db::user_view::UserView;
|
||||||
use crate::db::{Crud, SearchType};
|
use crate::db::{Crud, SearchType};
|
||||||
use crate::routes::nodeinfo::{NodeInfo, NodeInfoWellKnown};
|
use crate::routes::nodeinfo::{NodeInfo, NodeInfoWellKnown};
|
||||||
use crate::settings::Settings;
|
|
||||||
use activitystreams::collection::OrderedCollection;
|
use activitystreams::collection::OrderedCollection;
|
||||||
use activitystreams::object::Page;
|
use activitystreams::object::Page;
|
||||||
use activitystreams::BaseBox;
|
use activitystreams::BaseBox;
|
||||||
|
@ -68,8 +67,8 @@ pub fn fetch_remote_object<Response>(url: &Url) -> Result<Response, Error>
|
||||||
where
|
where
|
||||||
Response: for<'de> Deserialize<'de>,
|
Response: for<'de> Deserialize<'de>,
|
||||||
{
|
{
|
||||||
if Settings::get().federation.tls_enabled && url.scheme() != "https" {
|
if !is_apub_id_valid(&url.to_string()) {
|
||||||
return Err(format_err!("Activitypub uri is insecure: {}", url));
|
return Err(format_err!("Activitypub uri invalid or blocked: {}", url));
|
||||||
}
|
}
|
||||||
// TODO: this function should return a future
|
// TODO: this function should return a future
|
||||||
let timeout = Duration::from_secs(60);
|
let timeout = Duration::from_secs(60);
|
||||||
|
@ -86,7 +85,7 @@ where
|
||||||
|
|
||||||
/// The types of ActivityPub objects that can be fetched directly by searching for their ID.
|
/// The types of ActivityPub objects that can be fetched directly by searching for their ID.
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize, Debug)]
|
||||||
pub enum SearchAcceptedObjects {
|
pub enum SearchAcceptedObjects {
|
||||||
Person(Box<PersonExt>),
|
Person(Box<PersonExt>),
|
||||||
Group(Box<GroupExt>),
|
Group(Box<GroupExt>),
|
||||||
|
|
|
@ -88,3 +88,26 @@ pub fn gen_keypair_str() -> (String, String) {
|
||||||
fn vec_bytes_to_str(bytes: Vec<u8>) -> String {
|
fn vec_bytes_to_str(bytes: Vec<u8>) -> String {
|
||||||
String::from_utf8_lossy(&bytes).into_owned()
|
String::from_utf8_lossy(&bytes).into_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Checks if the ID has a valid format, correct scheme, and is in the whitelist.
|
||||||
|
fn is_apub_id_valid(apub_id: &str) -> bool {
|
||||||
|
let url = match Url::parse(apub_id) {
|
||||||
|
Ok(u) => u,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if url.scheme() != get_apub_protocol_string() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let whitelist: Vec<String> = Settings::get()
|
||||||
|
.federation
|
||||||
|
.instance_whitelist
|
||||||
|
.split(',')
|
||||||
|
.map(|d| d.to_string())
|
||||||
|
.collect();
|
||||||
|
match url.domain() {
|
||||||
|
Some(d) => whitelist.contains(&d.to_owned()),
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,20 +17,19 @@ pub enum UserAcceptedObjects {
|
||||||
Accept(Accept),
|
Accept(Accept),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct Params {
|
|
||||||
user_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handler for all incoming activities to user inboxes.
|
/// Handler for all incoming activities to user inboxes.
|
||||||
pub async fn user_inbox(
|
pub async fn user_inbox(
|
||||||
input: web::Json<UserAcceptedObjects>,
|
input: web::Json<UserAcceptedObjects>,
|
||||||
params: web::Query<Params>,
|
path: web::Path<String>,
|
||||||
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
|
db: web::Data<Pool<ConnectionManager<PgConnection>>>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let input = input.into_inner();
|
let input = input.into_inner();
|
||||||
let conn = &db.get().unwrap();
|
let conn = &db.get().unwrap();
|
||||||
debug!("User {} received activity: {:?}", ¶ms.user_name, &input);
|
debug!(
|
||||||
|
"User {} received activity: {:?}",
|
||||||
|
&path.into_inner(),
|
||||||
|
&input
|
||||||
|
);
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
UserAcceptedObjects::Create(c) => handle_create(&c, conn),
|
UserAcceptedObjects::Create(c) => handle_create(&c, conn),
|
||||||
|
|
|
@ -64,6 +64,7 @@ pub struct Database {
|
||||||
pub struct Federation {
|
pub struct Federation {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub tls_enabled: bool,
|
pub tls_enabled: bool,
|
||||||
|
pub instance_whitelist: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
|
Loading…
Reference in a new issue