From 5afe9c1601028db60ad1f7953af8f4b0d8b11466 Mon Sep 17 00:00:00 2001 From: Djeex Date: Sat, 31 May 2025 19:46:12 +0000 Subject: [PATCH] First python commit --- Dockerfile | 23 +++++-- docker-compose.yml | 1 + entrypoint.py | 61 +++++++++++++++++ entrypoint.sh | 14 ---- update-blocklist.py | 156 ++++++++++++++++++++++++++++++++++++++++++++ update-blocklist.sh | 75 --------------------- 6 files changed, 235 insertions(+), 95 deletions(-) create mode 100644 entrypoint.py delete mode 100644 entrypoint.sh create mode 100644 update-blocklist.py delete mode 100644 update-blocklist.sh diff --git a/Dockerfile b/Dockerfile index 05db38b..fb42004 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,21 @@ -FROM alpine:latest +FROM python:3.11-slim -RUN apk add --no-cache curl bash busybox tzdata +# Install curl and cron +RUN apt-get update && apt-get install -y curl cron && rm -rf /var/lib/apt/lists/* -COPY update-blocklist.sh /usr/local/bin/update-blocklist.sh -COPY entrypoint.sh /entrypoint.sh +# Install Python requests +RUN pip install --no-cache-dir requests -RUN chmod +x /usr/local/bin/update-blocklist.sh /entrypoint.sh +# Create adguard config dir +RUN mkdir -p /adguard -ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file +# Copy update-blocklist script +COPY update-blocklist.py /usr/local/bin/update-blocklist.py +RUN chmod +x /usr/local/bin/update-blocklist.py + +# Copy entrypoint script (on next step) + +# Setup cron config dir +RUN mkdir -p /etc/crontabs + +ENTRYPOINT ["/usr/local/bin/entrypoint.py"] diff --git a/docker-compose.yml b/docker-compose.yml index f56559c..1594fa8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,6 +3,7 @@ services: adguard-cidre: build: . container_name: adguard-cidre + restart: unless-stopped environment: - TZ=Europe/Paris # change to your timezone - BLOCK_COUNTRIES=cn,ru,ir # choose countries listed IP to block. Full lists here https://github.com/vulnebify/cidre/tree/main/output/cidr/ipv4 diff --git a/entrypoint.py b/entrypoint.py new file mode 100644 index 0000000..7116c05 --- /dev/null +++ b/entrypoint.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +import os +import sys +import subprocess +import logging +from pathlib import Path + +logging.basicConfig( + level=logging.INFO, + format='[entrypoint] %(message)s', + stream=sys.stdout +) + +ADGUARD_YAML = Path("/adguard/AdGuardHome.yaml") +FIRST_BACKUP = Path("/adguard/AdGuardHome.yaml.first-start.bak") + +def backup_first_start(): + if not FIRST_BACKUP.exists(): + logging.info("Creating first start backup...") + FIRST_BACKUP.write_text(ADGUARD_YAML.read_text()) + else: + logging.info("First start backup already exists.") + +def run_initial_update(): + logging.info("Running initial update-blocklist.py script...") + try: + subprocess.run( + ["/usr/local/bin/update-blocklist.py"], + check=True, + stdout=sys.stdout, + stderr=sys.stderr, + ) + except subprocess.CalledProcessError as e: + logging.error(f"Initial update script failed: {e}") + sys.exit(1) + +def setup_cron(): + cron_expr = os.getenv("BLOCKLIST_CRON", "0 6 * * *") + cron_line = f"{cron_expr} root /usr/local/bin/update-blocklist.py\n" + cron_file = "/etc/crontabs/root" + logging.info(f"Setting cron job: {cron_line.strip()}") + with open(cron_file, "w") as f: + f.write(cron_line) + +def start_cron_foreground(): + logging.info("Starting cron in foreground...") + os.execvp("crond", ["crond", "-f"]) + +def main(): + # Check AdGuardHome.yaml exists + if not ADGUARD_YAML.exists(): + logging.error(f"{ADGUARD_YAML} not found. Exiting.") + sys.exit(1) + + backup_first_start() + run_initial_update() + setup_cron() + start_cron_foreground() + +if __name__ == "__main__": + main() diff --git a/entrypoint.sh b/entrypoint.sh deleted file mode 100644 index a4a93fa..0000000 --- a/entrypoint.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh -set -e - -if [ -n "$TZ" ]; then - if [ -f "/usr/share/zoneinfo/$TZ" ]; then - cp "/usr/share/zoneinfo/$TZ" /etc/localtime - echo "$TZ" > /etc/timezone - fi -fi - -CRON_EXPR="${BLOCKLIST_CRON:-"0 6 * * *"}" -echo "$CRON_EXPR /usr/local/bin/update-blocklist.sh" > /etc/crontabs/root - -exec crond -f -c /etc/crontabs \ No newline at end of file diff --git a/update-blocklist.py b/update-blocklist.py new file mode 100644 index 0000000..79f5a60 --- /dev/null +++ b/update-blocklist.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 +import os +import sys +import shutil +import logging +import re +import requests +from pathlib import Path + +logging.basicConfig( + level=logging.INFO, + format='[update-blocklist] %(levelname)s: %(message)s', + stream=sys.stdout +) + +# Config / variables +ADGUARD_YAML = Path("/adguard/AdGuardHome.yaml") +FIRST_BACKUP = Path("/adguard/AdGuardHome.yaml.first-start.bak") +LAST_CRON_BACKUP = Path("/adguard/AdGuardHome.yaml.last-cron.bak") +MANUAL_IPS_FILE = Path("/adguard/manually_blocked_ips.conf") +CIDR_BASE_URL = "https://raw.githubusercontent.com/vulnebify/cidre/main/output/cidr/ipv4" +COUNTRIES = os.getenv("BLOCK_COUNTRIES", "") +DOCKER_API_URL = os.getenv("DOCKER_API_URL", "http://socket-proxy-adguard:2375") +CONTAINER_NAME = os.getenv("ADGUARD_CONTAINER_NAME", "adguard-home") +TMP_YAML = Path("/tmp/AdGuardHome.yaml") +TMP_DIR = Path("/tmp/cidr") + +def backup_first_start(): + if not FIRST_BACKUP.exists(): + logging.info(f"Creating first-start backup: {FIRST_BACKUP}") + shutil.copy2(ADGUARD_YAML, FIRST_BACKUP) + else: + logging.info("First-start backup already exists, skipping.") + +def backup_last_cron(): + logging.info(f"Creating last-cron backup: {LAST_CRON_BACKUP}") + shutil.copy2(ADGUARD_YAML, LAST_CRON_BACKUP) + +def download_cidr_lists(countries): + if not countries: + logging.error("No countries specified in BLOCK_COUNTRIES environment variable.") + sys.exit(1) + + TMP_DIR.mkdir(parents=True, exist_ok=True) + all_ips = [] + + codes = [c.strip().lower() for c in countries.split(",") if c.strip()] + for code in codes: + url = f"{CIDR_BASE_URL}/{code}.cidr" + logging.info(f"Downloading CIDR list for {code} from {url}") + try: + r = requests.get(url, timeout=15) + r.raise_for_status() + lines = r.text.strip().splitlines() + logging.info(f"Downloaded {len(lines)} CIDR entries for {code}") + all_ips.extend(lines) + except Exception as e: + logging.warning(f"Failed to download {url}: {e}") + + return all_ips + +def read_manual_ips(): + ips = [] + if MANUAL_IPS_FILE.exists(): + logging.info(f"Reading manual IPs from {MANUAL_IPS_FILE}") + try: + with MANUAL_IPS_FILE.open() as f: + for line in f: + line = line.strip() + if re.match(r'^(\d{1,3}\.){3}\d{1,3}(/\d{1,2})?$', line): + ips.append(line) + else: + logging.debug(f"Ignoring invalid manual IP line: {line}") + logging.info(f"Read {len(ips)} valid manual IP entries") + except Exception as e: + logging.warning(f"Error reading manual IPs: {e}") + else: + logging.info("Manual IPs file does not exist, skipping.") + return ips + +def format_ips_yaml_list(ips): + return [f" - {ip}\n" for ip in ips] + +def update_yaml_with_ips(ips): + if not ADGUARD_YAML.exists(): + logging.error(f"AdGuardHome.yaml not found at {ADGUARD_YAML}") + sys.exit(1) + + with ADGUARD_YAML.open() as f: + lines = f.readlines() + + new_lines = [] + inside_disallowed = False + ips_inserted = False + + for line in lines: + stripped = line.rstrip("\n") + + if stripped.startswith(" disallowed_clients:"): + # Write key line without any value (no [] etc) + new_lines.append(" disallowed_clients:\n") + # Insert ips + if ips: + new_lines.extend(format_ips_yaml_list(ips)) + # mark inserted + inside_disallowed = True + ips_inserted = True + continue + + if inside_disallowed: + # skip old IP entries starting with ' - ' + if stripped.startswith(" - "): + continue + else: + inside_disallowed = False + + new_lines.append(line) + + if not ips_inserted: + # disallowed_clients not found - append at end + new_lines.append("\n disallowed_clients:\n") + if ips: + new_lines.extend(format_ips_yaml_list(ips)) + + with TMP_YAML.open("w") as f: + f.writelines(new_lines) + + TMP_YAML.replace(ADGUARD_YAML) + logging.info(f"Updated {ADGUARD_YAML} with {len(ips)} disallowed_clients entries") + +def restart_container(): + url = f"{DOCKER_API_URL}/containers/{CONTAINER_NAME}/restart" + logging.info(f"Restarting container '{CONTAINER_NAME}' via {url}") + try: + r = requests.post(url, timeout=10) + if r.status_code == 204: + logging.info("Container restarted successfully.") + else: + logging.error(f"Failed to restart container. Status: {r.status_code} Response: {r.text}") + except Exception as e: + logging.error(f"Exception during container restart: {e}") + +def main(): + backup_first_start() + backup_last_cron() + cidr_ips = download_cidr_lists(COUNTRIES) + manual_ips = read_manual_ips() + combined_ips = cidr_ips + manual_ips + if not combined_ips: + logging.warning("No IPs to add to disallowed_clients. The list will be empty.") + update_yaml_with_ips(combined_ips) + restart_container() + logging.info("Blocklist update complete.") + +if __name__ == "__main__": + main() diff --git a/update-blocklist.sh b/update-blocklist.sh deleted file mode 100644 index cdddeb1..0000000 --- a/update-blocklist.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/bash - -set -e - -ADGUARD_YAML="/adguard/AdGuardHome.yaml" -TMP_YAML="/tmp/AdGuardHome.yaml" -MANUAL_IPS_FILE="/adguard/manually_blocked_ips.conf" -CIDR_BASE_URL="https://raw.githubusercontent.com/vulnebify/cidre/main/output/cidr/ipv4" -COUNTRIES=${BLOCK_COUNTRIES:-""} -DOCKER_API_URL=${DOCKER_API_URL:-"http://socket-proxy-adguard:2375"} -CONTAINER_NAME=${ADGUARD_CONTAINER_NAME:-"adguard-home"} - -if [ -z "$COUNTRIES" ]; then - echo "No countries specified in BLOCK_COUNTRIES." - exit 1 -fi - -mkdir -p /tmp/cidr -> /tmp/cidr/all.txt - -IFS=',' read -ra CODES <<< "$COUNTRIES" -for CODE in "${CODES[@]}"; do - echo "Downloading CIDR list for $CODE..." - curl -sf "$CIDR_BASE_URL/${CODE,,}.cidr" -o "/tmp/cidr/${CODE}.cidr" || continue - cat "/tmp/cidr/${CODE}.cidr" >> /tmp/cidr/all.txt -done - -if [ -f "$MANUAL_IPS_FILE" ]; then - echo "Validating and adding manually blocked IPs from $MANUAL_IPS_FILE..." - grep -E '^([0-9]{1,3}\.){3}[0-9]{1,3}(/[0-9]{1,2})?$' "$MANUAL_IPS_FILE" >> /tmp/cidr/all.txt -fi - -# Format IPs as YAML list items -sed 's/^/ - /' /tmp/cidr/all.txt > /tmp/cidr/ips_formatted.txt - -awk ' -BEGIN { - # Read formatted IPs into array - while ((getline line < "/tmp/cidr/ips_formatted.txt") > 0) { - ips[++count] = line - } - close("/tmp/cidr/ips_formatted.txt") - inside=0 -} - -/^ disallowed_clients:/ { - print - inside=1 - next -} - -/^ [^ ]/ && inside==1 { - # Insert all IPs here - for (i=1; i<=count; i++) print ips[i] - inside=0 -} - -{ - if (!inside) print -} - -END { - # If file ended while still inside disallowed_clients section - if (inside==1) { - for (i=1; i<=count; i++) print ips[i] - } -} -' "$ADGUARD_YAML" > "$TMP_YAML" - -mv "$TMP_YAML" "$ADGUARD_YAML" - -echo "Restarting $CONTAINER_NAME container..." -curl -s -X POST "$DOCKER_API_URL/containers/$CONTAINER_NAME/restart" -o /dev/null - -echo "Done."