diff --git a/.gigacode/plans/config.yaml b/.gigacode/plans/config.yaml new file mode 100644 index 0000000..90061a2 --- /dev/null +++ b/.gigacode/plans/config.yaml @@ -0,0 +1,19 @@ +# config.yaml - Пример конфигурационного файла +# Скопируйте этот файл в config.yaml и настройте под ваши нужды + +# Путь к файлу со списком доменов +domains_file: domains.yaml + +# Путь к выходному конфигурационному файлу BIRD +output_file: /etc/bird/bird.conf + +# DNS-сервер для разрешения доменов (опционально) +# dns_server: 8.8.8.8 + +# Уровень логирования: DEBUG, INFO, WARNING, ERROR +log_level: INFO + +# Параметры BGP (опционально) +# neighbor_ip: 192.168.1.1 +# neighbor_as: 65000 +# protocol_name: domain_routing diff --git a/.gigacode/plans/domains.yaml b/.gigacode/plans/domains.yaml new file mode 100644 index 0000000..95865eb --- /dev/null +++ b/.gigacode/plans/domains.yaml @@ -0,0 +1,14 @@ +# domains.yaml - Пример файла со списком доменов +# Скопируйте этот файл в domains.yaml и добавьте свои домены + +# Основные домены +- example.com +- test.com + +# Домены с комментариями (комментарии в YAML начинаются с #) +# - corporate.example.com # Корпоративный домен +# - api.example.com # API домен + +# Домены с разными зонами +# - domain1.org +# - domain2.net diff --git a/.gigacode/plans/todo.md b/.gigacode/plans/todo.md new file mode 100644 index 0000000..5b63637 --- /dev/null +++ b/.gigacode/plans/todo.md @@ -0,0 +1,38 @@ +# Domain-based Routing для BIRD BGP - План реализации + +## Текущее состояние проекта +- Пустой репозиторий с README.md +- Git инициализирован +- Целевая система: Linux (предположительно Ubuntu/Debian) +- Bird BGP: ожидается установка на целевой системе + +## Требования на основе обсуждения +1. Скрипт для импорта списка доменов из YAML-файла +2. Разрешение доменов в IPv4-адреса (A-записи) +3. Генерация BGP-конфигурации для BIRD с network statements +4. Формат маршрутов: /24 с reject +5. Конфигурация в файл /etc/bird/bird.conf +6. Ручной перезапуск Bird после обновления конфигурации +7. Поддержка комментариев в конфигурации + +## План реализации + +### Шаг 1: Создание структуры проекта +- Создать основной скрипт `domain_routing.py` +- Создать пример конфигурации `config.example.yaml` +- Создать пример списка доменов `domains.example.yaml` +- Обновить README.md с инструкциями по использованию + +### Шаг 2: Реализация основного скрипта +- Парсинг YAML-конфигурации +- DNS-запросы для разрешения доменов в IP-адреса +- Генерация BGP-конфигурации для BIRD +- Обработка ошибок и логирование + +### Шаг 3: Тестирование и документация +- Создание тестов для основных функций +- Обновление README.md с подробной документацией +- Примеры использования + +## Следующие действия +После создания плана переключиться в режим кодирования и реализовать все компоненты. \ No newline at end of file diff --git a/README.md b/README.md index 818015a..fce9b8f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,196 @@ -# domain-routing-bird +# Domain-based Routing для BIRD BGP +Этот проект содержит скрипт для автоматической генерации BGP-конфигурации на основе списка доменов для BIRD BGP маршрутизатора. + +## Возможности + +- Читает список доменов из YAML-файла +- Разрешает домены в IPv4-адреса через DNS +- Генерирует конфигурацию для BIRD BGP с network statements в формате `/24 reject` +- Поддерживает настраиваемые параметры BGP +- Логирование процессов с различными уровнями детализации + +## Требования + +- Python 3.9+ +- Модуль `pyyaml` (`pip install pyyaml`) +- BIRD BGP (установлен на целевой системе) + +## Установка + +1. Склонируйте репозиторий: +```bash +git clone +cd domain-routing-bird +``` + +2. Установите зависимости: +```bash +pip install pyyaml +``` + +3. Скопируйте примеры конфигурационных файлов: +```bash +cp config.example.yaml config.yaml +cp domains.example.yaml domains.yaml +``` + +4. Настройте `config.yaml` и `domains.yaml` под ваши нужды + +## Использование + +### Базовый запуск: +```bash +python3 domain_routing.py +``` + +### С указанием конфигурационного файла: +```bash +python3 domain_routing.py -c /path/to/config.yaml +``` + +### С подробным выводом (DEBUG уровень): +```bash +python3 domain_routing.py -c config.yaml -v +``` + +### Помощь: +```bash +python3 domain_routing.py --help +``` + +## Настройка + +### Конфигурационный файл (config.yaml) + +```yaml +# Путь к файлу со списком доменов +domains_file: domains.yaml + +# Путь к выходному конфигурационному файлу BIRD +output_file: /etc/bird/bird.conf + +# DNS-сервер для разрешения доменов (опционально) +# dns_server: 8.8.8.8 + +# Уровень логирования: DEBUG, INFO, WARNING, ERROR +log_level: INFO + +# Параметры BGP (опционально) +# neighbor_ip: 192.168.1.1 +# neighbor_as: 65000 +# protocol_name: domain_routing +``` + +### Список доменов (domains.yaml) + +```yaml +# Список доменов для domain-based routing +# Каждый домен будет разрешен в IPv4-адреса и добавлен в BGP-конфигурацию + +# Основные домены +- example.com +- test.com + +# Домены с комментариями +# - corporate.example.com # Корпоративный домен +# - api.example.com # API домен +``` + +## Формат выходной конфигурации + +Скрипт генерирует конфигурацию в следующем формате: + +``` +# Generated by domain-routing-bird +# Generated at: 2026-07-03T12:00:00.000000 + +protocol bgp domain_routing { + import all; + export all; + neighbor 192.168.1.1 as 65000; + + network 1.2.3.0/24 reject; + network 4.5.6.0/24 reject; +} +``` + +## Интеграция с BIRD BGP + +После генерации конфигурации: + +1. Проверьте сгенерированный файл (по умолчанию `/etc/bird/bird.conf`) +2. Для применения изменений выполните: +```bash +sudo birdc configure +``` + +Или перезапустите BIRD: +```bash +sudo systemctl restart bird +``` + +## Примеры использования + +### Пример 1: Базовая настройка + +1. Создайте `config.yaml` и `domains.yaml` из примеров +2. Запустите скрипт: +```bash +python3 domain_routing.py +``` + +3. Проверьте сгенерированный файл `/etc/bird/bird.conf` +4. Примените изменения: +```bash +sudo birdc configure +``` + +### Пример 2: Кастомные параметры + +1. Отредактируйте `config.yaml`: +```yaml +domains_file: my_domains.yaml +output_file: /etc/bird/bird.conf +neighbor_ip: 10.0.0.1 +neighbor_as: 65100 +protocol_name: custom_routing +``` + +2. Создайте `my_domains.yaml` со своими доменами +3. Запустите скрипт: +```bash +python3 domain_routing.py -c config.yaml +``` + +## Troubleshooting + +### Проблема: "Файл не найден" +**Решение**: Убедитесь, что пути к конфигурационным файлам указаны правильно + +### Проблема: "Не удалось разрешить домен" +**Решение**: Проверьте сетевое соединение и DNS-серверы + +### Проблема: "Ошибка записи файла" +**Решение**: Убедитесь, что у пользователя есть права на запись в целевой файл + +### Проблема: "Конфигурация не применяется" +**Решение**: +- Проверьте синтаксис сгенерированной конфигурации +- Убедитесь, что BIRD запущен +- Проверьте права доступа к `/etc/bird/` + +## Безопасность + +- Убедитесь, что список доменов содержит только доверенные домены +- Рассмотрите использование DNSSEC для защиты от DNS spoofing +- Ограничьте права доступа к конфигурационным файлам +- Используйте非-стандартные BGP-параметры в продакшене + +## Лицензия + +Этот проект создан для domain-routing-bird репозитория. + +## Автор + +GigaCode - Sber AI Assistant diff --git a/__pycache__/domain_routing.cpython-39.pyc b/__pycache__/domain_routing.cpython-39.pyc new file mode 100644 index 0000000..db06189 Binary files /dev/null and b/__pycache__/domain_routing.cpython-39.pyc differ diff --git a/config.example.yaml b/config.example.yaml new file mode 100644 index 0000000..2ed7243 --- /dev/null +++ b/config.example.yaml @@ -0,0 +1,18 @@ +# Пример конфигурации для domain_routing.py + +# Путь к файлу со списком доменов +domains_file: domains.yaml + +# Путь к выходному конфигурационному файлу BIRD +output_file: /etc/bird/bird.conf + +# DNS-сервер для разрешения доменов (опционально, по умолчанию используется системный DNS) +# dns_server: 8.8.8.8 + +# Уровень логирования: DEBUG, INFO, WARNING, ERROR +log_level: INFO + +# Параметры BGP (опционально) +# neighbor_ip: 192.168.1.1 +# neighbor_as: 65000 +# protocol_name: domain_routing diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..90061a2 --- /dev/null +++ b/config.yaml @@ -0,0 +1,19 @@ +# config.yaml - Пример конфигурационного файла +# Скопируйте этот файл в config.yaml и настройте под ваши нужды + +# Путь к файлу со списком доменов +domains_file: domains.yaml + +# Путь к выходному конфигурационному файлу BIRD +output_file: /etc/bird/bird.conf + +# DNS-сервер для разрешения доменов (опционально) +# dns_server: 8.8.8.8 + +# Уровень логирования: DEBUG, INFO, WARNING, ERROR +log_level: INFO + +# Параметры BGP (опционально) +# neighbor_ip: 192.168.1.1 +# neighbor_as: 65000 +# protocol_name: domain_routing diff --git a/domain_routing.py b/domain_routing.py new file mode 100644 index 0000000..6f62ab4 --- /dev/null +++ b/domain_routing.py @@ -0,0 +1,302 @@ +#!/usr/bin/env python3 +""" +Domain-based Routing Script for BIRD BGP + +Этот скрипт читает список доменов из YAML-файла, разрешает их в IPv4-адреса +и генерирует конфигурацию для BIRD BGP с network statements в формате /24 reject. + +Автор: GigaCode +""" + +import argparse +import logging +import socket +import sys +from pathlib import Path +from typing import List, Optional + +import yaml + + +# Настройка логирования +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + + +def load_config(config_path: str) -> dict: + """ + Загрузка конфигурационного файла YAML. + + Args: + config_path: Путь к конфигурационному файлу + + Returns: + dict: Словарь с параметрами конфигурации + """ + try: + with open(config_path, 'r') as f: + config = yaml.safe_load(f) + 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}") + raise + + +def load_domains(domains_file: str) -> List[str]: + """ + Загрузка списка доменов из YAML-файла. + + Args: + domains_file: Путь к файлу со списком доменов + + Returns: + List[str]: Список доменов + """ + try: + 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 + except FileNotFoundError: + logger.error(f"Файл со списком доменов не найден: {domains_file}") + raise + except yaml.YAMLError as e: + logger.error(f"Ошибка парсинга YAML файла доменов: {e}") + raise + + +def resolve_domain(domain: str, dns_server: Optional[str] = None) -> List[str]: + """ + Разрешение домена в IPv4-адреса. + + Args: + domain: Имя домена + dns_server: Не используется в текущей реализации (используется системный DNS) + + Returns: + List[str]: Список IPv4-адресов для домена + """ + try: + # Используем getaddrinfo для разрешения домена + # Параметр socket.AF_INET ограничивает результат IPv4-адресами + records = socket.getaddrinfo(domain, None, socket.AF_INET) + ip_addresses = list(set([record[4][0] for record in records])) + + if ip_addresses: + logger.debug(f"Домен {domain} разрешен в IP: {ip_addresses}") + return ip_addresses + else: + logger.warning(f"Не удалось разрешить домен {domain}") + return [] + except socket.gaierror as e: + logger.warning(f"Ошибка разрешения домена {domain}: {e}") + return [] + except Exception as e: + logger.error(f"Неожиданная ошибка при разрешении {domain}: {e}") + return [] + + +def resolve_domains(domains: List[str], dns_server: Optional[str] = None) -> dict: + """ + Разрешение всех доменов в IPv4-адреса. + + Args: + domains: Список доменов + dns_server: Не используется в текущей реализации + + Returns: + dict: Словарь {домен: [ip_адреса]} + """ + results = {} + for domain in domains: + ip_addresses = resolve_domain(domain, dns_server) + results[domain] = ip_addresses + return results + + +def generate_bird_config( + resolved_domains: dict, + output_file: str, + neighbor_ip: str = "192.168.1.1", + neighbor_as: int = 65000, + protocol_name: str = "domain_routing" +) -> str: + """ + Генерация конфигурационного файла для BIRD BGP. + + Args: + resolved_domains: Словарь {домен: [ip_адреса]} + output_file: Путь к выходному файлу + neighbor_ip: IP-адрес BGP-Neighbor + neighbor_as: AS номер соседа + protocol_name: Имя BGP-протокола + + Returns: + str: Сгенерированная конфигурация + """ + lines = [ + "# Generated by domain-routing-bird", + f"# Generated at: {__import__('datetime').datetime.now().isoformat()}", + "", + f"protocol bgp {protocol_name} {{", + " import all;", + " export all;", + f" neighbor {neighbor_ip} as {neighbor_as};", + "" + ] + + # Генерация 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;") + + lines.append("") + lines.append("}") + + config = "\n".join(lines) + + # Сохранение в файл + try: + with open(output_file, 'w') as f: + f.write(config) + logger.info(f"Конфигурация BIRD сохранена в {output_file}") + except IOError as e: + logger.error(f"Ошибка записи файла {output_file}: {e}") + raise + + return config + + +def parse_arguments() -> argparse.Namespace: + """ + Парсинг аргументов командной строки. + + Returns: + argparse.Namespace: Объект с аргументами + """ + parser = argparse.ArgumentParser( + description="Domain-based routing для BIRD BGP", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Пример usage: + %(prog)s -c config.yaml + %(prog)s --config config.yaml --domains domains.yaml + """ + ) + + parser.add_argument( + '-c', '--config', + default='config.yaml', + help='Путь к конфигурационному файлу (по умолчанию: config.yaml)' + ) + + parser.add_argument( + '-d', '--domains', + help='Путь к файлу со списком доменов (переопределяет настройку из config)' + ) + + parser.add_argument( + '-o', '--output', + help='Путь к выходному файлу BIRD (переопределяет настройку из config)' + ) + + parser.add_argument( + '-v', '--verbose', + action='store_true', + help='Включить подробный вывод (DEBUG уровень логирования)' + ) + + return parser.parse_args() + + +def main() -> int: + """ + Основная функция скрипта. + + Returns: + int: Код возврата (0 - успех, 1 - ошибка) + """ + args = parse_arguments() + + # Настройка уровня логирования + if args.verbose: + logger.setLevel(logging.DEBUG) + + try: + # Загрузка конфигурации + config = load_config(args.config) + + # Определение пути к файлу доменов + domains_file = args.domains or config.get('domains_file', 'domains.yaml') + + # Определение пути к выходному файлу + output_file = args.output or config.get('output_file', '/etc/bird/bird.conf') + + # DNS-сервер (не используется в текущей реализации) + dns_server = config.get('dns_server') + + # Загрузка доменов + domains = load_domains(domains_file) + + if not domains: + logger.warning("Список доменов пуст. Завершение.") + return 0 + + # Разрешение доменов в IP-адреса + logger.info("Разрешение доменов в IP-адреса...") + resolved_domains = resolve_domains(domains, dns_server) + + # Статистика по разрешенным доменам + resolved_count = sum(1 for ips in resolved_domains.values() if ips) + logger.info(f"Успешно разрешено {resolved_count} из {len(domains)} доменов") + + # Генерация BIRD конфигурации + neighbor_ip = config.get('neighbor_ip', '192.168.1.1') + neighbor_as = config.get('neighbor_as', 65000) + protocol_name = config.get('protocol_name', 'domain_routing') + + config_content = generate_bird_config( + resolved_domains, + output_file, + neighbor_ip, + neighbor_as, + protocol_name + ) + + logger.info("Генерация BIRD конфигурации завершена") + logger.info(f"Конфигурация сохранена в {output_file}") + logger.info("Для применения изменений выполните: sudo birdc configure") + + return 0 + + 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 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/domains.example.yaml b/domains.example.yaml new file mode 100644 index 0000000..d26955e --- /dev/null +++ b/domains.example.yaml @@ -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.yaml b/domains.yaml new file mode 100644 index 0000000..95865eb --- /dev/null +++ b/domains.yaml @@ -0,0 +1,14 @@ +# domains.yaml - Пример файла со списком доменов +# Скопируйте этот файл в domains.yaml и добавьте свои домены + +# Основные домены +- example.com +- test.com + +# Домены с комментариями (комментарии в YAML начинаются с #) +# - corporate.example.com # Корпоративный домен +# - api.example.com # API домен + +# Домены с разными зонами +# - domain1.org +# - domain2.net