فهرست منبع

libnetwork: set IPv6 without reexec

unshare.Go() is not used as an existing network namespace needs to be
entered, not a new one created. Explicitly lock main() to the initial
thread so as not to depend on the side effects of importing the
internal/unshare package to achieve the same.

Signed-off-by: Cory Snider <csnider@mirantis.com>
Cory Snider 2 سال پیش
والد
کامیت
7037c48e58
1فایلهای تغییر یافته به همراه51 افزوده شده و 56 حذف شده
  1. 51 56
      libnetwork/osl/namespace_linux.go

+ 51 - 56
libnetwork/osl/namespace_linux.go

@@ -5,7 +5,6 @@ import (
 	"fmt"
 	"net"
 	"os"
-	"os/exec"
 	"path/filepath"
 	"runtime"
 	"strconv"
@@ -18,7 +17,6 @@ import (
 	"github.com/docker/docker/libnetwork/ns"
 	"github.com/docker/docker/libnetwork/osl/kernel"
 	"github.com/docker/docker/libnetwork/types"
-	"github.com/docker/docker/pkg/reexec"
 	"github.com/sirupsen/logrus"
 	"github.com/vishvananda/netlink"
 	"github.com/vishvananda/netns"
@@ -28,13 +26,11 @@ import (
 const defaultPrefix = "/var/run/docker"
 
 func init() {
-	reexec.Register("set-ipv6", reexecSetIPv6)
-
 	// Lock main() to the initial thread to exclude the goroutines spawned
-	// by func (*networkNamespace) InvokeFunc() from being scheduled onto
-	// that thread. Changes to the network namespace of the initial thread
-	// alter /proc/self/ns/net, which would break any code which
-	// (incorrectly) assumes that that file is a handle to the network
+	// by func (*networkNamespace) InvokeFunc() or func setIPv6() below from
+	// being scheduled onto that thread. Changes to the network namespace of
+	// the initial thread alter /proc/self/ns/net, which would break any
+	// code which (incorrectly) assumes that that file is the network
 	// namespace for the thread it is currently executing on.
 	runtime.LockOSThread()
 }
@@ -603,66 +599,65 @@ func (n *networkNamespace) checkLoV6() {
 	n.loV6Enabled = enable
 }
 
-func reexecSetIPv6() {
-	runtime.LockOSThread()
-	defer runtime.UnlockOSThread()
-
-	if len(os.Args) < 3 {
-		logrus.Errorf("invalid number of arguments for %s", os.Args[0])
-		os.Exit(1)
+func setIPv6(nspath, iface string, enable bool) error {
+	origns, err := netns.Get()
+	if err != nil {
+		return fmt.Errorf("failed to get current network namespace: %w", err)
 	}
+	defer origns.Close()
 
-	ns, err := netns.GetFromPath(os.Args[1])
+	ns, err := netns.GetFromPath(nspath)
 	if err != nil {
-		logrus.Errorf("failed get network namespace %q: %v", os.Args[1], err)
-		os.Exit(2)
+		return fmt.Errorf("failed get network namespace %q: %w", nspath, err)
 	}
 	defer ns.Close()
 
-	if err = netns.Set(ns); err != nil {
-		logrus.Errorf("setting into container netns %q failed: %v", os.Args[1], err)
-		os.Exit(3)
-	}
+	errCh := make(chan error, 1)
+	go func() {
+		defer close(errCh)
 
-	var (
-		action = "disable"
-		value  = byte('1')
-		path   = fmt.Sprintf("/proc/sys/net/ipv6/conf/%s/disable_ipv6", os.Args[2])
-	)
+		runtime.LockOSThread()
+		if err = netns.Set(ns); err != nil {
+			errCh <- fmt.Errorf("setting into container netns %q failed: %w", nspath, err)
+			return
+		}
+		defer func() {
+			if err := netns.Set(origns); err != nil {
+				logrus.WithError(err).Error("libnetwork: restoring thread network namespace failed")
+				// The error is only fatal for the current thread. Keep this
+				// goroutine locked to the thread to make the runtime replace it
+				// with a clean thread once this goroutine returns.
+			} else {
+				runtime.UnlockOSThread()
+			}
+		}()
 
-	if os.Args[3] == "true" {
-		action = "enable"
-		value = byte('0')
-	}
+		var (
+			action = "disable"
+			value  = byte('1')
+			path   = fmt.Sprintf("/proc/sys/net/ipv6/conf/%s/disable_ipv6", iface)
+		)
 
-	if _, err := os.Stat(path); err != nil {
-		if os.IsNotExist(err) {
-			logrus.Warnf("file does not exist: %s : %v Has IPv6 been disabled in this node's kernel?", path, err)
-			os.Exit(0)
+		if enable {
+			action = "enable"
+			value = '0'
 		}
-		logrus.Errorf("failed to stat %s : %v", path, err)
-		os.Exit(5)
-	}
 
-	if err = os.WriteFile(path, []byte{value, '\n'}, 0644); err != nil {
-		logrus.Errorf("failed to %s IPv6 forwarding for container's interface %s: %v", action, os.Args[2], err)
-		os.Exit(4)
-	}
-
-	os.Exit(0)
-}
+		if _, err := os.Stat(path); err != nil {
+			if os.IsNotExist(err) {
+				logrus.WithError(err).Warn("Cannot configure IPv6 forwarding on container interface. Has IPv6 been disabled in this node's kernel?")
+				return
+			}
+			errCh <- err
+			return
+		}
 
-func setIPv6(path, iface string, enable bool) error {
-	cmd := &exec.Cmd{
-		Path:   reexec.Self(),
-		Args:   append([]string{"set-ipv6"}, path, iface, strconv.FormatBool(enable)),
-		Stdout: os.Stdout,
-		Stderr: os.Stderr,
-	}
-	if err := cmd.Run(); err != nil {
-		return fmt.Errorf("reexec to set IPv6 failed: %v", err)
-	}
-	return nil
+		if err = os.WriteFile(path, []byte{value, '\n'}, 0o644); err != nil {
+			errCh <- fmt.Errorf("failed to %s IPv6 forwarding for container's interface %s: %w", action, iface, err)
+			return
+		}
+	}()
+	return <-errCh
 }
 
 // ApplyOSTweaks applies linux configs on the sandbox