diff --git a/README.md b/README.md index fce9b8f..1d9dcfa 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,16 @@ ## Возможности -- Читает список доменов из YAML-файла +- Читает список доменов из текстового файла (по одному домену на строку) - Разрешает домены в IPv4-адреса через DNS -- Генерирует конфигурацию для BIRD BGP с network statements в формате `/24 reject` +- Генерирует файл с IP-адресами в формате `route 185.76.151.0/24 reject;` +- Генерирует конфигурацию для BIRD BGP с загруженными маршрутами - Поддерживает настраиваемые параметры BGP - Логирование процессов с различными уровнями детализации ## Требования - Python 3.9+ -- Модуль `pyyaml` (`pip install pyyaml`) - BIRD BGP (установлен на целевой системе) ## Установка @@ -24,18 +24,13 @@ git clone cd domain-routing-bird ``` -2. Установите зависимости: -```bash -pip install pyyaml -``` - -3. Скопируйте примеры конфигурационных файлов: +2. Скопируйте примеры конфигурационных файлов: ```bash cp config.example.yaml config.yaml -cp domains.example.yaml domains.yaml +cp domains.example.txt domains.txt ``` -4. Настройте `config.yaml` и `domains.yaml` под ваши нужды +3. Настройте `config.yaml` и `domains.txt` под ваши нужды ## Использование @@ -64,12 +59,15 @@ python3 domain_routing.py --help ### Конфигурационный файл (config.yaml) ```yaml -# Путь к файлу со списком доменов -domains_file: domains.yaml +# Путь к файлу со списком доменов (текстовый файл, по одному домену на строку) +domains_file: domains.txt # Путь к выходному конфигурационному файлу BIRD output_file: /etc/bird/bird.conf +# Путь к файлу с маршрутами (выходной файл с route statements) +routes_file: routes.conf + # DNS-сервер для разрешения доменов (опционально) # dns_server: 8.8.8.8 @@ -82,24 +80,38 @@ log_level: INFO # protocol_name: domain_routing ``` -### Список доменов (domains.yaml) +### Список доменов (domains.txt) -```yaml +``` # Список доменов для domain-based routing # Каждый домен будет разрешен в IPv4-адреса и добавлен в BGP-конфигурацию # Основные домены -- example.com -- test.com +example.com +test.com -# Домены с комментариями -# - corporate.example.com # Корпоративный домен -# - api.example.com # API домен +# Домены с комментариями (комментарии начинаются с #) +# corporate.example.com # Корпоративный домен +# api.example.com # API домен ``` -## Формат выходной конфигурации +## Формат выходных файлов -Скрипт генерирует конфигурацию в следующем формате: +### Файл с маршрутами (routes.conf) + +Скрипт генерирует файл с маршрутами в следующем формате: + +``` +# Generated by domain-routing-bird +# Generated at: 2026-07-03T12:00:00.000000 + +route 185.76.151.0/24 reject; +route 192.168.1.0/24 reject; +``` + +### Конфигурация BIRD (bird.conf) + +Объединяет основную конфигурацию BIRD с загруженными маршрутами: ``` # Generated by domain-routing-bird @@ -110,8 +122,8 @@ protocol bgp domain_routing { export all; neighbor 192.168.1.1 as 65000; - network 1.2.3.0/24 reject; - network 4.5.6.0/24 reject; + route 185.76.151.0/24 reject; + route 192.168.1.0/24 reject; } ``` @@ -119,7 +131,7 @@ protocol bgp domain_routing { После генерации конфигурации: -1. Проверьте сгенерированный файл (по умолчанию `/etc/bird/bird.conf`) +1. Проверьте сгенерированные файлы (`routes.conf` и `/etc/bird/bird.conf`) 2. Для применения изменений выполните: ```bash sudo birdc configure @@ -134,13 +146,16 @@ sudo systemctl restart bird ### Пример 1: Базовая настройка -1. Создайте `config.yaml` и `domains.yaml` из примеров +1. Создайте `config.yaml` и `domains.txt` из примеров 2. Запустите скрипт: ```bash python3 domain_routing.py ``` -3. Проверьте сгенерированный файл `/etc/bird/bird.conf` +3. Проверьте сгенерированные файлы: + - `routes.conf` - файл с маршрутами + - `/etc/bird/bird.conf` - конфигурация BIRD + 4. Примените изменения: ```bash sudo birdc configure @@ -150,19 +165,34 @@ sudo birdc configure 1. Отредактируйте `config.yaml`: ```yaml -domains_file: my_domains.yaml +domains_file: my_domains.txt output_file: /etc/bird/bird.conf +routes_file: my_routes.conf neighbor_ip: 10.0.0.1 neighbor_as: 65100 protocol_name: custom_routing ``` -2. Создайте `my_domains.yaml` со своими доменами +2. Создайте `my_domains.txt` со своими доменами 3. Запустите скрипт: ```bash python3 domain_routing.py -c config.yaml ``` +### Пример 3: Кастомный файл с маршрутами + +1. Отредактируйте `config.yaml`: +```yaml +routes_file: /etc/bird/routes.conf +``` + +2. Запустите скрипт: +```bash +python3 domain_routing.py +``` + +Скрипт создаст файл `/etc/bird/routes.conf` с маршрутами и обновит основную конфигурацию BIRD. + ## Troubleshooting ### Проблема: "Файл не найден" @@ -185,7 +215,7 @@ python3 domain_routing.py -c config.yaml - Убедитесь, что список доменов содержит только доверенные домены - Рассмотрите использование DNSSEC для защиты от DNS spoofing - Ограничьте права доступа к конфигурационным файлам -- Используйте非-стандартные BGP-параметры в продакшене +- Используйте не-стандартные BGP-параметры в продакшене ## Лицензия diff --git a/config.example.yaml b/config.example.yaml index 2ed7243..5bbd61b 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -1,12 +1,15 @@ # Пример конфигурации для domain_routing.py -# Путь к файлу со списком доменов -domains_file: domains.yaml +# Путь к файлу со списком доменов (текстовый файл, по одному домену на строку) +domains_file: domains.txt # Путь к выходному конфигурационному файлу BIRD output_file: /etc/bird/bird.conf -# DNS-сервер для разрешения доменов (опционально, по умолчанию используется системный DNS) +# Путь к файлу с маршрутами (выходной файл с route statements) +routes_file: routes.conf + +# DNS-сервер для разрешения доменов (опционально) # dns_server: 8.8.8.8 # Уровень логирования: DEBUG, INFO, WARNING, ERROR diff --git a/config.yaml b/config.yaml index 90061a2..5bbd61b 100644 --- a/config.yaml +++ b/config.yaml @@ -1,12 +1,14 @@ -# config.yaml - Пример конфигурационного файла -# Скопируйте этот файл в config.yaml и настройте под ваши нужды +# Пример конфигурации для domain_routing.py -# Путь к файлу со списком доменов -domains_file: domains.yaml +# Путь к файлу со списком доменов (текстовый файл, по одному домену на строку) +domains_file: domains.txt # Путь к выходному конфигурационному файлу BIRD output_file: /etc/bird/bird.conf +# Путь к файлу с маршрутами (выходной файл с route statements) +routes_file: routes.conf + # DNS-сервер для разрешения доменов (опционально) # dns_server: 8.8.8.8 diff --git a/domain_routing.py b/domain_routing.py old mode 100644 new mode 100755 index 6f62ab4..8fa30bb --- a/domain_routing.py +++ b/domain_routing.py @@ -2,8 +2,11 @@ """ Domain-based Routing Script for BIRD BGP -Этот скрипт читает список доменов из YAML-файла, разрешает их в IPv4-адреса -и генерирует конфигурацию для BIRD BGP с network statements в формате /24 reject. +Этот скрипт читает список доменов из текстового файла, разрешает их в IPv4-адреса, +создает файл с IP-адресами и генерирует BGP-конфигурацию для BIRD. + +Использует формат: + route 185.76.151.0/24 reject; Автор: GigaCode """ @@ -15,9 +18,6 @@ import sys from pathlib import Path from typing import List, Optional -import yaml - - # Настройка логирования logging.basicConfig( level=logging.INFO, @@ -38,20 +38,26 @@ def load_config(config_path: str) -> dict: """ try: with open(config_path, 'r') as f: - config = yaml.safe_load(f) + config = {} + for line in f: + line = line.strip() + if line and not line.startswith('#'): + if ':' in line: + key, value = line.split(':', 1) + config[key.strip()] = value.strip() logger.info(f"Конфигурация загружена из {config_path}") return config except FileNotFoundError: logger.error(f"Конфигурационный файл не найден: {config_path}") raise - except yaml.YAMLError as e: - logger.error(f"Ошибка парсинга YAML конфигурации: {e}") + except Exception as e: + logger.error(f"Ошибка парсинга конфигурации: {e}") raise def load_domains(domains_file: str) -> List[str]: """ - Загрузка списка доменов из YAML-файла. + Загрузка списка доменов из текстового файла (по одному домену на строку). Args: domains_file: Путь к файлу со списком доменов @@ -60,19 +66,20 @@ def load_domains(domains_file: str) -> List[str]: List[str]: Список доменов """ try: + domains = [] with open(domains_file, 'r') as f: - domains = yaml.safe_load(f) - if domains is None: - domains = [] - # Фильтрация комментариев (если они есть в списке) - domains = [d for d in domains if isinstance(d, str) and d.strip()] - logger.info(f"Загружено {len(domains)} доменов из {domains_file}") - return domains + for line in f: + line = line.strip() + # Пропускаем пустые строки и комментарии + if line and not line.startswith('#'): + domains.append(line) + logger.info(f"Загружено {len(domains)} доменов из {domains_file}") + return domains except FileNotFoundError: logger.error(f"Файл со списком доменов не найден: {domains_file}") raise - except yaml.YAMLError as e: - logger.error(f"Ошибка парсинга YAML файла доменов: {e}") + except Exception as e: + logger.error(f"Ошибка чтения файла доменов: {e}") raise @@ -125,8 +132,74 @@ def resolve_domains(domains: List[str], dns_server: Optional[str] = None) -> dic return results +def generate_routes_file(resolved_domains: dict, routes_file: str) -> str: + """ + Генерация файла с IP-адресами в формате BIRD route statements. + + Args: + resolved_domains: Словарь {домен: [ip_адреса]} + routes_file: Путь к выходному файлу с маршрутами + + Returns: + str: Сгенерированное содержимое файла с маршрутами + """ + lines = [ + "# Generated by domain-routing-bird", + f"# Generated at: {__import__('datetime').datetime.now().isoformat()}", + "" + ] + + # Генерация route statements для каждого IP-адреса + networks_added = set() + for domain, ip_list in resolved_domains.items(): + for ip in ip_list: + # Создаем /24 сеть из IP-адреса (последний октет заменяется на 0) + ip_parts = ip.split('.') + if len(ip_parts) == 4: + network = f"{ip_parts[0]}.{ip_parts[1]}.{ip_parts[2]}.0/24" + if network not in networks_added: + networks_added.add(network) + lines.append(f"route {network} reject;") + + routes_content = "\n".join(lines) + + # Сохранение в файл + try: + with open(routes_file, 'w') as f: + f.write(routes_content) + logger.info(f"Файл с маршрутами сохранен в {routes_file}") + except IOError as e: + logger.error(f"Ошибка записи файла {routes_file}: {e}") + raise + + return routes_content + + +def load_routes(routes_file: str) -> str: + """ + Загрузка существующего файла с маршрутами. + + Args: + routes_file: Путь к файлу с маршрутами + + Returns: + str: Содержимое файла с маршрутами + """ + try: + with open(routes_file, 'r') as f: + content = f.read() + logger.info(f"Загружен файл с маршрутами из {routes_file}") + return content + except FileNotFoundError: + logger.warning(f"Файл с маршрутами не найден: {routes_file}") + return "" + except IOError as e: + logger.error(f"Ошибка чтения файла {routes_file}: {e}") + raise + + def generate_bird_config( - resolved_domains: dict, + routes_file: str, output_file: str, neighbor_ip: str = "192.168.1.1", neighbor_as: int = 65000, @@ -136,7 +209,7 @@ def generate_bird_config( Генерация конфигурационного файла для BIRD BGP. Args: - resolved_domains: Словарь {домен: [ip_адреса]} + routes_file: Путь к файлу с маршрутами output_file: Путь к выходному файлу neighbor_ip: IP-адрес BGP-Neighbor neighbor_as: AS номер соседа @@ -156,17 +229,16 @@ def generate_bird_config( "" ] - # Генерация network statements для каждого IP-адреса - networks_added = set() - for domain, ip_list in resolved_domains.items(): - for ip in ip_list: - # Создаем /24 сеть из IP-адреса (последний октет заменяется на 0) - ip_parts = ip.split('.') - if len(ip_parts) == 4: - network = f"{ip_parts[0]}.{ip_parts[1]}.{ip_parts[2]}.0/24" - if network not in networks_added: - networks_added.add(network) - lines.append(f" network {network} reject;") + # Загрузка маршрутов из файла + try: + with open(routes_file, 'r') as f: + routes_content = f.read() + # Извлечение строк с route statements + for line in routes_content.split('\n'): + if line.strip().startswith('route '): + lines.append(f" {line.strip()}") + except FileNotFoundError: + logger.warning(f"Файл с маршрутами не найден: {routes_file}") lines.append("") lines.append("}") @@ -198,7 +270,7 @@ def parse_arguments() -> argparse.Namespace: epilog=""" Пример usage: %(prog)s -c config.yaml - %(prog)s --config config.yaml --domains domains.yaml + %(prog)s --config config.yaml --domains domains.txt """ ) @@ -218,6 +290,11 @@ def parse_arguments() -> argparse.Namespace: help='Путь к выходному файлу BIRD (переопределяет настройку из config)' ) + parser.add_argument( + '-r', '--routes', + help='Путь к файлу с маршрутами (переопределяет настройку из config)' + ) + parser.add_argument( '-v', '--verbose', action='store_true', @@ -245,11 +322,14 @@ def main() -> int: config = load_config(args.config) # Определение пути к файлу доменов - domains_file = args.domains or config.get('domains_file', 'domains.yaml') + domains_file = args.domains or config.get('domains_file', 'domains.txt') # Определение пути к выходному файлу output_file = args.output or config.get('output_file', '/etc/bird/bird.conf') + # Определение пути к файлу с маршрутами + routes_file = args.routes or config.get('routes_file', 'routes.conf') + # DNS-сервер (не используется в текущей реализации) dns_server = config.get('dns_server') @@ -268,13 +348,17 @@ def main() -> int: resolved_count = sum(1 for ips in resolved_domains.values() if ips) logger.info(f"Успешно разрешено {resolved_count} из {len(domains)} доменов") + # Генерация файла с маршрутами + logger.info("Генерация файла с маршрутами...") + generate_routes_file(resolved_domains, routes_file) + # Генерация BIRD конфигурации neighbor_ip = config.get('neighbor_ip', '192.168.1.1') - neighbor_as = config.get('neighbor_as', 65000) + neighbor_as = int(config.get('neighbor_as', '65000')) protocol_name = config.get('protocol_name', 'domain_routing') config_content = generate_bird_config( - resolved_domains, + routes_file, output_file, neighbor_ip, neighbor_as, @@ -282,7 +366,8 @@ def main() -> int: ) logger.info("Генерация BIRD конфигурации завершена") - logger.info(f"Конфигурация сохранена в {output_file}") + logger.info(f"Конфигурация BIRD сохранена в {output_file}") + logger.info(f"Файл с маршрутами сохранен в {routes_file}") logger.info("Для применения изменений выполните: sudo birdc configure") return 0 @@ -290,9 +375,6 @@ def main() -> int: except FileNotFoundError as e: logger.error(f"Файл не найден: {e}") return 1 - except yaml.YAMLError as e: - logger.error(f"Ошибка YAML: {e}") - return 1 except Exception as e: logger.error(f"Неожиданная ошибка: {e}") return 1 diff --git a/domains.example.txt b/domains.example.txt new file mode 100644 index 0000000..c968d87 --- /dev/null +++ b/domains.example.txt @@ -0,0 +1,14 @@ +# Пример списка доменов для domain-based routing +# Каждый домен будет разрешен в IPv4-адреса и добавлен в BGP-конфигурацию + +# Основные домены +example.com +test.com + +# Домены с комментариями (комментарии начинаются с #) +# corporate.example.com # Корпоративный домен +# api.example.com # API домен + +# Домены с разными зонами +# domain1.org +# domain2.net diff --git a/domains.example.yaml b/domains.example.yaml index d26955e..c968d87 100644 --- a/domains.example.yaml +++ b/domains.example.yaml @@ -1,14 +1,14 @@ -# Список доменов для domain-based routing +# Пример списка доменов для domain-based routing # Каждый домен будет разрешен в IPv4-адреса и добавлен в BGP-конфигурацию # Основные домены -- example.com -- test.com +example.com +test.com -# Домены с комментариями -# - corporate.example.com # Корпоративный домен -# - api.example.com # API домен +# Домены с комментариями (комментарии начинаются с #) +# corporate.example.com # Корпоративный домен +# api.example.com # API домен # Домены с разными зонами -# - domain1.org -# - domain2.net +# domain1.org +# domain2.net diff --git a/domains.txt b/domains.txt new file mode 100644 index 0000000..df1570f --- /dev/null +++ b/domains.txt @@ -0,0 +1,73 @@ +# Пример списка доменов для domain-based routing +# Каждый домен будет разрешен в IPv4-адреса и добавлен в BGP-конфигурацию + +# Основные домены +# example.com +# test.com + +# Домены с комментариями (комментарии начинаются с #) +# corporate.example.com # Корпоративный домен +# api.example.com # API домен + +# Домены с разными зонами +# domain1.org +# domain2.net +youtube.googleapis.com +yt3.ggpht.com +yt4.ggpht.com +yt3.googleusercontent.com +googlevideo.com +jnn-pa.googleapis.com +stable.dl2.discordapp.net +wide-youtube.l.google.com +youtube-nocookie.com +youtube-ui.l.google.com +youtube.com +youtubeembeddedplayer.googleapis.com +youtubekids.com +youtubei.googleapis.com +youtu.be +yt-video-upload.l.google.com +ytimg.com +ytimg.l.google.com +googlevideo.com +rr1---sn-5hne6nsd.googlevideo.com +rr1---sn-ntqe6nee.googlevideo.com +rr1---sn-q4fl6nzy.googlevideo.com +rr1---sn-n8v7snl7.googlevideo.com +rr1---sn-q4flrnss.googlevideo.com +rr1---sn-q4fl6ndz.googlevideo.com +rr1---sn-5hneknes.googlevideo.com +rr1---sn-5hne6nzs.googlevideo.com +rr1---sn-ntqe6n7r.googlevideo.com +rr1---sn-gvnuxaxjvh-nbje.googlevideo.com +rr2---sn-5hne6nsd.googlevideo.com +rr2---sn-ntqe6nee.googlevideo.com +rr2---sn-q4fl6nzy.googlevideo.com +rr2---sn-n8v7snl7.googlevideo.com +rr2---sn-q4flrnss.googlevideo.com +rr2---sn-q4fl6ndz.googlevideo.com +rr2---sn-5hneknes.googlevideo.com +rr2---sn-5hne6nzs.googlevideo.com +rr2---sn-ntqe6n7r.googlevideo.com +rr2---sn-gvnuxaxjvh-nbje.googlevideo.com +rr4---sn-5hne6nsd.googlevideo.com +rr4---sn-ntqe6nee.googlevideo.com +rr4---sn-q4fl6nzy.googlevideo.com +rr4---sn-n8v7snl7.googlevideo.com +rr4---sn-q4flrnss.googlevideo.com +rr4---sn-q4fl6ndz.googlevideo.com +rr4---sn-5hneknes.googlevideo.com +rr4---sn-5hne6nzs.googlevideo.com +rr4---sn-ntqe6n7r.googlevideo.com +rr4---sn-gvnuxaxjvh-nbje.googlevideo.com +rr5---sn-5hne6nsd.googlevideo.com +rr5---sn-ntqe6nee.googlevideo.com +rr5---sn-q4fl6nzy.googlevideo.com +rr5---sn-n8v7snl7.googlevideo.com +rr5---sn-q4flrnss.googlevideo.com +rr5---sn-q4fl6ndz.googlevideo.com +rr5---sn-5hneknes.googlevideo.com +rr5---sn-5hne6nzs.googlevideo.com +rr5---sn-ntqe6n7r.googlevideo.com +rr5---sn-gvnuxaxjvh-nbje.googlevideo.com \ No newline at end of file diff --git a/domains.yaml b/domains.yaml index 95865eb..4a165f2 100644 --- a/domains.yaml +++ b/domains.yaml @@ -1,14 +1,14 @@ -# domains.yaml - Пример файла со списком доменов -# Скопируйте этот файл в domains.yaml и добавьте свои домены +# Список доменов для domain-based routing +# Каждый домен будет разрешен в IPv4-адреса и добавлен в BGP-конфигурацию # Основные домены -- example.com -- test.com +example.com +test.com -# Домены с комментариями (комментарии в YAML начинаются с #) -# - corporate.example.com # Корпоративный домен -# - api.example.com # API домен +# Домены с комментариями (комментарии начинаются с #) +# corporate.example.com # Корпоративный домен +# api.example.com # API домен # Домены с разными зонами -# - domain1.org -# - domain2.net +# domain1.org +# domain2.net diff --git a/routes.conf b/routes.conf new file mode 100644 index 0000000..5f40387 --- /dev/null +++ b/routes.conf @@ -0,0 +1,32 @@ +# Generated by domain-routing-bird +# Generated at: 2026-07-03T17:05:18.067428 + +route 216.239.32.0/24 reject; +route 216.239.34.0/24 reject; +route 216.239.38.0/24 reject; +route 216.239.36.0/24 reject; +route 173.194.220.0/24 reject; +route 64.233.162.0/24 reject; +route 74.125.205.0/24 reject; +route 173.194.221.0/24 reject; +route 209.85.233.0/24 reject; +route 64.233.161.0/24 reject; +route 108.177.14.0/24 reject; +route 64.233.164.0/24 reject; +route 142.251.1.0/24 reject; +route 173.194.222.0/24 reject; +route 74.125.131.0/24 reject; +route 172.253.152.0/24 reject; +route 64.233.163.0/24 reject; +route 173.194.73.0/24 reject; +route 64.233.165.0/24 reject; +route 172.253.130.0/24 reject; +route 142.250.150.0/24 reject; +route 34.126.226.0/24 reject; +route 172.217.132.0/24 reject; +route 74.125.109.0/24 reject; +route 74.125.3.0/24 reject; +route 173.194.151.0/24 reject; +route 173.194.57.0/24 reject; +route 173.194.141.0/24 reject; +route 74.125.8.0/24 reject; \ No newline at end of file