netconfig: do not re-create nftables ruleset from scratch

The current behavior stomps on the rules that programs like
podman or tailscale set up for port forwarding.

With this change, we split port forwardings into a separate chain,
which allows us to create the ruleset once at startup and then only
update the port forwardings specifically (the only dynamic part
of router7’s nftables ruleset).
This commit is contained in:
Michael Stapelberg 2024-05-09 09:55:45 +02:00
parent ac71701d8c
commit f835cdf1d6
2 changed files with 57 additions and 3 deletions

View File

@ -151,13 +151,17 @@ func goldenNftablesRules(additionalForwarding bool) string {
ip daddr != 127.0.0.0/8 ip daddr != 10.0.0.0/24 fib daddr type 2 tcp dport 8045 dnat to 192.168.42.22:8045`
}
return `table ip nat {
chain prerouting {
type nat hook prerouting priority 0; policy accept;
chain router7-portforwardings {
ip daddr != 127.0.0.0/8 ip daddr != 10.0.0.0/24 fib daddr type 2 tcp dport 8080 dnat to 192.168.42.23:9999` + add + `
ip daddr != 127.0.0.0/8 ip daddr != 10.0.0.0/24 fib daddr type 2 tcp dport 8040-8060 dnat to 192.168.42.99:8040-8060
ip daddr != 127.0.0.0/8 ip daddr != 10.0.0.0/24 fib daddr type 2 udp dport 53 dnat to 192.168.42.99:53
}
chain prerouting {
type nat hook prerouting priority 0; policy accept;
jump router7-portforwardings
}
chain postrouting {
type nat hook postrouting priority 100; policy accept;
oifname "uplink0" masquerade

View File

@ -876,9 +876,42 @@ func hairpinDNAT() []expr.Any {
}
}
const pfChain = "router7-portforwardings"
// Only update port forwarding if there are existing rules.
// This is required to not stomp over podman port forwarding, for example.
func updatePortforwardingsOnly(dir, ifname string) error {
c := &nftables.Conn{}
nat, err := c.ListTable("nat")
if err != nil {
return err
}
chain, err := c.ListChain(nat, pfChain)
if err != nil {
return err
}
log.Printf("rules already configured, only updating port forwardings")
c.FlushChain(chain)
if err := applyPortForwardings(dir, ifname, c, nat, chain); err != nil {
return err
}
return c.Flush()
}
func applyFirewall(dir, ifname string) error {
c := &nftables.Conn{}
if err := updatePortforwardingsOnly(dir, ifname); err != nil {
log.Printf("could not update port forwardings (%v), creating ruleset from scratch", err)
} else {
return nil // keep existing ruleset
}
c.FlushRuleset()
nat := c.AddTable(&nftables.Table{
@ -886,6 +919,12 @@ func applyFirewall(dir, ifname string) error {
Name: "nat",
})
pf := c.AddChain(&nftables.Chain{
Name: pfChain,
Table: nat,
Type: nftables.ChainTypeNAT,
})
prerouting := c.AddChain(&nftables.Chain{
Name: "prerouting",
Hooknum: nftables.ChainHookPrerouting,
@ -894,6 +933,17 @@ func applyFirewall(dir, ifname string) error {
Type: nftables.ChainTypeNAT,
})
c.AddRule(&nftables.Rule{
Table: nat,
Chain: prerouting,
Exprs: []expr.Any{
&expr.Verdict{
Kind: expr.VerdictJump,
Chain: pfChain,
},
},
})
postrouting := c.AddChain(&nftables.Chain{
Name: "postrouting",
Hooknum: nftables.ChainHookPostrouting,
@ -925,7 +975,7 @@ func applyFirewall(dir, ifname string) error {
Exprs: hairpinDNAT(),
})
if err := applyPortForwardings(dir, ifname, c, nat, prerouting); err != nil {
if err := applyPortForwardings(dir, ifname, c, nat, pf); err != nil {
return err
}