Issues with setting up a InterVlan filtering firewall rules

Hi all.

I’m facing an issue with setting up a interVLAN filtering firewall rules which drops any connection with one VLAN to another. I have a router with configurated 3 VLANS: vlan31-staff (10.8.31.0/24), vlan32-guest (10.8.32.0/24) and vlan33-iot (10.8.33.0/24).

  • vlan31-staff represents the internal network and is suppose to access all Vlans, including the internet
  • vlan-ids=32 and vlan-ids=33 represents “host and iot” vlans and should not be able to access any other vlan network, including the router (10.8.31.1) on vlan-ids=31. Should be able to access the internet only!

I have setup firewall rules that should drop any interVlan communication between vlans: vlan32-guestvlan31-istaff and vlan33-iotvlan31-istaff

add action=drop chain=forward comment="Drop all Guest Wifi to internal network" in-interface=vlan32-guest out-interface=vlan31-staff
add action=drop chain=forward comment="Drop all from IoT Wifi to internal network" in-interface=vlan33-iot out-interface=vlan31-staff

For some reason even though these rules are present vlan32-guest and vlan33-iot is able to communicate with “vlan31-istaff”.
I can’t figure out what I’m missing / What did I overlooked or didn’t realized.

Any help would be highly appreciated.

Config:

# oct/25/2022 12:55:40 by RouterOS 7.5
# model = RB760iGS
/interface bridge
add ingress-filtering=no name=bridge-local vlan-filtering=yes
/interface ethernet
set [ find default-name=sfp1 ] disabled=yes
/interface wireguard
add listen-port=13231 mtu=1420 name=vpn-dtml-core
/interface vlan
add interface=bridge-local name=vlan31-staff vlan-id=31
add interface=bridge-local name=vlan32-guest vlan-id=32
add interface=bridge-local name=vlan33-iot vlan-id=33
/interface list
add name=WAN
add name=LAN
/interface wireless security-profiles
set [ find default=yes ] supplicant-identity=MikroTik
/ip pool
add name=dhcp-pool-staff ranges=10.8.31.20-10.8.31.249
add name=dhcp-pool-guest ranges=10.8.32.20-10.8.32.249
add name=dhcp-pool-iot ranges=10.8.33.20-10.8.33.249
/ip dhcp-server
add address-pool=dhcp-pool-staff interface=vlan31-staff name=dhcp-staff
add address-pool=dhcp-pool-guest interface=vlan32-guest name=dhcp-guest
add address-pool=dhcp-pool-iot interface=vlan33-iot name=dhcp-iot
/port
set 0 name=serial0
/system logging action
set 0 memory-lines=5000
/interface bridge port
add bridge=bridge-local frame-types=admit-only-untagged-and-priority-tagged ingress-filtering=no interface=\
    ether2 multicast-router=disabled pvid=31
add bridge=bridge-local frame-types=admit-only-untagged-and-priority-tagged ingress-filtering=no interface=\
    ether3 multicast-router=disabled pvid=31
add bridge=bridge-local frame-types=admit-only-untagged-and-priority-tagged ingress-filtering=no interface=\
    ether4 multicast-router=disabled pvid=31
add bridge=bridge-local ingress-filtering=no interface=ether5 multicast-router=disabled pvid=31
/ip neighbor discovery-settings
set discover-interface-list=!dynamic
/ipv6 settings
set disable-ipv6=yes max-neighbor-entries=8192
/interface bridge vlan
add bridge=bridge-local tagged=bridge-local untagged=ether2,ether3,ether4,ether5 vlan-ids=31
add bridge=bridge-local tagged=ether5,bridge-local vlan-ids=32
add bridge=bridge-local tagged=ether5,bridge-local vlan-ids=33
/interface list member
add interface=ether1 list=WAN
add interface=ether2 list=LAN
add interface=ether3 list=LAN
add interface=ether4 list=LAN
add interface=ether5 list=LAN
add disabled=yes interface=sfp1 list=LAN
add interface=*9 list=WAN
add interface=bridge-local list=LAN
add interface=*B list=WAN
/interface ovpn-server server
set auth=sha1,md5
/interface wireguard peers
add allowed-address=10.255.69.7/32,10.7.0.0/16,172.27.11.0/24,10.38.65.0/24 comment="Router in Prague Office" \
    endpoint-address=*** endpoint-port=13231 interface=vpn-dtml-core persistent-keepalive=25s \
    public-key=***"
add allowed-address=10.255.69.9/32,10.9.0.0/16 comment="Router in Brno Office" endpoint-address=*** \
    endpoint-port=13231 interface=vpn-dtml-core persistent-keepalive=25s public-key=\
    "***"
/ip address
add address=10.8.31.1/24 interface=vlan31-staff network=10.8.31.0
add address=10.8.32.1/24 interface=vlan32-guest network=10.8.32.0
add address=10.8.33.1/24 interface=vlan33-iot network=10.8.33.0
add address=10.255.69.8/24 interface=vpn-dtml-core network=10.255.69.0
/ip dhcp-client
add disabled=yes interface=bridge-local
add interface=ether1 use-peer-dns=no
/ip dhcp-server lease
add address=10.8.31.2 client-id=ff:b6:22:f:eb:0:2:0:0:ab:11:c5:c0:54:f2:a7:4b:fd:1c mac-address=\
    1C:69:7A:A8:23:D1 server=dhcp-staff
add address=10.8.31.3 client-id=1:78:45:58:88:c8:33 mac-address=78:45:58:88:C8:33 server=dhcp-staff
/ip dhcp-server network
add address=10.8.31.0/24 dns-server=10.8.31.1 gateway=10.8.31.1
add address=10.8.32.0/24 dns-server=10.8.31.1 gateway=10.8.32.1
add address=10.8.33.0/24 dns-server=10.8.31.1 gateway=10.8.33.1
/ip dns
set allow-remote-requests=yes cache-max-ttl=30s cache-size=512KiB max-concurrent-queries=500 \
    max-concurrent-tcp-sessions=200 servers=10.8.31.2
/ip firewall filter
add action=accept chain=input comment=WireGuard dst-port=13231 protocol=udp src-address=10.255.69.7
add action=accept chain=input comment="Accept established, related" connection-state=established,related
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=input comment=ICMP protocol=icmp
add action=drop chain=input comment="Drop invalid" connection-state=invalid
add action=drop chain=forward comment="Drop all Guest Wifi to internal network." in-interface=\
    vlan32-guest out-interface=vlan31-staff
add action=drop chain=forward comment="Drop all from IoT Wifi to internal network." in-interface=\
    vlan33-iot out-interface=vlan31-staff
/ip firewall nat
add action=masquerade chain=srcnat src-address=10.8.32.0/24
add action=masquerade chain=srcnat src-address=10.8.31.0/24
add action=masquerade chain=srcnat src-address=10.8.33.0/24
add action=masquerade chain=srcnat comment=Default out-interface=ether1
/ip route
add disabled=yes distance=1 dst-address=10.7.31.0/24 gateway=10.8.31.2 pref-src="" routing-table=main scope=30 \
    suppress-hw-offload=no target-scope=10
add disabled=no distance=1 dst-address=10.7.31.0/24 gateway=vpn-dtml-core pref-src=0.0.0.0 routing-table=main \
    scope=30 suppress-hw-offload=no target-scope=10
add disabled=no distance=1 dst-address=10.9.0.0/16 gateway=vpn-dtml-core pref-src=0.0.0.0 routing-table=main \
    scope=30 suppress-hw-offload=no target-scope=10

Very inefficient approach to firewall rules.

Take the forward chain.
All you need are the default rules…


DEFAULT RULES
ADMIN RULES TO ALLOW TRAFFIC
LAST RULE BLOCK ALL ELSE.

So this looks like.
{forward chain}
add action=fasttrack-connection chain=forward comment=“defconf: fasttrack” 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=accept chain=forward comment=“allow internet traffic” in-interface-list=LAN out-interface-list=WAN
add action=accept chain=forward comment=“allow port forwarding” connection-nat-state=dstnat

------ ADD your RULES HERE -----
add action=drop chain=forward

It should be clear that the above config allows two types of traffic from the admin. If they are not required they can be removed.
a. Internet traffic from the LAN to the WAN, and
b. Port forwarding from both external and internal users (destination nat by another name).

Now for you all you need to do is add what vlans have access to other vlans, OR
what vlans have access to a shared device on another vlan (could be a shared printer).

Common is the admin on one vlan wants access to all vlans.
add action=accept chain=forward in-interface=VLANX src-address=adminPC out-interface-list=LAN

The beauty about this approach is that you dont have to worry about blocking any traffic, the last rule drops everything!!!

++++++++++++++++++++
Just to confirm you have a hybrid port on ether5, what device is at the end of this cable???

(1) Your Interface list members needs to be cleaned up!!!

/interface list member
add interface=ether1 list=WAN
add interface=ether2 list=LAN
add interface=ether3 list=LAN
add interface=ether4 list=LAN
add interface=ether5 list=LAN
add disabled=yes interface=sfp1 list=LAN
add interface=*9 list=WAN
add interface=bridge-local list=LAN

/interface list member
add interface=ether1 list=WAN
add interface=vlan31-staff list=LAN
add interface=vlan32-guest list=LAN
add interface=vlan33-iot list=LAN

add interface=*9 list=WAN
add interface=*B list=WAN

(not sure what you are doing with *9, *B)


(2) THis is not required… I mean the source address… the security is in the encryption,
add action=accept chain=input comment=WireGuard dst-port=13231 protocol=udp src-address=10.255.69.7

IS THAT PUBLIC IP? or a LANIP??? If its a LANIP or IP address of an interface, as I suspect, it has no business on an input chain rule… GET RID OF IT..

(3) The reason I bring this up is because then you have the same IP as a wireguard PEER which does makes sense if this is an incoming subnet or identification of another Wireguard device!
YOU DONT need a keep alives at this end.

Assuming the main router is acting as the only server!
Is your intention for this main router to be able to initiate a connection with prague or Brno???

(4) You mix up input chain rules and forward chain rules… Much easier to read if they are within their same group.


(5) Can clean up masquerade… first three rules not needed.
/ip firewall nat
add action=masquerade chain=srcnat src-address=10.8.32.0/24
add action=masquerade chain=srcnat src-address=10.8.31.0/24
add action=masquerade chain=srcnat src-address=10.8.33.0/24
add action=masquerade chain=srcnat comment=Default out-interface=ether1

/ip firewall nat
add action=masquerade chain=srcnat comment=Default out-interface=ether1

Assuming ether1 is the correct name of the working wan interface, (for example if its pppoe then use pppoe name, if vlan use vlan interface name etc…)

(5) WHY are your routes so specific and your allowed IP ranges so wide?
They should match up more cleanly…
USe firewall rules at either end to allow or not allow specific subnet access.

ALSO many allowed IPs are not setup in IP routes and thus what is the point of having them as allowed IPs… ??

Thanks for the feedback. I appreciate your time!


add action=fasttrack-connection chain=forward comment=“defconf: fasttrack” connection-state=established,related

I’ve never used fasttrack in Mikrotik but it seems to be a good way to speed up connection processing.

Removed all the questions in this post as I already understand it.

(1) Your Interface list members need to be cleaned up!!!

/interface list member
add interface=ether1 list=WAN
add interface=ether2 list=LAN
add interface=ether3 list=LAN
add interface=ether4 list=LAN
add interface=ether5 list=LAN
add disabled=yes interface=sfp1 list=LAN
add interface=*9 list=WAN
add interface=bridge-local list=LAN

/interface list member
add interface=ether1 list=WAN
add interface=vlan31-staff list=LAN
add interface=vlan32-guest list=LAN
add interface=vlan33-iot list=LAN
add interface=*9 list=WAN
add interface=*B list=WAN

Yea, this was indeed a mess. Thanks for bringing this up!


(not sure what you are doing with *9, *B)

It was not supposed to be there, no idea how it got there.


(2) THis is not required… I mean the source address… the security is in the encryption,
add action=accept chain=input comment=WireGuard dst-port=13231 protocol=udp src-address=10.255.69.7

IS THAT PUBLIC IP? or a LANIP??? If its a LANIP or IP address of an interface, as I suspect, it has no business on an input chain rule… GET RID OF IT..

It was the IP of the WireGuard interface on the other side. The idea was to make the WireGuard more secure to allow only WireGuard communication on this specific address/port.


(3) The reason I bring this up is because then you have the same IP as a wireguard PEER which makes sense if this is an incoming subnet or identification of another Wireguard device!
YOU DON’T need a keep alives at this end.

Yes, as already mentioned, the IP address is the IP address of WireGuard on the other side. Not sure what you mean by “YOU DON’T need a keep alives at this end” though.


Assuming the main router is acting as the only server!
Is your intention for this main router to be able to initiate a connection with prague or Brno???

Yes, this router initiates connection with Prague..


(4) You mix up input chain rules and forward chain rules… Much easier to read if they are within their same group.

By same group you mean input chains under each other and forward chains under each other as well?


(5) Can clean up masquerade… first three rules not needed.
/ip firewall nat
add action=masquerade chain=srcnat src-address=10.8.32.0/24
add action=masquerade chain=srcnat src-address=10.8.31.0/24
add action=masquerade chain=srcnat src-address=10.8.33.0/24
add action=masquerade chain=srcnat comment=Default out-interface=ether1

The 3 rules were left there from some “experiencing”. I forgot to remove them. Thanks!


/ip firewall nat
add action=masquerade chain=srcnat comment=Default out-interface=ether1

Assuming ether1 is the correct name of the working wan interface, (for example if its pppoe then use pppoe name, if vlan use vlan interface name etc…)

Yes, ether1 is the actual WAN interface.


(5) WHY are your routes so specific and your allowed IP ranges so wide?
They should match up more cleanly…
USe firewall rules at either end to allow or not allow specific subnet access.

Can you be more specific, please?. What do you mean by so specific?/ How much more clean?
By wide ranges, you mean specifically this ip route?

add disabled=no distance=1 dst-address=10.9.0.0/16 gateway=vpn-dtml-core pref-src=0.0.0.0 routing-table=main



ALSO many allowed IPs are not setup in IP routes and thus what is the point of having them as allowed IPs… ??

Again, can you be more specific? What allowed IPs you have on mind, also where allowed?

In short, no. Not with the config you posted. Unless you mean router’s own 10.8.31.1 on vlan31-staff. Access to that is possible and would have to blocked in input chain. But you’d want to block all access to router, to any address, because they are all equal.

About @anav, he likes to keep things clean, including same config everywhere. People are sometimes surprised when he tries to fix things they didn’t want to have fixed. :wink: But he means well. And personally I also like the “allow what should be allowed and block everything else” approach better.

In short, no. Not with the config you posted. Unless you mean router’s own 10.8.31.1 on vlan31-staff. Access to that is possible and would have to blocked in input chain. But you’d want to block all access to router, to any address, because they are all equal.

So what you are saying is that my solution should work and these rules should drop all from vlan32-guest and vlan33-iot to vlan31-staff, except the router on 10.8.31.1 (vlan31-staff network)?

add action=drop chain=forward comment="Drop all Guest Wifi to internal network" in-interface=vlan32-guest out-interface=vlan31-staff
add action=drop chain=forward comment="Drop all from IoT Wifi to internal network" in-interface=vlan33-iot out-interface=vlan31-staff

And router will still be accessible from these two vlans, unless I add another rule which will drop all from all from one of the vlans network. For example:

add action=drop chain=input src-address=10.8.32.0/24 dst-address=10.8.31.1



About @anav, he likes to keep things clean, including same config everywhere. People are sometimes surprised when he tries to fix things they didn’t want to have fixed. > :wink: > But he means well. And personally I also like the “allow what should be allowed and block everything else” approach better.

I do actually appreciate this. I also like to keep things clean and clear to understand. :slight_smile: The approach “allow what should be allowed and block everything else” seems to me also cleaner.:slight_smile:

There are different schools of thought in terms of “allow everything + block specific”, vs “block everything and allow specific”

However, it can sometimes be better to explicitly allow specific traffic, so that later on, if extra interfaces are livened up, extra VLANs added, there will be no unexpected access…

Even with interface lists and firewall address lists, it is easy to forget to add new networks to multiple lists and deny rules.

If traffic is blocked by default, then you can carefully craft new rules / amend existing rules to allow the additional subnets.

If you add an interface or VLAN, you are (probably) unlikely to validate all the existing block rules to make sure that it is blocked as it should be…

Yes, there is always a chance you could craft a new rule which permits access to a wider than intended scope, but that is probably less likely than accidentally defaulting to allow rules for new subnets, etc.

Only other comment I would make is that before you implement a generic block rule to the input interface of the router, you might want to include a specific rule (beforehand) to accept input traffic directed to port 53, if you want to use the Mikrotik as a local DNS resolver for any clients in that subnet. Even then, you might want to limit the traffic from each VLAN to only the input IP of the default gateway for that network, if you have concerns that someone might try to map out your network by issuing DNS queries to the assumed network gateway for each neighbouring subnet…

R

@jzizka: Correct. Traffic between interfaces (to different connected devices) uses forward chain, where you have only four rules:

#1 - allow established and related connections
#2 - drop invalid packets
#3 - drop vlan32-guest → vlan31-staff traffic
#4 - drop vlan33-iot → vlan31-staff traffic
#5 - allow everything else (default action, not a visible rule)

Rules are checked from top to bottom and first matching one is used. There’s no way to get around #3 and #4 if conditions match. Same logic applies for traffic to router itself, which goes in input chain. It doesn’t matter which interface has target address, the fact that router has it has priority over it being on another interface.

(this was already described ^^^, so just in different words, since I already wrote it) Downside of blocking things like you do is that you have to think about too many things. Do you want to allow guests to access iot? Or iot to access guests? If not, you need another two blocking rules. Or guests or iot accessing remote offices? Probably not. So that’s other blocking rules. And then you may add another VPN later and you’ll have to add yet another blocking. If you forget, you might not even notice that something is wrong. With the opposite approach, everything not specifically allowed will be blocked. If you add something new, nothing bad happens, it’s still blocked. And if it shouldn’t be, you’ll notice immediatelly, because you won’t be able to access it.

It’s also possible to have “something in between”, e.g. (“!” means “not”):

/ip firewall filter
add chain=forward in-interface=vlan32-guest out-interface=!<WAN> action=drop

So no matter what you add later, guests would still be safely isolated. But same problem persists for other interfaces. And I find rules without negations easier to understand.

CONCUR: You know what I teach at my MTUNA Certification Class on Firewalls!!!
KISS : Keep it Simple Sob :wink:

I cleaned up the firewall and more based on your and @anav feedback. It should be in better state now.(hopefully). If not I’m opened to your suggestions :wink: I also included some of my rules:

  • Allowed only vlan31-staff to be able to access all other vlans.

  • Allowed all established, related connections for input chain.
    a) Does this rule means that only the connection that are currently already established or related will match this rule? A new connection will not match this rule.

  • Allowed access to router for all, except vlan32-guest and vlan33-iot.
    a) Is this sufficient? The router should be accessible not only to vlan32-staff but also some subnets behind (WireGuard and Tinc - one tinc site is runinng behind this router).

There is one more thing that I don’t understand:

As you can see from Config I have WIreGuard connection up and running. I tried to test input chain rules to make sure that connections that specifically don’t have a accepted rule will be dropped. I test this with WireGuard. This is what happened.
a) I disabled the router-access input rule which should drop all traffic originating only from vlan32-guest and vlan33-iot. The WireGuard was still able to do handshakes. - If u understand this currently it is because of established, related input chain rule. The connection is still established and so the WireGuard should work until the connection is lost and a new connection initiated.
b) I enabled the WireGuard again and connection was successfully remade even though the only accept input chain rule was disabled. I ask how. Should not the new WireGuard connection be dropped because there was no input chain which would accept it?

Updated Config:

/interface bridge
add ingress-filtering=no name=bridge-local vlan-filtering=yes

/interface ethernet
set [ find default-name=sfp1 ] disabled=yes

/interface wireguard
add listen-port=13231 mtu=1420 name=vpn-dtml-core

/interface vlan
add interface=bridge-local name=vlan31-staff vlan-id=31
add interface=bridge-local name=vlan32-guest vlan-id=32
add interface=bridge-local name=vlan33-iot vlan-id=33

/interface list
add name=WAN
add name=LAN
add name=NON-INTERNAL-LAN

/interface wireless security-profiles
set [ find default=yes ] supplicant-identity=MikroTik

/ip pool
add name=dhcp-pool-staff ranges=10.8.31.20-10.8.31.249
add name=dhcp-pool-guest ranges=10.8.32.20-10.8.32.249
add name=dhcp-pool-iot ranges=10.8.33.20-10.8.33.249

/ip dhcp-server
add address-pool=dhcp-pool-staff interface=vlan31-staff name=dhcp-staff
add address-pool=dhcp-pool-guest interface=vlan32-guest name=dhcp-guest
add address-pool=dhcp-pool-iot interface=vlan33-iot name=dhcp-iot

/port
set 0 name=serial0

/system logging action
set 0 memory-lines=5000

/interface bridge port
add bridge=bridge-local frame-types=admit-only-untagged-and-priority-tagged ingress-filtering=no interface=ether2 multicast-router=disabled pvid=31
add bridge=bridge-local frame-types=admit-only-untagged-and-priority-tagged ingress-filtering=no interface=ether3 multicast-router=disabled pvid=31
add bridge=bridge-local frame-types=admit-only-untagged-and-priority-tagged ingress-filtering=no interface=ether4 multicast-router=disabled pvid=31
add bridge=bridge-local ingress-filtering=no interface=ether5 multicast-router=disabled pvid=31

/ip neighbor discovery-settings
set discover-interface-list=!dynamic

/ipv6 settings
set disable-ipv6=yes max-neighbor-entries=8192

/interface bridge vlan
add bridge=bridge-local tagged=bridge-local untagged=ether2,ether3,ether4,ether5 vlan-ids=31
add bridge=bridge-local tagged=ether5,bridge-local vlan-ids=32
add bridge=bridge-local tagged=ether5,bridge-local vlan-ids=33

/interface list member
add interface=ether1 list=WAN
add disabled=yes interface=sfp1 list=LAN
add interface=vlan31-staff list=LAN
add interface=vlan32-guest list=LAN
add interface=vlan33-iot list=LAN
add interface=vlan32-guest list=NON-INTERNAL-LAN
add interface=vlan33-iot list=NON-INTERNAL-LAN

/interface ovpn-server server
set auth=sha1,md5

/interface wireguard peers
add allowed-address=10.255.69.7/32,10.7.0.0/16,172.27.11.0/24,10.38.65.0/24 comment="Router in Prague Office" endpoint-address=*** endpoint-port=13231 interface=vpn-dtml-core persistent-keepalive=25s public-key=\
    "***"
add allowed-address=10.255.69.9/32,10.9.0.0/16 comment="Router in Brno Office" endpoint-address=*** endpoint-port=13231 interface=vpn-dtml-core persistent-keepalive=25s public-key="***"

/ip address
add address=10.8.31.1/24 interface=vlan31-staff network=10.8.31.0
add address=10.8.32.1/24 interface=vlan32-guest network=10.8.32.0
add address=10.8.33.1/24 interface=vlan33-iot network=10.8.33.0
add address=10.255.69.8/24 interface=vpn-dtml-core network=10.255.69.0

/ip dhcp-client
add disabled=yes interface=bridge-local
add interface=ether1 use-peer-dns=no

/ip dhcp-server lease
add address=10.8.31.2 client-id=ff:b6:22:f:eb:0:2:0:0:ab:11:c5:c0:54:f2:a7:4b:fd:1c mac-address=1C:69:7A:A8:23:D1 server=dhcp-staff
add address=10.8.31.3 client-id=1:78:45:58:88:c8:33 mac-address=78:45:58:88:C8:33 server=dhcp-staff

/ip dhcp-server network
add address=10.8.31.0/24 dns-server=10.8.31.1 gateway=10.8.31.1
add address=10.8.32.0/24 dns-server=10.8.31.1 gateway=10.8.32.1
add address=10.8.33.0/24 dns-server=10.8.31.1 gateway=10.8.33.1

/ip dns
set allow-remote-requests=yes cache-max-ttl=30s cache-size=512KiB max-concurrent-queries=500 max-concurrent-tcp-sessions=200 servers=10.8.31.2

/ip firewall filter
add action=fasttrack-connection chain=forward comment="defconf: fasttrack" connection-state=established,related hw-offload=yes
add action=accept chain=forward comment="defconf: accept established,related" connection-state=established,related
add action=drop chain=forward comment="defconf: drop invalid" connection-state=invalid
add action=accept chain=forward comment="allow internet traffic" in-interface-list=LAN out-interface-list=WAN
add action=accept chain=forward comment="allow vlan31-staff to access all other vlans" in-interface=vlan31-staff out-interface-list=LAN
add action=drop chain=forward comment="drop all forward"
add action=accept chain=input comment="defconf: accept established, related" connection-state=established,related
add action=accept chain=input comment="allow access to router for all internal networks" in-interface-list=!NON-INTERNAL-LAN
add action=drop chain=input comment="drop all input"

/ip firewall nat
add action=masquerade chain=srcnat comment=Default out-interface=ether1

/ip route
add comment=Tinc disabled=yes distance=1 dst-address=10.7.31.0/24 gateway=10.8.31.2 pref-src="" routing-table=main scope=30 suppress-hw-offload=no target-scope=10
add disabled=no distance=1 dst-address=10.7.31.0/24 gateway=vpn-dtml-core pref-src=0.0.0.0 routing-table=main scope=30 suppress-hw-offload=no target-scope=10
add disabled=no distance=1 dst-address=10.9.0.0/16 gateway=vpn-dtml-core pref-src=0.0.0.0 routing-table=main scope=30 suppress-hw-offload=no target-scope=10

No clue on your testing methods, dont care really.
The input chain rule for wireguard allows an external user connect to establish the initial handshake connection.

WG settings/firewall rules/routes determine where the traffic will go.

There are two basic reasons for admin entered rules in the input chain.
1 - to allow VPNs to connect and by that I mean initial handshake.
2- allow users access to Router services which amounts to
a. allow admin to config router
b. allow users to access needed router services typically DNS and sometimes NTP.

I prefer to separate A and B for obvious reasons.
The default rule allows all users full access to the router I prefer to change this to something like.

{DEFAULT RULES HERE}
{ADMIN RULES}
add action=accept chain=input dst-port=WGport protocol=udp
add action=accept chain=input in-interface-list=MGMT src-address-list=Authorized
add action=accept chain=input dst-port=53,123 protocol=udp in-interface-list=LAN
add action=accept chain=input dst-port=53 protocol=tcp in-interface-list=LAN
add action=drop chain=input

If you remove the first rule, there is no way for the wireguard to make the initial handshake and no traffic will ensue.
If you do not include the wireguard interface in the interface list of MGMT or associated IPs in src-address-list, then a user coming in on WG will not be able to access the router for config purposes. You would need to make an additional line
add action=accept chain=input in-interface=wireguard_interface_name {with or without} src_address-list=WG_authorized IPs

In other words you have to understand the rules you are using/purpose and how they relate to interface lists and interfaces.

For example many folk simply make the wg interface a LAN LIST member so something like this works and provides included wg access/functionality.......

_add chain=input action=accept in-interface-list=LAN
add chain=input action=drop

add chain=forward action=accept in-interface-list=LAN out-interface-list=WAN_

Finally you need to be cognizant of routes required for the WG traffic and any other forward chain firewall rules required.

Thanks again.

Does established, related connection type means that only the connection that are currently already established or related will match this rule? A new connection will basically not match the connection type rule. Is that correct? Also when is this kinda of of connection type should be used? Can you explain?

By looking on the firewall rules I will need another rule for VPN/TINC I’m currently connected to router via VPN/TINC (not via WG) I’m accessing the router remotely via Tinc which is running on the server(10.8.31.2) behind this router. When I try to remove this current rule I lost connection with the router. Not sure what rule I should use as I don’t even see any vpn/tinc traffic going through the router.

add action=accept chain=input comment="defconf: accept established, related" connection-state=established,related

It’s not good. In input chain, when you allow everything from “not NON-INTERNAL-LAN”, you do allow all other LANs, but also WAN, because it’s not in NON-INTERNAL-LAN list. So all access from internet is now allowed. You don’t want that. Do you remember how I mentioned negations in rules? :slight_smile:

You should probably put guests and iot in UNSAFE-LAN list or something, and not include them in LAN list at all. You’d need another rule in forward to let them access internet, but that’s no problem. You could also have stricter rules for them, when you can’t really trust them, for example not allow them to send spam:

/ip firewall filter
add chain=forward in-interface-list=UNSAFE-LAN out-interface-list=WAN action=jump jump-target=unsafe-out
add chain=unsafe-out protocol=tcp dst-port=25 action=reject reject-with=icmp-admin-prohibited
add chain=unsafe-out action=accept

As for access to router, it could be e.g.:

/ip firewall filter
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=accept chain=input comment="allow access to router from all trusted internal networks" in-interface-list=LAN
add action=accept chain=input comment="allow DNS" in-interface-list=UNSAFE-LAN protocol=udp dst-port=53
add action=accept chain=input comment="allow DNS" in-interface-list=UNSAFE-LAN protocol=tcp dst-port=53
add action=accept chain=input comment="allow WG" protocol=udp dst-port=13231
add action=drop chain=input comment="drop all other input"

Connection state is at first new (when first packet comes). If it’s allowed (by some action=accept rule), further packets are established. Some can be related (recognized by connection tracking as belonging to other already established connection), e.g. data connections for FTP.

You don’t have any dstnat rules, so Tinc on server must be doing outgoing connections to somewhere else, nothing special on router is needed for that. If the machine accesses router, it’s like anything else on same interface doing it.

I Prefer a cleaner approach, made the additional interface list for MGMT (trusted) and keep the LAN covering all subnets.

Hence…
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=accept chain=input comment=“allow access to router from all trusted internal networks” in-interface-list=MNGMT src-address-list=authorized
add action=accept chain=input comment=“allow DNS” in-interface-list=LAN protocol=udp dst-port=53
add action=accept chain=input comment=“allow DNS” in-interface-list=LAN protocol=tcp dst-port=53
add action=accept chain=input comment=“allow WG” protocol=udp dst-port=13231
add action=drop chain=input comment=“drop all other input”

By doing it this way one can opt to, or not to, add a source address list to further narrow to IP addresses what users on the MGMT interface or trusted interface are allowed to access the router for config purposes.

A very nice nuance is that by leaving the LAN as is, I can actually narrow down the security Line to specific winbox access for example, and the LAN entries below it ensure all LANS, including the MGMT subnets get access to needed DNS sercvices…

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=accept chain=input comment=“allow access to router from all trusted internal networks” in-interface-list=MNGMT src-address-list=authorized dst-port=winboxport# protocol=tcp
add action=accept chain=input comment=“allow DNS” in-interface-list=LAN protocol=udp dst-port=53
add action=accept chain=input comment=“allow DNS” in-interface-list=LAN protocol=tcp dst-port=53
add action=accept chain=input comment=“allow WG” protocol=udp dst-port=13231
add action=drop chain=input comment=“drop all other input”

Where it would make sense to split off the LAN subnets into something else (besides management) is if you have different rules for access to the internet.

Yeah, why not. It’s fine tuning according to personal preferences, whatever works best for you, there’s no clear right or wrong.

So can we assume @anav prefers python to perl?

From What is the basic difference between Python vs Perl?

Different philosophy.

Python does not trust the programmer to write a readable code, and limits the language in places where people may misunderstand code. The most extreme example is with “indentation as part of the syntax”.

Perl prefers to give the programmer as much as possible syntactic choices, in the hopes that it will result in a readable, or shorter, code. That’s why so much perl code out-there looks like random garbage.

Its not what I prefer its what solves the requirements in a clean manner that is easily understood and changes (additions/modifications) are facilitated.

I like both your and @sov approach. They both seems to be clean in it’s own way. I went with MGMT approach though since I might need to specify some src-address in future.

I ended up with this:

/ip firewall filter
add action=fasttrack-connection chain=forward comment="defconf: fasttrack" connection-state=established,related hw-offload=yes
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=accept chain=forward comment="allow internet traffic" in-interface-list=LAN out-interface-list=WAN
add action=accept chain=forward comment="allow interVLAN access" in-interface-list=MGMT out-interface-list=LAN
add action=accept chain=forward comment="allow AP to communicate with UnifiController" dst-address=10.7.31.32 dst-port=8080 in-interface-list=MGMT out-interface-list=MGMT protocol=tcp src-address=10.8.31.3
add action=drop chain=forward comment="drop all"
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=accept chain=input comment="allow access to router from all trusted internal networks" in-interface-list=MGMT
add action=accept chain=input comment="allow dns query" dst-port=53 in-interface-list=LAN protocol=udp
add action=accept chain=input comment="allow dns query" dst-port=53 in-interface-list=LAN protocol=tcp
add action=accept chain=input comment="allow WG" dst-port=13231 protocol=udp
add action=drop chain=input comment="drop all "

Maybe this this is more clean:

add action=accept chain=forward comment="allow AP to communicate with UnifiController" dst-address=10.7.31.32 dst-port=8080 in-interface=vlan31-staff out-interface=vpn-dtml-core protocol=tcp src-address=10.8.31.3

then this:

add action=accept chain=forward comment="allow AP to communicate with UnifiController" dst-address=10.7.31.32 dst-port=8080 in-interface-list=MGMT out-interface-list=MGMT protocol=tcp src-address=10.8.31.3

Opinions? I see the first more clean as there seems to be no benefit of using the interface-list MGMT. Using MGMT for both in.interface and out.interface is kinda confusing and not seems like a good practice to me.