From d51b99214de3a9e2f9d8eadb016788d80f7c0c09 Mon Sep 17 00:00:00 2001 From: mrrfv Date: Tue, 2 May 2023 17:26:54 +0200 Subject: [PATCH] Initial version --- .env.example | 4 + .github/images/gateway_analytics.png | Bin 0 -> 59533 bytes .gitignore | 4 + README.md | 47 +++++++++- cf_gateway_rule_create.js | 65 +++++++++++++ cf_gateway_rule_delete.js | 50 ++++++++++ cf_list_create.js | 132 +++++++++++++++++++++++++++ cf_list_delete.js | 52 +++++++++++ get_recommended_filters.sh | 32 +++++++ package-lock.json | 111 ++++++++++++++++++++++ package.json | 6 ++ 11 files changed, 501 insertions(+), 2 deletions(-) create mode 100644 .env.example create mode 100644 .github/images/gateway_analytics.png create mode 100644 cf_gateway_rule_create.js create mode 100644 cf_gateway_rule_delete.js create mode 100644 cf_list_create.js create mode 100644 cf_list_delete.js create mode 100644 get_recommended_filters.sh create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..b54a472 --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +CLOUDFLARE_API_KEY="" +CLOUDFLARE_ACCOUNT_ID="" +CLOUDFLARE_ACCOUNT_EMAIL="" +CLOUDFLARE_LIST_ITEM_LIMIT="300000" \ No newline at end of file diff --git a/.github/images/gateway_analytics.png b/.github/images/gateway_analytics.png new file mode 100644 index 0000000000000000000000000000000000000000..f7ab1c8729e01a8e120640376a887c249952b77a GIT binary patch literal 59533 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 literal 0 HcmV?d00001 diff --git a/.gitignore b/.gitignore index 20b0c92..7b2a616 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,10 @@ yarn-error.log* lerna-debug.log* .pnpm-debug.log* +# CGPS +output +input.csv + # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json diff --git a/README.md b/README.md index 349773d..9743ecc 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,45 @@ -# cloudflare-gateway-pihole-scripts - Use Cloudflare Gateway VPN to block ads and tracking domains - free alternative to NextDNS, Pi-hole and Adguard +# Cloudflare Gateway Pi-hole Scripts (CGPS) + +![Cloudflare Gateway Analytics screenshot](.github/images/gateway_analytics.png) + +Cloudflare Gateway allows you to create custom rules to filter HTTP, DNS, and network traffic based on your firewall policies. This is a collection of scripts that can be used to get a similar experience as if you were using Pi-hole, but with Cloudflare Gateway - so no servers to maintain or need to buy a Raspberry Pi! + +## About the individual scripts + +- `cf_list_delete.js` - Deletes all lists created by CGPS from Cloudflare Gateway. This is useful for subsequent runs. +- `cf_list_create.js` - Takes an input.csv file containing domains and creates lists in Cloudflare Gateway +- `cf_gateway_rule_create.js` - Creates a Cloudflare Gateway rule to block all traffic if it matches the lists created by CGPS. +- `cf_gateway_rule_delete.js` - Deletes the Cloudflare Gateway rule created by CGPS. Useful for subsequent runs. + +## Features + +- Support for hosts files +- Full support for domain lists +- Automatically cleans up filter lists: removes duplicates, invalid domains, comments and more +- Works fully unattended + +## Usage + +### Prerequisites + +1. Node.js installed on your machine +2. Cloudflare Zero Trust account - the Free plan is enough. Use the Cloudflare documentation for details. +3. Cloudflare email, API key (NOT the API token), and account ID +4. A filter list of domains you want to block - **max 300,000 domains for the free plan** - in the working directory named `input.csv`. Mullvad provides awesome [DNS blocklists](https://github.com/mullvad/dns-blocklists) that work well with this project. + +--- + +1. Clone this repository. +2. Run `npm install` to install dependencies. +3. Copy `.env.example` to `.env` and fill in the values. +4. If this is a subsequent run, execute `cf_gateway_rule_delete.js` and `cf_list_delete.js` (in order) to clean up. +5. If you're on Linux and haven't downloaded any filters yourself, use the `get_recommended_filters.sh` script to download recommended filter lists (about 250 000 domains). +6. Run `cf_list_create.js` to create the lists in Cloudflare Gateway. +7. Run `cf_gateway_rule_create.js` to create the firewall rule in Cloudflare Gateway. +8. Profit! + +### Running in GitHub Actions + +This project can be run in GitHub Actions so your filter lists will be automatically updated and pushed to Cloudflare Gateway. + +[work in progress] diff --git a/cf_gateway_rule_create.js b/cf_gateway_rule_create.js new file mode 100644 index 0000000..c25ff17 --- /dev/null +++ b/cf_gateway_rule_create.js @@ -0,0 +1,65 @@ +require("dotenv").config(); +const axios = require('axios'); + +const API_TOKEN = process.env.CLOUDFLARE_API_KEY; +const ACCOUNT_ID = process.env.CLOUDFLARE_ACCOUNT_ID; +const ACCOUNT_EMAIL = process.env.CLOUDFLARE_ACCOUNT_EMAIL; + +// Function to read Cloudflare Zero Trust lists +async function getZeroTrustLists() { + const response = await axios.get( + `https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/gateway/lists`, + { + headers: { + 'Authorization': `Bearer ${API_TOKEN}`, + 'Content-Type': 'application/json', + 'X-Auth-Email': ACCOUNT_EMAIL, + 'X-Auth-Key': API_TOKEN, + }, + } + ); + + return response.data.result; +} + +;(async() => { + const lists = await getZeroTrustLists(); + const filtered_lists = lists.filter(list => list.name.startsWith('CGPS List')); + + let wirefilter_expression = ''; + + // Build the wirefilter expression + for (const list of filtered_lists) { + wirefilter_expression += `dns.fqdn in \$${list.id} or `; + } + // Remove the trailing ' or ' + if (wirefilter_expression.endsWith(' or ')) { + wirefilter_expression = wirefilter_expression.slice(0, -4); + } + wirefilter_expression = wirefilter_expression.trim().replace('\n', ''); + console.log(`Firewall expression contains ${wirefilter_expression.length} characters, and checks against ${filtered_lists.length} filter lists.`) + + const resp = await axios.request({ + method: 'POST', + url: `https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/gateway/rules`, + headers: { + 'Authorization': `Bearer ${API_TOKEN}`, + 'Content-Type': 'application/json', + 'X-Auth-Email': ACCOUNT_EMAIL, + 'X-Auth-Key': API_TOKEN, + }, + data: { + "name": "CGPS Filter Lists", + "description": "Filter lists created by Cloudflare Gateway Pi-hole Scripts. Avoid editing this rule. Changing the name of this rule will break the script.", + "enabled": true, + "action": "block", + "filters": ["dns"], + "traffic": wirefilter_expression, + } + }); + console.log('Success:', resp.data.success); +})(); + +async function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} \ No newline at end of file diff --git a/cf_gateway_rule_delete.js b/cf_gateway_rule_delete.js new file mode 100644 index 0000000..97949e5 --- /dev/null +++ b/cf_gateway_rule_delete.js @@ -0,0 +1,50 @@ +require("dotenv").config(); +const axios = require('axios'); + +const API_TOKEN = process.env.CLOUDFLARE_API_KEY; +const ACCOUNT_ID = process.env.CLOUDFLARE_ACCOUNT_ID; +const ACCOUNT_EMAIL = process.env.CLOUDFLARE_ACCOUNT_EMAIL; + +// Function to read Cloudflare Zero Trust rules +async function getZeroTrustRules() { + const response = await axios.get( + `https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/gateway/rules`, + { + headers: { + 'Authorization': `Bearer ${API_TOKEN}`, + 'Content-Type': 'application/json', + 'X-Auth-Email': ACCOUNT_EMAIL, + 'X-Auth-Key': API_TOKEN, + }, + } + ); + + return response.data.result; +} + +;(async() => { + const rules = await getZeroTrustRules(); + const [filtered_rule] = rules.filter(rule => rule.name === "CGPS Filter Lists"); + + if (!filtered_rule) return console.warn("No rule with matching name found - this is not an issue if you haven't run the create script yet. Exiting."); + + console.log(`Deleting rule ${filtered_rule.name} with ID ${filtered_rule.id}`); + + const resp = await axios.request({ + method: 'DELETE', + url: `https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/gateway/rules/${filtered_rule.id}`, + headers: { + 'Authorization': `Bearer ${API_TOKEN}`, + 'Content-Type': 'application/json', + 'X-Auth-Email': ACCOUNT_EMAIL, + 'X-Auth-Key': API_TOKEN, + }, + }); + + console.log('Success: ', resp.data.success); + await sleep(350); // Cloudflare API rate limit is 1200 requests per 5 minutes, so we sleep for 350ms to be safe +})(); + +async function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} \ No newline at end of file diff --git a/cf_list_create.js b/cf_list_create.js new file mode 100644 index 0000000..2e2c179 --- /dev/null +++ b/cf_list_create.js @@ -0,0 +1,132 @@ +require("dotenv").config(); +const fs = require('fs'); +const axios = require('axios'); + +const API_TOKEN = process.env.CLOUDFLARE_API_KEY; +const ACCOUNT_ID = process.env.CLOUDFLARE_ACCOUNT_ID; +const ACCOUNT_EMAIL = process.env.CLOUDFLARE_ACCOUNT_EMAIL; +const LIST_ITEM_LIMIT = Number.isSafeInteger(Number(process.env.CLOUDFLARE_LIST_ITEM_LIMIT)) ? Number(process.env.CLOUDFLARE_LIST_ITEM_LIMIT) : 300000; + +console.log(`List item limit set to ${LIST_ITEM_LIMIT}`); + +// Read input.csv and parse domains +fs.readFile('input.csv', 'utf8', async (err, data) => { + if (err) { + console.error('Error reading input.csv:', err); + return; + } + + // Convert into array and cleanup input + const domainValidationPattern = /^(?!-)[A-Za-z0-9-]+([\-\.]{1}[a-z0-9]+)*\.[A-Za-z]{2,6}$/; + let domains = data.split('\n').filter(domain => { + // Remove entire lines starting with "127.0.0.1" or "::1", empty lines or comments + return domain && !domain.startsWith('#') && !domain.startsWith('//') && !domain.startsWith('/*') && !domain.startsWith('*/') && !(domain === '\r'); + }).map(domain => { + // Remove "\r", "0.0.0.0 ", "127.0.0.1 ", "::1 " and similar from domain items + return domain + .replace('\r', '') + .replace('0.0.0.0 ', '') + .replace('127.0.0.1 ', '') + .replace('::1 ', '') + .replace(':: ', ''); + }).filter(domain => { + return domainValidationPattern.test(domain); + }); + + // Trim array to 300,000 domains if it's longer than that + if (domains.length > LIST_ITEM_LIMIT) { + domains = trimArray(domains, LIST_ITEM_LIMIT); + console.warn(`More than ${LIST_ITEM_LIMIT} domains found in input.csv - input has to be trimmed`); + } + + // Check for duplicates in domains array + let uniqueDomains = []; + let seen = new Set(); // Use a set to store seen values + for (let domain of domains) { + if (!seen.has(domain)) { // If the domain is not in the set + seen.add(domain); // Add it to the set + uniqueDomains.push(domain); // Push the domain to the uniqueDomains array + } else { // If the domain is in the set + console.warn(`Duplicate domain found: ${domain} - removing`); // Log the duplicate domain + } + } + + // Replace domains array with uniqueDomains array + domains = uniqueDomains; + + const listsToCreate = Math.ceil(domains.length / 1000); + + console.log(`Found ${domains.length} valid domains in input.csv after cleanup - ${listsToCreate} list(s) will be created`); + + // Separate domains into chunks of 1000 (Cloudflare list cap) + const chunks = chunkArray(domains, 1000); + + // Create Cloudflare Zero Trust lists + for (const [index, chunk] of chunks.entries()) { + const listName = `CGPS List - Chunk ${index}`; + + let properList = []; + + chunk.forEach(domain => { + properList.push({ "value": domain }) + }); + + try { + await createZeroTrustList(listName, properList, (index+1), listsToCreate); + await sleep(350); // Sleep for 350ms between list additions + } catch (error) { + console.error(`Error creating list "${listName}":`, error.response.data); + } + } +}); + +function trimArray(arr, size) { + return arr.slice(0, size); +} + +// Function to check if a domain is valid +function isValidDomain(domain) { + const regex = /^((?!-)[A-Za-z0-9-]{1,63}(? setTimeout(resolve, ms)); +} diff --git a/cf_list_delete.js b/cf_list_delete.js new file mode 100644 index 0000000..111f29b --- /dev/null +++ b/cf_list_delete.js @@ -0,0 +1,52 @@ +require("dotenv").config(); +const axios = require('axios'); + +const API_TOKEN = process.env.CLOUDFLARE_API_KEY; +const ACCOUNT_ID = process.env.CLOUDFLARE_ACCOUNT_ID; +const ACCOUNT_EMAIL = process.env.CLOUDFLARE_ACCOUNT_EMAIL; + +// Function to read Cloudflare Zero Trust lists +async function getZeroTrustLists() { + const response = await axios.get( + `https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/gateway/lists`, + { + headers: { + 'Authorization': `Bearer ${API_TOKEN}`, + 'Content-Type': 'application/json', + 'X-Auth-Email': ACCOUNT_EMAIL, + 'X-Auth-Key': API_TOKEN, + }, + } + ); + + return response.data.result; +} + +;(async() => { + const lists = await getZeroTrustLists(); + if (!lists) return console.warn("No file lists found - this is not an issue if it's your first time running this script. Exiting."); + const cgps_lists = lists.filter(list => list.name.startsWith('CGPS List')); + if (!cgps_lists.length) return console.warn("No lists with matching name found - this is not an issue if you haven't created any filter lists before. Exiting."); + + console.log(`Got ${lists.length} lists, ${cgps_lists.length} of which are CGPS lists that will be deleted.`); + + for (const list of cgps_lists) { + console.log(`Deleting list ${list.name} with ID ${list.id}`); + const resp = await axios.request({ + method: 'DELETE', + url: `https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/gateway/lists/${list.id}`, + headers: { + 'Authorization': `Bearer ${API_TOKEN}`, + 'Content-Type': 'application/json', + 'X-Auth-Email': ACCOUNT_EMAIL, + 'X-Auth-Key': API_TOKEN, + }, + }); + console.log('Success:', resp.data.success); + await sleep(350); // Cloudflare API rate limit is 1200 requests per 5 minutes, so we sleep for 350ms to be safe + } +})(); + +async function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} \ No newline at end of file diff --git a/get_recommended_filters.sh b/get_recommended_filters.sh new file mode 100644 index 0000000..e183f3f --- /dev/null +++ b/get_recommended_filters.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# create an empty input.csv file +touch input.csv + +# declare an array of urls +urls=( + https://raw.githubusercontent.com/mullvad/dns-blocklists/main/output/doh/doh_adblock.txt + https://raw.githubusercontent.com/mullvad/dns-blocklists/main/output/doh/doh_gambling.txt + https://raw.githubusercontent.com/mullvad/dns-blocklists/main/output/doh/doh_privacy.txt + https://raw.githubusercontent.com/FadeMind/hosts.extras/master/add.Risk/hosts + https://raw.githubusercontent.com/DandelionSprout/adfilt/master/Alternate%20versions%20Anti-Malware%20List/AntiMalwareHosts.txt + https://rescure.me/rescure_domain_blacklist.txt + https://adaway.org/hosts.txt + https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts +) + +# loop through the urls and download each file with curl +for url in "${urls[@]}"; do + # get the file name from the url + file=$(basename "$url") + # download the file with curl and save it as file.txt + curl -o "$file.txt" "$url" + # append the file contents to input.csv and add a newline + cat "$file.txt" >> input.csv + echo "" >> input.csv + # remove the file.txt + rm "$file.txt" +done + +# print a message when done +echo "Done. The input.csv file contains merged data from recommended filter lists." \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ebd7ac6 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,111 @@ +{ + "name": "cloudflare-gateway-pihole-scripts", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "axios": "^1.4.0", + "dotenv": "^16.0.3" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..5265f2e --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "axios": "^1.4.0", + "dotenv": "^16.0.3" + } +}