Does extractClientIP not return IP when running in localhost?

I’m trying to follow the example for extractClientIP, however ip.toOption returns a None. I’m wondering if this might be because I’m in my dev env right now (i.e running on localhost), but will work when I deploy, or if there’s some setting I need to enable?

Code:

val foo: Route = path("foo") {
    post {
      extractClientIP(ip => {
        val ipStr: String = ip.toOption.map(_.getHostAddress).getOrElse("unknown")
        log.info(s"Ip: $ipStr" )
        complete("ok")
      })
    }
  }

Then I make the request thru curl:

curl -v -XPOST 'http://localhost:8080/foo'

And in the server logs I see:

2018-09-29 05:30:26,486 INFO default-akka.actor.default-dispatcher-6 Routes.$anonfun$foo$3 - Ip: unknown

Did you remember to enable akka.http.server.remote-address-header in your config as described in the docs? https://doc.akka.io/docs/akka-http/current/routing-dsl/directives/misc-directives/extractClientIP.html#description

1 Like

@johanandren I’ve enabled this setting, here’s my application.conf:

include "akka-http-version"
akka.http {
  server {
    server-header = ProjectName 0.1
    remote-address-header = on # For getting user IP client side

    parsing {
      max-content-length: 5k
      max-to-strict-bytes = 5k
    }
  }
}

akka.http.routing {
  verbose-error-messages = on
}

But when I make the curl request mentioned in OP, I still don’t see the IP in the logs, and still see unknown. Code for getting the IP is the same as OP.

Any ideas?

I believe I’ve figured this out. I was using localhost as the interface when binding the server port… stupid, I know.

Using 0.0.0.0 as the interface seems to work and returns 127.0.0.1 as the IP. Anyone know if this is to be expected, and if 0.0.0.0 is the right interface to use on deployment as well?

Thanks.

For the server, binding to 0.0.0.0 means all ipv4 interfaces on the machine.

You should be getting the client IP regardless of what you bind to, with the right config applied, but if you bind to 127.0.0.1 the client will ofc always be 127.0.0.1 (I double checked this locally so there isn’t a bug lurking).

Just to confirm we are on the same page, I bound to 0.0.0.0 and I’m running the server from within the IDE. And if I make a curl req to localhost:8080/foo , the logs print my ip as extracted by extractclientip as 127.0.0.1.

Is that to be expected in this case and will change to the real ip when deployed?

Hi,

as the documentation states, the directive extracts the value of the X-Forwarded-For, Remote-Address, and X-Real-Ip header in that order, i.e. if there’s an X-Forwarded-For header its value is taken.

That means that if a request already contains such a header its value will be used. akka.http.server.remote-address-header = on will add a Remote-Address header but it will have lower precedence as X-Forwarded-For which is commonly added by reverse proxies. What will happen in your deployment depends on what headers your server will receive.

It’s unlikely that your original problem was solved by changing the bind interface. More likely, the feature was wrongly configured before.

1 Like

I see. Thanks for clarifying. Sounds like its normal to receive 127.0.0.1 as the IP when both server and client are on the same machine (and connection), then.

Just a further clarification - if neither of those headers are present (e.g someone making a curl request), then this header is automatically added if that setting is on, right?

Seems like just a header is a bit of weak security, e.g if someone were DDOSing, they could just send a different header each time and you wouldn’t be able to blacklist the source IP.

That’s correct, the reason why headers are accepted is that it is extremely common to put a proxy of some sort in front, most commonly a load balancer, and then you’d get the proxy ip as client for every request without the header. (The header is the “de facto standard” for figuring out the remote host https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host )

I don’t think we have API for extracting only the remote host/ip from the connection, or disable the logic with the headers, but you may be able to just read the header Remote-Address instead, which I think is never parsed from the request but always is a synthetic header based on the TCP connection.

Thanks for the explanation.

What’s the recourse in a ddos scenario with spoofed headers?

The Remote-Address header isn’t a standard header, so it isn’t parsed by akka-http. An incoming Remote-Address header will therefore only be parsed as a RawHeader while the one added by the server has the right type.

In any case, defending against DDOS attacks is super hard. If the requests get to the backend HTTP servers you’ve already lost.

Good points. Thanks again for the help :)