diff --git a/changelog.d/1932.feature b/changelog.d/1932.feature new file mode 100644 index 000000000..f1575a9ae --- /dev/null +++ b/changelog.d/1932.feature @@ -0,0 +1 @@ +Added a configuration option to allow choosing a client IP source that is not the TCP connecting IP. Contributed by @nex. diff --git a/conduwuit-example.toml b/conduwuit-example.toml index a98a35639..211c62e8f 100644 --- a/conduwuit-example.toml +++ b/conduwuit-example.toml @@ -290,6 +290,26 @@ # #ip_lookup_strategy = 5 +# The source to use for discovering the real connecting client IP. +# +# Takes the number of any of the following options: +# +# 0 - Use the IP address of the TCP connection +# 1 - `Cf-Connecting-Ip` header +# 2 - `CloudFront-Viewer-Address` header +# 3 - `Fly-Client-IP` header +# 4 - rightmost value of the `X-Forwarded-For` header +# 5 - `True-Client-Ip` header +# 6 - `X-Envoy-External-Address` header +# 7 - `X-Real-Ip` header +# +# Only change this from `0` if you are certain only your reverse proxy +# will send the expected header. There is no "is the connecting IP allowed +# to set this header" check; if the header selected is present, it is +# used. +# +#request_ip_source = 0 + # Max request size for file uploads in bytes. Defaults to 20MB. # Also limits incoming federated media. # diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index e56dcbf0b..0adbf7b09 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -370,6 +370,28 @@ pub struct Config { #[serde(default = "default_ip_lookup_strategy")] pub ip_lookup_strategy: u8, + /// The source to use for discovering the real connecting client IP. + /// + /// Takes the number of any of the following options: + /// + /// 0 - Use the IP address of the TCP connection + /// 1 - `Cf-Connecting-Ip` header + /// 2 - `CloudFront-Viewer-Address` header + /// 3 - `Fly-Client-IP` header + /// 4 - rightmost value of the `X-Forwarded-For` header + /// 5 - `True-Client-Ip` header + /// 6 - `X-Envoy-External-Address` header + /// 7 - `X-Real-Ip` header + /// + /// Only change this from `0` if you are certain only your reverse proxy + /// will send the expected header. There is no "is the connecting IP allowed + /// to set this header" check; if the header selected is present, it is + /// used. + /// + /// default: 0 + #[serde(default)] + pub request_ip_source: u8, + /// Max request size for file uploads in bytes. Defaults to 20MB. /// Also limits incoming federated media. /// diff --git a/src/router/layers.rs b/src/router/layers.rs index 9b61cdb04..8404e97cb 100644 --- a/src/router/layers.rs +++ b/src/router/layers.rs @@ -48,6 +48,17 @@ pub(crate) fn build(services: &Arc) -> Result<(Router, Guard)> { ))] let layers = layers.layer(compression_layer(server)); + let client_ip_layer = match services.config.request_ip_source { + | 1 => ClientIpSource::CfConnectingIp, + | 2 => ClientIpSource::CloudFrontViewerAddress, + | 3 => ClientIpSource::FlyClientIp, + | 4 => ClientIpSource::RightmostXForwardedFor, + | 5 => ClientIpSource::TrueClientIp, + | 6 => ClientIpSource::XEnvoyExternalAddress, + | 7 => ClientIpSource::XRealIp, + | _ => ClientIpSource::ConnectInfo, + }; + let services_ = services.clone(); let layers = layers .layer(SetSensitiveHeadersLayer::new([header::AUTHORIZATION])) @@ -59,7 +70,7 @@ pub(crate) fn build(services: &Arc) -> Result<(Router, Guard)> { .on_response(DefaultOnResponse::new().level(Level::DEBUG)), ) .layer(axum::middleware::from_fn_with_state(Arc::clone(services), request::handle)) - .layer(ClientIpSource::ConnectInfo.into_extension()) + .layer(client_ip_layer.into_extension()) .layer(ResponseBodyTimeoutLayer::new(Duration::from_secs( server.config.client_response_timeout, )))