Skip to content

Commit 4929dce

Browse files
authored
Merge pull request moby#50501 from robmry/nftables_interface
Rework the interface to libnet/internal/nftables
2 parents 42ecd90 + 785ae9a commit 4929dce

File tree

25 files changed

+1489
-866
lines changed

25 files changed

+1489
-866
lines changed

daemon/libnetwork/drivers/bridge/internal/nftabler/endpoint.go

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,24 @@ func (n *network) DelEndpoint(ctx context.Context, epIPv4, epIPv6 netip.Addr) er
2525

2626
func (n *network) modEndpoint(ctx context.Context, epIPv4, epIPv6 netip.Addr, enable bool) error {
2727
if n.fw.config.IPv4 && epIPv4.IsValid() {
28-
if err := n.filterDirectAccess(ctx, n.fw.table4, n.config.Config4, epIPv4, enable); err != nil {
29-
return err
28+
tm := nftables.Modifier{}
29+
updater := tm.Create
30+
if !enable {
31+
updater = tm.Delete
3032
}
31-
if err := nftApply(ctx, n.fw.table4); err != nil {
33+
n.filterDirectAccess(updater, nftables.IPv4, n.config.Config4, epIPv4)
34+
if err := n.fw.table4.Apply(ctx, tm); err != nil {
3235
return fmt.Errorf("adding rules for bridge %s: %w", n.config.IfName, err)
3336
}
3437
}
3538
if n.fw.config.IPv6 && epIPv6.IsValid() {
36-
if err := n.filterDirectAccess(ctx, n.fw.table6, n.config.Config6, epIPv6, enable); err != nil {
37-
return err
39+
tm := nftables.Modifier{}
40+
updater := tm.Create
41+
if !enable {
42+
updater = tm.Delete
3843
}
39-
if err := nftApply(ctx, n.fw.table6); err != nil {
44+
n.filterDirectAccess(updater, nftables.IPv6, n.config.Config6, epIPv6)
45+
if err := n.fw.table6.Apply(ctx, tm); err != nil {
4046
return fmt.Errorf("adding rules for bridge %s: %w", n.config.IfName, err)
4147
}
4248
}
@@ -53,17 +59,21 @@ func (n *network) modEndpoint(ctx context.Context, epIPv4, epIPv6 netip.Addr, en
5359
// kernel support).
5460
//
5561
// Packets originating on the bridge's own interface and addressed directly to the
56-
// container are allowed - the host always has direct access to its own containers
57-
// (it doesn't need to use the port mapped to its own addresses, although it can).
62+
// container are allowed - the host always has direct access to its own containers.
63+
// (It doesn't need to use the port mapped to its own addresses, although it can.)
5864
//
5965
// "Trusted interfaces" are treated in the same way as the bridge itself.
60-
func (n *network) filterDirectAccess(ctx context.Context, table nftables.TableRef, conf firewaller.NetworkConfigFam, epIP netip.Addr, enable bool) error {
66+
func (n *network) filterDirectAccess(updater func(nftables.Obj), fam nftables.Family, conf firewaller.NetworkConfigFam, epIP netip.Addr) {
6167
if n.config.Internal || conf.Unprotected || conf.Routed || n.fw.config.AllowDirectRouting {
62-
return nil
68+
return
6369
}
64-
updater := table.ChainUpdateFunc(ctx, rawPreroutingChain, enable)
6570
ifNames := strings.Join(n.config.TrustedHostInterfaces, ", ")
66-
return updater(ctx, rawPreroutingPortsRuleGroup,
67-
`%s daddr %s iifname != { %s, %s } counter drop comment "DROP DIRECT ACCESS"`,
68-
table.Family(), epIP, n.config.IfName, ifNames)
71+
updater(nftables.Rule{
72+
Chain: rawPreroutingChain,
73+
Group: rawPreroutingPortsRuleGroup,
74+
Rule: []string{
75+
string(fam), "daddr", epIP.String(),
76+
"iifname != {", n.config.IfName, ",", ifNames, `} counter drop comment "DROP DIRECT ACCESS"`,
77+
},
78+
})
6979
}

daemon/libnetwork/drivers/bridge/internal/nftabler/link.go

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import (
77
"errors"
88
"fmt"
99
"net/netip"
10+
"strconv"
1011

1112
"github.com/containerd/log"
13+
"github.com/moby/moby/v2/daemon/libnetwork/internal/nftables"
1214
"github.com/moby/moby/v2/daemon/libnetwork/types"
1315
)
1416

@@ -20,44 +22,47 @@ func (n *network) AddLink(ctx context.Context, parentIP, childIP netip.Addr, por
2022
return errors.New("cannot link to a container with an empty child IP address")
2123
}
2224

23-
chain := n.fw.table4.Chain(ctx, chainFilterFwdIn(n.config.IfName))
25+
tm := nftables.Modifier{}
2426
for _, port := range ports {
25-
for _, rule := range legacyLinkRules(parentIP, childIP, port) {
26-
if err := chain.AppendRule(ctx, fwdInLegacyLinksRuleGroup, rule); err != nil {
27-
return err
28-
}
29-
}
27+
updateLegacyLinkRules(tm.Create, chainFilterFwdIn(n.config.IfName), parentIP, childIP, port)
3028
}
31-
if err := nftApply(ctx, n.fw.table4); err != nil {
29+
if err := n.fw.table4.Apply(ctx, tm); err != nil {
3230
return fmt.Errorf("adding rules for bridge %s: %w", n.config.IfName, err)
3331
}
3432
return nil
3533
}
3634

3735
func (n *network) DelLink(ctx context.Context, parentIP, childIP netip.Addr, ports []types.TransportPort) {
38-
chain := n.fw.table4.Chain(ctx, chainFilterFwdIn(n.config.IfName))
36+
tm := nftables.Modifier{}
3937
for _, port := range ports {
40-
for _, rule := range legacyLinkRules(parentIP, childIP, port) {
41-
if err := chain.DeleteRule(ctx, fwdInLegacyLinksRuleGroup, rule); err != nil {
42-
log.G(ctx).WithFields(log.Fields{
43-
"rule": rule,
44-
"error": err,
45-
}).Warn("Failed to remove link between containers")
46-
}
47-
}
38+
updateLegacyLinkRules(tm.Delete, chainFilterFwdIn(n.config.IfName), parentIP, childIP, port)
4839
}
49-
if err := nftApply(ctx, n.fw.table4); err != nil {
40+
if err := n.fw.table4.Apply(ctx, tm); err != nil {
5041
log.G(ctx).WithError(err).Warn("Removing link, failed to update nftables")
5142
}
5243
}
5344

54-
func legacyLinkRules(parentIP, childIP netip.Addr, port types.TransportPort) []string {
45+
func updateLegacyLinkRules(updater func(command nftables.Obj), chainName string, parentIP, childIP netip.Addr, port types.TransportPort) {
5546
// TODO(robmry) - could combine rules for each proto by using an anonymous set.
56-
return []string{
57-
// Match the iptables implementation, but without checking iifname/oifname (not needed
58-
// because the addresses belong to the bridge).
59-
fmt.Sprintf("ip saddr %s ip daddr %s %s dport %d counter accept", parentIP.Unmap(), childIP.Unmap(), port.Proto, port.Port),
60-
// Conntrack will allow responses. So, this must be to allow unsolicited packets from an exposed port.
61-
fmt.Sprintf("ip daddr %s ip saddr %s %s sport %d counter accept", parentIP.Unmap(), childIP.Unmap(), port.Proto, port.Port),
62-
}
47+
// Match the iptables implementation, but without checking iifname/oifname (not needed
48+
// because the addresses belong to the bridge).
49+
updater(nftables.Rule{
50+
Chain: chainName,
51+
Group: fwdInLegacyLinksRuleGroup,
52+
Rule: []string{
53+
"ip saddr", parentIP.Unmap().String(),
54+
"ip daddr", childIP.Unmap().String(), port.Proto.String(), "dport", strconv.Itoa(int(port.Port)),
55+
"counter accept",
56+
},
57+
})
58+
// Conntrack will allow responses. So, this must be to allow unsolicited packets from an exposed port.
59+
updater(nftables.Rule{
60+
Chain: chainName,
61+
Group: fwdInLegacyLinksRuleGroup,
62+
Rule: []string{
63+
"ip saddr", childIP.Unmap().String(), port.Proto.String(), "sport", strconv.Itoa(int(port.Port)),
64+
"ip daddr", parentIP.Unmap().String(),
65+
"counter accept",
66+
},
67+
})
6368
}

0 commit comments

Comments
 (0)