add access check for IP networks

This commit is contained in:
Benjamin Fry 2024-01-06 11:23:58 -08:00
parent bf655508e9
commit 4b6c7022c4
5 changed files with 114 additions and 0 deletions

21
Cargo.lock generated
View File

@ -1074,7 +1074,9 @@ dependencies = [
"hickory-recursor",
"hickory-resolver",
"http",
"ipnet",
"openssl",
"prefix-trie",
"rusqlite",
"rustls",
"serde",
@ -1370,6 +1372,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
@ -1551,6 +1562,16 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "prefix-trie"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e27ac3b8f918b78293e43b30307c6f7fd3fd6144ba9b27a2f553e3adddde5812"
dependencies = [
"ipnet",
"num-traits",
]
[[package]]
name = "proc-macro2"
version = "1.0.78"

View File

@ -92,6 +92,7 @@ js-sys = "0.3.44"
once_cell = "1.18.0"
lru-cache = "0.1.2"
pin-utils = "0.1.0"
prefix-trie = "0.2.4"
radix_trie = "0.2.0"
rand = "0.8"
regex = "1.3.4"

View File

@ -122,7 +122,9 @@ h2 = { workspace = true, features = ["stream"], optional = true }
h3 = { workspace = true, optional = true }
h3-quinn = { workspace = true, optional = true }
http = { workspace = true, optional = true }
ipnet.workspace = true
openssl = { workspace = true, features = ["v102", "v110"], optional = true }
prefix-trie.workspace = true
rusqlite = { workspace = true, features = ["bundled", "time"], optional = true }
rustls = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"] }

View File

@ -0,0 +1,89 @@
use std::net::IpAddr;
use ipnet::{IpNet, Ipv4Net, Ipv6Net};
use prefix_trie::PrefixSet;
/// Type to evaluate access from a source address for accessing the server.
#[derive(Default)]
pub(crate) struct Access {
allow_ipv4: Option<PrefixSet<Ipv4Net>>,
allow_ipv6: Option<PrefixSet<Ipv6Net>>,
}
impl Access {
/// Insert a new network that is allowed access to the server
pub(crate) fn insert(&mut self, network: IpNet) {
match network {
IpNet::V4(v4) => {
if self.allow_ipv4.is_none() {
self.allow_ipv4 = Some(PrefixSet::default());
}
self.allow_ipv4.as_mut().unwrap().insert(v4);
}
IpNet::V6(v6) => {
if self.allow_ipv6.is_none() {
self.allow_ipv6 = Some(PrefixSet::default());
}
self.allow_ipv6.as_mut().unwrap().insert(v6);
}
}
}
/// Evaluate the IP address against the allowed networks
///
/// # Return
///
/// Ok if access is granted, Err otherwise
pub(crate) fn allow(&self, ip: IpAddr) -> Result<(), ()> {
match ip {
IpAddr::V4(v4) => {
let v4 = Ipv4Net::from(v4);
self.allow_ipv4.as_ref().map_or(Ok(()), |allow_ipv4| {
allow_ipv4.get_lpm(&v4).map(|_| ()).ok_or(())
})
}
IpAddr::V6(v6) => {
let v6 = Ipv6Net::from(v6);
self.allow_ipv6.as_ref().map_or(Ok(()), |allow_ipv6| {
allow_ipv6.get_lpm(&v6).map(|_| ()).ok_or(())
})
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_none() {
let mut access = Access::default();
assert!(access.allow("192.168.1.1".parse().unwrap()).is_ok());
assert!(access.allow("fd00::1".parse().unwrap()).is_ok());
}
#[test]
fn test_v4() {
let mut access = Access::default();
access.insert("192.168.1.0/24".parse().unwrap());
assert!(access.allow("192.168.1.1".parse().unwrap()).is_ok());
assert!(access.allow("192.168.1.255".parse().unwrap()).is_ok());
assert!(access.allow("192.168.2.1".parse().unwrap()).is_err());
assert!(access.allow("192.168.0.0".parse().unwrap()).is_err());
}
#[test]
fn test_v6() {
let mut access = Access::default();
access.insert("fd00::/120".parse().unwrap());
assert!(access.allow("fd00::1".parse().unwrap()).is_ok());
assert!(access.allow("fd00::00ff".parse().unwrap()).is_ok());
assert!(access.allow("fd00::ffff".parse().unwrap()).is_err());
assert!(access.allow("fd00::1:1".parse().unwrap()).is_err());
}
}

View File

@ -54,6 +54,7 @@ pub use hickory_recursor as recursor;
#[cfg_attr(docsrs, doc(cfg(feature = "resolver")))]
pub use hickory_resolver as resolver;
mod access;
pub mod authority;
pub mod config;
pub mod error;