Виртуальная лаба в домашних условиях. Часть 1: Сеть

На новогодних каникулах появилось, наконец, свободное время, чтобы заняться домашним сервером. Вместо Ubuntu 12.04 LTS был установлен VMWare ESXi 6.5, а сама система была разделена на несколько виртуалок, каждая под свою задачу.
С контейнерами пока решил не связываться – я пока не могу придумать реальную задачу, чтобы ради неё засесть за изучение Docker или LXC. Так что у нас будет классика – Debian 8 с последними апдейтами, свежая VMWare и Mikrotik (“железный”) в качестве роутера для всего этого счастья.

0. Disclaimer

Для начала о том, чего тут НЕ будет.
– Здесь не будет описываться установка VMWare или Debian
– Здесь не будет описываться настройка Mikrotik “с нуля”
– Здесь не будет описываться установка пакетов через apt (будь то ansible, zabbix или apache)
Я предполагаю, что компетенция моих читателей достаточна для того, чтобы не только прочитать всё то, что написано ниже, но и применять на практике с умом и пониманием, что вообще делается и для чего.

1. Связь. Настройка аплинков, failover, nat

С чего начинается лаба? Правильно, с сети. У меня в наличии роутер Mikrotik, два интернет-провайдера и некоторое количество клиентов, часть из которых – собственно сервер с виртуалками, часть – обычная домашняя сеть.
Разделять локалку на подсети не будем (пока), без ipv6 тоже обойдемся (хотя в будущем, разумеется, попробуем). Зато сделаем балансировку трафика (виртуалки должны ходить через резервный канал связи по умолчанию) и failover. А т.к. у меня на виртуалках есть ресурсы, опубликованные в интернет, то хотелось бы иметь к ним доступ изнутри сети, не заморачиваясь с перенастройкой DNS.
Нечто похожее я описывал 4,5 года назад в своей статье про policy-based routing на Mikrotik. С тех пор утекло много воды (и версий ROS), так что пользоваться ip rule уже не модно – гораздо проще маркировать пакеты с помощью mangle-цепочки фаервола.

1.1 Два провайдера: балансировка нагрузки

Имеем: два провайдера.
Один из них отдаёт нам честный белый ip-адрес по DHCP. Его мы будем использовать для доступа к виртуалкам снаружи и выпуска самих виртуалок в инет. Для остальных клиентов в локалке он является резервным.
Второй провайдер интернет даёт через l2tp-подключение, и выделенный ip не предоставляет. Его мы будем использовать для всей остальной локалки, как основной, и резервный для виртуалок. В принципе, можно даже настроить DynDNS, чтобы не терять связи с виртуалками в случае падения первого канала. Но не будем пытаться объять необъятное и впихнуть невпихуемое.
Основная задача – настроить автоматическое переключение между каналами в случае падения одного из линков.

Начальная конфигурация:
Активируем dhcp-клиенты на интерфейсах, куда включены провайдеры (у меня это первый и пятый порты)
[su_spoiler title=”ip dhcp-client” style=”fancy” icon=”plus-circle”]
/ip dhcp-client option
add code=55 name=parameter_request_list value=0x01F90321062A
/ip dhcp-client
add add-default-route=no comment=BeeLine dhcp-options=parameter_request_list disabled=no interface=ether1-gateway
add comment=Skynet default-route-distance=10 dhcp-options=hostname,clientid disabled=no interface=ether5-slave-local
[/su_spoiler]

Поднимаем l2tp-сессию с оператором:
[su_spoiler title=”interface l2tp-client” style=”fancy” icon=”plus-circle”]

/interface l2tp-client
add add-default-route=yes allow=chap connect-to=tp.internet.beeline.ru disabled=no keepalive-timeout=disabled max-mru=1420 max-mtu=1420 name=l2tp-beeline password=Aa123456789 profile=ppp-profile-beeline user=0123456789

[/su_spoiler]

Настраиваем роутинг. Т.к. нам нужно иметь два активных маршрута, то используем routing-mark. Да, маршруты придётся дублировать с теми, что прилетели по dhcp, а что делать?
В некоторых мануалах по настройке мультипровайдерной конфигурации советуют использовать директиву check-gateway в вариантe arp или ping. Это разумно, но нужно быть внимательным – шлюз провайдера на icmp-запросы отвечать, в общем случае, не обязан.
[su_spoiler title=”ip route” style=”fancy” icon=”plus-circle”]

/ip route
#Дефолтные маршруты. Для каждой марки их две, т.к. нам нужно проверять оба шлюза и выпускать трафик тоже через обоих.
add distance=1 gateway=128.75.224.1 routing-mark=route_to_bee
add distance=100 gateway=94.20.244.1 routing-mark=route_to_bee
add distance=1 gateway=94.20.244.1 routing-mark=route_to_sky
add distance=100 gateway=128.75.224.1 routing-mark=route_to_sky

#Маршруты на внутрисеть провайдера, который является основным для локалки (у меня это билайн)
add distance=1 dst-address=10.0.0.0/8 gateway=10.75.64.1 routing-mark=route_to_bee
add distance=1 dst-address=78.107.196.0/22 gateway=10.75.64.1 routing-mark=route_to_bee
add distance=1 dst-address=85.21.0.0/24 gateway=10.75.64.1 routing-mark=route_to_bee
add distance=1 dst-address=85.21.59.0/24 gateway=10.75.64.1 routing-mark=route_to_bee
add distance=1 dst-address=85.21.192.0/24 gateway=10.75.64.1 routing-mark=route_to_bee
add distance=1 dst-address=89.179.76.0/24 gateway=10.75.64.1 routing-mark=route_to_bee
add distance=1 dst-address=213.234.192.0/24 gateway=10.75.64.1 routing-mark=route_to_bee

#Маршруты на внутрисеть второго провайдера (для торрентов будет полезно, чтобы почём зря не грузить l2tp-подключение)
add distance=1 dst-address=93.100.0.0/16 gateway=94.20.244.1 routing-mark=route_to_sky
add distance=1 dst-address=94.19.0.0/16 gateway=94.20.244.1 routing-mark=route_to_sky
add distance=1 dst-address=188.242.0.0/15 gateway=94.20.244.1 routing-mark=route_to_sky

#Специальные маршруты до DNS-серверов и l2tp-терминаторов Билайн, чтобы соединение, ненароком, не установилось через другого провайдера
add distance=1 dst-address=83.102.254.223/32 gateway=10.75.64.1
add distance=100 dst-address=83.102.254.223/32 type=blackhole
add distance=1 dst-address=83.102.255.55/32 gateway=10.75.64.1
add distance=100 dst-address=83.102.255.55/32 type=blackhole
add distance=1 dst-address=85.21.192.3/32 gateway=10.75.64.1
add distance=1 dst-address=213.234.192.8/32 gateway=10.75.64.1

[/su_spoiler]

Настраиваем правила таблицы mangle фаервола. С помощью этих правил мы будем помечать соединения пришедшие через тот или другой аплинк, а также маркировать исходящие из локалки пакеты, чтобы они попадали в нужный аплинк
[su_spoiler title=”ip firewall mangle” style=”fancy” icon=”plus-circle”]

/ip firewall mangle
add action=mark-connection chain=input in-interface=l2tp-beeline new-connection-mark=conn_bee passthrough=no
add action=mark-connection chain=input in-interface=ether5-slave-local new-connection-mark=conn_sky passthrough=no

add action=mark-routing chain=output connection-mark=conn_bee new-routing-mark=route_to_bee passthrough=no
add action=mark-routing chain=output connection-mark=conn_sky new-routing-mark=route_to_sky passthrough=no

#Здесь мы балансируем каналы между потребителями. По умолчанию в SkyNet отправляются виртуалки (address-list "VMs"), а в билайн все остальные (address-list "LAN"). Для сетей обоих провайдеров пакеты маркируются вне зависимости от источника.
add action=mark-routing chain=prerouting comment="Routes to SkyNet LAN" dst-address-list=Skynet new-routing-mark=route_to_sky passthrough=no src-address-list=LAN
add action=mark-routing chain=prerouting comment="Routes to BeeLine LAN" dst-address-list=BeeLine new-routing-mark=route_to_bee passthrough=no src-address-list=LAN
add action=mark-routing chain=prerouting comment="VMs go to Internet over SkyNet by default" new-routing-mark=route_to_sky passthrough=no src-address-list=VMs
add action=mark-routing chain=prerouting comment="LAN go to Internet over BeeLine by default" new-routing-mark=route_to_bee passthrough=no src-address-list=LAN

[/su_spoiler]

Правила NAT. Ну тут совсем всё просто: делаем src-nat на всех исходящих интерфейсах (у меня это два eth-интерфейса и l2tp).
[su_spoiler title=”ip firewall nat” style=”fancy” icon=”plus-circle”]

/ip firewall nat
add action=masquerade chain=srcnat comment="NAT to SkyNet" out-interface=ether5
add action=masquerade chain=srcnat comment="NAT to Beeline" out-interface=l2tp-beeline
add action=masquerade chain=srcnat comment="NAT to BeeLine LAN" out-interface=ether1

[/su_spoiler]

Осталось только собрать всё это вместе и проверить, что всё работает. Отключить сначала один шнурок, потом второй. Переключение должно произойти автоматически.

В таблице маршрутизации после всех настроек мы увидим примерно следующее (сейчас работают оба провайдера):
[su_spoiler title=”ip route print” style=”fancy” icon=”plus-circle”]

Flags: X - disabled, A - active, D - dynamic, C - connect, S - static, r - rip, b - bgp, o - ospf, m - mme, B - blackhole, U - unreachable, P - prohibit
# DST-ADDRESS PREF-SRC GATEWAY DISTANCE
0 A S 0.0.0.0/0 128.75.224.1 1
1 S 0.0.0.0/0 94.20.244.1 100
2 A S 10.0.0.0/8 10.75.64.1 1
3 A S 78.107.196.0/22 10.75.64.1 1
4 A S 85.21.0.0/24 10.75.64.1 1
5 A S 85.21.59.0/24 10.75.64.1 1
6 A S 85.21.192.0/24 10.75.64.1 1
7 A S 89.179.76.0/24 10.75.64.1 1
8 A S 213.234.192.0/24 10.75.64.1 1
9 A S 0.0.0.0/0 94.20.244.1 1
10 S 0.0.0.0/0 128.75.224.1 100
11 A S 93.100.0.0/16 94.20.244.1 1
12 A S 94.19.0.0/16 94.20.244.1 1
13 A S 188.242.0.0/15 94.20.244.1 1
14 ADS 0.0.0.0/0 128.75.224.1 0
15 DS 0.0.0.0/0 94.20.244.1 10
16 ADC 10.0.10.1/32 10.0.10.2 l2tp-nixman 0
17 ADC 10.75.64.0/21 10.75.64.93 ether1-gateway 0
18 A S 83.102.254.223/32 10.75.64.1 1
19 SB 83.102.254.223/32 100
20 ADS 83.102.255.55/32 10.75.64.1 0
21 S 83.102.255.55/32 10.75.64.1 1
22 SB 83.102.255.55/32 100
24 A S 85.21.192.3/32 10.75.64.1 1
25 ADC 94.20.244.0/24 94.20.244.244 ether5-slave-local 0
26 ADC 128.75.224.1/32 128.75.249.101 l2tp-beeline 0
30 S 192.168.1.0/24 192.168.10.0 1
31 ADC 192.168.88.0/24 192.168.88.1 bridge-local 0
34 A S 213.234.192.8/32 10.75.64.1 1

[/su_spoiler]

1.2 Hairpin NAT в условиях балансировки трафика

Балансировку мы настроили, теперь идем дальше. Некоторые ресурсы, расположенные на виртуалках, опубликованы в инет. Изнутри локалки тоже хотелось бы иметь к ним доступ, и при этом не мудрить с подменой DNS-записей. При обычном DST-NAT у нас ничего не получится – т.к. сервер увидит, что src-ip находится в одной с ним подсети, и просто пошлёт ему ответный пакет, минуя роутер. Таким образом, TCP-соединение не сможет установиться. Чтобы обойти эту проблему, был придуман механизм hairpin NAT, когда при обращении из локалки на внешний ip роутера делается не только DST-NAT, но и SRC-NAT в адрес роутера, смотрящий в локалку.

Настраивается всё это достаточно просто. Для начала, добавляем нужные записи в настройки NAT:
[su_spoiler title=”ip firewall nat” style=”fancy” icon=”plus-circle”]

/ip firewall nat
add action=masquerade chain=srcnat comment="HTTP Hairpin" dst-address=192.168.88.189 dst-port=80 out-interface=bridge-local protocol=tcp src-address=192.168.88.0/24
add action=masquerade chain=srcnat comment="HTTPS Hairpin" dst-address=192.168.88.189 dst-port=443 out-interface=bridge-local protocol=tcp src-address=192.168.88.0/24
add action=dst-nat chain=dstnat comment="HTTPS proxy" dst-address=94.20.244.244 dst-port=443 protocol=tcp to-addresses=192.168.88.189 to-ports=443
add action=dst-nat chain=dstnat comment="HTTP proxy" dst-address=94.20.244.244 dst-port=80 protocol=tcp to-addresses=192.168.88.189 to-ports=80

[/su_spoiler]
Здесь 192.168.88.189 – это адрес моего прокси-сервера, который уже более “детально” разбирает запросы и раскидывает их по нужным виртуалкам.

В таблице mangle мы добавляем правила hairpin в “исключения” (я просто навесил routing-mark, который нигде более не используется), чтобы они не попадали в правила балансировки исходящего трафика. Эти правила необходимо поместить ДО правил, отвечающих за маркировку пакетов
[su_spoiler title=”ip firewall mangle” style=”fancy” icon=”plus-circle”]

/ip firewall mangle
...
add action=mark-packet chain=prerouting comment="Hairpin NAT" dst-address-list=Self new-packet-mark=hairpin passthrough=no src-address-list=LAN
add action=mark-packet chain=prerouting comment="Hairpin NAT" dst-address-list=LAN new-packet-mark=hairpin passthrough=no src-address-list=Self
add action=mark-routing chain=prerouting comment="Routes to SkyNet LAN" dst-address-list=Skynet new-routing-mark=route_to_sky passthrough=no src-address-list=LAN
add action=mark-routing chain=prerouting comment="Routes to BeeLine LAN" dst-address-list=BeeLine new-routing-mark=route_to_bee passthrough=no src-address-list=LAN
add action=mark-routing chain=prerouting comment="VMs go to Internet over SkyNet by default" new-routing-mark=route_to_sky passthrough=no src-address-list=VMs
add action=mark-routing chain=prerouting comment="LAN go to Internet over BeeLine by default" new-routing-mark=route_to_bee passthrough=no src-address-list=LAN

[/su_spoiler]

Если у вас есть на роутере еще VPN для качания торрентов доступа, например, к рабочим ресурсам и сетям, то нужно поступить по аналогии и добавить правило, вешающее соответствующий routing-mark:

/ip firewall mangle
...
add action=mark-packet chain=prerouting comment="VPN to Nixman" dst-address-list=VPN new-packet-mark=nixman passthrough=no src-address-list=LAN

В dst-address VPN помещаем ресурсы, к которым мы ходим через VPN.

На этом всё. Сеть мы настроили, теперь можно идти дальше – к настройке reverse proxy, ssl и прочего хозяйства.

Бонус

Бонусом к этой части я оставлю два скрипта, которые существенно ускоряют процесс переключения. Первый определяет шлюз по умолчанию, выданный по l2tp (а он время от времени меняется), и подставляет его в наш маршрут.
[su_spoiler title=”system script” style=”fancy” icon=”plus-circle”]

/system script
add comment="Detect changes of BeeLine gateway IP" name=beegwipchecker owner=admin policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive source="#default gw in marked route\
\n:local currbeegwip [/ip route get [find dst-address=\"0.0.0.0/0\" and routing-mark=\"route_to_bee\"] gateway]\
\n#Detect actual beeline default gw\
\n:local newbeegwip [/ip route get [find gateway=l2tp-beeline] dst-address]\
\n:set newbeegwip [:pick \$newbeegwip 0 [:find \$newbeegwip \"/\"]]\
\n:if (\$currbeegwip!=\$newbeegwip) do={/ip route set [find dst-address=\"0.0.0.0/0\" and routing-mark=\"route_to_bee\"] gateway=\$newbeegwip}"


[/su_spoiler]

Теперь добавляем наш скрипт в шедулер.
Заодно пишем второй – он раз в 30 секунд резолвит имя l2tp-терминатора Билайн через его же DNS-сервер (т.к. при переключении на резервный канал становятся активными его DNS-сервера, и достучаться до точки входа становится невозможно).
[su_spoiler title=”system scheduler” style=”fancy” icon=”plus-circle”]

/system scheduler
add interval=5s name=bee_gwip_checker on-event=beegwipchecker policy=read,write,policy,test,sniff start-date=jan/07/2017 start-time=18:58:42
add comment="Resolve l2tp gateway for beeline" interval=30s name=bee_tp_resolver on-event=":resolve tp.internet.beeline.ru server=213.234.192.8" policy=\
ftp,reboot,read,write,policy,test,password,sniff,sensitive start-date=jan/08/2017 start-time=02:41:55

[/su_spoiler]

Вот теперь точно всё. Ждите следующую серию 😉

2 Comments

  1. Возможно ли пояснить четвёртый раздел кода с маркировкой по листам. В коде присутствуют три листа: Skynet, который судя из комента должен быть VMs, LAN ну тут всё понятно, и собственно VMs. Можно подробнее пояснить содержание этих листов?

    1. Ну да ещё и address-list=BeeLine. Что перечисляется в листе с именем провайдера?

Leave a Reply