From 5345613408f78eab433d7bd6a2b4977da2bd2fc4 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 21 Mar 2023 00:20:27 -0500 Subject: [PATCH] Add rate-limit policy --- rate-limit-policy.ts | 59 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100755 rate-limit-policy.ts diff --git a/rate-limit-policy.ts b/rate-limit-policy.ts new file mode 100755 index 0000000..54da6ec --- /dev/null +++ b/rate-limit-policy.ts @@ -0,0 +1,59 @@ +#!/bin/sh +//bin/true; exec deno run -A "$0" "$@" +import { readLines } from 'https://deno.land/std@0.178.0/io/mod.ts'; +import { Keydb } from 'https://deno.land/x/keydb@1.0.0/sqlite.ts'; + +const IP_WHITELIST = (Deno.env.get('IP_WHITELIST') || '').split(','); + +const RATE_LIMIT_INTERVAL = Number(Deno.env.get('RATE_LIMIT_INTERVAL') || 60000); +const RATE_LIMIT_MAX = Number(Deno.env.get('RATE_LIMIT_MAX') || 10); + +interface InputMessage { + type: 'new' | 'lookback'; + event: Event; + receivedAt: number; + sourceType: 'IP4' | 'IP6' | 'Import' | 'Stream' | 'Sync'; + sourceInfo: string; +} + +interface OutputMessage { + id: string; + action: 'accept' | 'reject' | 'shadowReject'; + msg: string; +} + +interface Event { + id: string; + sig: string; + kind: number; + tags: string[][]; + pubkey: string; + content: string; + created_at: number; +} + +async function handleMessage(msg: InputMessage): Promise { + if ((msg.sourceType === 'IP4' || msg.sourceType === 'IP6') && !IP_WHITELIST.includes(msg.sourceInfo)) { + const db = new Keydb('sqlite:///tmp/strfry-rate-limit-policy.sqlite3'); + const count = await db.get(msg.sourceInfo) || 0; + await db.set(msg.sourceInfo, count + 1, RATE_LIMIT_INTERVAL); + + if (count >= RATE_LIMIT_MAX) { + return { + id: msg.event.id, + action: 'reject', + msg: 'Rate-limited.', + }; + } + } + + return { + id: msg.event.id, + action: 'accept', + msg: '', + }; +} + +for await (const line of readLines(Deno.stdin)) { + console.log(JSON.stringify(await handleMessage(JSON.parse(line)))); +}