imbolc / axum-client-ip

A client IP address extractor for Axum
MIT License
41 stars 13 forks source link

[Question]: How to get client IP address that you can get on the web? #22

Closed blueowlgreenfish closed 11 months ago

blueowlgreenfish commented 11 months ago

I'm working on rate limiting with user's IP address.

use axum_client_ip::{SecureClientIp, SecureClientIpSource};

async fn handler(secure_ip: SecureClientIp) -> String {
    format!("{}", secure_ip.0)
}

#[tokio::main]
async fn main() {
        let router = Router::new()
            .route("/", get(handler))
            // other routes go here

        axum_server::bind(addr)
            .serve(router.into_make_service_with_connect_info::<SocketAddr>())
            .await
            .unwrap();

With the above code, I get 172.18.0.1 on localhost and 10.0.0.4 on production network with AWS. These IP addresses are different from what I get on the web by searching What is my IP address. I would like to get the same IP address using axum-client-ip as the one that I can get by searching my IP address on the web. How could I achieve this?

imbolc commented 11 months ago

172.18.0.1 looks like a private and 10.0.0.4 like a public AWS IP, which means they come from the ConnectInfo, you're enabling by this line .serve(router.into_make_service_with_connect_info::<SocketAddr>()). So you should set up a correct IP source, it depends on your server configuration, e.g. behind Nginx it would be XRealIp. If you're not sure about the source, look for the real IP in the request headers. You're probably want to pass the source via an env variable or something, so you still can default to ConnectInfo locally without changing the code.

blueowlgreenfish commented 11 months ago

I've tried all IP sources on localhost and RightmostXForwardedFor on production network hoping something might happen, but nothing worked. For example, error for RightmostXForwardedFor says Couldn't find a valid IP in the X-Forwarded-For header, and error for XRealIp says No X-Real-Ip header, or the IP is invalid.

I don't use any reverse proxy like Nginx. I only use axum and axum-server.

I tested this with the code below.

use axum_client_ip::{SecureClientIp, SecureClientIpSource};

async fn handler(secure_ip: SecureClientIp) -> String {
    format!("{}", secure_ip.0)
}

#[tokio::main]
async fn main() {
    let router = Router::new()
        .route("/", get(handler))
        // this one uses RightmostXForwardedFor but tried other IP sources as well
        .layer(SecureClientIpSource::RightmostXForwardedFor.into_extension())
        // other routes go here

    axum_server::bind(addr)
        .serve(router.into_make_service())
        .await
        .unwrap();
imbolc commented 11 months ago

In any case the IP is extracted from headers. Display all the headers with a route like this to identify the one to use as the source:

.route( "/hdrs", axum::routing::get(|hdrs: axum::http::HeaderMap| async move { format!("{:#?}", hdrs) }))
blueowlgreenfish commented 11 months ago

Thank you for your instructions. I tried that and here is what I got on localhost.

{
"user-agent": "Mozilla/5.0 (X11; Linux x86_64; rv:120.0) Gecko/20100101 Firefox/120.0",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"accept-language": "en-US,en;q=0.5",
"accept-encoding": "gzip, deflate, br",
// changed the value of cookie for privacy
"cookie": "__stripe_mid=f4e5bf9a-13e8-4217-221c-332e3591ad7ae90324",
"upgrade-insecure-requests": "1",
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "none",
"sec-fetch-user": "?1",
"te": "trailers",
}

And this is what I got on production network.

{
// changed the value of host for privacy
"host": "example.com",
"user-agent": "Mozilla/5.0 (X11; Linux x86_64; rv:120.0) Gecko/20100101 Firefox/120.0",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"accept-language": "en-US,en;q=0.5",
"accept-encoding": "gzip, deflate, br",
"connection": "keep-alive",
// changed the value of cookie for privacy
"cookie": "__stripe_mid=f599d4f1-8517-4e32-3829-52d0f973c41f2baa82",
"upgrade-insecure-requests": "1",
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"sec-fetch-site": "none",
"sec-fetch-user": "?1",
}

Could you tell me how to extract user's IP address from this? Thank you again for your support.

imbolc commented 11 months ago

There's no IP in the headers you've shown. What AWS service is it?

blueowlgreenfish commented 11 months ago

I use EC2 instances.

I first build docker image of my application with docker compose on my development environment and deploy it to an AWS EC2 instance. I use docker swarm on production environment to distribute the application to the world. I'm starting to think that this might be the reason why I can't get user's IP address on production.

However, even on the localhost without Docker, I can't get user's IP address so maybe it is not Docker's issue.

imbolc commented 11 months ago

On localhost there's no proxy to setup an IP header, that's why we use ConnectInfo there. I'd bet on Docker involvement. You can try running a bare linux ES2 instance without Docker and look into the headers to be sure.

blueowlgreenfish commented 11 months ago

If I understand you correctly, there is no way to extract user's real IP address on localhost. The only way to extract user's real IP address is to be on production without Docker. Correct?

imbolc commented 11 months ago

there is no way to extract user's real IP address on localhost

IP is extracted from headers, if you add a header to your request on localhost you'll get an ip. There's no magic, try curl -H "X-Real-IP: 123.123.123.123" http://localhost:8000/ with the corresponding extractor.

blueowlgreenfish commented 11 months ago

Okay, that curl command returned the IP address 123.123.123.123. Does this mean that I need to setup the header manually to get the IP address on localhost?

imbolc commented 11 months ago

What's the purpose of having it on localhost, testing the rate limiter? Then yes, just add the header into the testing client requests.

blueowlgreenfish commented 11 months ago

Thank you for taking your time to answer my questions. I might have to reconsider using Docker on production environment if I want to do rate limiting by user's IP address. Anyway, thanks!