Browse code

libnet/d/bridge/i/rlkclient: move to libnet/i/rlkclient

Signed-off-by: Albin Kerouanton <albinker@gmail.com>

Albin Kerouanton authored on 2025/07/21 22:53:21
Showing 4 changed files
... ...
@@ -21,10 +21,10 @@ import (
21 21
 	"github.com/docker/docker/daemon/libnetwork/drivers/bridge/internal/firewaller"
22 22
 	"github.com/docker/docker/daemon/libnetwork/drivers/bridge/internal/iptabler"
23 23
 	"github.com/docker/docker/daemon/libnetwork/drivers/bridge/internal/nftabler"
24
-	"github.com/docker/docker/daemon/libnetwork/drivers/bridge/internal/rlkclient"
25 24
 	"github.com/docker/docker/daemon/libnetwork/drvregistry"
26 25
 	"github.com/docker/docker/daemon/libnetwork/internal/netiputil"
27 26
 	"github.com/docker/docker/daemon/libnetwork/internal/nftables"
27
+	"github.com/docker/docker/daemon/libnetwork/internal/rlkclient"
28 28
 	"github.com/docker/docker/daemon/libnetwork/iptables"
29 29
 	"github.com/docker/docker/daemon/libnetwork/netlabel"
30 30
 	"github.com/docker/docker/daemon/libnetwork/netutils"
31 31
deleted file mode 100644
... ...
@@ -1,168 +0,0 @@
1
-// RootlessKit integration - if required by RootlessKit's port driver, let it know
2
-// about port mappings as they're added and removed.
3
-//
4
-// This is based on / copied from rootlesskit-docker-proxy, which was previously
5
-// installed as a proxy for docker-proxy:
6
-// https://github.com/rootless-containers/rootlesskit/blob/4fb2e2cb80bf13eb28b7f2a4317b63406b89ad32/cmd/rootlesskit-docker-proxy/main.go
7
-
8
-package rlkclient
9
-
10
-import (
11
-	"context"
12
-	"errors"
13
-	"fmt"
14
-	"net/netip"
15
-	"os"
16
-	"path/filepath"
17
-	"strings"
18
-
19
-	"github.com/rootless-containers/rootlesskit/v2/pkg/api/client"
20
-	"github.com/rootless-containers/rootlesskit/v2/pkg/port"
21
-)
22
-
23
-type PortDriverClient struct {
24
-	client         client.Client
25
-	portDriverName string
26
-	protos         map[string]struct{}
27
-	childIP        netip.Addr
28
-}
29
-
30
-func NewPortDriverClient(ctx context.Context) (*PortDriverClient, error) {
31
-	stateDir := os.Getenv("ROOTLESSKIT_STATE_DIR")
32
-	if stateDir == "" {
33
-		return nil, errors.New("$ROOTLESSKIT_STATE_DIR needs to be set")
34
-	}
35
-	socketPath := filepath.Join(stateDir, "api.sock")
36
-	c, err := client.New(socketPath)
37
-	if err != nil {
38
-		return nil, fmt.Errorf("error while connecting to RootlessKit API socket: %w", err)
39
-	}
40
-
41
-	info, err := c.Info(ctx)
42
-	if err != nil {
43
-		return nil, fmt.Errorf("failed to call info API, probably RootlessKit binary is too old (needs to be v0.14.0 or later): %w", err)
44
-	}
45
-
46
-	// info.PortDriver is currently nil for "none" and "implicit", but this may change in future
47
-	if info.PortDriver == nil || info.PortDriver.Driver == "none" || info.PortDriver.Driver == "implicit" {
48
-		return nil, nil
49
-	}
50
-
51
-	pdc := &PortDriverClient{
52
-		client:         c,
53
-		portDriverName: info.PortDriver.Driver,
54
-	}
55
-
56
-	if info.PortDriver.DisallowLoopbackChildIP {
57
-		// i.e., port-driver="slirp4netns"
58
-		if info.NetworkDriver.ChildIP == nil {
59
-			return nil, fmt.Errorf("RootlessKit port driver (%q) does not allow loopback child IP, but network driver (%q) has no non-loopback IP",
60
-				info.PortDriver.Driver, info.NetworkDriver.Driver)
61
-		}
62
-		childIP, ok := netip.AddrFromSlice(info.NetworkDriver.ChildIP)
63
-		if !ok {
64
-			return nil, fmt.Errorf("unable to use child IP %s from network driver (%q)",
65
-				info.NetworkDriver.ChildIP, info.NetworkDriver.Driver)
66
-		}
67
-		pdc.childIP = childIP
68
-	}
69
-
70
-	pdc.protos = make(map[string]struct{}, len(info.PortDriver.Protos))
71
-	for _, p := range info.PortDriver.Protos {
72
-		pdc.protos[p] = struct{}{}
73
-	}
74
-
75
-	return pdc, nil
76
-}
77
-
78
-// ChildHostIP returns the address that must be used in the child network
79
-// namespace in place of hostIP, a host IP address. In particular, port
80
-// mappings from host IP addresses, and DNAT rules, must use this child
81
-// address in place of the real host address.
82
-func (c *PortDriverClient) ChildHostIP(hostIP netip.Addr) netip.Addr {
83
-	if c == nil {
84
-		return hostIP
85
-	}
86
-	if c.childIP.IsValid() {
87
-		return c.childIP
88
-	}
89
-	if hostIP.Is6() {
90
-		return netip.IPv6Loopback()
91
-	}
92
-	return netip.MustParseAddr("127.0.0.1")
93
-}
94
-
95
-// ProtocolUnsupportedError is returned when apiProto is not supported by portDriverName.
96
-type ProtocolUnsupportedError struct {
97
-	apiProto       string
98
-	portDriverName string
99
-}
100
-
101
-func (e *ProtocolUnsupportedError) Error() string {
102
-	return fmt.Sprintf("protocol %q is not supported by the RootlessKit port driver %q",
103
-		e.apiProto, e.portDriverName)
104
-}
105
-
106
-// AddPort makes a request to RootlessKit asking it to set up a port
107
-// mapping between a host IP address and a child host IP address.
108
-//
109
-// AddPort may return [ProtocolUnsupportedError].
110
-func (c *PortDriverClient) AddPort(
111
-	ctx context.Context,
112
-	proto string,
113
-	hostIP netip.Addr,
114
-	childIP netip.Addr,
115
-	hostPort int,
116
-) (func() error, error) {
117
-	if c == nil {
118
-		return func() error { return nil }, nil
119
-	}
120
-	// proto is like "tcp", but we need to convert it to "tcp4" or "tcp6" explicitly
121
-	// for libnetwork >= 20201216
122
-	//
123
-	// See https://github.com/moby/libnetwork/pull/2604/files#diff-8fa48beed55dd033bf8e4f8c40b31cf69d0b2cc5d4bb53cde8594670ea6c938aR20
124
-	// See also https://github.com/rootless-containers/rootlesskit/issues/231
125
-	apiProto := proto
126
-	if !strings.HasSuffix(apiProto, "4") && !strings.HasSuffix(apiProto, "6") {
127
-		if hostIP.Is6() {
128
-			apiProto += "6"
129
-		} else {
130
-			apiProto += "4"
131
-		}
132
-	}
133
-
134
-	if _, ok := c.protos[apiProto]; !ok {
135
-		// This happens when apiProto="tcp6", portDriverName="slirp4netns",
136
-		// because "slirp4netns" port driver does not support listening on IPv6 yet.
137
-		//
138
-		// Note that "slirp4netns" port driver is not used by default,
139
-		// even when network driver is set to "slirp4netns".
140
-		//
141
-		// Most users are using "builtin" port driver and will not see this warning.
142
-		err := &ProtocolUnsupportedError{
143
-			apiProto:       apiProto,
144
-			portDriverName: c.portDriverName,
145
-		}
146
-		return nil, err
147
-	}
148
-
149
-	pm := c.client.PortManager()
150
-	p := port.Spec{
151
-		Proto:      apiProto,
152
-		ParentIP:   hostIP.String(),
153
-		ParentPort: hostPort,
154
-		ChildIP:    childIP.String(),
155
-		ChildPort:  hostPort,
156
-	}
157
-	st, err := pm.AddPort(ctx, p)
158
-	if err != nil {
159
-		return nil, fmt.Errorf("error while calling RootlessKit PortManager.AddPort(): %w", err)
160
-	}
161
-	deferFunc := func() error {
162
-		if dErr := pm.RemovePort(context.WithoutCancel(ctx), st.ID); dErr != nil {
163
-			return fmt.Errorf("error while calling RootlessKit PortManager.RemovePort(): %w", dErr)
164
-		}
165
-		return nil
166
-	}
167
-	return deferFunc, nil
168
-}
... ...
@@ -16,7 +16,7 @@ import (
16 16
 
17 17
 	"github.com/containerd/log"
18 18
 	"github.com/docker/docker/daemon/libnetwork/drivers/bridge/internal/firewaller"
19
-	"github.com/docker/docker/daemon/libnetwork/drivers/bridge/internal/rlkclient"
19
+	"github.com/docker/docker/daemon/libnetwork/internal/rlkclient"
20 20
 	"github.com/docker/docker/daemon/libnetwork/netutils"
21 21
 	"github.com/docker/docker/daemon/libnetwork/portallocator"
22 22
 	"github.com/docker/docker/daemon/libnetwork/portmapper"
23 23
new file mode 100644
... ...
@@ -0,0 +1,168 @@
0
+// RootlessKit integration - if required by RootlessKit's port driver, let it know
1
+// about port mappings as they're added and removed.
2
+//
3
+// This is based on / copied from rootlesskit-docker-proxy, which was previously
4
+// installed as a proxy for docker-proxy:
5
+// https://github.com/rootless-containers/rootlesskit/blob/4fb2e2cb80bf13eb28b7f2a4317b63406b89ad32/cmd/rootlesskit-docker-proxy/main.go
6
+
7
+package rlkclient
8
+
9
+import (
10
+	"context"
11
+	"errors"
12
+	"fmt"
13
+	"net/netip"
14
+	"os"
15
+	"path/filepath"
16
+	"strings"
17
+
18
+	"github.com/rootless-containers/rootlesskit/v2/pkg/api/client"
19
+	"github.com/rootless-containers/rootlesskit/v2/pkg/port"
20
+)
21
+
22
+type PortDriverClient struct {
23
+	client         client.Client
24
+	portDriverName string
25
+	protos         map[string]struct{}
26
+	childIP        netip.Addr
27
+}
28
+
29
+func NewPortDriverClient(ctx context.Context) (*PortDriverClient, error) {
30
+	stateDir := os.Getenv("ROOTLESSKIT_STATE_DIR")
31
+	if stateDir == "" {
32
+		return nil, errors.New("$ROOTLESSKIT_STATE_DIR needs to be set")
33
+	}
34
+	socketPath := filepath.Join(stateDir, "api.sock")
35
+	c, err := client.New(socketPath)
36
+	if err != nil {
37
+		return nil, fmt.Errorf("error while connecting to RootlessKit API socket: %w", err)
38
+	}
39
+
40
+	info, err := c.Info(ctx)
41
+	if err != nil {
42
+		return nil, fmt.Errorf("failed to call info API, probably RootlessKit binary is too old (needs to be v0.14.0 or later): %w", err)
43
+	}
44
+
45
+	// info.PortDriver is currently nil for "none" and "implicit", but this may change in future
46
+	if info.PortDriver == nil || info.PortDriver.Driver == "none" || info.PortDriver.Driver == "implicit" {
47
+		return nil, nil
48
+	}
49
+
50
+	pdc := &PortDriverClient{
51
+		client:         c,
52
+		portDriverName: info.PortDriver.Driver,
53
+	}
54
+
55
+	if info.PortDriver.DisallowLoopbackChildIP {
56
+		// i.e., port-driver="slirp4netns"
57
+		if info.NetworkDriver.ChildIP == nil {
58
+			return nil, fmt.Errorf("RootlessKit port driver (%q) does not allow loopback child IP, but network driver (%q) has no non-loopback IP",
59
+				info.PortDriver.Driver, info.NetworkDriver.Driver)
60
+		}
61
+		childIP, ok := netip.AddrFromSlice(info.NetworkDriver.ChildIP)
62
+		if !ok {
63
+			return nil, fmt.Errorf("unable to use child IP %s from network driver (%q)",
64
+				info.NetworkDriver.ChildIP, info.NetworkDriver.Driver)
65
+		}
66
+		pdc.childIP = childIP
67
+	}
68
+
69
+	pdc.protos = make(map[string]struct{}, len(info.PortDriver.Protos))
70
+	for _, p := range info.PortDriver.Protos {
71
+		pdc.protos[p] = struct{}{}
72
+	}
73
+
74
+	return pdc, nil
75
+}
76
+
77
+// ChildHostIP returns the address that must be used in the child network
78
+// namespace in place of hostIP, a host IP address. In particular, port
79
+// mappings from host IP addresses, and DNAT rules, must use this child
80
+// address in place of the real host address.
81
+func (c *PortDriverClient) ChildHostIP(hostIP netip.Addr) netip.Addr {
82
+	if c == nil {
83
+		return hostIP
84
+	}
85
+	if c.childIP.IsValid() {
86
+		return c.childIP
87
+	}
88
+	if hostIP.Is6() {
89
+		return netip.IPv6Loopback()
90
+	}
91
+	return netip.MustParseAddr("127.0.0.1")
92
+}
93
+
94
+// ProtocolUnsupportedError is returned when apiProto is not supported by portDriverName.
95
+type ProtocolUnsupportedError struct {
96
+	apiProto       string
97
+	portDriverName string
98
+}
99
+
100
+func (e *ProtocolUnsupportedError) Error() string {
101
+	return fmt.Sprintf("protocol %q is not supported by the RootlessKit port driver %q",
102
+		e.apiProto, e.portDriverName)
103
+}
104
+
105
+// AddPort makes a request to RootlessKit asking it to set up a port
106
+// mapping between a host IP address and a child host IP address.
107
+//
108
+// AddPort may return [ProtocolUnsupportedError].
109
+func (c *PortDriverClient) AddPort(
110
+	ctx context.Context,
111
+	proto string,
112
+	hostIP netip.Addr,
113
+	childIP netip.Addr,
114
+	hostPort int,
115
+) (func() error, error) {
116
+	if c == nil {
117
+		return func() error { return nil }, nil
118
+	}
119
+	// proto is like "tcp", but we need to convert it to "tcp4" or "tcp6" explicitly
120
+	// for libnetwork >= 20201216
121
+	//
122
+	// See https://github.com/moby/libnetwork/pull/2604/files#diff-8fa48beed55dd033bf8e4f8c40b31cf69d0b2cc5d4bb53cde8594670ea6c938aR20
123
+	// See also https://github.com/rootless-containers/rootlesskit/issues/231
124
+	apiProto := proto
125
+	if !strings.HasSuffix(apiProto, "4") && !strings.HasSuffix(apiProto, "6") {
126
+		if hostIP.Is6() {
127
+			apiProto += "6"
128
+		} else {
129
+			apiProto += "4"
130
+		}
131
+	}
132
+
133
+	if _, ok := c.protos[apiProto]; !ok {
134
+		// This happens when apiProto="tcp6", portDriverName="slirp4netns",
135
+		// because "slirp4netns" port driver does not support listening on IPv6 yet.
136
+		//
137
+		// Note that "slirp4netns" port driver is not used by default,
138
+		// even when network driver is set to "slirp4netns".
139
+		//
140
+		// Most users are using "builtin" port driver and will not see this warning.
141
+		err := &ProtocolUnsupportedError{
142
+			apiProto:       apiProto,
143
+			portDriverName: c.portDriverName,
144
+		}
145
+		return nil, err
146
+	}
147
+
148
+	pm := c.client.PortManager()
149
+	p := port.Spec{
150
+		Proto:      apiProto,
151
+		ParentIP:   hostIP.String(),
152
+		ParentPort: hostPort,
153
+		ChildIP:    childIP.String(),
154
+		ChildPort:  hostPort,
155
+	}
156
+	st, err := pm.AddPort(ctx, p)
157
+	if err != nil {
158
+		return nil, fmt.Errorf("error while calling RootlessKit PortManager.AddPort(): %w", err)
159
+	}
160
+	deferFunc := func() error {
161
+		if dErr := pm.RemovePort(context.WithoutCancel(ctx), st.ID); dErr != nil {
162
+			return fmt.Errorf("error while calling RootlessKit PortManager.RemovePort(): %w", dErr)
163
+		}
164
+		return nil
165
+	}
166
+	return deferFunc, nil
167
+}