Compare commits
10 Commits
df2a598c9e
...
3c7f61fb37
Author | SHA1 | Date |
---|---|---|
/techno | 3c7f61fb37 | |
slashtechno | b9013e051d | |
deepsource-autofix[bot] | 79ca52987b | |
slashtechno | 3a18a4536c | |
/techno | 263a970edb | |
deepsource-autofix[bot] | 7cad46cf8a | |
/techno | 0ec3e62c03 | |
/techno | bc94e67635 | |
deepsource-autofix[bot] | 4bb9c47e6c | |
deepsource-io[bot] | cdbd7dd369 |
|
@ -0,0 +1,13 @@
|
||||||
|
version = 1
|
||||||
|
|
||||||
|
[[analyzers]]
|
||||||
|
name = "python"
|
||||||
|
|
||||||
|
[analyzers.meta]
|
||||||
|
runtime_version = "3.x.x"
|
||||||
|
|
||||||
|
[[transformers]]
|
||||||
|
name = "black"
|
||||||
|
|
||||||
|
[[transformers]]
|
||||||
|
name = "isort"
|
|
@ -1,2 +1,2 @@
|
||||||
export CLOUDFLARE_ACCOUNT_ID=
|
CLOUDFLARE_ACCOUNT_ID=
|
||||||
export CLOUDFLARE_TOKEN=
|
CLOUDFLARE_TOKEN=
|
|
@ -8,3 +8,4 @@ tmp.py
|
||||||
.venv
|
.venv
|
||||||
dist/
|
dist/
|
||||||
.ruff_cache/
|
.ruff_cache/
|
||||||
|
hosts.txt
|
|
@ -1,4 +1,10 @@
|
||||||
{
|
{
|
||||||
"python.languageServer": "Pylance",
|
"python.languageServer": "Pylance", // Ruff is used for linting but Pylance still is useful for intellisense
|
||||||
"python.analysis.ignore": [ "*" ] // Ruff is used for linting but Pylance still is useful
|
"python.analysis.ignore": [
|
||||||
|
"*"
|
||||||
|
],
|
||||||
|
"python.analysis.exclude": [
|
||||||
|
"."
|
||||||
|
],
|
||||||
|
"python.linting.enabled": false // https://github.com/microsoft/vscode-python/wiki/Migration-to-Python-Tools-Extensions
|
||||||
}
|
}
|
||||||
|
|
46
README.md
46
README.md
|
@ -1,2 +1,46 @@
|
||||||
# Cloudflare Gateway Adblocking
|
# Cloudflare Gateway Adblocking
|
||||||
Serverless adblocking via Cloudflare Zero Trust Gateway
|
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`
|
|
@ -1,6 +1,6 @@
|
||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "cloudflare-gateway-adblocking"
|
name = "cloudflare-gateway-adblocking"
|
||||||
version = "0.1.9"
|
version = "0.1.0"
|
||||||
description = "Serverless adblocking via Cloudflare Zero Trust Gateway"
|
description = "Serverless adblocking via Cloudflare Zero Trust Gateway"
|
||||||
authors = ["slastechno <77907286+slashtechno@users.noreply.github.com>"]
|
authors = ["slastechno <77907286+slashtechno@users.noreply.github.com>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
# To run from the root project directory, run the following command:
|
# To run from the root project directory, run the following command:
|
||||||
# python -m src.__main__
|
# python -m src.__main__
|
||||||
from loguru import logger
|
# python -m src # also works because __main__ is the default module
|
||||||
from .utils import upload, delete, utils
|
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import dotenv
|
|
||||||
from sys import exit, stderr
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from sys import exit, stderr
|
||||||
|
|
||||||
|
import dotenv
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
from .utils import delete, upload, utils
|
||||||
|
|
||||||
TOKEN = None
|
TOKEN = None
|
||||||
ACCOUNT_ID = None
|
ACCOUNT_ID = None
|
||||||
|
@ -70,13 +72,13 @@ def main():
|
||||||
"--blocklists",
|
"--blocklists",
|
||||||
"-b",
|
"-b",
|
||||||
help="Either a blocklist hosts file or a directory containing blocklist hosts files", # noqa E501
|
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(
|
upload_parser.add_argument(
|
||||||
"--whitelists",
|
"--whitelists",
|
||||||
"-w",
|
"-w",
|
||||||
help="Either a whitelist hosts file or a directory containing whitelist hosts files", # noqa E501
|
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
|
# Add subcommand: delete
|
||||||
delete_parser = subparsers.add_parser(
|
delete_parser = subparsers.add_parser(
|
||||||
|
@ -102,8 +104,9 @@ def main():
|
||||||
try:
|
try:
|
||||||
args.func(args)
|
args.func(args)
|
||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
logger.debug(e)
|
logger.error("No subcommand specified")
|
||||||
argparser.print_help()
|
argparser.print_help()
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
def upload_to_cloudflare(args):
|
def upload_to_cloudflare(args):
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# This is a scriprt to undo the changes made by adblock-zerotrust.py
|
# This is a scriprt to undo the changes made by adblock-zerotrust.py
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from . import utils
|
from . import utils
|
||||||
|
|
||||||
|
|
||||||
|
@ -43,11 +44,14 @@ def delete_adblock_policy(policies: dict, account_id: str, token: str):
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
rules = utils.get_gateway_rules()
|
account_id = input("Enter your Cloudflare account ID: ")
|
||||||
delete_adblock_policy(rules)
|
token = input("Enter your Cloudflare API token: ")
|
||||||
lists = utils.get_lists()
|
|
||||||
|
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)
|
lists = utils.filter_adblock_lists(lists)
|
||||||
delete_adblock_list(lists)
|
delete_adblock_list(lists, account_id, token)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import requests
|
|
||||||
from . import utils
|
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
def get_blocklists(hosts_path: str = None):
|
from . import utils
|
||||||
|
|
||||||
|
|
||||||
|
def get_blocklists(hosts_path: str = "blocklists"):
|
||||||
blocklists = []
|
blocklists = []
|
||||||
hosts_path = pathlib.Path(hosts_path)
|
hosts_path = pathlib.Path(hosts_path)
|
||||||
if hosts_path.is_file():
|
if hosts_path.is_file():
|
||||||
|
@ -16,7 +18,7 @@ def get_blocklists(hosts_path: str = None):
|
||||||
return blocklists
|
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 file, convert it to a list.
|
||||||
# If whitelist is a directory, convert all files in it to a list and combine them.
|
# 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
|
# 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():
|
def main():
|
||||||
|
account_id = input("Enter your Cloudflare account ID: ")
|
||||||
|
token = input("Enter your Cloudflare API token: ")
|
||||||
|
|
||||||
blocklists = get_blocklists()
|
blocklists = get_blocklists()
|
||||||
blocklists = apply_whitelists(blocklists)
|
blocklists = apply_whitelists(blocklists)
|
||||||
lists = split_list(blocklists)
|
lists = split_list(blocklists)
|
||||||
upload_to_cloudflare(lists)
|
upload_to_cloudflare(lists, account_id, token)
|
||||||
cloud_lists = utils.get_lists()
|
cloud_lists = utils.get_lists(account_id, token)
|
||||||
cloud_lists = utils.filter_adblock_lists(cloud_lists)
|
cloud_lists = utils.filter_adblock_lists(cloud_lists)
|
||||||
create_dns_policy(cloud_lists)
|
create_dns_policy(cloud_lists, account_id, token)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import re
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
@ -53,23 +54,34 @@ class Config:
|
||||||
# Convert a hosts file to a simple hostname list
|
# Convert a hosts file to a simple hostname list
|
||||||
def convert_to_list(file: pathlib.Path) -> list:
|
def convert_to_list(file: pathlib.Path) -> list:
|
||||||
with open(file, "r") as f:
|
with open(file, "r") as f:
|
||||||
# Don't read commented lines; strip whitespace;
|
# Loop through the file and using regex, only get the domain names
|
||||||
# remove 127.0.0.1 from beginning of line;
|
# Remove the prefixed loopback domain and suffixed comments
|
||||||
# ignore lines with "localhost"; ignore lines with "::1";
|
# Remove any empty strings
|
||||||
# ignore newlines
|
loopback = [
|
||||||
hosts = [
|
"localhost",
|
||||||
i[10:].strip()
|
"::1",
|
||||||
for i in f.readlines()
|
"localhost.localdomain",
|
||||||
if not i.startswith("#") and "localhost" not in i and "::1" not in i
|
"broadcasthost",
|
||||||
|
"local",
|
||||||
|
"ip6-localhost",
|
||||||
|
"ip6-loopback",
|
||||||
|
"ip6-localnet",
|
||||||
|
"ip6-mcastprefix",
|
||||||
|
"ip6-allnodes",
|
||||||
|
"ip6-allrouters",
|
||||||
|
"ip6-allhosts",
|
||||||
|
"0.0.0.0",
|
||||||
]
|
]
|
||||||
# Equivalent to:
|
matches = [
|
||||||
# for x in f.readlines():
|
re.search(r"^(?:127\.0\.0\.1|0\.0\.0\.0|::1)\s+(.+?)(?:\s+#.+)?$", line)
|
||||||
# if not x.startswith('#') and 'localhost' not in x and '::1' not in x:
|
for line in f
|
||||||
# hosts.append(x[10:].strip())
|
]
|
||||||
|
hosts = [
|
||||||
# If there are any empty strings in the list, remove them
|
match.group(1)
|
||||||
# For some reason, whitelist seems to still be present
|
for match in matches
|
||||||
hosts = [i for i in hosts if i != ""]
|
if match and match.group(1) not in loopback
|
||||||
|
]
|
||||||
|
print(f"First 5 hosts: {hosts[:5]}")
|
||||||
return hosts
|
return hosts
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue