Issue Capturing Public IPs

I am trying to retrieve our users’ public IP addresses for geolocation purposes. Our app is hosted in Galaxy, with no special configuration (as far as I know). In over 50% of cases this.connection.clientAddress and this.connection.httpHeaders["X-Forwarded-For"] contain the same single private IP address (e.g., 10.0.4.225, 10.0.0.46). To be clear, the “X-Forwarded-For” does not contain the expected public IP, just a single private IP.

I think something in Galaxy’s environment configuration is causing the original client IP not to be properly forwarded. But, what is most confusing is why approx. 50% of IP addresses are received correctly.

I read that I should set the HTTP_FORWARDED_COUNT environment variable to the number of proxies we are using, but we just have a regular Galaxy environment (plus for 50% of users we do receive the correct IP), so I am at a loss.

1 Like

How about you do some tests. You can use something like this:

const parseIp = (req) =>
    (typeof req.headers['x-forwarded-for'] === 'string'
        && req.headers['x-forwarded-for'].split(',').shift())
    || req.connection?.remoteAddress
    || req.socket?.remoteAddress
    || req.connection?.socket?.remoteAddress

const ipAddress = parseIp(req);

I think I picked this up from this forum a while ago.

I am in AWS with Elastic Beanstalk with an application load balancer. My HTTP_FORWARDED_COUNT is set to 2.
Not sure how the Galaxy stack is built but I would not be amazed if you’d have 3-4 proxies there. But with this I am only saying that I would not be amazed and not that there is a high probability.

I run my own analytics data collection for the logged in users. The way I capture IP is with something like this:

 Accounts.onLogin(function (login) {
//         UserPresenceServer.createConnection(login.user._id, login.connection)
        if (login) {
          processUserData({
            ip: login.connection?.clientAddress,
            agent: login.connection?.httpHeaders?.['user-agent'],
            lang: login.connection?.httpHeaders?.['accept-language'],
            userId: login.user._id
          })
            .catch(err => console.log(err))
        }
      })

And this requires some ip conversions. I will only copy the IP part of the function:

const ip2int = ip => ip.split('.').reduce((ipInt, octet) => { return (ipInt << 8) + parseInt(octet, 10) }, 0) >>> 0

export const processUserData = async ({ ip, agent, lang, userId }) => {
  ip = ip.indexOf(':') > -1 ? ip?.slice(0, ip.indexOf(':')) : ip
  const ipInt = ip2int(ip)

  // ....
}

2 Likes

One mention though, capturing this data without user consent might be an infringement of different privacy policies such as GDPR and CCPA.

1 Like

That could potentially explain why 50% of the users have the same IP. From EU users AWS masks their IP due to GDPR. Just a thought.

1 Like

I checked my DB and there seems to be 100% correct IP capturing. My VPC is in Frankfurt (eu-central-1).

Thanka @paulishca and @storyteller. I’ll implement some tests as suggested by @paulishca.

I don’t think the private IPs we are seeing are limited to EU users because I inspected their behavior and they did things that only US-based users would do.