domain routing

This commit is contained in:
a.kazantsev 2026-07-03 16:21:48 +03:00
parent b4ac6c3b19
commit 0b8bae3ab0
10 changed files with 1261 additions and 0 deletions

View file

@ -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

185
README_DOMAIN_ROUTING.md Normal file
View file

@ -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

View file

@ -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

View file

@ -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"

344
domain-routing-bird.sh Executable file
View file

@ -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 "$@"

View file

@ -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 (до<D0B4><D0BE>жно быть уникальным)
# IPSET_NAME="domain_routing"
# Имя зоны firewalld
# FIREWALL_ZONE="domain_routing"
# ============================================
# ДОПОЛНИТЕЛЬНЫЕ НАСТРОЙКИ
# ============================================
# Дополнительные параметры ipset (hashsize, timeout и т.д.)
# IPSET_OPTIONS="hashsize 4096"
# Время жизни записей в ipset (в секундах, 0 = бесконечно)
# IPSET_TIMEOUT=0

303
domain-routing.sh Executable file
View file

@ -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 "$@"

11
domains-bird.txt.example Normal file
View file

@ -0,0 +1,11 @@
# Список доменов для domain-based routing с BIRD BGP
# Один домен на строку
# Строки, начинающиеся с # - комментарии
# Примеры доменов
google.com
www.google.com
amazon.com
cloudflare.com
github.com
# Добавьте свои домены здесь

11
domains.txt.example Normal file
View file

@ -0,0 +1,11 @@
# Список доменов для domain-based routing
# Один домен на строку
# Строки, начинающиеся с # - комментарии
# Примеры доменов
google.com
www.google.com
amazon.com
cloudflare.com
github.com
# Пример списка доменов для конкретной цели

40
setup-example.sh Executable file
View file

@ -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"