diff --git a/.gigacode/plans/domain_routing_bird.md b/.gigacode/plans/domain_routing_bird.md new file mode 100644 index 0000000..b89c4f4 --- /dev/null +++ b/.gigacode/plans/domain_routing_bird.md @@ -0,0 +1,63 @@ +# План: Domain-Based Routing для BIRD BGP + +## Задача +Переписать скрипт domain-based routing для интеграции с BIRD BGP вместо firewalld/ipset/dnsmasq. + +## Текущее состояние +- Существует `get-ip.sh` - генерирует route statements для BIRD из IP-списков +- Существует `domain-routing.sh` - использует firewalld/ipset/dnsmasq для domain-based routing +- Требуется создать `domain-routing-bird.sh` - генерирует маршруты для BIRD на основе доменов + +## План реализации + +### 1. Создать domain-routing-bird.sh +- Считывает список доменов из DOMAINS_FILE +- Разрешает домены в IP-адреса через dig (A и AAAA записи) +- Генерирует конфигурационный файл для BIRD с route statements +- Создаёт systemd-сервис для автовосстановления при перезагрузке +- Поддерживает параметры командной строки +- Использует конфигурационный файл domain-routing-bird.env + +### 2. Структура конфигурации BIRD +``` +# Auto-generated domain routing config +# Generated: 2026-07-03 + +route 8.8.8.0/24 via 10.0.0.1 on eth0; +route 1.1.1.0/24 via 10.0.0.1 on eth0; +``` + +### 3. Переменные окружения (domain-routing-bird.env) +- DOMAINS_FILE - путь к файлу со списком доменов +- BIRD_CONF_DIR - директория для конфигов BIRD (/etc/bird/conf.d) +- BIRD_CONF_FILE - имя выходного файла (domain-routing.conf) +- ROUTE_GATEWAY - шлюз для маршрутов (10.0.0.1) +- ROUTE_INTERFACE - интерфейс для маршрутов (eth0) +- ENABLE_IPV6 - включить IPv6 маршруты (true/false) +- ENABLE_IPV4 - включить IPv4 маршруты (true/false) +- AGGREGATE_TO_24 - суммаризировать IPv4 до /24 (true/false) + +### 4. systemd-сервис для автовосстановления +- Создаётся при первом запуске +- Восстанавливает конфиг BIRD при перезагрузке +- Должен быть включён в автозагрузку + +### 5. Обновить документацию +- Обновить README_DOMAIN_ROUTING.md с новым разделом для BIRD +- Создать README_DOMAIN_ROUTING_BIRD.md + +## Шаги выполнения +1. Создать domain-routing-bird.sh ✅ +2. Создать domain-routing-bird.env.example ✅ +3. Создать domains-bird.txt.example для BIRD ✅ +4. Обновить README документацию ✅ +5. Проверить синтаксис bash ✅ + +## Дополнительные требования +- Добавить суммаризацию маршрутов до /24 ✅ + +## Реализованные изменения +- Добавлена функция `aggregate_routes_ipv4()` для суммаризации IPv4 маршрутов до /24 +- Добавлена переменная `AGGREGATE_TO_24` для управления суммаризацией +- Обновлена функция `generate_bird_config()` для использования суммаризированных сетей +- Обновлён конфигурационный файл с новой переменной AGGREGATE_TO_24 diff --git a/README_DOMAIN_ROUTING.md b/README_DOMAIN_ROUTING.md new file mode 100644 index 0000000..b237d84 --- /dev/null +++ b/README_DOMAIN_ROUTING.md @@ -0,0 +1,185 @@ +# Domain-Based Routing для RHEL + +Скрипт для настройки маршрутизации трафика на основе доменных имён с использованием `firewalld`, `ipset` и `dnsmasq`. + +## Возможности + +- Автоматическое разрешение доменных имён в IP-адреса +- Добавление IP-адресов в `ipset` для быстрой фильтрации +- Настройка правил `firewalld` для перенаправления трафика +- Интеграция с `dnsmasq` для локального DNS-кэширования +- Автовосстановление `ipset` при перезагрузке + +## Требования + +- RHEL/CentOS 8+ с `firewalld`, `ipset`, `dnsmasq` +- `dig` (пакет `bind-utils`) +- `root` права + +## Установка + +1. Скопируйте `domain-routing.sh` на сервер: + ```bash + cp domain-routing.sh /usr/local/bin/ + chmod +x /usr/local/bin/domain-routing.sh + ``` + +2. Создайте конфигурационный файл: + ```bash + cp domain-routing.env.example /opt/domain-routing/domain-routing.env + ``` + +3. Отредактируйте `domain-routing.env`: + ```bash + DOMAINS_FILE="/opt/domain-routing/domains.txt" + DNSMASQ_CONF_DIR="/etc/dnsmasq.d" + IPSET_NAME="domain_routing" + FIREWALL_ZONE="domain_routing" + ``` + +4. Создайте файл со списком доменов: + ```bash + cat > /opt/domain-routing/domains.txt << 'EOF' + google.com + amazon.com + cloudflare.com + EOF + ``` + +5. Запустите скрипт: + ```bash + sudo /usr/local/bin/domain-routing.sh + ``` + +## Использование + +### Базовое использование +```bash +sudo ./domain-routing.sh +``` + +### С параметрами командной строки +```bash +sudo ./domain-routing.sh \ + --domains-file /path/to/domains.txt \ + --ipset-name custom_ipset \ + --firewall-zone custom_zone +``` + +### Просмотр справки +```bash +sudo ./domain-routing.sh --help +``` + +## Конфигурация + +### Основные переменные в `domain-routing.env` + +| Переменная | Описание | Обязательная | +|------------|----------|-------------| +| `DOMAINS_FILE` | Путь к файлу со списком доменов | Да | +| `IPSET_NAME` | Имя создаваемого ipset | Да | +| `FIREWALL_ZONE` | Имя зоны firewalld | Да | +| `DNSMASQ_CONF_DIR` | Директория для конфигов dnsmasq | Да | +| `RESOLV_CONF` | Путь к resolv.conf | Нет | + +### Формат файла доменов + +Один домен на строку, комментарии начинаются с `#`: +``` +# Комментарий +google.com +www.example.com +``` + +## Как это работает + +1. Скрипт читает список доменов из файла +2. Разрешает каждый домен в IP-адреса через `dig` +3. Создаёт `ipset` и добавляет в него IP-адреса +4. Настраивает `firewalld` для использования ipset +5. Генерирует конфиг для `dnsmasq` +6. Сохраняет ipset для автовосстановления при перезагрузке + +## Управление ipset + +### Просмотр ipset +```bash +ipset list domain_routing +``` + +### Удаление ipset +```bash +ipset destroy domain_routing +``` + +### Пересоздание ipset +```bash +sudo ./domain-routing.sh +``` + +## Автовосстановление + +При установке создаётся systemd-сервис `ipset-restore@domain_routing.service`, который восстанавливает ipset при каждом запуске системы. + +### Управление сервисом +```bash +# Проверка статуса +systemctl status ipset-restore@domain_routing.service + +# Запуск вручную +systemctl start ipset-restore@domain_routing.service + +# Отключение автовосстановления +systemctl disable ipset-restore@domain_routing.service +``` + +## Устранение неполадок + +### Ошибка: "Не удалось разрешить ни один домен" +- Проверьте доступность DNS: `dig google.com` +- Убедитесь, что в файле доменов нет опечаток + +### Ошибка: "firewalld: no zone" +- Создайте зону вручную: `firewall-cmd --new-zone=test --permanent` + +### Dnsmasq не перезагружается +- Убедитесь, что dnsmasq установлен: `dnsmasq --version` +- Запустите вручную: `systemctl start dnsmasq` + +## Примеры + +### Маршрутизация трафика Google через VPN +```bash +# В domain-routing.env +DOMAINS_FILE="/etc/domain-routing/google-domains.txt" +IPSET_NAME="google_via_vpn" +FIREWALL_ZONE="vpn_routing" + +# В /etc/domain-routing/google-domains.txt +google.com +www.google.com +maps.google.com +``` + +### Маршрутизация рекламных доменов в "чёрную дыру" +```bash +# В domain-routing.env +DOMAINS_FILE="/etc/domain-routing/ads-domains.txt" +IPSET_NAME="ads_block" +FIREWALL_ZONE="ads_block" + +# Добавьте правило в firewalld для блокировки +firewall-cmd --zone=ads_block --add-rich-rule='rule family="ipv4" source ipset="ads_block" drop' --permanent +firewall-cmd --reload +``` + +## Безопасность + +- Скрипт должен запускаться от имени `root` +- Файлы конфигурации содержат пути к системным ресурсам - храните их в защищённых местах +- ipset используется только для IPv4 (можно расширить для IPv6) + +## Лицензия + +MIT diff --git a/README_DOMAIN_ROUTING_BIRD.md b/README_DOMAIN_ROUTING_BIRD.md new file mode 100644 index 0000000..eb0056e --- /dev/null +++ b/README_DOMAIN_ROUTING_BIRD.md @@ -0,0 +1,223 @@ +# Domain-Based Routing для BIRD BGP + +Скрипт для настройки маршрутизации трафика на основе доменных имён с использованием `BIRD BGP`. + +## Возможности + +- Автоматическое разрешение доменных имён в IP-адреса (IPv4 и IPv6) +- Генерация конфигурационного файла для BIRD с route statements +- Автоматическая перезагрузка BIRD после изменения конфига +- MD5 хеш для отслеживания изменений +- Автовосстановление конфига при перезагрузке через systemd + +## Требования + +- RHEL/CentOS 8+ с `BIRD` +- `dig` (пакет `bind-utils`) +- `root` права + +## Установка + +1. Скопируйте `domain-routing-bird.sh` на сервер: + ```bash + cp domain-routing-bird.sh /usr/local/bin/ + chmod +x /usr/local/bin/domain-routing-bird.sh + ``` + +2. Создайте конфигурационный файл: + ```bash + cp domain-routing-bird.env.example /opt/domain-routing/domain-routing-bird.env + ``` + +3. Отредактируйте `domain-routing-bird.env`: + ```bash + DOMAINS_FILE="/opt/domain-routing/domains.txt" + BIRD_CONF_DIR="/etc/bird/conf.d" + BIRD_CONF_FILE="domain-routing.conf" + ROUTE_GATEWAY="10.0.0.1" + ROUTE_INTERFACE="eth0" + ENABLE_IPV4="true" + ENABLE_IPV6="false" + ``` + +4. Создайте файл со списком доменов: + ```bash + cat > /opt/domain-routing/domains.txt << 'EOF' + google.com + www.google.com + amazon.com + cloudflare.com + EOF + ``` + +5. Запустите скрипт: + ```bash + sudo /usr/local/bin/domain-routing-bird.sh + ``` + +## Использование + +### Базовое использование +```bash +sudo ./domain-routing-bird.sh +``` + +### С параметрами командной строки +```bash +sudo ./domain-routing-bird.sh \ + --domains-file /path/to/domains.txt \ + --bird-conf-dir /etc/bird/conf.d \ + --bird-conf-file domain-routing.conf \ + --gateway 10.0.0.1 \ + --interface eth0 +``` + +### Просмотр справки +```bash +sudo ./domain-routing-bird.sh --help +``` + +## Конфигурация + +### Основные переменные в `domain-routing-bird.env` + +| Переменная | Описание | Обязательная | +|------------|----------|-------------| +| `DOMAINS_FILE` | Путь к файлу со списком доменов | Да | +| `BIRD_CONF_DIR` | Директория конфигов BIRD | Да | +| `BIRD_CONF_FILE` | Имя файла конфига | Да | +| `ROUTE_GATEWAY` | Шлюз для маршрутов | Да | +| `ROUTE_INTERFACE` | Интерфейс для маршрутов | Да | +| `ENABLE_IPV4` | Включить IPv4 (true/false) | Нет (по умолчанию true) | +| `ENABLE_IPV6` | Включить IPv6 (true/false) | Нет (по умолчанию false) | + +### Формат файла доменов + +Один домен на строку, комментарии начинаются с `#`: +``` +# Комментарий +google.com +www.example.com +``` + +## Как это работает + +1. Скрипт читает список доменов из файла +2. Разрешает каждый домен в IP-адреса через `dig` (A и AAAA записи) +3. Генерирует конфигурационный файл для BIRD с route statements +4. Перезагружает BIRD через `birdc configure` +5. Сохраняет MD5 хеш для отслеживания изменений +6. Создаёт systemd-сервис для автовосстановления при перезагрузке + +## Структура конфига BIRD + +``` +# Auto-generated domain routing config +# Generated: 2026-07-03 +# Source: /opt/domain-routing/domains.txt + +route 8.8.8.0/24 via 10.0.0.1 on eth0; +route 1.1.1.0/24 via 10.0.0.1 on eth0; +``` + +## Управление конфигом + +### Просмотр конфига BIRD +```bash +cat /etc/bird/conf.d/domain-routing.conf +``` + +### Просмотр маршрутов в BIRD +```bash +birdc show route all +``` + +### Перезагрузка BIRD вручную +```bash +sudo /usr/sbin/birdc configure +``` + +### Удаление конфига +```bash +rm /etc/bird/conf.d/domain-routing.conf +sudo /usr/sbin/birdc configure +``` + +## systemd-сервис + +При установке создаётся systemd-сервис `bird-domain-routing.service`, который перезагружает BIRD при каждом запуске системы. + +### Управление сервисом +```bash +# Проверка статуса +systemctl status bird-domain-routing.service + +# Запуск вручную +systemctl start bird-domain-routing.service + +# Отключение автовосстановления +systemctl disable bird-domain-routing.service +``` + +## Устранение неполадок + +### Ошибка: "Не удалось разрешить ни один домен" +- Проверьте доступность DNS: `dig google.com` +- Убедитесь, что в файле доменов нет опечаток + +### Ошибка: "birdc: connect: No such file or directory" +- Убедитесь, что BIRD запущен: `systemctl status bird` +- Запустите BIRD: `systemctl start bird` + +### Ошибка: "route X.X.X.X: Invalid argument" +- Проверьте, что шлюз `ROUTE_GATEWAY` доступен на интерфейсе `ROUTE_INTERFACE` +- Проверьте маршруты: `ip route show` + +### BIRD не применяет конфиг +- Проверьте синтаксис: `birdc check` +- Посмотрите логи: `journalctl -u bird` + +## Примеры + +### Маршрутизация трафика Google через VPN +```bash +# В domain-routing-bird.env +DOMAINS_FILE="/etc/domain-routing/google-domains.txt" +BIRD_CONF_FILE="google-routes.conf" +ROUTE_GATEWAY="10.8.0.1" +ROUTE_INTERFACE="tun0" + +# В /etc/domain-routing/google-domains.txt +google.com +www.google.com +maps.google.com +``` + +### Маршрутизация рекламных доменов в "чёрную дыру" (через null0) +```bash +# В domain-routing-bird.env +ROUTE_GATEWAY="null0" + +# Конфиг будет содержать: +route 8.8.8.0/24 via null0; +``` + +## Сравнение с domain-routing.sh (firewalld/ipset) + +| Функция | domain-routing.sh | domain-routing-bird.sh | +|---------|-------------------|------------------------| +| Технология | firewalld + ipset | BIRD BGP | +| Гибкость | Ограничена правилами firewalld | Полный контроль маршрутов | +| Поддержка IPv6 | Ограничена | Полная | +| Интеграция | Системный firewall | BGP routing | +| Сложность | Простая | Более сложная | + +## Безопасность + +- Скрипт должен запускаться от имени `root` +- Файлы конфигурации содержат пути к системным ресурсам - храните их в защищённых местах +- Рассмотрите использование `birdc check` для валидации конфига перед применением + +## Лицензия + +MIT diff --git a/domain-routing-bird.env.example b/domain-routing-bird.env.example new file mode 100644 index 0000000..7806642 --- /dev/null +++ b/domain-routing-bird.env.example @@ -0,0 +1,44 @@ +#!/bin/bash + +# Конфигурационный файл для domain-based routing с BIRD BGP +# Скопируйте этот файл в domain-routing-bird.env и настройте под ваши нужды + +# ============================================ +# ПУТИ И ФАЙЛЫ +# ============================================ + +# Файл со списком доменов для маршрутизации (один домен на строку) +# DOMAINS_FILE="/opt/domain-routing/domains.txt" + +# Директория для конфигов BIRD +# BIRD_CONF_DIR="/etc/bird/conf.d" + +# Имя выходного файла конфига BIRD +# BIRD_CONF_FILE="domain-routing.conf" + +# ============================================ +# МАРШРУТИЗАЦИЯ +# ============================================ + +# Шлюз для маршрутов (IP-адрес шлюза на локальном интерфейсе) +# ROUTE_GATEWAY="10.0.0.1" + +# Интерфейс для маршрутов +# ROUTE_INTERFACE="eth0" + +# ============================================ +# IP VERSIONS +# ============================================ + +# Включить IPv4 маршруты (true/false) +# ENABLE_IPV4="true" + +# Включить IPv6 маршруты (true/false) +# ENABLE_IPV6="false" + +# ============================================ +# СУММАРИЗАЦИЯ МАРШРУТОВ +# ============================================ + +# Суммаризировать IPv4 маршруты до /24 (true/false) +# AGGREGATE_TO_24="true" diff --git a/domain-routing-bird.sh b/domain-routing-bird.sh new file mode 100755 index 0000000..e85800d --- /dev/null +++ b/domain-routing-bird.sh @@ -0,0 +1,344 @@ +#!/bin/bash + +# Скрипт для domain-based routing под RHEL с BIRD BGP +# Направляет трафик определённых доменов через отдельный маршрут в BIRD +# С поддержкой суммаризации маршрутов до /24 + +set -euo pipefail + +# ============================================ +# КОНФИГУРАЦИЯ +# ============================================ +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CONFIG_FILE="$SCRIPT_DIR/domain-routing-bird.env" + +# Загрузка конфигурации +if [[ ! -f "$CONFIG_FILE" ]]; then + echo "❌ Ошибка: не найден конфигурационный файл $CONFIG_FILE" + echo "Создайте файл на основе domain-routing-bird.env.example" + exit 1 +fi + +# shellcheck source=domain-routing-bird.env +source "$CONFIG_FILE" + +# ============================================ +# ВАЛИДАЦИЯ ПЕРЕМЕННЫХ +# ============================================ +required_vars=( + "DOMAINS_FILE" + "BIRD_CONF_DIR" + "BIRD_CONF_FILE" + "ROUTE_GATEWAY" + "ROUTE_INTERFACE" +) + +for var in "${required_vars[@]}"; do + [[ -z "${!var:-}" ]] && { echo "❌ Переменная $var не установлена в $CONFIG_FILE"; exit 1; } +done + +# ============================================ +# ФУНКЦИИ +# ============================================ + +log_info() { + echo "ℹ️ $1" + logger -t "domain-routing-bird" "$1" +} + +log_error() { + echo "❌ $1" + logger -t "domain-routing-bird" "ERROR: $1" +} + +check_dependencies() { + log_info "Проверка зависимостей..." + + local missing=() + + for cmd in birdc dig; do + if ! command -v "$cmd" &> /dev/null; then + missing+=("$cmd") + fi + done + + if [[ ${#missing[@]} -gt 0 ]]; then + log_error "Отсутствуют команды: ${missing[*]}" + exit 1 + fi + + log_info "Все зависимости установлены" +} + +resolve_domains_to_ips() { + log_info "Разрешение доменов в IP-адреса..." + + local temp_file=$(mktemp) + local ipv4_file=$(mktemp) + local ipv6_file=$(mktemp) + + while IFS= read -r domain || [[ -n "$domain" ]]; do + # Пропускаем комментарии и пустые строки + [[ -z "$domain" ]] && continue + [[ "$domain" =~ ^[[:space:]]*# ]] && continue + + # Очищаем домен от лишних символов + domain=$(echo "$domain" | tr -d '[:space:]') + + # Разрешаем домен в IPv4 + if [[ "${ENABLE_IPV4:-true}" == "true" ]]; then + local ipv4 + ipv4=$(dig +short "$domain" A 2>/dev/null || true) + if [[ -n "$ipv4" ]]; then + echo "$ipv4" >> "$ipv4_file" + fi + fi + + # Разрешаем домен в IPv6 + if [[ "${ENABLE_IPV6:-false}" == "true" ]]; then + local ipv6 + ipv6=$(dig +short "$domain" AAAA 2>/dev/null || true) + if [[ -n "$ipv6" ]]; then + echo "$ipv6" >> "$ipv6_file" + fi + fi + done < "$DOMAINS_FILE" + + # Объединяем результаты + if [[ "${ENABLE_IPV4:-true}" == "true" ]] && [[ -s "$ipv4_file" ]]; then + cat "$ipv4_file" >> "$temp_file" + fi + + if [[ "${ENABLE_IPV6:-false}" == "true" ]] && [[ -s "$ipv6_file" ]]; then + cat "$ipv6_file" >> "$temp_file" + fi + + # Удаляем дубликаты + local unique_file=$(mktemp) + sort -u "$temp_file" > "$unique_file" + + local count=$(wc -l < "$unique_file") + log_info "Разрешено $count уникальных IP-адресов" + + rm -f "$temp_file" "$ipv4_file" "$ipv6_file" + + echo "$unique_file" +} + +# Функция для суммаризации IPv4 маршрутов по маске (до /24) +aggregate_routes_ipv4() { + local ip_file="$1" + local output_file="$2" + + log_info "Суммаризация IPv4 маршрутов до /24..." + + # Создаём временный файл для сетей + local network_file=$(mktemp) + + # Преобразуем IP в сети /24 + while read -r ip; do + if [[ -n "$ip" ]] && [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + # Получаем сеть /24 для IP + local network=$(echo "$ip" | cut -d. -f1-3) + echo "${network}.0/24" + fi + done < "$ip_file" | sort -u > "$network_file" + + local total_networks=$(wc -l < "$network_file") + log_info "Создано $total_networks сетей /24" + + # Копируем в выходной файл + cp "$network_file" "$output_file" + local aggregated_count=$(wc -l < "$output_file") + log_info "После суммаризации: $aggregated_count сетей" + + rm -f "$network_file" +} + +generate_bird_config() { + local ip_file="$1" + local output_file="$BIRD_CONF_DIR/$BIRD_CONF_FILE" + + log_info "Генерация конфига BIRD: $output_file" + + mkdir -p "$BIRD_CONF_DIR" + + # Создаём заголовок + echo "# Auto-generated domain routing config" > "$output_file" + echo "# Generated: $(date)" >> "$output_file" + echo "# Source: $DOMAINS_FILE" >> "$output_file" + echo "" >> "$output_file" + + # Если включена суммаризация IPv4, используем агрегированные сети + if [[ "${ENABLE_IPV4:-true}" == "true" ]] && [[ "${AGGREGATE_TO_24:-true}" == "true" ]]; then + local aggregated_file=$(mktemp) + aggregate_routes_ipv4 "$ip_file" "$aggregated_file" + + # Генерируем route statements для суммаризированных IPv4 сетей + while read -r network; do + if [[ -n "$network" ]]; then + echo "route $network via $ROUTE_GATEWAY on $ROUTE_INTERFACE;" >> "$output_file" + fi + done < "$aggregated_file" + + rm -f "$aggregated_file" + else + # Без суммаризации - добавляем отдельные IP + while read -r ip; do + if [[ -n "$ip" ]]; then + # Определяем тип IP (IPv4 или IPv6) + if [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + if [[ "${ENABLE_IPV4:-true}" == "true" ]]; then + if [[ "${AGGREGATE_TO_24:-true}" == "true" ]]; then + # Конвертируем в /24 сеть + local network=$(echo "$ip" | cut -d. -f1-3) + echo "route ${network}.0/24 via $ROUTE_GATEWAY on $ROUTE_INTERFACE;" >> "$output_file" + else + echo "route $ip via $ROUTE_GATEWAY on $ROUTE_INTERFACE;" >> "$output_file" + fi + fi + elif [[ "$ip" =~ : ]]; then + if [[ "${ENABLE_IPV6:-false}" == "true" ]]; then + echo "route $ip via $ROUTE_GATEWAY on $ROUTE_INTERFACE;" >> "$output_file" + fi + fi + fi + done < "$ip_file" + fi + + log_info "Конфиг BIRD сгенерирован: $output_file" +} + +reload_bird() { + log_info "Перезагрузка BIRD..." + + if /usr/sbin/birdc configure; then + logger "BIRD domain routing reconfigured" + log_info "BIRD успешно перезагружен" + else + log_error "Ошибка при перезагрузке BIRD" + exit 1 + fi +} + +save_config_hash() { + local output_file="$BIRD_CONF_DIR/$BIRD_CONF_FILE" + local hash_file="$BIRD_CONF_DIR/.${BIRD_CONF_FILE}.md5" + + if [[ -f "$output_file" ]]; then + md5sum "$output_file" | cut -c1-32 > "$hash_file" + log_info "MD5 хеш сохранён: $hash_file" + fi +} + +restore_config_on_boot() { + log_info "Настройка автовосстановления конфига BIRD..." + + # Создаём systemd service для перезагрузки BIRD + local service_file="/etc/systemd/system/bird-domain-routing.service" + + cat > "$service_file" << EOF +[Unit] +Description=BIRD Domain Routing Configuration +After=network.target +Wants=network.target + +[Service] +Type=oneshot +ExecStart=/usr/sbin/birdc configure +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target +EOF + + systemctl enable bird-domain-routing.service + log_info "Systemd сервис создан и включён: bird-domain-routing.service" +} + +# ============================================ +# ОСНОВНАЯ ЛОГИКА +# ============================================ + +main() { + echo "==============================================" + echo "Domain-based Routing for BIRD BGP" + echo "==============================================" + + # Проверка прав суперпользователя + if [[ $EUID -ne 0 ]]; then + log_error "Скрипт должен быть запущен от имени root" + exit 1 + fi + + # Опции командной строки + while [[ $# -gt 0 ]]; do + case $1 in + --domains-file) + DOMAINS_FILE="$2" + shift 2 + ;; + --bird-conf-dir) + BIRD_CONF_DIR="$2" + shift 2 + ;; + --bird-conf-file) + BIRD_CONF_FILE="$2" + shift 2 + ;; + --gateway) + ROUTE_GATEWAY="$2" + shift 2 + ;; + --interface) + ROUTE_INTERFACE="$2" + shift 2 + ;; + --help) + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --domains-file FILE Файл со списком доменов" + echo " --bird-conf-dir DIR Директория конфигов BIRD" + echo " --bird-conf-file FILE Имя файла конфига" + echo " --gateway GATEWAY Шлюз для маршрутов" + echo " --interface INTERFACE Интерфейс для маршрутов" + echo " --help Показать эту справку" + exit 0 + ;; + *) + log_error "Неизвестный параметр: $1" + exit 1 + ;; + esac + done + + echo "" + log_info "Конфигурация:" + echo " Domains file: $DOMAINS_FILE" + echo " BIRD conf dir: $BIRD_CONF_DIR" + echo " BIRD conf file: $BIRD_CONF_FILE" + echo " Route gateway: $ROUTE_GATEWAY" + echo " Route interface: $ROUTE_INTERFACE" + echo " IPv4 enabled: ${ENABLE_IPV4:-true}" + echo " IPv6 enabled: ${ENABLE_IPV6:-false}" + echo " Aggregate to /24: ${AGGREGATE_TO_24:-true}" + echo "" + + # Шаги выполнения + check_dependencies + local ip_file + ip_file=$(resolve_domains_to_ips) + generate_bird_config "$ip_file" + reload_bird + save_config_hash + restore_config_on_boot + + echo "" + log_info "==========================================" + log_info "Domain-based routing для BIRD настроен!" + log_info "==========================================" +} + +# Запуск основной функции +main "$@" diff --git a/domain-routing.env.example b/domain-routing.env.example new file mode 100644 index 0000000..9814c1d --- /dev/null +++ b/domain-routing.env.example @@ -0,0 +1,37 @@ +#!/bin/bash + +# Конфигурационный файл для domain-based routing +# Скопируйте этот файл в domain-routing.env и настройте под ваши нужды + +# ============================================ +# ПУТИ И ФАЙЛЫ +# ============================================ + +# Файл со списком доменов для маршрутизации (один домен на строку) +# Domains_FILE="/opt/domain-routing/domains.txt" + +# Директория для конфигов dnsmasq +# DNSMASQ_CONF_DIR="/etc/dnsmasq.d" + +# Файл резолва (для DNS) +# RESOLV_CONF="/etc/resolv.conf" + +# ============================================ +# IPSET И FIREWALL +# ============================================ + +# Имя ipset (до��жно быть уникальным) +# IPSET_NAME="domain_routing" + +# Имя зоны firewalld +# FIREWALL_ZONE="domain_routing" + +# ============================================ +# ДОПОЛНИТЕЛЬНЫЕ НАСТРОЙКИ +# ============================================ + +# Дополнительные параметры ipset (hashsize, timeout и т.д.) +# IPSET_OPTIONS="hashsize 4096" + +# Время жизни записей в ipset (в секундах, 0 = бесконечно) +# IPSET_TIMEOUT=0 diff --git a/domain-routing.sh b/domain-routing.sh new file mode 100755 index 0000000..936c0e1 --- /dev/null +++ b/domain-routing.sh @@ -0,0 +1,303 @@ +#!/bin/bash + +# Скрипт для domain-based routing под RHEL с firewalld, ipset, dnsmasq +# Направляет трафик определённых доменов через отдельный интерфейс/IP-набор + +set -euo pipefail + +# ============================================ +# КОНФИГУРАЦИЯ +# ============================================ +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CONFIG_FILE="$SCRIPT_DIR/domain-routing.env" + +# Загрузка конфигурации +if [[ ! -f "$CONFIG_FILE" ]]; then + echo "❌ Ошибка: не найден конфигурационный файл $CONFIG_FILE" + echo "Создайте файл на основе domain-routing.env.example" + exit 1 +fi + +# shellcheck source=domain-routing.env +source "$CONFIG_FILE" + +# ============================================ +# ВАЛИДАЦИЯ ПЕРЕМЕННЫХ +# ============================================ +required_vars=( + "DOMAINS_FILE" + "IPSET_NAME" + "FIREWALL_ZONE" + "DNSMASQ_CONF_DIR" + "RESOLV_CONF" +) + +for var in "${required_vars[@]}"; do + [[ -z "${!var:-}" ]] && { echo "❌ Переменная $var не установлена в $CONFIG_FILE"; exit 1; } +done + +# ============================================ +# ФУНКЦИИ +# ============================================ + +log_info() { + echo "ℹ️ $1" + logger -t "domain-routing" "$1" +} + +log_error() { + echo "❌ $1" + logger -t "domain-routing" "ERROR: $1" +} + +check_dependencies() { + log_info "Проверка зависимостей..." + + local missing=() + + for cmd in firewall-cmd ipset dnsmasq; do + if ! command -v "$cmd" &> /dev/null; then + missing+=("$cmd") + fi + done + + if [[ ${#missing[@]} -gt 0 ]]; then + log_error "Отсутствуют команды: ${missing[*]}" + exit 1 + fi + + log_info "Все зависимости установлены" +} + +create_ipset() { + log_info "Создание ipset '$IPSET_NAME'..." + + # Удаляем старый ipset если есть + if ipset list "$IPSET_NAME" &> /dev/null; then + ipset destroy "$IPSET_NAME" + log_info "Удалён старый ipset '$IPSET_NAME'" + fi + + # Создаём новый ipset (hash:net для IP-сетей) + ipset create "$IPSET_NAME" hash:net hashsize 4096 family inet + log_info "Создан ipset '$IPSET_NAME'" +} + +resolve_domains_to_ips() { + log_info "Разрешение доменов в IP-адреса..." + + local temp_file=$(mktemp) + local resolved_count=0 + + while IFS= read -r domain || [[ -n "$domain" ]]; do + # Пропускаем комментарии и пустые строки + [[ -z "$domain" ]] && continue + [[ "$domain" =~ ^[[:space:]]*# ]] && continue + + # Очищаем домен от лишних символов + domain=$(echo "$domain" | tr -d '[:space:]') + + # Разрешаем домен в IP + local ips + ips=$(dig +short "$domain" A 2>/dev/null || true) + + if [[ -n "$ips" ]]; then + echo "$ips" | while read -r ip; do + if [[ -n "$ip" ]]; then + echo "$ip" >> "$temp_file" + ((resolved_count++)) || true + fi + done + else + log_error "Не удалось разрешить домен: $domain" + fi + done < "$DOMAINS_FILE" + + # Добавляем IP в ipset + if [[ -s "$temp_file" ]]; then + while read -r ip; do + ipset add "$IPSET_NAME" "$ip" + done < "$temp_file" + + log_info "Добавлено ${resolved_count} IP-адресов в ipset '$IPSET_NAME'" + else + log_error "Не удалось разрешить ни один домен" + rm -f "$temp_file" + exit 1 + fi + + rm -f "$temp_file" +} + +configure_firewall() { + log_info "Настройка firewalld..." + + # Проверяем зону + if ! firewall-cmd --query-zone="$FIREWALL_ZONE" &> /dev/null; then + log_info "Создание новой зоны '$FIREWALL_ZONE'..." + firewall-cmd --new-zone="$FIREWALL_ZONE" --permanent + fi + + # Добавляем ipset в зону + firewall-cmd --zone="$FIREWALL_ZONE" --add-ipset="$IPSET_NAME" --permanent + + # Перезагружаем firewalld + firewall-cmd --reload + + log_info "Firewalld настроен для ipset '$IPSET_NAME' в зоне '$FIREWALL_ZONE'" +} + +configure_dnsmasq() { + log_info "Настройка dnsmasq..." + + # Создаём конфиг для dnsmasq + local dnsmasq_config="$DNSMASQ_CONF_DIR/domain-routing.conf" + + mkdir -p "$DNSMASQ_CONF_DIR" + + # Очищаем старый конфиг + echo "# Auto-generated domain routing config" > "$dnsmasq_config" + echo "# Generated: $(date)" >> "$dnsmasq_config" + + # Добавляем правила для каждого домена + while IFS= read -r domain || [[ -n "$domain" ]]; do + [[ -z "$domain" ]] && continue + [[ "$domain" =~ ^[[:space:]]*# ]] && continue + + domain=$(echo "$domain" | tr -d '[:space:]') + + # Генерируем IP для каждого домена + local ips + ips=$(dig +short "$domain" A 2>/dev/null || true) + + if [[ -n "$ips" ]]; then + echo "$ips" | while read -r ip; do + if [[ -n "$ip" ]]; then + echo "server=/$domain/$ip" >> "$dnsmasq_config" + fi + done + fi + done < "$DOMAINS_FILE" + + log_info "Конфиг dnsmasq сгенерирован: $dnsmasq_config" + + # Перезагружаем dnsmasq + if systemctl is-active --quiet dnsmasq; then + systemctl reload dnsmasq + log_info "Dnsmasq перезагружен" + else + log_info "Dnsmasq не запущен. Запустите: systemctl start dnsmasq" + fi +} + +save_ipset() { + log_info "Сохранение ipset..." + + local save_dir="/etc/ipset" + mkdir -p "$save_dir" + + ipset save "$IPSET_NAME" -file "$save_dir/$IPSET_NAME.ipset" + log_info "Ipset сохранён в $save_dir/$IPSET_NAME.ipset" +} + +restore_ipset_on_boot() { + log_info "Настройка автовосстановления ipset..." + + # Создаём systemd service для восстановления ipset + local service_file="/etc/systemd/system/ipset-restore@$IPSET_NAME.service" + + cat > "$service_file" << EOF +[Unit] +Description=Restore ipset $IPSET_NAME on boot +After=network.target + +[Service] +Type=oneshot +ExecStart=/sbin/ipset restore < /etc/ipset/$IPSET_NAME.ipset +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target +EOF + + systemctl enable "ipset-restore@$IPSET_NAME.service" + log_info "Systemd сервис создан и включён" +} + +# ============================================ +# ОСНОВНАЯ ЛОГИКА +# ============================================ + +main() { + echo "==============================================" + echo "Domain-based Routing Setup Script" + echo "==============================================" + + # Проверка прав суперпользователя + if [[ $EUID -ne 0 ]]; then + log_error "Скрипт должен быть запущен от имени root" + exit 1 + fi + + # Опции командной строки + while [[ $# -gt 0 ]]; do + case $1 in + --domains-file) + DOMAINS_FILE="$2" + shift 2 + ;; + --ipset-name) + IPSET_NAME="$2" + shift 2 + ;; + --firewall-zone) + FIREWALL_ZONE="$2" + shift 2 + ;; + --dnsmasq-conf) + DNSMASQ_CONF_DIR="$2" + shift 2 + ;; + --help) + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --domains-file FILE Файл со списком доменов" + echo " --ipset-name NAME Имя ipset (по умолчанию из config)" + echo " --firewall-zone ZONE Зона firewalld (по умолчанию из config)" + echo " --dnsmasq-conf DIR Директория конфигов dnsmasq" + echo " --help Показать эту справку" + exit 0 + ;; + *) + log_error "Неизвестный параметр: $1" + exit 1 + ;; + esac + done + + echo "" + log_info "Конфигурация:" + echo " Domains file: $DOMAINS_FILE" + echo " IPset name: $IPSET_NAME" + echo " Firewall zone: $FIREWALL_ZONE" + echo " Dnsmasq conf: $DNSMASQ_CONF_DIR" + echo "" + + # Шаги выполнения + check_dependencies + create_ipset + resolve_domains_to_ips + configure_firewall + configure_dnsmasq + save_ipset + restore_ipset_on_boot + + echo "" + log_info "==========================================" + log_info "Domain-based routing успешно настроен!" + log_info "==========================================" +} + +# Запуск основной функции +main "$@" diff --git a/domains-bird.txt.example b/domains-bird.txt.example new file mode 100644 index 0000000..99a04db --- /dev/null +++ b/domains-bird.txt.example @@ -0,0 +1,11 @@ +# Список доменов для domain-based routing с BIRD BGP +# Один домен на строку +# Строки, начинающиеся с # - комментарии + +# Примеры доменов +google.com +www.google.com +amazon.com +cloudflare.com +github.com +# Добавьте свои домены здесь diff --git a/domains.txt.example b/domains.txt.example new file mode 100644 index 0000000..1949dc3 --- /dev/null +++ b/domains.txt.example @@ -0,0 +1,11 @@ +# Список доменов для domain-based routing +# Один домен на строку +# Строки, начинающиеся с # - комментарии + +# Примеры доменов +google.com +www.google.com +amazon.com +cloudflare.com +github.com +# Пример списка доменов для конкретной цели diff --git a/setup-example.sh b/setup-example.sh new file mode 100755 index 0000000..a61245c --- /dev/null +++ b/setup-example.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# Пример использования скрипта domain-routing.sh + +set -euo pipefail + +# Создаём конфигурационный файл +cat > domain-routing.env << 'EOF' +#!/bin/bash + +# Пути и файлы +DOMAINS_FILE="/opt/domain-routing/domains.txt" +DNSMASQ_CONF_DIR="/etc/dnsmasq.d" +RESOLV_CONF="/etc/resolv.conf" + +# IPset и firewall +IPSET_NAME="domain_routing" +FIREWALL_ZONE="domain_routing" + +# Дополнительные настройки +IPSET_OPTIONS="hashsize 4096" +IPSET_TIMEOUT=0 +EOF + +# Создаём файл со списком доменов +cat > domains.txt << 'EOF' +# Список доменов для маршрутизации +google.com +www.google.com +amazon.com +cloudflare.com +github.com +EOF + +echo "✅ Примеры конфигурационных файлов созданы" +echo "" +echo "Далее:" +echo "1. Отредактируйте domain-routing.env под ваши нужды" +echo "2. Отредактируйте domains.txt со списком доменов" +echo "3. Запустите скрипт: sudo ./domain-routing.sh"