github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/cmd/rootlessport/main.go (about)

     1  //go:build linux
     2  // +build linux
     3  
     4  package main
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"net"
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  	"strings"
    17  
    18  	"github.com/containernetworking/plugins/pkg/ns"
    19  	"github.com/containers/common/libnetwork/types"
    20  	"github.com/hanks177/podman/v4/pkg/rootlessport"
    21  	"github.com/pkg/errors"
    22  	rkport "github.com/rootless-containers/rootlesskit/pkg/port"
    23  	rkbuiltin "github.com/rootless-containers/rootlesskit/pkg/port/builtin"
    24  	rkportutil "github.com/rootless-containers/rootlesskit/pkg/port/portutil"
    25  	"github.com/sirupsen/logrus"
    26  	"golang.org/x/sys/unix"
    27  )
    28  
    29  const (
    30  	// ReexecChildKey is used internally for the second reexec
    31  	ReexecChildKey       = "rootlessport-child"
    32  	reexecChildEnvOpaque = "_CONTAINERS_ROOTLESSPORT_CHILD_OPAQUE"
    33  )
    34  
    35  func main() {
    36  	if len(os.Args) > 1 {
    37  		fmt.Fprintln(os.Stderr, `too many arguments, rootlessport expects a json config via STDIN`)
    38  		os.Exit(1)
    39  	}
    40  	var err error
    41  	if os.Args[0] == ReexecChildKey {
    42  		err = child()
    43  	} else {
    44  		err = parent()
    45  	}
    46  	if err != nil {
    47  		fmt.Println(err)
    48  		os.Exit(1)
    49  	}
    50  }
    51  
    52  func loadConfig(r io.Reader) (*rootlessport.Config, io.ReadCloser, io.WriteCloser, error) {
    53  	stdin, err := ioutil.ReadAll(r)
    54  	if err != nil {
    55  		return nil, nil, nil, err
    56  	}
    57  	var cfg rootlessport.Config
    58  	if err := json.Unmarshal(stdin, &cfg); err != nil {
    59  		return nil, nil, nil, err
    60  	}
    61  	if cfg.NetNSPath == "" {
    62  		return nil, nil, nil, errors.New("missing NetNSPath")
    63  	}
    64  	if cfg.ExitFD <= 0 {
    65  		return nil, nil, nil, errors.New("missing ExitFD")
    66  	}
    67  	exitFile := os.NewFile(uintptr(cfg.ExitFD), "exitfile")
    68  	if exitFile == nil {
    69  		return nil, nil, nil, errors.New("invalid ExitFD")
    70  	}
    71  	if cfg.ReadyFD <= 0 {
    72  		return nil, nil, nil, errors.New("missing ReadyFD")
    73  	}
    74  	readyFile := os.NewFile(uintptr(cfg.ReadyFD), "readyfile")
    75  	if readyFile == nil {
    76  		return nil, nil, nil, errors.New("invalid ReadyFD")
    77  	}
    78  	return &cfg, exitFile, readyFile, nil
    79  }
    80  
    81  func parent() error {
    82  	// load config from stdin
    83  	cfg, exitR, readyW, err := loadConfig(os.Stdin)
    84  	if err != nil {
    85  		return err
    86  	}
    87  
    88  	socketDir := filepath.Join(cfg.TmpDir, "rp")
    89  	err = os.MkdirAll(socketDir, 0700)
    90  	if err != nil {
    91  		return err
    92  	}
    93  
    94  	// create the parent driver
    95  	stateDir, err := ioutil.TempDir(cfg.TmpDir, "rootlessport")
    96  	if err != nil {
    97  		return err
    98  	}
    99  	defer os.RemoveAll(stateDir)
   100  	driver, err := rkbuiltin.NewParentDriver(&logrusWriter{prefix: "parent: "}, stateDir)
   101  	if err != nil {
   102  		return err
   103  	}
   104  	initComplete := make(chan struct{})
   105  	quit := make(chan struct{})
   106  	errCh := make(chan error)
   107  	// start the parent driver. initComplete will be closed when the child connected to the parent.
   108  	logrus.Infof("Starting parent driver")
   109  	go func() {
   110  		driverErr := driver.RunParentDriver(initComplete, quit, nil)
   111  		if driverErr != nil {
   112  			logrus.WithError(driverErr).Warn("Parent driver exited")
   113  		}
   114  		errCh <- driverErr
   115  		close(errCh)
   116  	}()
   117  	opaque := driver.OpaqueForChild()
   118  	logrus.Infof("opaque=%+v", opaque)
   119  	opaqueJSON, err := json.Marshal(opaque)
   120  	if err != nil {
   121  		return err
   122  	}
   123  	childQuitR, childQuitW, err := os.Pipe()
   124  	if err != nil {
   125  		return err
   126  	}
   127  	defer func() {
   128  		// stop the child
   129  		logrus.Info("Stopping child driver")
   130  		if err := childQuitW.Close(); err != nil {
   131  			logrus.WithError(err).Warn("Unable to close childQuitW")
   132  		}
   133  	}()
   134  
   135  	// reexec the child process in the child netns
   136  	cmd := exec.Command("/proc/self/exe")
   137  	cmd.Args = []string{ReexecChildKey}
   138  	cmd.Stdin = childQuitR
   139  	cmd.Stdout = &logrusWriter{prefix: "child"}
   140  	cmd.Stderr = cmd.Stdout
   141  	cmd.Env = append(os.Environ(), reexecChildEnvOpaque+"="+string(opaqueJSON))
   142  	childNS, err := ns.GetNS(cfg.NetNSPath)
   143  	if err != nil {
   144  		return err
   145  	}
   146  	if err := childNS.Do(func(_ ns.NetNS) error {
   147  		logrus.Infof("Starting child driver in child netns (%q %v)", cmd.Path, cmd.Args)
   148  		return cmd.Start()
   149  	}); err != nil {
   150  		return err
   151  	}
   152  
   153  	childErrCh := make(chan error)
   154  	go func() {
   155  		err := cmd.Wait()
   156  		childErrCh <- err
   157  		close(childErrCh)
   158  	}()
   159  
   160  	defer func() {
   161  		if err := unix.Kill(cmd.Process.Pid, unix.SIGTERM); err != nil {
   162  			logrus.WithError(err).Warn("Kill child process")
   163  		}
   164  	}()
   165  
   166  	logrus.Info("Waiting for initComplete")
   167  	// wait for the child to connect to the parent
   168  outer:
   169  	for {
   170  		select {
   171  		case <-initComplete:
   172  			logrus.Infof("initComplete is closed; parent and child established the communication channel")
   173  			break outer
   174  		case err := <-childErrCh:
   175  			if err != nil {
   176  				return err
   177  			}
   178  		case err := <-errCh:
   179  			if err != nil {
   180  				return err
   181  			}
   182  		}
   183  	}
   184  
   185  	defer func() {
   186  		logrus.Info("Stopping parent driver")
   187  		quit <- struct{}{}
   188  		if err := <-errCh; err != nil {
   189  			logrus.WithError(err).Warn("Parent driver returned error on exit")
   190  		}
   191  	}()
   192  
   193  	// let parent expose ports
   194  	logrus.Infof("Exposing ports %v", cfg.Mappings)
   195  	if err := exposePorts(driver, cfg.Mappings, cfg.ChildIP); err != nil {
   196  		return err
   197  	}
   198  
   199  	// we only need to have a socket to reload ports when we run under rootless cni
   200  	if cfg.RootlessCNI {
   201  		socketfile := filepath.Join(socketDir, cfg.ContainerID)
   202  		// make sure to remove the file if it exists to prevent EADDRINUSE
   203  		_ = os.Remove(socketfile)
   204  		// workaround to bypass the 108 char socket path limit
   205  		// open the fd and use the path to the fd as bind argument
   206  		fd, err := unix.Open(socketDir, unix.O_PATH, 0)
   207  		if err != nil {
   208  			return err
   209  		}
   210  		socket, err := net.ListenUnix("unixpacket", &net.UnixAddr{Name: fmt.Sprintf("/proc/self/fd/%d/%s", fd, cfg.ContainerID), Net: "unixpacket"})
   211  		if err != nil {
   212  			return err
   213  		}
   214  		err = unix.Close(fd)
   215  		// remove the socket file on exit
   216  		defer os.Remove(socketfile)
   217  		if err != nil {
   218  			logrus.Warnf("Failed to close the socketDir fd: %v", err)
   219  		}
   220  		defer socket.Close()
   221  		go serve(socket, driver)
   222  	}
   223  
   224  	logrus.Info("Ready")
   225  
   226  	// https://github.com/containers/podman/issues/11248
   227  	// Copy /dev/null to stdout and stderr to prevent SIGPIPE errors
   228  	if f, err := os.OpenFile("/dev/null", os.O_WRONLY, 0755); err == nil {
   229  		unix.Dup2(int(f.Fd()), 1) // nolint:errcheck
   230  		unix.Dup2(int(f.Fd()), 2) // nolint:errcheck
   231  		f.Close()
   232  	}
   233  	// write and close ReadyFD (convention is same as slirp4netns --ready-fd)
   234  	if _, err := readyW.Write([]byte("1")); err != nil {
   235  		return err
   236  	}
   237  	if err := readyW.Close(); err != nil {
   238  		return err
   239  	}
   240  
   241  	// wait for ExitFD to be closed
   242  	logrus.Info("Waiting for exitfd to be closed")
   243  	if _, err := ioutil.ReadAll(exitR); err != nil {
   244  		return err
   245  	}
   246  	return nil
   247  }
   248  
   249  func serve(listener net.Listener, pm rkport.Manager) {
   250  	for {
   251  		conn, err := listener.Accept()
   252  		if err != nil {
   253  			// we cannot log this error, stderr is already closed
   254  			continue
   255  		}
   256  		ctx := context.TODO()
   257  		err = handler(ctx, conn, pm)
   258  		if err != nil {
   259  			_, _ = conn.Write([]byte(err.Error()))
   260  		} else {
   261  			_, _ = conn.Write([]byte("OK"))
   262  		}
   263  		conn.Close()
   264  	}
   265  }
   266  
   267  func handler(ctx context.Context, conn io.Reader, pm rkport.Manager) error {
   268  	var childIP string
   269  	dec := json.NewDecoder(conn)
   270  	err := dec.Decode(&childIP)
   271  	if err != nil {
   272  		return errors.Wrap(err, "rootless port failed to decode ports")
   273  	}
   274  	portStatus, err := pm.ListPorts(ctx)
   275  	if err != nil {
   276  		return errors.Wrap(err, "rootless port failed to list ports")
   277  	}
   278  	for _, status := range portStatus {
   279  		err = pm.RemovePort(ctx, status.ID)
   280  		if err != nil {
   281  			return errors.Wrap(err, "rootless port failed to remove port")
   282  		}
   283  	}
   284  	// add the ports with the new child IP
   285  	for _, status := range portStatus {
   286  		// set the new child IP
   287  		status.Spec.ChildIP = childIP
   288  		_, err = pm.AddPort(ctx, status.Spec)
   289  		if err != nil {
   290  			return errors.Wrap(err, "rootless port failed to add port")
   291  		}
   292  	}
   293  	return nil
   294  }
   295  
   296  func exposePorts(pm rkport.Manager, portMappings []types.PortMapping, childIP string) error {
   297  	ctx := context.TODO()
   298  	for _, port := range portMappings {
   299  		protocols := strings.Split(port.Protocol, ",")
   300  		for _, protocol := range protocols {
   301  			hostIP := port.HostIP
   302  			if hostIP == "" {
   303  				hostIP = "0.0.0.0"
   304  			}
   305  			for i := uint16(0); i < port.Range; i++ {
   306  				spec := rkport.Spec{
   307  					Proto:      protocol,
   308  					ParentIP:   hostIP,
   309  					ParentPort: int(port.HostPort + i),
   310  					ChildPort:  int(port.ContainerPort + i),
   311  					ChildIP:    childIP,
   312  				}
   313  
   314  				for _, spec = range splitDualStackSpecIfWsl(spec) {
   315  					if err := validateAndAddPort(ctx, pm, spec); err != nil {
   316  						return err
   317  					}
   318  				}
   319  			}
   320  		}
   321  	}
   322  	return nil
   323  }
   324  
   325  func validateAndAddPort(ctx context.Context, pm rkport.Manager, spec rkport.Spec) error {
   326  	if err := rkportutil.ValidatePortSpec(spec, nil); err != nil {
   327  		return err
   328  	}
   329  	if _, err := pm.AddPort(ctx, spec); err != nil {
   330  		return err
   331  	}
   332  
   333  	return nil
   334  }
   335  
   336  func child() error {
   337  	// load the config from the parent
   338  	var opaque map[string]string
   339  	if err := json.Unmarshal([]byte(os.Getenv(reexecChildEnvOpaque)), &opaque); err != nil {
   340  		return err
   341  	}
   342  
   343  	// start the child driver
   344  	quit := make(chan struct{})
   345  	errCh := make(chan error)
   346  	go func() {
   347  		d := rkbuiltin.NewChildDriver(os.Stderr)
   348  		dErr := d.RunChildDriver(opaque, quit)
   349  		errCh <- dErr
   350  	}()
   351  	defer func() {
   352  		logrus.Info("Stopping child driver")
   353  		quit <- struct{}{}
   354  		if err := <-errCh; err != nil {
   355  			logrus.WithError(err).Warn("Child driver returned error on exit")
   356  		}
   357  	}()
   358  
   359  	// wait for stdin to be closed
   360  	if _, err := ioutil.ReadAll(os.Stdin); err != nil {
   361  		return err
   362  	}
   363  	return nil
   364  }
   365  
   366  type logrusWriter struct {
   367  	prefix string
   368  }
   369  
   370  func (w *logrusWriter) Write(p []byte) (int, error) {
   371  	logrus.Infof("%s%s", w.prefix, string(p))
   372  	return len(p), nil
   373  }