* feat(gateway): add auth rate-limiting & brute-force protection Add a per-IP sliding-window rate limiter to Gateway authentication endpoints (HTTP, WebSocket upgrade, and WS message-level auth). When gateway.auth.rateLimit is configured, failed auth attempts are tracked per client IP. Once the threshold is exceeded within the sliding window, further attempts are blocked with HTTP 429 + Retry-After until the lockout period expires. Loopback addresses are exempt by default so local CLI sessions are never locked out. The limiter is only created when explicitly configured (undefined otherwise), keeping the feature fully opt-in and backward-compatible. * fix(gateway): isolate auth rate-limit scopes and normalize 429 responses --------- Co-authored-by: buerbaumer <buerbaumer@users.noreply.github.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>
54 lines
1.9 KiB
TypeScript
54 lines
1.9 KiB
TypeScript
import type { WebSocketServer } from "ws";
|
|
import type { createSubsystemLogger } from "../logging/subsystem.js";
|
|
import type { AuthRateLimiter } from "./auth-rate-limit.js";
|
|
import type { ResolvedGatewayAuth } from "./auth.js";
|
|
import type { GatewayRequestContext, GatewayRequestHandlers } from "./server-methods/types.js";
|
|
import type { GatewayWsClient } from "./server/ws-types.js";
|
|
import { attachGatewayWsConnectionHandler } from "./server/ws-connection.js";
|
|
|
|
export function attachGatewayWsHandlers(params: {
|
|
wss: WebSocketServer;
|
|
clients: Set<GatewayWsClient>;
|
|
port: number;
|
|
gatewayHost?: string;
|
|
canvasHostEnabled: boolean;
|
|
canvasHostServerPort?: number;
|
|
resolvedAuth: ResolvedGatewayAuth;
|
|
/** Optional rate limiter for auth brute-force protection. */
|
|
rateLimiter?: AuthRateLimiter;
|
|
gatewayMethods: string[];
|
|
events: string[];
|
|
logGateway: ReturnType<typeof createSubsystemLogger>;
|
|
logHealth: ReturnType<typeof createSubsystemLogger>;
|
|
logWsControl: ReturnType<typeof createSubsystemLogger>;
|
|
extraHandlers: GatewayRequestHandlers;
|
|
broadcast: (
|
|
event: string,
|
|
payload: unknown,
|
|
opts?: {
|
|
dropIfSlow?: boolean;
|
|
stateVersion?: { presence?: number; health?: number };
|
|
},
|
|
) => void;
|
|
context: GatewayRequestContext;
|
|
}) {
|
|
attachGatewayWsConnectionHandler({
|
|
wss: params.wss,
|
|
clients: params.clients,
|
|
port: params.port,
|
|
gatewayHost: params.gatewayHost,
|
|
canvasHostEnabled: params.canvasHostEnabled,
|
|
canvasHostServerPort: params.canvasHostServerPort,
|
|
resolvedAuth: params.resolvedAuth,
|
|
rateLimiter: params.rateLimiter,
|
|
gatewayMethods: params.gatewayMethods,
|
|
events: params.events,
|
|
logGateway: params.logGateway,
|
|
logHealth: params.logHealth,
|
|
logWsControl: params.logWsControl,
|
|
extraHandlers: params.extraHandlers,
|
|
broadcast: params.broadcast,
|
|
buildRequestContext: () => params.context,
|
|
});
|
|
}
|