Skip to content

Commit

Permalink
Merge pull request #53 from pbojinov/cloudflare-extra-headers
Browse files Browse the repository at this point in the history
Cloudflare fallback headers and updates eslint and prettier configs
  • Loading branch information
pbojinov authored Jun 1, 2022
2 parents 56ca5ae + de7c791 commit 1edd068
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 106 deletions.
5 changes: 3 additions & 2 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"presets": ["@babel/preset-env"]
}
"presets": ["@babel/preset-env"],
"comments": false
}
7 changes: 7 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": [
"plugin:@shopify/node",
"plugin:@shopify/esnext",
"plugin:@shopify/prettier"
]
}
11 changes: 0 additions & 11 deletions .eslintrc.js

This file was deleted.

22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,13 @@ The user ip is determined by the following order:
6. `X-Real-IP` (Nginx proxy/FastCGI)
7. `X-Cluster-Client-IP` (Rackspace LB, Riverbed Stingray)
8. `X-Forwarded`, `Forwarded-For` and `Forwarded` (Variations of #2)
9. `req.connection.remoteAddress`
10. `req.socket.remoteAddress`
11. `req.connection.socket.remoteAddress`
12. `req.info.remoteAddress`
13. `request.raw` (Fastify)
9. `appengine-user-ip` (Google App Engine)
10. `req.connection.remoteAddress`
11. `req.socket.remoteAddress`
12. `req.connection.socket.remoteAddress`
13. `req.info.remoteAddress`
14. `Cf-Pseudo-IPv4` (Cloudflare fallback)
15. `request.raw` (Fastify)

If an IP address cannot be found, it will return `null`.

Expand All @@ -87,6 +89,14 @@ Run the integration tests
npm test
```

## Building

Compiles the current ES6 code to ES5 using Babel.

```
npm build
```

## Release Notes

See the wonderful [changelog](https://github.com/pbojinov/request-ip/blob/master/CHANGELOG.md)
Expand All @@ -102,4 +112,4 @@ To easily generate a new changelog, install [github-changelog-generator](https:/

## License

The MIT License (MIT) - 2018
The MIT License (MIT) - 2022
85 changes: 27 additions & 58 deletions dist/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
"use strict";

function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }

var is = require('is_js');
/**
* Parse x-forwarded-for headers.
*
* @param {string} value - The value to be parsed.
* @return {string|null} First known IP address, if any.
*/


function getClientIpFromXForwardedFor(value) {
if (!is.existy(value)) {
Expand All @@ -18,79 +11,58 @@ function getClientIpFromXForwardedFor(value) {

if (is.not.string(value)) {
throw new TypeError("Expected a string, got \"".concat(_typeof(value), "\""));
} // x-forwarded-for may return multiple IP addresses in the format:
// "client IP, proxy 1 IP, proxy 2 IP"
// Therefore, the right-most IP address is the IP address of the most recent proxy
// and the left-most IP address is the IP address of the originating client.
// source: http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/x-forwarded-headers.html
// Azure Web App's also adds a port for some reason, so we'll only use the first part (the IP)

}

var forwardedIps = value.split(',').map(function (e) {
var ip = e.trim();

if (ip.includes(':')) {
var splitted = ip.split(':'); // make sure we only use this if it's ipv4 (ip:port)
var splitted = ip.split(':');

if (splitted.length === 2) {
return splitted[0];
}
}

return ip;
}); // Sometimes IP addresses in this header can be 'unknown' (http://stackoverflow.com/a/11285650).
// Therefore taking the left-most IP address that is not unknown
// A Squid configuration directive can also set the value to "unknown" (http://www.squid-cache.org/Doc/config/forwarded_for/)
});

return forwardedIps.find(is.ip);
}
/**
* Determine client IP address.
*
* @param req
* @returns {string} ip - The IP address if known, defaulting to empty string if unknown.
*/
for (var i = forwardedIps.length - 1; i >= 0; i -= 1) {
if (is.ip(forwardedIps[i])) {
return forwardedIps[i];
}
}

return null;
}

function getClientIp(req) {
// Server is probably behind a proxy.
if (req.headers) {
// Standard headers used by Amazon EC2, Heroku, and others.
if (is.ip(req.headers['x-client-ip'])) {
return req.headers['x-client-ip'];
} // Load-balancers (AWS ELB) or proxies.

}

var xForwardedFor = getClientIpFromXForwardedFor(req.headers['x-forwarded-for']);

if (is.ip(xForwardedFor)) {
return xForwardedFor;
} // Cloudflare.
// @see https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers-
// CF-Connecting-IP - applied to every request to the origin.

}

if (is.ip(req.headers['cf-connecting-ip'])) {
return req.headers['cf-connecting-ip'];
} // Fastly and Firebase hosting header (When forwared to cloud function)

}

if (is.ip(req.headers['fastly-client-ip'])) {
return req.headers['fastly-client-ip'];
} // Akamai and Cloudflare: True-Client-IP.

}

if (is.ip(req.headers['true-client-ip'])) {
return req.headers['true-client-ip'];
} // Default nginx proxy/fcgi; alternative to x-forwarded-for, used by some proxies.

}

if (is.ip(req.headers['x-real-ip'])) {
return req.headers['x-real-ip'];
} // (Rackspace LB and Riverbed's Stingray)
// http://www.rackspace.com/knowledge_center/article/controlling-access-to-linux-cloud-sites-based-on-the-client-ip-address
// https://splash.riverbed.com/docs/DOC-1926

}

if (is.ip(req.headers['x-cluster-client-ip'])) {
return req.headers['x-cluster-client-ip'];
Expand All @@ -107,8 +79,11 @@ function getClientIp(req) {
if (is.ip(req.headers.forwarded)) {
return req.headers.forwarded;
}
} // Remote address checks.

if (is.ip(req.headers['x-appengine-user-ip'])) {
return req.headers['x-appengine-user-ip'];
}
}

if (is.existy(req.connection)) {
if (is.ip(req.connection.remoteAddress)) {
Expand All @@ -126,27 +101,21 @@ function getClientIp(req) {

if (is.existy(req.info) && is.ip(req.info.remoteAddress)) {
return req.info.remoteAddress;
} // AWS Api Gateway + Lambda

}

if (is.existy(req.requestContext) && is.existy(req.requestContext.identity) && is.ip(req.requestContext.identity.sourceIp)) {
return req.requestContext.identity.sourceIp;
}

if (is.existy(req.raw)) {
return getClientIp(req.raw);
}

return null;
}
/**
* Expose request IP as a middleware.
*
* @param {object} [options] - Configuration.
* @param {string} [options.attributeName] - Name of attribute to augment request object with.
* @return {*}
*/


function mw(options) {
// Defaults.
var configuration = is.not.existy(options) ? {} : options; // Validation.
var configuration = is.not.existy(options) ? {} : options;

if (is.not.object(configuration)) {
throw new TypeError('Options must be an object!');
Expand Down
23 changes: 14 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "request-ip",
"version": "2.1.3",
"description": "A small node.js module to retrieve the request's IP address",
"version": "2.2.0",
"description": "A small Node.js module to retrieve the request's IP address",
"keywords": [
"request ip",
"ip",
Expand All @@ -24,7 +24,11 @@
"req.info.remoteAddress",
"middleware",
"ipv4",
"ipv6"
"ipv6",
"fastify",
"x-appengine-user-ip",
"cloudflare",
"Cf-Pseudo-IPv4"
],
"homepage": "https://github.com/pbojinov/request-ip",
"bugs": {
Expand Down Expand Up @@ -52,17 +56,18 @@
"coverage": "nyc report --reporter=text-lcov | coveralls",
"test": "nyc --reporter=html --reporter=text --check-coverage --lines=100 --statements=100 tape ./test/index.js"
},
"prettier": "@shopify/prettier-config",
"dependencies": {
"is_js": "^0.9.0"
},
"devDependencies": {
"@babel/cli": "^7.0.0-beta.51",
"@babel/core": "^7.0.0-beta.51",
"@babel/preset-env": "^7.0.0-beta.51",
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@shopify/eslint-plugin": "^41.3.0",
"@shopify/prettier-config": "^1.1.2",
"coveralls": "^3.0.2",
"eslint": "^5.8.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-plugin-import": "^2.2.0",
"eslint": "^8.16.0",
"nyc": "^13.1.0",
"request": "^2.54.0",
"tap-spec": "^5.0.0",
Expand Down
28 changes: 21 additions & 7 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,17 @@ function getClientIpFromXForwardedFor(value) {
* @returns {string} ip - The IP address if known, defaulting to empty string if unknown.
*/
function getClientIp(req) {

// Server is probably behind a proxy.
if (req.headers) {

// Standard headers used by Amazon EC2, Heroku, and others.
if (is.ip(req.headers['x-client-ip'])) {
return req.headers['x-client-ip'];
}

// Load-balancers (AWS ELB) or proxies.
const xForwardedFor = getClientIpFromXForwardedFor(req.headers['x-forwarded-for']);
const xForwardedFor = getClientIpFromXForwardedFor(
req.headers['x-forwarded-for'],
);
if (is.ip(xForwardedFor)) {
return xForwardedFor;
}
Expand Down Expand Up @@ -115,7 +115,6 @@ function getClientIp(req) {
if (is.ip(req.headers['x-appengine-user-ip'])) {
return req.headers['x-appengine-user-ip'];
}

}

// Remote address checks.
Expand All @@ -124,7 +123,10 @@ function getClientIp(req) {
if (is.ip(req.connection.remoteAddress)) {
return req.connection.remoteAddress;
}
if (is.existy(req.connection.socket) && is.ip(req.connection.socket.remoteAddress)) {
if (
is.existy(req.connection.socket) &&
is.ip(req.connection.socket.remoteAddress)
) {
return req.connection.socket.remoteAddress;
}
}
Expand All @@ -138,10 +140,22 @@ function getClientIp(req) {
}

// AWS Api Gateway + Lambda
if (is.existy(req.requestContext) && is.existy(req.requestContext.identity) && is.ip(req.requestContext.identity.sourceIp)) {
if (
is.existy(req.requestContext) &&
is.existy(req.requestContext.identity) &&
is.ip(req.requestContext.identity.sourceIp)
) {
return req.requestContext.identity.sourceIp;
}

// Cloudflare fallback
// https://blog.cloudflare.com/eliminating-the-last-reasons-to-not-enable-ipv6/#introducingpseudoipv4
if (req.headers) {
if (is.ip(req.headers['Cf-Pseudo-IPv4'])) {
return req.headers['Cf-Pseudo-IPv4'];
}
}

// Fastify https://www.fastify.io/docs/latest/Reference/Request/
if (is.existy(req.raw)) {
return getClientIp(req.raw);
Expand Down Expand Up @@ -171,7 +185,7 @@ function mw(options) {
const ip = getClientIp(req);
Object.defineProperty(req, attributeName, {
get: () => ip,
configurable: true
configurable: true,
});
next();
};
Expand Down
Loading

0 comments on commit 1edd068

Please sign in to comment.