How to Find the True Client IP in Apigee X

In Apigee X, the X-Forwarded-For header will contain 3 values by default. This will include following:

[Client IP, Load Balancer IP, Istio Gateway IP]

 

If the client tries to spoof the client IP address by sending a X-Forwarded-For header, those values will get added to the front of the above list. For an example:

[Spoof IP1, Spoof IP2, Client IP, Load Balancer IP, Istio Gateway IP]

 

Therefore, if you need to find the actual client IP which sent the API request to the Load Balancer, that can be read using a JavaScript policy similar to following:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Javascript continueOnError="false" enabled="true" timeLimit="200" name="Find-Client-Ip">
    <DisplayName>Find-Client-Ip</DisplayName>
    <Properties/>
    <ResourceURL>jsc://Find-Client-Ip.js</ResourceURL>
</Javascript>

Find-Client-Ip.js:

var xffArray = context.getVariable("request.header.x-forwarded-for.values");
var xffArrayLength = context.getVariable("request.header.X-Forwarded-For.values.count");
var clientIp = context.getVariable("request.header.x-forwarded-for." + (xffArrayLength - 2));

context.setVariable("response.content", clientIp);

 Proxy Endpoint:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ProxyEndpoint name="default">
    <PreFlow name="PreFlow">
        <Request/>
        <Response>
            <Step>
                <Name>Find-Client-Ip</Name>
            </Step>
        </Response>
    </PreFlow>
    <Flows/>
    <PostFlow name="PostFlow">
        <Request/>
        <Response/>
    </PostFlow>
    <HTTPProxyConnection>
        <BasePath>/find-my-ip</BasePath>
    </HTTPProxyConnection>
    <RouteRule name="noroute"/>
</ProxyEndpoint>

 

An example API request to verify above implementation:

curl -i -k --resolve ${hostname}:443:${load_balancer_ip} -H 'X-Forwarded-For: 10.0.0.1, 10.0.0.2' https://${hostname}/find-my-ip

An example output by masking sensitive information:

HTTP/2 200 
user-agent: curl/7.61.1
accept: */*
x-cloud-trace-context: ---masked---
x-forwarded-for: 10.0.0.1, 10.0.0.2, ---true-client-ip---, ---load-balancer-ip---,10.128.0.3
x-forwarded-proto: https
x-request-id: ---masked---
x-b3-traceid: ---masked---
x-b3-spanid: ---masked---
x-b3-sampled: 0
content-length: 12
date: Fri, 30 Jul 2021 04:12:01 GMT
server: apigee
via: 1.1 google, 1.1 google
alt-svc: clear
---true-client-ip---
Contributors
Comments
Huw
Bronze 4
Bronze 4

@imesh it seems like we might just be able to use the proxy.client.ip flow variable.

Just wondering if there are any issues with  that?

I did some checking with our setup:

Client --> External HTTP Load Balancer --[via Network Bridge VMs]--> Apigee X
  • Client is 100.100.100.100
  • Client also adds a spoof "200.200.200.200" to the X-Forwarded-For
  • External HTTP Load Balancer is 34.120.113.103
  • Apigee X (Istio Gateway) is 192.168.108.29

And this is what I get when I echo out the flow variables.  As you can see, it seems that proxy.client.ip correctly identified the end client

{
    "client.ip": "192.168.108.29",
    "proxy.client.ip": "100.100.100.100",
    "request.header.True-Client-IP": "",
    "request.header.True-Client-IP.values": "[]",
    "request.header.X-Forwarded-For.values": "200.200.200.200, 100.100.100.100, 34.120.113.103, 192.168.108.29]"
}

 

Version history
Last update:
‎07-29-2021 09:30 PM
Updated by: