Кто хоть раз писал политику фильрации firewall знает, что это дело не простое и сопряжено с кучей ошибок, когда колличество сетевых зон больше 2-х. В этой сутации вам поможет скрипт из этой статьи.
Под сетевой зоной я подразумеваю совокупность интерфейсов или IP адресов, для которых применяется правило фильтрации. В моем скрипте IP адреса зоны закреплены за интерфейсом. То есть, если мы ожидаем «доверенные» IP адреса из локальной сети, то будет странно, если соответствующие соединения прилетят к нам от провайдера.
Все вертится вокруг описания зон, где мы задаем как они между собой взаимодействуют.
В моей конфигурации, если не заданы другие правила, весь трафик будет сброшен с правилом DROP, поэтому мы будем задавать правила ACCEPT и REJECT.
Далее скрипт в цикле проходит по всем описаниям зон и создает:
IP зона назначения трафика от интерфейсной отличается тем, что её имени нет в разделе для интерфейсов. Таким образом скрипт и понимает, где у нас назначение ip, а где интерфейс.
Само описание разделено на два блока: интерфейсы и ip адреса. Для интерфейсов есть специальные переменные:
Так же есть две специальные зоны: rt, это сам роутер, и all, как не трудно догадаться «любое направление», all не может быть указан в качестве источника трафика.
Основное действие скрипта, это создание серии правил jump с фильтрацией по источнику и назначению трафика, которые терминируются правилом с ACCEPT или REJECT.
Описание зоны может быть редуцированно, как в примере для ISP и rt (должно быть всегда).
Если вы хотите терминировать цепочку своим правилом, то в описание зоны просто вместо accept или reject впишите custom (или что угодно), тогда цепочка jump-ов не будет завершена никаким правилом, а вы сможете создать его самостоятельно.
Задано 4-е зоны для интерфейсов, одна из которых, обязательная rt:
B 3-и ip зоны:
Данный скрипт мне помог сильно облегчить настройку сложных политик, однако, ошибки в нем все-же могут быть. Перед применением проконсультируйтесь со специалистом. В случае обнаружения побочных явлений, пишите, будем исправлять.
Введение
Под сетевой зоной я подразумеваю совокупность интерфейсов или IP адресов, для которых применяется правило фильтрации. В моем скрипте IP адреса зоны закреплены за интерфейсом. То есть, если мы ожидаем «доверенные» IP адреса из локальной сети, то будет странно, если соответствующие соединения прилетят к нам от провайдера.
Как оно работает
Все вертится вокруг описания зон, где мы задаем как они между собой взаимодействуют.
В моей конфигурации, если не заданы другие правила, весь трафик будет сброшен с правилом DROP, поэтому мы будем задавать правила ACCEPT и REJECT.
Далее скрипт в цикле проходит по всем описаниям зон и создает:
- Списки интерфейсов IF-<имя зоны>
- Списки адресов IP-<имя зоны>
- Задает дефолтовую и немного мной дополненую конфигурацию filter, mangle и raw
IP зона назначения трафика от интерфейсной отличается тем, что её имени нет в разделе для интерфейсов. Таким образом скрипт и понимает, где у нас назначение ip, а где интерфейс.
Переменные
- gping — Разрешить ping во всех направлениях и на пересылке. Иными словами, пинговать можно будет даже то, что было закрыто другими правилами в filter. Включать по желанию ;)
- debug — Ставит в самое начало корневых цепочек ACCEPT правило, что делает все остальные правила бесполезными. Очень полезно на этапе отладки правил. Делаете политику как вы хотите, включаете Safe Mode, а потом эти правила отключаете. Если роутер стал недоступен, то уже через 10 сек правила будут включены опять и вы сможете подумать, а в чем вы ошиблись?
- allout — Разрешает исходящий трафик роутера, обычно роутер у нас не генерит плохого трафика, так что в ряде конфигураций такое приемлимо.
Описание зон
Само описание разделено на два блока: интерфейсы и ip адреса. Для интерфейсов есть специальные переменные:
- is_wan: добавляет правила для обработки тунелей и DNS
- do_masq: добавляет правила для маскардинга трафика в эту зону
- is_lan: добавляет правила для ответа на DHCP и DNS
- z2s: добавляет правила разрешающие форвард с интерфейса зоны на другой её интерфейс
- mss: добавляет правила корректировки mss для туннельных интерфейсов, если встроенные средства вас не устраивают
- ADDoS: Добавляет правила защиты от DDoS или подбора паролей. Работет примитивно — обнаруживает факты слишком частых (по нашему мнению) соединений, и такой IP помещает в список для дальнейшего бана. имеются предопределенные политики:
- gen — generic, не зависит от протокола, лочит все что похоже на плохое.
- mngr — managment, работает на протоколы управления mikrotik (winbox, ssh)
- Любое другое имя — создаст отключенную цепочку, для вашего варианта протокола.
Так же есть две специальные зоны: rt, это сам роутер, и all, как не трудно догадаться «любое направление», all не может быть указан в качестве источника трафика.
Основное действие скрипта, это создание серии правил jump с фильтрацией по источнику и назначению трафика, которые терминируются правилом с ACCEPT или REJECT.
Описание зоны может быть редуцированно, как в примере для ISP и rt (должно быть всегда).
Если вы хотите терминировать цепочку своим правилом, то в описание зоны просто вместо accept или reject впишите custom (или что угодно), тогда цепочка jump-ов не будет завершена никаким правилом, а вы сможете создать его самостоятельно.
Пояснение правил из скрипта
Зоны
:local gping 1 :local debug 0 :local zones { "if"={ "ISP"={ "is_wan"=1; "do_masq"=1; "ADDoS"={ mngr={ dst-limit="4/1m,1,src-address/1m40s"; timeout="1d"; comment="Blocking bruteforcing winbox & ssh port"; excl=[:toarray ("Trusted")]; }; gen={ dst-limit="50,50,src-address/10s"; timeout="1d"; comment="Blocking potential DDoS"; excl=[:toarray ("Trusted")]; }; }; }; "LAN"={ "is_lan"=1; "z2s"=1; policy={ "all"="accept"; }; }; "TUN"={ mss=1400; }; "rt"={}; }; "ip"={ "rt"={ policy={ "all"="accept" }; }; "TUN"={ "Staff"={ "Server"="accept"; }; "Manager"={ "all"="accept"; }; }; "ISP"={ "Trusted"={ "rt"="accept"; }; }; }; }
Задано 4-е зоны для интерфейсов, одна из которых, обязательная rt:
- ISP — для провайдера, поэтому указано создавать дополнительные правила для некоторых протоколов.
- LAN — для локальной сети, ей заданы правила для lan и есть общее разрешающие действие на любые направления.
- TUN — для собственно тунелей, указана корректировка MSS.
B 3-и ip зоны:
- Staff и Manager на интерфейсах TUN:
- Staff — имеет доступ только к ip зоне Server
- Manager — могут куда угодно
- Trusted на интерфейсе ISP имеет доступ к роутеру
gen-filter
# jun/29/2020 10:00:00 by RouterOS 6.46.6 # RoS filter generator v 0.9.7 :local gping 0 :local debug 1 :local allout 1 :local zones { "if"={ "ISP"={ "is_wan"=1; "do_masq"=1; "ADDoS"={ mngr={ dst-limit="4/1m,1,src-address/1m40s"; timeout="1d"; comment="Blocking bruteforcing winbox & ssh port"; excl=[:toarray ("Trusted")]; }; gen={ dst-limit="50,50,src-address/10s"; timeout="1d"; comment="Blocking potential DDoS"; excl=[:toarray ("Trusted")]; }; }; }; "LAN"={ "is_lan"=1; "z2s"=1; policy={ "all"="accept"; }; }; "TUN"={ mss=1400; }; "rt"={}; }; "ip"={ "rt"={ policy={ "all"="accept" }; }; "TUN"={ "Staff"={ "Server"="accept"; }; "Manager"={ "all"="accept"; }; }; "ISP"={ "Trusted"={ "rt"="accept"; }; }; }; } /ip firewall raw add action=notrack chain=prerouting ipsec-policy=in,ipsec comment="Notrack ipsec" add action=notrack chain=prerouting dst-address-type=multicast comment="Notrack multicast" /ip firewall filter :if ( ($debug)=0 ) do={ add action=accept chain=forward comment=DEBUG!!! disabled=yes add action=accept chain=input comment=DEBUG!!! disabled=yes add action=accept chain=output comment=DEBUG!!! disabled=yes } else={ add action=accept chain=forward comment=DEBUG!!! add action=accept chain=input comment=DEBUG!!! add action=accept chain=output comment=DEBUG!!! } add action=accept chain=input comment="defconf: accept to local loopback (for CAPsMAN)" dst-address=127.0.0.1 dst-port=5246,5247 protocol=udp src-address-type=local add action=accept chain=input comment="defconf: accept established,related,untracked" connection-state=established,related,untracked add action=drop chain=input comment="defconf: drop invalid" connection-state=invalid add action=jump chain=input comment="defconf: new input" connection-state=new jump-target=in-new add action=jump chain=input comment="defconf: notrack input" jump-target=in-notrack add action=drop chain=input comment="defconf: drop all not allowed" add action=accept chain=output comment="defconf: accept established,related,untracked" connection-state=established,related,untracked add action=jump chain=output comment="defconf: new output" connection-state=new jump-target=out-new add action=jump chain=output comment="defconf: notrack output" jump-target=out-notrack :if ( ($allout)=1 ) do={ add action=accept chain=out-notrack comment="defconf: Allow any trafic from rt" } else={ add action=accept chain=out-notrack comment="defconf: Allow any trafic from rt" disabled=yes } add action=drop chain=output comment="defconf: drop all not allowed" add action=accept chain=forward comment="defconf: accept in ipsec policy" ipsec-policy=in,ipsec add action=accept chain=forward comment="defconf: accept out ipsec policy" ipsec-policy=out,ipsec add action=fasttrack-connection chain=forward comment="defconf: fasttrack" connection-mark=no-mark connection-state=established,related add action=accept chain=forward comment="defconf: accept established,related, untracked" connection-state=established,related,untracked add action=drop chain=forward comment="defconf: drop invalid" connection-state=invalid add action=jump chain=forward comment="defconf: new forward" connection-state=new jump-target=fw-new add action=jump chain=forward comment="defconf: notrack forward" jump-target=fw-notrack add action=accept chain=forward comment="defconf: Accept all forward DSTNATed" connection-nat-state=dstnat add action=drop chain=forward comment="defconf: drop all not allowed for forward" :if ( ($gping)=1 ) do={ add action=accept chain=WAN2RT-STD-PROTO comment=ICMP protocol=icmp disabled=yes } else={ add action=accept chain=WAN2RT-STD-PROTO comment=ICMP protocol=icmp } add action=accept chain=WAN2RT-STD-PROTO comment=GRE ipsec-policy=in,ipsec protocol=gre add action=accept chain=WAN2RT-STD-PROTO comment=IPSec protocol=ipsec-esp add action=accept chain=WAN2RT-STD-PROTO comment=IPSec protocol=ipsec-ah add action=accept chain=WAN2RT-STD-PROTO comment="IPSec encapsulated" dst-port=500,4500 protocol=udp add action=accept chain=WAN2RT-STD-PROTO comment=L2TP dst-port=1701 ipsec-policy=in,ipsec protocol=udp add action=accept chain=WAN2RT-STD-PROTO comment=PPtP dst-port=1723 protocol=tcp add action=accept chain=LAN2RT-STD-PROTO comment=DNS dst-port=53 protocol=tcp add action=accept chain=LAN2RT-STD-PROTO comment=NTP,DNS,DHCP dst-port=53,123,67-68 protocol=udp add action=accept chain=LAN2RT-STD-PROTO comment=DHCP dst-port=67-68 protocol=udp add action=accept chain=RT2WAN-STD-PROTO comment=DNS dst-port=53 protocol=tcp add action=accept chain=RT2WAN-STD-PROTO comment=NTP,DNS dst-port=53,123 protocol=udp add action=accept chain=WAN2RT-STD-PROTO comment=IPSec protocol=ipsec-esp add action=accept chain=RT2WAN-STD-PROTO comment=IPSec protocol=ipsec-ah add action=accept chain=RT2WAN-STD-PROTO comment="IPSec encapsulated" dst-port=500,4500 protocol=udp add action=reject chain=RT2WAN-STD-PROTO comment=GRE ipsec-policy=out,none protocol=gre reject-with=icmp-admin-prohibited add action=reject chain=RT2WAN-STD-PROTO comment=L2TP dst-port=1701 ipsec-policy=out,none protocol=udp reject-with=icmp-admin-prohibited :if ( ($gping)=1 ) do={ add action=accept chain=fw-new comment=ICMP protocol=icmp add action=accept chain=in-new comment=ICMP protocol=icmp add action=accept chain=out-new comment=ICMP protocol=icmp } else={ add action=accept chain=fw-new comment=ICMP protocol=icmp disabled=yes add action=accept chain=in-new comment=ICMP protocol=icmp disabled=yes add action=accept chain=out-new comment=ICMP protocol=icmp disabled=yes } /ip firewall filter :foreach zone,conf in=($zones->"if") do={ :if ( ($zone)!="rt" ) do={ :if ( [/interface list print count-only where name=("IF-".$zone)] = 0) do={ /interface list add name=("IF-".$zone) } add action=jump chain=fw-new in-interface-list=("IF-".$zone) comment=("Fwd plc from if ".$zone) jump-target=("fw-plc-s:".$zone) add action=jump chain=in-new in-interface-list=("IF-".$zone) comment=("In plc for if ".$zone) jump-target=("in-plc-s:".$zone) :foreach ddos,val in=($conf->"ADDoS") do={ :if ( $ddos="mngr" ) do={ add action=jump chain=("in-plc-s:".$zone) dst-port=22,8291 jump-target=("ADDoS-s:".$zone."-".$ddos) comment=("AntiDDoS for ".$ddos." from ".$zone) protocol=tcp } :if ( $ddos="gen" ) do={ add action=jump chain=("in-plc-s:".$zone) jump-target=("ADDoS-s:".$zone."-".$ddos) comment=("AntiDDoS for ".$ddos." from ".$zone) } :if ( $ddos="mngr" || $ddos="gen" ) do={ :foreach excl in=(($val)->"excl") do={ add action=return chain=("ADDoS-s:".$zone."-".$ddos) comment=("Execption for ".$excl." from ".$zone) src-address-list=("IP-".$excl) } add action=return chain=("ADDoS-s:".$zone."-".$ddos) comment=("Limit number incoming connections for ".$ddos." from ".$zone) dst-limit=(($val)->"dst-limit") add action=add-src-to-address-list address-list=("IP-DDoS-s:".$zone."-".$ddos) address-list-timeout=(($val)->"timeout") chain=("ADDoS-s:".$zone."-".$ddos) comment=("Add to IP-DDoS-".$ddos." list from ".$zone) } :if ( $ddos!="mngr" && $ddos!="gen" ) do={ add action=jump chain=("in-plc-s:".$zone) jump-target=("ADDoS-".$ddos) comment=("AntiDDoS for ".$ddos." from ".$zone) disabled=yes :foreach excl in=(($val)->"excl") do={ add action=return chain=("ADDoS-s:".$zone."-".$ddos) comment=("Execption for ".$excl." from ".$zone) src-address-list=("IP-".$excl) } add action=return chain=("ADDoS-s:".$zone."-".$ddos) comment=("Limit number incoming connections for ".$ddos." from ".$zone) } /ip firewall raw add action=drop chain=prerouting comment=("DROP Banned by ".$ddos." from ".$zone) in-interface-list=("IF-".$zone) src-address-list=("IP-DDoS-s:".$zone."-".$ddos) } } else={ add action=jump chain=out-new comment=("Out plc for rt") jump-target="out-plc-s:rt" } } :foreach zone,conf in=($zones->"if") do={ :if ( ($zone)!="rt" && ($zone)!="all") do={ :if ( ($conf->"is_lan")=1 ) do={ add action=jump chain=in-notrack comment=("In Allow plc for STD LAN PROTO from ".$zone) in-interface-list=("IF-".$zone) jump-target=LAN2RT-STD-PROTO } :if ( ($conf->"is_wan")=1) do={ add action=jump chain=in-notrack comment=("In Allow plc for STD WAN PROTO from ".$zone) in-interface-list=("IF-".$zone) jump-target=WAN2RT-STD-PROTO add action=jump chain=out-notrack out-interface-list=("IF-".$zone) comment=("Out Allow plc for STD WAN PROTO to ".$zone) jump-target=RT2WAN-STD-PROTO } :if ( ($conf->"do_masq")=1) do={ /ip firewall nat add action=masquerade chain=srcnat out-interface-list=("IF-".$zone) comment=("Masquerade traffic going to ".$zone) } :if ( [:len ($conf->"mss")]!=0 ) do={ /ip firewall mangle add action=change-mss chain=forward in-interface-list=("IF-".$zone) new-mss=($conf->"mss") passthrough=yes protocol=tcp tcp-flags=syn tcp-mss=(($conf->"mss"+1)."-65535") comment=("Fix mss on tunel ".$zone) /ip firewall mangle add action=change-mss chain=forward out-interface-list=("IF-".$zone) new-mss=($conf->"mss") passthrough=yes protocol=tcp tcp-flags=syn tcp-mss=(($conf->"mss"+1)."-65535") comment=("Fix mss on tunel ".$zone) } :if ( ($conf->"z2s")=1 ) do={ add action=accept chain=("fw-plc-s:".$zone) out-interface-list=("IF-".$zone) comment=("Fwd plc from if ".$zone." to self") } :foreach src,val in=(($zones->"ip")->$"zone") do={ :foreach tgt,policy in=$val do={ :if ( ($tgt)!="rt" && ($tgt)!="all") do={ :if ( [:len (($zones->"if")->$"tgt")]=0 ) do={ add action=jump chain=("fw-plc-s:".$zone) src-address-list=("IP-".$src) dst-address-list=("IP-".$tgt) comment=("Fwd plc from if ".$zone." & ip ".$src." to ".$tgt) jump-target=("fw-plc-s:".$zone."&".$src.">".$tgt) } else={ add action=jump chain=("fw-plc-s:".$zone) src-address-list=("IP-".$src) out-interface-list=("IF-".$tgt) comment=("Fwd plc from if ".$zone." & ip ".$src." to ".$tgt) jump-target=("fw-plc-s:".$zone."&".$src.">".$tgt) } :if ( ($policy)="accept") do={ add action=accept chain=("fw-plc-s:".$zone."&".$src.">".$tgt) comment=("Fwd plc from if ".$zone." & ip ".$src." Accept to ".$tgt) } :if ( ($policy)="reject") do={ add action=reject chain=("fw-plc-s:".$zone."&".$src.">".$tgt) comment=("Fwd plc from if ".$zone." & ip ".$src." Reject to ".$tgt) } } :if ( ($tgt)="rt" ) do={ add action=jump chain=("in-plc-s:".$zone) src-address-list=("IP-".$src) comment=("In plc for if ".$zone." & ip ".$src." to rt") jump-target=("in-plc-s:".$zone."&".$src.">rt") :if ( ($policy)="accept") do={ add action=accept chain=("in-plc-s:".$zone."&".$src.">rt") comment=("In plc for if ".$zone." & ip ".$src." Accept to rt") } :if ( ($policy)="reject") do={ add action=reject chain=("in-plc-s:".$zone."&".$src.">rt") comment=("In plc for if ".$zone." & ip ".$src." Accept to rt") } } :if ( ($tgt)="all" ) do={ add action=jump chain=("fw-plc-s:".$zone) src-address-list=("IP-".$src) comment=("Fwd plc from if ".$zone." & ip ".$src." to All") jump-target=("fw-plc-s:".$zone."&".$src.">all") add action=jump chain=("in-plc-s:".$zone) src-address-list=("IP-".$src) comment=("In plc for if ".$zone." & ip ".$src." to All") jump-target=("in-plc-s:".$zone."&".$src.">all") :if ( ($policy)="accept") do={ add action=accept chain=("fw-plc-s:".$zone."&".$src.">all") comment=("Fwd plc from if ".$zone." & ip ".$src." Accept to All") add action=accept chain=("in-plc-s:".$zone."&".$src.">all") comment=("In plc from if ".$zone." & ip ".$src." Accept to All") } :if ( ($policy)="reject") do={ add action=reject chain=("fw-plc-s:".$zone."&".$src.">all") comment=("Fwd plc from if ".$zone." & ip ".$src." Reject to All") add action=reject chain=("in-plc-s:".$zone."&".$src.">all") comment=("In plc from if ".$zone." & ip ".$src." Reject to All") } } } } :foreach tgt,policy in=($conf->"policy") do={ :if ( ($tgt)!="rt" && ($tgt)!="all") do={ :if ( [:len (($zones->"if")->$"tgt")]=0 ) do={ add action=jump chain=("fw-plc-s:".$zone) dst-address-list=("IP-".$tgt) comment=("Fwd plc from if ".$zone." to ".$tgt) jump-target=("fw-plc-s:".$zone.">".$tgt) } else={ add action=jump chain=("fw-plc-s:".$zone) out-interface-list=("IF-".$tgt) comment=("Fwd plc from if ".$zone." to ".$tgt) jump-target=("fw-plc-s:".$zone.">".$tgt) } :if ( ($policy)="accept") do={ add action=accept chain=("fw-plc-s:".$zone.">".$tgt) comment=("Fwd plc from if ".$zone." Accept to ".$tgt) } :if ( ($policy)="reject") do={ add action=reject chain=("fw-plc-s:".$zone.">".$tgt) comment=("Fwd plc from if ".$zone." Reject to ".$tgt) } } :if ( ($tgt)="rt" ) do={ :if ( ($conf->"is_lan")!=1 && ($conf->"is_wan")!=1 ) do={ add action=jump chain=("in-plc-s:".$zone) comment=("In plc for if ".$zone." to rt") jump-target=("in-plc-s:".$zone.">rt") } :if ( ($policy)="accept") do={ add action=accept chain=("in-plc-s:".$zone.">rt") comment=("In plc for if ".$zone." Accept to rt") } :if ( ($policy)="reject") do={ add action=reject chain=("in-plc-s:".$zone.">rt") comment=("In plc for if ".$zone." Reject to rt") } } :if ( ($tgt)="all" ) do={ add action=jump chain=("fw-plc-s:".$zone) comment=("Fwd plc from if ".$zone." to All") jump-target=("fw-plc-s:".$zone.">all") add action=jump chain=("in-plc-s:".$zone) comment=("In plc for if ".$zone." to All") jump-target=("in-plc-s:".$zone.">all") :if ( ($policy)="accept") do={ add action=accept chain=("fw-plc-s:".$zone.">all") comment=("Fwd plc from if ".$zone." Accept to All") add action=accept chain=("in-plc-s:".$zone.">all") comment=("In plc from if ".$zone." Accept to All") } :if ( ($policy)="reject") do={ add action=reject chain=("fw-plc-s:".$zone.">all") comment=("Fwd plc from if ".$zone." Reject to All") add action=reject chain=("in-plc-s:".$zone.">all") comment=("In plc from if ".$zone." Reject to All") } } } } }
Заключение
Данный скрипт мне помог сильно облегчить настройку сложных политик, однако, ошибки в нем все-же могут быть. Перед применением проконсультируйтесь со специалистом. В случае обнаружения побочных явлений, пишите, будем исправлять.
