From 223230c2cf442b3f5232db1625b5bb2309c2380d Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Wed, 20 Nov 2024 14:20:46 +0100 Subject: [PATCH 01/21] CloudFlare: initial release --- src/appmixer/cloudflare/RulesIPsModel.js | 36 ++++ src/appmixer/cloudflare/jobs.js | 10 +- src/appmixer/cloudflare/routes.js | 24 +++ src/appmixer/cloudflare/rules.js | 116 +++++++++++++ .../cloudflare/waf/CloudflareWAFClient.js | 130 ++++++++++++++ .../CreateCustomRules/CreateCustomRules.js | 82 +++++++++ .../waf/CreateCustomRules/component.json | 70 ++++++++ .../create-custom-rules-handler.js | 35 ++++ src/appmixer/cloudflare/waf/auth.js | 38 +++++ src/appmixer/cloudflare/waf/bundle.json | 5 + src/appmixer/cloudflare/waf/lib.js | 161 ++++++++++++++++++ src/appmixer/cloudflare/waf/module.json | 7 + 12 files changed, 712 insertions(+), 2 deletions(-) create mode 100644 src/appmixer/cloudflare/RulesIPsModel.js create mode 100644 src/appmixer/cloudflare/rules.js create mode 100644 src/appmixer/cloudflare/waf/CloudflareWAFClient.js create mode 100644 src/appmixer/cloudflare/waf/CreateCustomRules/CreateCustomRules.js create mode 100644 src/appmixer/cloudflare/waf/CreateCustomRules/component.json create mode 100644 src/appmixer/cloudflare/waf/CreateCustomRules/create-custom-rules-handler.js create mode 100644 src/appmixer/cloudflare/waf/auth.js create mode 100644 src/appmixer/cloudflare/waf/bundle.json create mode 100644 src/appmixer/cloudflare/waf/lib.js create mode 100644 src/appmixer/cloudflare/waf/module.json diff --git a/src/appmixer/cloudflare/RulesIPsModel.js b/src/appmixer/cloudflare/RulesIPsModel.js new file mode 100644 index 000000000..0fa33c80d --- /dev/null +++ b/src/appmixer/cloudflare/RulesIPsModel.js @@ -0,0 +1,36 @@ +'use strict'; + +module.exports = context => { + + class RulesIPsModel extends context.db.Model { + + static get collection() { + + return 'rulesIPsModel'; + } + + static get idProperty() { + + return 'id'; + } + + static get properties() { + + return [ + 'id', + 'ip', + 'ruleId', + 'rulesetId', + 'zoneId', + 'removeAfter', + 'auth', + 'mtime' + ]; + } + } + + RulesIPsModel.createSettersAndGetters(); + + return RulesIPsModel; +}; + diff --git a/src/appmixer/cloudflare/jobs.js b/src/appmixer/cloudflare/jobs.js index 54caa8839..163441f82 100644 --- a/src/appmixer/cloudflare/jobs.js +++ b/src/appmixer/cloudflare/jobs.js @@ -1,8 +1,10 @@ const ZoneCloudflareClient = require('./ZoneCloudflareClient'); +const rules = require('./rules'); module.exports = async (context) => { const IPListModel = require('./IPListModel')(context); + const RulesIPsModel = require('./RulesIPsModel')(context); const config = require('./config')(context); @@ -13,7 +15,8 @@ module.exports = async (context) => { await context.log('trace', '[CloudFlare] rule delete job started.'); try { - await deleteExpireIps(context); + await deleteExpireIpsFromList(context); + await rules.deleteExpireIps(context); } finally { lock.unlock(); await context.log('trace', '[CloudFlare] rule delete job finished. Lock unlocked.'); @@ -29,7 +32,7 @@ module.exports = async (context) => { } }); - const deleteExpireIps = async function(context) { + const deleteExpireIpsFromList = async function(context) { const expired = await getExpiredItems(context); @@ -107,6 +110,9 @@ module.exports = async (context) => { const expired = await context.db.collection(IPListModel.collection).deleteMany({ $expr: { $gt: [{ $subtract: ['$mtime', '$removeAfter'] }, timespan] } }); + await context.db.collection(RulesIPsModel.collection).deleteMany({ + $expr: { $gt: [{ $subtract: ['$mtime', '$removeAfter'] }, timespan] } + }); await context.log('info', { stage: `[CloudFlare] Deleted ${expired.deletedCount} orphaned rules.` }); } catch (err) { diff --git a/src/appmixer/cloudflare/routes.js b/src/appmixer/cloudflare/routes.js index 53a1c60d0..20ea3bcbc 100644 --- a/src/appmixer/cloudflare/routes.js +++ b/src/appmixer/cloudflare/routes.js @@ -3,6 +3,7 @@ module.exports = (context, options) => { const IPListModel = require('./IPListModel')(context); + const RulesIPsModel = require('./RulesIPsModel')(context); context.http.router.register({ method: 'POST', path: '/ip-list', options: { @@ -19,6 +20,21 @@ module.exports = (context, options) => { } }); + context.http.router.register({ + method: 'POST', path: '/block-ip-rules', options: { + handler: async req => { + + const items = req.payload.items; + const operations = items.map(item => ({ + updateOne: { + filter: { ip: item.ip, zoneId: item.zoneId }, update: { $set: item }, upsert: true + } + })); + return await (context.db.collection(RulesIPsModel.collection)).bulkWrite(operations); + } + } + }); + context.http.router.register({ method: 'GET', path: '/ip-list', options: { handler: async req => { @@ -26,4 +42,12 @@ module.exports = (context, options) => { } } }); + + context.http.router.register({ + method: 'GET', path: '/block-ip-rules', options: { + handler: async req => { + return RulesIPsModel.find({}); + } + } + }); }; diff --git a/src/appmixer/cloudflare/rules.js b/src/appmixer/cloudflare/rules.js new file mode 100644 index 000000000..bffb1ee89 --- /dev/null +++ b/src/appmixer/cloudflare/rules.js @@ -0,0 +1,116 @@ +const CloudflareWAFClient = require('./waf/CloudflareWAFClient'); +const lib = require('./waf/lib'); + +const deleteExpireIps = async function(context) { + + const expired = await getExpiredItems(context); + + await context.log('info', { type: '[CloudFlareWAF] expired.', expired }); + + const groups = Object.values(expired); + const getRulePromises = groups.map(item => { + const client = new CloudflareWAFClient({ token: item.auth.token, zoneId: item.zoneId }); + return client.getRules(context, item.rulesetId); + }); + + const rulesToUpdate = []; + (await Promise.allSettled(getRulePromises)).forEach((result, i) => { + + if (result.status === 'fulfilled') { + const item = groups[i]; + + const { result: { rules = [] } } = result.value; + const rule = rules.find(r => r.id === item.ruleId); + if (rule) { + rulesToUpdate.push({ + ...item, rule: lib.removeIpsFromRule(rule, item?.ips?.map(i => i.ip)) + }); + } + } + }); + + await context.log('info', { type: '[CloudFlareWAF] to update.', rulesToUpdate }); + const updatePromises = rulesToUpdate.map(rulesToUpdate => { + + const { zoneId, rulesetId, auth, rule } = rulesToUpdate; + const client = new CloudflareWAFClient({ token: auth.token, zoneId }); + + return client.updateBlockRule(context, rulesetId, rule); + }); + + try { + + let dbItemsToDelete = []; + (await Promise.allSettled(updatePromises)).forEach(async (result, i) => { + + const item = rulesToUpdate[i]; + const dbItems = item?.ips?.map(i => i.id) || []; + context.log('info', { type: '[CloudFlareWAF] db item', item, s: result.status, dbItems }); + + if (result.status === 'fulfilled') { + dbItemsToDelete = dbItemsToDelete.concat(dbItems); + } else { + context.log('info', { type: '[CloudFlareWAF] db item delet 111' }); + const model = require('./RulesIPsModel')(context); + context.log('info', { type: '[CloudFlareWAF] db item delet 222' }); + + const operations = dbItems.map(item => ({ + updateOne: { + filter: { id: item.id }, update: { $set: { mtime: new Date } } + } + })); + context.log('info', { type: '[CloudFlareWAF] db item ops', operations }); + + await (context.db.collection(model.collection)).bulkWrite(operations); + + context.log('info', { type: '[CloudFlareWAF] AAA' }); + + // context.log('error', { type: '[CloudFlareWAF] Update rule failed', data: { ...item.rule } }); + } + }); + + await deleteDBItems(context, dbItemsToDelete); + } catch (e) { + context.log('error', { type: '[CloudFlareWAF] Unexpected error', data: e?.message || e }); + } +}; + +const deleteDBItems = async function(context, ids = []) { + + context.log('info', { type: '[CloudFlareWAF] db items to delete', data: ids }); + + const model = require('./RulesIPsModel')(context); + if (ids.length) { + + const deleted = await context.db.collection(model.collection) + .deleteMany({ id: { $in: ids } }); + + await context.log('info', `[CloudFlare] Deleted ${deleted.deletedCount} ips.`); + } +}; + +const getExpiredItems = async function(context) { + + const model = require('./RulesIPsModel')(context); + + const expired = await context.db.collection(model.collection) + .find({ removeAfter: { $lt: Date.now() } }) + .toArray(); + + return expired.reduce((res, item) => { + const key = item.ruleId; + res[key] = res[key] || { ips: [] }; + res[key].ips.push({ ip: item.ip, id: item.id }); + res[key].auth = item.auth; + res[key].id = item.id; + res[key].zoneId = item.zoneId; + res[key].ruleId = item.ruleId; + res[key].rulesetId = item.rulesetId; + + return res; + }, {}); +}; + +module.exports = { + deleteExpireIps +}; diff --git a/src/appmixer/cloudflare/waf/CloudflareWAFClient.js b/src/appmixer/cloudflare/waf/CloudflareWAFClient.js new file mode 100644 index 000000000..213e2fa62 --- /dev/null +++ b/src/appmixer/cloudflare/waf/CloudflareWAFClient.js @@ -0,0 +1,130 @@ +module.exports = class CloudflareWAFClient { + + constructor({ email, apiKey, zoneId, token }) { + this.email = email; + this.apiKey = apiKey; + this.zoneId = zoneId; + this.apiKey = apiKey; + this.token = token; + } + + getHeaders() { + if (this.token) { + return { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${this.token}` + }; + } + + return { + 'Content-Type': 'application/json', + 'X-Auth-Email': this.email, + 'X-Auth-Key': this.apiKey + }; + } + + /** + * https://developers.cloudflare.com/api/operations/getZoneRuleset + */ + listZoneRulesets(context) { + + return context.httpRequest({ + method: 'GET', + url: `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets`, + headers: this.getHeaders() + }).then(resp => resp.data.result); + } + + async verify(context) { + + const headers = this.getHeaders(); + + return context.httpRequest({ + method: 'GET', + url: 'https://api.cloudflare.com/client/v4/user/tokens/verify', + headers + }); + } + + async callEndpoint(context, { + action, + method = 'GET', + data, + params + }) { + + const headers = this.getHeaders(); + + return context.httpRequest({ + method, + url: `https://api.cloudflare.com/client/v4${action}`, + headers, + data, + params + }); + } + + async getRules(context, rulesetId) { + + const response = await context.httpRequest({ + method: 'GET', + url: `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets/${rulesetId}`, + headers: this.getHeaders() + }); + + return response.data; + } + + async createRulesetAndBlockRule(context, ips) { + + const { data } = await context.httpRequest({ + method: 'POST', + url: `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets`, + headers: this.getHeaders(), + data: { + kind: 'zone', + phase: 'http_request_firewall_custom', + name: 'Http Request Firewall Custom Ruleset', + description: 'Created by Salt Security\'s Cloudflare Integration. Firewall rules of identified attackers will be added to this ruleset.', + rules: [this.getBlockRule(1, ips)] + } + }); + + return data; + } + + // https://developers.cloudflare.com/api/resources/rulesets/subresources/rules/ + createBlockRule(context, { rulesetId, rule }) { + const url = `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets/${rulesetId}/rules`; + const headers = this.getHeaders(); + return context.httpRequest.post(url, rule, { headers }).then(resp => resp.data); + } + + updateBlockRule(context, rulesetId, rule) { + const url = `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets/${rulesetId}/rules/${rule.id}`; + const headers = this.getHeaders(); + return context.httpRequest.patch(url, rule, { headers }).then(resp => resp.data); + } + + async findIdsForIPs({ context, client, ips = [], account, list }) { + + const result = []; + for (let ipItem of ips) { + + const { data } = await client.callEndpoint(context, { + method: 'GET', + action: `/accounts/${account}/rules/lists/${list}/items`, + params: { + per_page: 1, + search: ipItem.ip + } + }); + + if (data?.result[0] && data?.result.length === 1) { + result.push({ ...data.result[0] }); + } + } + + return result; + }; +}; diff --git a/src/appmixer/cloudflare/waf/CreateCustomRules/CreateCustomRules.js b/src/appmixer/cloudflare/waf/CreateCustomRules/CreateCustomRules.js new file mode 100644 index 000000000..c24ac6e38 --- /dev/null +++ b/src/appmixer/cloudflare/waf/CreateCustomRules/CreateCustomRules.js @@ -0,0 +1,82 @@ +const CloudflareWAFClient = require('../CloudflareWAFClient'); +const lib = require('../../lib'); +const crypto = require('crypto'); + +module.exports = { + async receive(context) { + + const { apiToken } = context.auth; + const { zoneId } = context.properties; + const { ips, ttl } = context.messages.in.content; + + if (ips.length === 0) { + return context.sendJson([], 'out'); + } + + const parsedIps = Array.isArray(ips) ? ips : ips.split(','); + const client = new CloudflareWAFClient({ zoneId, token: apiToken }); + + const ruleset = (await client.listZoneRulesets(context)) + .find(ruleset => ruleset.kind === 'zone' && ruleset.phase === 'http_request_firewall_custom'); + + let resultRules = []; // all affected rules - created or updated + if (!ruleset) { + const { data } = await client.createRulesetAndBlockRule(context, parsedIps); + resultRules = data?.result?.rules || []; + } else { + const { result: { rules = [] } } = await client.getRules(context, ruleset.id); + resultRules = rules; + + const rulesToUpdate = lib.prepareRulesForCreateOrUpdate(parsedIps, rules); + + const promises = rulesToUpdate.map(rule => { + return rule.id ? + client.updateBlockRule(context, ruleset.id, rule) : + client.createBlockRule(context, { rulesetId: ruleset.id, rule }); + }); + + (await Promise.allSettled(promises)).forEach(result => { + updatedOrCreatedRules = result?.value?.result?.rules || []; + updatedOrCreatedRules.forEach(rule => { + const index = resultRules.findIndex(r => r.id === rule.id); + if (index !== -1) { + resultRules[index] = rule; + } else { + resultRules.push(rule); + } + }); + }); + } + + const updatedIps = lib.findIpsInRules(resultRules, parsedIps); + const updatedIpsArray = Object.entries(updatedIps).map(([ip, { id }]) => ({ ip, ruleId: id })); + + if (ttl) { + + const removeAfter = new Date().getTime() + ttl * 1000; + const dbItems = updatedIpsArray.map(item => { + const { ip, ruleId } = item; + return { + id: crypto.randomUUID(), + ip, + ruleId, + rulesetId: ruleset.id, + zoneId, + removeAfter, + auth: { + token: apiToken + } + }; + }); + + await context.callAppmixer({ + endPoint: '/plugins/appmixer/cloudflare/block-ip-rules', + method: 'POST', + body: { items: dbItems } + }); + } + + return context.sendJson(updatedIpsArray, 'out'); + + } +}; diff --git a/src/appmixer/cloudflare/waf/CreateCustomRules/component.json b/src/appmixer/cloudflare/waf/CreateCustomRules/component.json new file mode 100644 index 000000000..3c8f548fb --- /dev/null +++ b/src/appmixer/cloudflare/waf/CreateCustomRules/component.json @@ -0,0 +1,70 @@ +{ + "name": "appmixer.cloudflare.waf.CreateCustomRules", + "label": "cloudflare CreateCustomRules", + "description": "Create custom rules in waf", + "private": false, + "auth": { + "service": "appmixer:cloudflare:waf" + }, + "inPorts": [ + { + "name": "in", + "schema": { + "type": "object", + "properties": { + "ips": { "type": "string" }, + "ttl": { "type": "number" } + }, + "required": ["ips", "ttl"] + }, + "inspector": { + "inputs": { + "ips": { + "type": "text", + "label": "IP address", + "tooltip": "IP address which will be blocked by adding a blocking custom rule to the waf.", + "index": 1 + }, + "ttl": { + "type": "number", + "label": "TTL", + "index": 2, + "tooltip": "Time to live in seconds. The IPs will be automatically removed after this time." + } + } + } + } + ], + "properties": { + "schema": { + "type": "object", + "properties": { + "zoneId": { + "type": "string" + } + }, + "required": [ + "zoneId" + ] + }, + "inspector": { + "inputs": { + "zoneId": { + "type": "text", + "label": "Zone Id", + "tooltip": "Zone Id", + "index": 3 + } + } + } + }, + "outPorts": [ + { "name": "out" } + ], + "icon": "data:image/png;base64,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", + "version": "1.0.5", + "dependencies": { + "service": "1.0.5", + "module": "1.0.0" + } +} \ No newline at end of file diff --git a/src/appmixer/cloudflare/waf/CreateCustomRules/create-custom-rules-handler.js b/src/appmixer/cloudflare/waf/CreateCustomRules/create-custom-rules-handler.js new file mode 100644 index 000000000..b8d4c3e1d --- /dev/null +++ b/src/appmixer/cloudflare/waf/CreateCustomRules/create-custom-rules-handler.js @@ -0,0 +1,35 @@ + +function output(context, message, output) { + return context.sendJson({ message }, output.valueOf()); +} + +function handleResponseError(context, err) { + const cloudflareAuthenticationError = 'Authentication error'; + const zoneIdNotFoundError = new RegExp( + 'Could not route to /client/v4/zones/.*/rulesets, perhaps your object identifier is invalid?' + ); + + if (context.httpRequest.isAxiosError(err) && err.response) { + const cloudflareResponse = err.response.data; + const errorMessage = cloudflareResponse.errors + ?.map(error => error.message).toString(); + const otherMessages = cloudflareResponse.messages + ?.map(msg => msg.message).toString(); + if (errorMessage === cloudflareAuthenticationError) { + return messages.AuthenticationError; + } else if (zoneIdNotFoundError.test(errorMessage)) { + return messages.ZoneIdNotFound; + } else if (errorMessage) return errorMessage; + else if (otherMessages) return otherMessages; + else return messages.cloudflareUnknownError(err); + } else if (err instanceof Error) { + return err.message; + } else { + return 'Got unknown error when trying to send request to cloudflare server.'; + } +} + +module.exports = { + output, + handleResponseError +}; diff --git a/src/appmixer/cloudflare/waf/auth.js b/src/appmixer/cloudflare/waf/auth.js new file mode 100644 index 000000000..a20682eff --- /dev/null +++ b/src/appmixer/cloudflare/waf/auth.js @@ -0,0 +1,38 @@ +'use strict'; + +const CloudflareWAFClient = require('./CloudflareWAFClient'); + +module.exports = { + + type: 'apiKey', + + definition: { + + accountNameFromProfileInfo: 'account', + + auth: { + apiToken: { + type: 'text', + name: 'API Token', + tooltip: 'Manage access and permissions for your accounts, sites, and products".' + } + }, + + requestProfileInfo: async function(context) { + + const { apiToken } = context; + + const threshold = 10; + if (apiToken.length > threshold) { + return { account: apiToken.slice(0, 5) + ' ... ' + apiToken.slice(-3) }; + } + }, + + validate: async function(context) { + + const client = new CloudflareWAFClient({ token: context.apiToken }); + const { data } = await client.verify(context); + return data.success || false; + } + } +}; diff --git a/src/appmixer/cloudflare/waf/bundle.json b/src/appmixer/cloudflare/waf/bundle.json new file mode 100644 index 000000000..728d2ee7c --- /dev/null +++ b/src/appmixer/cloudflare/waf/bundle.json @@ -0,0 +1,5 @@ +{ + "name": "appmixer.cloudflare.waf", + "version": "1.0.0", + "changelog": [] +} diff --git a/src/appmixer/cloudflare/waf/lib.js b/src/appmixer/cloudflare/waf/lib.js new file mode 100644 index 000000000..2a37d55f9 --- /dev/null +++ b/src/appmixer/cloudflare/waf/lib.js @@ -0,0 +1,161 @@ +const { Address4, Address6 } = require('ip-address'); +const ruleDescription = 'Salt detected high severity attacker'; +const ruleRefPrefix = 'SALT'; +const RULE_MAX_CAPACITY = 4096; + +module.exports = { + extractIPs, + getIpsFromRules, + prepareRulesForCreateOrUpdate, + getBlockRule, + findIpsInRules, + removeIpsFromRule +}; + +function extractIPs(expression) { + + // Regular expression to match IP addresses within the curly braces + // const ipRegex = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g; + // Extracting the section of the expression that contains the IPs + + const match = expression.match(/{([^}]+)}/); + if (!match) { + // If no match for IP address block found, return empty array + return []; + } + + const ipsBlock = match[1]; // This contains "192.0.2.0 192.0.2.2 192.0.2.3" + return ipsBlock.split(' '); +} + +function removeIpsFromRule(rule, ipsToRemove = []) { + const data = extractIPs(rule.expression); + const ips = []; + data.forEach(ip => { + if (!ipsToRemove.includes(ip)) { + ips.push(ip); + } + }); + + const expression = getBlockExpression(ips); + + return { ...rule, expression }; +} + +function getIpsFromRules(rules) { + + const ips = {}; + rules.forEach(rule => { + + const data = extractIPs(rule.expression); + data.forEach(ip => { + ips[ip] = { id: rule.id }; + }); + }); + + return ips; +} + +function findIpsInRules(rules, refIps = []) { + + const ips = {}; + rules.forEach(rule => { + + const ipsFromRule = extractIPs(rule.expression); + ipsFromRule.forEach(ip => { + if (refIps.includes(ip)) { + ips[ip] = { id: rule.id }; + } + }); + }); + + return ips; +} + +function prepareRulesForCreateOrUpdate(ips, rules, ruleCapacity) { + const ipsList = assignIpsToRules(ips, rules, ruleCapacity); + + const ipsGroupedByRules = Object.keys(ipsList).reduce((acc, ip) => { + const ruleId = ipsList[ip].id; + if (!acc[ruleId]) acc[ruleId] = []; + acc[ruleId].push(ip); + return acc; + }, {}); + + const res = []; + const groups = Object.entries(ipsGroupedByRules); + groups.forEach(entry => { + const ruleId = entry[0]; + const existingRule = rules.find(rule => rule.id === ruleId); + const expression = getBlockExpression(entry[1]); + + if (existingRule) { + if (existingRule.expression !== expression) { + res.push({ ...existingRule, expression }); + } + } else { + res.push(this.getBlockRule(groups.length, entry[1])); + } + }); + return res; +} + +function getBlockRule(index, ips) { + return { + action: 'block', + description: `${ruleDescription}#${index}`, + enabled: true, + expression: getBlockExpression(ips), + ref: `${ruleRefPrefix}#${index}` + }; +} + +function assignIpsToRules(ips, rules, ruleCapacity) { + const ipsList = getIpsFromRules(rules); + const rulesMetadata = rules.map(rule => { + return { + id: rule.id, + usedCapacity: rule.expression.length + }; + }); + + ips.forEach(ip => { + + const availableRule = getAvailableRule(rulesMetadata, ip.length + 1, ruleCapacity); + + if (availableRule) { + availableRule.usedCapacity += ip.length + 1; + ipsList[ip] = { id: availableRule.id }; + } else { + ipsList[ip] = { id: 'NULL', expression: '' }; + } + }); + + return ipsList; +} + +function getAvailableRule(rules, requiredCapacity, ruleCapacity = RULE_MAX_CAPACITY) { + + return rules.find(rule => rule.usedCapacity + requiredCapacity < ruleCapacity); +} + +function removeInterfaceIdentifierAndAddCidr(ip) { + const networkPrefix = ip + .split(':') + .slice(0, 4) + .join(':'); + return `${networkPrefix}::/64`; +} + +function getBlockExpression(ips) { + const ipv4 = ips.filter(ip => Address4.isValid(ip)); + const ipv6 = ips.filter(ip => Address6.isValid(ip)); + const formattedIpv6 = ipv6.length > 0 + ? ipv6.map(ip => removeInterfaceIdentifierAndAddCidr(ip)) + : ipv6; + const allIps = [...ipv4, ...formattedIpv6].sort(); + + return `(ip.src in {${allIps.join(' ')}})`; +} + + diff --git a/src/appmixer/cloudflare/waf/module.json b/src/appmixer/cloudflare/waf/module.json new file mode 100644 index 000000000..0c1905033 --- /dev/null +++ b/src/appmixer/cloudflare/waf/module.json @@ -0,0 +1,7 @@ +{ + "name": "appmixer.cloudflare.waf", + "label": "CloudFlare WAF", + "category": "applications", + "description": "CloudFlare WAF integrations allows you to actively block attacker IP by using CloudFlare WAF infrastructure.", + "icon": "data:image/png;base64,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" +} From 887f55cce9651a9cd9104d821c00e70c8347852e Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Thu, 9 Jan 2025 18:08:31 +0100 Subject: [PATCH 02/21] update --- .../cloudflare/CloudflareZonesUrlBuilder.js | 32 ----- .../cloudflare/ZoneCloudflareClient.js | 130 ------------------ src/appmixer/cloudflare/jobs.js | 4 +- src/appmixer/cloudflare/lib.js | 36 ----- .../cloudflare/waf/CloudflareWAFClient.js | 21 --- .../cloudflare/{rules.js => waf/jobs.waf.js} | 0 6 files changed, 2 insertions(+), 221 deletions(-) delete mode 100644 src/appmixer/cloudflare/CloudflareZonesUrlBuilder.js delete mode 100644 src/appmixer/cloudflare/lib.js rename src/appmixer/cloudflare/{rules.js => waf/jobs.waf.js} (100%) diff --git a/src/appmixer/cloudflare/CloudflareZonesUrlBuilder.js b/src/appmixer/cloudflare/CloudflareZonesUrlBuilder.js deleted file mode 100644 index f4348b2b6..000000000 --- a/src/appmixer/cloudflare/CloudflareZonesUrlBuilder.js +++ /dev/null @@ -1,32 +0,0 @@ -module.exports = class CloudflareZonesUrlBuilder { - zonesBaseUrl = 'https://api.cloudflare.com/client/v4/zones'; - - constructor(zoneId) { - this.zoneId = zoneId; - this.url = `${this.zonesBaseUrl}/${this.zoneId}`; - } - - addRulesets() { - this.url += '/rulesets'; - return this; - } - - addRulesetId(rulesetId) { - this.url += `/${rulesetId}`; - return this; - } - - addRules() { - this.url += '/rules'; - return this; - } - - addRuleId(ruleId) { - this.url += `/${ruleId}`; - return this; - } - - getUrl() { - return this.url; - } -}; diff --git a/src/appmixer/cloudflare/ZoneCloudflareClient.js b/src/appmixer/cloudflare/ZoneCloudflareClient.js index 29b1ae6bf..b6d141489 100644 --- a/src/appmixer/cloudflare/ZoneCloudflareClient.js +++ b/src/appmixer/cloudflare/ZoneCloudflareClient.js @@ -1,14 +1,7 @@ -const { Address4, Address6 } = require('ip-address'); - -const CloudflareZonesUrlBuilder = require('./CloudflareZonesUrlBuilder'); - module.exports = class ZoneCloudflareClient { - ruleDescription = 'Salt detected high severity attacker'; - ruleRefPrefix = 'SALT'; constructor({ email, apiKey, zoneId, token }) { this.email = email; - this.apiKey = apiKey; this.zoneId = zoneId; this.email = email; this.apiKey = apiKey; @@ -30,58 +23,6 @@ module.exports = class ZoneCloudflareClient { }; } - getRuleDescription(attackerId) { - return `${this.ruleDescription} ${attackerId}`; - } - - getRuleRef(attackerId) { - return `${this.ruleRefPrefix}-${attackerId}`; - } - - removeInterfaceIdentifierAndAddCidr(ip) { - const networkPrefix = ip - .split(':') - .slice(0, 4) - .join(':'); - return `${networkPrefix}::/64`; - } - - getBlockExpression(ips) { - const ipv4 = ips.filter(ip => Address4.isValid(ip)); - const ipv6 = ips.filter(ip => Address6.isValid(ip)); - const formattedIpv6 = ipv6.length > 0 - ? ipv6.map(ip => this.removeInterfaceIdentifierAndAddCidr(ip)) - : ipv6; - const allIps = [...ipv4, ...formattedIpv6].sort(); - - if (ipv4.length === 1 && formattedIpv6.length === 0) { - return `ip.src eq ${ipv4[0]}`; // To be backward compatible with the current integration behavior - } - return `ip.src in {${allIps.join(' ')}}`; - } - - getBlockRule(attackerId, ips) { - return { - action: 'block', - description: this.getRuleDescription(attackerId), - enabled: true, - expression: this.getBlockExpression(ips), - ref: this.getRuleRef(attackerId) - }; - } - - /** - * https://developers.cloudflare.com/api/operations/getZoneRuleset - */ - listZoneRulesets(context) { - - return context.httpRequest({ - method: 'GET', - url: `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets`, - headers: this.getHeaders() - }).then(resp => resp.data.result); - } - async verify(context) { const headers = this.getHeaders(); @@ -122,58 +63,6 @@ module.exports = class ZoneCloudflareClient { }); } - async getRules(context, rulesetId) { - - const response = await context.httpRequest({ - method: 'GET', - url: `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets/${rulesetId}`, - headers: this.getHeaders() - }); - - return response.data; - } - - async createRulesetAndBlockRule(context, attackerId, ips) { - - const { data } = await context.httpRequest({ - method: 'POST', - url: `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets`, - headers: this.getHeaders(), - data: { - kind: 'zone', - phase: 'http_request_firewall_custom', - name: 'Http Request Firewall Custom Ruleset', - description: 'Created by Salt Security\'s Cloudflare Integration. Firewall rules of identified attackers will be added to this ruleset.', - rules: [this.getBlockRule(attackerId, ips)] - } - }); - - return data; - } - - createBlockRule(context, { rulesetId, attackerId, ips }) { - const url = new CloudflareZonesUrlBuilder(this.zoneId) - .addRulesets() - .addRulesetId(rulesetId) - .addRules() - .getUrl(); - const headers = this.getHeaders(); - const body = this.getBlockRule(attackerId, ips); - return context.httpRequest.post(url, body, { headers }).then(resp => resp.data); - } - - updateBlockRule(context, rulesetId, ruleId, attackerId, ips) { - const url = new CloudflareZonesUrlBuilder(this.zoneId) - .addRulesets() - .addRulesetId(rulesetId) - .addRules() - .addRuleId(ruleId) - .getUrl(); - const headers = this.getHeaders(); - const body = this.getBlockRule(attackerId, ips); - return context.httpRequest.patch(url, body, { headers }).then(resp => resp.data); - } - async findIdsForIPs({ context, client, ips = [], account, list }) { const result = []; @@ -199,23 +88,4 @@ module.exports = class ZoneCloudflareClient { return result; }; - - isCloudflareGetRulesetResponse(data) { - return ( - data && - typeof data === 'object' && - Array.isArray(data.errors) && - Array.isArray(data.messages) && - data.result && - typeof data.result === 'object' && - typeof data.result.description === 'string' && - typeof data.result.id === 'string' && - typeof data.result.kind === 'string' && - typeof data.result.last_updated === 'string' && - typeof data.result.name === 'string' && - typeof data.result.phase === 'string' && - typeof data.result.version === 'string' && - Array.isArray(data.result.rules) - ); - } }; diff --git a/src/appmixer/cloudflare/jobs.js b/src/appmixer/cloudflare/jobs.js index 163441f82..639913fd0 100644 --- a/src/appmixer/cloudflare/jobs.js +++ b/src/appmixer/cloudflare/jobs.js @@ -1,5 +1,5 @@ const ZoneCloudflareClient = require('./ZoneCloudflareClient'); -const rules = require('./rules'); +const wafJobs = require('./waf/jobs.waf'); module.exports = async (context) => { @@ -16,7 +16,7 @@ module.exports = async (context) => { try { await deleteExpireIpsFromList(context); - await rules.deleteExpireIps(context); + await wafJobs.deleteExpireIps(context); } finally { lock.unlock(); await context.log('trace', '[CloudFlare] rule delete job finished. Lock unlocked.'); diff --git a/src/appmixer/cloudflare/lib.js b/src/appmixer/cloudflare/lib.js deleted file mode 100644 index 973129a1d..000000000 --- a/src/appmixer/cloudflare/lib.js +++ /dev/null @@ -1,36 +0,0 @@ -module.exports = { - extractIPs(expression) { - // Regular expression to match IP addresses within the curly braces - // const ipRegex = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g; - // Extracting the section of the expression that contains the IPs - - const match = expression.match(/{([^}]+)}/); - if (!match) { - // If no match for IP address block found, return empty array - return []; - } - - const ipsBlock = match[1]; // This contains "192.0.2.0 192.0.2.2 192.0.2.3" - return ipsBlock.split(' '); - }, - - getIpsFromRules(rules) { - - const ips = new Set(); - rules.forEach(rule => { - - const data = this.extractIPs(rule.expression); - data.forEach(item => ips.add(item)); - - }); - - return ips; - }, - - OUTPUT_PORT: { - SUCCESS: 'success', - FAILURE: 'failure' - } - -}; - diff --git a/src/appmixer/cloudflare/waf/CloudflareWAFClient.js b/src/appmixer/cloudflare/waf/CloudflareWAFClient.js index 213e2fa62..26e4bd79d 100644 --- a/src/appmixer/cloudflare/waf/CloudflareWAFClient.js +++ b/src/appmixer/cloudflare/waf/CloudflareWAFClient.js @@ -106,25 +106,4 @@ module.exports = class CloudflareWAFClient { return context.httpRequest.patch(url, rule, { headers }).then(resp => resp.data); } - async findIdsForIPs({ context, client, ips = [], account, list }) { - - const result = []; - for (let ipItem of ips) { - - const { data } = await client.callEndpoint(context, { - method: 'GET', - action: `/accounts/${account}/rules/lists/${list}/items`, - params: { - per_page: 1, - search: ipItem.ip - } - }); - - if (data?.result[0] && data?.result.length === 1) { - result.push({ ...data.result[0] }); - } - } - - return result; - }; }; diff --git a/src/appmixer/cloudflare/rules.js b/src/appmixer/cloudflare/waf/jobs.waf.js similarity index 100% rename from src/appmixer/cloudflare/rules.js rename to src/appmixer/cloudflare/waf/jobs.waf.js From 8cac39af64d5955641391ff1e0132fd40f04b4f9 Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Thu, 9 Jan 2025 18:10:10 +0100 Subject: [PATCH 03/21] update --- src/appmixer/cloudflare/lists/auth.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/appmixer/cloudflare/lists/auth.js b/src/appmixer/cloudflare/lists/auth.js index 3af59f7c6..d39573691 100644 --- a/src/appmixer/cloudflare/lists/auth.js +++ b/src/appmixer/cloudflare/lists/auth.js @@ -38,7 +38,6 @@ module.exports = { const client = new ZoneCloudflareClient({ email, apiKey }); const { data } = await client.verifyGlobalApiKey(context); return data.success || false; - } } }; From da2ee8c2953789f669278f49374fbcb41db67a97 Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Thu, 9 Jan 2025 18:12:17 +0100 Subject: [PATCH 04/21] update --- src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js | 4 ++-- .../CloudflareListClient.js} | 0 .../cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js | 4 ++-- src/appmixer/cloudflare/lists/auth.js | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) rename src/appmixer/cloudflare/{ZoneCloudflareClient.js => lists/CloudflareListClient.js} (100%) diff --git a/src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js b/src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js index 257a18cdf..8a4d86dd6 100644 --- a/src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js +++ b/src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js @@ -1,4 +1,4 @@ -const ZoneCloudflareClient = require('../../ZoneCloudflareClient'); +const CloudflareListClient = require('../CloudflareListClient'); const lib = require('../lib'); let attempts = 0; @@ -36,7 +36,7 @@ module.exports = { const { accountsFetch, listFetch } = context.properties; const { account, list, ips, ttl } = context.messages.in.content; - const client = new ZoneCloudflareClient({ email, apiKey }); + const client = new CloudflareListClient({ email, apiKey }); if (accountsFetch || listFetch) { return await lib.fetchInputs(context, client, { account, listFetch, accountsFetch }); diff --git a/src/appmixer/cloudflare/ZoneCloudflareClient.js b/src/appmixer/cloudflare/lists/CloudflareListClient.js similarity index 100% rename from src/appmixer/cloudflare/ZoneCloudflareClient.js rename to src/appmixer/cloudflare/lists/CloudflareListClient.js diff --git a/src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js b/src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js index 7100c011b..2a76d95d8 100644 --- a/src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js +++ b/src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js @@ -1,4 +1,4 @@ -const ZoneCloudflareClient = require('../../ZoneCloudflareClient'); +const CloudflareListClient = require('../CloudflareListClient'); let attempts = 0; const getStatus = async function(context, client, { account, id }) { @@ -32,7 +32,7 @@ module.exports = { const { apiKey, email } = context.auth; const { account, list, ips } = context.messages.in.content; - const client = new ZoneCloudflareClient({ email, apiKey }); + const client = new CloudflareListClient({ email, apiKey }); const ipsList = ips.AND; diff --git a/src/appmixer/cloudflare/lists/auth.js b/src/appmixer/cloudflare/lists/auth.js index d39573691..be0b807a4 100644 --- a/src/appmixer/cloudflare/lists/auth.js +++ b/src/appmixer/cloudflare/lists/auth.js @@ -1,6 +1,6 @@ 'use strict'; -const ZoneCloudflareClient = require('../ZoneCloudflareClient'); +const CloudflareListClient = require('./CloudflareListClient'); module.exports = { @@ -35,7 +35,7 @@ module.exports = { validate: async function(context) { const { email, apiKey } = context; - const client = new ZoneCloudflareClient({ email, apiKey }); + const client = new CloudflareListClient({ email, apiKey }); const { data } = await client.verifyGlobalApiKey(context); return data.success || false; } From d9685d5b533660a82fbf70e0621a1a7c398a03fb Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Thu, 9 Jan 2025 18:16:30 +0100 Subject: [PATCH 05/21] update --- src/appmixer/cloudflare/auth.js | 53 --------------------------------- src/appmixer/cloudflare/jobs.js | 2 +- 2 files changed, 1 insertion(+), 54 deletions(-) delete mode 100644 src/appmixer/cloudflare/auth.js diff --git a/src/appmixer/cloudflare/auth.js b/src/appmixer/cloudflare/auth.js deleted file mode 100644 index 4a32b345d..000000000 --- a/src/appmixer/cloudflare/auth.js +++ /dev/null @@ -1,53 +0,0 @@ -'use strict'; - -const ZoneCloudflareClient = require('./ZoneCloudflareClient'); - -module.exports = { - - type: 'apiKey', - - definition: { - - accountNameFromProfileInfo: 'account', - - auth: { - apiToken: { - type: 'text', - name: 'API Token', - tooltip: 'Manage access and permissions for your accounts, sites, and products".' - }, - email: { - type: 'text', - name: 'Email', - tooltip: 'Enter your "Email".' - }, - apiKey: { - type: 'text', - name: 'Global API Key', - tooltip: 'Enter your "Global API Key".' - } - }, - - requestProfileInfo: async function(context) { - - const { email, apiToken } = context; - - if (email) { - return { account: email }; - } - - const threshold = 10; - if (apiToken.length > threshold) { - return { account: apiToken.slice(0, 5) + ' ... ' + apiToken.slice(-3) }; - } - }, - - validate: async function(context) { - - const client = new ZoneCloudflareClient({ token: context.apiToken }); - - const { data } = await client.verify(context); - return data.success || false; - } - } -}; diff --git a/src/appmixer/cloudflare/jobs.js b/src/appmixer/cloudflare/jobs.js index 639913fd0..f36973ccd 100644 --- a/src/appmixer/cloudflare/jobs.js +++ b/src/appmixer/cloudflare/jobs.js @@ -1,4 +1,4 @@ -const ZoneCloudflareClient = require('./ZoneCloudflareClient'); +const ZoneCloudflareClient = require('./lists/CloudflareListClient'); const wafJobs = require('./waf/jobs.waf'); module.exports = async (context) => { From a60fbb9c5cb747edba25dc8c3897f8c84ae8c810 Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Thu, 9 Jan 2025 18:19:04 +0100 Subject: [PATCH 06/21] update --- src/appmixer/cloudflare/waf/jobs.waf.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/appmixer/cloudflare/waf/jobs.waf.js b/src/appmixer/cloudflare/waf/jobs.waf.js index bffb1ee89..c349bf757 100644 --- a/src/appmixer/cloudflare/waf/jobs.waf.js +++ b/src/appmixer/cloudflare/waf/jobs.waf.js @@ -1,5 +1,5 @@ -const CloudflareWAFClient = require('./waf/CloudflareWAFClient'); -const lib = require('./waf/lib'); +const CloudflareWAFClient = require('./CloudflareWAFClient'); +const lib = require('./lib'); const deleteExpireIps = async function(context) { @@ -51,7 +51,7 @@ const deleteExpireIps = async function(context) { dbItemsToDelete = dbItemsToDelete.concat(dbItems); } else { context.log('info', { type: '[CloudFlareWAF] db item delet 111' }); - const model = require('./RulesIPsModel')(context); + const model = require('../RulesIPsModel')(context); context.log('info', { type: '[CloudFlareWAF] db item delet 222' }); const operations = dbItems.map(item => ({ @@ -79,7 +79,7 @@ const deleteDBItems = async function(context, ids = []) { context.log('info', { type: '[CloudFlareWAF] db items to delete', data: ids }); - const model = require('./RulesIPsModel')(context); + const model = require('../RulesIPsModel')(context); if (ids.length) { const deleted = await context.db.collection(model.collection) @@ -91,7 +91,7 @@ const deleteDBItems = async function(context, ids = []) { const getExpiredItems = async function(context) { - const model = require('./RulesIPsModel')(context); + const model = require('../RulesIPsModel')(context); const expired = await context.db.collection(model.collection) .find({ removeAfter: { $lt: Date.now() } }) From 826281158d835ffa1285a852e07b4ae9344d3f40 Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Fri, 10 Jan 2025 06:49:34 +0100 Subject: [PATCH 07/21] update --- src/appmixer/cloudflare/jobs.js | 70 +------------------ src/appmixer/cloudflare/jobs.lists.js | 67 ++++++++++++++++++ src/appmixer/cloudflare/{waf => }/jobs.waf.js | 4 +- .../cloudflare/lists/CloudflareListClient.js | 2 +- 4 files changed, 73 insertions(+), 70 deletions(-) create mode 100644 src/appmixer/cloudflare/jobs.lists.js rename src/appmixer/cloudflare/{waf => }/jobs.waf.js (97%) diff --git a/src/appmixer/cloudflare/jobs.js b/src/appmixer/cloudflare/jobs.js index f36973ccd..73dc9ae7c 100644 --- a/src/appmixer/cloudflare/jobs.js +++ b/src/appmixer/cloudflare/jobs.js @@ -1,5 +1,5 @@ -const ZoneCloudflareClient = require('./lists/CloudflareListClient'); -const wafJobs = require('./waf/jobs.waf'); +const wafJobs = require('./jobs.waf'); +const listsJobs = require('./jobs.lists'); module.exports = async (context) => { @@ -15,7 +15,7 @@ module.exports = async (context) => { await context.log('trace', '[CloudFlare] rule delete job started.'); try { - await deleteExpireIpsFromList(context); + await listsJobs.deleteExpireIpsFromList(context); await wafJobs.deleteExpireIps(context); } finally { lock.unlock(); @@ -32,70 +32,6 @@ module.exports = async (context) => { } }); - const deleteExpireIpsFromList = async function(context) { - - const expired = await getExpiredItems(context); - - const groups = Object.values(expired); - - const promises = groups.map(chunk => { - - const { email, apiKey, account, list } = chunk.auth; - - context.log('info', { stage: '[CloudFlare] removing ', ips: chunk.ips, list, account }); - - const client = new ZoneCloudflareClient({ email, apiKey }); - // https://developers.cloudflare.com/api/operations/lists-delete-list-items - return client.callEndpoint(context, { - method: 'DELETE', - action: `/accounts/${account}/rules/lists/${list}/items`, - data: { items: chunk.ips } - }); - }); - - const itemsToDelete = { ids: [], lists: [] }; - - (await Promise.allSettled(promises)).forEach(async (result, i) => { - if (result.status === 'fulfilled') { - itemsToDelete.ids = itemsToDelete.ids.concat(groups[i].ips); - itemsToDelete.lists.push(groups[i]?.auth?.list); - } else { - const operations = groups[i].ips.map(item => ({ - updateOne: { - filter: { id: item.id }, update: { $set: { mtime: new Date } } - } - })); - await (context.db.collection(IPListModel.collection)).bulkWrite(operations); - } - }); - - if (itemsToDelete.ids.length) { - const deleted = await context.db.collection(IPListModel.collection) - .deleteMany({ id: { $in: itemsToDelete.ids.map(item => item.id) } }); - await context.log('info', { - stage: `[CloudFlare] Deleted total ${deleted.deletedCount} ips.`, - lists: itemsToDelete.lists, - itemIds: itemsToDelete.ids - }); - } - }; - - const getExpiredItems = async function(context) { - - const expired = await context.db.collection(IPListModel.collection) - .find({ removeAfter: { $lt: Date.now() } }) - .toArray(); - - return expired.reduce((res, item) => { - const key = item.auth.list; // listId - res[key] = res[key] || { ips: [] }; - res[key].auth = item.auth; - res[key].ips.push({ id: item.id }); - - return res; - }, {}); - }; - // Self-healing job to remove rules that have created>mtime. These rules are stuck in the system and should be removed. await context.scheduleJob('cloud-flare-lists-ips-cleanup-job', config.cleanup.schedule, async () => { diff --git a/src/appmixer/cloudflare/jobs.lists.js b/src/appmixer/cloudflare/jobs.lists.js new file mode 100644 index 000000000..41b47f00b --- /dev/null +++ b/src/appmixer/cloudflare/jobs.lists.js @@ -0,0 +1,67 @@ +const CloudflareListClient = require('./lists/CloudflareListClient'); + +const deleteExpireIpsFromList = async function(context) { + + const expired = await getExpiredItems(context); + + const groups = Object.values(expired); + + const promises = groups.map(chunk => { + + const { email, apiKey, account, list } = chunk.auth; + + context.log('info', { stage: '[CloudFlare] removing ', ips: chunk.ips, list, account }); + + const client = new CloudflareListClient({ email, apiKey }); + // https://developers.cloudflare.com/api/operations/lists-delete-list-items + return client.callEndpoint(context, { + method: 'DELETE', action: `/accounts/${account}/rules/lists/${list}/items`, data: { items: chunk.ips } + }); + }); + + const itemsToDelete = { ids: [], lists: [] }; + + (await Promise.allSettled(promises)).forEach(async (result, i) => { + if (result.status === 'fulfilled') { + itemsToDelete.ids = itemsToDelete.ids.concat(groups[i].ips); + itemsToDelete.lists.push(groups[i]?.auth?.list); + } else { + const operations = groups[i].ips.map(item => ({ + updateOne: { + filter: { id: item.id }, update: { $set: { mtime: new Date } } + } + })); + await (context.db.collection(IPListModel.collection)).bulkWrite(operations); + } + }); + + if (itemsToDelete.ids.length) { + const deleted = await context.db.collection(IPListModel.collection) + .deleteMany({ id: { $in: itemsToDelete.ids.map(item => item.id) } }); + await context.log('info', { + stage: `[CloudFlare] Deleted total ${deleted.deletedCount} ips.`, + lists: itemsToDelete.lists, + itemIds: itemsToDelete.ids + }); + } +}; + +const getExpiredItems = async function(context) { + + const expired = await context.db.collection(IPListModel.collection) + .find({ removeAfter: { $lt: Date.now() } }) + .toArray(); + + return expired.reduce((res, item) => { + const key = item.auth.list; // listId + res[key] = res[key] || { ips: [] }; + res[key].auth = item.auth; + res[key].ips.push({ id: item.id }); + + return res; + }, {}); +}; + +module.exports = { + deleteExpireIpsFromList +}; diff --git a/src/appmixer/cloudflare/waf/jobs.waf.js b/src/appmixer/cloudflare/jobs.waf.js similarity index 97% rename from src/appmixer/cloudflare/waf/jobs.waf.js rename to src/appmixer/cloudflare/jobs.waf.js index c349bf757..4e0f9e8c8 100644 --- a/src/appmixer/cloudflare/waf/jobs.waf.js +++ b/src/appmixer/cloudflare/jobs.waf.js @@ -1,5 +1,5 @@ -const CloudflareWAFClient = require('./CloudflareWAFClient'); -const lib = require('./lib'); +const CloudflareWAFClient = require('./waf/CloudflareWAFClient'); +const lib = require('./waf/lib'); const deleteExpireIps = async function(context) { diff --git a/src/appmixer/cloudflare/lists/CloudflareListClient.js b/src/appmixer/cloudflare/lists/CloudflareListClient.js index b6d141489..8690bf02e 100644 --- a/src/appmixer/cloudflare/lists/CloudflareListClient.js +++ b/src/appmixer/cloudflare/lists/CloudflareListClient.js @@ -1,4 +1,4 @@ -module.exports = class ZoneCloudflareClient { +module.exports = class CloudflareListClient { constructor({ email, apiKey, zoneId, token }) { this.email = email; From e513fda9eb0ef77c92643e441e36b40284db6557 Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Fri, 10 Jan 2025 10:13:40 +0100 Subject: [PATCH 08/21] update --- ...loudflareWAFClient.js => CloudflareAPI.js} | 61 +++++-- src/appmixer/cloudflare/jobs.js | 4 +- src/appmixer/cloudflare/jobs.lists.js | 12 +- src/appmixer/cloudflare/jobs.waf.js | 163 ++++++++++++------ .../lists/AddToIPList/AddToIPList.js | 4 +- .../cloudflare/lists/CloudflareListClient.js | 91 ---------- .../RemoveFromIPList/RemoveFromIPList.js | 4 +- src/appmixer/cloudflare/lists/auth.js | 4 +- src/appmixer/cloudflare/plugin.js | 6 +- src/appmixer/cloudflare/service.json | 2 +- .../CreateCustomRules/CreateCustomRules.js | 6 +- .../waf/CreateCustomRules/component.json | 11 +- src/appmixer/cloudflare/waf/auth.js | 4 +- src/appmixer/cloudflare/waf/lib.js | 58 +------ 14 files changed, 191 insertions(+), 239 deletions(-) rename src/appmixer/cloudflare/{waf/CloudflareWAFClient.js => CloudflareAPI.js} (73%) delete mode 100644 src/appmixer/cloudflare/lists/CloudflareListClient.js diff --git a/src/appmixer/cloudflare/waf/CloudflareWAFClient.js b/src/appmixer/cloudflare/CloudflareAPI.js similarity index 73% rename from src/appmixer/cloudflare/waf/CloudflareWAFClient.js rename to src/appmixer/cloudflare/CloudflareAPI.js index 26e4bd79d..c6800493f 100644 --- a/src/appmixer/cloudflare/waf/CloudflareWAFClient.js +++ b/src/appmixer/cloudflare/CloudflareAPI.js @@ -1,9 +1,9 @@ -module.exports = class CloudflareWAFClient { +module.exports = class CloudflareAPI { constructor({ email, apiKey, zoneId, token }) { this.email = email; - this.apiKey = apiKey; this.zoneId = zoneId; + this.email = email; this.apiKey = apiKey; this.token = token; } @@ -23,25 +23,24 @@ module.exports = class CloudflareWAFClient { }; } - /** - * https://developers.cloudflare.com/api/operations/getZoneRuleset - */ - listZoneRulesets(context) { + async verify(context) { + + const headers = this.getHeaders(); return context.httpRequest({ method: 'GET', - url: `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets`, - headers: this.getHeaders() - }).then(resp => resp.data.result); + url: 'https://api.cloudflare.com/client/v4/user/tokens/verify', + headers + }); } - async verify(context) { + async verifyGlobalApiKey(context) { const headers = this.getHeaders(); return context.httpRequest({ method: 'GET', - url: 'https://api.cloudflare.com/client/v4/user/tokens/verify', + url: 'https://api.cloudflare.com/client/v4/accounts', headers }); } @@ -64,6 +63,45 @@ module.exports = class CloudflareWAFClient { }); } + async findIdsForIPs({ context, client, ips = [], account, list }) { + + const result = []; + for (let ipItem of ips) { + + try { + const { data } = await client.callEndpoint(context, { + method: 'GET', + action: `/accounts/${account}/rules/lists/${list}/items`, + params: { + per_page: 1, + search: ipItem.ip + } + }); + if (data?.result[0] && data?.result.length === 1) { + result.push({ ...data.result[0] }); + } + + } catch (err) { + context.log({ stage: `Invalid IP, IP ${ipItem} hasn't been found in the list ${list}` }); + } + } + + return result; + }; + + // WAF + /** + * https://developers.cloudflare.com/api/operations/getZoneRuleset + */ + listZoneRulesets(context) { + + return context.httpRequest({ + method: 'GET', + url: `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets`, + headers: this.getHeaders() + }).then(resp => resp.data.result); + } + async getRules(context, rulesetId) { const response = await context.httpRequest({ @@ -105,5 +143,4 @@ module.exports = class CloudflareWAFClient { const headers = this.getHeaders(); return context.httpRequest.patch(url, rule, { headers }).then(resp => resp.data); } - }; diff --git a/src/appmixer/cloudflare/jobs.js b/src/appmixer/cloudflare/jobs.js index 73dc9ae7c..b02578254 100644 --- a/src/appmixer/cloudflare/jobs.js +++ b/src/appmixer/cloudflare/jobs.js @@ -50,7 +50,9 @@ module.exports = async (context) => { $expr: { $gt: [{ $subtract: ['$mtime', '$removeAfter'] }, timespan] } }); - await context.log('info', { stage: `[CloudFlare] Deleted ${expired.deletedCount} orphaned rules.` }); + if (expired.deletedCount) { + await context.log('info', { stage: `[CloudFlare] Deleted ${expired.deletedCount} orphaned rules.` }); + } } catch (err) { if (err.message !== 'locked') { context.log('error', { diff --git a/src/appmixer/cloudflare/jobs.lists.js b/src/appmixer/cloudflare/jobs.lists.js index 41b47f00b..87e549101 100644 --- a/src/appmixer/cloudflare/jobs.lists.js +++ b/src/appmixer/cloudflare/jobs.lists.js @@ -1,4 +1,6 @@ -const CloudflareListClient = require('./lists/CloudflareListClient'); +const CloudflareAPI = require('./CloudflareAPI'); + +const getModel = (context) => require('./IPListModel')(context); const deleteExpireIpsFromList = async function(context) { @@ -12,7 +14,7 @@ const deleteExpireIpsFromList = async function(context) { context.log('info', { stage: '[CloudFlare] removing ', ips: chunk.ips, list, account }); - const client = new CloudflareListClient({ email, apiKey }); + const client = new CloudflareAPI({ email, apiKey }); // https://developers.cloudflare.com/api/operations/lists-delete-list-items return client.callEndpoint(context, { method: 'DELETE', action: `/accounts/${account}/rules/lists/${list}/items`, data: { items: chunk.ips } @@ -31,12 +33,12 @@ const deleteExpireIpsFromList = async function(context) { filter: { id: item.id }, update: { $set: { mtime: new Date } } } })); - await (context.db.collection(IPListModel.collection)).bulkWrite(operations); + await (context.db.collection(getModel(context).collection)).bulkWrite(operations); } }); if (itemsToDelete.ids.length) { - const deleted = await context.db.collection(IPListModel.collection) + const deleted = await context.db.collection(getModel(context).collection) .deleteMany({ id: { $in: itemsToDelete.ids.map(item => item.id) } }); await context.log('info', { stage: `[CloudFlare] Deleted total ${deleted.deletedCount} ips.`, @@ -48,7 +50,7 @@ const deleteExpireIpsFromList = async function(context) { const getExpiredItems = async function(context) { - const expired = await context.db.collection(IPListModel.collection) + const expired = await context.db.collection(getModel(context).collection) .find({ removeAfter: { $lt: Date.now() } }) .toArray(); diff --git a/src/appmixer/cloudflare/jobs.waf.js b/src/appmixer/cloudflare/jobs.waf.js index 4e0f9e8c8..d523ff2f5 100644 --- a/src/appmixer/cloudflare/jobs.waf.js +++ b/src/appmixer/cloudflare/jobs.waf.js @@ -1,99 +1,107 @@ -const CloudflareWAFClient = require('./waf/CloudflareWAFClient'); -const lib = require('./waf/lib'); +const CloudflareAPI = require('./CloudflareAPI'); +const { Address4, Address6 } = require('ip-address'); + +const getModel = (context) => require('./RulesIPsModel')(context); const deleteExpireIps = async function(context) { - const expired = await getExpiredItems(context); + try { + + // db model items grouped by ruleId + const expired = await getExpiredItems(context); + + if (Object.keys(expired).length) { + await context.log('info', { type: '[CloudFlareWAF] expired.', expired }); + } + + const rulesToUpdate = await retrieveRulesForUpdate(context, expired); + + const dbItemsToDelete = await updateRules(context, rulesToUpdate); + + await deleteDBItems(context, dbItemsToDelete); + + } catch (e) { + await context.log('error', { + type: '[CloudFlareWAF] Unexpected error', + error: context.utils.Error.stringify(e) + }); + } +}; - await context.log('info', { type: '[CloudFlareWAF] expired.', expired }); +const retrieveRulesForUpdate = async function(context, expired) { + const rulesToUpdate = []; const groups = Object.values(expired); const getRulePromises = groups.map(item => { - const client = new CloudflareWAFClient({ token: item.auth.token, zoneId: item.zoneId }); + const client = new CloudflareAPI({ token: item.auth.token, zoneId: item.zoneId }); return client.getRules(context, item.rulesetId); }); - const rulesToUpdate = []; (await Promise.allSettled(getRulePromises)).forEach((result, i) => { + const dbModelItem = groups[i]; + if (result.status === 'fulfilled') { - const item = groups[i]; const { result: { rules = [] } } = result.value; - const rule = rules.find(r => r.id === item.ruleId); + const rule = rules.find(r => r.id === dbModelItem.ruleId); if (rule) { rulesToUpdate.push({ - ...item, rule: lib.removeIpsFromRule(rule, item?.ips?.map(i => i.ip)) + model: dbModelItem, rule: removeIpsFromRule(rule, dbModelItem?.ips?.map(i => i.ip)) }); } + } else { + // eslint-disable-next-line no-unused-vars + const { auth, ...info } = dbModelItem; + context.log('info', { type: '[CloudFlareWAF] Unable to retrieve rule for expired item', data: info }); } }); - await context.log('info', { type: '[CloudFlareWAF] to update.', rulesToUpdate }); + return rulesToUpdate; +}; + +const updateRules = async function(context, rulesToUpdate) { + + let dbItemsToDelete = []; const updatePromises = rulesToUpdate.map(rulesToUpdate => { const { zoneId, rulesetId, auth, rule } = rulesToUpdate; - const client = new CloudflareWAFClient({ token: auth.token, zoneId }); + const client = new CloudflareAPI({ token: auth.token, zoneId }); return client.updateBlockRule(context, rulesetId, rule); }); - try { - - let dbItemsToDelete = []; - (await Promise.allSettled(updatePromises)).forEach(async (result, i) => { - - const item = rulesToUpdate[i]; - const dbItems = item?.ips?.map(i => i.id) || []; - context.log('info', { type: '[CloudFlareWAF] db item', item, s: result.status, dbItems }); - - if (result.status === 'fulfilled') { - dbItemsToDelete = dbItemsToDelete.concat(dbItems); - } else { - context.log('info', { type: '[CloudFlareWAF] db item delet 111' }); - const model = require('../RulesIPsModel')(context); - context.log('info', { type: '[CloudFlareWAF] db item delet 222' }); - - const operations = dbItems.map(item => ({ - updateOne: { - filter: { id: item.id }, update: { $set: { mtime: new Date } } - } - })); - context.log('info', { type: '[CloudFlareWAF] db item ops', operations }); - - await (context.db.collection(model.collection)).bulkWrite(operations); + (await Promise.allSettled(updatePromises)).forEach(async (result, i) => { - context.log('info', { type: '[CloudFlareWAF] AAA' }); + const item = rulesToUpdate[i]; + const dbItems = item?.ips?.map(i => i.id) || []; - // context.log('error', { type: '[CloudFlareWAF] Update rule failed', data: { ...item.rule } }); - } - }); + if (result.status === 'fulfilled') { + dbItemsToDelete = dbItemsToDelete.concat(dbItems); + } else { + // eslint-disable-next-line no-unused-vars + const { auth, ...info } = item; + context.log('info', { + type: '[CloudFlareWAF] Unable to delete IPs from rule.', + data: info + }); + } + }); - await deleteDBItems(context, dbItemsToDelete); - } catch (e) { - context.log('error', { type: '[CloudFlareWAF] Unexpected error', data: e?.message || e }); - } + return dbItemsToDelete; }; const deleteDBItems = async function(context, ids = []) { - context.log('info', { type: '[CloudFlareWAF] db items to delete', data: ids }); - - const model = require('../RulesIPsModel')(context); if (ids.length) { - - const deleted = await context.db.collection(model.collection) + await context.db.collection(getModel(context).collection) .deleteMany({ id: { $in: ids } }); - - await context.log('info', `[CloudFlare] Deleted ${deleted.deletedCount} ips.`); } }; const getExpiredItems = async function(context) { - const model = require('../RulesIPsModel')(context); - - const expired = await context.db.collection(model.collection) + const expired = await context.db.collection(getModel(context).collection) .find({ removeAfter: { $lt: Date.now() } }) .toArray(); @@ -111,6 +119,53 @@ const getExpiredItems = async function(context) { }, {}); }; +function removeIpsFromRule(rule, ipsToRemove = []) { + const data = extractIPs(rule.expression); + const ips = []; + data.forEach(ip => { + if (!ipsToRemove.includes(ip)) { + ips.push(ip); + } + }); + + const expression = getBlockExpression(ips); + + return { ...rule, expression }; +} + +function extractIPs(expression) { + + // Regular expression to match IP addresses within the curly braces + // const ipRegex = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g; + // Extracting the section of the expression that contains the IPs + + const match = expression.match(/{([^}]+)}/); + if (!match) { + // If no match for IP address block found, return empty array + return []; + } + + const ipsBlock = match[1]; // This contains "192.0.2.0 192.0.2.2 192.0.2.3" + return ipsBlock.split(' '); +} + +function getBlockExpression(ips) { + const ipv4 = ips.filter(ip => Address4.isValid(ip)); + const ipv6 = ips.filter(ip => Address6.isValid(ip)); + const formattedIpv6 = ipv6.length > 0 ? ipv6.map(ip => removeInterfaceIdentifierAndAddCidr(ip)) : ipv6; + const allIps = [...ipv4, ...formattedIpv6].sort(); + + return `(ip.src in {${allIps.join(' ')}})`; +} + +function removeInterfaceIdentifierAndAddCidr(ip) { + const networkPrefix = ip + .split(':') + .slice(0, 4) + .join(':'); + return `${networkPrefix}::/64`; +} + module.exports = { - deleteExpireIps + deleteExpireIps, getBlockExpression, extractIPs }; diff --git a/src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js b/src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js index 8a4d86dd6..3bc0bf376 100644 --- a/src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js +++ b/src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js @@ -1,4 +1,4 @@ -const CloudflareListClient = require('../CloudflareListClient'); +const CloudflareAPI = require('../../CloudflareAPI'); const lib = require('../lib'); let attempts = 0; @@ -36,7 +36,7 @@ module.exports = { const { accountsFetch, listFetch } = context.properties; const { account, list, ips, ttl } = context.messages.in.content; - const client = new CloudflareListClient({ email, apiKey }); + const client = new CloudflareAPI({ email, apiKey }); if (accountsFetch || listFetch) { return await lib.fetchInputs(context, client, { account, listFetch, accountsFetch }); diff --git a/src/appmixer/cloudflare/lists/CloudflareListClient.js b/src/appmixer/cloudflare/lists/CloudflareListClient.js deleted file mode 100644 index 8690bf02e..000000000 --- a/src/appmixer/cloudflare/lists/CloudflareListClient.js +++ /dev/null @@ -1,91 +0,0 @@ -module.exports = class CloudflareListClient { - - constructor({ email, apiKey, zoneId, token }) { - this.email = email; - this.zoneId = zoneId; - this.email = email; - this.apiKey = apiKey; - this.token = token; - } - - getHeaders() { - if (this.token) { - return { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${this.token}` - }; - } - - return { - 'Content-Type': 'application/json', - 'X-Auth-Email': this.email, - 'X-Auth-Key': this.apiKey - }; - } - - async verify(context) { - - const headers = this.getHeaders(); - - return context.httpRequest({ - method: 'GET', - url: 'https://api.cloudflare.com/client/v4/user/tokens/verify', - headers - }); - } - - async verifyGlobalApiKey(context) { - - const headers = this.getHeaders(); - - return context.httpRequest({ - method: 'GET', - url: 'https://api.cloudflare.com/client/v4/accounts', - headers - }); - } - - async callEndpoint(context, { - action, - method = 'GET', - data, - params - }) { - - const headers = this.getHeaders(); - - return context.httpRequest({ - method, - url: `https://api.cloudflare.com/client/v4${action}`, - headers, - data, - params - }); - } - - async findIdsForIPs({ context, client, ips = [], account, list }) { - - const result = []; - for (let ipItem of ips) { - - try { - const { data } = await client.callEndpoint(context, { - method: 'GET', - action: `/accounts/${account}/rules/lists/${list}/items`, - params: { - per_page: 1, - search: ipItem.ip - } - }); - if (data?.result[0] && data?.result.length === 1) { - result.push({ ...data.result[0] }); - } - - } catch (err) { - context.log({ stage: `Invalid IP, IP ${ipItem} hasn't been found in the list ${list}` }); - } - } - - return result; - }; -}; diff --git a/src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js b/src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js index 2a76d95d8..a5699ed11 100644 --- a/src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js +++ b/src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js @@ -1,4 +1,4 @@ -const CloudflareListClient = require('../CloudflareListClient'); +const CloudflareAPI = require('../../CloudflareAPI'); let attempts = 0; const getStatus = async function(context, client, { account, id }) { @@ -32,7 +32,7 @@ module.exports = { const { apiKey, email } = context.auth; const { account, list, ips } = context.messages.in.content; - const client = new CloudflareListClient({ email, apiKey }); + const client = new CloudflareAPI({ email, apiKey }); const ipsList = ips.AND; diff --git a/src/appmixer/cloudflare/lists/auth.js b/src/appmixer/cloudflare/lists/auth.js index be0b807a4..4f873a46f 100644 --- a/src/appmixer/cloudflare/lists/auth.js +++ b/src/appmixer/cloudflare/lists/auth.js @@ -1,6 +1,6 @@ 'use strict'; -const CloudflareListClient = require('./CloudflareListClient'); +const CloudflareAPI = require('../CloudflareAPI'); module.exports = { @@ -35,7 +35,7 @@ module.exports = { validate: async function(context) { const { email, apiKey } = context; - const client = new CloudflareListClient({ email, apiKey }); + const client = new CloudflareAPI({ email, apiKey }); const { data } = await client.verifyGlobalApiKey(context); return data.success || false; } diff --git a/src/appmixer/cloudflare/plugin.js b/src/appmixer/cloudflare/plugin.js index 03088a109..a96c44169 100644 --- a/src/appmixer/cloudflare/plugin.js +++ b/src/appmixer/cloudflare/plugin.js @@ -1,11 +1,7 @@ 'use strict'; module.exports = async context => { - context.log('info', '[CloudFlare] Initializing CloudFlare plugin.'); await require('./routes')(context); - - context.log('info', '[CloudFlare] Scheduling CloudFlare jobs.'); await require('./jobs')(context); - - context.log('info', '[CloudFlare] CloudFlare plugin initialized.'); + context.log('info', '[CloudFlare] CloudFlare plugin successfully initialized.'); }; diff --git a/src/appmixer/cloudflare/service.json b/src/appmixer/cloudflare/service.json index e83a1161d..151b1d03b 100644 --- a/src/appmixer/cloudflare/service.json +++ b/src/appmixer/cloudflare/service.json @@ -4,5 +4,5 @@ "category": "applications", "description": "CloudFlare provides content delivery network services, cloud cybersecurity, DDoS mitigation, wide area network services, reverse proxies, Domain Name Service, and ICANN-accredited domain registration services.", "icon": "data:image/png;base64,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", - "version": "1.0.0" + "version": "1.0.5" } diff --git a/src/appmixer/cloudflare/waf/CreateCustomRules/CreateCustomRules.js b/src/appmixer/cloudflare/waf/CreateCustomRules/CreateCustomRules.js index c24ac6e38..e1660285a 100644 --- a/src/appmixer/cloudflare/waf/CreateCustomRules/CreateCustomRules.js +++ b/src/appmixer/cloudflare/waf/CreateCustomRules/CreateCustomRules.js @@ -1,5 +1,5 @@ -const CloudflareWAFClient = require('../CloudflareWAFClient'); -const lib = require('../../lib'); +const CloudflareAPI = require('../../CloudflareAPI'); +const lib = require('../lib'); const crypto = require('crypto'); module.exports = { @@ -14,7 +14,7 @@ module.exports = { } const parsedIps = Array.isArray(ips) ? ips : ips.split(','); - const client = new CloudflareWAFClient({ zoneId, token: apiToken }); + const client = new CloudflareAPI({ zoneId, token: apiToken }); const ruleset = (await client.listZoneRulesets(context)) .find(ruleset => ruleset.kind === 'zone' && ruleset.phase === 'http_request_firewall_custom'); diff --git a/src/appmixer/cloudflare/waf/CreateCustomRules/component.json b/src/appmixer/cloudflare/waf/CreateCustomRules/component.json index 3c8f548fb..83fb8a3b3 100644 --- a/src/appmixer/cloudflare/waf/CreateCustomRules/component.json +++ b/src/appmixer/cloudflare/waf/CreateCustomRules/component.json @@ -3,6 +3,10 @@ "label": "cloudflare CreateCustomRules", "description": "Create custom rules in waf", "private": false, + "version": "1.0.5", + "dependencies": { + "service": "1.0.5" + }, "auth": { "service": "appmixer:cloudflare:waf" }, @@ -61,10 +65,5 @@ "outPorts": [ { "name": "out" } ], - "icon": "data:image/png;base64,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", - "version": "1.0.5", - "dependencies": { - "service": "1.0.5", - "module": "1.0.0" - } + "icon": "data:image/png;base64,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" } \ No newline at end of file diff --git a/src/appmixer/cloudflare/waf/auth.js b/src/appmixer/cloudflare/waf/auth.js index a20682eff..a9db7a581 100644 --- a/src/appmixer/cloudflare/waf/auth.js +++ b/src/appmixer/cloudflare/waf/auth.js @@ -1,6 +1,6 @@ 'use strict'; -const CloudflareWAFClient = require('./CloudflareWAFClient'); +const CloudflareAPI = require('../CloudflareAPI'); module.exports = { @@ -30,7 +30,7 @@ module.exports = { validate: async function(context) { - const client = new CloudflareWAFClient({ token: context.apiToken }); + const client = new CloudflareAPI({ token: context.apiToken }); const { data } = await client.verify(context); return data.success || false; } diff --git a/src/appmixer/cloudflare/waf/lib.js b/src/appmixer/cloudflare/waf/lib.js index 2a37d55f9..76397acce 100644 --- a/src/appmixer/cloudflare/waf/lib.js +++ b/src/appmixer/cloudflare/waf/lib.js @@ -1,47 +1,18 @@ -const { Address4, Address6 } = require('ip-address'); const ruleDescription = 'Salt detected high severity attacker'; const ruleRefPrefix = 'SALT'; const RULE_MAX_CAPACITY = 4096; +// extractIPs, getBlockExpression are also used in jobs.waf. They cannot be here, as jobs.waf +// cannot require waf/lib, therefore these methods are defined in the jobs.waf.js file +const { extractIPs, getBlockExpression } = require('../jobs.waf'); + module.exports = { - extractIPs, getIpsFromRules, prepareRulesForCreateOrUpdate, getBlockRule, - findIpsInRules, - removeIpsFromRule + findIpsInRules }; -function extractIPs(expression) { - - // Regular expression to match IP addresses within the curly braces - // const ipRegex = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g; - // Extracting the section of the expression that contains the IPs - - const match = expression.match(/{([^}]+)}/); - if (!match) { - // If no match for IP address block found, return empty array - return []; - } - - const ipsBlock = match[1]; // This contains "192.0.2.0 192.0.2.2 192.0.2.3" - return ipsBlock.split(' '); -} - -function removeIpsFromRule(rule, ipsToRemove = []) { - const data = extractIPs(rule.expression); - const ips = []; - data.forEach(ip => { - if (!ipsToRemove.includes(ip)) { - ips.push(ip); - } - }); - - const expression = getBlockExpression(ips); - - return { ...rule, expression }; -} - function getIpsFromRules(rules) { const ips = {}; @@ -139,23 +110,4 @@ function getAvailableRule(rules, requiredCapacity, ruleCapacity = RULE_MAX_CAPAC return rules.find(rule => rule.usedCapacity + requiredCapacity < ruleCapacity); } -function removeInterfaceIdentifierAndAddCidr(ip) { - const networkPrefix = ip - .split(':') - .slice(0, 4) - .join(':'); - return `${networkPrefix}::/64`; -} - -function getBlockExpression(ips) { - const ipv4 = ips.filter(ip => Address4.isValid(ip)); - const ipv6 = ips.filter(ip => Address6.isValid(ip)); - const formattedIpv6 = ipv6.length > 0 - ? ipv6.map(ip => removeInterfaceIdentifierAndAddCidr(ip)) - : ipv6; - const allIps = [...ipv4, ...formattedIpv6].sort(); - - return `(ip.src in {${allIps.join(' ')}})`; -} - From 77f4882784d18e605bd04c2cf13c53f255318809 Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Fri, 10 Jan 2025 11:29:57 +0100 Subject: [PATCH 09/21] update --- src/appmixer/cloudflare/jobs.waf.js | 69 ++++++++++++++++++----------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/src/appmixer/cloudflare/jobs.waf.js b/src/appmixer/cloudflare/jobs.waf.js index d523ff2f5..7c356f0a7 100644 --- a/src/appmixer/cloudflare/jobs.waf.js +++ b/src/appmixer/cloudflare/jobs.waf.js @@ -7,53 +7,54 @@ const deleteExpireIps = async function(context) { try { - // db model items grouped by ruleId const expired = await getExpiredItems(context); - - if (Object.keys(expired).length) { - await context.log('info', { type: '[CloudFlareWAF] expired.', expired }); + if (expired.length) { + await context.log('info', { type: '[Cloudflare WAF] expired.', data: sanitizeItems(expired) }); } const rulesToUpdate = await retrieveRulesForUpdate(context, expired); + if (rulesToUpdate.length) { + await context.log('info', { type: '[Cloudflare WAF] rulesTo update.', data: sanitizeItems(rulesToUpdate) }); + } const dbItemsToDelete = await updateRules(context, rulesToUpdate); - await deleteDBItems(context, dbItemsToDelete); } catch (e) { await context.log('error', { - type: '[CloudFlareWAF] Unexpected error', - error: context.utils.Error.stringify(e) + type: '[Cloudflare WAF] Unexpected error', error: context.utils.Error.stringify(e) }); } }; -const retrieveRulesForUpdate = async function(context, expired) { +const retrieveRulesForUpdate = async function(context, expired = []) { const rulesToUpdate = []; - const groups = Object.values(expired); - const getRulePromises = groups.map(item => { - const client = new CloudflareAPI({ token: item.auth.token, zoneId: item.zoneId }); - return client.getRules(context, item.rulesetId); + const getRulePromises = expired.map(item => { + const { auth, zoneId, rulesetId } = item; + const client = new CloudflareAPI({ token: auth.token, zoneId }); + return client.getRules(context, rulesetId); }); (await Promise.allSettled(getRulePromises)).forEach((result, i) => { - const dbModelItem = groups[i]; + const item = expired[i]; if (result.status === 'fulfilled') { const { result: { rules = [] } } = result.value; - const rule = rules.find(r => r.id === dbModelItem.ruleId); + const rule = rules.find(r => r.id === item.ruleId); if (rule) { rulesToUpdate.push({ - model: dbModelItem, rule: removeIpsFromRule(rule, dbModelItem?.ips?.map(i => i.ip)) + ...item, + rule: removeIpsFromRule(rule, item?.ips?.map(i => i.ip)) }); } } else { - // eslint-disable-next-line no-unused-vars - const { auth, ...info } = dbModelItem; - context.log('info', { type: '[CloudFlareWAF] Unable to retrieve rule for expired item', data: info }); + context.log('info', { + type: '[Cloudflare WAF] Unable to retrieve rule for expired item', + data: sanitizeItems([item]) + }); } }); @@ -63,9 +64,10 @@ const retrieveRulesForUpdate = async function(context, expired) { const updateRules = async function(context, rulesToUpdate) { let dbItemsToDelete = []; - const updatePromises = rulesToUpdate.map(rulesToUpdate => { + const updatePromises = rulesToUpdate.map(ruleToUpdate => { + + const { zoneId, rulesetId, auth, rule } = ruleToUpdate; - const { zoneId, rulesetId, auth, rule } = rulesToUpdate; const client = new CloudflareAPI({ token: auth.token, zoneId }); return client.updateBlockRule(context, rulesetId, rule); @@ -79,11 +81,9 @@ const updateRules = async function(context, rulesToUpdate) { if (result.status === 'fulfilled') { dbItemsToDelete = dbItemsToDelete.concat(dbItems); } else { - // eslint-disable-next-line no-unused-vars - const { auth, ...info } = item; context.log('info', { - type: '[CloudFlareWAF] Unable to delete IPs from rule.', - data: info + type: '[Cloudflare WAF] Unable to delete IPs from rule.', + data: sanitizeItems([item]) }); } }); @@ -105,7 +105,9 @@ const getExpiredItems = async function(context) { .find({ removeAfter: { $lt: Date.now() } }) .toArray(); - return expired.reduce((res, item) => { + if (expired.length === 0) return []; + + const groupedByRules = expired.reduce((res, item) => { const key = item.ruleId; res[key] = res[key] || { ips: [] }; res[key].ips.push({ ip: item.ip, id: item.id }); @@ -114,9 +116,10 @@ const getExpiredItems = async function(context) { res[key].zoneId = item.zoneId; res[key].ruleId = item.ruleId; res[key].rulesetId = item.rulesetId; - return res; }, {}); + + return Object.values(groupedByRules); }; function removeIpsFromRule(rule, ipsToRemove = []) { @@ -166,6 +169,20 @@ function removeInterfaceIdentifierAndAddCidr(ip) { return `${networkPrefix}::/64`; } +/** + * remove sensitive info from objects + * @param items + * @returns {Omit<*, 'auth'>[]} + */ +function sanitizeItems(items = []) { + + return items.map(item => { + // eslint-disable-next-line no-unused-vars + const { auth, ...info } = item; + return info; + }); +} + module.exports = { deleteExpireIps, getBlockExpression, extractIPs }; From 0e35f38145f4a6b0c082eb6b5c6605ecf8543609 Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Fri, 10 Jan 2025 12:07:28 +0100 Subject: [PATCH 10/21] split to new connectors --- src/appmixer/cloudflare/service.json | 8 - .../CloudflareAPI.js | 0 .../IPListModel.js | 0 .../lists => cloudflareLists}/bundle.json | 0 .../{cloudflare => cloudflareLists}/config.js | 0 .../{cloudflare => cloudflareLists}/jobs.js | 3 - .../jobs.lists.js | 0 .../lists/AddToIPList/AddToIPList.js | 0 .../lists/AddToIPList/component.json | 4 +- .../RemoveFromIPList/RemoveFromIPList.js | 0 .../lists/RemoveFromIPList/component.json | 4 +- .../lists/auth.js | 0 .../lists/lib.js | 0 .../cloudflareLists/package-lock.json | 23 +++ .../package.json | 0 .../{cloudflare => cloudflareLists}/plugin.js | 0 .../{cloudflare => cloudflareLists}/quota.js | 0 .../{cloudflare => cloudflareLists}/routes.js | 0 .../service.json} | 5 +- src/appmixer/cloudflareWAF/CloudflareAPI.js | 146 ++++++++++++++++++ .../RulesIPsModel.js | 0 .../{cloudflare/waf => cloudflareWAF}/auth.js | 0 .../waf => cloudflareWAF}/bundle.json | 2 +- src/appmixer/cloudflareWAF/config.js | 20 +++ src/appmixer/cloudflareWAF/jobs.js | 63 ++++++++ .../{cloudflare => cloudflareWAF}/jobs.waf.js | 0 src/appmixer/cloudflareWAF/package-lock.json | 23 +++ src/appmixer/cloudflareWAF/package.json | 8 + src/appmixer/cloudflareWAF/plugin.js | 7 + src/appmixer/cloudflareWAF/quota.js | 15 ++ src/appmixer/cloudflareWAF/routes.js | 53 +++++++ .../service.json} | 5 +- .../CreateCustomRules/CreateCustomRules.js | 0 .../waf/CreateCustomRules/component.json | 6 +- .../create-custom-rules-handler.js | 0 .../{cloudflare => cloudflareWAF}/waf/lib.js | 0 36 files changed, 372 insertions(+), 23 deletions(-) delete mode 100644 src/appmixer/cloudflare/service.json rename src/appmixer/{cloudflare => cloudflareLists}/CloudflareAPI.js (100%) rename src/appmixer/{cloudflare => cloudflareLists}/IPListModel.js (100%) rename src/appmixer/{cloudflare/lists => cloudflareLists}/bundle.json (100%) rename src/appmixer/{cloudflare => cloudflareLists}/config.js (100%) rename src/appmixer/{cloudflare => cloudflareLists}/jobs.js (94%) rename src/appmixer/{cloudflare => cloudflareLists}/jobs.lists.js (100%) rename src/appmixer/{cloudflare => cloudflareLists}/lists/AddToIPList/AddToIPList.js (100%) rename src/appmixer/{cloudflare => cloudflareLists}/lists/AddToIPList/component.json (98%) rename src/appmixer/{cloudflare => cloudflareLists}/lists/RemoveFromIPList/RemoveFromIPList.js (100%) rename src/appmixer/{cloudflare => cloudflareLists}/lists/RemoveFromIPList/component.json (98%) rename src/appmixer/{cloudflare => cloudflareLists}/lists/auth.js (100%) rename src/appmixer/{cloudflare => cloudflareLists}/lists/lib.js (100%) create mode 100644 src/appmixer/cloudflareLists/package-lock.json rename src/appmixer/{cloudflare => cloudflareLists}/package.json (100%) rename src/appmixer/{cloudflare => cloudflareLists}/plugin.js (100%) rename src/appmixer/{cloudflare => cloudflareLists}/quota.js (100%) rename src/appmixer/{cloudflare => cloudflareLists}/routes.js (100%) rename src/appmixer/{cloudflare/lists/module.json => cloudflareLists/service.json} (98%) create mode 100644 src/appmixer/cloudflareWAF/CloudflareAPI.js rename src/appmixer/{cloudflare => cloudflareWAF}/RulesIPsModel.js (100%) rename src/appmixer/{cloudflare/waf => cloudflareWAF}/auth.js (100%) rename src/appmixer/{cloudflare/waf => cloudflareWAF}/bundle.json (54%) create mode 100644 src/appmixer/cloudflareWAF/config.js create mode 100644 src/appmixer/cloudflareWAF/jobs.js rename src/appmixer/{cloudflare => cloudflareWAF}/jobs.waf.js (100%) create mode 100644 src/appmixer/cloudflareWAF/package-lock.json create mode 100644 src/appmixer/cloudflareWAF/package.json create mode 100644 src/appmixer/cloudflareWAF/plugin.js create mode 100644 src/appmixer/cloudflareWAF/quota.js create mode 100644 src/appmixer/cloudflareWAF/routes.js rename src/appmixer/{cloudflare/waf/module.json => cloudflareWAF/service.json} (98%) rename src/appmixer/{cloudflare => cloudflareWAF}/waf/CreateCustomRules/CreateCustomRules.js (100%) rename src/appmixer/{cloudflare => cloudflareWAF}/waf/CreateCustomRules/component.json (97%) rename src/appmixer/{cloudflare => cloudflareWAF}/waf/CreateCustomRules/create-custom-rules-handler.js (100%) rename src/appmixer/{cloudflare => cloudflareWAF}/waf/lib.js (100%) diff --git a/src/appmixer/cloudflare/service.json b/src/appmixer/cloudflare/service.json deleted file mode 100644 index 151b1d03b..000000000 --- a/src/appmixer/cloudflare/service.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "appmixer.cloudflare", - "label": "Salt CloudFlare", - "category": "applications", - "description": "CloudFlare provides content delivery network services, cloud cybersecurity, DDoS mitigation, wide area network services, reverse proxies, Domain Name Service, and ICANN-accredited domain registration services.", - "icon": "data:image/png;base64,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", - "version": "1.0.5" -} diff --git a/src/appmixer/cloudflare/CloudflareAPI.js b/src/appmixer/cloudflareLists/CloudflareAPI.js similarity index 100% rename from src/appmixer/cloudflare/CloudflareAPI.js rename to src/appmixer/cloudflareLists/CloudflareAPI.js diff --git a/src/appmixer/cloudflare/IPListModel.js b/src/appmixer/cloudflareLists/IPListModel.js similarity index 100% rename from src/appmixer/cloudflare/IPListModel.js rename to src/appmixer/cloudflareLists/IPListModel.js diff --git a/src/appmixer/cloudflare/lists/bundle.json b/src/appmixer/cloudflareLists/bundle.json similarity index 100% rename from src/appmixer/cloudflare/lists/bundle.json rename to src/appmixer/cloudflareLists/bundle.json diff --git a/src/appmixer/cloudflare/config.js b/src/appmixer/cloudflareLists/config.js similarity index 100% rename from src/appmixer/cloudflare/config.js rename to src/appmixer/cloudflareLists/config.js diff --git a/src/appmixer/cloudflare/jobs.js b/src/appmixer/cloudflareLists/jobs.js similarity index 94% rename from src/appmixer/cloudflare/jobs.js rename to src/appmixer/cloudflareLists/jobs.js index b02578254..7e3a0f2b3 100644 --- a/src/appmixer/cloudflare/jobs.js +++ b/src/appmixer/cloudflareLists/jobs.js @@ -1,10 +1,8 @@ -const wafJobs = require('./jobs.waf'); const listsJobs = require('./jobs.lists'); module.exports = async (context) => { const IPListModel = require('./IPListModel')(context); - const RulesIPsModel = require('./RulesIPsModel')(context); const config = require('./config')(context); @@ -16,7 +14,6 @@ module.exports = async (context) => { try { await listsJobs.deleteExpireIpsFromList(context); - await wafJobs.deleteExpireIps(context); } finally { lock.unlock(); await context.log('trace', '[CloudFlare] rule delete job finished. Lock unlocked.'); diff --git a/src/appmixer/cloudflare/jobs.lists.js b/src/appmixer/cloudflareLists/jobs.lists.js similarity index 100% rename from src/appmixer/cloudflare/jobs.lists.js rename to src/appmixer/cloudflareLists/jobs.lists.js diff --git a/src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js b/src/appmixer/cloudflareLists/lists/AddToIPList/AddToIPList.js similarity index 100% rename from src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js rename to src/appmixer/cloudflareLists/lists/AddToIPList/AddToIPList.js diff --git a/src/appmixer/cloudflare/lists/AddToIPList/component.json b/src/appmixer/cloudflareLists/lists/AddToIPList/component.json similarity index 98% rename from src/appmixer/cloudflare/lists/AddToIPList/component.json rename to src/appmixer/cloudflareLists/lists/AddToIPList/component.json index 267aa3d4f..db4ee9d20 100644 --- a/src/appmixer/cloudflare/lists/AddToIPList/component.json +++ b/src/appmixer/cloudflareLists/lists/AddToIPList/component.json @@ -1,10 +1,10 @@ { - "name": "appmixer.cloudflare.lists.AddToIPList", + "name": "appmixer.cloudflareLists.lists.AddToIPList", "label": "Add IP to IP List", "description": "Appends new items to the IP list. Maximum capacity if the list is 1000 items.", "private": false, "auth": { - "service": "appmixer:cloudflare:lists" + "service": "appmixer:cloudflareLists" }, "quota": { "manager": "appmixer:cloudflare", diff --git a/src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js b/src/appmixer/cloudflareLists/lists/RemoveFromIPList/RemoveFromIPList.js similarity index 100% rename from src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js rename to src/appmixer/cloudflareLists/lists/RemoveFromIPList/RemoveFromIPList.js diff --git a/src/appmixer/cloudflare/lists/RemoveFromIPList/component.json b/src/appmixer/cloudflareLists/lists/RemoveFromIPList/component.json similarity index 98% rename from src/appmixer/cloudflare/lists/RemoveFromIPList/component.json rename to src/appmixer/cloudflareLists/lists/RemoveFromIPList/component.json index fffbe3ced..3e19d912c 100644 --- a/src/appmixer/cloudflare/lists/RemoveFromIPList/component.json +++ b/src/appmixer/cloudflareLists/lists/RemoveFromIPList/component.json @@ -1,10 +1,10 @@ { - "name": "appmixer.cloudflare.lists.RemoveFromIPList", + "name": "appmixer.cloudflareLists.lists.RemoveFromIPList", "label": "Remove IPs From IP List", "description": "Removes IPs from the IP list.", "private": false, "auth": { - "service": "appmixer:cloudflare:lists" + "service": "appmixer:cloudflareLists" }, "quota": { "manager": "appmixer:cloudflare", diff --git a/src/appmixer/cloudflare/lists/auth.js b/src/appmixer/cloudflareLists/lists/auth.js similarity index 100% rename from src/appmixer/cloudflare/lists/auth.js rename to src/appmixer/cloudflareLists/lists/auth.js diff --git a/src/appmixer/cloudflare/lists/lib.js b/src/appmixer/cloudflareLists/lists/lib.js similarity index 100% rename from src/appmixer/cloudflare/lists/lib.js rename to src/appmixer/cloudflareLists/lists/lib.js diff --git a/src/appmixer/cloudflareLists/package-lock.json b/src/appmixer/cloudflareLists/package-lock.json new file mode 100644 index 000000000..49189ba94 --- /dev/null +++ b/src/appmixer/cloudflareLists/package-lock.json @@ -0,0 +1,23 @@ +{ + "name": "appmixer.cloudflare", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "appmixer.cloudflare", + "version": "1.0.0", + "dependencies": { + "ip-address": "^10.0.1" + } + }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "engines": { + "node": ">= 12" + } + } + } +} diff --git a/src/appmixer/cloudflare/package.json b/src/appmixer/cloudflareLists/package.json similarity index 100% rename from src/appmixer/cloudflare/package.json rename to src/appmixer/cloudflareLists/package.json diff --git a/src/appmixer/cloudflare/plugin.js b/src/appmixer/cloudflareLists/plugin.js similarity index 100% rename from src/appmixer/cloudflare/plugin.js rename to src/appmixer/cloudflareLists/plugin.js diff --git a/src/appmixer/cloudflare/quota.js b/src/appmixer/cloudflareLists/quota.js similarity index 100% rename from src/appmixer/cloudflare/quota.js rename to src/appmixer/cloudflareLists/quota.js diff --git a/src/appmixer/cloudflare/routes.js b/src/appmixer/cloudflareLists/routes.js similarity index 100% rename from src/appmixer/cloudflare/routes.js rename to src/appmixer/cloudflareLists/routes.js diff --git a/src/appmixer/cloudflare/lists/module.json b/src/appmixer/cloudflareLists/service.json similarity index 98% rename from src/appmixer/cloudflare/lists/module.json rename to src/appmixer/cloudflareLists/service.json index 26a97504f..8571af62a 100644 --- a/src/appmixer/cloudflare/lists/module.json +++ b/src/appmixer/cloudflareLists/service.json @@ -1,7 +1,8 @@ { - "name": "appmixer.cloudflare.lists", + "name": "appmixer.cloudflareLists", "label": "CloudFlare Lists", "category": "applications", "description": "CloudFlare List integrations allows you to work with IP lists.", - "icon": "data:image/png;base64,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" + "icon": "data:image/png;base64,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", + "version": "1.0.5" } diff --git a/src/appmixer/cloudflareWAF/CloudflareAPI.js b/src/appmixer/cloudflareWAF/CloudflareAPI.js new file mode 100644 index 000000000..c6800493f --- /dev/null +++ b/src/appmixer/cloudflareWAF/CloudflareAPI.js @@ -0,0 +1,146 @@ +module.exports = class CloudflareAPI { + + constructor({ email, apiKey, zoneId, token }) { + this.email = email; + this.zoneId = zoneId; + this.email = email; + this.apiKey = apiKey; + this.token = token; + } + + getHeaders() { + if (this.token) { + return { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${this.token}` + }; + } + + return { + 'Content-Type': 'application/json', + 'X-Auth-Email': this.email, + 'X-Auth-Key': this.apiKey + }; + } + + async verify(context) { + + const headers = this.getHeaders(); + + return context.httpRequest({ + method: 'GET', + url: 'https://api.cloudflare.com/client/v4/user/tokens/verify', + headers + }); + } + + async verifyGlobalApiKey(context) { + + const headers = this.getHeaders(); + + return context.httpRequest({ + method: 'GET', + url: 'https://api.cloudflare.com/client/v4/accounts', + headers + }); + } + + async callEndpoint(context, { + action, + method = 'GET', + data, + params + }) { + + const headers = this.getHeaders(); + + return context.httpRequest({ + method, + url: `https://api.cloudflare.com/client/v4${action}`, + headers, + data, + params + }); + } + + async findIdsForIPs({ context, client, ips = [], account, list }) { + + const result = []; + for (let ipItem of ips) { + + try { + const { data } = await client.callEndpoint(context, { + method: 'GET', + action: `/accounts/${account}/rules/lists/${list}/items`, + params: { + per_page: 1, + search: ipItem.ip + } + }); + if (data?.result[0] && data?.result.length === 1) { + result.push({ ...data.result[0] }); + } + + } catch (err) { + context.log({ stage: `Invalid IP, IP ${ipItem} hasn't been found in the list ${list}` }); + } + } + + return result; + }; + + // WAF + /** + * https://developers.cloudflare.com/api/operations/getZoneRuleset + */ + listZoneRulesets(context) { + + return context.httpRequest({ + method: 'GET', + url: `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets`, + headers: this.getHeaders() + }).then(resp => resp.data.result); + } + + async getRules(context, rulesetId) { + + const response = await context.httpRequest({ + method: 'GET', + url: `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets/${rulesetId}`, + headers: this.getHeaders() + }); + + return response.data; + } + + async createRulesetAndBlockRule(context, ips) { + + const { data } = await context.httpRequest({ + method: 'POST', + url: `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets`, + headers: this.getHeaders(), + data: { + kind: 'zone', + phase: 'http_request_firewall_custom', + name: 'Http Request Firewall Custom Ruleset', + description: 'Created by Salt Security\'s Cloudflare Integration. Firewall rules of identified attackers will be added to this ruleset.', + rules: [this.getBlockRule(1, ips)] + } + }); + + return data; + } + + // https://developers.cloudflare.com/api/resources/rulesets/subresources/rules/ + createBlockRule(context, { rulesetId, rule }) { + const url = `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets/${rulesetId}/rules`; + const headers = this.getHeaders(); + return context.httpRequest.post(url, rule, { headers }).then(resp => resp.data); + } + + updateBlockRule(context, rulesetId, rule) { + const url = `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets/${rulesetId}/rules/${rule.id}`; + const headers = this.getHeaders(); + return context.httpRequest.patch(url, rule, { headers }).then(resp => resp.data); + } +}; diff --git a/src/appmixer/cloudflare/RulesIPsModel.js b/src/appmixer/cloudflareWAF/RulesIPsModel.js similarity index 100% rename from src/appmixer/cloudflare/RulesIPsModel.js rename to src/appmixer/cloudflareWAF/RulesIPsModel.js diff --git a/src/appmixer/cloudflare/waf/auth.js b/src/appmixer/cloudflareWAF/auth.js similarity index 100% rename from src/appmixer/cloudflare/waf/auth.js rename to src/appmixer/cloudflareWAF/auth.js diff --git a/src/appmixer/cloudflare/waf/bundle.json b/src/appmixer/cloudflareWAF/bundle.json similarity index 54% rename from src/appmixer/cloudflare/waf/bundle.json rename to src/appmixer/cloudflareWAF/bundle.json index 728d2ee7c..3c2410e1c 100644 --- a/src/appmixer/cloudflare/waf/bundle.json +++ b/src/appmixer/cloudflareWAF/bundle.json @@ -1,5 +1,5 @@ { - "name": "appmixer.cloudflare.waf", + "name": "appmixer.cloudflareWAF", "version": "1.0.0", "changelog": [] } diff --git a/src/appmixer/cloudflareWAF/config.js b/src/appmixer/cloudflareWAF/config.js new file mode 100644 index 000000000..2b8488140 --- /dev/null +++ b/src/appmixer/cloudflareWAF/config.js @@ -0,0 +1,20 @@ +'use strict'; + +module.exports = context => { + + return { + ipDeleteJob: { + + /** Default: every 10sec */ + schedule: context.config.ipDeleteJobSchedule || '*/10 * * * * *', + /** Lock TTL, default: 10sec */ + lockTTL: context.config.ipDeleteJobLockTTL || 10000 + }, + cleanup: { + /** Default: every 10min */ + schedule: context.config.cleanupJobSchedule || '0 */10 * * * *', + /** Lock TTL, default: 10sec */ + lockTTL: context.config.cleanupJobLockTTL || 10000 + } + }; +}; diff --git a/src/appmixer/cloudflareWAF/jobs.js b/src/appmixer/cloudflareWAF/jobs.js new file mode 100644 index 000000000..532ea776f --- /dev/null +++ b/src/appmixer/cloudflareWAF/jobs.js @@ -0,0 +1,63 @@ +const wafJobs = require('./jobs.waf'); + +module.exports = async (context) => { + + const RulesIPsModel = require('./RulesIPsModel')(context); + + const config = require('./config')(context); + + await context.scheduleJob('cloud-flare-waf-ips-delete-job', config.ipDeleteJob.schedule, async () => { + + try { + const lock = await context.job.lock('cloud-flare-lists-rule-block-ips-delete-job', { ttl: config.ipDeleteJob.lockTTL }); + await context.log('trace', '[CloudFlare WAF] rule delete job started.'); + + try { + await wafJobs.deleteExpireIps(context); + } finally { + lock.unlock(); + await context.log('trace', '[CloudFlare] rule delete job finished. Lock unlocked.'); + } + } catch (err) { + if (err.message !== 'locked') { + context.log('error', { + stage: '[CloudFlare] Error checking rules to delete', + error: err, + errorRaw: context.utils.Error.stringify(err) + }); + } + } + }); + + // Self-healing job to remove rules that have created>mtime. These rules are stuck in the system and should be removed. + await context.scheduleJob('cloud-flare-waf-ips-cleanup-job', config.cleanup.schedule, async () => { + + let lock = null; + try { + lock = await context.job.lock('cloud-flare-waf-ips-cleanup-job', { ttl: config.cleanup.lockTTL }); + await context.log('trace', '[CloudFlare WAF] IPs cleanup job started.'); + + // Delete IPs where the time difference between 'mtime' and 'removeAfter' exceeds the specified timespan, + // indicating that deletion attempts have persisted for the entire timespan. + const timespan = 30 * 60 * 1000; // 30 min + await context.db.collection(RulesIPsModel.collection).deleteMany({ + $expr: { $gt: [{ $subtract: ['$mtime', '$removeAfter'] }, timespan] } + }); + + if (expired.deletedCount) { + await context.log('info', { stage: `[CloudFlare WAF] Deleted ${expired.deletedCount} orphaned rules.` }); + } + } catch (err) { + if (err.message !== 'locked') { + context.log('error', { + stage: '[CloudFlare WAF] Error checking orphaned ips', + error: err, + errorRaw: context.utils.Error.stringify(err) + }); + } + } finally { + lock?.unlock(); + await context.log('trace', '[CloudFlare WAF] IPs cleanup job finished. Lock unlocked.'); + } + }); +}; diff --git a/src/appmixer/cloudflare/jobs.waf.js b/src/appmixer/cloudflareWAF/jobs.waf.js similarity index 100% rename from src/appmixer/cloudflare/jobs.waf.js rename to src/appmixer/cloudflareWAF/jobs.waf.js diff --git a/src/appmixer/cloudflareWAF/package-lock.json b/src/appmixer/cloudflareWAF/package-lock.json new file mode 100644 index 000000000..49189ba94 --- /dev/null +++ b/src/appmixer/cloudflareWAF/package-lock.json @@ -0,0 +1,23 @@ +{ + "name": "appmixer.cloudflare", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "appmixer.cloudflare", + "version": "1.0.0", + "dependencies": { + "ip-address": "^10.0.1" + } + }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "engines": { + "node": ">= 12" + } + } + } +} diff --git a/src/appmixer/cloudflareWAF/package.json b/src/appmixer/cloudflareWAF/package.json new file mode 100644 index 000000000..93228288f --- /dev/null +++ b/src/appmixer/cloudflareWAF/package.json @@ -0,0 +1,8 @@ +{ + "name": "appmixer.cloudflare", + "version": "1.0.0", + "author": "Salt", + "dependencies": { + "ip-address": "10.0.1" + } +} diff --git a/src/appmixer/cloudflareWAF/plugin.js b/src/appmixer/cloudflareWAF/plugin.js new file mode 100644 index 000000000..a96c44169 --- /dev/null +++ b/src/appmixer/cloudflareWAF/plugin.js @@ -0,0 +1,7 @@ +'use strict'; + +module.exports = async context => { + await require('./routes')(context); + await require('./jobs')(context); + context.log('info', '[CloudFlare] CloudFlare plugin successfully initialized.'); +}; diff --git a/src/appmixer/cloudflareWAF/quota.js b/src/appmixer/cloudflareWAF/quota.js new file mode 100644 index 000000000..0d1dd297e --- /dev/null +++ b/src/appmixer/cloudflareWAF/quota.js @@ -0,0 +1,15 @@ +'use strict'; + +module.exports = { + + rules: [ + // CloudFlare quota is 1200 per 5 minutes + { + limit: 80, // components call multiple api call - add/remove + get status + (optional: get id) + window: 1000 * 60, + throttling: 'window-sliding', + queueing: 'fifo', + resource: 'requests' + } + ] +}; diff --git a/src/appmixer/cloudflareWAF/routes.js b/src/appmixer/cloudflareWAF/routes.js new file mode 100644 index 000000000..20ea3bcbc --- /dev/null +++ b/src/appmixer/cloudflareWAF/routes.js @@ -0,0 +1,53 @@ +'use strict'; + +module.exports = (context, options) => { + + const IPListModel = require('./IPListModel')(context); + const RulesIPsModel = require('./RulesIPsModel')(context); + + context.http.router.register({ + method: 'POST', path: '/ip-list', options: { + handler: async req => { + + const items = req.payload.items; + const operations = items.map(item => ({ + updateOne: { + filter: { id: item.id }, update: { $set: item }, upsert: true + } + })); + return await (context.db.collection(IPListModel.collection)).bulkWrite(operations); + } + } + }); + + context.http.router.register({ + method: 'POST', path: '/block-ip-rules', options: { + handler: async req => { + + const items = req.payload.items; + const operations = items.map(item => ({ + updateOne: { + filter: { ip: item.ip, zoneId: item.zoneId }, update: { $set: item }, upsert: true + } + })); + return await (context.db.collection(RulesIPsModel.collection)).bulkWrite(operations); + } + } + }); + + context.http.router.register({ + method: 'GET', path: '/ip-list', options: { + handler: async req => { + return IPListModel.find({}); + } + } + }); + + context.http.router.register({ + method: 'GET', path: '/block-ip-rules', options: { + handler: async req => { + return RulesIPsModel.find({}); + } + } + }); +}; diff --git a/src/appmixer/cloudflare/waf/module.json b/src/appmixer/cloudflareWAF/service.json similarity index 98% rename from src/appmixer/cloudflare/waf/module.json rename to src/appmixer/cloudflareWAF/service.json index 0c1905033..9647447a3 100644 --- a/src/appmixer/cloudflare/waf/module.json +++ b/src/appmixer/cloudflareWAF/service.json @@ -1,7 +1,8 @@ { - "name": "appmixer.cloudflare.waf", + "name": "appmixer.cloudflare", "label": "CloudFlare WAF", "category": "applications", "description": "CloudFlare WAF integrations allows you to actively block attacker IP by using CloudFlare WAF infrastructure.", - "icon": "data:image/png;base64,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" + "icon": "data:image/png;base64,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", + "version": "1.0.5" } diff --git a/src/appmixer/cloudflare/waf/CreateCustomRules/CreateCustomRules.js b/src/appmixer/cloudflareWAF/waf/CreateCustomRules/CreateCustomRules.js similarity index 100% rename from src/appmixer/cloudflare/waf/CreateCustomRules/CreateCustomRules.js rename to src/appmixer/cloudflareWAF/waf/CreateCustomRules/CreateCustomRules.js diff --git a/src/appmixer/cloudflare/waf/CreateCustomRules/component.json b/src/appmixer/cloudflareWAF/waf/CreateCustomRules/component.json similarity index 97% rename from src/appmixer/cloudflare/waf/CreateCustomRules/component.json rename to src/appmixer/cloudflareWAF/waf/CreateCustomRules/component.json index 83fb8a3b3..683bf5ac1 100644 --- a/src/appmixer/cloudflare/waf/CreateCustomRules/component.json +++ b/src/appmixer/cloudflareWAF/waf/CreateCustomRules/component.json @@ -1,6 +1,6 @@ { - "name": "appmixer.cloudflare.waf.CreateCustomRules", - "label": "cloudflare CreateCustomRules", + "name": "appmixer.cloudflareWAF.waf.CreateCustomRules", + "label": "CreateCustomRules", "description": "Create custom rules in waf", "private": false, "version": "1.0.5", @@ -8,7 +8,7 @@ "service": "1.0.5" }, "auth": { - "service": "appmixer:cloudflare:waf" + "service": "appmixer:cloudflareWAF" }, "inPorts": [ { diff --git a/src/appmixer/cloudflare/waf/CreateCustomRules/create-custom-rules-handler.js b/src/appmixer/cloudflareWAF/waf/CreateCustomRules/create-custom-rules-handler.js similarity index 100% rename from src/appmixer/cloudflare/waf/CreateCustomRules/create-custom-rules-handler.js rename to src/appmixer/cloudflareWAF/waf/CreateCustomRules/create-custom-rules-handler.js diff --git a/src/appmixer/cloudflare/waf/lib.js b/src/appmixer/cloudflareWAF/waf/lib.js similarity index 100% rename from src/appmixer/cloudflare/waf/lib.js rename to src/appmixer/cloudflareWAF/waf/lib.js From 6f07f5c9f340abbdb0b0646437e98279b6689c1a Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Fri, 10 Jan 2025 12:08:53 +0100 Subject: [PATCH 11/21] split to new connectors --- src/appmixer/{cloudflareLists => cloudflare}/CloudflareAPI.js | 0 src/appmixer/{cloudflareLists => cloudflare}/IPListModel.js | 0 src/appmixer/{cloudflareLists/lists => cloudflare}/auth.js | 2 +- src/appmixer/{cloudflareLists => cloudflare}/bundle.json | 2 +- src/appmixer/{cloudflareLists => cloudflare}/config.js | 0 src/appmixer/{cloudflareLists => cloudflare}/jobs.js | 0 src/appmixer/{cloudflareLists => cloudflare}/jobs.lists.js | 0 .../lists/AddToIPList/AddToIPList.js | 0 .../lists/AddToIPList/component.json | 4 ++-- .../lists/RemoveFromIPList/RemoveFromIPList.js | 0 .../lists/RemoveFromIPList/component.json | 4 ++-- src/appmixer/{cloudflareLists => cloudflare}/lists/lib.js | 0 .../{cloudflareLists => cloudflare}/package-lock.json | 0 src/appmixer/{cloudflareLists => cloudflare}/package.json | 0 src/appmixer/{cloudflareLists => cloudflare}/plugin.js | 0 src/appmixer/{cloudflareLists => cloudflare}/quota.js | 0 src/appmixer/{cloudflareLists => cloudflare}/routes.js | 0 src/appmixer/{cloudflareLists => cloudflare}/service.json | 2 +- 18 files changed, 7 insertions(+), 7 deletions(-) rename src/appmixer/{cloudflareLists => cloudflare}/CloudflareAPI.js (100%) rename src/appmixer/{cloudflareLists => cloudflare}/IPListModel.js (100%) rename src/appmixer/{cloudflareLists/lists => cloudflare}/auth.js (94%) rename src/appmixer/{cloudflareLists => cloudflare}/bundle.json (53%) rename src/appmixer/{cloudflareLists => cloudflare}/config.js (100%) rename src/appmixer/{cloudflareLists => cloudflare}/jobs.js (100%) rename src/appmixer/{cloudflareLists => cloudflare}/jobs.lists.js (100%) rename src/appmixer/{cloudflareLists => cloudflare}/lists/AddToIPList/AddToIPList.js (100%) rename src/appmixer/{cloudflareLists => cloudflare}/lists/AddToIPList/component.json (98%) rename src/appmixer/{cloudflareLists => cloudflare}/lists/RemoveFromIPList/RemoveFromIPList.js (100%) rename src/appmixer/{cloudflareLists => cloudflare}/lists/RemoveFromIPList/component.json (98%) rename src/appmixer/{cloudflareLists => cloudflare}/lists/lib.js (100%) rename src/appmixer/{cloudflareLists => cloudflare}/package-lock.json (100%) rename src/appmixer/{cloudflareLists => cloudflare}/package.json (100%) rename src/appmixer/{cloudflareLists => cloudflare}/plugin.js (100%) rename src/appmixer/{cloudflareLists => cloudflare}/quota.js (100%) rename src/appmixer/{cloudflareLists => cloudflare}/routes.js (100%) rename src/appmixer/{cloudflareLists => cloudflare}/service.json (99%) diff --git a/src/appmixer/cloudflareLists/CloudflareAPI.js b/src/appmixer/cloudflare/CloudflareAPI.js similarity index 100% rename from src/appmixer/cloudflareLists/CloudflareAPI.js rename to src/appmixer/cloudflare/CloudflareAPI.js diff --git a/src/appmixer/cloudflareLists/IPListModel.js b/src/appmixer/cloudflare/IPListModel.js similarity index 100% rename from src/appmixer/cloudflareLists/IPListModel.js rename to src/appmixer/cloudflare/IPListModel.js diff --git a/src/appmixer/cloudflareLists/lists/auth.js b/src/appmixer/cloudflare/auth.js similarity index 94% rename from src/appmixer/cloudflareLists/lists/auth.js rename to src/appmixer/cloudflare/auth.js index 4f873a46f..f1aa1a03a 100644 --- a/src/appmixer/cloudflareLists/lists/auth.js +++ b/src/appmixer/cloudflare/auth.js @@ -1,6 +1,6 @@ 'use strict'; -const CloudflareAPI = require('../CloudflareAPI'); +const CloudflareAPI = require('./CloudflareAPI'); module.exports = { diff --git a/src/appmixer/cloudflareLists/bundle.json b/src/appmixer/cloudflare/bundle.json similarity index 53% rename from src/appmixer/cloudflareLists/bundle.json rename to src/appmixer/cloudflare/bundle.json index 5c272ce3b..87e04c876 100644 --- a/src/appmixer/cloudflareLists/bundle.json +++ b/src/appmixer/cloudflare/bundle.json @@ -1,5 +1,5 @@ { - "name": "appmixer.cloudflare.lists", + "name": "appmixer.cloudflare", "version": "1.0.0", "changelog": [] } diff --git a/src/appmixer/cloudflareLists/config.js b/src/appmixer/cloudflare/config.js similarity index 100% rename from src/appmixer/cloudflareLists/config.js rename to src/appmixer/cloudflare/config.js diff --git a/src/appmixer/cloudflareLists/jobs.js b/src/appmixer/cloudflare/jobs.js similarity index 100% rename from src/appmixer/cloudflareLists/jobs.js rename to src/appmixer/cloudflare/jobs.js diff --git a/src/appmixer/cloudflareLists/jobs.lists.js b/src/appmixer/cloudflare/jobs.lists.js similarity index 100% rename from src/appmixer/cloudflareLists/jobs.lists.js rename to src/appmixer/cloudflare/jobs.lists.js diff --git a/src/appmixer/cloudflareLists/lists/AddToIPList/AddToIPList.js b/src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js similarity index 100% rename from src/appmixer/cloudflareLists/lists/AddToIPList/AddToIPList.js rename to src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js diff --git a/src/appmixer/cloudflareLists/lists/AddToIPList/component.json b/src/appmixer/cloudflare/lists/AddToIPList/component.json similarity index 98% rename from src/appmixer/cloudflareLists/lists/AddToIPList/component.json rename to src/appmixer/cloudflare/lists/AddToIPList/component.json index db4ee9d20..e5efe8fcb 100644 --- a/src/appmixer/cloudflareLists/lists/AddToIPList/component.json +++ b/src/appmixer/cloudflare/lists/AddToIPList/component.json @@ -1,10 +1,10 @@ { - "name": "appmixer.cloudflareLists.lists.AddToIPList", + "name": "appmixer.cloudflare.lists.AddToIPList", "label": "Add IP to IP List", "description": "Appends new items to the IP list. Maximum capacity if the list is 1000 items.", "private": false, "auth": { - "service": "appmixer:cloudflareLists" + "service": "appmixer:cloudflare" }, "quota": { "manager": "appmixer:cloudflare", diff --git a/src/appmixer/cloudflareLists/lists/RemoveFromIPList/RemoveFromIPList.js b/src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js similarity index 100% rename from src/appmixer/cloudflareLists/lists/RemoveFromIPList/RemoveFromIPList.js rename to src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js diff --git a/src/appmixer/cloudflareLists/lists/RemoveFromIPList/component.json b/src/appmixer/cloudflare/lists/RemoveFromIPList/component.json similarity index 98% rename from src/appmixer/cloudflareLists/lists/RemoveFromIPList/component.json rename to src/appmixer/cloudflare/lists/RemoveFromIPList/component.json index 3e19d912c..4051592a2 100644 --- a/src/appmixer/cloudflareLists/lists/RemoveFromIPList/component.json +++ b/src/appmixer/cloudflare/lists/RemoveFromIPList/component.json @@ -1,10 +1,10 @@ { - "name": "appmixer.cloudflareLists.lists.RemoveFromIPList", + "name": "appmixer.cloudflare.lists.RemoveFromIPList", "label": "Remove IPs From IP List", "description": "Removes IPs from the IP list.", "private": false, "auth": { - "service": "appmixer:cloudflareLists" + "service": "appmixer:cloudflare" }, "quota": { "manager": "appmixer:cloudflare", diff --git a/src/appmixer/cloudflareLists/lists/lib.js b/src/appmixer/cloudflare/lists/lib.js similarity index 100% rename from src/appmixer/cloudflareLists/lists/lib.js rename to src/appmixer/cloudflare/lists/lib.js diff --git a/src/appmixer/cloudflareLists/package-lock.json b/src/appmixer/cloudflare/package-lock.json similarity index 100% rename from src/appmixer/cloudflareLists/package-lock.json rename to src/appmixer/cloudflare/package-lock.json diff --git a/src/appmixer/cloudflareLists/package.json b/src/appmixer/cloudflare/package.json similarity index 100% rename from src/appmixer/cloudflareLists/package.json rename to src/appmixer/cloudflare/package.json diff --git a/src/appmixer/cloudflareLists/plugin.js b/src/appmixer/cloudflare/plugin.js similarity index 100% rename from src/appmixer/cloudflareLists/plugin.js rename to src/appmixer/cloudflare/plugin.js diff --git a/src/appmixer/cloudflareLists/quota.js b/src/appmixer/cloudflare/quota.js similarity index 100% rename from src/appmixer/cloudflareLists/quota.js rename to src/appmixer/cloudflare/quota.js diff --git a/src/appmixer/cloudflareLists/routes.js b/src/appmixer/cloudflare/routes.js similarity index 100% rename from src/appmixer/cloudflareLists/routes.js rename to src/appmixer/cloudflare/routes.js diff --git a/src/appmixer/cloudflareLists/service.json b/src/appmixer/cloudflare/service.json similarity index 99% rename from src/appmixer/cloudflareLists/service.json rename to src/appmixer/cloudflare/service.json index 8571af62a..a09d4579f 100644 --- a/src/appmixer/cloudflareLists/service.json +++ b/src/appmixer/cloudflare/service.json @@ -1,5 +1,5 @@ { - "name": "appmixer.cloudflareLists", + "name": "appmixer.cloudflare", "label": "CloudFlare Lists", "category": "applications", "description": "CloudFlare List integrations allows you to work with IP lists.", From 89e91d3409ee39ad635573f8e3f456c6266c6899 Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Fri, 10 Jan 2025 12:52:58 +0100 Subject: [PATCH 12/21] update --- src/appmixer/cloudflare/jobs.js | 3 --- src/appmixer/cloudflare/routes.js | 24 ------------------- src/appmixer/cloudflareWAF/auth.js | 2 +- src/appmixer/cloudflareWAF/jobs.js | 4 ++-- src/appmixer/cloudflareWAF/plugin.js | 2 +- src/appmixer/cloudflareWAF/routes.js | 24 ------------------- src/appmixer/cloudflareWAF/service.json | 2 +- .../waf/CreateCustomRules/component.json | 2 +- 8 files changed, 6 insertions(+), 57 deletions(-) diff --git a/src/appmixer/cloudflare/jobs.js b/src/appmixer/cloudflare/jobs.js index 7e3a0f2b3..f40762f0f 100644 --- a/src/appmixer/cloudflare/jobs.js +++ b/src/appmixer/cloudflare/jobs.js @@ -43,9 +43,6 @@ module.exports = async (context) => { const expired = await context.db.collection(IPListModel.collection).deleteMany({ $expr: { $gt: [{ $subtract: ['$mtime', '$removeAfter'] }, timespan] } }); - await context.db.collection(RulesIPsModel.collection).deleteMany({ - $expr: { $gt: [{ $subtract: ['$mtime', '$removeAfter'] }, timespan] } - }); if (expired.deletedCount) { await context.log('info', { stage: `[CloudFlare] Deleted ${expired.deletedCount} orphaned rules.` }); diff --git a/src/appmixer/cloudflare/routes.js b/src/appmixer/cloudflare/routes.js index 20ea3bcbc..53a1c60d0 100644 --- a/src/appmixer/cloudflare/routes.js +++ b/src/appmixer/cloudflare/routes.js @@ -3,7 +3,6 @@ module.exports = (context, options) => { const IPListModel = require('./IPListModel')(context); - const RulesIPsModel = require('./RulesIPsModel')(context); context.http.router.register({ method: 'POST', path: '/ip-list', options: { @@ -20,21 +19,6 @@ module.exports = (context, options) => { } }); - context.http.router.register({ - method: 'POST', path: '/block-ip-rules', options: { - handler: async req => { - - const items = req.payload.items; - const operations = items.map(item => ({ - updateOne: { - filter: { ip: item.ip, zoneId: item.zoneId }, update: { $set: item }, upsert: true - } - })); - return await (context.db.collection(RulesIPsModel.collection)).bulkWrite(operations); - } - } - }); - context.http.router.register({ method: 'GET', path: '/ip-list', options: { handler: async req => { @@ -42,12 +26,4 @@ module.exports = (context, options) => { } } }); - - context.http.router.register({ - method: 'GET', path: '/block-ip-rules', options: { - handler: async req => { - return RulesIPsModel.find({}); - } - } - }); }; diff --git a/src/appmixer/cloudflareWAF/auth.js b/src/appmixer/cloudflareWAF/auth.js index a9db7a581..242fd94bf 100644 --- a/src/appmixer/cloudflareWAF/auth.js +++ b/src/appmixer/cloudflareWAF/auth.js @@ -1,6 +1,6 @@ 'use strict'; -const CloudflareAPI = require('../CloudflareAPI'); +const CloudflareAPI = require('./CloudflareAPI'); module.exports = { diff --git a/src/appmixer/cloudflareWAF/jobs.js b/src/appmixer/cloudflareWAF/jobs.js index 532ea776f..4a30eabe4 100644 --- a/src/appmixer/cloudflareWAF/jobs.js +++ b/src/appmixer/cloudflareWAF/jobs.js @@ -9,14 +9,14 @@ module.exports = async (context) => { await context.scheduleJob('cloud-flare-waf-ips-delete-job', config.ipDeleteJob.schedule, async () => { try { - const lock = await context.job.lock('cloud-flare-lists-rule-block-ips-delete-job', { ttl: config.ipDeleteJob.lockTTL }); + const lock = await context.job.lock('cloud-flare-waf-rule-block-ips-delete-job', { ttl: config.ipDeleteJob.lockTTL }); await context.log('trace', '[CloudFlare WAF] rule delete job started.'); try { await wafJobs.deleteExpireIps(context); } finally { lock.unlock(); - await context.log('trace', '[CloudFlare] rule delete job finished. Lock unlocked.'); + await context.log('trace', '[CloudFlare WAF] rule delete job finished. Lock unlocked.'); } } catch (err) { if (err.message !== 'locked') { diff --git a/src/appmixer/cloudflareWAF/plugin.js b/src/appmixer/cloudflareWAF/plugin.js index a96c44169..bbc1143f0 100644 --- a/src/appmixer/cloudflareWAF/plugin.js +++ b/src/appmixer/cloudflareWAF/plugin.js @@ -3,5 +3,5 @@ module.exports = async context => { await require('./routes')(context); await require('./jobs')(context); - context.log('info', '[CloudFlare] CloudFlare plugin successfully initialized.'); + context.log('info', '[CloudFlare WAF] CloudFlare WAF plugin successfully initialized.'); }; diff --git a/src/appmixer/cloudflareWAF/routes.js b/src/appmixer/cloudflareWAF/routes.js index 20ea3bcbc..00a35aa3f 100644 --- a/src/appmixer/cloudflareWAF/routes.js +++ b/src/appmixer/cloudflareWAF/routes.js @@ -2,24 +2,8 @@ module.exports = (context, options) => { - const IPListModel = require('./IPListModel')(context); const RulesIPsModel = require('./RulesIPsModel')(context); - context.http.router.register({ - method: 'POST', path: '/ip-list', options: { - handler: async req => { - - const items = req.payload.items; - const operations = items.map(item => ({ - updateOne: { - filter: { id: item.id }, update: { $set: item }, upsert: true - } - })); - return await (context.db.collection(IPListModel.collection)).bulkWrite(operations); - } - } - }); - context.http.router.register({ method: 'POST', path: '/block-ip-rules', options: { handler: async req => { @@ -35,14 +19,6 @@ module.exports = (context, options) => { } }); - context.http.router.register({ - method: 'GET', path: '/ip-list', options: { - handler: async req => { - return IPListModel.find({}); - } - } - }); - context.http.router.register({ method: 'GET', path: '/block-ip-rules', options: { handler: async req => { diff --git a/src/appmixer/cloudflareWAF/service.json b/src/appmixer/cloudflareWAF/service.json index 9647447a3..f48ebe660 100644 --- a/src/appmixer/cloudflareWAF/service.json +++ b/src/appmixer/cloudflareWAF/service.json @@ -1,5 +1,5 @@ { - "name": "appmixer.cloudflare", + "name": "appmixer.cloudflareWAF", "label": "CloudFlare WAF", "category": "applications", "description": "CloudFlare WAF integrations allows you to actively block attacker IP by using CloudFlare WAF infrastructure.", diff --git a/src/appmixer/cloudflareWAF/waf/CreateCustomRules/component.json b/src/appmixer/cloudflareWAF/waf/CreateCustomRules/component.json index 683bf5ac1..b28c4e3d6 100644 --- a/src/appmixer/cloudflareWAF/waf/CreateCustomRules/component.json +++ b/src/appmixer/cloudflareWAF/waf/CreateCustomRules/component.json @@ -1,6 +1,6 @@ { "name": "appmixer.cloudflareWAF.waf.CreateCustomRules", - "label": "CreateCustomRules", + "label": "Block IPs", "description": "Create custom rules in waf", "private": false, "version": "1.0.5", From 14de2568a9c9a52023dd2c03c51e652d81f0b778 Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Fri, 10 Jan 2025 13:27:14 +0100 Subject: [PATCH 13/21] update --- src/appmixer/cloudflare/CloudflareAPI.js | 1 - src/appmixer/cloudflareWAF/CloudflareAPI.js | 26 ------------------ src/appmixer/cloudflareWAF/config.js | 4 +-- src/appmixer/cloudflareWAF/jobs.js | 14 +++++----- src/appmixer/cloudflareWAF/jobs.waf.js | 27 +++++++------------ .../CreateCustomRules/CreateCustomRules.js | 2 +- 6 files changed, 19 insertions(+), 55 deletions(-) diff --git a/src/appmixer/cloudflare/CloudflareAPI.js b/src/appmixer/cloudflare/CloudflareAPI.js index c6800493f..c62d06050 100644 --- a/src/appmixer/cloudflare/CloudflareAPI.js +++ b/src/appmixer/cloudflare/CloudflareAPI.js @@ -89,7 +89,6 @@ module.exports = class CloudflareAPI { return result; }; - // WAF /** * https://developers.cloudflare.com/api/operations/getZoneRuleset */ diff --git a/src/appmixer/cloudflareWAF/CloudflareAPI.js b/src/appmixer/cloudflareWAF/CloudflareAPI.js index c6800493f..4a8c540ff 100644 --- a/src/appmixer/cloudflareWAF/CloudflareAPI.js +++ b/src/appmixer/cloudflareWAF/CloudflareAPI.js @@ -63,32 +63,6 @@ module.exports = class CloudflareAPI { }); } - async findIdsForIPs({ context, client, ips = [], account, list }) { - - const result = []; - for (let ipItem of ips) { - - try { - const { data } = await client.callEndpoint(context, { - method: 'GET', - action: `/accounts/${account}/rules/lists/${list}/items`, - params: { - per_page: 1, - search: ipItem.ip - } - }); - if (data?.result[0] && data?.result.length === 1) { - result.push({ ...data.result[0] }); - } - - } catch (err) { - context.log({ stage: `Invalid IP, IP ${ipItem} hasn't been found in the list ${list}` }); - } - } - - return result; - }; - // WAF /** * https://developers.cloudflare.com/api/operations/getZoneRuleset diff --git a/src/appmixer/cloudflareWAF/config.js b/src/appmixer/cloudflareWAF/config.js index 2b8488140..5dbf26cd9 100644 --- a/src/appmixer/cloudflareWAF/config.js +++ b/src/appmixer/cloudflareWAF/config.js @@ -11,8 +11,8 @@ module.exports = context => { lockTTL: context.config.ipDeleteJobLockTTL || 10000 }, cleanup: { - /** Default: every 10min */ - schedule: context.config.cleanupJobSchedule || '0 */10 * * * *', + /** Default: every hour */ + schedule: context.config.cleanupJobSchedule || '0 0 * * * *', /** Lock TTL, default: 10sec */ lockTTL: context.config.cleanupJobLockTTL || 10000 } diff --git a/src/appmixer/cloudflareWAF/jobs.js b/src/appmixer/cloudflareWAF/jobs.js index 4a30eabe4..2c0ab60d8 100644 --- a/src/appmixer/cloudflareWAF/jobs.js +++ b/src/appmixer/cloudflareWAF/jobs.js @@ -21,7 +21,7 @@ module.exports = async (context) => { } catch (err) { if (err.message !== 'locked') { context.log('error', { - stage: '[CloudFlare] Error checking rules to delete', + stage: '[CloudFlare WAF] Error checking rules to delete', error: err, errorRaw: context.utils.Error.stringify(err) }); @@ -35,13 +35,12 @@ module.exports = async (context) => { let lock = null; try { lock = await context.job.lock('cloud-flare-waf-ips-cleanup-job', { ttl: config.cleanup.lockTTL }); - await context.log('trace', '[CloudFlare WAF] IPs cleanup job started.'); - // Delete IPs where the time difference between 'mtime' and 'removeAfter' exceeds the specified timespan, - // indicating that deletion attempts have persisted for the entire timespan. + // Delete IPs where the time difference between current date and 'removeAfter' exceeds the specified timespan, + // indicating that IPs cannot be deleted from the rules. const timespan = 30 * 60 * 1000; // 30 min - await context.db.collection(RulesIPsModel.collection).deleteMany({ - $expr: { $gt: [{ $subtract: ['$mtime', '$removeAfter'] }, timespan] } + const expired = await context.db.collection(RulesIPsModel.collection).deleteMany({ + removeAfter: { $lt: new Date(Date.now() - timespan).valueOf() } }); if (expired.deletedCount) { @@ -50,14 +49,13 @@ module.exports = async (context) => { } catch (err) { if (err.message !== 'locked') { context.log('error', { - stage: '[CloudFlare WAF] Error checking orphaned ips', + stage: '[CloudFlare WAF] Error checking orphaned ips', error: err, errorRaw: context.utils.Error.stringify(err) }); } } finally { lock?.unlock(); - await context.log('trace', '[CloudFlare WAF] IPs cleanup job finished. Lock unlocked.'); } }); }; diff --git a/src/appmixer/cloudflareWAF/jobs.waf.js b/src/appmixer/cloudflareWAF/jobs.waf.js index 7c356f0a7..1fae9a8de 100644 --- a/src/appmixer/cloudflareWAF/jobs.waf.js +++ b/src/appmixer/cloudflareWAF/jobs.waf.js @@ -5,26 +5,19 @@ const getModel = (context) => require('./RulesIPsModel')(context); const deleteExpireIps = async function(context) { - try { + const expired = await getExpiredItems(context); - const expired = await getExpiredItems(context); - if (expired.length) { - await context.log('info', { type: '[Cloudflare WAF] expired.', data: sanitizeItems(expired) }); - } - - const rulesToUpdate = await retrieveRulesForUpdate(context, expired); - if (rulesToUpdate.length) { - await context.log('info', { type: '[Cloudflare WAF] rulesTo update.', data: sanitizeItems(rulesToUpdate) }); - } - - const dbItemsToDelete = await updateRules(context, rulesToUpdate); - await deleteDBItems(context, dbItemsToDelete); + if (expired.length) { + await context.log('info', { type: '[Cloudflare WAF] expired.', data: sanitizeItems(expired) }); + } - } catch (e) { - await context.log('error', { - type: '[Cloudflare WAF] Unexpected error', error: context.utils.Error.stringify(e) - }); + const rulesToUpdate = await retrieveRulesForUpdate(context, expired); + if (rulesToUpdate.length) { + await context.log('info', { type: '[Cloudflare WAF] rules to update.', data: sanitizeItems(rulesToUpdate) }); } + + const dbItemsToDelete = await updateRules(context, rulesToUpdate); + await deleteDBItems(context, dbItemsToDelete); }; const retrieveRulesForUpdate = async function(context, expired = []) { diff --git a/src/appmixer/cloudflareWAF/waf/CreateCustomRules/CreateCustomRules.js b/src/appmixer/cloudflareWAF/waf/CreateCustomRules/CreateCustomRules.js index e1660285a..e1116b5d0 100644 --- a/src/appmixer/cloudflareWAF/waf/CreateCustomRules/CreateCustomRules.js +++ b/src/appmixer/cloudflareWAF/waf/CreateCustomRules/CreateCustomRules.js @@ -70,7 +70,7 @@ module.exports = { }); await context.callAppmixer({ - endPoint: '/plugins/appmixer/cloudflare/block-ip-rules', + endPoint: '/plugins/appmixer/cloudflareWAF/block-ip-rules', method: 'POST', body: { items: dbItems } }); From f3d8a259c069e6a14d619f0785f38c4bfd18c760 Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Mon, 13 Jan 2025 10:00:16 +0100 Subject: [PATCH 14/21] update --- src/appmixer/cloudflare/CloudflareAPI.js | 26 ----- src/appmixer/cloudflare/jobs.js | 14 +-- src/appmixer/cloudflare/jobs.lists.js | 4 +- .../lists/AddToIPList/AddToIPList.js | 21 ++-- .../RemoveFromIPList/RemoveFromIPList.js | 16 ++- src/appmixer/cloudflare/lists/lib.js | 100 +++++++++++++----- src/appmixer/cloudflare/plugin.js | 2 +- 7 files changed, 102 insertions(+), 81 deletions(-) diff --git a/src/appmixer/cloudflare/CloudflareAPI.js b/src/appmixer/cloudflare/CloudflareAPI.js index c62d06050..42130b33b 100644 --- a/src/appmixer/cloudflare/CloudflareAPI.js +++ b/src/appmixer/cloudflare/CloudflareAPI.js @@ -63,32 +63,6 @@ module.exports = class CloudflareAPI { }); } - async findIdsForIPs({ context, client, ips = [], account, list }) { - - const result = []; - for (let ipItem of ips) { - - try { - const { data } = await client.callEndpoint(context, { - method: 'GET', - action: `/accounts/${account}/rules/lists/${list}/items`, - params: { - per_page: 1, - search: ipItem.ip - } - }); - if (data?.result[0] && data?.result.length === 1) { - result.push({ ...data.result[0] }); - } - - } catch (err) { - context.log({ stage: `Invalid IP, IP ${ipItem} hasn't been found in the list ${list}` }); - } - } - - return result; - }; - /** * https://developers.cloudflare.com/api/operations/getZoneRuleset */ diff --git a/src/appmixer/cloudflare/jobs.js b/src/appmixer/cloudflare/jobs.js index f40762f0f..864b62520 100644 --- a/src/appmixer/cloudflare/jobs.js +++ b/src/appmixer/cloudflare/jobs.js @@ -10,18 +10,18 @@ module.exports = async (context) => { try { const lock = await context.job.lock('cloud-flare-lists-rule-block-ips-delete-job', { ttl: config.ipDeleteJob.lockTTL }); - await context.log('trace', '[CloudFlare] rule delete job started.'); + await context.log('trace', '[CloudFlare Lists] rule delete job started.'); try { await listsJobs.deleteExpireIpsFromList(context); } finally { lock.unlock(); - await context.log('trace', '[CloudFlare] rule delete job finished. Lock unlocked.'); + await context.log('trace', '[CloudFlare Lists] rule delete job finished. Lock unlocked.'); } } catch (err) { if (err.message !== 'locked') { context.log('error', { - stage: '[CloudFlare] Error checking rules to delete', + stage: '[CloudFlare Lists] Error checking rules to delete', error: err, errorRaw: context.utils.Error.stringify(err) }); @@ -35,7 +35,7 @@ module.exports = async (context) => { let lock = null; try { lock = await context.job.lock('cloud-flare-lists-ips-cleanup-job', { ttl: config.cleanup.lockTTL }); - await context.log('trace', '[CloudFlare] IPs cleanup job started.'); + await context.log('trace', '[CloudFlare Lists] IPs cleanup job started.'); // Delete IPs where the time difference between 'mtime' and 'removeAfter' exceeds the specified timespan, // indicating that deletion attempts have persisted for the entire timespan. @@ -45,19 +45,19 @@ module.exports = async (context) => { }); if (expired.deletedCount) { - await context.log('info', { stage: `[CloudFlare] Deleted ${expired.deletedCount} orphaned rules.` }); + await context.log('info', { stage: `[CloudFlare Lists] Deleted ${expired.deletedCount} orphaned rules.` }); } } catch (err) { if (err.message !== 'locked') { context.log('error', { - stage: '[CloudFlare] Error checking orphaned ips', + stage: '[CloudFlare Lists] Error checking orphaned ips', error: err, errorRaw: context.utils.Error.stringify(err) }); } } finally { lock?.unlock(); - await context.log('trace', '[CloudFlare] IPs cleanup job finished. Lock unlocked.'); + await context.log('trace', '[CloudFlare Lists] IPs cleanup job finished. Lock unlocked.'); } }); }; diff --git a/src/appmixer/cloudflare/jobs.lists.js b/src/appmixer/cloudflare/jobs.lists.js index 87e549101..758b98b2e 100644 --- a/src/appmixer/cloudflare/jobs.lists.js +++ b/src/appmixer/cloudflare/jobs.lists.js @@ -12,7 +12,7 @@ const deleteExpireIpsFromList = async function(context) { const { email, apiKey, account, list } = chunk.auth; - context.log('info', { stage: '[CloudFlare] removing ', ips: chunk.ips, list, account }); + context.log('info', { stage: '[CloudFlare Lists] removing expired IPs', ips: chunk.ips, list, account }); const client = new CloudflareAPI({ email, apiKey }); // https://developers.cloudflare.com/api/operations/lists-delete-list-items @@ -41,7 +41,7 @@ const deleteExpireIpsFromList = async function(context) { const deleted = await context.db.collection(getModel(context).collection) .deleteMany({ id: { $in: itemsToDelete.ids.map(item => item.id) } }); await context.log('info', { - stage: `[CloudFlare] Deleted total ${deleted.deletedCount} ips.`, + stage: `[CloudFlare Lists] Deleted total ${deleted.deletedCount} ips.`, lists: itemsToDelete.lists, itemIds: itemsToDelete.ids }); diff --git a/src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js b/src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js index 3bc0bf376..ce3f5056a 100644 --- a/src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js +++ b/src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js @@ -1,12 +1,13 @@ -const CloudflareAPI = require('../../CloudflareAPI'); +'use strict'; + const lib = require('../lib'); let attempts = 0; -const getStatus = async function(context, client, { account, id }) { +const getStatus = async function(context, { account, id }) { context.log({ stage: 'GETTING STATUS', id, attempts }); // https://developers.cloudflare.com/api/operations/lists-get-bulk-operation-status - const { data } = await client.callEndpoint(context, { + const { data } = await lib.callEndpoint(context, { action: `/accounts/${account}/rules/lists/bulk_operations/${id}` }); @@ -19,7 +20,7 @@ const getStatus = async function(context, client, { account, id }) { attempts++; if (attempts <= 5) { await new Promise(r => setTimeout(r, 2000)); - return await getStatus(context, client, { account, id }); + return await getStatus(context, { account, id }); } else { throw new context.CancelError(data.errors); } @@ -28,7 +29,6 @@ const getStatus = async function(context, client, { account, id }) { return data.result; }; - module.exports = { async receive(context) { @@ -36,10 +36,8 @@ module.exports = { const { accountsFetch, listFetch } = context.properties; const { account, list, ips, ttl } = context.messages.in.content; - const client = new CloudflareAPI({ email, apiKey }); - if (accountsFetch || listFetch) { - return await lib.fetchInputs(context, client, { account, listFetch, accountsFetch }); + return await lib.fetchInputs(context, { account, listFetch, accountsFetch }); } const ipsList = ips.AND; @@ -49,13 +47,14 @@ module.exports = { } // https://developers.cloudflare.com/api/operations/lists-create-list-items - const { data } = await client.callEndpoint(context, { + const { data } = await lib.callEndpoint(context, { method: 'POST', action: `/accounts/${account}/rules/lists/${list}/items`, data: ipsList }); - const status = await getStatus(context, client, { id: data.result.operation_id, account }); + + const status = await getStatus(context, { id: data.result.operation_id, account }); if (status.error) { throw new context.CancelError(status.error); @@ -63,7 +62,7 @@ module.exports = { if (ttl) { const removeAfter = new Date().getTime() + ttl * 1000; - const listItemsWithIds = await client.findIdsForIPs({ context, client, ips: ipsList, account, list }); + const listItemsWithIds = await lib.findIdsForIPs({ context, ips: ipsList, account, list }); const dbItems = listItemsWithIds.map(item => { const { ip, id } = item; diff --git a/src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js b/src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js index a5699ed11..8603ca856 100644 --- a/src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js +++ b/src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js @@ -1,11 +1,11 @@ -const CloudflareAPI = require('../../CloudflareAPI'); +const lib = require('../lib'); let attempts = 0; -const getStatus = async function(context, client, { account, id }) { +const getStatus = async function(context, { account, id }) { context.log({ stage: 'GETTING STATUS', id, attempts }); // https://developers.cloudflare.com/api/operations/lists-get-bulk-operation-status - const { data } = await client.callEndpoint(context, { + const { data } = await lib.callEndpoint(context, { action: `/accounts/${account}/rules/lists/bulk_operations/${id}` }); @@ -18,7 +18,7 @@ const getStatus = async function(context, client, { account, id }) { attempts++; if (attempts <= 10) { await new Promise(r => setTimeout(r, 1000)); - return await getStatus(context, client, { account, id }); + return await getStatus(context, { account, id }); } else { throw new context.CancelError(data.errors); } @@ -30,26 +30,24 @@ const getStatus = async function(context, client, { account, id }) { module.exports = { async receive(context) { - const { apiKey, email } = context.auth; const { account, list, ips } = context.messages.in.content; - const client = new CloudflareAPI({ email, apiKey }); const ipsList = ips.AND; - const listItemsWithIds = await client.findIdsForIPs({ context, client, ips: ipsList, account, list }); + const listItemsWithIds = await lib.findIdsForIPs({ context, ips: ipsList, account, list }); if (!listItemsWithIds.length) { return context.sendJson({ id: 'N/A', status: 'Completed', completed: new Date().toISOString() }, 'out'); } context.log({ stage: 'removing IPs ', items: listItemsWithIds }); // https://developers.cloudflare.com/api/operations/lists-create-list-items - const { data } = await client.callEndpoint(context, { + const { data } = await lib.callEndpoint(context, { method: 'DELETE', action: `/accounts/${account}/rules/lists/${list}/items`, data: { items: listItemsWithIds.map(item => ({ id: item.id })) } }); - const status = await getStatus(context, client, { id: data.result.operation_id, account }); + const status = await getStatus(context, { id: data.result.operation_id, account }); if (status.error) { throw new context.CancelError(status.error); diff --git a/src/appmixer/cloudflare/lists/lib.js b/src/appmixer/cloudflare/lists/lib.js index f25d5f014..efcf393ef 100644 --- a/src/appmixer/cloudflare/lists/lib.js +++ b/src/appmixer/cloudflare/lists/lib.js @@ -1,33 +1,83 @@ -module.exports = { - async fetchInputs(context, client, { account, accountsFetch, listFetch }) { +const callEndpoint = async function(context, { + action, + method = 'GET', + data, + params +}) { - try { - if (accountsFetch) { - const { data } = await client.callEndpoint(context, { action: '/accounts?per_page=50' }); - const items = data.result.map(item => { - return { - label: item.name, - value: item.id - }; - }); - - return context.sendJson(items, 'out'); - } + return context.httpRequest({ + method, + url: `https://api.cloudflare.com/client/v4${action}`, + headers: { + 'Content-Type': 'application/json', + 'X-Auth-Email': context.auth.email, + 'X-Auth-Key': context.auth.apiKey + }, + data, + params + }); +}; + +const fetchInputs = async function(context, { account, accountsFetch, listFetch }) { - if (listFetch) { - const { data } = await client.callEndpoint(context, { action: `/accounts/${account}/rules/lists` }); - const items = data.result.map(item => { - return { - label: item.name, - value: item.id - }; - }); + try { + if (accountsFetch) { + const { data } = await callEndpoint(context, { action: '/accounts?per_page=50' }); + const items = data.result.map(item => { + return { + label: item.name, + value: item.id + }; + }); + + return context.sendJson(items, 'out'); + } + + if (listFetch) { + const { data } = await callEndpoint(context, { action: `/accounts/${account}/rules/lists` }); + const items = data.result.map(item => { + return { + label: item.name, + value: item.id + }; + }); + + return context.sendJson(items, 'out'); + } - return context.sendJson(items, 'out'); + } catch (e) { + return context.sendJson([], 'out'); + } +}; + +const findIdsForIPs = async function({ context, ips = [], account, list }) { + + const result = []; + for (let ipItem of ips) { + + try { + const { data } = await callEndpoint(context, { + method: 'GET', + action: `/accounts/${account}/rules/lists/${list}/items`, + params: { + per_page: 1, + search: ipItem.ip + } + }); + if (data?.result[0] && data?.result.length === 1) { + result.push({ ...data.result[0] }); } - } catch (e) { - return context.sendJson([], 'out'); + } catch (err) { + context.log({ stage: `Invalid IP, IP ${ipItem} hasn't been found in the list ${list}` }); } } + + return result; }; + +module.exports = { + findIdsForIPs, + fetchInputs, + callEndpoint +} \ No newline at end of file diff --git a/src/appmixer/cloudflare/plugin.js b/src/appmixer/cloudflare/plugin.js index a96c44169..dd63974d8 100644 --- a/src/appmixer/cloudflare/plugin.js +++ b/src/appmixer/cloudflare/plugin.js @@ -3,5 +3,5 @@ module.exports = async context => { await require('./routes')(context); await require('./jobs')(context); - context.log('info', '[CloudFlare] CloudFlare plugin successfully initialized.'); + context.log('info', '[CloudFlare Lists] CloudFlare plugin successfully initialized.'); }; From 4671df0d118158c4c9b3e3aaed5732acb9f095dd Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Mon, 13 Jan 2025 10:04:41 +0100 Subject: [PATCH 15/21] update --- src/appmixer/cloudflare/lists/lib.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/appmixer/cloudflare/lists/lib.js b/src/appmixer/cloudflare/lists/lib.js index efcf393ef..2769051a8 100644 --- a/src/appmixer/cloudflare/lists/lib.js +++ b/src/appmixer/cloudflare/lists/lib.js @@ -80,4 +80,4 @@ module.exports = { findIdsForIPs, fetchInputs, callEndpoint -} \ No newline at end of file +}; From d5b256358cc509b7723e4e0dc290f00a7ced4479 Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Mon, 13 Jan 2025 10:09:47 +0100 Subject: [PATCH 16/21] rename --- src/appmixer/cloudflare/jobs.js | 12 ++++++------ src/appmixer/cloudflare/jobs.lists.js | 4 ++-- src/appmixer/cloudflare/plugin.js | 2 +- src/appmixer/cloudflare/service.json | 2 +- src/appmixer/cloudflareWAF/jobs.js | 10 +++++----- src/appmixer/cloudflareWAF/plugin.js | 2 +- src/appmixer/cloudflareWAF/service.json | 4 ++-- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/appmixer/cloudflare/jobs.js b/src/appmixer/cloudflare/jobs.js index 864b62520..ec1ecea37 100644 --- a/src/appmixer/cloudflare/jobs.js +++ b/src/appmixer/cloudflare/jobs.js @@ -10,18 +10,18 @@ module.exports = async (context) => { try { const lock = await context.job.lock('cloud-flare-lists-rule-block-ips-delete-job', { ttl: config.ipDeleteJob.lockTTL }); - await context.log('trace', '[CloudFlare Lists] rule delete job started.'); + await context.log('trace', '[Cloudflare Lists] rule delete job started.'); try { await listsJobs.deleteExpireIpsFromList(context); } finally { lock.unlock(); - await context.log('trace', '[CloudFlare Lists] rule delete job finished. Lock unlocked.'); + await context.log('trace', '[Cloudflare Lists] rule delete job finished. Lock unlocked.'); } } catch (err) { if (err.message !== 'locked') { context.log('error', { - stage: '[CloudFlare Lists] Error checking rules to delete', + stage: '[Cloudflare Lists] Error checking rules to delete', error: err, errorRaw: context.utils.Error.stringify(err) }); @@ -45,19 +45,19 @@ module.exports = async (context) => { }); if (expired.deletedCount) { - await context.log('info', { stage: `[CloudFlare Lists] Deleted ${expired.deletedCount} orphaned rules.` }); + await context.log('info', { stage: `[Cloudflare Lists] Deleted ${expired.deletedCount} orphaned rules.` }); } } catch (err) { if (err.message !== 'locked') { context.log('error', { - stage: '[CloudFlare Lists] Error checking orphaned ips', + stage: '[Cloudflare Lists] Error checking orphaned ips', error: err, errorRaw: context.utils.Error.stringify(err) }); } } finally { lock?.unlock(); - await context.log('trace', '[CloudFlare Lists] IPs cleanup job finished. Lock unlocked.'); + await context.log('trace', '[Cloudflare Lists] IPs cleanup job finished. Lock unlocked.'); } }); }; diff --git a/src/appmixer/cloudflare/jobs.lists.js b/src/appmixer/cloudflare/jobs.lists.js index 758b98b2e..7354051ea 100644 --- a/src/appmixer/cloudflare/jobs.lists.js +++ b/src/appmixer/cloudflare/jobs.lists.js @@ -12,7 +12,7 @@ const deleteExpireIpsFromList = async function(context) { const { email, apiKey, account, list } = chunk.auth; - context.log('info', { stage: '[CloudFlare Lists] removing expired IPs', ips: chunk.ips, list, account }); + context.log('info', { stage: '[Cloudflare Lists] removing expired IPs', ips: chunk.ips, list, account }); const client = new CloudflareAPI({ email, apiKey }); // https://developers.cloudflare.com/api/operations/lists-delete-list-items @@ -41,7 +41,7 @@ const deleteExpireIpsFromList = async function(context) { const deleted = await context.db.collection(getModel(context).collection) .deleteMany({ id: { $in: itemsToDelete.ids.map(item => item.id) } }); await context.log('info', { - stage: `[CloudFlare Lists] Deleted total ${deleted.deletedCount} ips.`, + stage: `[Cloudflare Lists] Deleted total ${deleted.deletedCount} ips.`, lists: itemsToDelete.lists, itemIds: itemsToDelete.ids }); diff --git a/src/appmixer/cloudflare/plugin.js b/src/appmixer/cloudflare/plugin.js index dd63974d8..d341ed5aa 100644 --- a/src/appmixer/cloudflare/plugin.js +++ b/src/appmixer/cloudflare/plugin.js @@ -3,5 +3,5 @@ module.exports = async context => { await require('./routes')(context); await require('./jobs')(context); - context.log('info', '[CloudFlare Lists] CloudFlare plugin successfully initialized.'); + context.log('info', '[Cloudflare Lists] CloudFlare plugin successfully initialized.'); }; diff --git a/src/appmixer/cloudflare/service.json b/src/appmixer/cloudflare/service.json index a09d4579f..129dceee1 100644 --- a/src/appmixer/cloudflare/service.json +++ b/src/appmixer/cloudflare/service.json @@ -1,6 +1,6 @@ { "name": "appmixer.cloudflare", - "label": "CloudFlare Lists", + "label": "Cloudflare Lists", "category": "applications", "description": "CloudFlare List integrations allows you to work with IP lists.", "icon": "data:image/png;base64,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", diff --git a/src/appmixer/cloudflareWAF/jobs.js b/src/appmixer/cloudflareWAF/jobs.js index 2c0ab60d8..de6cdb2e7 100644 --- a/src/appmixer/cloudflareWAF/jobs.js +++ b/src/appmixer/cloudflareWAF/jobs.js @@ -10,18 +10,18 @@ module.exports = async (context) => { try { const lock = await context.job.lock('cloud-flare-waf-rule-block-ips-delete-job', { ttl: config.ipDeleteJob.lockTTL }); - await context.log('trace', '[CloudFlare WAF] rule delete job started.'); + await context.log('trace', '[Cloudflare WAF] rule delete job started.'); try { await wafJobs.deleteExpireIps(context); } finally { lock.unlock(); - await context.log('trace', '[CloudFlare WAF] rule delete job finished. Lock unlocked.'); + await context.log('trace', '[Cloudflare WAF] rule delete job finished. Lock unlocked.'); } } catch (err) { if (err.message !== 'locked') { context.log('error', { - stage: '[CloudFlare WAF] Error checking rules to delete', + stage: '[Cloudflare WAF] Error checking rules to delete', error: err, errorRaw: context.utils.Error.stringify(err) }); @@ -44,12 +44,12 @@ module.exports = async (context) => { }); if (expired.deletedCount) { - await context.log('info', { stage: `[CloudFlare WAF] Deleted ${expired.deletedCount} orphaned rules.` }); + await context.log('info', { stage: `[Cloudflare WAF] Deleted ${expired.deletedCount} orphaned rules.` }); } } catch (err) { if (err.message !== 'locked') { context.log('error', { - stage: '[CloudFlare WAF] Error checking orphaned ips', + stage: '[Cloudflare WAF] Error checking orphaned ips', error: err, errorRaw: context.utils.Error.stringify(err) }); diff --git a/src/appmixer/cloudflareWAF/plugin.js b/src/appmixer/cloudflareWAF/plugin.js index bbc1143f0..dfe28f968 100644 --- a/src/appmixer/cloudflareWAF/plugin.js +++ b/src/appmixer/cloudflareWAF/plugin.js @@ -3,5 +3,5 @@ module.exports = async context => { await require('./routes')(context); await require('./jobs')(context); - context.log('info', '[CloudFlare WAF] CloudFlare WAF plugin successfully initialized.'); + context.log('info', '[Cloudflare WAF] Cloudflare WAF plugin successfully initialized.'); }; diff --git a/src/appmixer/cloudflareWAF/service.json b/src/appmixer/cloudflareWAF/service.json index f48ebe660..37d10be19 100644 --- a/src/appmixer/cloudflareWAF/service.json +++ b/src/appmixer/cloudflareWAF/service.json @@ -1,8 +1,8 @@ { "name": "appmixer.cloudflareWAF", - "label": "CloudFlare WAF", + "label": "Cloudflare WAF", "category": "applications", - "description": "CloudFlare WAF integrations allows you to actively block attacker IP by using CloudFlare WAF infrastructure.", + "description": "Cloudflare WAF integrations allows you to actively block attacker IP by using Cloudflare WAF infrastructure.", "icon": "data:image/png;base64,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", "version": "1.0.5" } From d9dc26031eefd23a9b50c1f07ba408378984cbff Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Mon, 13 Jan 2025 10:10:51 +0100 Subject: [PATCH 17/21] rename --- src/appmixer/cloudflare/jobs.js | 2 +- src/appmixer/cloudflare/plugin.js | 2 +- src/appmixer/cloudflare/service.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/appmixer/cloudflare/jobs.js b/src/appmixer/cloudflare/jobs.js index ec1ecea37..e591bed0c 100644 --- a/src/appmixer/cloudflare/jobs.js +++ b/src/appmixer/cloudflare/jobs.js @@ -35,7 +35,7 @@ module.exports = async (context) => { let lock = null; try { lock = await context.job.lock('cloud-flare-lists-ips-cleanup-job', { ttl: config.cleanup.lockTTL }); - await context.log('trace', '[CloudFlare Lists] IPs cleanup job started.'); + await context.log('trace', '[Cloudflare Lists] IPs cleanup job started.'); // Delete IPs where the time difference between 'mtime' and 'removeAfter' exceeds the specified timespan, // indicating that deletion attempts have persisted for the entire timespan. diff --git a/src/appmixer/cloudflare/plugin.js b/src/appmixer/cloudflare/plugin.js index d341ed5aa..c208c6faf 100644 --- a/src/appmixer/cloudflare/plugin.js +++ b/src/appmixer/cloudflare/plugin.js @@ -3,5 +3,5 @@ module.exports = async context => { await require('./routes')(context); await require('./jobs')(context); - context.log('info', '[Cloudflare Lists] CloudFlare plugin successfully initialized.'); + context.log('info', '[Cloudflare Lists] Cloudflare plugin successfully initialized.'); }; diff --git a/src/appmixer/cloudflare/service.json b/src/appmixer/cloudflare/service.json index 129dceee1..42246fa23 100644 --- a/src/appmixer/cloudflare/service.json +++ b/src/appmixer/cloudflare/service.json @@ -2,7 +2,7 @@ "name": "appmixer.cloudflare", "label": "Cloudflare Lists", "category": "applications", - "description": "CloudFlare List integrations allows you to work with IP lists.", + "description": "Cloudflare Lists integrations allows you to work with IP lists.", "icon": "data:image/png;base64,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", "version": "1.0.5" } From f640c5de9843197d579236c90db6decf6c5c2add Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Wed, 15 Jan 2025 14:43:55 +0100 Subject: [PATCH 18/21] update --- src/appmixer/cloudflare/jobs.js | 14 +++++++------- src/appmixer/cloudflare/jobs.lists.js | 4 ++-- .../cloudflare/lists/AddToIPList/AddToIPList.js | 3 +-- .../lists/RemoveFromIPList/RemoveFromIPList.js | 5 ++--- src/appmixer/cloudflare/lists/lib.js | 2 +- src/appmixer/cloudflareWAF/jobs.waf.js | 8 ++++---- 6 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/appmixer/cloudflare/jobs.js b/src/appmixer/cloudflare/jobs.js index e591bed0c..b5f8effd9 100644 --- a/src/appmixer/cloudflare/jobs.js +++ b/src/appmixer/cloudflare/jobs.js @@ -6,10 +6,10 @@ module.exports = async (context) => { const config = require('./config')(context); - await context.scheduleJob('cloud-flare-lists-ips-delete-job', config.ipDeleteJob.schedule, async () => { + await context.scheduleJob('cloudflare-lists-ips-delete-job', config.ipDeleteJob.schedule, async () => { try { - const lock = await context.job.lock('cloud-flare-lists-rule-block-ips-delete-job', { ttl: config.ipDeleteJob.lockTTL }); + const lock = await context.job.lock('cloudflare-lists-rule-block-ips-delete-job', { ttl: config.ipDeleteJob.lockTTL }); await context.log('trace', '[Cloudflare Lists] rule delete job started.'); try { @@ -21,7 +21,7 @@ module.exports = async (context) => { } catch (err) { if (err.message !== 'locked') { context.log('error', { - stage: '[Cloudflare Lists] Error checking rules to delete', + step: '[Cloudflare Lists] Error checking rules to delete', error: err, errorRaw: context.utils.Error.stringify(err) }); @@ -30,11 +30,11 @@ module.exports = async (context) => { }); // Self-healing job to remove rules that have created>mtime. These rules are stuck in the system and should be removed. - await context.scheduleJob('cloud-flare-lists-ips-cleanup-job', config.cleanup.schedule, async () => { + await context.scheduleJob('cloudflare-lists-ips-cleanup-job', config.cleanup.schedule, async () => { let lock = null; try { - lock = await context.job.lock('cloud-flare-lists-ips-cleanup-job', { ttl: config.cleanup.lockTTL }); + lock = await context.job.lock('cloudflare-lists-ips-cleanup-job', { ttl: config.cleanup.lockTTL }); await context.log('trace', '[Cloudflare Lists] IPs cleanup job started.'); // Delete IPs where the time difference between 'mtime' and 'removeAfter' exceeds the specified timespan, @@ -45,12 +45,12 @@ module.exports = async (context) => { }); if (expired.deletedCount) { - await context.log('info', { stage: `[Cloudflare Lists] Deleted ${expired.deletedCount} orphaned rules.` }); + await context.log('info', { step: `[Cloudflare Lists] Deleted ${expired.deletedCount} orphaned rules.` }); } } catch (err) { if (err.message !== 'locked') { context.log('error', { - stage: '[Cloudflare Lists] Error checking orphaned ips', + step: '[Cloudflare Lists] Error checking orphaned ips', error: err, errorRaw: context.utils.Error.stringify(err) }); diff --git a/src/appmixer/cloudflare/jobs.lists.js b/src/appmixer/cloudflare/jobs.lists.js index 7354051ea..7fed04708 100644 --- a/src/appmixer/cloudflare/jobs.lists.js +++ b/src/appmixer/cloudflare/jobs.lists.js @@ -12,7 +12,7 @@ const deleteExpireIpsFromList = async function(context) { const { email, apiKey, account, list } = chunk.auth; - context.log('info', { stage: '[Cloudflare Lists] removing expired IPs', ips: chunk.ips, list, account }); + context.log('info', { step: '[Cloudflare Lists] removing expired IPs', ips: chunk.ips, list, account }); const client = new CloudflareAPI({ email, apiKey }); // https://developers.cloudflare.com/api/operations/lists-delete-list-items @@ -41,7 +41,7 @@ const deleteExpireIpsFromList = async function(context) { const deleted = await context.db.collection(getModel(context).collection) .deleteMany({ id: { $in: itemsToDelete.ids.map(item => item.id) } }); await context.log('info', { - stage: `[Cloudflare Lists] Deleted total ${deleted.deletedCount} ips.`, + step: `[Cloudflare Lists] Deleted total ${deleted.deletedCount} ips.`, lists: itemsToDelete.lists, itemIds: itemsToDelete.ids }); diff --git a/src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js b/src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js index ce3f5056a..cde17d266 100644 --- a/src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js +++ b/src/appmixer/cloudflare/lists/AddToIPList/AddToIPList.js @@ -5,13 +5,12 @@ const lib = require('../lib'); let attempts = 0; const getStatus = async function(context, { account, id }) { - context.log({ stage: 'GETTING STATUS', id, attempts }); // https://developers.cloudflare.com/api/operations/lists-get-bulk-operation-status const { data } = await lib.callEndpoint(context, { action: `/accounts/${account}/rules/lists/bulk_operations/${id}` }); - context.log({ stage: 'STATUS DATA', data }); + context.log({ step: 'getting status', data }); if (data?.result?.status === 'failed') { throw new context.CancelError(data?.result?.error || data?.errors); } diff --git a/src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js b/src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js index 8603ca856..3b46c57eb 100644 --- a/src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js +++ b/src/appmixer/cloudflare/lists/RemoveFromIPList/RemoveFromIPList.js @@ -3,13 +3,12 @@ const lib = require('../lib'); let attempts = 0; const getStatus = async function(context, { account, id }) { - context.log({ stage: 'GETTING STATUS', id, attempts }); // https://developers.cloudflare.com/api/operations/lists-get-bulk-operation-status const { data } = await lib.callEndpoint(context, { action: `/accounts/${account}/rules/lists/bulk_operations/${id}` }); - context.log({ stage: 'STATUS DATA', data }); + context.log({ step: 'getting status', data }); if (data?.result?.status === 'failed') { throw new context.CancelError(data?.result?.error || data?.errors); } @@ -39,7 +38,7 @@ module.exports = { if (!listItemsWithIds.length) { return context.sendJson({ id: 'N/A', status: 'Completed', completed: new Date().toISOString() }, 'out'); } - context.log({ stage: 'removing IPs ', items: listItemsWithIds }); + context.log({ step: 'removing IPs ', items: listItemsWithIds }); // https://developers.cloudflare.com/api/operations/lists-create-list-items const { data } = await lib.callEndpoint(context, { method: 'DELETE', diff --git a/src/appmixer/cloudflare/lists/lib.js b/src/appmixer/cloudflare/lists/lib.js index 2769051a8..56f317e68 100644 --- a/src/appmixer/cloudflare/lists/lib.js +++ b/src/appmixer/cloudflare/lists/lib.js @@ -69,7 +69,7 @@ const findIdsForIPs = async function({ context, ips = [], account, list }) { } } catch (err) { - context.log({ stage: `Invalid IP, IP ${ipItem} hasn't been found in the list ${list}` }); + context.log({ step: `Invalid IP, IP ${ipItem} hasn't been found in the list ${list}` }); } } diff --git a/src/appmixer/cloudflareWAF/jobs.waf.js b/src/appmixer/cloudflareWAF/jobs.waf.js index 1fae9a8de..29978fa09 100644 --- a/src/appmixer/cloudflareWAF/jobs.waf.js +++ b/src/appmixer/cloudflareWAF/jobs.waf.js @@ -8,12 +8,12 @@ const deleteExpireIps = async function(context) { const expired = await getExpiredItems(context); if (expired.length) { - await context.log('info', { type: '[Cloudflare WAF] expired.', data: sanitizeItems(expired) }); + await context.log('info', { step: '[Cloudflare WAF] expired.', data: sanitizeItems(expired) }); } const rulesToUpdate = await retrieveRulesForUpdate(context, expired); if (rulesToUpdate.length) { - await context.log('info', { type: '[Cloudflare WAF] rules to update.', data: sanitizeItems(rulesToUpdate) }); + await context.log('info', { step: '[Cloudflare WAF] rules to update.', data: sanitizeItems(rulesToUpdate) }); } const dbItemsToDelete = await updateRules(context, rulesToUpdate); @@ -45,7 +45,7 @@ const retrieveRulesForUpdate = async function(context, expired = []) { } } else { context.log('info', { - type: '[Cloudflare WAF] Unable to retrieve rule for expired item', + step: '[Cloudflare WAF] Unable to retrieve rule for expired item', data: sanitizeItems([item]) }); } @@ -75,7 +75,7 @@ const updateRules = async function(context, rulesToUpdate) { dbItemsToDelete = dbItemsToDelete.concat(dbItems); } else { context.log('info', { - type: '[Cloudflare WAF] Unable to delete IPs from rule.', + step: '[Cloudflare WAF] Unable to delete IPs from rule.', data: sanitizeItems([item]) }); } From a8b8ebc02a83a118fb207282e00a64dc1cdae7fd Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Wed, 15 Jan 2025 14:53:30 +0100 Subject: [PATCH 19/21] update --- src/appmixer/cloudflare/jobs.js | 2 +- src/appmixer/cloudflare/jobs.lists.js | 4 ++-- src/appmixer/cloudflareWAF/jobs.waf.js | 5 ++++- src/appmixer/cloudflareWAF/waf/lib.js | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/appmixer/cloudflare/jobs.js b/src/appmixer/cloudflare/jobs.js index b5f8effd9..a1f4cb915 100644 --- a/src/appmixer/cloudflare/jobs.js +++ b/src/appmixer/cloudflare/jobs.js @@ -13,7 +13,7 @@ module.exports = async (context) => { await context.log('trace', '[Cloudflare Lists] rule delete job started.'); try { - await listsJobs.deleteExpireIpsFromList(context); + await listsJobs.deleteExpiredIpsFromList(context); } finally { lock.unlock(); await context.log('trace', '[Cloudflare Lists] rule delete job finished. Lock unlocked.'); diff --git a/src/appmixer/cloudflare/jobs.lists.js b/src/appmixer/cloudflare/jobs.lists.js index 7fed04708..198a214ae 100644 --- a/src/appmixer/cloudflare/jobs.lists.js +++ b/src/appmixer/cloudflare/jobs.lists.js @@ -2,7 +2,7 @@ const CloudflareAPI = require('./CloudflareAPI'); const getModel = (context) => require('./IPListModel')(context); -const deleteExpireIpsFromList = async function(context) { +const deleteExpiredIpsFromList = async function(context) { const expired = await getExpiredItems(context); @@ -65,5 +65,5 @@ const getExpiredItems = async function(context) { }; module.exports = { - deleteExpireIpsFromList + deleteExpiredIpsFromList }; diff --git a/src/appmixer/cloudflareWAF/jobs.waf.js b/src/appmixer/cloudflareWAF/jobs.waf.js index 29978fa09..a095ee339 100644 --- a/src/appmixer/cloudflareWAF/jobs.waf.js +++ b/src/appmixer/cloudflareWAF/jobs.waf.js @@ -177,5 +177,8 @@ function sanitizeItems(items = []) { } module.exports = { - deleteExpireIps, getBlockExpression, extractIPs + deleteExpireIps, + getBlockExpression, + extractIPs, + removeIpsFromRule }; diff --git a/src/appmixer/cloudflareWAF/waf/lib.js b/src/appmixer/cloudflareWAF/waf/lib.js index 76397acce..1af92425b 100644 --- a/src/appmixer/cloudflareWAF/waf/lib.js +++ b/src/appmixer/cloudflareWAF/waf/lib.js @@ -10,7 +10,8 @@ module.exports = { getIpsFromRules, prepareRulesForCreateOrUpdate, getBlockRule, - findIpsInRules + findIpsInRules, + extractIPs }; function getIpsFromRules(rules) { From 80d5ab2a7ae94632a0fe492651aee57c7c211d38 Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Wed, 15 Jan 2025 15:17:29 +0100 Subject: [PATCH 20/21] update --- .../cloudflareWAF/waf/CreateCustomRules/CreateCustomRules.js | 2 +- .../cloudflareWAF/waf/CreateCustomRules/component.json | 4 ++-- src/appmixer/cloudflareWAF/waf/lib.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/appmixer/cloudflareWAF/waf/CreateCustomRules/CreateCustomRules.js b/src/appmixer/cloudflareWAF/waf/CreateCustomRules/CreateCustomRules.js index e1116b5d0..d188810be 100644 --- a/src/appmixer/cloudflareWAF/waf/CreateCustomRules/CreateCustomRules.js +++ b/src/appmixer/cloudflareWAF/waf/CreateCustomRules/CreateCustomRules.js @@ -13,7 +13,7 @@ module.exports = { return context.sendJson([], 'out'); } - const parsedIps = Array.isArray(ips) ? ips : ips.split(','); + const parsedIps = ips.split(/\s+|,/); // Split by comma or any whitespace const client = new CloudflareAPI({ zoneId, token: apiToken }); const ruleset = (await client.listZoneRulesets(context)) diff --git a/src/appmixer/cloudflareWAF/waf/CreateCustomRules/component.json b/src/appmixer/cloudflareWAF/waf/CreateCustomRules/component.json index b28c4e3d6..1691491b1 100644 --- a/src/appmixer/cloudflareWAF/waf/CreateCustomRules/component.json +++ b/src/appmixer/cloudflareWAF/waf/CreateCustomRules/component.json @@ -26,14 +26,14 @@ "ips": { "type": "text", "label": "IP address", - "tooltip": "IP address which will be blocked by adding a blocking custom rule to the waf.", + "tooltip": "List of IP address which will be blocked by adding a blocking custom rule to the waf. The values must be separated by a comma, space or newline.", "index": 1 }, "ttl": { "type": "number", "label": "TTL", "index": 2, - "tooltip": "Time to live in seconds. The IPs will be automatically removed after this time." + "tooltip": "Time to live in seconds. The IP(s) will be automatically removed from a rule after this time. If not set, the the IP(s) will remain permanently." } } } diff --git a/src/appmixer/cloudflareWAF/waf/lib.js b/src/appmixer/cloudflareWAF/waf/lib.js index 1af92425b..f349928cf 100644 --- a/src/appmixer/cloudflareWAF/waf/lib.js +++ b/src/appmixer/cloudflareWAF/waf/lib.js @@ -1,5 +1,5 @@ -const ruleDescription = 'Salt detected high severity attacker'; -const ruleRefPrefix = 'SALT'; +const ruleDescription = 'Generated Rule'; +const ruleRefPrefix = 'generated_rule'; const RULE_MAX_CAPACITY = 4096; // extractIPs, getBlockExpression are also used in jobs.waf. They cannot be here, as jobs.waf From 2ab3c5d33d75d202ba8b7db064f2daa0dfeba55d Mon Sep 17 00:00:00 2001 From: vladimir talas Date: Thu, 16 Jan 2025 13:12:30 +0100 Subject: [PATCH 21/21] cleanup --- src/appmixer/cloudflare/CloudflareAPI.js | 65 ------------------------ 1 file changed, 65 deletions(-) diff --git a/src/appmixer/cloudflare/CloudflareAPI.js b/src/appmixer/cloudflare/CloudflareAPI.js index 42130b33b..11b35cadb 100644 --- a/src/appmixer/cloudflare/CloudflareAPI.js +++ b/src/appmixer/cloudflare/CloudflareAPI.js @@ -23,17 +23,6 @@ module.exports = class CloudflareAPI { }; } - async verify(context) { - - const headers = this.getHeaders(); - - return context.httpRequest({ - method: 'GET', - url: 'https://api.cloudflare.com/client/v4/user/tokens/verify', - headers - }); - } - async verifyGlobalApiKey(context) { const headers = this.getHeaders(); @@ -62,58 +51,4 @@ module.exports = class CloudflareAPI { params }); } - - /** - * https://developers.cloudflare.com/api/operations/getZoneRuleset - */ - listZoneRulesets(context) { - - return context.httpRequest({ - method: 'GET', - url: `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets`, - headers: this.getHeaders() - }).then(resp => resp.data.result); - } - - async getRules(context, rulesetId) { - - const response = await context.httpRequest({ - method: 'GET', - url: `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets/${rulesetId}`, - headers: this.getHeaders() - }); - - return response.data; - } - - async createRulesetAndBlockRule(context, ips) { - - const { data } = await context.httpRequest({ - method: 'POST', - url: `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets`, - headers: this.getHeaders(), - data: { - kind: 'zone', - phase: 'http_request_firewall_custom', - name: 'Http Request Firewall Custom Ruleset', - description: 'Created by Salt Security\'s Cloudflare Integration. Firewall rules of identified attackers will be added to this ruleset.', - rules: [this.getBlockRule(1, ips)] - } - }); - - return data; - } - - // https://developers.cloudflare.com/api/resources/rulesets/subresources/rules/ - createBlockRule(context, { rulesetId, rule }) { - const url = `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets/${rulesetId}/rules`; - const headers = this.getHeaders(); - return context.httpRequest.post(url, rule, { headers }).then(resp => resp.data); - } - - updateBlockRule(context, rulesetId, rule) { - const url = `https://api.cloudflare.com/client/v4/zones/${this.zoneId}/rulesets/${rulesetId}/rules/${rule.id}`; - const headers = this.getHeaders(); - return context.httpRequest.patch(url, rule, { headers }).then(resp => resp.data); - } };