Compare commits
10 Commits
e3632a84fe
...
main
Author | SHA1 | Date | |
---|---|---|---|
157a0f7830 | |||
13379a3419 | |||
fedbe1e227 | |||
15ba895b04 | |||
7676d34a39 | |||
c8451688ca | |||
bb6634ffc1 | |||
1e589ba91a | |||
2340aea618 | |||
5f54c87cf2 |
17
Dockerfile
17
Dockerfile
@ -1,17 +1,14 @@
|
|||||||
FROM python:3.11-slim
|
FROM python:3.11-alpine
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends curl tzdata && rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
RUN pip install --no-cache-dir requests pyyaml schedule
|
|
||||||
|
|
||||||
ENV TZ=Europe/Paris
|
ENV TZ=Europe/Paris
|
||||||
|
|
||||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
RUN apk add --no-cache tzdata curl \
|
||||||
|
&& cp /usr/share/zoneinfo/$TZ /etc/localtime \
|
||||||
|
&& echo $TZ > /etc/timezone \
|
||||||
|
&& pip install --no-cache-dir requests pyyaml schedule
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY blocklist_scheduler.py /app/blocklist_scheduler.py
|
COPY blocklist_scheduler.py .
|
||||||
|
|
||||||
RUN chmod +x /app/blocklist_scheduler.py
|
ENTRYPOINT ["python3", "blocklist_scheduler.py"]
|
||||||
|
|
||||||
ENTRYPOINT ["python3", "/app/blocklist_scheduler.py"]
|
|
||||||
|
25
README.md
25
README.md
@ -1,13 +1,11 @@
|
|||||||
<h1 align="center"> Adguard CIDRE Sync</h1>
|
<h1 align="center"> Adguard CIDRE Sync</h1>
|
||||||
<div align="center">
|
|
||||||
<a href="https://discord.gg/gxffg3GA96">
|
|
||||||
<img src="https://img.shields.io/badge/JV%20hardware-rejoindre-green?style=flat-square&logo=discord&logoColor=%23fff" alt="JV Hardware">
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
**Adguard CIDRE Sync** - A bot to synchronize adguard clients disallow list with countries CIDR list of your choices.
|
**Adguard CIDRE Sync** - A bot to synchronize adguard clients disallow list with countries CIDR list of your choices.
|
||||||
|
|
||||||
*The code is partially generated by AI*
|
> [!NOTE]
|
||||||
|
>_The code was partially written and structured using a generative AI._
|
||||||
|
>
|
||||||
|
>_Github repo is a mirror of https://git.djeex.fr/Djeex/adguard-cidre. You'll find full package, history and release note there._
|
||||||
|
|
||||||
## Sommaire
|
## Sommaire
|
||||||
|
|
||||||
@ -20,7 +18,7 @@
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Downloads CIDR lists by country from GitHub
|
- Downloads CIDR lists by country from GitHub
|
||||||
- Adds manual IPs from a `manually_blocked_ips.conf` file
|
- (Optional) Adds manual IPs from a `manually_blocked_ips.conf` file
|
||||||
- Updates the `AdGuardHome.yaml` file by replacing the `disallowed_clients` list
|
- Updates the `AdGuardHome.yaml` file by replacing the `disallowed_clients` list
|
||||||
- Creates a backup of the original config (`AdGuardHome.yaml.first-start.bak`) on first run
|
- Creates a backup of the original config (`AdGuardHome.yaml.first-start.bak`) on first run
|
||||||
- Creates a backup before each update (`AdGuardHome.yaml.last-update.bak`)
|
- Creates a backup before each update (`AdGuardHome.yaml.last-update.bak`)
|
||||||
@ -34,7 +32,7 @@
|
|||||||
| Variable | Description | Example | Possible Values |
|
| Variable | Description | Example | Possible Values |
|
||||||
|--------------------------|--------------------------------------------------------------------------|-----------------------------|---------------------------------------------|
|
|--------------------------|--------------------------------------------------------------------------|-----------------------------|---------------------------------------------|
|
||||||
| `TZ` | Timezone of the container to correctly schedule updates | `Europe/Paris` | Any valid timezone (e.g., `UTC`, `America/New_York`, etc.) |
|
| `TZ` | Timezone of the container to correctly schedule updates | `Europe/Paris` | Any valid timezone (e.g., `UTC`, `America/New_York`, etc.) |
|
||||||
| `BLOCK_COUNTRIES` | List of country codes for CIDR lists, separated by commas | `cn,ru,ir` | ISO 2-letter country codes |
|
| `BLOCK_COUNTRIES` | List of country codes for CIDR lists, separated by commas. You can also define an exclude list (all countries except the specified ones) by prefixing each country code with !. Mixing inclusion and exclusion codes is not supported. | including list : `cn,ru,ir`, excluding list : `!cn,!ru,!ir` | ISO 2-letter country codes |
|
||||||
| `BLOCKLIST_CRON_TYPE` | Scheduling type: `daily` or `weekly` | `daily` | `daily`, `weekly` |
|
| `BLOCKLIST_CRON_TYPE` | Scheduling type: `daily` or `weekly` | `daily` | `daily`, `weekly` |
|
||||||
| `BLOCKLIST_CRON_TIME` | Time to run update in `HH:MM` 24-hour format | `06:00` | 24-hour time format |
|
| `BLOCKLIST_CRON_TIME` | Time to run update in `HH:MM` 24-hour format | `06:00` | 24-hour time format |
|
||||||
| `BLOCKLIST_CRON_DAY` | Day of the week for weekly schedule (e.g., `mon`, `tue`, etc.) | `mon` | `mon`, `tue`, `wed`, `thu`, `fri`, `sat`, `sun` |
|
| `BLOCKLIST_CRON_DAY` | Day of the week for weekly schedule (e.g., `mon`, `tue`, etc.) | `mon` | `mon`, `tue`, `wed`, `thu`, `fri`, `sat`, `sun` |
|
||||||
@ -47,11 +45,10 @@
|
|||||||
|
|
||||||
## File Structure
|
## File Structure
|
||||||
|
|
||||||
- `update-blocklist.sh`: Main script to download CIDRs, merge manual IPs, update config, and restart AdGuard.
|
- `blocklist_scheduler.py`: Script to backup, schedule, download CIDRs, merge manual IPs, update config, and restart AdGuard.
|
||||||
- `entrypoint.sh`: Sets up the cron job to periodically run the update script.
|
- `Dockerfile`: Builds the lightweight python3-slim image.
|
||||||
- `Dockerfile`: Builds the lightweight Alpine-based image.
|
|
||||||
- `docker-compose.yml`: Example compose file to run the container.
|
- `docker-compose.yml`: Example compose file to run the container.
|
||||||
- `manually_blocked_ips.conf`: (Volume mount) Add extra IPs to block manually.
|
- (optional) `manually_blocked_ips.conf`: Add extra IPs to block manually.
|
||||||
|
|
||||||
## Installation and Usage
|
## Installation and Usage
|
||||||
|
|
||||||
@ -96,7 +93,7 @@
|
|||||||
2. **Modify docker-compose.yml**
|
2. **Modify docker-compose.yml**
|
||||||
|
|
||||||
- Set `BLOCK_COUNTRIES` environment variable with the countries you want to block.
|
- Set `BLOCK_COUNTRIES` environment variable with the countries you want to block.
|
||||||
- Adjust `BLOCKLIST_CRON` if you want a different update frequency.
|
- Adjust `BLOCKLIST_CRON` variables if you want a different update frequency.
|
||||||
- Bind mount your adguard configuration folder (wich contains `AdGuardHome.yaml`) to `/adguard`
|
- Bind mount your adguard configuration folder (wich contains `AdGuardHome.yaml`) to `/adguard`
|
||||||
- (optionnally) create and edit `manually_blocked_ips.conf` file in your adguard configuration folder to add other IPs you want to block. Only valid IP or CIDR entries will be processed, for exemple :
|
- (optionnally) create and edit `manually_blocked_ips.conf` file in your adguard configuration folder to add other IPs you want to block. Only valid IP or CIDR entries will be processed, for exemple :
|
||||||
|
|
||||||
@ -127,7 +124,7 @@
|
|||||||
2. **Modify docker-compose.yml**
|
2. **Modify docker-compose.yml**
|
||||||
|
|
||||||
- Set `BLOCK_COUNTRIES` environment variable with the countries you want to block.
|
- Set `BLOCK_COUNTRIES` environment variable with the countries you want to block.
|
||||||
- Adjust `BLOCKLIST_CRON` if you want a different update frequency.
|
- Adjust `BLOCKLIST_CRON` variables if you want a different update frequency.
|
||||||
- Bind mount your adguard configuration folder (wich contains `AdGuardHome.yaml`) to `/adguard`
|
- Bind mount your adguard configuration folder (wich contains `AdGuardHome.yaml`) to `/adguard`
|
||||||
- (optionnally) create and edit `manually_blocked_ips.conf` file in your adguard configuration folder to add other IPs you want to block. Only valid IP or CIDR entries will be processed, for exemple :
|
- (optionnally) create and edit `manually_blocked_ips.conf` file in your adguard configuration folder to add other IPs you want to block. Only valid IP or CIDR entries will be processed, for exemple :
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import requests
|
|||||||
import yaml
|
import yaml
|
||||||
import schedule
|
import schedule
|
||||||
import time
|
import time
|
||||||
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@ -18,14 +19,15 @@ ADGUARD_YAML = Path("/adguard/AdGuardHome.yaml")
|
|||||||
TMP_YAML = ADGUARD_YAML.parent / (ADGUARD_YAML.name + ".tmp")
|
TMP_YAML = ADGUARD_YAML.parent / (ADGUARD_YAML.name + ".tmp")
|
||||||
MANUAL_IPS_FILE = Path("/adguard/manually_blocked_ips.conf")
|
MANUAL_IPS_FILE = Path("/adguard/manually_blocked_ips.conf")
|
||||||
CIDR_BASE_URL = "https://raw.githubusercontent.com/vulnebify/cidre/main/output/cidr/ipv4"
|
CIDR_BASE_URL = "https://raw.githubusercontent.com/vulnebify/cidre/main/output/cidr/ipv4"
|
||||||
|
COUNTRY_LIST_URL = "https://raw.githubusercontent.com/vulnebify/cidre/refs/heads/main/cidre/countries.py"
|
||||||
|
|
||||||
FIRST_BACKUP = ADGUARD_YAML.parent / "AdGuardHome.yaml.first-start.bak"
|
FIRST_BACKUP = ADGUARD_YAML.parent / "AdGuardHome.yaml.first-start.bak"
|
||||||
LAST_UPDATE_BACKUP = ADGUARD_YAML.parent / "AdGuardHome.yaml.last-update.bak"
|
LAST_UPDATE_BACKUP = ADGUARD_YAML.parent / "AdGuardHome.yaml.last-update.bak"
|
||||||
|
|
||||||
BLOCK_COUNTRIES = os.getenv("BLOCK_COUNTRIES", "")
|
BLOCK_COUNTRIES = os.getenv("BLOCK_COUNTRIES", "")
|
||||||
BLOCKLIST_CRON_TYPE = os.getenv("BLOCKLIST_CRON_TYPE", "daily").lower() # daily or weekly
|
BLOCKLIST_CRON_TYPE = os.getenv("BLOCKLIST_CRON_TYPE", "daily").lower()
|
||||||
BLOCKLIST_CRON_TIME = os.getenv("BLOCKLIST_CRON_TIME", "06:00") # HH:MM format
|
BLOCKLIST_CRON_TIME = os.getenv("BLOCKLIST_CRON_TIME", "06:00")
|
||||||
BLOCKLIST_CRON_DAY = os.getenv("BLOCKLIST_CRON_DAY", "mon").lower() # only if weekly
|
BLOCKLIST_CRON_DAY = os.getenv("BLOCKLIST_CRON_DAY", "mon").lower()
|
||||||
|
|
||||||
ADGUARD_CONTAINER_NAME = os.getenv("ADGUARD_CONTAINER_NAME", "adguardhome")
|
ADGUARD_CONTAINER_NAME = os.getenv("ADGUARD_CONTAINER_NAME", "adguardhome")
|
||||||
DOCKER_API_URL = os.getenv("DOCKER_API_URL", "http://socket-proxy-adguard:2375")
|
DOCKER_API_URL = os.getenv("DOCKER_API_URL", "http://socket-proxy-adguard:2375")
|
||||||
@ -41,6 +43,44 @@ def backup_last_update():
|
|||||||
logging.info(f"Creating last update backup: {LAST_UPDATE_BACKUP}")
|
logging.info(f"Creating last update backup: {LAST_UPDATE_BACKUP}")
|
||||||
LAST_UPDATE_BACKUP.write_text(ADGUARD_YAML.read_text())
|
LAST_UPDATE_BACKUP.write_text(ADGUARD_YAML.read_text())
|
||||||
|
|
||||||
|
def fetch_all_country_codes():
|
||||||
|
try:
|
||||||
|
resp = requests.get(COUNTRY_LIST_URL, timeout=15)
|
||||||
|
resp.raise_for_status()
|
||||||
|
matches = re.findall(r'"([A-Z]{2})"', resp.text)
|
||||||
|
return set(code.lower() for code in matches)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Failed to fetch available country codes: {e}")
|
||||||
|
return set()
|
||||||
|
|
||||||
|
def get_selected_countries():
|
||||||
|
if not BLOCK_COUNTRIES:
|
||||||
|
logging.error("BLOCK_COUNTRIES is not set. Skipping update.")
|
||||||
|
return []
|
||||||
|
|
||||||
|
raw_codes = [c.strip() for c in BLOCK_COUNTRIES.split(",") if c.strip()]
|
||||||
|
if not raw_codes:
|
||||||
|
logging.error("No valid country codes provided.")
|
||||||
|
return []
|
||||||
|
|
||||||
|
is_exclusion = all(c.startswith("!") for c in raw_codes)
|
||||||
|
is_inclusion = all(not c.startswith("!") for c in raw_codes)
|
||||||
|
|
||||||
|
if not (is_exclusion or is_inclusion):
|
||||||
|
logging.error("Mixed syntax in BLOCK_COUNTRIES. Use only inclusion (e.g. 'fr,de') or only exclusion (e.g. '!fr,!de').")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
available = fetch_all_country_codes()
|
||||||
|
selected = {c.lstrip("!") for c in raw_codes}
|
||||||
|
unknown = selected - available
|
||||||
|
if unknown:
|
||||||
|
logging.warning(f"Unknown country codes: {', '.join(sorted(unknown))}")
|
||||||
|
|
||||||
|
if is_exclusion:
|
||||||
|
return sorted(available - selected)
|
||||||
|
else:
|
||||||
|
return sorted(selected & available)
|
||||||
|
|
||||||
def download_cidr_lists(countries):
|
def download_cidr_lists(countries):
|
||||||
combined_ips = []
|
combined_ips = []
|
||||||
for code in countries:
|
for code in countries:
|
||||||
@ -76,12 +116,15 @@ def update_yaml_with_ips(ips):
|
|||||||
logging.error(f"{ADGUARD_YAML} does not exist. Cannot update.")
|
logging.error(f"{ADGUARD_YAML} does not exist. Cannot update.")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
data = None
|
try:
|
||||||
with ADGUARD_YAML.open() as f:
|
with ADGUARD_YAML.open() as f:
|
||||||
data = yaml.safe_load(f)
|
data = yaml.safe_load(f)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Failed to parse YAML file: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
if data is None:
|
if not isinstance(data, dict):
|
||||||
logging.error(f"Failed to parse YAML file {ADGUARD_YAML}")
|
logging.error("Invalid YAML format.")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
data['dns']['disallowed_clients'] = ips
|
data['dns']['disallowed_clients'] = ips
|
||||||
@ -106,12 +149,12 @@ def restart_adguard_container():
|
|||||||
logging.error(f"Error restarting container: {e}")
|
logging.error(f"Error restarting container: {e}")
|
||||||
|
|
||||||
def update_blocklist():
|
def update_blocklist():
|
||||||
if not BLOCK_COUNTRIES:
|
countries = get_selected_countries()
|
||||||
logging.error("No countries specified in BLOCK_COUNTRIES environment variable. Skipping update.")
|
if not countries:
|
||||||
|
logging.error("No valid countries to process. Skipping update.")
|
||||||
return
|
return
|
||||||
|
|
||||||
countries_list = [c.strip() for c in BLOCK_COUNTRIES.split(",") if c.strip()]
|
cidr_ips = download_cidr_lists(countries)
|
||||||
cidr_ips = download_cidr_lists(countries_list)
|
|
||||||
manual_ips = read_manual_ips()
|
manual_ips = read_manual_ips()
|
||||||
combined_ips = cidr_ips + manual_ips
|
combined_ips = cidr_ips + manual_ips
|
||||||
|
|
||||||
@ -132,7 +175,7 @@ def schedule_job():
|
|||||||
schedule.every().day.at(f"{hour:02d}:{minute:02d}").do(update_blocklist)
|
schedule.every().day.at(f"{hour:02d}:{minute:02d}").do(update_blocklist)
|
||||||
logging.info(f"Scheduled daily update at {hour:02d}:{minute:02d}")
|
logging.info(f"Scheduled daily update at {hour:02d}:{minute:02d}")
|
||||||
elif BLOCKLIST_CRON_TYPE == "weekly":
|
elif BLOCKLIST_CRON_TYPE == "weekly":
|
||||||
valid_days = ["mon","tue","wed","thu","fri","sat","sun"]
|
valid_days = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"]
|
||||||
day = BLOCKLIST_CRON_DAY[:3]
|
day = BLOCKLIST_CRON_DAY[:3]
|
||||||
if day not in valid_days:
|
if day not in valid_days:
|
||||||
logging.error(f"Invalid BLOCKLIST_CRON_DAY '{BLOCKLIST_CRON_DAY}', must be one of {valid_days}. Defaulting to Monday.")
|
logging.error(f"Invalid BLOCKLIST_CRON_DAY '{BLOCKLIST_CRON_DAY}', must be one of {valid_days}. Defaulting to Monday.")
|
||||||
@ -146,9 +189,7 @@ def schedule_job():
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
logging.info("Starting blocklist scheduler...")
|
logging.info("Starting blocklist scheduler...")
|
||||||
|
|
||||||
backup_first_start()
|
backup_first_start()
|
||||||
|
|
||||||
update_blocklist()
|
update_blocklist()
|
||||||
schedule_job()
|
schedule_job()
|
||||||
while True:
|
while True:
|
||||||
|
Reference in New Issue
Block a user