diff --git a/server/core/src/config.rs b/server/core/src/config.rs index ad3d3bd9c..5ff4b9830 100644 --- a/server/core/src/config.rs +++ b/server/core/src/config.rs @@ -100,6 +100,59 @@ pub struct TlsConfiguration { pub client_ca: Option<PathBuf>, } +#[derive(Deserialize, Debug, Clone, Default)] +pub enum LdapAddressInfo { + #[default] + None, + #[serde(rename = "proxy-v2")] + ProxyV2, +} + +impl LdapAddressInfo { + pub fn proxy_v2(&self) -> bool { + matches!(self, Self::ProxyV2) + } +} + +impl Display for LdapAddressInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::None => f.write_str("none"), + Self::ProxyV2 => f.write_str("proxy-v2"), + } + } +} + +#[derive(Deserialize, Debug, Clone, Default)] +pub enum HttpAddressInfo { + #[default] + None, + #[serde(rename = "x-forward-for")] + XForwardFor, + #[serde(rename = "proxy-v2")] + ProxyV2, +} + +impl HttpAddressInfo { + pub fn is_x_forward_for(&self) -> bool { + matches!(self, Self::XForwardFor) + } + + pub fn proxy_v2(&self) -> bool { + matches!(self, Self::ProxyV2) + } +} + +impl Display for HttpAddressInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::None => f.write_str("none"), + Self::XForwardFor => f.write_str("x-forward-for"), + Self::ProxyV2 => f.write_str("proxy-v2"), + } + } +} + /// This is the Server Configuration as read from `server.toml` or environment variables. /// /// Fields noted as "REQUIRED" are required for the server to start, even if they show as optional due to how file parsing works. @@ -217,7 +270,10 @@ pub struct ServerConfigV2 { role: Option<ServerRole>, log_level: Option<LogLevel>, online_backup: Option<OnlineBackup>, - trust_x_forward_for: Option<bool>, + + http_client_address_info: Option<HttpAddressInfo>, + ldap_client_address_info: Option<LdapAddressInfo>, + adminbindpath: Option<String>, thread_count: Option<usize>, maximum_request_size_bytes: Option<usize>, @@ -490,7 +546,10 @@ pub struct Configuration { pub db_fs_type: Option<FsType>, pub db_arc_size: Option<usize>, pub maximum_request: usize, - pub trust_x_forward_for: bool, + + pub http_client_address_info: HttpAddressInfo, + pub ldap_client_address_info: LdapAddressInfo, + pub tls_config: Option<TlsConfiguration>, pub integration_test_config: Option<Box<IntegrationTestConfig>>, pub online_backup: Option<OnlineBackup>, @@ -522,7 +581,8 @@ impl Configuration { db_fs_type: None, db_arc_size: None, maximum_request: 256 * 1024, // 256k - trust_x_forward_for: None, + http_client_address_info: HttpAddressInfo::default(), + ldap_client_address_info: LdapAddressInfo::default(), tls_key: None, tls_chain: None, tls_client_ca: None, @@ -547,7 +607,8 @@ impl Configuration { db_fs_type: None, db_arc_size: None, maximum_request: 256 * 1024, // 256k - trust_x_forward_for: false, + http_client_address_info: HttpAddressInfo::default(), + ldap_client_address_info: LdapAddressInfo::default(), tls_config: None, integration_test_config: None, online_backup: None, @@ -587,7 +648,17 @@ impl fmt::Display for Configuration { None => write!(f, "arcsize: AUTO, "), }?; write!(f, "max request size: {}b, ", self.maximum_request)?; - write!(f, "trust X-Forwarded-For: {}, ", self.trust_x_forward_for)?; + write!( + f, + "http client address info: {}, ", + self.http_client_address_info + )?; + write!( + f, + "ldap client address info: {}, ", + self.ldap_client_address_info + )?; + write!(f, "with TLS: {}, ", self.tls_config.is_some())?; match &self.online_backup { Some(bck) => write!( @@ -642,7 +713,8 @@ pub struct ConfigurationBuilder { db_fs_type: Option<FsType>, db_arc_size: Option<usize>, maximum_request: usize, - trust_x_forward_for: Option<bool>, + http_client_address_info: HttpAddressInfo, + ldap_client_address_info: LdapAddressInfo, tls_key: Option<PathBuf>, tls_chain: Option<PathBuf>, tls_client_ca: Option<PathBuf>, @@ -691,8 +763,8 @@ impl ConfigurationBuilder { self.db_arc_size = env_config.db_arc_size; } - if env_config.trust_x_forward_for.is_some() { - self.trust_x_forward_for = env_config.trust_x_forward_for; + if env_config.trust_x_forward_for == Some(true) { + self.http_client_address_info = HttpAddressInfo::XForwardFor; } if env_config.tls_key.is_some() { @@ -813,8 +885,8 @@ impl ConfigurationBuilder { self.db_arc_size = config.db_arc_size; } - if config.trust_x_forward_for.is_some() { - self.trust_x_forward_for = config.trust_x_forward_for; + if config.trust_x_forward_for == Some(true) { + self.http_client_address_info = HttpAddressInfo::XForwardFor; } if config.online_backup.is_some() { @@ -893,8 +965,12 @@ impl ConfigurationBuilder { self.db_arc_size = config.db_arc_size; } - if config.trust_x_forward_for.is_some() { - self.trust_x_forward_for = config.trust_x_forward_for; + if let Some(http_client_address_info) = config.http_client_address_info { + self.http_client_address_info = http_client_address_info + } + + if let Some(ldap_client_address_info) = config.ldap_client_address_info { + self.ldap_client_address_info = ldap_client_address_info } if config.online_backup.is_some() { @@ -930,7 +1006,8 @@ impl ConfigurationBuilder { db_fs_type, db_arc_size, maximum_request, - trust_x_forward_for, + http_client_address_info, + ldap_client_address_info, tls_key, tls_chain, tls_client_ca, @@ -986,7 +1063,6 @@ impl ConfigurationBuilder { let adminbindpath = adminbindpath.unwrap_or(env!("KANIDM_SERVER_ADMIN_BIND_PATH").to_string()); let address = bindaddress.unwrap_or(DEFAULT_SERVER_ADDRESS.to_string()); - let trust_x_forward_for = trust_x_forward_for.unwrap_or_default(); let output_mode = output_mode.unwrap_or_default(); let role = role.unwrap_or(ServerRole::WriteReplica); let log_level = log_level.unwrap_or_default(); @@ -1000,7 +1076,8 @@ impl ConfigurationBuilder { db_fs_type, db_arc_size, maximum_request, - trust_x_forward_for, + http_client_address_info, + ldap_client_address_info, tls_config, online_backup, domain, diff --git a/server/core/src/https/mod.rs b/server/core/src/https/mod.rs index 645f35202..c0302c911 100644 --- a/server/core/src/https/mod.rs +++ b/server/core/src/https/mod.rs @@ -211,7 +211,7 @@ pub async fn create_https_server( error!(?err, "Unable to generate content security policy"); })?; - let trust_x_forward_for = config.trust_x_forward_for; + let trust_x_forward_for = config.http_client_address_info.is_x_forward_for(); let origin = Url::parse(&config.origin) // Should be impossible! diff --git a/server/testkit-macros/src/entry.rs b/server/testkit-macros/src/entry.rs index 81e1ef701..90369891a 100644 --- a/server/testkit-macros/src/entry.rs +++ b/server/testkit-macros/src/entry.rs @@ -10,7 +10,7 @@ const ALLOWED_ATTRIBUTES: &[&str] = &[ "threads", "db_path", "maximum_request", - "trust_x_forward_for", + "http_client_address_info", "role", "output_mode", "log_level", diff --git a/server/testkit/tests/testkit/https_extractors.rs b/server/testkit/tests/testkit/https_extractors.rs index b664517cb..520816138 100644 --- a/server/testkit/tests/testkit/https_extractors.rs +++ b/server/testkit/tests/testkit/https_extractors.rs @@ -5,12 +5,13 @@ use std::{ use kanidm_client::KanidmClient; use kanidm_proto::constants::X_FORWARDED_FOR; +use kanidmd_core::config::HttpAddressInfo; const DEFAULT_IP_ADDRESS: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)); // *test where we don't trust the x-forwarded-for header -#[kanidmd_testkit::test(trust_x_forward_for = false)] +#[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::None)] async fn dont_trust_xff_send_header(rsclient: &KanidmClient) { let client = rsclient.client(); @@ -31,7 +32,7 @@ async fn dont_trust_xff_send_header(rsclient: &KanidmClient) { assert_eq!(ip_res, DEFAULT_IP_ADDRESS); } -#[kanidmd_testkit::test(trust_x_forward_for = false)] +#[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::None)] async fn dont_trust_xff_dont_send_header(rsclient: &KanidmClient) { let client = rsclient.client(); @@ -57,7 +58,7 @@ async fn dont_trust_xff_dont_send_header(rsclient: &KanidmClient) { // *test where we trust the x-forwarded-for header -#[kanidmd_testkit::test(trust_x_forward_for = true)] +#[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor)] async fn trust_xff_send_invalid_header_single_value(rsclient: &KanidmClient) { let client = rsclient.client(); @@ -77,7 +78,7 @@ async fn trust_xff_send_invalid_header_single_value(rsclient: &KanidmClient) { // TODO: Right now we reject the request only if the leftmost address is invalid. In the future that could change so we could also have a test // with a valid leftmost address and an invalid address later in the list. Right now it wouldn't work. // -#[kanidmd_testkit::test(trust_x_forward_for = true)] +#[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor)] async fn trust_xff_send_invalid_header_multiple_values(rsclient: &KanidmClient) { let client = rsclient.client(); @@ -94,7 +95,7 @@ async fn trust_xff_send_invalid_header_multiple_values(rsclient: &KanidmClient) assert_eq!(res.status(), 400); } -#[kanidmd_testkit::test(trust_x_forward_for = true)] +#[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor)] async fn trust_xff_send_valid_header_single_ipv4_address(rsclient: &KanidmClient) { let ip_addr = "2001:db8:85a3:8d3:1319:8a2e:370:7348"; @@ -114,7 +115,7 @@ async fn trust_xff_send_valid_header_single_ipv4_address(rsclient: &KanidmClient assert_eq!(ip_res, IpAddr::from_str(ip_addr).unwrap()); } -#[kanidmd_testkit::test(trust_x_forward_for = true)] +#[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor)] async fn trust_xff_send_valid_header_single_ipv6_address(rsclient: &KanidmClient) { let ip_addr = "203.0.113.195"; @@ -134,7 +135,7 @@ async fn trust_xff_send_valid_header_single_ipv6_address(rsclient: &KanidmClient assert_eq!(ip_res, IpAddr::from_str(ip_addr).unwrap()); } -#[kanidmd_testkit::test(trust_x_forward_for = true)] +#[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor)] async fn trust_xff_send_valid_header_multiple_address(rsclient: &KanidmClient) { let first_ip_addr = "203.0.113.195, 2001:db8:85a3:8d3:1319:8a2e:370:7348"; @@ -175,7 +176,7 @@ async fn trust_xff_send_valid_header_multiple_address(rsclient: &KanidmClient) { ); } -#[kanidmd_testkit::test(trust_x_forward_for = true)] +#[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor)] async fn trust_xff_dont_send_header(rsclient: &KanidmClient) { let client = rsclient.client();