github.com/containerd/nerdctl@v1.7.7/pkg/rootlessutil/parent_linux.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package rootlessutil
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"os"
    23  	"os/exec"
    24  	"path/filepath"
    25  	"strconv"
    26  	"strings"
    27  	"syscall"
    28  
    29  	"github.com/containerd/log"
    30  )
    31  
    32  func IsRootlessParent() bool {
    33  	return os.Geteuid() != 0
    34  }
    35  
    36  func RootlessKitStateDir() (string, error) {
    37  	if v := os.Getenv("ROOTLESSKIT_STATE_DIR"); v != "" {
    38  		return v, nil
    39  	}
    40  	xdr, err := XDGRuntimeDir()
    41  	if err != nil {
    42  		return "", err
    43  	}
    44  	// "${XDG_RUNTIME_DIR}/containerd-rootless" is hardcoded in containerd-rootless.sh
    45  	stateDir := filepath.Join(xdr, "containerd-rootless")
    46  	if _, err := os.Stat(stateDir); err != nil {
    47  		return "", err
    48  	}
    49  	return stateDir, nil
    50  }
    51  
    52  func RootlessKitChildPid(stateDir string) (int, error) {
    53  	pidFilePath := filepath.Join(stateDir, "child_pid")
    54  	if _, err := os.Stat(pidFilePath); err != nil {
    55  		return 0, err
    56  	}
    57  
    58  	pidFileBytes, err := os.ReadFile(pidFilePath)
    59  	if err != nil {
    60  		return 0, err
    61  	}
    62  	pidStr := strings.TrimSpace(string(pidFileBytes))
    63  	return strconv.Atoi(pidStr)
    64  }
    65  
    66  func ParentMain(hostGatewayIP string) error {
    67  	if !IsRootlessParent() {
    68  		return errors.New("should not be called when !IsRootlessParent()")
    69  	}
    70  	stateDir, err := RootlessKitStateDir()
    71  	log.L.Debugf("stateDir: %s", stateDir)
    72  	if err != nil {
    73  		return fmt.Errorf("rootless containerd not running? (hint: use `containerd-rootless-setuptool.sh install` to start rootless containerd): %w", err)
    74  	}
    75  	childPid, err := RootlessKitChildPid(stateDir)
    76  	if err != nil {
    77  		return err
    78  	}
    79  
    80  	wd, err := os.Getwd()
    81  	if err != nil {
    82  		return err
    83  	}
    84  
    85  	// FIXME: remove dependency on `nsenter` binary
    86  	arg0, err := exec.LookPath("nsenter")
    87  	if err != nil {
    88  		return err
    89  	}
    90  	// args are compatible with both util-linux nsenter and busybox nsenter
    91  	args := []string{
    92  		"-r/",     // root dir (busybox nsenter wants this to be explicitly specified),
    93  		"-w" + wd, // work dir
    94  		"--preserve-credentials",
    95  		"-m", "-n", "-U",
    96  		"-t", strconv.Itoa(childPid),
    97  		"-F", // no fork
    98  	}
    99  	args = append(args, os.Args...)
   100  	log.L.Debugf("rootless parent main: executing %q with %v", arg0, args)
   101  
   102  	// Env vars corresponds to RootlessKit spec:
   103  	// https://github.com/rootless-containers/rootlesskit/tree/v0.13.1#environment-variables
   104  	os.Setenv("ROOTLESSKIT_STATE_DIR", stateDir)
   105  	os.Setenv("ROOTLESSKIT_PARENT_EUID", strconv.Itoa(os.Geteuid()))
   106  	os.Setenv("ROOTLESSKIT_PARENT_EGID", strconv.Itoa(os.Getegid()))
   107  	os.Setenv("NERDCTL_HOST_GATEWAY_IP", hostGatewayIP)
   108  	return syscall.Exec(arg0, args, os.Environ())
   109  }