fix(core): Add support for proxy using forward headers (#15006)

Co-authored-by: Danny Martini <danny@n8n.io>
This commit is contained in:
Phillip Morton
2025-05-22 10:26:14 +01:00
committed by GitHub
parent 23ab61c0c7
commit b1687c6be2
2 changed files with 160 additions and 8 deletions

View File

@@ -100,6 +100,35 @@ export class Push extends TypedEmitter<PushEvents> {
);
}
/**
* Construct the expected origin out of the host and forward headers.
* If `x-forwarded-host` and `x-forwarded-proto` are both defined they take
* precedence over `host`.
* If they are not both defined then `host` is used and the protocol is
* inferred from `origin`.
*/
private constructExpectedOrigin(req: SSEPushRequest | WebSocketPushRequest) {
const headers = req.headers;
if (headers.origin) {
const forwardedHost =
typeof headers['x-forwarded-host'] === 'string' ? headers['x-forwarded-host'] : undefined;
const forwardedProto =
typeof headers['x-forwarded-proto'] === 'string' ? headers['x-forwarded-proto'] : undefined;
const allForwardHeadersAreDefined = forwardedHost && forwardedProto;
const host = allForwardHeadersAreDefined ? forwardedHost : headers.host;
const proto = allForwardHeadersAreDefined
? forwardedProto
: headers.origin?.toLowerCase().startsWith('https://')
? 'https'
: 'http';
return { success: true, expectedOrigin: `${proto}://${host}` } as const;
} else {
return { success: false } as const;
}
}
handleRequest(req: SSEPushRequest | WebSocketPushRequest, res: PushResponse) {
const {
ws,
@@ -109,12 +138,31 @@ export class Push extends TypedEmitter<PushEvents> {
} = req;
let connectionError = '';
const expectedOriginResult = this.constructExpectedOrigin(req);
if (!pushRef) {
connectionError = 'The query parameter "pushRef" is missing!';
} else if (!expectedOriginResult.success) {
this.logger.warn('Origin header is missing');
connectionError = 'Invalid origin!';
} else if (
inProduction &&
!(headers.origin === `http://${headers.host}` || headers.origin === `https://${headers.host}`)
headers.origin?.toLowerCase() !== expectedOriginResult.expectedOrigin.toLowerCase()
) {
this.logger.warn(
`Origin header does NOT match the expected origin. (Origin: "${headers.origin}", Expected: "${expectedOriginResult.expectedOrigin}")`,
{
expectedOrigin: expectedOriginResult.expectedOrigin,
headers: {
host: req.headers.host,
origin: req.headers.origin,
['x-forwarded-proto']: req.headers['x-forwarded-proto'],
['x-forwarded-host']: req.headers['x-forwarded-host'],
},
},
);
connectionError = 'Invalid origin!';
}