diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8e5e480 --- /dev/null +++ b/.gitignore @@ -0,0 +1,162 @@ +### Python ### + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/Readme.md b/Readme.md index a0d9a72..8a80575 100644 --- a/Readme.md +++ b/Readme.md @@ -1,48 +1,34 @@ -Этот скрипт предназначен для добавления статических маршрутов на роутер Keenetic с использованием нотации CIDR. Он загружает список CIDR-блоков с сайта antifilter.download, объединяет их с заранее определенным списком пользовательских CIDR-блоков и добавляет их в таблицу маршрутизации роутера для доступа к ресурсам через заранее настроенный VPN. Скрипт использует базовую аутентификацию для безопасного доступа к роутеру. +Этот скрипт предназначен для автоматического добавления статических маршрутов на роутер Keenetic. Он загружает список CIDR-блоков из указанного URL, объединяет их с предопределенным списком пользовательских CIDR-блоков и добавляет их в таблицу маршрутизации роутера через SSH-соединение. ## Требования -* Python 3.x -* Библиотеки urllib и base64 (входят в стандартную библиотеку) -* Переменные окружения для настройки +- Python 3.x + +## Установка + +Убедитесь, что у вас установлен Python 3.x. Если нет, скачайте и установите его с официального сайта Python. + +Установите необходимые библиотеки с помощью pip: + +```bash +pip install -r requirements.txt +``` + +Скачайте скрипт и сохраните его в файл, например update_routes.py. ## Переменные окружения Перед запуском скрипта убедитесь, что установлены следующие переменные окружения: -* KEENETIC_USERNAME: Имя пользователя для роутера Keenetic (по умолчанию: admin) -* KEENETIC_PASSWORD: Пароль для роутера Keenetic (обязательно) -* KEENETIC_HOST: IP-адрес роутера Keenetic (по умолчанию: 192.168.0.1) -* KEENETIC_INTERFACE: Сетевая интерфейс для использования (по умолчанию: Proxy0) +- KEENETIC_USERNAME: Имя пользователя для роутера Keenetic (по умолчанию: admin) +- KEENETIC_PASSWORD: Пароль для роутера Keenetic (обязательно) +- KEENETIC_HOST: IP-адрес роутера Keenetic (по умолчанию: 192.168.0.1) +- KEENETIC_PORT: SSH-порт роутера Keenetic (по умолчанию: 22) +- KEENETIC_INTERFACE: Интерфейс, через который отправлять трафик (по умолчанию: Wireguard0) ## Пользовательский список CIDR -Скрипт включает заранее определенный список CIDR-блоков для YouTube. Вы можете изменить этот список в переменной CUSTOM_CIDR_LIST. - -```python -CUSTOM_CIDR_LIST = [ - "64.18.0.0/20", - "64.233.160.0/19", - "66.102.0.0/20", - "66.249.80.0/20", - "72.14.192.0/18", - "74.125.0.0/16", - "173.194.0.0/16", - "207.126.144.0/20", - "209.85.128.0/17", - "216.58.208.0/20", - "216.239.32.0/19", - "213.0.0.0/8", -] -``` - -## Функции - -* cidr_to_netmask(cidr): Преобразует нотацию CIDR в маску подсети. -* fetch_cidr_list(url): Загружает список CIDR-блоков из указанного URL. -* add_route_to_keenetic(ip, netmask, gateway, username, password): Добавляет маршрут на роутер Keenetic. -* add_routes_to_keenetic(routes, gateway, username, password, max_retries=3): Добавляет несколько маршрутов с логикой повторных попыток. -* main(): Основная функция, которая управляет загрузкой и добавлением маршрутов. +Скрипт включает предопределенный список CIDR-блоков для YouTube. Вы можете изменить этот список в переменной CUSTOM_CIDR_LIST. ## Использование @@ -52,12 +38,26 @@ CUSTOM_CIDR_LIST = [ python3 update_routes.py ``` -## Пример +## Логика работы -Чтобы использовать скрипт, установите необходимые переменные окружения и запустите его. Скрипт выведет статус добавления каждого маршрута: +- Скрипт загружает список CIDR-блоков из указанного URL. +- Объединяет загруженный список с пользовательским списком CIDR-блоков. +- Преобразует CIDR-нотацию в IP-адреса и маски подсети. +- Устанавливает SSH-соединение с роутером Keenetic. +- Для каждого маршрута: + - Удаляет существующий маршрут (если есть). + - Добавляет новый маршрут. + - В случае ошибки "Channel closed" повторяет попытку выполнения команды до 3 раз. -```text -Маршрут 64.18.0.0 с маской 255.255.240.0 успешно добавлен. -Маршрут 64.233.160.0 с маской 255.255.224.0 успешно добавлен. -... -``` +## Обработка ошибок + +Скрипт включает обработку ошибок для следующих ситуаций: + +- Ошибки при загрузке CIDR-списка +- Ошибки при подключении к роутеру +- Ошибки при выполнении SSH-команд +- Повторные попытки при ошибке "Channel closed" + +## Примечание + +Убедитесь, что у вас есть необходимые права доступа к роутеру Keenetic для выполнения команд по SSH. Также проверьте, что указанный интерфейс KEENTIC_INTERFACE существует на вашем роутере. diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1ede316 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +requests +paramiko diff --git a/update_routes.py b/update_routes.py index 524ac21..de3a9d1 100755 --- a/update_routes.py +++ b/update_routes.py @@ -1,15 +1,12 @@ -#!/usr/bin/env python3 - -import urllib.request -import urllib.parse -import base64 -import time +import paramiko import os +import time USERNAME = os.getenv("KEENETIC_USERNAME", "admin") PASSWORD = os.getenv("KEENETIC_PASSWORD", None) KEENETIC_HOST = os.getenv("KEENETIC_HOST", "192.168.0.1") -INTERFACE = os.getenv("KEENETIC_INTERFACE", "Proxy0") +KEENETIC_PORT = os.getenv("KEENETIC_PORT", 22) +KEENTIC_INTERFACE = os.getenv("KEENTIC_INTERFACE", "Proxy0") CUSTOM_CIDR_LIST = [ # Youtube @@ -29,94 +26,83 @@ CUSTOM_CIDR_LIST = [ def cidr_to_netmask(cidr): - """Преобразует префикс CIDR в маску подсети.""" - try: - ip, prefix_length = cidr.split("/") - prefix_length = int(prefix_length) - - # Создаем маску подсети из префикса - mask = (0xFFFFFFFF >> (32 - prefix_length)) << (32 - prefix_length) - netmask = ".".join([str((mask >> (i * 8)) & 0xFF) for i in range(4)[::-1]]) - - return ip, netmask - except ValueError: - raise ValueError("Неверный формат CIDR") + ip, prefix_length = cidr.split("/") + prefix_length = int(prefix_length) + mask = (0xFFFFFFFF >> (32 - prefix_length)) << (32 - prefix_length) + netmask = ".".join([str((mask >> (i * 8)) & 0xFF) for i in range(4)[::-1]]) + return ip, netmask def fetch_cidr_list(url): - """Загружает список CIDR из указанного URL.""" - with urllib.request.urlopen(url) as response: - return response.read().decode().splitlines() + import requests + + response = requests.get(url) + response.raise_for_status() + return response.text.splitlines() -def add_route_to_keenetic(ip, netmask, gateway, username, password): - """Добавляет маршрут на роутер Keenetic с использованием GET-запроса.""" - url = f"http://{KEENETIC_HOST}/cgi-bin/luci/admin/network/routes" - params = {"ip": ip, "mask": netmask, "gateway": gateway, "interface": INTERFACE} +def execute_command(ssh, command, retries=3): + attempt = 0 + while attempt < retries: + try: + stdin, stdout, stderr = ssh.exec_command(command) + error = stderr.read() + if error: + print(f"Ошибка: {error.decode()}") + return stdout.read().decode() + except paramiko.SSHException as e: + if "Channel closed" in str(e): + print(f"Channel closed detected. Retrying... ({attempt + 1}/{retries})") + attempt += 1 + time.sleep(1) + else: + raise e + raise Exception(f"Failed to execute command after {retries} attempts: {command}") - # Кодируем параметры для URL - query_string = urllib.parse.urlencode(params) - full_url = f"{url}?{query_string}" - # Создаем запрос - request = urllib.request.Request(full_url) - request.add_header("Content-Type", "application/x-www-form-urlencoded") +def add_routes_via_ssh(routes): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - # Аутентификация - credentials = f"{username}:{password}" - base64_credentials = base64.b64encode(credentials.encode()).decode() - request.add_header("Authorization", f"Basic {base64_credentials}") - - # Отправка запроса try: - with urllib.request.urlopen(request) as response: - print(f"Маршрут {ip} с маской {netmask} успешно добавлен.") - return True + print("Connecting to Keenetic") + ssh.connect( + KEENETIC_HOST, port=KEENETIC_PORT, username=USERNAME, password=PASSWORD + ) + print("Connected") + + for ip, netmask in routes: + print(f"Добавление маршрута: {ip} {netmask}") + command = f"no ip route {ip} {netmask}" + execute_command(ssh, command) + command = f"ip route {ip} {netmask} {KEENTIC_INTERFACE}" + execute_command(ssh, command) + + print("Все маршруты добавлены.") except Exception as e: - print(f"Ошибка при добавлении маршрута {ip}: {e}") - return False - - -def add_routes_to_keenetic(routes, gateway, username, password, max_retries=3): - """Добавляет маршруты на роутер Keenetic с повторными попытками.""" - for route in routes: - ip, netmask = route - success = False - attempts = 0 - - while not success and attempts < max_retries: - success = add_route_to_keenetic(ip, netmask, gateway, username, password) - if not success: - attempts += 1 - print(f"Попытка {attempts} для маршрута {ip}.") - time.sleep(2) # Задержка перед повторной попыткой + print(f"Ошибка при подключении или выполнении команд: {e}") + finally: + ssh.close() def main(): url = "https://antifilter.download/list/allyouneed.lst" - if PASSWORD is None: - print( - "Для использования данного скрипта необходимо установить переменные окружения KEENETIC_USERNAME и KEENETIC_PASSWORD." - ) - return try: cidr_list = fetch_cidr_list(url) cidr_list.extend(CUSTOM_CIDR_LIST) + routes_to_add = [] - # Генерируем маршруты for cidr in cidr_list: - if cidr.strip(): # Проверяем, что строка не пустая + if cidr.strip(): ip, netmask = cidr_to_netmask(cidr.strip()) routes_to_add.append((ip, netmask)) - # Добавляем маршруты пачками - add_routes_to_keenetic(routes_to_add, KEENETIC_HOST, USERNAME, PASSWORD) + add_routes_via_ssh(routes_to_add) except Exception as e: print(f"Ошибка: {e}") -# Пример использования if __name__ == "__main__": main()