Skip to content

Commit

Permalink
set address header explicitly
Browse files Browse the repository at this point in the history
  • Loading branch information
Rich-Harris committed Mar 14, 2022
1 parent bbf862c commit 1fa6823
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 84 deletions.
29 changes: 26 additions & 3 deletions packages/adapter-node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default {
host: 'HOST_HEADER'
}
},
trustProxy: false
xForwardedForIndex: -1
})
}
};
Expand Down Expand Up @@ -64,6 +64,14 @@ PROTOCOL_HEADER=x-forwarded-proto HOST_HEADER=x-forwarded-host node build

> [`x-forwarded-proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto) and [`x-forwarded-host`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host) are de facto standard headers that forward the original protocol and host if you're using a reverse proxy (think load balancers and CDNs). You should only set these variables if you trust the reverse proxy.
The [RequestEvent](https://kit.svelte.dev/docs/types#additional-types-requestevent) object passed to hooks and endpoints includes an `event.clientAddress` property representing the client's IP address. By default this is the connecting `remoteAddress`. If your server is behind one or more proxies (such as a load balancer), this value will contain the innermost proxy's IP address rather than the client's, so we need to specify an `ADDRESS_HEADER` to read the address from:

```
ADDRESS_HEADER=True-Client-IP node build
```

> Headers can easily be spoofed. As with `PROTOCOL_HEADER` and `HOST_HEADER`, you should [know what you're doing](https://adam-p.ca/blog/2022/03/x-forwarded-for/) before setting these.
All of these environment variables can be changed, if necessary, using the `env` option:

```js
Expand All @@ -72,6 +80,7 @@ env: {
port: 'MY_PORT_VARIABLE',
origin: 'MY_ORIGINURL',
headers: {
address: 'MY_ADDRESS_HEADER',
protocol: 'MY_PROTOCOL_HEADER',
host: 'MY_HOST_HEADER'
}
Expand All @@ -85,9 +94,23 @@ MY_ORIGINURL=https://my.site \
node build
```

### trustProxy
### xForwardedForIndex

If the `ADDRESS_HEADER` is `X-Forwarded-For`, the header value will contain a comma-separated list of IP addresses. For example, if there are three proxies between your server and the client, proxy 3 will forward the addresses of the client and the first two proxies:

```
<client address>, <proxy 1 address>, <proxy 2 address>
```

To get the client address we could use `xForwardedFor: 0` or `xForwardedFor: -3`, which counts back from the number of addresses.

**X-Forwarded-For is [trivial to spoof](https://adam-p.ca/blog/2022/03/x-forwarded-for/), howevever**:

```
<spoofed address>, <client address>, <proxy 1 address>, <proxy 2 address>
```

In order for `event.clientAddress` to show the client's IP address, `adapter-node` must read it from one of several possible request headers. Since these headers can be spoofed, it will only do this if `trustProxy` is `true`.
For that reason you should always use a negative number (depending on the number of proxies) if you need to trust `event.clientAddress`. In the above example, `0` would yield the spoofed address while `-3` would continue to work.

## Custom server

Expand Down
3 changes: 2 additions & 1 deletion packages/adapter-node/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ interface AdapterOptions {
port?: string;
origin?: string;
headers?: {
address?: string;
protocol?: string;
host?: string;
};
};
trustProxy?: boolean;
xForwardedForIndex?: number;
}

declare function plugin(options?: AdapterOptions): Adapter;
Expand Down
6 changes: 4 additions & 2 deletions packages/adapter-node/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ export default function ({
port: port_env = 'PORT',
origin: origin_env = 'ORIGIN',
headers: {
address: address_header_env = 'ADDRESS_HEADER',
protocol: protocol_header_env = 'PROTOCOL_HEADER',
host: host_header_env = 'HOST_HEADER'
} = {}
} = {},
trustProxy = false
xForwardedForIndex = -1
} = {}) {
return {
name: '@sveltejs/adapter-node',
Expand Down Expand Up @@ -54,7 +55,8 @@ export default function ({
ORIGIN: origin_env ? `process.env[${JSON.stringify(origin_env)}]` : 'undefined',
PROTOCOL_HEADER: JSON.stringify(protocol_header_env),
HOST_HEADER: JSON.stringify(host_header_env),
TRUST_PROXY: JSON.stringify(trustProxy)
ADDRESS_HEADER: JSON.stringify(address_header_env),
X_FORWARDED_FOR_INDEX: JSON.stringify(xForwardedForIndex)
}
});

Expand Down
72 changes: 0 additions & 72 deletions packages/adapter-node/src/get-client-address.js

This file was deleted.

3 changes: 2 additions & 1 deletion packages/adapter-node/src/handler.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import type { Handle } from '@sveltejs/kit';

declare global {
const ORIGIN: string;
const ADDRESS_HEADER: string;
const HOST_HEADER: string;
const PROTOCOL_HEADER: string;
const TRUST_PROXY: boolean;
const X_FORWARDED_FOR_INDEX: number;
}

export const handler: Handle;
25 changes: 20 additions & 5 deletions packages/adapter-node/src/handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import { fileURLToPath } from 'url';
import { getRequest, setResponse } from '@sveltejs/kit/node';
import { Server } from 'SERVER';
import { manifest } from 'MANIFEST';
import { get_client_address } from './get-client-address';

/* global ORIGIN, PROTOCOL_HEADER, HOST_HEADER, TRUST_PROXY */
/* global ORIGIN, ADDRESS_HEADER, PROTOCOL_HEADER, HOST_HEADER */

const server = new Server(manifest);
const origin = ORIGIN;

const address_header = ADDRESS_HEADER && (process.env[ADDRESS_HEADER] || '').toLowerCase();
const protocol_header = PROTOCOL_HEADER && process.env[PROTOCOL_HEADER];
const host_header = (HOST_HEADER && process.env[HOST_HEADER]) || 'host';

Expand Down Expand Up @@ -50,11 +51,25 @@ const ssr = async (req, res) => {
res,
await server.respond(request, {
getClientAddress: () => {
if (TRUST_PROXY) {
return get_client_address(req);
if (address_header) {
const value = /** @type {string} */ (req.headers[address_header]) || '';

if (address_header === 'x-forwarded-for') {
const addresses = value.split(',');
return addresses[(addresses.length + X_FORWARDED_FOR_INDEX) % addresses.length].trim();
}

return value;
}

throw new Error('You must enable the adapter-node trustProxy option to read clientAddress');
return (
req.connection?.remoteAddress ||
// @ts-expect-error
req.connection?.socket?.remoteAddress ||
req.socket?.remoteAddress ||
// @ts-expect-error
req.info?.remoteAddress
);
}
})
);
Expand Down

0 comments on commit 1fa6823

Please sign in to comment.