Как настроить сетевой балансировщик нагрузки TIAR
Добавил(а) Кальметов Игорь больше 1 года назад
Балансировщик нагрузки Tiar для почтового сервера Tegu,¶
Представляет из себя классический маршрутизатор на Linux, а в качестве балансировки используется скрипт написан нами на Python
для работы с nftables и может быть полезен как сам по себе, так и для понимания принципа балансировки трафика с помощью собственного оборудования пользователя.
Рассмотрим два варианта:¶
1. Когда одна нога сетевого интерфейса балансировщика смотрит в интернет напрямую. 2. Когда одной ногой сетевого интерфейса балансировщик смотрит через VPN наружу из другой подсети.
отсюда разные настройки сетевых интерфейсов и разные настройки правил nftables.
В качестве подробного примера рассмотрим балансировщик построенный на Linux Debian 11.
При установке операционной системы в экспертном режиме создаем одну рутовую учетную запись.
Лучшим вариантом будет использованием контейнера в виртуализированной среде Proxmox.
Проверяем обновления пакетов.
apt update
Устанавливаем обновления на операционную систему.
apt-full upgrade
Удаляем iptables
apt purge iptables
Устанавливаем nftables
apt install nftables
Далее, необходимо настроить службу и автоматический старт:
systemctl enable nftables systemctl start nftables systemctl status nftables
Устанавливаем сетевые утилиты:
apt install tcpdump ethtool iftop net-tools procps
Вариант первый:¶
Одна нога балансировщика смотрит в интернет напрямую.¶
Настраиваем на балансировщике сетевые интерфейсы согласно схеме.¶
nano /etc/network/interfaces
auto lo iface lo inet loopback auto ens19 iface ens19 inet static address 75.137.210.126/24 gateway 75.137.210.1 auto ens18 iface ens18 inet static address 10.199.199.130/24
Устанавливаем bind9¶
apt install bind9
Прописываем следующие настройки.
nano /etc/default/bind9
# # run resolvconf? RESOLVCONF=no # startup options for the server OPTIONS="-4 -u bind"
Меняем DNS на 127.0.0.1
nano /etc/resolv.conf
Должно получится так:
search lan nameserver 127.0.0.1
Включаем форвардинг.
Необходимо раскоментировать строку net.ipv4.ip_forward=1
nano /etc/sysctl.d/99-sysctl.conf
Перезапускаем bind9
systemctl restart bind9
Прописываем настройки nftables согласно схеме.¶
nano /etc/nftables.conf
#!/usr/sbin/nft -f flush ruleset table ip nat { chain prerouting { type nat hook prerouting priority -150; policy accept; } chain postrouting { type nat hook postrouting priority 100; policy accept; oifname "ens19" ip saddr 10.199.199.0/24 counter masquerade } } table inet filter { chain input { type filter hook input priority 0; policy accept; ct state established,related counter accept icmp type echo-request counter accept iifname "ens19" tcp dport 2223 counter accept comment "SSH-Доступ" iifname "ens19" tcp dport http counter accept comment "Let's Encrypt auth" iifname "ens19" counter drop } chain forward { type filter hook forward priority 0; policy accept; ct state established,related counter accept iifname "ens19" mark != 333444555 counter drop } chain output { type filter hook output priority 0; policy accept; } }
P. S
Если в nftables в основной цепочке forward последним правилом настроено отбрасывание всех внешних пакетов, то необходимо исключить из этого правила пакеты с меткой 333444555:
iifname "eth1" mark != 333444555 counter drop
Разрешаем вход через SSH.¶
nano /etc/ssh/sshd_config
Находим строку
#PermitRootLogin prohibit-password
меняем значение на
PermitRootLogin yes
перезапускаем сервис SSH.
systemctl restart sshd
Добавляем в SSH дополнительный порт 2223¶
nano /etc/ssh/sshd_config
Port 22 Port 2223
systemctl restart sshd service nftables restart
Рестартуем procps и nftables
service procps restart service nftables restart
Устанавливаем fail2ban¶
apt install fail2ban
Приводим fail2ban в соответствии к следующим настройкам:¶
nano /etc/fail2ban/jail.d/defaults-debian.conf
[DEFAULT] bantime = 10800 findtime = 3600 ignoreip = 127.0.0.1/8 maxretry = 3 banaction = nftables-multiport [sshd] port = 2223 enabled = true
рестартуем nftables и fail2ban
service nftables restart && service fail2ban restart
Проверяем логи fail2ban на наличие ошибок.¶
tail -F /var/log/fail2ban.log
Установка и настройка скрипта балансировщика.¶
Установим дополнительные библиотеки:
apt install python3-nftables nmap python3-nmap python3-scapy
Прописываем скрипт.¶
(В самом скрипте менять ничего не нужно)
nano /opt/nft_lb.py
#!/usr/bin/env python3 import signal import socket import nftables import time from systemd.journal import JournalHandler import logging log = logging.getLogger('nft_lb') log.addHandler(JournalHandler()) log.setLevel(logging.INFO) class ServiceDestination: def __init__(self, name, dip): self.name = name self.dip = dip self.online = True class LbService: def __init__(self, name, vip, vport): self.name = name self.vip = vip self.vport = vport self.dest = {} class Event: def __init__(self): self.shutdown = False ev = Event() lb_map = {} def create_rules(): nft = nftables.Nftables() code, _, __ = nft.cmd('flush table ip lb') if code != 0: code, _, __ = nft.cmd('create table ip lb') nft.cmd('add chain ip lb prerouting { type nat hook prerouting priority 0 ; }') nft.cmd('add chain ip lb forward { type filter hook forward priority -10 ; }') for svc_name in lb_map.keys(): nft.cmd(f'add chain ip lb dnat_{svc_name}') nft.cmd(f'add rule ip lb prerouting ip daddr {lb_map[svc_name].vip} tcp dport {lb_map[svc_name].vport} counter jump dnat_{svc_name}') dest_count = len(lb_map[svc_name].dest) dest_list = [] for dest_num, dest_name in enumerate(lb_map[svc_name].dest.keys()): dest_list.append(f'{dest_num} : {lb_map[svc_name].dest[dest_name].dip}') nft.cmd(f'add rule ip lb forward ip daddr {lb_map[svc_name].dest[dest_name].dip} tcp dport {lb_map[svc_name].vport} counter mark set 333444555 accept comment "[{svc_name}] -> {dest_name}"') nft.cmd(f'add rule ip lb dnat_{svc_name} counter dnat to numgen inc mod {dest_count} map {{ {", ".join(dest_list)} }}') def recreate_service(svc_name): nft = nftables.Nftables() dest_list = [] online_exists = False for dest_name in lb_map[svc_name].dest.keys(): if lb_map[svc_name].dest[dest_name].online: dest_list.append(lb_map[svc_name].dest[dest_name].dip) online_exists = True dest_count = len(dest_list) dest_map_list = [] for num, dest in enumerate(dest_list): dest_map_list.append(f'{num} : {dest}') nft.cmd(f'flush chain ip lb dnat_{svc_name}') if online_exists: nft.cmd(f'add rule ip lb dnat_{svc_name} counter dnat to numgen inc mod {dest_count} map {{ {", ".join(dest_map_list)} }}') def shutdown_lb(sig_num, frame): nft = nftables.Nftables() nft.cmd('flush table ip lb') nft.cmd('delete table ip lb') ev.shutdown = True def check_backends(): for svc_name in lb_map.keys(): need_recreate = False for dest_name in lb_map[svc_name].dest.keys(): a_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) a_socket.settimeout(1) host = lb_map[svc_name].dest[dest_name].dip port = lb_map[svc_name].vport if a_socket.connect_ex((host, port)) != 0: if lb_map[svc_name].dest[dest_name].online: log.info(f'[{svc_name}] Node "{dest_name}" offline') lb_map[svc_name].dest[dest_name].online = False need_recreate = True else: if not lb_map[svc_name].dest[dest_name].online: log.info(f'[{svc_name}] Node "{dest_name}" online') lb_map[svc_name].dest[dest_name].online = True need_recreate = True a_socket.close() if need_recreate: recreate_service(svc_name) def process_config(): with open('/opt/nft_lb_rules', 'r') as f: rules_str = f.read() for rule_line in rules_str.split('\n'): if rule_line.strip() == '': continue svc_name, vip, vport, dest_name, dip = rule_line.split('|') if svc_name not in lb_map: lb_map[svc_name] = LbService(svc_name, vip, int(vport)) lb_map[svc_name].dest[dest_name] = ServiceDestination(dest_name, dip) create_rules() if __name__ == '__main__': signal.signal(signal.SIGINT, shutdown_lb) signal.signal(signal.SIGTERM, shutdown_lb) signal.signal(signal.SIGHUP, shutdown_lb) process_config() tik_count = 0 while not ev.shutdown: if tik_count >= 20: check_backends() tik_count = 0 continue time.sleep(1) tik_count += 1
Делаем скрипт исполняемым.
chmod +x /opt/nft_lb.py
Данный скрипт использует список правил из файла /opt/nft_lb_rules в следующем формате:
smtp|1.2.3.4|25|node1|10.199.199.231 smtp|1.2.3.4|25|node2|10.199.199.232 smtp|1.2.3.4|25|node3|10.199.199.233 imap|1.2.3.4|993|node1|10.199.199.231 imap|1.2.3.4|993|node2|10.199.199.232 imap|1.2.3.4|993|node3|10.199.199.233 smtps|1.2.3.4|465|node1|10.199.199.231 smtps|1.2.3.4|465|node2|10.199.199.232 smtps|1.2.3.4|465|node3|10.199.199.233 webadm|1.2.3.4|9999|node1|10.199.199.231 webadm|1.2.3.4|9999|node2|10.199.199.232 webadm|1.2.3.4|9999|node3|10.199.199.233
Для настройки автозапуска скрипта балансировщика, надо создать, включить и запустить сервис systemd:
/etc/systemd/system/nft_lb.service
[Unit] Description=Nftables python balancer After=multi-user.target [Service] Type=simple Restart=always ExecStart=/usr/bin/python3 /opt/nft_lb.py [Install] WantedBy=multi-user.target
Команды для управления сервисом nft_lb.service
systemctl enable nft_lb.service systemctl start nft_lb.service systemctl status nft_lb.service systemctl stop nft_lb.service systemctl restart nftables.service
Ноды Tegu.¶
На стороне почтовых нод приводим сетевые настройки в соответствии схемы.¶
Не забываем о том, что шлюзом у нас является балансировщик.
nano /etc/network/interfaces
# The loopback network interface auto lo iface lo inet loopback # The primary network interface auto ens18 iface ens18 inet static address 10.199.199.131/24 gateway 10.199.199.130
Также не забываем про маршруты
nano /etc/network/routes
Маршруты прописываются в следующем формате:
# For example: # # 172.1.1.0 255.255.255.0 192.168.0.1 any
Вариант 2¶
Балансировщик одной ногой сетевого интерфейса смотрит через VPN наружу из другой подсети.¶
Отметим основные отличия:
На балансировщике настраиваем сеть
nano /etc/network/interfaces
auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 10.44.44.14/24 gateway 10.44.44.254 auto eth1 iface eth1 inet static address 10.33.33.10/24
Настройки nftables¶
nano /etc/nftables.conf
#!/usr/sbin/nft -f flush ruleset table ip nat { chain prerouting { type nat hook prerouting priority -150; policy accept; } chain postrouting { type nat hook postrouting priority 100; policy accept; oifname eth1 ip saddr 10.44.44.0/24 ip daddr { 10.33.33.20, 10.33.33.21, 10.33.33.22 } counter masquerade oifname "eth0" ip saddr 10.33.33.0/24 counter masquerade } } table inet filter { chain input { type filter hook input priority 0; policy accept; ct state established,related counter accept iifname "eth0" tcp dport 80 counter accept comment "Let's Encrypt" iifname "eth0" tcp dport 2223 counter accept comment "SSH-Доступ" iifname "eth0" tcp dport http counter accept comment "Let's Encrypt auth" iifname "eth0" counter drop } chain forward { type filter hook forward priority 0; policy accept; ct state established,related counter accept iifname "eth0" mark != 333444555 counter drop } chain output { type filter hook output priority 0; policy accept; } }
Ноды Tegu.¶
На стороне почтовых нод приводим сетевые настройки в соответствии схемы.¶
nano /etc/network/interfaces
auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 10.44.44.22/24 auto eth1 iface eth1 inet static address 10.33.33.20/24 gateway 10.33.33.10
и маршруты
nano /etc/network/routes
10.199.199.0 255.255.255.0 10.44.44.254 eth0 10.252.128.0 255.255.255.0 10.44.44.254 eth0
P/S Маршруты приведены для примера.