From ac71e6af01c872455e9dca03497c7fcfef1216f2 Mon Sep 17 00:00:00 2001 From: Roman Dodin Date: Sat, 18 Jan 2025 21:54:10 +0200 Subject: [PATCH 1/2] skip docker-user ipv6 manipulations if the chain is not found --- runtime/docker/firewall/nftables/client.go | 105 ++++++++++++--------- 1 file changed, 63 insertions(+), 42 deletions(-) diff --git a/runtime/docker/firewall/nftables/client.go b/runtime/docker/firewall/nftables/client.go index da2193de2..cfe44a493 100644 --- a/runtime/docker/firewall/nftables/client.go +++ b/runtime/docker/firewall/nftables/client.go @@ -30,6 +30,8 @@ type NftablesClient struct { nftConn *nftables.Conn // is ip6_tables supported ip6_tables bool + // is ip6 for docker supported + ip6_docker bool } // NewNftablesClient returns a new NftablesClient. @@ -42,22 +44,31 @@ func NewNftablesClient() (*NftablesClient, error) { nftC := &NftablesClient{ nftConn: nftConn, - ip6_tables: true, + ip6_tables: false, + ip6_docker: false, } - chains, err := nftC.getChains(definitions.DockerUserChain, nftables.TableFamilyIPv4) + chain, err := nftC.getChain(definitions.ForwardChain, definitions.FilterTable, nftables.TableFamilyIPv4) if err != nil { return nil, err } - if len(chains) == 0 { + if chain == nil { log.Debugf("nftables does not seem to be in use, no %s chain found.", definitions.DockerUserChain) return nil, definitions.ErrNotAvailable } - // check if ip6_tables is available - v6Tables, err := nftC.nftConn.ListTablesOfFamily(nftables.TableFamilyIPv6) - if err != nil || len(v6Tables) == 0 { - nftC.ip6_tables = false + // check if ip6_tables is generally available + v6Chains, err := nftC.nftConn.ListChainsOfTableFamily(nftables.TableFamilyIPv6) + if err == nil && len(v6Chains) > 0 { + log.Debugf("nftables: ip6 address family generally supported") + nftC.ip6_tables = true + } + + // check if ip6 docker-user chain is available + v6DockerChain, err := nftC.getChain(definitions.DockerUserChain, definitions.FilterTable, nftables.TableFamilyIPv6) + if err == nil && v6DockerChain != nil { + log.Debugf("nftables: ip6 docker is supported") + nftC.ip6_docker = true } return nftC, nil @@ -83,15 +94,23 @@ func (c *NftablesClient) DeleteForwardingRules(rule definitions.FirewallRule) er return fmt.Errorf("%w. See http://containerlab.dev/manual/network/#external-access", err) } - var v6rules []*nftables.Rule - if c.ip6_tables { - v6rules, err = c.getRules(rule.Chain, rule.Table, nftables.TableFamilyIPv6) + if c.ip6_tables && rule.Chain == definitions.ForwardChain { + v6rules, err := c.getRules(rule.Chain, rule.Table, nftables.TableFamilyIPv6) if err != nil { - return fmt.Errorf("%w. See http://containerlab.dev/manual/network/#external-access", err) + return fmt.Errorf("failed to get iptables rules for chain %s, table %s, family %s: %w", + rule.Chain, rule.Table, afMap[nftables.TableFamilyIPv6], err) } + allRules = append(allRules, v6rules...) } - allRules = append(allRules, v6rules...) + if c.ip6_docker && rule.Chain == definitions.DockerUserChain { + v6rules, err := c.getRules(rule.Chain, rule.Table, nftables.TableFamilyIPv6) + if err != nil { + return fmt.Errorf("failed to get iptables rules for chain %s, table %s, family %s: %w", + rule.Chain, rule.Table, afMap[nftables.TableFamilyIPv6], err) + } + allRules = append(allRules, v6rules...) + } clabRules := c.getClabRulesForInterface(iface, allRules) if len(clabRules) == 0 { @@ -129,11 +148,18 @@ func (c *NftablesClient) InstallForwardingRules(rule definitions.FirewallRule) e return err } - if c.ip6_tables { - err = c.InstallForwardingRulesForAF(nftables.TableFamilyIPv6, rule) + if !c.ip6_tables && rule.Chain == definitions.ForwardChain { + log.Debug("ip6_tables is not supported, skipping installation of ip6 forwarding rules") + return nil + } + + if !c.ip6_docker && rule.Chain == definitions.DockerUserChain { + log.Debug("ip6_tables is not supported by docker, skipping installation of ip6 forwarding rules") + return nil } - return err + // now it is safe to install the ip6 rules + return c.InstallForwardingRulesForAF(nftables.TableFamilyIPv6, rule) } // InstallForwardingRulesForAF installs the forwarding rules for the specified address family @@ -184,21 +210,20 @@ func (c *NftablesClient) InstallForwardingRulesForAF(af nftables.TableFamily, ru return nil } -// getChains returns all chains with the provided name and family. -func (nftC *NftablesClient) getChains(name string, family nftables.TableFamily) ([]*nftables.Chain, error) { - var result []*nftables.Chain - +// getChain returns a chain for the provided name, table name and family. +func (nftC *NftablesClient) getChain(name, table string, family nftables.TableFamily) (*nftables.Chain, error) { chains, err := nftC.nftConn.ListChainsOfTableFamily(family) if err != nil { return nil, err } for _, c := range chains { - if c.Name == name { - result = append(result, c) + if c.Name == name && c.Table.Name == table { + return c, nil } } - return result, nil + + return nil, nil } func (nftC *NftablesClient) deleteRule(r *nftables.Rule) error { @@ -207,44 +232,40 @@ func (nftC *NftablesClient) deleteRule(r *nftables.Rule) error { // getRules returns all rules for the provided chain name, table name and family. func (nftC *NftablesClient) getRules(chainName, tableName string, family nftables.TableFamily) ([]*nftables.Rule, error) { - // get chain reference - chains, err := nftC.getChains(chainName, family) + chain, err := nftC.getChain(chainName, tableName, family) if err != nil { return nil, err } - for _, c := range chains { - if c.Table.Name == tableName && c.Table.Family == family { - return nftC.nftConn.GetRules(c.Table, c) - } + if chain == nil { + return nil, fmt.Errorf("no match for chain %q, table %q with family %q found", chainName, tableName, family) } - return nil, fmt.Errorf("no match for chain %q, table %q with family %q found", chainName, tableName, family) + return nftC.nftConn.GetRules(chain.Table, chain) + } func (nftC *NftablesClient) newClabNftablesRule(chainName, tableName string, family nftables.TableFamily, position uint64, ) (*clabNftablesRule, error) { - chains, err := nftC.getChains(chainName, family) + chain, err := nftC.getChain(chainName, tableName, family) if err != nil { return nil, err } - for _, c := range chains { - if c.Table.Name == tableName && c.Table.Family == family { - r := &nftables.Rule{ - Handle: 0, - Position: position, - Table: c.Table, - Chain: c, - Exprs: []expr.Any{}, - } + if chain == nil { + return nil, errors.New("chain " + chainName + " not found in table " + tableName + " with family " + string(family)) + } - return &clabNftablesRule{rule: r}, nil - } + r := &nftables.Rule{ + Handle: 0, + Position: position, + Table: chain.Table, + Chain: chain, + Exprs: []expr.Any{}, } - return nil, errors.New("chain " + chainName + " not found in table " + tableName + " with family " + string(family)) + return &clabNftablesRule{rule: r}, nil } // InsertRule inserts a rule. From e6afc82baf3e20a1533192023c6b63f76b9317af Mon Sep 17 00:00:00 2001 From: Roman Dodin Date: Sat, 18 Jan 2025 22:17:47 +0200 Subject: [PATCH 2/2] added rn --- docs/rn/0.62.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/rn/0.62.md b/docs/rn/0.62.md index 7a1624ae2..86417423d 100644 --- a/docs/rn/0.62.md +++ b/docs/rn/0.62.md @@ -47,4 +47,8 @@ The Cisco IOL node type now supports the [`save`](../cmd/save.md) command that s * fix iptables handling for both mgmt network and bridge nodes. The rules are now set up in both directions #2406 +### 0.62.2 + +* do not attempt manipulate iptables rules in the DOCKER-USER chain if it does not exist #2410 + [^1]: You can add it for your platform of interest.