API Gateway & Origin IP

I was in the process of adding an API Gateway, between Cloudfront and an existing ALB; when we discovered that valid requests were being rejected.

After some digging, we realised that API Gateway was using the more modern Forwarded header, rather than the usual XFF headers. It’s relatively straightforward to parse the format, or find a library that will:

const parse = require('forwarded-parse');

module.exports = function() {
    return function(req, res, next) {
        if (req.headers.forwarded) {
            const forwarded = parse(req.headers.forwarded).map(f => f.for);
            Object.defineProperties(req, {
                'ip': {
                    configurable: true,
                    enumerable: true,
                    get() {
                        return forwarded[0];
                    },
                },
                'ips': {
                    configurable: true,
                    enumerable: true,
                    get() {
                        return forwarded;
                    },
                },
            });
        }

        next();
    };
};

With this middleware plugged in, the routes with IP filtering were behaving correctly again. However.. if you want to run the same app in an env with just an ALB, e.g. as you roll out the API Gateway, then you have a problem.

Because the ALB does not know about that header, it will quite happily let you pass a spoofed version through. And there doesn’t seem to be an easy way to detect that you are behind an API Gateway (there is a Via header, but the ALB doesn’t overwrite that either).

curl "https://..." -H 'content-type: application/json' -d '{...}' -H 'forwarded: by=...;for=...,for=...;host=...;proto=https' -H 'via: HTTP/1.1 AmazonAPIGateway'

In the end, we were forced to use an env var (“feature flag”), to only enable that middleware in envs where we knew it was safe.