mirror of
https://github.com/netbirdio/netbird.git
synced 2024-11-28 11:03:30 +01:00
[client] Add table filter rules using iptables (#2727)
This specifically concerns the established/related rule since this one is not compatible with iptables-nft even if it is generated the same way by iptables-translate.
This commit is contained in:
parent
da3a053e2b
commit
3a88ac78ff
@ -315,28 +315,33 @@ func insertReturnTrafficRule(conn *nftables.Conn, table *nftables.Table, chain *
|
|||||||
rule := &nftables.Rule{
|
rule := &nftables.Rule{
|
||||||
Table: table,
|
Table: table,
|
||||||
Chain: chain,
|
Chain: chain,
|
||||||
Exprs: []expr.Any{
|
Exprs: getEstablishedExprs(1),
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.InsertRule(rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEstablishedExprs(register uint32) []expr.Any {
|
||||||
|
return []expr.Any{
|
||||||
&expr.Ct{
|
&expr.Ct{
|
||||||
Key: expr.CtKeySTATE,
|
Key: expr.CtKeySTATE,
|
||||||
Register: 1,
|
Register: register,
|
||||||
},
|
},
|
||||||
&expr.Bitwise{
|
&expr.Bitwise{
|
||||||
SourceRegister: 1,
|
SourceRegister: register,
|
||||||
DestRegister: 1,
|
DestRegister: register,
|
||||||
Len: 4,
|
Len: 4,
|
||||||
Mask: binaryutil.NativeEndian.PutUint32(expr.CtStateBitESTABLISHED | expr.CtStateBitRELATED),
|
Mask: binaryutil.NativeEndian.PutUint32(expr.CtStateBitESTABLISHED | expr.CtStateBitRELATED),
|
||||||
Xor: binaryutil.NativeEndian.PutUint32(0),
|
Xor: binaryutil.NativeEndian.PutUint32(0),
|
||||||
},
|
},
|
||||||
&expr.Cmp{
|
&expr.Cmp{
|
||||||
Op: expr.CmpOpNeq,
|
Op: expr.CmpOpNeq,
|
||||||
Register: 1,
|
Register: register,
|
||||||
Data: []byte{0, 0, 0, 0},
|
Data: []byte{0, 0, 0, 0},
|
||||||
},
|
},
|
||||||
|
&expr.Counter{},
|
||||||
&expr.Verdict{
|
&expr.Verdict{
|
||||||
Kind: expr.VerdictAccept,
|
Kind: expr.VerdictAccept,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
conn.InsertRule(rule)
|
|
||||||
}
|
}
|
||||||
|
@ -109,6 +109,7 @@ func TestNftablesManager(t *testing.T) {
|
|||||||
Register: 1,
|
Register: 1,
|
||||||
Data: []byte{0, 0, 0, 0},
|
Data: []byte{0, 0, 0, 0},
|
||||||
},
|
},
|
||||||
|
&expr.Counter{},
|
||||||
&expr.Verdict{
|
&expr.Verdict{
|
||||||
Kind: expr.VerdictAccept,
|
Kind: expr.VerdictAccept,
|
||||||
},
|
},
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/coreos/go-iptables/iptables"
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/google/nftables"
|
"github.com/google/nftables"
|
||||||
"github.com/google/nftables/binaryutil"
|
"github.com/google/nftables/binaryutil"
|
||||||
@ -81,7 +82,7 @@ func newRouter(parentCtx context.Context, workTable *nftables.Table, wgIface iFa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = r.cleanUpDefaultForwardRules()
|
err = r.removeAcceptForwardRules()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to clean up rules from FORWARD chain: %s", err)
|
log.Errorf("failed to clean up rules from FORWARD chain: %s", err)
|
||||||
}
|
}
|
||||||
@ -98,40 +99,7 @@ func (r *router) Reset() error {
|
|||||||
// clear without deleting the ipsets, the nf table will be deleted by the caller
|
// clear without deleting the ipsets, the nf table will be deleted by the caller
|
||||||
r.ipsetCounter.Clear()
|
r.ipsetCounter.Clear()
|
||||||
|
|
||||||
return r.cleanUpDefaultForwardRules()
|
return r.removeAcceptForwardRules()
|
||||||
}
|
|
||||||
|
|
||||||
func (r *router) cleanUpDefaultForwardRules() error {
|
|
||||||
if r.filterTable == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
chains, err := r.conn.ListChainsOfTableFamily(nftables.TableFamilyIPv4)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("list chains: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, chain := range chains {
|
|
||||||
if chain.Table.Name != r.filterTable.Name || chain.Name != chainNameForward {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
rules, err := r.conn.GetRules(r.filterTable, chain)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("get rules: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, rule := range rules {
|
|
||||||
if bytes.Equal(rule.UserData, []byte(userDataAcceptForwardRuleIif)) ||
|
|
||||||
bytes.Equal(rule.UserData, []byte(userDataAcceptForwardRuleOif)) {
|
|
||||||
if err := r.conn.DelRule(rule); err != nil {
|
|
||||||
return fmt.Errorf("delete rule: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return r.conn.Flush()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *router) loadFilterTable() (*nftables.Table, error) {
|
func (r *router) loadFilterTable() (*nftables.Table, error) {
|
||||||
@ -167,7 +135,9 @@ func (r *router) createContainers() error {
|
|||||||
Type: nftables.ChainTypeNAT,
|
Type: nftables.ChainTypeNAT,
|
||||||
})
|
})
|
||||||
|
|
||||||
r.acceptForwardRules()
|
if err := r.acceptForwardRules(); err != nil {
|
||||||
|
log.Errorf("failed to add accept rules for the forward chain: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := r.refreshRulesMap(); err != nil {
|
if err := r.refreshRulesMap(); err != nil {
|
||||||
log.Errorf("failed to clean up rules from FORWARD chain: %s", err)
|
log.Errorf("failed to clean up rules from FORWARD chain: %s", err)
|
||||||
@ -577,19 +547,60 @@ func (r *router) RemoveAllLegacyRouteRules() error {
|
|||||||
// that our traffic is not dropped by existing rules there.
|
// that our traffic is not dropped by existing rules there.
|
||||||
// The existing FORWARD rules/policies decide outbound traffic towards our interface.
|
// The existing FORWARD rules/policies decide outbound traffic towards our interface.
|
||||||
// In case the FORWARD policy is set to "drop", we add an established/related rule to allow return traffic for the inbound rule.
|
// In case the FORWARD policy is set to "drop", we add an established/related rule to allow return traffic for the inbound rule.
|
||||||
func (r *router) acceptForwardRules() {
|
func (r *router) acceptForwardRules() error {
|
||||||
if r.filterTable == nil {
|
if r.filterTable == nil {
|
||||||
log.Debugf("table 'filter' not found for forward rules, skipping accept rules")
|
log.Debugf("table 'filter' not found for forward rules, skipping accept rules")
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fw := "iptables"
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
log.Debugf("Used %s to add accept forward rules", fw)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Try iptables first and fallback to nftables if iptables is not available
|
||||||
|
ipt, err := iptables.New()
|
||||||
|
if err != nil {
|
||||||
|
// filter table exists but iptables is not
|
||||||
|
log.Warnf("Will use nftables to manipulate the filter table because iptables is not available: %v", err)
|
||||||
|
|
||||||
|
fw = "nftables"
|
||||||
|
return r.acceptForwardRulesNftables()
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.acceptForwardRulesIptables(ipt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *router) acceptForwardRulesIptables(ipt *iptables.IPTables) error {
|
||||||
|
var merr *multierror.Error
|
||||||
|
for _, rule := range r.getAcceptForwardRules() {
|
||||||
|
if err := ipt.Insert("filter", chainNameForward, 1, rule...); err != nil {
|
||||||
|
merr = multierror.Append(err, fmt.Errorf("add iptables rule: %v", err))
|
||||||
|
} else {
|
||||||
|
log.Debugf("added iptables rule: %v", rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nberrors.FormatErrorOrNil(merr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *router) getAcceptForwardRules() [][]string {
|
||||||
|
intf := r.wgIface.Name()
|
||||||
|
return [][]string{
|
||||||
|
{"-i", intf, "-j", "ACCEPT"},
|
||||||
|
{"-o", intf, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *router) acceptForwardRulesNftables() error {
|
||||||
intf := ifname(r.wgIface.Name())
|
intf := ifname(r.wgIface.Name())
|
||||||
|
|
||||||
// Rule for incoming interface (iif) with counter
|
// Rule for incoming interface (iif) with counter
|
||||||
iifRule := &nftables.Rule{
|
iifRule := &nftables.Rule{
|
||||||
Table: r.filterTable,
|
Table: r.filterTable,
|
||||||
Chain: &nftables.Chain{
|
Chain: &nftables.Chain{
|
||||||
Name: "FORWARD",
|
Name: chainNameForward,
|
||||||
Table: r.filterTable,
|
Table: r.filterTable,
|
||||||
Type: nftables.ChainTypeFilter,
|
Type: nftables.ChainTypeFilter,
|
||||||
Hooknum: nftables.ChainHookForward,
|
Hooknum: nftables.ChainHookForward,
|
||||||
@ -609,6 +620,15 @@ func (r *router) acceptForwardRules() {
|
|||||||
}
|
}
|
||||||
r.conn.InsertRule(iifRule)
|
r.conn.InsertRule(iifRule)
|
||||||
|
|
||||||
|
oifExprs := []expr.Any{
|
||||||
|
&expr.Meta{Key: expr.MetaKeyOIFNAME, Register: 1},
|
||||||
|
&expr.Cmp{
|
||||||
|
Op: expr.CmpOpEq,
|
||||||
|
Register: 1,
|
||||||
|
Data: intf,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
// Rule for outgoing interface (oif) with counter
|
// Rule for outgoing interface (oif) with counter
|
||||||
oifRule := &nftables.Rule{
|
oifRule := &nftables.Rule{
|
||||||
Table: r.filterTable,
|
Table: r.filterTable,
|
||||||
@ -619,36 +639,72 @@ func (r *router) acceptForwardRules() {
|
|||||||
Hooknum: nftables.ChainHookForward,
|
Hooknum: nftables.ChainHookForward,
|
||||||
Priority: nftables.ChainPriorityFilter,
|
Priority: nftables.ChainPriorityFilter,
|
||||||
},
|
},
|
||||||
Exprs: []expr.Any{
|
Exprs: append(oifExprs, getEstablishedExprs(2)...),
|
||||||
&expr.Meta{Key: expr.MetaKeyOIFNAME, Register: 1},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: expr.CmpOpEq,
|
|
||||||
Register: 1,
|
|
||||||
Data: intf,
|
|
||||||
},
|
|
||||||
&expr.Ct{
|
|
||||||
Key: expr.CtKeySTATE,
|
|
||||||
Register: 2,
|
|
||||||
},
|
|
||||||
&expr.Bitwise{
|
|
||||||
SourceRegister: 2,
|
|
||||||
DestRegister: 2,
|
|
||||||
Len: 4,
|
|
||||||
Mask: binaryutil.NativeEndian.PutUint32(expr.CtStateBitESTABLISHED | expr.CtStateBitRELATED),
|
|
||||||
Xor: binaryutil.NativeEndian.PutUint32(0),
|
|
||||||
},
|
|
||||||
&expr.Cmp{
|
|
||||||
Op: expr.CmpOpNeq,
|
|
||||||
Register: 2,
|
|
||||||
Data: []byte{0, 0, 0, 0},
|
|
||||||
},
|
|
||||||
&expr.Counter{},
|
|
||||||
&expr.Verdict{Kind: expr.VerdictAccept},
|
|
||||||
},
|
|
||||||
UserData: []byte(userDataAcceptForwardRuleOif),
|
UserData: []byte(userDataAcceptForwardRuleOif),
|
||||||
}
|
}
|
||||||
|
|
||||||
r.conn.InsertRule(oifRule)
|
r.conn.InsertRule(oifRule)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *router) removeAcceptForwardRules() error {
|
||||||
|
if r.filterTable == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try iptables first and fallback to nftables if iptables is not available
|
||||||
|
ipt, err := iptables.New()
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Will use nftables to manipulate the filter table because iptables is not available: %v", err)
|
||||||
|
return r.removeAcceptForwardRulesNftables()
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.removeAcceptForwardRulesIptables(ipt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *router) removeAcceptForwardRulesNftables() error {
|
||||||
|
chains, err := r.conn.ListChainsOfTableFamily(nftables.TableFamilyIPv4)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("list chains: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, chain := range chains {
|
||||||
|
if chain.Table.Name != r.filterTable.Name || chain.Name != chainNameForward {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rules, err := r.conn.GetRules(r.filterTable, chain)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("get rules: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rule := range rules {
|
||||||
|
if bytes.Equal(rule.UserData, []byte(userDataAcceptForwardRuleIif)) ||
|
||||||
|
bytes.Equal(rule.UserData, []byte(userDataAcceptForwardRuleOif)) {
|
||||||
|
if err := r.conn.DelRule(rule); err != nil {
|
||||||
|
return fmt.Errorf("delete rule: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := r.conn.Flush(); err != nil {
|
||||||
|
return fmt.Errorf(flushError, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *router) removeAcceptForwardRulesIptables(ipt *iptables.IPTables) error {
|
||||||
|
var merr *multierror.Error
|
||||||
|
for _, rule := range r.getAcceptForwardRules() {
|
||||||
|
if err := ipt.DeleteIfExists("filter", chainNameForward, rule...); err != nil {
|
||||||
|
merr = multierror.Append(err, fmt.Errorf("remove iptables rule: %v", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nberrors.FormatErrorOrNil(merr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveNatRule removes a nftables rule pair from nat chains
|
// RemoveNatRule removes a nftables rule pair from nat chains
|
||||||
|
Loading…
Reference in New Issue
Block a user