Make IP check public

This commit is contained in:
Felix Ableitner 2026-04-14 14:26:32 +02:00
parent 5e8e918003
commit a087c489a0
2 changed files with 41 additions and 9 deletions

View file

@ -20,7 +20,7 @@ use crate::{
http_signatures::sign_request,
protocol::verification::verify_domains_match,
traits::{Activity, Actor},
utils::is_invalid_ip,
utils::validate_ip,
};
use async_trait::async_trait;
use bytes::Bytes;
@ -183,7 +183,7 @@ impl<T: Clone> FederationConfig<T> {
}
let allow_local = std::env::var("DANGER_FEDERATION_ALLOW_LOCAL_IP").is_ok();
if !allow_local && is_invalid_ip(domain).await? {
if !allow_local && self.is_valid_ip(&url).await.is_err() {
return Err(Error::DomainResolveError(domain.to_string()));
}
}
@ -222,6 +222,15 @@ impl<T: Clone> FederationConfig<T> {
pub fn domain(&self) -> &str {
&self.domain
}
/// Resolve domain of the url and throw error if it points to local/private IP.
pub async fn is_valid_ip(&self, url: &Url) -> Result<(), Error> {
if self.debug {
return Ok(());
}
validate_ip(url).await
}
}
impl<T: Clone> FederationConfigBuilder<T> {

View file

@ -2,16 +2,33 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use crate::error::Error;
use tokio::net::lookup_host;
use url::{Host, Url};
// Resolve domain and see if it points to private IP
// TODO: Use is_global() once stabilized
// https://doc.rust-lang.org/std/net/enum.IpAddr.html#method.is_global
pub(crate) async fn is_invalid_ip(domain: &str) -> Result<bool, Error> {
let mut ips = lookup_host((domain, 80)).await?;
Ok(ips.any(|addr| match addr.ip() {
pub(crate) async fn validate_ip(url: &Url) -> Result<(), Error> {
let mut ip = vec![];
let host = url
.host()
.ok_or(Error::UrlVerificationError("Url must have a domain"))?;
match host {
Host::Domain(domain) => ip.extend(
lookup_host((domain.to_owned(), 80))
.await?
.map(|s| s.ip().to_canonical()),
),
Host::Ipv4(ipv4) => ip.push(ipv4.into()),
Host::Ipv6(ipv6) => ip.push(ipv6.into()),
};
let invalid_ip = ip.into_iter().any(|addr| match addr {
IpAddr::V4(addr) => v4_is_invalid(addr),
IpAddr::V6(addr) => v6_is_invalid(addr),
}))
});
if invalid_ip {
return Err(Error::DomainResolveError(host.to_string()));
}
Ok(())
}
fn v4_is_invalid(v4: Ipv4Addr) -> bool {
@ -48,8 +65,14 @@ mod test {
#[tokio::test]
async fn test_is_valid_ip() -> Result<(), Error> {
assert!(!is_invalid_ip("example.com").await?);
assert!(is_invalid_ip("localhost").await?);
assert!(validate_ip(&Url::parse("http://example.com")?)
.await
.is_ok());
assert!(validate_ip(&Url::parse("http://172.66.147.243")?)
.await
.is_ok());
assert!(validate_ip(&Url::parse("http://localhost")?).await.is_err());
assert!(validate_ip(&Url::parse("http://127.0.0.1")?).await.is_err());
Ok(())
}
}