Начальные параметры:

  1. Головной офис предприятия с двумя пограничными прокси Kerio Control v.9.2.9 build 3171 (за Kerio расположен свич Cisco 3550, определяющий конфигурацию локальной сети офиса).
  2. На каждом Kerio организовано по два канала с балансировкой нагрузки до ISP (на схеме — ISP #1 и ISP #2) со статичными белыми IP.
  3. Со стороны удалённого офиса установлен MikroTik 951G-2HnD (OS v.6.43.11).
  4. На MikroTik приходят два ISP (на схеме — ISP #3 и ISP #4).

На момент написания статьи и в головном и в удалённом офисе соединение с провайдерами было по витой паре.

Список задач:


  1. Организовать IPSec VPN-соединение между MikroTik и Kerio Control, где инициатором будет выступать MikroTik.
  2. Обеспечить отказоустойчивость VPN-соединения, т.е. кроме того, что MikroTik должен отслеживать работоспособность своих ISP(статья здесь), он также должен мониторить доступность каждого сервера Kerio и определять, доступ по какому каналу (через какого ISP со стороны Kerio) будет производиться подключение.
  3. Обеспечить возможность изменения адреса сети, с которым MikroTik подключается к Kerio. Это обусловлено тем, что в головном офисе «на границе» стоят Kerio, а не маршрутизатор.


Что получим на выходе?


  1. При запуске MikroTik (scheduleStartup) будет организован канал к сети предприятия (инициатором организации канала выступит MikroTik), позволяющий удалённому пользователю работать с корпоративными ресурсами без запуска Kerio Client и без настройки дополнительного VPN-подключения средствами операционной системы;
  2. В MikroTik будет реализован Failover, позволяющий автоматически переключаться на «живой» ISP;
  3. Вы сможете настроить приоритеты для точек подключения к сети предприятия, изменяя в MikroTik принадлежность peers, настроенных на подключение к Kerio Control, к той или иной Policy Template Group;
  4. MikroTik сможет в автоматическом режиме отслеживать работоспособность Kerio Control серверов, и, в случае, если связь с приоритетной точкой разорвана, самостоятельно переключиться на «живой» канал;
  5. Если ваши Kerio Control серверы опубликованы на внешних DNS-серверах, MikroTik сможет отслеживать изменения их IP-адресов (напр. в случае, если у вас поменяется провайдер) и самостоятельно вносить изменения в свою конфигурацию (scriptSetIPSecSADstAddrFromDNS).


Сразу оговорюсь, что эта статья не является учебным пособием (с маршрутизаторами в принципе, и с MikroTik в частности я познакомился всего за два месяца (конец 2017 года) до начала создания конфигурации) и в ней не рассматриваются вопросы «почему?», здесь будет приведено описание рабочей конфигурации MikroTik, которая используется на реальном предприятии.

Примечание:
  1. Чтобы при переносе кода скриптов в MikroTik сохранились русскоязычные комментарии, перед тем как скопировать текст в буфер и перед вставкой из буфера проверьте, чтобы была включена русская раскладка клавиатуры;
  2. Если вы посчитаете, что логирование работы скриптов — это лишнее, вы можете закомментировать или удалить строки с «log warning» и «log error»;
  3. Также вы можете заметить в коде закомментированные «log warning» и «log error», это попытка добавить возможность использовать логирование на английском языке...


Итак, приступим:

Базовые параметры:

  1. Сеть головной организации (за Kerio) — 192.168.77.0/24 (здесь используем адрес сети, в которой находится Kerio в сети предприятия)
  2. Сеть филиала (за MikroTik) — 192.168.11.0/24
  3. Сеть, в которую будет мапиться сеть филиала при подключении к Kerio Control #1 — 192.168.22.0/24
  4. Сеть, в которую будет мапиться сеть филиала при подключении к Kerio Control #2 — 192.168.33.0/24
  5. IP-адрес из пула ISP #1 (Kerio #1) — 11.11.11.111
  6. IP-адрес из пула ISP #2 (Kerio #1) — 22.22.22.111
  7. IP-адрес из пула ISP #1 (Kerio #2) — 11.11.11.222
  8. IP-адрес из пула ISP #2 (Kerio #2) — 22.22.22.222
  9. IP-адрес из пула ISP #3 (MikroTik) — 33.33.33.111
  10. IP-адрес из пула ISP #4 (MikroTik) — 44.44.44.111

Первое, что необходимо сделать, это включить на MikroTik использование DDNS:

/ip cloud
set ddns-enabled=yes

В связи с тем, что на MikroTik не будет статического белого IP-адреса, дальнейшая работа конфигурации и скриптов построена на использовании DDNS.

Также IP ---> Cloud используется для определения внешнего IP-адреса MikroTik, с которого он смотрит в интернет.

Теперь произведём настройку Kerio Control #1, чтобы потом к нему уже не возвращаться:
Заходим в раздел «Интерфейсы» и добавляем новый интерфейс «VPN-туннель»…

Настройка туннеля в Kerio Control
1. В поле имя присваиваем имя интерфейсу;
2. Ставим переключатель в положение «Пассивное — только принимает входящие подключения»;
3. Тип оставляем «IPSec»;
4. Закладка «Аутентификация»:

  • 4.1 В поле «Предопределённый ключ:» вводим ключевую фразу, которая будет использована для соединения;
  • Примечание:

    Категорически рекомендую на все VPN-туннели, создаваемые на одном конкретном сервере Kerio, задавать разные ключевые фразы!

    Обусловлено это тем, что замечено, что у Kerio присутствует плавающий баг, который выражается в следующем.

    Представим, что в конфигурации Kerio, как в моём случае, присутствует несколько интерфейсов «VPN-туннель», настроенных на соединение с MikroTik, которые отличаются друг от друга только настройками в поле «Локальный ИД:» (будут рассмотрены ниже).

    Так вот, при создании (назову его так) входящего туннеля, вне зависимости от того, на какой внешний IP-адрес Kerio будет обращаться MikroTik, Kerio (почему-то) активирует первый попавшийся интерфейс, и, если IP-адрес, указанный в настройках туннеля со стороны Kerio, отличается от того, на который обращается MikroTik, туннель не организуется.

    А в случае, когда для всех туннелей указаны разные ключевые фразы, это проблема купируется.
  • 4.2 В поле «Локальный ИД:» вводим IP-адрес из пула адресов ISP #1 (в примере — 11.11.11.111), присвоенных WAN-интерфейсу Kerio Control #1, на который будет обращаться MikroTik;
  • 4.3 В поле «Отдалённый ИД:» вводим FQDN, который был получен нашим MikroTik из DDNS (IP ---> Cloud ---> DNS Name). Эта настройка позволяет нам не заботиться о том, через какого ISP MikroTik обращается к Kerio;
  • 4.4 В поле «Шифр фазы 1 (IKE):» выбираем из списка aes128-sha1-modp2048;
  • 4.5 В поле «Шифр фазы 2 (ESP):» выбираем из списка 3des-sha1-modp2048;
  • Примечание:
    Редактирование используемых настроек по умолчанию через кнопку «Изменить...»;
    Оба шифра подобраны «методом научного тыка».

5. Закладка «Удалённые сети»:

Здесь мы вводим IP-адрес локальной сети, который будет использовать MikroTik при подключении к этому конкретному серверу Kerio Control (в примере — 192.168.22.0/24).
Важно! Во всех остальных (в моём случае определяется количеством ISP со стороны Kerio) туннелях на MikroTik, на этом сервере Kerio, должен быть указан этот же IP-адрес!

Напомню, что это связано с необходимостью настройки маршрута на эту сеть из сети головного офиса.

6. Закладка «Локальные сети»:

  • 6.1 Снимаем флажок «Использовать автоматически определённые локальные сети»;
  • 6.2 Устанавливаем флажок «Использовать пользовательские сети:», а в список сетей добавляем адрес сети «накрывающий» весь диапазон адресов, используемых в локальной сети за Kerio Control (в примере — 192.168.0.0/16).

Повторяем все вышеуказанные действия для второго туннеля на этом же сервере Kerio.

Отличаться настройка второго туннеля будет только использованием другой ключевой фразы (п.4.1) и другого внешнего IP-адреса, из пула адресов ISP #2 (п.4.2) (в примере — 22.22.22.111).

Настройки Kerio Control #2 идентичны вышеописанным, за исключением адреса сети, указываемого на закладке «Удалённые сети» (п.5) (в примере — 192.168.33.0/24) и, соответственно, IP-адресов в поле «Локальный ИД:» (п.4.2), которые должны быть выбраны из IP-адресов присвоенных WAN-интерфейсам Kerio Control #2 (в примере — 11.11.11.222 и 22.22.22.222).

Далее создаём разрешающее правило на пингование наших Kerio со стороны MikroTik…

Правило пинга в Kerio Control
Заходим в раздел «Правила трафика» и создаём новое правило со следующими параметрами:

  • источник — указываем FQDN, который был получен нашим MikroTik из DDNS;
  • назначение — Брандмауэр;
  • служба — Ping;
  • также можно указать версию IP (IPv4), но это не обязательно.

Сохраняем правило с понятным для вас именем и «перетаскиваем» его в самый верх списка правил.

Эту же процедуру повторяем на втором Kerio-сервере.

Не забываем прописать маршруты на сети за MikroTik в свичи или маршрутизаторы на стороне головного офиса, чтобы сеть головного офиса знала куда направлять трафик (в моём случае это два статических маршрута на сети 192.168.22.0/24 и 192.168.33.0/24).

Со стороны головного офиса мы всё сделали, теперь переходим к MikroTik.

Начнём с того, что создадим базовые объекты конфигурации для организации и проверки VPN-туннеля.

Первым делом создадим список адресов «Local subnet». Его мы будем использовать в правилах Firewall.

/ip firewall address-list
# адрес основной локальной сети за MikroTik,
# IP-адреса из которой он раздаёт через DHCP

add address=192.168.11.0/24 list="Local subnet"

# адрес сети, в которую будет мапиться основная локальная сеть MikroTik
# при организации VPN-туннеля с Kerio Control #1
# (мы её определили когда настраивали VPN-интерфейсы на Kerio Control #1,
# на закладке "Удалённые сети")

add address=192.168.22.0/24 list="Local subnet"

# адрес сети, в которую будет мапиться основная локальная сеть MikroTik
# при организации VPN-туннеля с Kerio Control #2
# (мы её определили когда настраивали VPN-интерфейсы на Kerio Control #2,
# на закладке "Удалённые сети")

add address=192.168.33.0/24 list="Local subnet"

Далее создадим разрешающее правило для IKE-трафика и поместим его в самый верх списка правил.

add action=accept chain=input comment="VPN Allow IKE" dst-port=500 protocol=udp

Затем добавим два правила в Firewall Filter для работы с VPN-трафиком…

/ip firewall filter
add action=accept chain=forward comment="VPN In IpSec" dst-address-list=	"Local subnet" ipsec-policy=in,ipsec src-address=192.168.0.0/16 	src-address-list="!Local subnet"
add action=accept chain=forward comment="VPN Out" dst-address=192.168.0.0/16 	dst-address-list="!Local subnet" src-address-list="Local subnet"

… и переместим их в позицию сразу над drop-правилом, запрещающим входящий трафик не из LAN. В моей дефолтной конфигурации оно было с комментарием «defconf: drop all not coming from LAN»

Идём в Firewall Mangle и создаём следующие правила:

/ip firewall mangle
# перехватываем входящий трафик из головного офиса,
# маркируем соединение...
add action=mark-connection chain=prerouting comment="VPN In" 	new-connection-mark=VPN_conn_in passthrough=no src-address=192.168.0.0/16 	src-address-list="!Local subnet"
# ...и обратный маршрут
add action=mark-routing chain=output comment="VPN In" connection-mark=	VPN_conn_in new-routing-mark=VPN_route_in passthrough=yes
# перехватываем исходящий трафик с MikroTik в головной офис и маркируем соединение.
# Этот маркер используется в NAT.
add action=mark-connection chain=postrouting comment="VPN Out" dst-address=	192.168.0.0/16 dst-address-list="!Local subnet" new-connection-mark=	VPN_conn_out passthrough=no

Далее отметимся в Firewall NAT:

/ip firewall nat
# мапим весь исходящий с MikroTik в головной офис трафик
# по маркеру соединения на адрес сети,
# определённый для используемого Kerio-сервера (напоминаю: для Kerio Control #1 - 192.168.22.0/24, для Kerio Control #2 - 192.168.33.0/24)
# Внимание!
# comment=KerioVpnNatOut используется в скриптах!
add action=netmap chain=srcnat comment=KerioVpnNatOut connection-mark=	VPN_conn_out to-addresses=192.168.22.0/24
# мапим весь входящий на MikroTik из головного офиса трафик
# по маркеру соединения на адрес основной локальной сети за MikroTik
add action=netmap chain=dstnat comment=KerioVpnNatIn connection-mark=	VPN_conn_in to-addresses=192.168.11.0/24
# разрешаем MikroTik пинговать Kerio-сервера
add action=accept chain=srcnat comment=KerioVpnNatPing out-interface-list=WAN protocol=icmp

Важно! Эти правила должны размещаться выше правила маскарадинга.

Теперь переходим в раздел IP ---> IPSec, где нам потребуется создать политику, peer, policy template groups (об их назначении я расскажу позже) и proposal для настройки шифра фазы 2.

ip ipsec proposal
/ip ipsec proposal
add enc-algorithms=3des name=KerioVPNProposal#01 pfs-group=modp2048


ip ipsec policy group
/ip ipsec policy group
add name=1
add name=2
add name=3
add name=4


ip ipsec peer
/ip ipsec peer
add address=11.11.11.111/32 comment=vs01-i01-01.domain.ru exchange-mode=	main-l2tp local-address=33.33.33.111 my-id=	fqdn:mikrotik.sn.mynetname.net policy-template-group=1 profile=	profile_4 secret=pass1111


ip ipsec policy
/ip ipsec policy
add comment=KerioVPNPolicy dst-address=192.168.77.0/24 proposal=KerioVPNProposal#01 	sa-dst-address=11.11.11.111 sa-src-address=33.33.33.111 src-address=	192.168.22.0/24 tunnel=yes


Комментарий:

1. /ip ipsec proposal — вводим те же параметры шифрования, которые были нами указаны при настройке Kerio Control, в поле «Шифр фазы 2 (ESP):».

Примечание:

Обратите внимание на параметр «name=KerioVPNProposal#01».

Использовать конкретно это имя не обязательно, но в случае, если вы решите использовать другое, то после его изменения вам необходимо проверить и, при необходимости, изменить настройку связанной IPSec-политики, а также изменить присваиваемое значение переменной DefKerioPropName в скрипте scriptCheckActiveVpnServer, о котором пойдёт речь далее.

(Собственно, большинство имен и комментариев объектов, в описываемой конфигурации, используются в скриптах, поэтому их переименование может доставить вам некоторые неудобства в связи с необходимостью внесения изменений в код скриптов. Я постараюсь далее по тексту делать соответствующие пометки для облегчения поиска таких объектов.)

2. /ip ipsec policy group

Создание групп обусловлено тем, что в дальнейшем мы будем обрабатывать их имена (1, 2, ...n) в скриптах и использовать для определения приоритетности IP-адресов серверов Kerio, к которым мы будем обращаться.

Я создаю сразу четыре группы т.к. у меня два ISP, с двумя внешними IP на каждом со стороны Kerio. На данном этапе мы пока используем только одну группу.

3. /ip ipsec peer

В peer указываем:

  • Address — IP-адрес Kerio, на который будет производиться обращение с MikroTik. Должен быть указан тот же адрес, который мы ввели в /ip ipsec policy sa-dst-address, только с маской "/32";
  • Local Address — IP-адрес MikroTik, с которого будет производиться обращение к Kerio. Должен быть указан тот же адрес, который мы ввели в /ip ipsec policy sa-src-address;
  • Auth. Method — выбираем из списка «pre shared key»;
  • Exchange Mode — выбираем из списка main-l2tp;
  • Если установлен, снимаем флажок «Passive»;
  • Secret — вводим парольную фразу, которую мы вводили, когда настраивали VPN-интерфейс на Kerio, на закладке «Аутентификация», в поле «Предопределённый ключ:»;
  • Policy Template Group — выбираем из списка созданную ранее нами группу с именем «1»;
  • Снимаем флажок NAT Traversal;
  • My ID Type — выбираем из списка значение «fqdn»;
  • My ID — вводим FQDN, присвоенное MikroTik в DDNS;
  • На закладке «Encryption» выбираем параметры шифрования, которые мы вводили, когда настраивали VPN-интерфейс на Kerio, на закладке «Аутентификация», в поле «Шифр фазы 1 (IKE):»;
  • Comment (Внимание! Используется в скриптах!).

В комментарии я указываю полный технический FQDN своих серверов, которые опубликованы на DNS-серверах, обслуживающих мою внешнюю зону.

Кроме применения в этой конфигурации, это позволяет мне поддерживать в актуальном состоянии информацию об используемых внешних IP-адресах Kerio-серверов (мне достаточно изменить IP-адрес на внешнем DNS-сервере и он будет автоматически изменён на MikroTik (статья здесь)).

Для тех, кому лень разбираться привожу рабочий код:

/system script
add dont-require-permissions=no name=scriptSetIPSecSADstAddrFromDNS owner=	admin policy=read,write

Листинг скрипта scriptSetIPSecSADstAddrFromDNS
:if ([:len [/system script job find script=SetIPSecSADstAddrFromDNS]]>1) do={
  :error
}

:local DnsNameFromComment
:local ResolvedIpFromComment
:local ResolvedIpWithMaskFromComment
:local IpPeerAddr

:foreach IpSecPeerCount in=[/ip ipsec peer find] do={
	:set DnsNameFromComment [/ip ipsec peer get $IpSecPeerCount comment]
	:if ($DnsNameFromComment!="") do={
		:do {
			:set ResolvedIpFromComment [:resolve $DnsNameFromComment]
			:set ResolvedIpWithMaskFromComment ($ResolvedIpFromComment . "/32")
			:set IpPeerAddr [/ip ipsec peer get $IpSecPeerCount address]
			:if ($ResolvedIpWithMaskFromComment!=$IpPeerAddr) do={
				:log warning ("[SetIPSecSADstAddrFromDNS] В пире на сервер " . DnsNameFromComment . " изменён IP-адрес с " . $IpPeerAddr . " на " . $ResolvedIpFromComment)
				#:log warning ("[SetIPSecSADstAddrFromDNS] In the peer to the server " . DnsNameFromComment . " changed IP address from " . $IpPeerAddr . " on " . $ResolvedIpFromComment)
				/ip ipsec peer set $IpSecPeerCount address=$ResolvedIpWithMaskFromComment
			}
		} on-error={
			:set ResolvedIpFromComment "unknown"
			:log error ("[SetIPSecSADstAddrFromDNS] Не удалось разрешить имя " . $DnsNameFromComment)
			#:log error ("[SetIPSecSADstAddrFromDNS] Cant resolve name " . $DnsNameFromComment)
		}
	}
}
:log warning ("[SetIPSecSADstAddrFromDNS] Проверка IP-адресов VPN-серверов произведена")
#:log warning ("[SetIPSecSADstAddrFromDNS] The IP-addresses of the VPN-servers are checked")


Основное правило, применяемое к комментарию в peers — имя должно начинаться с любых букв и/или цифр, без пробелов, за которыми ОБЯЗАТЕЛЬНО следует дефис ("-"), после которого может располагаться любое количество произвольных символов.

Я использую следующий формат:

vsNN-pNN-NN.domain.ru

Где:

vsNN — vpn-server #NN (Эта часть комментария обрабатывается в скриптах и используется в IP ---> Firewall ---> Address Lists (см. далее));
pNN — ISP #NN;
NN — порядковый номер внешнего IP-адреса в пуле адресов, выданных мне провайдером на Kerio;

4. /ip ipsec policy

В политике определяем:

  • Dst. Address — адрес сети за Kerio (dst-address);
  • Src. Address — адрес сети, в который MikroTik будет маскировать (см. далее настройку IP ---> Firewall ---> NAT) свою локальную сеть (src-address). Этот адрес сети будет виден со стороны Kerio (его мы указывали, когда настраивали VPN-интерфейс на Kerio, на закладке «Удалённые сети»);
  • Protocol — 255 (all);
  • Action — encrypt;
  • Level — require;
  • IPSec Protocols — esp;
  • устанавливаем параметр tunnel=yes;
  • SA Src. Address — Внешний IP-адрес MikroTik, с которого будет производиться обращение к Kerio (sa-src-address);
  • SA Dst. Address — IP-адрес Kerio, на который будет производиться обращение с MikroTik (sa-dst-address);
  • Proposal — Вводим значение, присвоенное параметру name в разделе /ip ipsec proposal (в данной конфигурации — KerioVPNProposal#01);
  • Comment (Внимание! Используется в скриптах!) — KerioVPNPolicy.

Если всё сделано правильно, то после сохранения политики, в поле «PH2 State» должно появиться значение «established», что говорит об установке VPN-канала между MikroTik и Kerio.
Убедиться в этом можно проверив состояние VPN-интерфейса в Kerio Control. Там в поле «Сведения», напротив того интерфейса, к которому произведено подключение, должна появиться надпись «Соединение с IP_адрес_вашего_MikroTik установлено».

Продолжаем…

Теперь создадим пиры на все оставшиеся у нас IP-адреса Kerio-серверов (в моей конфигурации необходимо создать ещё три пира).

Для этого нам необходимо повторить все действия указанные в п.3 (/ip ipsec peer), но обратить внимание на следующие изменения:

  • Address — изменяем IP-адрес Kerio;
  • Secret — вводим парольную фразу относящуюся к создаваемому соединению (pass2222, pass3333, ...passNNNN);
  • Policy Template Group — выбираем из списка следующую по порядку группу (2, 3, ...n).

Примечание:

Потом вы можете в любой момент изменить приоритетность своих серверов, изменив группу в настройках пира.

  • Comment — в моём случае изменяется на другой FQDN Kerio-сервера.

Все остальные параметры вводим такие же, как и в первом пире. Те, которые необходимо будет изменять, обрабатываются скриптами и вы можете пока их копировать без изменений.

Завершающий штрих конфигурирования перед подключением в работу скриптов — заставим MikroTik работать хранилищем констант, которые мы будем использовать в скриптах…
Добавим два списка в Firewall Address Lists (Внимание! Используется в скриптах!):

/ip firewall address-list
add address=192.168.22.0/24 list=vs01
add address=192.168.33.0/24 list=vs02

в них указываем адреса сетей с привязкой к префиксам имен Kerio-серверов, в которые мы будем мапить исходящий VPN-трафик.

Ну и для того, чтобы автоматизировать работу всего этого безобразия добавим два скрипта и три расписания (если решите переименовать скрипты, не забудьте внести соответствующие изменения в код).

/system script
add dont-require-permissions=no name=scriptFunctionsList owner=admin policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon

Листинг скрипта scriptFunctionsList
# Процедура синхронизации IP --> Cloud с внешними DNS-серверами
# входящий параметр:
#
# $start (true/false);
#
# возвращает "" или "updated"

:global subUpdateCloudDns do={
	put ($start);
	:if ($start=false) do={
		set $CloudDnsStatus;

		# Определяем переменную для счётчика
		set $m 1;
		log warning ("[subUpdateCloudDns] ---> DDNS статус ---> ЗАПУЩЕНА ПРОВЕРКА");
		#log warning ("[subUpdateCloudDns] ---> DDNS status ---> CHECK STARTED");
		do {
			log warning ("[subUpdateCloudDns] ---> DDNS проверка, попытка ---> " . $m);
			[/ip cloud force-update];
			delay 30000ms;
			set $CloudDnsStatus ([/ip cloud get status]);
			set $m ($m+1);
			:if ($CloudDnsStatus="updated") do={
				log warning ("[subUpdateCloudDns] ---> DDNS статус ---> " . $CloudDnsStatus);
				#log warning ("[subUpdateCloudDns] ---> DDNS status ---> " . $CloudDnsStatus);
			} else={
				log error ("[subUpdateCloudDns] ---> DDNS статус ---> " . $CloudDnsStatus);
				#log error ("[subUpdateCloudDns] ---> DDNS status ---> " . $CloudDnsStatus);
			}
		} while=(($CloudDnsStatus!="updated") and ($m<10));
		return ($CloudDnsStatus);
	}
}

# Процедура проверки состояния IP --> Cloud
# возвращает код состояния:
#
# 0 - деактивировано;
# 1 - активно, но не обновлено;
# 2 - активно и обновлено

:global subCheckCloudDDNS do={
	set $CloudDnsActive ([/ip cloud get ddns-enabled]);
	
	# Значение по-умолчанию ( $m=0 )
	set $m 0;
	:if ($CloudDnsActive=yes) do {
	
		# Если IP--->Cloud активировано ( $m=1 )
		set $m ($m+1);
		set $CloudDnsStatus ([/ip cloud get status]);
		
		# Проверяем синхронизацию внешних IP-адресов (сохранённого в IP--->Cloud и на внешних DNS)
		set $CloudDnsIP ([/ip cloud get public-address]);
		set $CheckIpAddr ([resolve [/ip cloud get dns-name]]);
		:if ($CloudDnsIP!=$CheckIpAddr) do={
		
			# Если IP разные (изменилось подключение MikroTik к провайдеру)...
			set $CloudDnsStatus "updating...";
		}
		:if ($CloudDnsStatus="updated") do {
		
			# Если IP--->Cloud активировано и обновлено ( $m=2 )
			set $m ($m+1);
		}
	}
	return ($m);
}

# Процедура повторного вызова скрипта на выполнение
# входящий параметр:
#
# $ScriptName (имя скрипта)

:global subRepeatScript do={
	put ($ScriptName);
	:if ($ScriptName!="") do {
		[/system script run $ScriptName];
	}
}

# Процедура формирования списка внешних VPN-серверов из пиров
#
# возвращает список IP-адресов VPN-серверов с привязкой к группам

:global subGetVpnServers do={

	# Получаем группы и IP-адреса VPN-серверов из пиров и считаем общее количество пиров
	:foreach IpSecPeerId in=[/ip ipsec peer find passive!=yes] do={
		set $CheckIpAddr [/ip ipsec peer get $IpSecPeerId address];
		:if ($CheckIpAddr!="") do={
			set $MaskPos [find $CheckIpAddr "/"];
			set $GroupFromPeer ([/ip ipsec peer get $IpSecPeerId policy-template-group]);
			set $IpFromPeer ([pick $CheckIpAddr 0 $MaskPos]);
			set $VpnServersList ($VpnServersList, {{$GroupFromPeer; $IpFromPeer}});
		}
	}
	return ($VpnServersList);
}

# Процедура выключения активных пиров и связанных политик
#
# процедура возвращает список ID всех политик

:global subDisableIpSecPeers do={

	# ...выключаем все включённые, активные (passive=false) пиры...
	:foreach IpSecPeerId in=[/ip ipsec peer find passive!=yes] do={
		log warning ("[IP IPSec Peer] ---> обрабатывается пир на ---> " . [/ip ipsec peer get $IpSecPeerId comment]);
		#log warning ("[IP IPSec Peer] ---> processed peer on ---> " . [/ip ipsec peer get $IpSecPeerId comment]);
		
		# Получаем address из текущего пира...
		set $CheckIpAddr [/ip ipsec peer get $IpSecPeerId address];
		:if ($CheckIpAddr!="") do={
		
			# Если address не пустой, отрезаем IP-адрес от маски...
			set $MaskPos ([find $CheckIpAddr "/"]);
			set $CheckIpAddr ([pick $CheckIpAddr 0 $MaskPos]);
			
			# ...и выключаем связанные IPSec-политики
			:foreach IpSecPolicyId in=[/ip ipsec policy find sa-dst-address=$CheckIpAddr] do={
				:if ($IpSecPolicyId!="") do={
					:if ([/ip ipsec policy get $IpSecPolicyId disabled]!=yes) do={
						[/ip ipsec policy disable $IpSecPolicyId];
						log warning ("[IP IPSec Policy] ---> политика " . [/ip ipsec policy get $IpSecPolicyId comment] . " деактивирована");
						#log warning ("[IP IPSec Policy] ---> policy " . [/ip ipsec policy get $IpSecPolicyId comment] . " deactivated");
					}

					# Заполняем массив списком ID политик для дальнейшего изменения
					set $IdList ($IdList, $IpSecPolicyId);
				}
			}
		}

		# Примечание: пиры выключаются после деактивации политик, чтобы не возникали системные ошибки
		:if ([/ip ipsec peer get $IpSecPeerId disabled]!=yes) do={
			[/ip ipsec peer disable $IpSecPeerId];
			log warning ("[IP IPSec Peer] ---> " . [/ip ipsec peer get $IpSecPeerId comment] . " ---> деактивирован");
			#log warning ("[IP IPSec Peer] ---> " . [/ip ipsec peer get $IpSecPeerId comment] . " ---> deactivated");
		}
	}
	return ($IdList);
}

# Процедура включения активных пиров и связанных политик
# входящие параметры:
#
# $PeerID (ID пира на VPN-сервер);
# $PolIdList (список ID из $subDisableIpSecPeers);
# $CloudIP (IP MikroTik из DDNS);
# $SrcIP (src-address для обращения к VPN-серверу)

:global subEnableIpSecPeers do={
	put ($PeerID);
	put ($PolIdList);
	put ($CloudIP);
	:if (($PeerID!="")&&($PeerID!=nil)&&($PolIdList!="")&&($PolIdList!=nil)&&($CloudIP!="")&&($CloudIP!=nil)) do={

		# Получаем адрес VPN-сервера
		set $ActiveVPN [/ip ipsec peer get $PeerID address];
		:if ($ActiveVPN!="") do={
		
			# Если найден, отрезаем IP-адрес от маски
			set $MaskPos [find $ActiveVPN "/"];
			set $ActiveVPN ([pick $ActiveVPN 0 $MaskPos]);
		}

		# Если пир выключен, делаем задержку исполнения и включаем
		:if ([/ip ipsec peer get $PeerID disabled]=yes) do={
			delay 5000ms;
			[/ip ipsec peer enable $PeerID];
			log warning ("[IP IPSec Peer] ---> активирован peer на ---> " . [/ip ipsec peer get $PeerID address]);
			#log warning ("[IP IPSec Peer] ---> activated peer on ---> " . [/ip ipsec peer get $PeerID address]);
		}

		# Ищем политики по ID из массива PolIdList, проверяем, при необходимости изменяем Src. Address, SA Src. Address и SA Dst. Address, и активируем
		:foreach IpSecPolicyId in=$PolIdList do={
			:if ($IpSecPolicyId!="") do={
				:if ([/ip ipsec policy get $IpSecPolicyId src-address]!=$SrcIP) do={
					[/ip ipsec policy set $IpSecPolicyId src-address=$SrcIP];
					log warning ("[IP IPSec Policy] ---> изменён src-address");
					#log warning ("[IP IPSec Policy] ---> src-address changed");
				}
				:if ([/ip ipsec policy get $IpSecPolicyId sa-src-address]!=$CloudIP) do={
					[/ip ipsec policy set $IpSecPolicyId sa-src-address=$CloudIP];
					log warning ("[IP IPSec Policy] ---> изменён sa-src-address");
					#log warning ("[IP IPSec Policy] ---> sa-src-address changed");
				}
				:if ([/ip ipsec policy get $IpSecPolicyId sa-dst-address]!=$ActiveVPN) do={
					[/ip ipsec policy set $IpSecPolicyId sa-dst-address=$ActiveVPN];
					log warning ("[IP IPSec Policy] ---> изменён sa-dst-address");
					#log warning ("[IP IPSec Policy] ---> sa-dst-address changed");
				}
				:if ([/ip ipsec policy get $IpSecPolicyId disabled]=yes) do={
					delay 3000ms;
					[/ip ipsec policy enable $IpSecPolicyId];
					log warning ("[IP IPSec Policy] ---> политика активирована");
					#log warning ("[IP IPSec Policy] ---> policy activated");
					
					# Очищаем DNS-кэш
					[/ip dns cache flush]
				}
			}
		}
	}
}

# Процедура формирования списка политик по ID пира
# входящие параметры:
#
# $PeerIP (IP из пира БЕЗ МАСКИ);
# $CloudIP (IP MikroTik из DDNS);
# $action (enable/disable/skip)
#
# процедура возвращает список ID, связанных с пиром, политик

:global subGetPoliciesByPeer do={
	put ($PeerIP);
	put ($CloudIP);
	put ($action);
	:if (($action="")||($action=nil)) do={
		set $action "skip";
	}

	:foreach IpSecPolicyId in=[/ip ipsec policy find sa-dst-address=$PeerIP] do={
		:if ($IpSecPolicyId!="") do={
		
			# Если политика включена И $action=disable, выключить политику
			:if (([/ip ipsec policy get $IpSecPolicyId disabled]!=yes)&&($action="disable")) do={
				[/ip ipsec policy disable $IpSecPolicyId];
				log warning ("[IP IPSec Policy] ---> политика деактивирована");
				#log warning ("[IP IPSec Policy] ---> policy deactivated");
			}
			
			# Если получен не пустой $CloudIP И sa-src-address!=$CloudIP (изменился ISP), выключить политику и изменить sa-src-address
			:if (($CloudIP!="")&&($CloudIP!=nil)&&([/ip ipsec policy get $IpSecPolicyId sa-src-address]!=$CloudIP)) do={
				[/ip ipsec policy disable $IpSecPolicyId];
				[/ip ipsec policy set $IpSecPolicyId sa-src-address=$CloudIP];
				log warning ("[IP IPSec Policy] ---> политика деактивирована ---> новый sa-src-address ---> " . $CloudIP);
				#log warning ("[IP IPSec Policy] ---> policy deactivated ---> new sa-src-address ---> " . $CloudIP);
				
				# Делаем задержку исполнения, чтобы Kerio разорвал канал
				delay 30000ms;
			}
			# Если политика выключена И $action=enable, включить политику
			:if (([/ip ipsec policy get $IpSecPolicyId disabled]=yes)&&($action="enable")) do={
				[/ip ipsec policy enable $IpSecPolicyId];
				log warning ("[IP IPSec Policy] ---> политика активирована");
				#log warning ("[IP IPSec Policy] ---> policy activated");
				
				# Очищаем DNS-кэш
				[/ip dns cache flush]
			}

			# Заполняем массив списком ID политик для дальнейшего изменения
			set $IdList ($IdList, $IpSecPolicyId);
		}
	}
	return ($IdList);
}

# Процедура проверки local-address пира
# входящие параметры:
#
# $PeerID (ID пира на VPN-сервер);
# $CloudIP (IP MikroTik из DDNS)

:global subCheckPeerLocalIp do={
	put ($PeerID);
	put ($CloudIP);
	
	:if (($PeerID!="")&&($PeerID!=nil)&&($CloudIP!="")&&($CloudIP!=nil)) do={
		# Проверяем изменение DDNS-IP
		:if ([/ip ipsec peer get $PeerID local-address]!=$CloudIP) do={
			[/ip ipsec peer set $PeerID local-address=$CloudIP];
			log warning ("[IP IPSec Peer] ---> изменён local-address");
			#log warning ("[IP IPSec Peer] ---> local-address changed");
		}
	}
}


/system script
add dont-require-permissions=no name=scriptCheckActiveVpnServer owner=admin policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon

Листинг скрипта scriptCheckActiveVpnServer
:if ([:len [/system script job find script=scriptCheckActiveVpnServer]]>1) do={
	:error
}

# Библиотеку процедур инициализируем в скрипте scheduleStartup
# и объявляем процедуры, которые будут использованы в этом скрипте
:global subCheckCloudDDNS
:global subCheckPeerLocalIp
:global subDisableIpSecPeers
:global subEnableIpSecPeers
:global subGetPoliciesByPeer
:global subGetVpnServers
:global subUpdateCloudDns

:local CheckIP
:local CheckPeer

# Заполняем переменную статусом активации DDNS (внешняя процедура)
:local CloudDnsStatus ([:put [$subCheckCloudDDNS]])

:local Exit false
:local DefMikroTikSrcNet 192.168.11.0/24
:local DefKerioDstNet 192.168.77.0/24
:local DefKerioPropName KerioVPNProposal#01
:local DefKerioPolName KerioVPNPolicy
:local IpSecPolicyId
:local KerioName
:local KerioVpnNatRuleName KerioVpnNatOut
:local m
:local n 1
:local PeerCount 0
:local PeerDisabled
:local PingCount 3
:local PingResult
:local PoliciesList
:local PublicIp
:local VpnServersList


# Проверяем активацию DDNS и, если активирован, считываем активный внешний IP-адрес MikroTik
:if ($CloudDnsStatus=0) do={

	# Если Cloud DDNS не активно, записываем сообщение в журнал и выходим из скрипта
	:log error ("[schedule CheckActiveVpnServer] ---> для подключения к VPN-серверу требуется активировать Cloud DDNS! (IP -> Cloud)")
#	:log error ("[schedule CheckActiveVpnServer] ---> to connect to the VPN server, you need to activate Cloud DDNS! (IP -> Cloud)")
	:error
}
:if ($CloudDnsStatus=1) do={

	# Если Cloud DDNS активно, но не обновлено, запускаем обновление (внешняя процедура)
	:set CloudDnsStatus [:put [$subUpdateCloudDns start=false]]
	:if ($CloudDnsStatus="updated") do={
		:set CloudDnsStatus 2
	}
}
:if ($CloudDnsStatus=2) do {
	
	# Если Cloud DDNS активно и обновлено ...
	# Получаем IP из DDNS
	:set PublicIp [/ip cloud get public-address]

	# Получаем список VPN-серверов с группами, определяющими приоритет подключения (внешняя процедура)
	:set VpnServersList ([:put [$subGetVpnServers]])

	# Считаем общее количество пиров
	:foreach VpnIpId in=$VpnServersList do={
		:set PeerCount ($PeerCount+1)
	} 

	# Ищем активный VPN-сервер пингом с внешнего DDNS-IP (для этого, на стороне VPN-сервера необходимо создать разрешающее правило)
	:while (($Exit!=true)&&$n<=$PeerCount) do={
		:foreach VpnIpId in=$VpnServersList do={

			# Перебираем IP-адреса VPN-серверов с учётом приоритета
			:if (($VpnIpId->0)=$n) do={
				:set CheckIP ($VpnIpId->1)
				:if ($CheckIP!="") do={
				
					# Проверяем доступность найденного IP
					:set PingResult ([:put [/ping address=$CheckIP count=$PingCount src-address=$PublicIp]])
					:if ($PingResult=$PingCount) do={
						:log warning ("[schedule CheckActiveVpnServer] ---> DDNS-IP ---> " . $PublicIp . " ---> VPN-IP ---> " . $CheckIP . " ---> Ping Result ---> " . $PingResult)
					
						# Если IP доступен, ищем пир на этот IP-адрес
						:set CheckPeer (:put [/ip ipsec peer find address=($CheckIP . "/32")])

						:if ($CheckPeer!="") do={
							
							# Если пир найден, проверяем наличие в конфигурации src-адреса (IP ---> Firewall ---> Address Lists), использующегося для соединения с Kerio
							# Получаем имя сервера Kerio из комментария пира
							:set KerioName [/ip ipsec peer get $CheckPeer comment]
							# Обрабатываем полученное имя (ИЗМЕНИТЬ АЛГОРИТМ обработки, если FQDN сервера Kerio отличается от KerioName-Parameter1-...-Parameter_n
							:if ($KerioName!="") do={
								# Находим первый дефис
								:set m ([find $KerioName "-"])
								# Отрезаем KerioName от всего остального
								:set KerioName ([pick $KerioName 0 $m])
								# Ищем адрес в Firewall -> Address List (соответственно там должны быть необходимые записи в формате:
								# Name ---> KerioName (eg srv1)
								# Address ---> DefMikroTikSrcNet (eg 192.168.99.0/24))
								:set m [/ip firewall address-list find list=$KerioName]
								:set DefMikroTikSrcNet ([/ip firewall address-list get $m address])
							}

							# ... проверяем наличие в конфигурации политик настроенных на соединение с Kerio
							:set IpSecPolicyId (:put [/ip ipsec policy find comment="$DefKerioPolName"])
							
							# Если ни одной политики не найдено, создаём политику с настройками по умолчанию
							# (задаются в разделе объявления переменных) и размещаем её в самом верху списка политик
							:if ($IpSecPolicyId="") do={
								[/ip ipsec policy add disabled=yes dst-address=$DefKerioDstNet proposal=$DefKerioPropName sa-dst-address=$CheckIP sa-src-address=$PublicIp src-address=$DefMikroTikSrcNet tunnel=yes comment=$DefKerioPolName place-before=0]

								:log warning ("[schedule CheckActiveVpnServer] ---> создана политика " . $DefKerioPolName . " ---> использованы параметры по умолчанию")
								#:log warning ("[schedule CheckActiveVpnServer] ---> created policy " . $DefKerioPolName . " ---> default parameters used")
							} else={
							
								# Если политика есть, проверяем указанный в ней src-address, при необходимости меняем.
								:if ($DefMikroTikSrcNet!=[/ip ipsec policy get $IpSecPolicyId src-address]) do={
									[/ip ipsec policy set $IpSecPolicyId src-address=$DefMikroTikSrcNet];
									:log warning ("[schedule CheckActiveVpnServer] ---> политика " . $DefKerioPolName . " изменена ---> src-address изменён на ---> " . $DefMikroTikSrcNet)
									#:log warning ("[schedule CheckActiveVpnServer] ---> policy " . $DefKerioPolName . " changed ---> src-address changed to ---> " . $DefMikroTikSrcNet)
								}
							}

							# очищаем переменную
							:set m
							# ищем NAT-правило для мапинга исходящего в сторону Kerio трафика
							:set m [/ip firewall nat find comment=$KerioVpnNatRuleName]
							:if ($m!="") do={
							
								# Если изменился src-address в политике ipsec, изменяем правило мапинга исходящего в сторону Kerio трафика
								:if ([/ip firewall nat get $m to-addresses]!=$DefMikroTikSrcNet) do={
									[/ip firewall nat set $m to-addresses $DefMikroTikSrcNet]
									:log warning ("[IP Firewall NAT] ---> изменено правило мапинга ---> " . $KerioVpnNatRuleName)
									#:log warning ("[IP Firewall NAT] ---> netmap rule changed ---> " . $KerioVpnNatRuleName)
								}
							}
							# ...сравниваем local-address найденного пира с текущим внешним IP MikroTik, при необходимости изменяем (внешняя процедура)
							:put [$subCheckPeerLocalIp PeerID=$CheckPeer CloudIP=$PublicIp]
							
							# ...проверяем состояние найденного пира
							:set PeerDisabled ([/ip ipsec peer get $CheckPeer disabled])

							:if ($PeerDisabled=true) do={

								# Если пир выключен...
								# Выключаем все другие пиры и политики (внешняя процедура)
								:set PoliciesList ([:put [$subDisableIpSecPeers]])

								# Находим пир на активный VPN-сервер и включаем (внешняя процедура)
								:put [$subEnableIpSecPeers PeerID=$CheckPeer PolIdList=$PoliciesList CloudIP=$PublicIp SrcIP=$DefMikroTikSrcNet]
							} else={
							
								# Если пир включен...
								# Находим все связанные с пиром политики, проверяем и включаем (внешняя процедура)
								:set PoliciesList ([:put [$subGetPoliciesByPeer PeerIP=$CheckIP CloudIP=$PublicIp SrcIP=$DefMikroTikSrcNet action="enable"]])
							}
							:set Exit true
						}
					} else={
						:log error ("[schedule CheckActiveVpnServer] ---> DDNS-IP ---> " . $PublicIp . " ---> VPN-IP ---> " . $CheckIP . " ---> Ping Result ---> " . $PingResult)
					}
				}
			}
		}
		:set n ($n+1)
	}
}


/system scheduler
add interval=1h name=scheduleCheckIPSecSADstAddrFromDNS on-event=	"/system script run scriptSetIPSecSADstAddrFromDNS" policy=read,write 	start-date=oct/30/2017 start-time=00:10:00
add name=scheduleStartup on-event=":global StartupScript true :global RepeatRun false /system script run scriptFunctionsList" policy=	ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon 	start-time=startup
add interval=5m name=scheduleCheckActiveVpnServer on-event=	"/system script run scriptCheckActiveVpnServer" policy=	ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon 	start-date=nov/29/2017 start-time=00:00:00

Листинг расписания scheduleStartup
:global StartupScript true
:global RepeatRun false
/system script run scriptFunctionsList


Листинг расписания scheduleCheckIPSecSADstAddrFromDNS
/system script run scriptSetIPSecSADstAddrFromDNS


Листинг расписания scheduleCheckActiveVpnServer
/system script run scriptCheckActiveVpnServer



Завершающий штрих:
для корректного обращения MikroTik к DNS-серверам предприятия, находящимся за Kerio Control, необходимо добавить статические записи с их адресами в MikroTik, в раздел IP ---> DNS ---> Static…

Ну вот где-то так!

Надеюсь, нигде не ошибся и ничего не забыл…

Спасибо за внимание!

p.s.
История правок и изменений:
  1. Добавлен раздел «Что получим на выходе?:»;
  2. Добавлен комментарий к адресу сети головного предприятия;
  3. Изменены IP-адреса сетей, из пулов ISP, использующиеся в описании конфигурации;
  4. Добавлен пункт «Завершающий штрих:»;

Комментарии (9)


  1. DickCancer
    10.02.2019 19:58

    Отличная статья! Низкий Вам поклон за столь обширный труд.


    1. domovoy-77 Автор
      10.02.2019 20:32

      Спасибо за добрые слова! Очень надеюсь, что она облегчит кому-нибудь жизнь.


  1. 0o0
    11.02.2019 16:26

    Я правильно понял, что в микротике до сих пор такой туннель не появляется в интерфейсах?
    Со всеми вытекающими…


    1. domovoy-77 Автор
      11.02.2019 16:56

      Если вы имеете ввиду, что в MikroTik, как в Kerio создаётся отдельный VPN-интерфейс, то нет


      1. 0o0
        11.02.2019 17:05

        Не обязатель, как в керио.
        Заходим в Интерфейсы, нажимаем плюсик и другие тоннели там есть. А с этим ipsec'ом подстава. Просто я тоже юзаю эту связку и жду очередного обновления микротика где они уже это пофиксят. Что бы дальше можно было создать правило «этот пакетик идет в этот интерфейс», а не «ползет на костылях».


        1. domovoy-77 Автор
          11.02.2019 17:44

          Меня IPSec вполне устраивает своей функциональностью, а ждать, согласитесь, лучше, когда что-то уже работает! Да и, собственно, эта конфигурация, наверняка может быть адаптирована и под будущие новые, потенциальные возможности MikroTik.


          1. 0o0
            11.02.2019 20:39

            Да у меня тоже работает. Просто подумал, вдруг уже пора переделать нормально. Ну раз нет, то окей


            1. domovoy-77 Автор
              11.02.2019 20:56

              Если быть совсем честным, я пока сильно не мониторил изменения в новых версиях RouterOS. Вполне вероятно, что могло что-нибудь и появиться, просто эта статья именно про IPSec.


    1. DaemonGloom
      12.02.2019 08:55
      +1

      Особенность в том, что и в линуксе ipsec не создаёт отдельный интерфейс. Отсюда, видимо, и идут проблемы.