This commit is contained in:
William Brown 2025-04-04 10:49:55 +10:00
parent b27de62f32
commit 8bc9c93ce9
4 changed files with 63 additions and 36 deletions
examples
server
core/src
testkit/tests/testkit

View file

@ -13,16 +13,6 @@ bindaddress = "[::]:443"
# Defaults to "" (disabled) # Defaults to "" (disabled)
# ldapbindaddress = "[::]:636" # ldapbindaddress = "[::]:636"
# #
# HTTPS requests can be reverse proxied by a loadbalancer.
# To preserve the original IP of the caller, these systems
# will often add a header such as "Forwarded" or
# "X-Forwarded-For". If set to true, then this header is
# respected as the "authoritative" source of the IP of the
# connected client. If you are not using a load balancer
# then you should leave this value as default.
# Defaults to false
# trust_x_forward_for = false
#
# The path to the kanidm database. # The path to the kanidm database.
db_path = "/var/lib/private/kanidm/kanidm.db" db_path = "/var/lib/private/kanidm/kanidm.db"
# #
@ -86,6 +76,30 @@ domain = "idm.example.com"
# origin = "https://idm.example.com" # origin = "https://idm.example.com"
origin = "https://idm.example.com:8443" origin = "https://idm.example.com:8443"
# #
# HTTPS requests can be reverse proxied by a loadbalancer.
# To preserve the original IP of the caller, these systems
# will often add a header such as "Forwarded" or
# "X-Forwarded-For". Some other proxies can use the HAProxy
# proxy v2 header.
# This setting allows configuration of the range of trusted
# IPs which can supply this header information, and which
# format the information is provided in.
# Defaults to "none" (no trusted sources)
# [http_client_address_info]
# proxy-v2 = ["127.0.0.1"]
# x-forward-for = ["127.0.0.1"]
# LDAPS requests can be reverse proxied by a loadbalancer.
# To preserve the original IP of the caller, these systems
# will can add a header such as the HAProxy proxy v2 header.
# This setting allows configuration of the range of trusted
# IPs which can supply this header information, and which
# format the information is provided in.
# Defaults to "none" (no trusted sources)
# [ldap_client_address_info]
# proxy-v2 = ["127.0.0.1"]
[online_backup] [online_backup]
# The path to the output folder for online backups # The path to the output folder for online backups
path = "/var/lib/private/kanidm/backups/" path = "/var/lib/private/kanidm/backups/"

View file

@ -13,16 +13,6 @@ bindaddress = "[::]:8443"
# Defaults to "" (disabled) # Defaults to "" (disabled)
# ldapbindaddress = "[::]:3636" # ldapbindaddress = "[::]:3636"
# #
# HTTPS requests can be reverse proxied by a loadbalancer.
# To preserve the original IP of the caller, these systems
# will often add a header such as "Forwarded" or
# "X-Forwarded-For". If set to true, then this header is
# respected as the "authoritative" source of the IP of the
# connected client. If you are not using a load balancer
# then you should leave this value as default.
# Defaults to false
# trust_x_forward_for = false
#
# The path to the kanidm database. # The path to the kanidm database.
db_path = "/data/kanidm.db" db_path = "/data/kanidm.db"
# #
@ -85,7 +75,30 @@ domain = "idm.example.com"
# not consistent, the server WILL refuse to start! # not consistent, the server WILL refuse to start!
# origin = "https://idm.example.com" # origin = "https://idm.example.com"
origin = "https://idm.example.com:8443" origin = "https://idm.example.com:8443"
#
# HTTPS requests can be reverse proxied by a loadbalancer.
# To preserve the original IP of the caller, these systems
# will often add a header such as "Forwarded" or
# "X-Forwarded-For". Some other proxies can use the HAProxy
# proxy v2 header.
# This setting allows configuration of the range of trusted
# IPs which can supply this header information, and which
# format the information is provided in.
# Defaults to "none" (no trusted sources)
# [http_client_address_info]
# proxy-v2 = ["127.0.0.1"]
# x-forward-for = ["127.0.0.1"]
# LDAPS requests can be reverse proxied by a loadbalancer.
# To preserve the original IP of the caller, these systems
# will can add a header such as the HAProxy proxy v2 header.
# This setting allows configuration of the range of trusted
# IPs which can supply this header information, and which
# format the information is provided in.
# Defaults to "none" (no trusted sources)
# [ldap_client_address_info]
# proxy-v2 = ["127.0.0.1"]
[online_backup] [online_backup]
# The path to the output folder for online backups # The path to the output folder for online backups
path = "/data/kanidm/backups/" path = "/data/kanidm/backups/"

View file

@ -105,12 +105,12 @@ pub enum LdapAddressInfo {
#[default] #[default]
None, None,
#[serde(rename = "proxy-v2")] #[serde(rename = "proxy-v2")]
ProxyV2 { trusted: HashSet<IpAddr> }, ProxyV2(HashSet<IpAddr>),
} }
impl LdapAddressInfo { impl LdapAddressInfo {
pub fn trusted_proxy_v2(&self) -> Option<HashSet<IpAddr>> { pub fn trusted_proxy_v2(&self) -> Option<HashSet<IpAddr>> {
if let Self::ProxyV2 { trusted } = self { if let Self::ProxyV2(trusted) = self {
Some(trusted.clone()) Some(trusted.clone())
} else { } else {
None None
@ -122,7 +122,7 @@ impl Display for LdapAddressInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::None => f.write_str("none"), Self::None => f.write_str("none"),
Self::ProxyV2 { trusted } => { Self::ProxyV2(trusted) => {
f.write_str("proxy-v2 [ ")?; f.write_str("proxy-v2 [ ")?;
for ip in trusted { for ip in trusted {
write!(f, "{} ", ip)?; write!(f, "{} ", ip)?;
@ -152,24 +152,24 @@ pub enum HttpAddressInfo {
#[default] #[default]
None, None,
#[serde(rename = "x-forward-for")] #[serde(rename = "x-forward-for")]
XForwardFor { trusted: HashSet<IpAddr> }, XForwardFor(HashSet<IpAddr>),
#[serde(rename = "x-forward-for-all-source-trusted")] #[serde(rename = "x-forward-for-all-source-trusted")]
XForwardForAllSourcesTrusted, XForwardForAllSourcesTrusted,
#[serde(rename = "proxy-v2")] #[serde(rename = "proxy-v2")]
ProxyV2 { trusted: HashSet<IpAddr> }, ProxyV2(HashSet<IpAddr>),
} }
impl HttpAddressInfo { impl HttpAddressInfo {
pub(crate) fn trusted_x_forward_for(&self) -> Option<AddressRange> { pub(crate) fn trusted_x_forward_for(&self) -> Option<AddressRange> {
match self { match self {
Self::XForwardForAllSourcesTrusted => Some(AddressRange::All), Self::XForwardForAllSourcesTrusted => Some(AddressRange::All),
Self::XForwardFor { trusted } => Some(AddressRange::Range(trusted.clone())), Self::XForwardFor(trusted) => Some(AddressRange::Range(trusted.clone())),
_ => None, _ => None,
} }
} }
pub(crate) fn trusted_proxy_v2(&self) -> Option<HashSet<IpAddr>> { pub(crate) fn trusted_proxy_v2(&self) -> Option<HashSet<IpAddr>> {
if let Self::ProxyV2 { trusted } = self { if let Self::ProxyV2(trusted) = self {
Some(trusted.clone()) Some(trusted.clone())
} else { } else {
None None
@ -182,7 +182,7 @@ impl Display for HttpAddressInfo {
match self { match self {
Self::None => f.write_str("none"), Self::None => f.write_str("none"),
Self::XForwardFor { trusted } => { Self::XForwardFor(trusted) => {
f.write_str("x-forward-for [ ")?; f.write_str("x-forward-for [ ")?;
for ip in trusted { for ip in trusted {
write!(f, "{} ", ip)?; write!(f, "{} ", ip)?;
@ -192,7 +192,7 @@ impl Display for HttpAddressInfo {
Self::XForwardForAllSourcesTrusted => { Self::XForwardForAllSourcesTrusted => {
f.write_str("x-forward-for [ ALL SOURCES TRUSTED ]") f.write_str("x-forward-for [ ALL SOURCES TRUSTED ]")
} }
Self::ProxyV2 { trusted } => { Self::ProxyV2(trusted) => {
f.write_str("proxy-v2 [ ")?; f.write_str("proxy-v2 [ ")?;
for ip in trusted { for ip in trusted {
write!(f, "{} ", ip)?; write!(f, "{} ", ip)?;

View file

@ -58,7 +58,7 @@ async fn dont_trust_xff_dont_send_header(rsclient: &KanidmClient) {
// *test where we trust the x-forwarded-for header // *test where we trust the x-forwarded-for header
#[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor { trusted: [DEFAULT_IP_ADDRESS].into() })] #[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor ( [DEFAULT_IP_ADDRESS].into() ))]
async fn trust_xff_send_invalid_header_single_value(rsclient: &KanidmClient) { async fn trust_xff_send_invalid_header_single_value(rsclient: &KanidmClient) {
let client = rsclient.client(); let client = rsclient.client();
@ -78,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 // 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. // with a valid leftmost address and an invalid address later in the list. Right now it wouldn't work.
// //
#[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor { trusted: [DEFAULT_IP_ADDRESS].into() })] #[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor ( [DEFAULT_IP_ADDRESS].into() ))]
async fn trust_xff_send_invalid_header_multiple_values(rsclient: &KanidmClient) { async fn trust_xff_send_invalid_header_multiple_values(rsclient: &KanidmClient) {
let client = rsclient.client(); let client = rsclient.client();
@ -95,7 +95,7 @@ async fn trust_xff_send_invalid_header_multiple_values(rsclient: &KanidmClient)
assert_eq!(res.status(), 400); assert_eq!(res.status(), 400);
} }
#[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor { trusted: [DEFAULT_IP_ADDRESS].into() })] #[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor ( [DEFAULT_IP_ADDRESS].into() ))]
async fn trust_xff_send_valid_header_single_ipv4_address(rsclient: &KanidmClient) { async fn trust_xff_send_valid_header_single_ipv4_address(rsclient: &KanidmClient) {
let ip_addr = "2001:db8:85a3:8d3:1319:8a2e:370:7348"; let ip_addr = "2001:db8:85a3:8d3:1319:8a2e:370:7348";
@ -115,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()); assert_eq!(ip_res, IpAddr::from_str(ip_addr).unwrap());
} }
#[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor { trusted: [DEFAULT_IP_ADDRESS].into() })] #[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor ( [DEFAULT_IP_ADDRESS].into() ))]
async fn trust_xff_send_valid_header_single_ipv6_address(rsclient: &KanidmClient) { async fn trust_xff_send_valid_header_single_ipv6_address(rsclient: &KanidmClient) {
let ip_addr = "203.0.113.195"; let ip_addr = "203.0.113.195";
@ -135,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()); assert_eq!(ip_res, IpAddr::from_str(ip_addr).unwrap());
} }
#[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor { trusted: [DEFAULT_IP_ADDRESS].into() })] #[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor ( [DEFAULT_IP_ADDRESS].into() ))]
async fn trust_xff_send_valid_header_multiple_address(rsclient: &KanidmClient) { 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"; let first_ip_addr = "203.0.113.195, 2001:db8:85a3:8d3:1319:8a2e:370:7348";
@ -176,7 +176,7 @@ async fn trust_xff_send_valid_header_multiple_address(rsclient: &KanidmClient) {
); );
} }
#[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor { trusted: [DEFAULT_IP_ADDRESS].into() })] #[kanidmd_testkit::test(http_client_address_info = HttpAddressInfo::XForwardFor ( [DEFAULT_IP_ADDRESS].into() ))]
async fn trust_xff_dont_send_header(rsclient: &KanidmClient) { async fn trust_xff_dont_send_header(rsclient: &KanidmClient) {
let client = rsclient.client(); let client = rsclient.client();