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:
parent
71d247232d
commit
fa0c083555
8 changed files with 186 additions and 3 deletions
61
Cargo.lock
generated
61
Cargo.lock
generated
|
@ -400,6 +400,7 @@ dependencies = [
|
||||||
"hyper",
|
"hyper",
|
||||||
"hyperlocal",
|
"hyperlocal",
|
||||||
"image",
|
"image",
|
||||||
|
"ipaddress",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"lru-cache",
|
"lru-cache",
|
||||||
|
@ -1080,6 +1081,20 @@ version = "3.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02"
|
checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ipaddress"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "957bb9f3645d6bb7f36df99d5105b4866aa79749819d7c176a170a27dc477cbf"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
|
"num",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipconfig"
|
name = "ipconfig"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
@ -1404,6 +1419,20 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
|
||||||
|
dependencies = [
|
||||||
|
"num-bigint",
|
||||||
|
"num-complex",
|
||||||
|
"num-integer",
|
||||||
|
"num-iter",
|
||||||
|
"num-rational",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-bigint"
|
name = "num-bigint"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
|
@ -1415,6 +1444,15 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-complex"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.45"
|
version = "0.1.45"
|
||||||
|
@ -1425,6 +1463,29 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-iter"
|
||||||
|
version = "0.1.43"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-rational"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-bigint",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
|
|
|
@ -101,6 +101,9 @@ tikv-jemallocator = { version = "0.5.0", features = ["unprefixed_malloc_on_suppo
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
async-trait = "0.1.77"
|
async-trait = "0.1.77"
|
||||||
|
|
||||||
|
# used for checking if an IP is in specific subnets / CIDR ranges
|
||||||
|
ipaddress = "0.1.3"
|
||||||
|
|
||||||
sd-notify = { version = "0.4.1", optional = true }
|
sd-notify = { version = "0.4.1", optional = true }
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
|
|
@ -77,7 +77,33 @@ max_request_size = 20_000_000 # in bytes
|
||||||
# See https://breachattack.com/ and https://wikipedia.org/wiki/BREACH before deciding to enable this.
|
# See https://breachattack.com/ and https://wikipedia.org/wiki/BREACH before deciding to enable this.
|
||||||
zstd_compression = false
|
zstd_compression = false
|
||||||
|
|
||||||
|
# Vector list of IPv4 and IPv6 CIDR ranges / subnets *in quotes* that you do not want conduwuit to send outbound requests to.
|
||||||
|
# Defaults to RFC1918, unroutable, loopback, multicast, and testnet addresses for security.
|
||||||
|
#
|
||||||
|
# To disable, set this to be an empty vector (`[]`).
|
||||||
|
#
|
||||||
|
# Currently this does not account for proxies in use like Synapse does.
|
||||||
|
ip_range_denylist = [
|
||||||
|
"127.0.0.0/8",
|
||||||
|
"10.0.0.0/8",
|
||||||
|
"172.16.0.0/12",
|
||||||
|
"192.168.0.0/16",
|
||||||
|
"100.64.0.0/10",
|
||||||
|
"192.0.0.0/24",
|
||||||
|
"169.254.0.0/16",
|
||||||
|
"192.88.99.0/24",
|
||||||
|
"198.18.0.0/15",
|
||||||
|
"192.0.2.0/24",
|
||||||
|
"198.51.100.0/24",
|
||||||
|
"203.0.113.0/24",
|
||||||
|
"224.0.0.0/4",
|
||||||
|
"::1/128",
|
||||||
|
"fe80::/10",
|
||||||
|
"fc00::/7",
|
||||||
|
"2001:db8::/32",
|
||||||
|
"ff00::/8",
|
||||||
|
"fec0::/10",
|
||||||
|
]
|
||||||
|
|
||||||
### Moderation / Privacy / Security
|
### Moderation / Privacy / Security
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ use futures_util::future::TryFutureExt;
|
||||||
use get_profile_information::v1::ProfileField;
|
use get_profile_information::v1::ProfileField;
|
||||||
use http::header::{HeaderValue, AUTHORIZATION};
|
use http::header::{HeaderValue, AUTHORIZATION};
|
||||||
|
|
||||||
|
use ipaddress::IPAddress;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
api::{
|
api::{
|
||||||
client::error::{Error as RumaError, ErrorKind},
|
client::error::{Error as RumaError, ErrorKind},
|
||||||
|
@ -114,7 +115,6 @@ impl FedDest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(request))]
|
|
||||||
pub(crate) async fn send_request<T: OutgoingRequest>(
|
pub(crate) async fn send_request<T: OutgoingRequest>(
|
||||||
destination: &ServerName,
|
destination: &ServerName,
|
||||||
request: T,
|
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}");
|
debug!("Preparing to send request to {destination}");
|
||||||
|
|
||||||
let mut write_destination_to_cache = false;
|
let mut write_destination_to_cache = false;
|
||||||
|
|
|
@ -6,6 +6,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use figment::Figment;
|
use figment::Figment;
|
||||||
|
|
||||||
use ruma::{OwnedServerName, RoomVersionId};
|
use ruma::{OwnedServerName, RoomVersionId};
|
||||||
use serde::{de::IgnoredAny, Deserialize};
|
use serde::{de::IgnoredAny, Deserialize};
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
|
@ -128,6 +129,9 @@ pub struct Config {
|
||||||
#[serde(default = "Vec::new")]
|
#[serde(default = "Vec::new")]
|
||||||
pub prevent_media_downloads_from: Vec<OwnedServerName>,
|
pub prevent_media_downloads_from: Vec<OwnedServerName>,
|
||||||
|
|
||||||
|
#[serde(default = "default_ip_range_denylist")]
|
||||||
|
pub ip_range_denylist: Vec<String>,
|
||||||
|
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub catchall: BTreeMap<String, IgnoredAny>,
|
pub catchall: BTreeMap<String, IgnoredAny>,
|
||||||
}
|
}
|
||||||
|
@ -307,6 +311,14 @@ impl fmt::Display for Config {
|
||||||
}
|
}
|
||||||
&lst.join(", ")
|
&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();
|
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 megabytes
|
||||||
4 * 1024 * 1024
|
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(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
|
@ -147,6 +147,12 @@ async fn main() {
|
||||||
};
|
};
|
||||||
let config = &services().globals.config;
|
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
|
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
|
||||||
{
|
{
|
||||||
|
|
|
@ -427,6 +427,10 @@ impl Service<'_> {
|
||||||
&self.config.prevent_media_downloads_from
|
&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> {
|
pub fn supported_room_versions(&self) -> Vec<RoomVersionId> {
|
||||||
let mut room_versions: Vec<RoomVersionId> = vec![];
|
let mut room_versions: Vec<RoomVersionId> = vec![];
|
||||||
room_versions.extend(self.stable_room_versions.clone());
|
room_versions.extend(self.stable_room_versions.clone());
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
mod data;
|
mod data;
|
||||||
|
|
||||||
pub use data::Data;
|
pub use data::Data;
|
||||||
|
use ipaddress::IPAddress;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::{BTreeMap, HashMap, HashSet},
|
collections::{BTreeMap, HashMap, HashSet},
|
||||||
|
@ -43,7 +44,7 @@ use tokio::{
|
||||||
select,
|
select,
|
||||||
sync::{mpsc, Mutex, Semaphore},
|
sync::{mpsc, Mutex, Semaphore},
|
||||||
};
|
};
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum OutgoingKind {
|
pub enum OutgoingKind {
|
||||||
|
@ -716,6 +717,29 @@ impl Service {
|
||||||
where
|
where
|
||||||
T: Debug,
|
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");
|
debug!("Waiting for permit");
|
||||||
let permit = self.maximum_requests.acquire().await;
|
let permit = self.maximum_requests.acquire().await;
|
||||||
debug!("Got permit");
|
debug!("Got permit");
|
||||||
|
|
Loading…
Add table
Reference in a new issue