From 11f95ff384ff8e857e6d660976b7cd3b82159f2e Mon Sep 17 00:00:00 2001 From: Nutomic Date: Wed, 28 Jan 2026 13:44:39 +0000 Subject: [PATCH] Improve error message, allow local IP federation via env var (#158) * Improve error message, allow local IP federation via env var (fixes #152) * fix --- src/config.rs | 34 +++++++++++++++++----------------- src/error.rs | 3 +++ 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/config.rs b/src/config.rs index 9eb0b97..a7cbf94 100644 --- a/src/config.rs +++ b/src/config.rs @@ -25,6 +25,7 @@ use async_trait::async_trait; use bytes::Bytes; use derive_builder::Builder; use dyn_clone::{clone_trait_object, DynClone}; +use itertools::Itertools; use moka::future::Cache; use regex::Regex; use reqwest::{redirect::Policy, Client, Request}; @@ -186,27 +187,26 @@ impl FederationConfig { // 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 - let invalid_ip = - lookup_host((domain.to_owned(), 80)) - .await? - .any(|addr| match addr.ip() { - IpAddr::V4(addr) => { - addr.is_private() - || addr.is_link_local() - || addr.is_loopback() - || addr.is_multicast() - } - IpAddr::V6(addr) => { - addr.is_loopback() + let mut ips = lookup_host((domain.to_owned(), 80)).await?; + let allow_local = std::env::var("DANGER_FEDERATION_ALLOW_LOCAL_IP").is_ok(); + let invalid_ip = !allow_local + && ips.any(|addr| match addr.ip() { + IpAddr::V4(addr) => { + addr.is_private() + || addr.is_link_local() + || addr.is_loopback() + || addr.is_multicast() + } + IpAddr::V6(addr) => { + addr.is_loopback() || addr.is_multicast() || ((addr.segments()[0] & 0xfe00) == 0xfc00) // is_unique_local || ((addr.segments()[0] & 0xffc0) == 0xfe80) // is_unicast_link_local - } - }); + } + }); if invalid_ip { - return Err(Error::UrlVerificationError( - "Localhost is only allowed in debug mode", - )); + let ip_addrs = ips.join(", "); + return Err(Error::DomainResolveError(domain.to_string(), ip_addrs)); } } diff --git a/src/error.rs b/src/error.rs index 0661071..07e377b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -28,6 +28,9 @@ pub enum Error { /// url verification error #[error("URL failed verification: {0}")] UrlVerificationError(&'static str), + /// Resolving domain points to local IP. + #[error("Resolving domain {0} points to local IP {1}. This may indicate an attacker attempting to access internal services. If intentional, you can ignore this error by setting DANGER_FEDERATION_ALLOW_LOCAL_IP=1")] + DomainResolveError(String, String), /// Incoming activity has invalid digest for body #[error("Incoming activity has invalid digest for body")] ActivityBodyDigestInvalid,