Likely bug in OpenWrt firewall rule generation

I think this bug has been here for a while but it's still 18.06.0.

With the following settings in /etc/config/firewall:

config defaults
	option syn_flood	1
	option input		ACCEPT
	option output		ACCEPT
	option forward		REJECT
# Uncomment this line to disable ipv6 rules
#	option disable_ipv6	1


config zone
	option name		wan
	list   network		'wan'
	list   network		'wan6'
	option input		DROP
	option output		ACCEPT
	option forward		DROP

This generates in both iptables and ip6tables the following issue:

-A FORWARD -i eth0.2 -m comment --comment "!fw3" -j zone_wan_forward
-A FORWARD -i pppoe-wan -m comment --comment "!fw3" -j zone_wan_forward

Nothing drops in zone_wan_forward and it jumps to:

-A zone_wan_forward -m comment --comment "!fw3" -j zone_wan_dest_DROP

In zone_wan_dest_DROP we have:

-A zone_wan_dest_DROP -o eth0.2 -m comment --comment "!fw3" -j DROP
-A zone_wan_dest_DROP -o pppoe-wan -m comment --comment "!fw3" -j DROP

But this was a packet that came IN either eth0.2 and pppoe-wan WAN interfaces so it will never match the "-o" i.e. the output interface in these rules.

So the packet WON'T get dropped!

The only thing that will then save you here is the default FORWARD rule is REJECT for most people (hopefully).

I presume the correct rule should have been like the zone_wan_input:

-A zone_wan_input -m comment --comment "!fw3" -j zone_wan_src_DROP

i.e. the last line of zone_wan_forward should have been:

-A zone_wan_forward -m comment --comment "!fw3" -j zone_wan_src_DROP

These rules are almost identical between IPv4 and IPv6 but it's more serious in IPv6, as internal hosts have real addresses so the inbound forwarding rule will be hit a lot.

In IPv4 most people will have NAT setups with a single IP on the external, so the FORWARD rule inbound is not really used (purely INPUT).

The rule generation is correct. The per-zone forward controls forwarding traffic among the ifaces of this zone. Traffic from/to other zones is handled by the global forward policy, or individual forwardings or rules.

Note that the forward policy is also commonly set to drop:

$ iptables -L FORWARD | head -n1
Chain FORWARD (policy DROP)

Okay, if you say this is correct. Where is this DROP ending up?

config zone
        option name		wan
	list   network		'wan'
	list   network		'wan6'
	option forward		DROP

This should surely means the default for forwarded packets incoming to this zone's interfaces is to DROP them (on no other rules matching). This isn't what happens, they get rejected for me! So this isn't working?

I see your definition now in the instructions. Not at all what I thought this represents. I guess I need to add lots of explicit catch all rule

"FORWARD rules for a zone describe what happens to traffic passing between different interfaces in that zone."

Not at all what I thought this represents. I guess I need to add lots of catch all rules between my various zones to DROP explicitly and REJECT where they are internal zones.

Or prevent forwarding to those zones. Add each to its own zone, and do not explicitly permit forwarding to one another.

Well the default setting is a global forward reject rule with a forward policy of "drop". So no traffic which is not explicitly accepted by something is rejected or dropped. You do not need extra rules in each zone to that.

Edit: maybe I misunderstood you and you mean explicit differentiation of REJECT -or- DROP per zone - in this case you would indeed require some sentinel rules.

1 Like

So what I want to achieve is from wan DROP but REJECT from my various internal zones where I haven't explicitly allowed ports. So I added at the bottom:

config rule
        option name             DroponWAN
        option src              wan
        option dest             *
        option target           DROP

Sadly this breaks my inbound ipv4 redirects as this seems to generate:

-A zone_wan_forward -p tcp -m comment --comment "!fw3: DroponWAN" -j DROP
-A zone_wan_forward -p udp -m comment --comment "!fw3: DroponWAN" -j DROP
-A zone_wan_forward -m conntrack --ctstate DNAT -m comment --comment "!fw3: Accept port forwards" -j ACCEPT

Anyway of achieving this?

I think you're not understanding zone based firewalls...when I was a noob with OpenWrt and similar firewalls, I also inquired.

OK, if WAN is in its default zone, inbound isn't permitted to any other zone.

Now, regarding the ports, if you're referring to outbound, remove forwarding to WAN on the LAN zone, then create individual outbound rules on the ports you desire. Likewise, if you're talking about between zones, forwarding is not permitted by default, so simply make your traffic rules for each port.

i do not see this debunked yet, so:

No! NAT happens before the routing decision (INPUT/FORWARD), so it will not make a difference.

(for ref https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg)

The scenario I'd like to try and implement is that I have internal VPN zones that are lesser trusted than LAN so I have rules opening up specific ports from VPN->LAN. But I'd like non opened ports to REJECT and not DROP on this, as I don't want hangs on these "internal" communications. But I'd like WAN->LAN to DROP to limit port scans.

The simpler way forward may be to use a global REJECT policy and then a sentinel DROP rule on WAN and other external zones you would like to drop instead of rejecting traffic.

Alternatively, when using the approach you posted above, you need to implement manual port-forward-allow rules for each port forward you declared as user rules are prioritized above any standard rules (like "allow all port forwards") as you've already noticed.

I guess I have fixed myself. Setting the default back to DROP and adding REJECT specifically for the vpnc:

config rule
	option name		REJECTOnVPN
	option src		vpnc
	option dest 		*
	option target		REJECT

I guess all that remains is the general point, I seem to add a catch all rule like this on WAN without breaking my INBOUND from WAN redirects.

The rule generation is correct. The per-zone forward controls forwarding traffic among the ifaces of this zone. Traffic from/to other zones is handled by the global forward policy, or individual forwardings or rules.

Oh wow, my whole life is a lie, I've been using this wrongly for years and never noticed!

Sorry for bumping the old thread, but this definitely could use better naming in LuCI, I've quickly done a PR, ideas for better names than "Intra-Zone Forward" are welcome: https://github.com/openwrt/luci/pull/6537

The description above the options already makes it clear how the controls operate:

The input and output options set the default policies for traffic entering and leaving this zone while the forward option describes the policy for forwarded traffic between different networks within the zone.

There's also the further text above the controls for forwarding between zones:

The options below control the forwarding policies between this zone (lan) and other zones. Destination zones cover forwarded traffic originating from lan . Source zones match forwarded traffic from other zones targeted at lan . The forwarding rule is unidirectional , e.g. a forward from lan to wan does not imply a permission to forward from wan to lan as well.

If that's insufficient for people to understand what the controls do then I don't think adding 'Intra-Zone' is going to make a huge amount of difference.

The one I really don't understand is that inbound IPv6 (or IPv4 if not a NAT I'd guess) that aren't allowed from WAN follow this path:

chain forward -> forward_wan -> drop_to_wan
goes to rule
oifname { "eth0.2", "pppoe-wan" } counter packets 0 bytes 0 drop comment "!fw4: drop ...

So won't trap the inbound packet, only gets blocked by the default drop on the forward. Maybe that is intended, just seems weird.