github.com/toplink-cn/moby@v0.0.0-20240305205811-460b4aebdf81/libnetwork/sandbox_externalkey_unix.go (about)

     1  //go:build linux || freebsd
     2  
     3  package libnetwork
     4  
     5  import (
     6  	"context"
     7  	"encoding/json"
     8  	"flag"
     9  	"fmt"
    10  	"io"
    11  	"net"
    12  	"os"
    13  	"path/filepath"
    14  
    15  	"github.com/containerd/log"
    16  	"github.com/docker/docker/libnetwork/types"
    17  	"github.com/docker/docker/pkg/reexec"
    18  	"github.com/docker/docker/pkg/stringid"
    19  	"github.com/opencontainers/runtime-spec/specs-go"
    20  )
    21  
    22  const (
    23  	execSubdir      = "libnetwork"
    24  	defaultExecRoot = "/run/docker"
    25  	success         = "success"
    26  )
    27  
    28  func init() {
    29  	// TODO(thaJeztah): should this actually be registered on FreeBSD, or only on Linux?
    30  	reexec.Register("libnetwork-setkey", processSetKeyReexec)
    31  }
    32  
    33  type setKeyData struct {
    34  	ContainerID string
    35  	Key         string
    36  }
    37  
    38  // processSetKeyReexec is a private function that must be called only on an reexec path
    39  // It expects 3 args { [0] = "libnetwork-setkey", [1] = <container-id>, [2] = <short-controller-id> }
    40  // It also expects specs.State as a json string in <stdin>
    41  // Refer to https://github.com/opencontainers/runc/pull/160/ for more information
    42  // The docker exec-root can be specified as "-exec-root" flag. The default value is "/run/docker".
    43  func processSetKeyReexec() {
    44  	if err := setKey(); err != nil {
    45  		_, _ = fmt.Fprintln(os.Stderr, err)
    46  		os.Exit(1)
    47  	}
    48  }
    49  
    50  func setKey() error {
    51  	execRoot := flag.String("exec-root", defaultExecRoot, "docker exec root")
    52  	flag.Parse()
    53  
    54  	// expecting 3 os.Args {[0]="libnetwork-setkey", [1]=<container-id>, [2]=<short-controller-id> }
    55  	// (i.e. expecting 2 flag.Args())
    56  	args := flag.Args()
    57  	if len(args) < 2 {
    58  		return fmt.Errorf("re-exec expects 2 args (after parsing flags), received : %d", len(args))
    59  	}
    60  	containerID, shortCtlrID := args[0], args[1]
    61  
    62  	// We expect specs.State as a json string in <stdin>
    63  	var state specs.State
    64  	if err := json.NewDecoder(os.Stdin).Decode(&state); err != nil {
    65  		return err
    66  	}
    67  
    68  	return setExternalKey(shortCtlrID, containerID, fmt.Sprintf("/proc/%d/ns/net", state.Pid), *execRoot)
    69  }
    70  
    71  // setExternalKey provides a convenient way to set an External key to a sandbox
    72  func setExternalKey(shortCtlrID string, containerID string, key string, execRoot string) error {
    73  	uds := filepath.Join(execRoot, execSubdir, shortCtlrID+".sock")
    74  	c, err := net.Dial("unix", uds)
    75  	if err != nil {
    76  		return err
    77  	}
    78  	defer c.Close()
    79  
    80  	err = json.NewEncoder(c).Encode(setKeyData{
    81  		ContainerID: containerID,
    82  		Key:         key,
    83  	})
    84  	if err != nil {
    85  		return fmt.Errorf("sendKey failed with : %v", err)
    86  	}
    87  	return processReturn(c)
    88  }
    89  
    90  func processReturn(r io.Reader) error {
    91  	buf := make([]byte, 1024)
    92  	n, err := r.Read(buf[:])
    93  	if err != nil {
    94  		return fmt.Errorf("failed to read buf in processReturn : %v", err)
    95  	}
    96  	if string(buf[0:n]) != success {
    97  		return fmt.Errorf(string(buf[0:n]))
    98  	}
    99  	return nil
   100  }
   101  
   102  func (c *Controller) startExternalKeyListener() error {
   103  	execRoot := defaultExecRoot
   104  	if v := c.Config().ExecRoot; v != "" {
   105  		execRoot = v
   106  	}
   107  	udsBase := filepath.Join(execRoot, execSubdir)
   108  	if err := os.MkdirAll(udsBase, 0o600); err != nil {
   109  		return err
   110  	}
   111  	shortCtlrID := stringid.TruncateID(c.id)
   112  	uds := filepath.Join(udsBase, shortCtlrID+".sock")
   113  	l, err := net.Listen("unix", uds)
   114  	if err != nil {
   115  		return err
   116  	}
   117  	if err := os.Chmod(uds, 0o600); err != nil {
   118  		l.Close()
   119  		return err
   120  	}
   121  	c.mu.Lock()
   122  	c.extKeyListener = l
   123  	c.mu.Unlock()
   124  
   125  	go c.acceptClientConnections(uds, l)
   126  	return nil
   127  }
   128  
   129  func (c *Controller) acceptClientConnections(sock string, l net.Listener) {
   130  	for {
   131  		conn, err := l.Accept()
   132  		if err != nil {
   133  			if _, err1 := os.Stat(sock); os.IsNotExist(err1) {
   134  				// This happens when the socket is closed by the daemon, eg. during shutdown.
   135  				log.G(context.TODO()).Debugf("Unix socket %s was closed. The external key listener will stop.", sock)
   136  				return
   137  			}
   138  			log.G(context.TODO()).Errorf("Error accepting connection %v", err)
   139  			continue
   140  		}
   141  		go func() {
   142  			defer conn.Close()
   143  
   144  			err := c.processExternalKey(conn)
   145  			ret := success
   146  			if err != nil {
   147  				ret = err.Error()
   148  			}
   149  
   150  			_, err = conn.Write([]byte(ret))
   151  			if err != nil {
   152  				log.G(context.TODO()).Errorf("Error returning to the client %v", err)
   153  			}
   154  		}()
   155  	}
   156  }
   157  
   158  func (c *Controller) processExternalKey(conn net.Conn) error {
   159  	buf := make([]byte, 1280)
   160  	nr, err := conn.Read(buf)
   161  	if err != nil {
   162  		return err
   163  	}
   164  	var s setKeyData
   165  	if err = json.Unmarshal(buf[0:nr], &s); err != nil {
   166  		return err
   167  	}
   168  	sb, err := c.GetSandbox(s.ContainerID)
   169  	if err != nil {
   170  		return types.InvalidParameterErrorf("failed to get sandbox for %s", s.ContainerID)
   171  	}
   172  	return sb.SetKey(s.Key)
   173  }
   174  
   175  func (c *Controller) stopExternalKeyListener() {
   176  	c.extKeyListener.Close()
   177  }