Compare commits

..

10 Commits

Author SHA1 Message Date
/techno 3c7f61fb37
Delete utils.py 2023-08-06 18:37:17 -04:00
slashtechno b9013e051d
Merge branch 'master' into restructure-project 2023-08-06 18:36:34 -04:00
deepsource-autofix[bot] 79ca52987b
style: Format code with black and isort 2023-08-06 22:26:16 +00:00
slashtechno 3a18a4536c
Updated `main()` for `delete.py` and `upload.py`
Also added information on whitelists to `README.md`
2023-08-06 18:25:55 -04:00
/techno 263a970edb
Merge pull request #2 from slashtechno/deepsource-transform-200a61ec
format code with black and isort
2023-08-06 17:55:45 -04:00
deepsource-autofix[bot] 7cad46cf8a
style: Format code with black and isort 2023-08-06 21:55:19 +00:00
/techno 0ec3e62c03
Updated README.md 2023-08-06 17:50:44 -04:00
/techno bc94e67635
Use regex for extracting hosts 2023-08-06 17:50:33 -04:00
deepsource-autofix[bot] 4bb9c47e6c
style: format code with black and isort
Format code with black and isort

This commit fixes the style issues introduced in cdbd7dd according to the output
from Black and isort.

Details: https://app.deepsource.com/gh/slashtechno/cloudflare-gateway-adblocking/transform/f9f81019-cc0f-4300-8f90-5884a2afa39a/
2023-08-06 18:46:14 +00:00
deepsource-io[bot] cdbd7dd369
ci: Add .deepsource.toml 2023-08-06 18:46:02 +00:00
10 changed files with 129 additions and 41 deletions

13
.deepsource.toml Normal file
View File

@ -0,0 +1,13 @@
version = 1
[[analyzers]]
name = "python"
[analyzers.meta]
runtime_version = "3.x.x"
[[transformers]]
name = "black"
[[transformers]]
name = "isort"

View File

@ -1,2 +1,2 @@
export CLOUDFLARE_ACCOUNT_ID=
export CLOUDFLARE_TOKEN=
CLOUDFLARE_ACCOUNT_ID=
CLOUDFLARE_TOKEN=

1
.gitignore vendored
View File

@ -8,3 +8,4 @@ tmp.py
.venv
dist/
.ruff_cache/
hosts.txt

10
.vscode/settings.json vendored
View File

@ -1,4 +1,10 @@
{
"python.languageServer": "Pylance",
"python.analysis.ignore": [ "*" ] // Ruff is used for linting but Pylance still is useful
"python.languageServer": "Pylance", // Ruff is used for linting but Pylance still is useful for intellisense
"python.analysis.ignore": [
"*"
],
"python.analysis.exclude": [
"."
],
"python.linting.enabled": false // https://github.com/microsoft/vscode-python/wiki/Migration-to-Python-Tools-Extensions
}

View File

@ -1,2 +1,46 @@
# Cloudflare Gateway Adblocking
Serverless adblocking via Cloudflare Zero Trust Gateway
### What is this?
This is a serverless adblocking solution that uses Cloudflare's Zero Trust Gateway to block ads by parsing a hosts file and creating a firewall rule to block the domains. It can be used as an alternative to Pi-Hole or other adblocking solutions.
This project was heavily inspired by [this blog post](https://blog.marcolancini.it/2022/blog-serverless-ad-blocking-with-cloudflare-gateway/)
### Pre-requisites
* Python > 3.10
* A Cloudflare account with Zero Trust enabled
* A Cloudflare API tolken with the following permissions:
* Zero Trust: Edit
* Account Firewall Access Rules: Edit
* Access: Apps and Policies: Edit
* A device with the WARP client installed and configured to use a Zero Trust account
<!--
### Installation
#### From PyPi
`pip install cloudflare-gateway-adblocking`
-->
### Usage
#### Setting Cloudflare credentials
##### Environment variables
The following environment variables can be used to set the Cloudflare credentials:
* `CLOUDFLARE_ACCOUNT_ID`
* `CLOUDFLARE_TOKEN`
These can either be set in the environment or in a `.env` file in the current working directory.
#### Command line flags
The following command line flags can be used to set the Cloudflare credentials:
* Cloudflare Account ID: `--account-id` / `-a`
* Cloudflare Token: `--token` / `-t`
#### Passing blocklists
Blocklists can be passed to the program via the command line flag `--blocklist` / `-b`. This flag can either point to a hosts file or a directory containing hosts files. If this flag is not passed, the program will look for a file or directory named `blocklists` in the current working directory.
# Passing whitelists
Whitelists can be passed to the program via the command line flag `--whitelist` / `-w`. This flag can either point to a hosts file or a directory containing hosts files. If this flag is not passed, then if a file or directory named `whitelists` exists in the current working directory, it will be used. Domains in this whitelist will be excluded from the blocklists.
#### Uploading blocklists and creating a firewall policy
To upload the blocklists to Cloudflare and create a firewall policy, use the `upload` subcommand.
For example:
`cloudflare-gateway-adblocking upload`
#### Deleting blocklists and firewall policy
To delete the blocklists from Cloudflare and delete the firewall policy, use the `delete` subcommand.
For example:
`cloudflare-gateway-adblocking delete`

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "cloudflare-gateway-adblocking"
version = "0.1.9"
version = "0.1.0"
description = "Serverless adblocking via Cloudflare Zero Trust Gateway"
authors = ["slastechno <77907286+slashtechno@users.noreply.github.com>"]
license = "MIT"

View File

@ -1,13 +1,15 @@
# To run from the root project directory, run the following command:
# python -m src.__main__
from loguru import logger
from .utils import upload, delete, utils
# python -m src # also works because __main__ is the default module
import argparse
import os
import dotenv
from sys import exit, stderr
from pathlib import Path
from sys import exit, stderr
import dotenv
from loguru import logger
from .utils import delete, upload, utils
TOKEN = None
ACCOUNT_ID = None
@ -70,13 +72,13 @@ def main():
"--blocklists",
"-b",
help="Either a blocklist hosts file or a directory containing blocklist hosts files", # noqa E501
default="blocklists",
default="blocklists", # Not really needed because the get_blocklists function will default to this # noqa: E501
)
upload_parser.add_argument(
"--whitelists",
"-w",
help="Either a whitelist hosts file or a directory containing whitelist hosts files", # noqa E501
default="whitelist.txt", # Need to change this so it's optional
default="whitelist.txt", # Not really needed because the apply_whitelists function will default to this # noqa: E501
)
# Add subcommand: delete
delete_parser = subparsers.add_parser(
@ -102,8 +104,9 @@ def main():
try:
args.func(args)
except AttributeError as e:
logger.debug(e)
logger.error("No subcommand specified")
argparser.print_help()
exit(1)
def upload_to_cloudflare(args):

View File

@ -1,6 +1,7 @@
# This is a scriprt to undo the changes made by adblock-zerotrust.py
import requests
from . import utils
@ -43,11 +44,14 @@ def delete_adblock_policy(policies: dict, account_id: str, token: str):
def main():
rules = utils.get_gateway_rules()
delete_adblock_policy(rules)
lists = utils.get_lists()
account_id = input("Enter your Cloudflare account ID: ")
token = input("Enter your Cloudflare API token: ")
rules = utils.get_gateway_rules(account_id, token)
delete_adblock_policy(rules, account_id, token)
lists = utils.get_lists(account_id, token)
lists = utils.filter_adblock_lists(lists)
delete_adblock_list(lists)
delete_adblock_list(lists, account_id, token)
if __name__ == "__main__":

View File

@ -1,9 +1,11 @@
import requests
from . import utils
import pathlib
import requests
def get_blocklists(hosts_path: str = None):
from . import utils
def get_blocklists(hosts_path: str = "blocklists"):
blocklists = []
hosts_path = pathlib.Path(hosts_path)
if hosts_path.is_file():
@ -16,7 +18,7 @@ def get_blocklists(hosts_path: str = None):
return blocklists
def apply_whitelists(blocklists, whitelist: str = None):
def apply_whitelists(blocklists, whitelist: str = "whitelists"):
# If whitelist is a file, convert it to a list.
# If whitelist is a directory, convert all files in it to a list and combine them.
# If it does not exist, return the original blocklists
@ -100,13 +102,16 @@ def create_dns_policy(lists, account_id: str, token: str) -> None:
def main():
account_id = input("Enter your Cloudflare account ID: ")
token = input("Enter your Cloudflare API token: ")
blocklists = get_blocklists()
blocklists = apply_whitelists(blocklists)
lists = split_list(blocklists)
upload_to_cloudflare(lists)
cloud_lists = utils.get_lists()
upload_to_cloudflare(lists, account_id, token)
cloud_lists = utils.get_lists(account_id, token)
cloud_lists = utils.filter_adblock_lists(cloud_lists)
create_dns_policy(cloud_lists)
create_dns_policy(cloud_lists, account_id, token)
if __name__ == "__main__":

View File

@ -1,4 +1,5 @@
import pathlib
import re
import requests
@ -53,23 +54,34 @@ class Config:
# Convert a hosts file to a simple hostname list
def convert_to_list(file: pathlib.Path) -> list:
with open(file, "r") as f:
# Don't read commented lines; strip whitespace;
# remove 127.0.0.1 from beginning of line;
# ignore lines with "localhost"; ignore lines with "::1";
# ignore newlines
hosts = [
i[10:].strip()
for i in f.readlines()
if not i.startswith("#") and "localhost" not in i and "::1" not in i
# Loop through the file and using regex, only get the domain names
# Remove the prefixed loopback domain and suffixed comments
# Remove any empty strings
loopback = [
"localhost",
"::1",
"localhost.localdomain",
"broadcasthost",
"local",
"ip6-localhost",
"ip6-loopback",
"ip6-localnet",
"ip6-mcastprefix",
"ip6-allnodes",
"ip6-allrouters",
"ip6-allhosts",
"0.0.0.0",
]
# Equivalent to:
# for x in f.readlines():
# if not x.startswith('#') and 'localhost' not in x and '::1' not in x:
# hosts.append(x[10:].strip())
# If there are any empty strings in the list, remove them
# For some reason, whitelist seems to still be present
hosts = [i for i in hosts if i != ""]
matches = [
re.search(r"^(?:127\.0\.0\.1|0\.0\.0\.0|::1)\s+(.+?)(?:\s+#.+)?$", line)
for line in f
]
hosts = [
match.group(1)
for match in matches
if match and match.group(1) not in loopback
]
print(f"First 5 hosts: {hosts[:5]}")
return hosts