How can I properly isolate devices behind a RelayD bridge using the firewall ?
The devices should only be allowed to do what’s absolutely necessary. Some should have access to internal traffic only, while others should be allowed to connect to the Internet, but only to specific addresses/ports.
Is that even reasonably possible with Relayd ? I don’t think so.
In general terms you are talking about “guest network” but you have a dull knife to cut through. With enough effort and (brain) force it will work.
Yes it is possible to attach a firewall to “our” side of relayd, but bridge firewall is totally not supported by fw4 or -3
See upstream documentation
Above general concepts probably worth exploring docker and its ebtables usage.
Feel free to ask further if you need help with firewall rules.
Sorry, I don’t quite understand that sentence. Does it mean that it does work — RelayD together with bridge firewalling — or that it’s not supported?
I’ve already managed to secure a guest network on another repeater using firewall rules without any issues.
However, in this case it’s not a guest network (which would normally be a separate IP subnet), but rather part of the local network itself.
Bridge filtering is SUPPORTED just not instrumented in any way in OpenWrt and You are to do the heavy lifting. You made through the normal routed-nat firewall with checkboxes, happy? For the bridge you will get none of that comfort.
Thanks a lot for the explanation. Too bad there’s no LuCI GUI for this.
Work through the ton of documentation, add counter and log to rules, or trace them
https://wiki.nftables.org/wiki-nftables/index.php/Ruleset_debug/tracing
Just drop a line if you need help.
My first attempt is planned like this:
Installation:
kmod-nft-bridge
kmod-br-netfilter
Startup script (/etc/rc.local):
#Enable bridge firewall
sysctl -w net.bridge.bridge-nf-call-iptables=1
sysctl -w net.bridge.bridge-nf-call-arptables=1
sysctl -w net.bridge.bridge-nf-call-ip6tables=1
#Load bridge firewall rules
nft -f /etc/config/nftables.bridge
** /etc/config/nftables.bridge:**
table bridge filter {
chain forward {
type filter hook forward priority filter; policy accept;
# Always allow management IP
ip saddr 192.168.1.31 accept
ip daddr 192.168.1.31 accept
# Trusted devices
ip saddr 192.168.1.27 accept # Heizraum-ESP
ip daddr 192.168.1.27 accept # Heizraum-ESP
ip saddr 192.168.1.34 accept # Heizraum-Heater
ip daddr 192.168.1.34 accept # Heizraum-Heater
ip saddr 192.168.1.18 accept # Smartmeter
ip daddr 192.168.1.18 accept # Smartmeter
ip saddr 192.168.1.13 accept # Sonoff
ip daddr 192.168.1.13 accept # Sonoff
# MQTT port 1883
tcp dport 1883 accept
# Modbus port 502
tcp dport 502 accept
# Allow HTTP
ip daddr 192.168.1.21 tcp dport 80 accept # SolvisBen
ip daddr 192.168.1.20 tcp dport 80 accept # GoodWe
# Log and drop everything else (simulation/analysis mode)
log prefix "BRIDGE_DROP: " flags all drop
# Example: logging only for SolvisBen
# ip saddr 192.168.1.21 log prefix "BRIDGE_LOG_SRC_SOLVISBEN: " flags all
# ip daddr 192.168.1.21 log prefix "BRIDGE_LOG_DST_SOLVISBEN: " flags all
# Optional: block LAN <-> WWAN
# iifname "br-lan" oifname "phy0-sta0" drop
# iifname "phy0-sta0" oifname "br-lan" drop
}
}
I’ll report back once I’ve tested it — unless someone with more experience sees any issues or has suggestions?
Part 1
set green {
typeof ip saddr
# flags dynamic
elements = {
192.168.1.31 ,
192.168.1.27 ,
192.168.1.34 ,
192.168.1.18 ,
192.168.1.13
}
}
chain forward {
type filter hook forward priority filter; policy accept;
ip saddr \@greem accept
ip daddr \@green accept
Part2:
This is stateless
tcp dport 1883 accept
tcp dport 502 accept
So you need to start with conntrack statement to turn hook content into conntrack
ct state established,related accept # dont dare to drop invalid
# host rules also after this
tcp dport {502,1883 } accept
Alternative stateless
tcp sport . tcp dport { 502 . 1-65535 , 1-65535 . 502 ….
You also need ARP broadcasts permitted. and DHCP broadcasts….
I’m disappointed — the bridge firewall doesn’t work with RelayD. I wanted to block access to 192.168.1.12, but unfortunately it didn’t work.
My nftables:
root@Repeater-KE:~# nft list ruleset
table bridge filter {
set green {
typeof ip saddr
elements = { 192.168.1.5, 192.168.1.13,
192.168.1.14, 192.168.1.16,
192.168.1.18, 192.168.1.27,
192.168.1.31, 192.168.1.34 }
}
set limited_internet {
typeof ip saddr
elements = { 192.168.1.20, 192.168.1.21 }
}
chain forward {
type filter hook forward priority filter; policy drop;
ip daddr 192.168.1.12 drop
ip saddr 192.168.1.12 drop
ether saddr 5c:cf:7f:1b:a9:89 drop
ether daddr 5c:cf:7f:1b:a9:89 drop
ct state established,related accept
ether type arp accept
udp dport { 67, 68 } accept
ip daddr 224.0.0.0/4 accept
ip daddr 255.255.255.255 accept
udp dport 53 accept
tcp dport 53 accept
udp dport 123 accept
ip protocol icmp icmp type { echo-reply, echo-request } accept
ip saddr @green accept
ip daddr @green accept
ip saddr @limited_internet ip daddr != 192.168.1.0/24 accept
ip saddr @limited_internet ip daddr 192.168.1.0/24 tcp dport 502 accept
ip saddr 192.168.1.0/24 ip daddr @limited_internet accept
log prefix "BRIDGE_DROP: " flags all drop
}
}
root@Repeater-KE:~#
relayd is “input” probably - make rate-limited log in other possible hook points.
Okay — using the input hook instead of forward made the difference. Thanks, brada4 !
ip daddr 192.168.1.12 drop
ip saddr 192.168.1.12 drop
ether saddr 5c:cf:7f:1b:a9:89 drop
ether daddr 5c:cf:7f:1b:a9:89 drop
ct state established,related accept
ether type arp accept
+ # more accurate match
- udp dport { 67, 68 } accept
+ meta nfproto ipv4 udp sport . udp dport { 67 . 68 , 68 . 67 } accept
+ # group these
- ip daddr 224.0.0.0/4 accept
- ip daddr 255.255.255.255 accept
+ ip daddr {224.0.0.0/4 , 255.255.255.255} accept
+ # and these
- udp dport 53 accept
- tcp dport 53 accept
- udp dport 123 accept
+ meta l4proto . th dport { udp . 53 , tcp . 53 , udp . 123} accept
+ conntrack handles echo reply
- ip protocol icmp icmp type { echo-reply, echo-request } accept
+ icmp type echo-request accept
ip saddr @green accept
ip daddr @green accept
ip saddr @limited_internet ip daddr != 192.168.1.0/24 accept
ip saddr @limited_internet ip daddr 192.168.1.0/24 tcp dport 502 accept
ip saddr 192.168.1.0/24 ip daddr @limited_internet accept
smth like that - nft list table bridge filter | nft -c -o -f -
Maybe this is faster than address matching ymmv, you need 10k of same filter to get measurable delays
iptables-translate -A input -m addrtype --dst-type broadcast,multicast
nft 'add rule ip filter input fib daddr type { broadcast, multicast } counter'