add access check for IP networks
This commit is contained in:
parent
bf655508e9
commit
4b6c7022c4
21
Cargo.lock
generated
21
Cargo.lock
generated
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"] }
|
||||
|
89
crates/server/src/access.rs
Normal file
89
crates/server/src/access.rs
Normal 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());
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user