don't send requests to specified list of IP CIDRs

this can most definitely be improved but this is a decent attempt.
the only annoying this is i couldn't just use a Vec<IPAddress> which
would have significantly simplified all of this, but serde can't
deserialise it on the config side i guess.

i may find a better way to do this in the future, but this should cover
most areas anyways.

Signed-off-by: strawberry <strawberry@puppygock.gay>
This commit is contained in:
strawberry 2024-01-21 22:59:06 -05:00 committed by June
parent 71d247232d
commit fa0c083555
8 changed files with 186 additions and 3 deletions

View file

@ -11,6 +11,7 @@ use futures_util::future::TryFutureExt;
use get_profile_information::v1::ProfileField;
use http::header::{HeaderValue, AUTHORIZATION};
use ipaddress::IPAddress;
use ruma::{
api::{
client::error::{Error as RumaError, ErrorKind},
@ -114,7 +115,6 @@ impl FedDest {
}
}
#[tracing::instrument(skip(request))]
pub(crate) async fn send_request<T: OutgoingRequest>(
destination: &ServerName,
request: T,
@ -132,6 +132,29 @@ where
));
}
if destination.is_ip_literal() {
info!("Destination is an IP literal, checking against IP range denylist.");
let ip = IPAddress::parse(destination.host()).map_err(|e| {
warn!("Failed to parse IP literal from string: {}", e);
Error::BadServerResponse("Invalid IP address")
})?;
let cidr_ranges_s = services().globals.ip_range_denylist().to_vec();
let mut cidr_ranges: Vec<IPAddress> = Vec::new();
for cidr in cidr_ranges_s {
cidr_ranges.push(IPAddress::parse(cidr).expect("we checked this at startup"));
}
for cidr in cidr_ranges {
if ip.includes(&cidr) {
return Err(Error::BadServerResponse(
"Not allowed to send requests to this IP",
));
}
}
}
debug!("Preparing to send request to {destination}");
let mut write_destination_to_cache = false;

View file

@ -6,6 +6,7 @@ use std::{
};
use figment::Figment;
use ruma::{OwnedServerName, RoomVersionId};
use serde::{de::IgnoredAny, Deserialize};
use tracing::{error, warn};
@ -128,6 +129,9 @@ pub struct Config {
#[serde(default = "Vec::new")]
pub prevent_media_downloads_from: Vec<OwnedServerName>,
#[serde(default = "default_ip_range_denylist")]
pub ip_range_denylist: Vec<String>,
#[serde(flatten)]
pub catchall: BTreeMap<String, IgnoredAny>,
}
@ -307,6 +311,14 @@ impl fmt::Display for Config {
}
&lst.join(", ")
}),
("Outbound Request IP Range Denylist", {
let mut lst = vec![];
for item in self.ip_range_denylist.iter().cloned().enumerate() {
let (_, ip): (usize, String) = item;
lst.push(ip);
}
&lst.join(", ")
}),
];
let mut msg: String = "Active config values:\n\n".to_owned();
@ -408,3 +420,27 @@ fn default_rocksdb_max_log_file_size() -> usize {
// 4 megabytes
4 * 1024 * 1024
}
fn default_ip_range_denylist() -> Vec<String> {
vec![
"127.0.0.0/8".to_owned(),
"10.0.0.0/8".to_owned(),
"172.16.0.0/12".to_owned(),
"192.168.0.0/16".to_owned(),
"100.64.0.0/10".to_owned(),
"192.0.0.0/24".to_owned(),
"169.254.0.0/16".to_owned(),
"192.88.99.0/24".to_owned(),
"198.18.0.0/15".to_owned(),
"192.0.2.0/24".to_owned(),
"198.51.100.0/24".to_owned(),
"203.0.113.0/24".to_owned(),
"224.0.0.0/4".to_owned(),
"::1/128".to_owned(),
"fe80::/10".to_owned(),
"fc00::/7".to_owned(),
"2001:db8::/32".to_owned(),
"ff00::/8".to_owned(),
"fec0::/10".to_owned(),
]
}

View file

@ -147,6 +147,12 @@ async fn main() {
};
let config = &services().globals.config;
// check if user specified valid IP CIDR ranges on startup
for cidr in services().globals.ip_range_denylist() {
let _ = ipaddress::IPAddress::parse(cidr)
.map_err(|e| error!("Error parsing specified IP CIDR range: {e}"));
}
if config.allow_registration
&& !config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
{

View file

@ -427,6 +427,10 @@ impl Service<'_> {
&self.config.prevent_media_downloads_from
}
pub fn ip_range_denylist(&self) -> &[String] {
&self.config.ip_range_denylist
}
pub fn supported_room_versions(&self) -> Vec<RoomVersionId> {
let mut room_versions: Vec<RoomVersionId> = vec![];
room_versions.extend(self.stable_room_versions.clone());

View file

@ -1,6 +1,7 @@
mod data;
pub use data::Data;
use ipaddress::IPAddress;
use std::{
collections::{BTreeMap, HashMap, HashSet},
@ -43,7 +44,7 @@ use tokio::{
select,
sync::{mpsc, Mutex, Semaphore},
};
use tracing::{debug, error, warn};
use tracing::{debug, error, info, warn};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum OutgoingKind {
@ -716,6 +717,29 @@ impl Service {
where
T: Debug,
{
if destination.is_ip_literal() {
info!("Destination is an IP literal, checking against IP range denylist.");
let ip = IPAddress::parse(destination.host()).map_err(|e| {
warn!("Failed to parse IP literal from string: {}", e);
Error::BadServerResponse("Invalid IP address")
})?;
let cidr_ranges_s = services().globals.ip_range_denylist().to_vec();
let mut cidr_ranges: Vec<IPAddress> = Vec::new();
for cidr in cidr_ranges_s {
cidr_ranges.push(IPAddress::parse(cidr).expect("we checked this at startup"));
}
for cidr in cidr_ranges {
if ip.includes(&cidr) {
return Err(Error::BadServerResponse(
"Not allowed to send requests to this IP",
));
}
}
}
debug!("Waiting for permit");
let permit = self.maximum_requests.acquire().await;
debug!("Got permit");