Feature request: "Service Group"

Hey guys,

I have seen a few people asking for TCP & UDP being allowed in a single rule in firewall/nat/mangle, but why not take it a bit further?

I would really like an option like Address Lists, but for different protocols/ports.

For example:

I create a “Service Group” called “IPSec_VPN”. This service group would contain:
protocol UPD, dsp port 500
protocol IPSec-ESP
protocol IP-Encap

Then I could create a firewall/NAT/Mangle rule which would use the “Service group” with the same functionality as we can use Address Lists for addresses.

A single rule that would match for multiple conditions as defined in the “Service Group”

A sample firewall config would be like this:

/ip firewall filter
# input chain
add action=accept chain=input connection-state=established,related
add action=drop chain=input connection-state=invalid
add action=accept chain=input limit=5,5 service-group="ICMP WAN"
add action=accept chain=input service-group="ROS Management WAN" in-interface=ether1-WAN
add action=accept chain=input service-group="ROS Management LAN" in-interface=ether2-LAN
add action=accept chain=input service-group="ROS VPN" src-address-list="VPN Partners"
add action=drop chain=input
# forward chain
add action=accept chain=forward connection-state=established,related
add action=drop chain=forward connection-state=invalid
add action=accept chain=forward in-interface=ether2-LAN out-interface=ether1-WAN comment="Allow LAN -> WAN"
add action=accept chain=forward dst-address=XXX.XXX.XXX.XXX service-group="HTTP"
add action=accept chain=forward dst-address=XXX.XXX.XXX.XXY service-group="DNS"
add action=accept chain=forward dst-address-list=Servers limit=2,2 service-group="ICMP Servers"
add action=drop chain=forward

Address Lists would be defined:

The Address List "Servers" would contain IPs 
XXX.XXX.XXX.YYY
XXX.XXX.XXX.ZZZ

The Address List "VPN Partners" would contain IPs
XXX.XX.X.YZ
XXX.XX.XYZ.YZ
XX.XYZ.XY.XY

And Service Groups would be defined:

The Service Group "ROS Management LAN" would contain
dst-port=5678,20561 protocol=udp
dst-port=22,8291 protocol=tcp

The Service Group "HTTP" would contain
dst-port=80 protocol=tcp
dst-port=443 protocol=tcp

The Service Group "DNS" would contain
dst-port=53 protocol=tcp
dst-port=53 protocol=udp

The Service Group "ICMP Servers" would contain
icmp-options=0:0-255 protocol=icmp
icmp-options=3:3 protocol=icmp
icmp-options=3:4 protocol=icmp
icmp-options=8:0-255 protocol=icmp
icmp-options=11:0-255 protocol=icmp

The Service Group "ROS Management WAN" would contain
dst-port=8291 protocol=tcp

The Service Group "ICMP WAN" would contain
icmp-options=0:0-255 protocol=icmp
icmp-options=3:3 protocol=icmp
icmp-options=3:4 protocol=icmp
icmp-options=8:0-255 protocol=icmp
icmp-options=11:0-255 protocol=icmp

The Service Group "ROS VPN" would contain
protocol=UDP dst-port=500
protocol=ipsec-esp
protocol=encap
protocol=ipip

These are just examples. Personally, this would really clean up my firewall chains and NAT table.
Any discussion on the topic is welcomed!

Thanks,

tom

tomaskir
Very nice idea !!!
like Cisco ASA:

(config)# object-group service web
(config-service)# service-object tcp http
(config-service)# service-object tcp https
#
#
(config)# object-group service VNC tcp
(config-service)# port-object range 5800 5806
#
(config)# object-group service RDP tcp
(config-service)# port-object eq 3389
#
(config)# object-group service remote tcp
(config-service)# group-object VNC
(config-service)# group-object RDP
(config-service)# group-object Dude

like Juniper NetScreen:

# Define Service
set service RDP protocol tcp src-port 0-65535 dst-port 3389-3389
set service MSSQL protocol tcp src-port 0-65535 dst-port 1433-1433
# Define Group Service
set group service DMZ_VPN comment "XO-DMZ-VPN Services"
set group service DMZ_VPN add RDP
set group service DMZ_VPN add MSSQL
set group service DMZ_VPN add ICMP-ANY

As I remember ASA limit Service Group by protocol - as example tcp or udp Service Group.

Couldn’t this be done via packet/connection marking in the mangle rules?

It could, but if you have any kind of load-balancing or fail-over, it would make mangle really a bad place to handle this.

Also, this is meant to cut down on the ammount of firewall/NAT/magle rules you have, not cut down on something while increasing complexity somewhere else.

SonicWall gear also does this. Groups make for more simplified management (something everyone should be for). In the mean time, I have found a … well.. a work-around, if you will:
Use chains as “service groups”. I’ll explain, using your examples as a guide:

Create a chain called “IPSec_VPN” with your rules:

/ip firewall filter add chain="IPSec_VPN" protocol="UDP" dst-port="500" action="accept"
add chain="IPSec_VPN" protocol="ipsec-esp" action="accept"
add chain="IPSec_VPN" protocol="encap" action="accept"
add chain="IPSec_VPN" action="jump" jump-target=SOME_OTHER_LIST_THAT_DENIES_ACCESS_TO_UNINITIATED_INCOMING_TRAFFIC

Assuming you are looking explicitly allow VPN access to your router, you need to add a jump to this newly created chain in your input chain. Packets that match this rule will jump to your new chain. Essentially, you’re providing context for using the IPSec_VPN chain by specifying what packets jump to it. So, here’s how it looks:

/ip firewall filter add chain="input" dst-address=WAN_IP action="jump" jump-target="IPSec_VPN"

If you want to have, say, a group of machines that you want to provide access to, use the address-list. If you’re still confused, here’s a snippet from my production setup:

/ip firewall filter
add action=jump chain=forward disabled=no dst-address-list=LAN jump-target=conn.in.est

# ---
# Use address list to provide context for jump to DMZ chain
add action=jump chain=forward comment=DMZ disabled=no dst-address-list=DMZ jump-target=DMZ src-address-list=!LAN
# --- ---
# Inside this context, use the dst-address for more specificity to jump to DMZ.WWW chain
add action=jump chain=DMZ comment=DMZ.WWW disabled=no dst-address=XXX.XXX.XXX.XXX jump-target=DMZ.WWW
# Inside DMZ.WWW chain, allow for stuff
# Traffic that doesn't match this chain stays in the parent context, DMZ
add action=accept chain=DMZ.WWW disabled=no dst-port=80 protocol=tcp
add action=accept chain=DMZ.WWW disabled=no dst-port=443 protocol=tcp
# This line is for my SSH rate-limiter
add action=jump chain=DMZ.WWW disabled=no dst-port=22 jump-target=limit_block protocol=tcp
# --- ---
# Inside this context, use the dst-address for more specificity to jump to DMZ.DNS chain
add action=jump chain=DMZ comment=DMZ.DNS disabled=no dst-address=XXX.XXX.XXX.XXX jump-target=DMZ.DNS
# Inside DMZ.DNS chain, allow for stuff
# Traffic that doesn't match this chain stays in the parent context, DMZ
add action=accept chain=DMZ.DNS disabled=no dst-port=53 protocol=udp
add action=accept chain=DMZ.DNS disabled=no dst-port=53 protocol=tcp
# This line is for my SSH rate-limiter. I probably could of abstracted this a bit more in the DMZ chain, but not all hosts use SSH. Why provide access where it's not needed?
add action=jump chain=DMZ.DNS disabled=no dst-port=22 jump-target=limit_block protocol=tcp
# ---
# This part applies to anything that matched the DMZ context, including WWW and DNS, since the chain continues downward (remember that it goes top-to-bottom!)
# Allow pingage by jumping to ICMP chain
add action=jump chain=DMZ disabled=no jump-target=ICMP protocol=icmp
# Provide context for any other traffic to flow to the conn.in.est (incoming established connection) chain
add action=jump chain=DMZ disabled=no jump-target=conn.in.est

# Incoming established connections
add action=accept chain=conn.in.est comment=conn.in.est connection-state=established disabled=no
add action=accept chain=conn.in.est connection-state=related disabled=no
add action=drop chain=conn.in.est connection-state=invalid disabled=no
# Jumps to drop chain for anything else
add action=jump chain=conn.in.est disabled=no jump-target=drop

add action=drop chain=drop comment="Drop all" disabled=no

# ICMP limit, but accept
add action=accept chain=ICMP comment="0:0 and limit for 5pac/s" disabled=no icmp-options=0:0-255 limit=5,5 protocol=icmp
add action=accept chain=ICMP comment="3:3 and limit for 5pac/s" disabled=no icmp-options=3:3 limit=5,5 protocol=icmp
add action=accept chain=ICMP comment="3:4 and limit for 5pac/s" disabled=no icmp-options=3:4 limit=5,5 protocol=icmp
add action=accept chain=ICMP comment="8:0 and limit for 5pac/s" disabled=no icmp-options=8:0-255 limit=5,5 protocol=icmp
add action=accept chain=ICMP comment="11:0 and limit for 5pac/s" disabled=no icmp-options=11:0-255 limit=5,5 protocol=icmp
add action=jump chain=ICMP comment="Drop everything else" disabled=no jump-target=drop

# Generic tarpit, usually use it for SSH
add action=accept chain=limit_block disabled=no src-address-list=limit_block_exempt
add action=accept chain=limit_block connection-state=new disabled=no limit=3/1m,2
add action=log chain=limit_block connection-state=new disabled=yes log-prefix=""
add action=add-src-to-address-list address-list=limit_block address-list-timeout=1w chain=limit_block connection-state=new disabled=no
add action=reject chain=limit_block disabled=no reject-with=icmp-host-unreachable src-address-list=limit_block

Obviously, it’s not as clean as any of us would like, but it’s clear that chains provide the abstraction that we need to implement something like what you wanted. You should be able to abstract service groups by stacking chains. I realize that it may be hard to grok, so I’ll be happy to field any questions.


Addendum:

I put together a visual “logic chain” to aid you. Have a look:
logic chain.png
Hope that helps a bit more.

Thanks for the input, but even in your example you still have 3 firewall rules for a single service, as there is no other way to do this.

/ip firewall filter add chain="IPSec_VPN" protocol="UDP" dst-port="500" action="accept"
add chain="IPSec_VPN" protocol="ipsec-esp" action="accept"
add chain="IPSec_VPN" protocol="encap" action="accept"
add chain="IPSec_VPN" action="jump" jump-target=SOME_OTHER_LIST_THAT_DENIES_ACCESS_TO_UNINITIATED_INCOMING_TRAFFIC

Which means you need to create 3 separate entries in firewall for a single service (VPN)

Using Service groups, this would be a single firewall entry, where the services would just reference a “Service group”, consisting of multiple services. Just like you reference multiple IP addresses in firewall rules using the “Address Lists”

This can not be worked around by chains, since all you do is put these services in a different chain. While I understand that it may make the firewall a bit more organized, it however makes you look all around the place trying to figure out what jumps where (how you need a logic chain).

For example, a much cleaner firewall could be achieved like this:
(this is derived from one of my production configs as well, but modified to show how much Service Groups could help)

/ip firewall filter
# input chain
add action=accept chain=input connection-state=established,related
add action=drop chain=input connection-state=invalid
add action=accept chain=input limit=5,5 service-group="ICMP WAN"
add action=accept chain=input service-group="ROS Management WAN" in-interface=ether1-WAN
add action=accept chain=input service-group="ROS Management LAN" in-interface=ether2-LAN
add action=accept chain=input service-group="ROS VPN" src-address-list="VPN Partners"
add action=drop chain=input
# forward chain
add action=accept chain=forward connection-state=established,related
add action=drop chain=forward connection-state=invalid
add action=accept chain=forward in-interface=ether2-LAN out-interface=ether1-WAN comment="Allow LAN -> WAN"
add action=accept chain=forward dst-address=XXX.XXX.XXX.XXX service-group="HTTP"
add action=accept chain=forward dst-address=XXX.XXX.XXX.XXY service-group="DNS"
add action=accept chain=forward dst-address-list=Servers limit=2,2 service-group="ICMP Servers"
add action=drop chain=forward

Address Lists would be defined:

The Address List "Servers" would contain IPs 
XXX.XXX.XXX.YYY
XXX.XXX.XXX.ZZZ

The Address List "VPN Partners" would contain IPs
XXX.XX.X.YZ
XXX.XX.XYZ.YZ
XX.XYZ.XY.XY

And Service Groups would be defined:

The Service Group "ROS Management LAN" would contain
dst-port=5678,20561 protocol=udp
dst-port=22,8291 protocol=tcp

The Service Group "HTTP" would contain
dst-port=80 protocol=tcp
dst-port=443 protocol=tcp

The Service Group "DNS" would contain
dst-port=53 protocol=tcp
dst-port=53 protocol=udp

The Service Group "ICMP Servers" would contain
icmp-options=0:0-255 protocol=icmp
icmp-options=3:3 protocol=icmp
icmp-options=3:4 protocol=icmp
icmp-options=8:0-255 protocol=icmp
icmp-options=11:0-255 protocol=icmp

The Service Group "ROS Management WAN" would contain
dst-port=8291 protocol=tcp

The Service Group "ICMP WAN" would contain
icmp-options=0:0-255 protocol=icmp
icmp-options=3:3 protocol=icmp
icmp-options=3:4 protocol=icmp
icmp-options=8:0-255 protocol=icmp
icmp-options=11:0-255 protocol=icmp

The Service Group "ROS VPN" would contain
protocol=UDP dst-port=500
protocol=ipsec-esp
protocol=encap
protocol=ipip

Using this, the firewall is so simple and easy to follow that you dont need a logic chain diagram, and it still functions the same :slight_smile:

Also, if I wanted to allow SSH from WAN to a particular network resource, all I would need to do is add “dst-port=22 protocol=tcp” to that Service Group.

What is the status of this obvious and logical request. (group ports/services) much like addresses for an object approach, in order to reduce clutter in FW rules and allowing for changing a list, without having to monkey with FW rules.

By the way the same request for group ports/services, is equally important for Port Forwarding rules.

Its boggling my mind that something equivalent to address list (IP) is not available for ports/services.
Im trying to transition from a zyxel router and appreciating the logic and ways of other established vendors!!

As you can see, this post is all the way back from 2012.

There has been no change on this, which is sad.
There still is no way to define any groupings for protocols/ports/services in RouterOS.

If nothing else, I now have a friend in Slovakia who at least in one respect thinks alike LOL.
I am going to post in the built-in function requests thread then, perhaps, it can be made a function…

It would be nice to have support for more advanced lists, and it would be more effective especially with many items. But it’s not like the solution with chains is too bad either:

/ip firewall filter
#basic firewall
add action=accept chain=forward comment="accept established & related" connection-state=established,related
add action=drop chain=forward comment="drop invalid" connection-state=invalid
add action=accept chain=forward comment="worknet has unlimited access" in-interface=work-lan
add action=jump chain=forward comment="guests can use only web" in-interface=guest-lan jump-target=service_web
add action=accept chain=forward comment="allow dstnatted connections" connection-nat-state=dstnat
add action=jump chain=forward comment="connections from internet" in-interface=wan jump-target=incoming
add action=reject chain=forward comment="block everything else" reject-with=icmp-admin-prohibited
#incoming connections
add action=jump chain=incoming comment="mail server" dst-address=1.2.3.4 jump-target=service_mail
add action=jump chain=incoming comment="dns server" dst-address=1.2.3.5 jump-target=service_dns
add action=jump chain=incoming comment="lone webserver" dst-address=1.2.3.6 jump-target=service_web
add action=jump chain=incoming comment="other webservers based on address list" dst-address-list=web-cluster1 jump-target=service_web
add action=jump chain=incoming comment="and another group of webservers" dst-address-list=web-cluster2 jump-target=service_web
add action=jump chain=incoming comment="ipsec peers" jump-target=service_ipsec src-address-list=ipsec-peers
#reusable chains for services
add action=accept chain=service_web dst-port=80,443 protocol=tcp
add action=accept chain=service_mail dst-port=25,110,143,465,587,993,995 protocol=tcp
add action=accept chain=service_ipsec dst-port=500,4500 protocol=udp
add action=accept chain=service_ipsec protocol=ipsec-esp
add action=accept chain=service_ipsec protocol=ipsec-ah
add action=accept chain=service_dns dst-port=53 protocol=tcp
add action=accept chain=service_dns dst-port=53 protocol=udp

Hi Sob, it will take me time to digest what your are doing here.
I don’t have a sweet clue about JUMP, as I am just getting familiar with input and forward rules (to router, across the router).
Any quick explanations appreciated!!

I also am bamboozled by your use of FW rules with no forward, input, output part but using = Service??
Also conceptually speaking are you defining Chain=service as sort of like an address list for IPs? but for services?
(since they are by themselves, no IP or interface or source/destination allocation)

That is certainly seems doable and better than each individual rule separately.

It’s simple. Firewall uses chains of rules and packets travel through them until a matching rule is found. There are some default chains like “forward” or “input”, but you can also create your own. Router won’t care about them just because they exist, you need to jump to them from default ones. When you do that, either the matching rule is found in given custom chain (and processing ends with it) or if there isn’t one, it returns back to the original chain and continues there.

And for the record, posted config is just a simple example for forward, normally there would be also some rules for input.

Okay so JUMP is used basically to intercept certain packets at a certain time and MOVE THEM to a different customized FW RULE (or set of rules).
If the packet matches the customed rule(s), then the action is taken and is complete, if the packet doesn’t match the rule it goes back to the next rule after the JUMP chain (back to the ordered set of rules?

So basically the JUMP Chain is like an IF statement,
IF this, then do this, otherwise carry on…
++++++++++++++++++++++++++++++++++++++++++++++++++++++


Similar to JUMP I now have discovered MANGLE. (Slow off the mark, I know). This seems similar in that it is used to identify some traffic and then
all kinds of weird and wonderful things can be done to these packets. I am trying (unsuccessfully) to use it now to send all my email traffic from both LANs to WAN2, where WAN1 is the primary (distance 1, ping). Do I put WAN2 email server IP address on the mangle rule (or just identify port and LAN Interface List. Do I put IP address only on the IP route rule or in both places. [using mark route and new route marking]. Fixed, I had no source address listed. I thought blank meant all possible LAN IPs, for some reason putting 0.0.0.0 in source did the trick. I dont know why though.