fix(api): доверять X-Real-IP со шлюза docker-сети в IPv4-mapped виде
Node отдаёт адрес сокета как ::ffff:172.31.120.1, а TRUSTED_PROXIES записан без префикса — все посетители попадали в один rate-limit.
This commit is contained in:
@@ -39,6 +39,12 @@ describe('clientIp', () => {
|
||||
expect(clientIp('198.51.100.1', '203.0.113.7', trusted)).toBe('198.51.100.1');
|
||||
});
|
||||
|
||||
it('шлюз docker-сети в IPv4-mapped виде совпадает с записью без префикса', () => {
|
||||
const withGateway = new Set<string>([...LOOPBACK, '172.31.120.1']);
|
||||
expect(clientIp('::ffff:172.31.120.1', '203.0.113.7', withGateway)).toBe('203.0.113.7');
|
||||
expect(clientIp('::ffff:198.51.100.1', '203.0.113.7', withGateway)).toBe('198.51.100.1');
|
||||
});
|
||||
|
||||
it('без заголовка — адрес сокета', () => {
|
||||
expect(clientIp('127.0.0.1', undefined, trusted)).toBe('127.0.0.1');
|
||||
expect(clientIp(undefined, '203.0.113.7', trusted)).toBe('unknown');
|
||||
|
||||
@@ -46,7 +46,13 @@ export function clientIp(
|
||||
realIpHeader: string | undefined,
|
||||
trustedProxies: ReadonlySet<string>,
|
||||
): string {
|
||||
const socket = socketAddress && unmapIpv4(socketAddress);
|
||||
const realIp = realIpHeader?.trim();
|
||||
if (socketAddress && trustedProxies.has(socketAddress) && realIp) return realIp;
|
||||
return socketAddress ?? 'unknown';
|
||||
if (socket && trustedProxies.has(socket) && realIp) return unmapIpv4(realIp);
|
||||
return socket || 'unknown';
|
||||
}
|
||||
|
||||
// Node на dual-stack сокете отдаёт IPv4 как «::ffff:172.31.120.1» — в TRUSTED_PROXIES пишут без префикса.
|
||||
function unmapIpv4(address: string): string {
|
||||
return address.startsWith('::ffff:') && address.includes('.') ? address.slice(7) : address;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user