Crates.io | axum-client-ip |
lib.rs | axum-client-ip |
version | 0.6.1 |
source | src |
created_at | 2022-02-09 05:21:05.494742 |
updated_at | 2024-10-06 05:46:40.424622 |
description | Client IP address extractors for Axum |
homepage | |
repository | https://github.com/imbolc/axum-client-ip |
max_upload_size | |
id | 529515 |
size | 66,226 |
axum-client-ip
Client IP address extractors for Axum
There are two distinct use cases for client IP which should be treated differently:
SecureClientIp
] or an extractor for a particular header.InsecureClientIp
].For a deep dive into the trade-off refer to this Adam Pritchard's article
SecureClientIp
vs specific header extractorsApart from [SecureClientIp
] there are concrete
[CfConnectingIp
],
[CloudFrontViewerAddress
],
[FlyClientIp
],
[Forwarded
],
[RightmostForwarded
],
[RightmostXForwardedFor
],
[TrueClientIp
],
[XForwardedFor
] and
[XRealIp
]
secure extractors. You can use them directly if your code assumes a specific
proxy configuration.
They work the same way - by extracting IP from the specified header you
control. The only difference is in the target header specification. With
SecureClientIp
you can specify the header at runtime, so you can use e.g.
environment variable for this setting (look at the implementation
example). While with specific extractors you'd need to
recompile your code if you'd like to change the target header (e.g. you're
moving to another cloud provider). To mitigate this change you can create a
type alias e.g. type InsecureIp = XRealIp
and use it in your handlers,
then the change will affect only one line.
use axum::{routing::get, Router};
use axum_client_ip::{InsecureClientIp, SecureClientIp, SecureClientIpSource};
use std::net::SocketAddr;
async fn handler(insecure_ip: InsecureClientIp, secure_ip: SecureClientIp) -> String {
format!("{insecure_ip:?} {secure_ip:?}")
}
#[tokio::main]
async fn main() {
async fn handler(insecure_ip: InsecureClientIp, secure_ip: SecureClientIp) -> String {
format!("{insecure_ip:?} {secure_ip:?}")
}
let app = Router::new().route("/", get(handler))
.layer(SecureClientIpSource::ConnectInfo.into_extension());
let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
axum::serve(
listener,
// Don't forget to add `ConnectInfo` if you aren't behind a proxy
app.into_make_service_with_connect_info::<SocketAddr>(),
)
.await
.unwrap()
}
The most often issue with this extractor is using it after one consuming
body e.g. [axum::extract::Json
].
To fix this rearrange extractors in your handler definition moving body
consumption to the end, see details.
please run .pre-commit.sh before sending a PR, it will check everything
This project is licensed under the MIT license.