Signed-off-by: Albin Kerouanton <albinker@gmail.com>
| ... | ... |
@@ -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 |
+} |