Skip to content

Commit 4a1a0b9

Browse files
authored
Merge pull request #15821 from github/repo-sync
repo sync
2 parents 119a92e + 9f08134 commit 4a1a0b9

3 files changed

Lines changed: 49 additions & 12 deletions

File tree

middleware/index.js

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,47 @@ const asyncMiddleware = (fn) => (req, res, next) => {
7575
Promise.resolve(fn(req, res, next)).catch(next)
7676
}
7777

78-
// The IP address that Fastly regards as the true client making the request w/ fallback to req.ip
79-
morgan.token('client-ip', (req) => req.headers['fastly-client-ip'] || req.ip)
80-
const productionLogFormat = `:client-ip - ":method :url" :status - :response-time ms`
78+
// By default, `:remote-addr` is described as following in the morgon docs:
79+
//
80+
// The remote address of the request. This will use req.ip, otherwise
81+
// the standard req.connection.remoteAddress value (socket address).
82+
//
83+
// But in production, by default, `req.ip` is the IP of the Azure machine
84+
// which is something like "104.156.87.177:28244" which is *not* the
85+
// end user. BUT! Because we configure `app.set('trust proxy', true)`
86+
// *before* morgain is enabled, it will use the first entry from
87+
// the `x-forwarded-for` header which is looking like this:
88+
// "75.40.90.27, 157.52.111.52, 104.156.87.177:5786" which is
89+
// "{USER'S IP}, {FASTLY'S POP IP}, {AZURE'S IP}".
90+
// Incidentally, that first IP in the comma separated list is the
91+
// same as the value of `req.headers['fastly-client-ip']` but
92+
// Fastly will put that into the X-Forwarded-IP.
93+
// By leaning in to X-Forwarded-IP (*and* the use
94+
// `app.set('trust proxy', true)`) we can express ourselves here
95+
// without having to use vendor specific headers.
96+
const productionLogFormat = `:remote-addr - ":method :url" :status - :response-time ms`
8197

8298
export default function (app) {
8399
// *** Request connection management ***
84100
if (!isTest) app.use(timeout)
85101
app.use(abort)
86102

103+
// Don't use the proxy's IP, use the requester's for rate limiting or
104+
// logging.
105+
// See https://expressjs.com/en/guide/behind-proxies.html
106+
// Essentially, setting this means it believe that the IP is the
107+
// first of the `X-Forwarded-For` header values.
108+
// If it was 0 (or false), the value would be that
109+
// of `req.socket.remoteAddress`.
110+
// Now, the `req.ip` becomes the first entry from x-forwarded-for
111+
// and falls back on `req.socket.remoteAddress` in all other cases.
112+
// Their documentation says:
113+
//
114+
// If true, the client's IP address is understood as the
115+
// left-most entry in the X-Forwarded-For header.
116+
//
117+
app.set('trust proxy', true)
118+
87119
// *** Request logging ***
88120
// Enabled in development and azure deployed environments
89121
// Not enabled in Heroku because the Heroku router + papertrail already logs the request information
@@ -181,13 +213,6 @@ export default function (app) {
181213
}
182214

183215
// *** Early exits ***
184-
// Don't use the proxy's IP, use the requester's for rate limiting
185-
// See https://expressjs.com/en/guide/behind-proxies.html
186-
// Essentially, setting this means it believe that the IP is the
187-
// first of the `X-Forwarded-For` header values.
188-
// If it was 0 (or false), the value would be that
189-
// of `req.socket.remoteAddress`.
190-
app.set('trust proxy', 1)
191216
app.use(rateLimit)
192217
app.use(instrument(handleInvalidPaths, './handle-invalid-paths'))
193218
app.use(asyncMiddleware(instrument(handleNextDataPath, './handle-next-data-path')))

middleware/rate-limit.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ export default rateLimit({
2121
legacyHeaders: false,
2222

2323
handler: (request, response, next, options) => {
24-
const ip = request.headers['x-forwarded-for'] || request.ip
25-
const tags = [`url:${request.url}`, `ip:${ip}`]
24+
const tags = [`url:${request.url}`, `ip:${request.ip}`]
2625
statsd.increment('middleware.rate_limit', 1, tags)
2726
// This is temporary until we fully understand fully that the
2827
// rate limiter really is working in production.

tests/routing/remote-ip.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,17 @@ describe('remote ip debugging', () => {
2424
const kv = JSON.parse(res.text)
2525
expect(kv['x-forwarded-for']).toBe('123.123.0.1')
2626
})
27+
28+
test('req.ip becomes the first value from x-forwarded-for', async () => {
29+
const xForwardedFor = '100.0.0.1, 100.0.0.2, 100.0.0.3'
30+
const res = await get('/_ip', {
31+
headers: {
32+
'X-Forwarded-For': xForwardedFor,
33+
},
34+
})
35+
expect(res.statusCode).toBe(200)
36+
const kv = JSON.parse(res.text)
37+
expect(kv.ip).toBe('100.0.0.1')
38+
expect(kv['x-forwarded-for']).toBe(xForwardedFor)
39+
})
2740
})

0 commit comments

Comments
 (0)