From 25c0748a134cf5551d37940e6cb8e203da7d0476 Mon Sep 17 00:00:00 2001 From: Juraj Uhlar Date: Thu, 21 Dec 2023 14:50:10 +0100 Subject: [PATCH] chore: refactor and clean up --- .../botd-firewall/updateFirewallRule.ts | 58 +++++++++++-------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/src/server/botd-firewall/updateFirewallRule.ts b/src/server/botd-firewall/updateFirewallRule.ts index 18ca3188..818b3ade 100644 --- a/src/server/botd-firewall/updateFirewallRule.ts +++ b/src/server/botd-firewall/updateFirewallRule.ts @@ -13,43 +13,48 @@ import { BlockedIpDbModel } from './saveBlockedIp'; * This strategy is applicable to any Firewall solution editable using an API. The limitations will vary depending on your cloud provider. */ const MAX_IPS_PER_RULE = 84; -const MAX_IPS = MAX_IPS_PER_RULE * 5; // 420 +const MAX_RULES = 5; +const MAX_IPS = MAX_IPS_PER_RULE * MAX_RULES; // 420 -export const syncCloudflareBotFirewallRule = async () => { +export const getFirewallRules = async (): Promise => { + // Get the list of blocked IPs from the database const blockedIps = await BlockedIpDbModel.findAll({ order: [['timestamp', 'DESC']], limit: MAX_IPS, }); - // Split the list of blocked IPs into chunks of MAX_IPS_PER_RULE length. + // Split the list of blocked IPs into chunks of MAX_IPS_PER_RULE length const chunks = chunk(blockedIps, MAX_IPS_PER_RULE); + + // Build the rule expression for each chunk const ruleExpressions = chunks.map((chunk) => { const ipList = chunk.map((ip) => `"${ip.ip}"`).join(' '); return `http.x_forwarded_for in {${ipList}}`; }); - await updateFirewallRuleset(ruleExpressions); + // Build a rule from each rule expression + const rules = ruleExpressions.map((expression, index) => ({ + action: 'block', + description: `Block Bot IP addresses #${index + 1}`, + expression, + })); + + return rules; }; -async function updateFirewallRuleset(ruleExpressions: string[]) { +type CloudflareRule = { + action: string; + description: string; + expression: string; +}; + +export async function updateFirewallRuleset(rules: CloudflareRule[]) { const apiToken = process.env.CLOUDFLARE_API_TOKEN ?? ''; const zoneId = process.env.CLOUDFLARE_ZONE_ID ?? ''; // You can get your Cloudflare API token, and zone ID from your Cloudflare dashboard. // But you might need to call the API to find the custom ruleset ID. See getCustomRulesetId() below. const customRulesetId = process.env.CLOUDFLARE_RULESET_ID ?? ''; - const rulesetPayload = { - description: 'Custom ruleset for blocking Fingerprint-detected bot IPs', - kind: 'root', - name: 'default', - phase: 'http_request_firewall_custom', - rules: ruleExpressions.map((expression, index) => ({ - action: 'block', - description: `Block Bot IP addresses #${index + 1}`, - expression, - })), - }; - const url = `https://api.cloudflare.com/client/v4/zones/${zoneId}/rulesets/${customRulesetId}`; const options = { method: 'PUT', @@ -57,14 +62,21 @@ async function updateFirewallRuleset(ruleExpressions: string[]) { 'Content-Type': 'application/json', Authorization: `Bearer ${apiToken}`, }, - body: JSON.stringify(rulesetPayload), + body: JSON.stringify({ + description: 'Custom ruleset for blocking Fingerprint-detected bot IPs', + kind: 'root', + name: 'default', + phase: 'http_request_firewall_custom', + rules, + }), }; - try { - const blockAction = await (await fetch(url, options)).json(); - return blockAction.result; - } catch (error) { - console.error('error:' + error); + const response = await fetch(url, options); + if (response.ok) { + return await response.json(); + } else { + console.log(response.statusText, await response.json()); + throw new Error('Updating firewall ruleset failed', { cause: response.statusText }); } }