Compare commits
No commits in common. "3c7f61fb37b0e979494277934cbb35129a9331ed" and "df2a598c9e61d44b87826e538410d61f2d51064a" have entirely different histories.
3c7f61fb37
...
df2a598c9e
|
@ -1,13 +0,0 @@
|
||||||
version = 1
|
|
||||||
|
|
||||||
[[analyzers]]
|
|
||||||
name = "python"
|
|
||||||
|
|
||||||
[analyzers.meta]
|
|
||||||
runtime_version = "3.x.x"
|
|
||||||
|
|
||||||
[[transformers]]
|
|
||||||
name = "black"
|
|
||||||
|
|
||||||
[[transformers]]
|
|
||||||
name = "isort"
|
|
|
@ -1,2 +1,2 @@
|
||||||
CLOUDFLARE_ACCOUNT_ID=
|
export CLOUDFLARE_ACCOUNT_ID=
|
||||||
CLOUDFLARE_TOKEN=
|
export CLOUDFLARE_TOKEN=
|
|
@ -8,4 +8,3 @@ tmp.py
|
||||||
.venv
|
.venv
|
||||||
dist/
|
dist/
|
||||||
.ruff_cache/
|
.ruff_cache/
|
||||||
hosts.txt
|
|
|
@ -1,10 +1,4 @@
|
||||||
{
|
{
|
||||||
"python.languageServer": "Pylance", // Ruff is used for linting but Pylance still is useful for intellisense
|
"python.languageServer": "Pylance",
|
||||||
"python.analysis.ignore": [
|
"python.analysis.ignore": [ "*" ] // Ruff is used for linting but Pylance still is useful
|
||||||
"*"
|
|
||||||
],
|
|
||||||
"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,46 +1,2 @@
|
||||||
# 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.0"
|
version = "0.1.9"
|
||||||
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,15 +1,13 @@
|
||||||
# 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__
|
||||||
# python -m src # also works because __main__ is the default module
|
from loguru import logger
|
||||||
|
from .utils import upload, delete, utils
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
|
||||||
from sys import exit, stderr
|
|
||||||
|
|
||||||
import dotenv
|
import dotenv
|
||||||
from loguru import logger
|
from sys import exit, stderr
|
||||||
|
from pathlib import Path
|
||||||
from .utils import delete, upload, utils
|
|
||||||
|
|
||||||
TOKEN = None
|
TOKEN = None
|
||||||
ACCOUNT_ID = None
|
ACCOUNT_ID = None
|
||||||
|
@ -72,13 +70,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", # Not really needed because the get_blocklists function will default to this # noqa: E501
|
default="blocklists",
|
||||||
)
|
)
|
||||||
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", # Not really needed because the apply_whitelists function will default to this # noqa: E501
|
default="whitelist.txt", # Need to change this so it's optional
|
||||||
)
|
)
|
||||||
# Add subcommand: delete
|
# Add subcommand: delete
|
||||||
delete_parser = subparsers.add_parser(
|
delete_parser = subparsers.add_parser(
|
||||||
|
@ -104,9 +102,8 @@ def main():
|
||||||
try:
|
try:
|
||||||
args.func(args)
|
args.func(args)
|
||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
logger.error("No subcommand specified")
|
logger.debug(e)
|
||||||
argparser.print_help()
|
argparser.print_help()
|
||||||
exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def upload_to_cloudflare(args):
|
def upload_to_cloudflare(args):
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# 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
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,14 +43,11 @@ def delete_adblock_policy(policies: dict, account_id: str, token: str):
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
account_id = input("Enter your Cloudflare account ID: ")
|
rules = utils.get_gateway_rules()
|
||||||
token = input("Enter your Cloudflare API token: ")
|
delete_adblock_policy(rules)
|
||||||
|
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, account_id, token)
|
delete_adblock_list(lists)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
|
import requests
|
||||||
|
from . import utils
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
import requests
|
|
||||||
|
|
||||||
from . import utils
|
def get_blocklists(hosts_path: str = None):
|
||||||
|
|
||||||
|
|
||||||
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():
|
||||||
|
@ -18,7 +16,7 @@ def get_blocklists(hosts_path: str = "blocklists"):
|
||||||
return blocklists
|
return blocklists
|
||||||
|
|
||||||
|
|
||||||
def apply_whitelists(blocklists, whitelist: str = "whitelists"):
|
def apply_whitelists(blocklists, whitelist: str = None):
|
||||||
# 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
|
||||||
|
@ -102,16 +100,13 @@ 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, account_id, token)
|
upload_to_cloudflare(lists)
|
||||||
cloud_lists = utils.get_lists(account_id, token)
|
cloud_lists = utils.get_lists()
|
||||||
cloud_lists = utils.filter_adblock_lists(cloud_lists)
|
cloud_lists = utils.filter_adblock_lists(cloud_lists)
|
||||||
create_dns_policy(cloud_lists, account_id, token)
|
create_dns_policy(cloud_lists)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import pathlib
|
import pathlib
|
||||||
import re
|
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
@ -54,34 +53,23 @@ 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:
|
||||||
# Loop through the file and using regex, only get the domain names
|
# Don't read commented lines; strip whitespace;
|
||||||
# Remove the prefixed loopback domain and suffixed comments
|
# remove 127.0.0.1 from beginning of line;
|
||||||
# Remove any empty strings
|
# ignore lines with "localhost"; ignore lines with "::1";
|
||||||
loopback = [
|
# ignore newlines
|
||||||
"localhost",
|
|
||||||
"::1",
|
|
||||||
"localhost.localdomain",
|
|
||||||
"broadcasthost",
|
|
||||||
"local",
|
|
||||||
"ip6-localhost",
|
|
||||||
"ip6-loopback",
|
|
||||||
"ip6-localnet",
|
|
||||||
"ip6-mcastprefix",
|
|
||||||
"ip6-allnodes",
|
|
||||||
"ip6-allrouters",
|
|
||||||
"ip6-allhosts",
|
|
||||||
"0.0.0.0",
|
|
||||||
]
|
|
||||||
matches = [
|
|
||||||
re.search(r"^(?:127\.0\.0\.1|0\.0\.0\.0|::1)\s+(.+?)(?:\s+#.+)?$", line)
|
|
||||||
for line in f
|
|
||||||
]
|
|
||||||
hosts = [
|
hosts = [
|
||||||
match.group(1)
|
i[10:].strip()
|
||||||
for match in matches
|
for i in f.readlines()
|
||||||
if match and match.group(1) not in loopback
|
if not i.startswith("#") and "localhost" not in i and "::1" not in i
|
||||||
]
|
]
|
||||||
print(f"First 5 hosts: {hosts[:5]}")
|
# 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 != ""]
|
||||||
return hosts
|
return hosts
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue