add ws rate limiter

This commit is contained in:
Evan
2025-02-21 09:21:04 -08:00
parent 59ae10771c
commit a072a24433
4 changed files with 45 additions and 9 deletions
+7
View File
@@ -44,6 +44,7 @@
"protobufjs": "^7.3.2",
"pureimage": "^0.4.13",
"raphael": "^2.3.0",
"rate-limiter-flexible": "^5.0.5",
"twemoji": "^14.0.2",
"uuid": "^10.0.0",
"wheelnav": "^1.7.1",
@@ -13895,6 +13896,12 @@
"eve-raphael": "0.5.0"
}
},
"node_modules/rate-limiter-flexible": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/rate-limiter-flexible/-/rate-limiter-flexible-5.0.5.tgz",
"integrity": "sha512-+/dSQfo+3FYwYygUs/V2BBdwGa9nFtakDwKt4l0bnvNB53TNT++QSFewwHX9qXrZJuMe9j+TUaU21lm5ARgqdQ==",
"license": "ISC"
},
"node_modules/raw-body": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
+1
View File
@@ -108,6 +108,7 @@
"protobufjs": "^7.3.2",
"pureimage": "^0.4.13",
"raphael": "^2.3.0",
"rate-limiter-flexible": "^5.0.5",
"twemoji": "^14.0.2",
"uuid": "^10.0.0",
"wheelnav": "^1.7.1",
+13 -1
View File
@@ -18,6 +18,7 @@ import WebSocket from "ws";
import { slog } from "./StructuredLog";
import { CreateGameRecord } from "../core/Util";
import { archive } from "./Archive";
import { RateLimiterMemory } from "rate-limiter-flexible";
export enum GamePhase {
Lobby = "LOBBY",
@@ -26,6 +27,11 @@ export enum GamePhase {
}
export class GameServer {
private rateLimiter = new RateLimiterMemory({
points: 20, // 20 messages
duration: 1, // per 1 second
});
private maxGameDuration = 5 * 60 * 60 * 1000; // 5 hours
private turns: Turn[] = [];
@@ -98,7 +104,13 @@ export class GameServer {
this.allClients.set(client.clientID, client);
client.ws.on("message", (message: string) => {
client.ws.on("message", async (message: string) => {
try {
await this.rateLimiter.consume(client.ip);
} catch (error) {
console.warn(`Rate limit exceeded for ${client.ip}`);
return;
}
try {
const clientMsg: ClientMessage = ClientMessageSchema.parse(
JSON.parse(message),
+24 -8
View File
@@ -32,6 +32,7 @@ import dotenv from "dotenv";
import crypto from "crypto";
dotenv.config();
import rateLimit from "express-rate-limit";
import { RateLimiterMemory } from "rate-limiter-flexible";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
@@ -61,15 +62,19 @@ app.use(
}),
);
app.set("trust proxy", 2);
app.use(
rateLimit({
windowMs: 1000, // 1 second
max: 20, // 20 requests per IP per second
}),
);
const rateLimiter = new RateLimiterMemory({
points: 20, // 20 messages
duration: 1, // per 1 second
});
const gm = new GameManager(serverConfig);
const gm = new GameManager(getServerConfig());
const bot = new DiscordBot();
try {
await bot.start();
} catch (error) {
console.error("Failed to start bot:", error);
}
let lobbiesString = "";
@@ -241,6 +246,17 @@ app.get("*", function (req, res) {
wss.on("connection", (ws, req) => {
ws.on("message", async (message: string) => {
let ip = "";
try {
const forwarded = req.headers["x-forwarded-for"];
ip = Array.isArray(forwarded)
? forwarded[0]
: forwarded || req.socket.remoteAddress;
await rateLimiter.consume(ip);
} catch (error) {
console.warn(`rate limit exceede for ${ip}`);
return;
}
try {
const clientMsg: ClientMessage = ClientMessageSchema.parse(
JSON.parse(message),