gateway-backend: add loop detection
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-gateway-backend': minor
|
||||
---
|
||||
|
||||
Added hop count tracking to prevent proxy loops. The gateway now tracks the number of proxy hops using the `backstage-gateway-hops` header and rejects requests that exceed 3 hops with a 508 Loop Detected error.
|
||||
@@ -161,6 +161,21 @@ describe('gateway', () => {
|
||||
expect(data).toEqual({ bar: true });
|
||||
});
|
||||
|
||||
it('should detect looped requests', async () => {
|
||||
const response = await fetch(
|
||||
'http://localhost:7777/api/nonexistent-plugin/foo',
|
||||
);
|
||||
expect(response.status).toBe(508);
|
||||
|
||||
const data = await response.json();
|
||||
expect(data).toEqual({
|
||||
error: {
|
||||
name: 'LoopDetectedError',
|
||||
message: 'Maximum proxy hop count exceeded (3)',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should close the response for sse connections', async () => {
|
||||
const eventSource = new EventSource(
|
||||
'http://localhost:7777/api/external-plugin/endpoint-sse',
|
||||
|
||||
@@ -23,9 +23,13 @@ import { createProxyMiddleware } from 'http-proxy-middleware';
|
||||
import { context } from '@opentelemetry/api';
|
||||
import { getRPCMetadata } from '@opentelemetry/core';
|
||||
|
||||
const MAX_HOPS = 3;
|
||||
const HOPS_HEADER = 'backstage-gateway-hops';
|
||||
|
||||
export async function createRouter({
|
||||
discovery,
|
||||
instanceMeta,
|
||||
logger,
|
||||
}: {
|
||||
discovery: DiscoveryService;
|
||||
instanceMeta: RootInstanceMetadataService;
|
||||
@@ -41,6 +45,12 @@ export async function createRouter({
|
||||
return discovery.getBaseUrl(pluginId);
|
||||
},
|
||||
on: {
|
||||
proxyReq(proxyReq, req: Request<{ pluginId: string }>) {
|
||||
const currentHops =
|
||||
parseInt(req.headers[HOPS_HEADER] as string, 10) || 0;
|
||||
|
||||
proxyReq.setHeader(HOPS_HEADER, currentHops + 1);
|
||||
},
|
||||
proxyRes(proxyRes, _req, res) {
|
||||
// https://github.com/chimurai/http-proxy-middleware/discussions/765
|
||||
proxyRes.on('close', () => {
|
||||
@@ -62,6 +72,20 @@ export async function createRouter({
|
||||
return;
|
||||
}
|
||||
|
||||
const currentHops = parseInt(req.headers[HOPS_HEADER] as string, 10) || 0;
|
||||
if (currentHops >= MAX_HOPS) {
|
||||
logger.warn(
|
||||
`Proxy loop detected for plugin '${req.params.pluginId}': request exceeded maximum hop count (${currentHops})`,
|
||||
);
|
||||
res.status(508).json({
|
||||
error: {
|
||||
name: 'LoopDetectedError',
|
||||
message: `Maximum proxy hop count exceeded (${currentHops})`,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const rpcMetadata = getRPCMetadata(context.active());
|
||||
if (rpcMetadata) {
|
||||
rpcMetadata.route = req.baseUrl;
|
||||
|
||||
Reference in New Issue
Block a user