github.com/hernad/nomad@v1.6.112/drivers/shared/executor/executor_universal_linux.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package executor
     5  
     6  import (
     7  	"fmt"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"strconv"
    11  	"strings"
    12  	"syscall"
    13  
    14  	"github.com/containernetworking/plugins/pkg/ns"
    15  	"github.com/hernad/nomad/client/lib/cgutil"
    16  	"github.com/hernad/nomad/client/lib/resources"
    17  	"github.com/hernad/nomad/client/taskenv"
    18  	"github.com/hernad/nomad/helper/users"
    19  	"github.com/hernad/nomad/plugins/drivers"
    20  	"github.com/opencontainers/runc/libcontainer/configs"
    21  	"github.com/opencontainers/runc/libcontainer/specconv"
    22  )
    23  
    24  // setCmdUser takes a user id as a string and looks up the user, and sets the command
    25  // to execute as that user.
    26  func setCmdUser(cmd *exec.Cmd, userid string) error {
    27  	u, err := users.Lookup(userid)
    28  	if err != nil {
    29  		return fmt.Errorf("failed to identify user %v: %v", userid, err)
    30  	}
    31  
    32  	// Get the groups the user is a part of
    33  	gidStrings, err := u.GroupIds()
    34  	if err != nil {
    35  		return fmt.Errorf("unable to lookup user's group membership: %v", err)
    36  	}
    37  
    38  	gids := make([]uint32, len(gidStrings))
    39  	for _, gidString := range gidStrings {
    40  		u, err := strconv.ParseUint(gidString, 10, 32)
    41  		if err != nil {
    42  			return fmt.Errorf("unable to convert user's group to uint32 %s: %v", gidString, err)
    43  		}
    44  
    45  		gids = append(gids, uint32(u))
    46  	}
    47  
    48  	// Convert the uid and gid
    49  	uid, err := strconv.ParseUint(u.Uid, 10, 32)
    50  	if err != nil {
    51  		return fmt.Errorf("unable to convert userid to uint32: %s", err)
    52  	}
    53  	gid, err := strconv.ParseUint(u.Gid, 10, 32)
    54  	if err != nil {
    55  		return fmt.Errorf("unable to convert groupid to uint32: %s", err)
    56  	}
    57  
    58  	// Set the command to run as that user and group.
    59  	if cmd.SysProcAttr == nil {
    60  		cmd.SysProcAttr = &syscall.SysProcAttr{}
    61  	}
    62  	if cmd.SysProcAttr.Credential == nil {
    63  		cmd.SysProcAttr.Credential = &syscall.Credential{}
    64  	}
    65  	cmd.SysProcAttr.Credential.Uid = uint32(uid)
    66  	cmd.SysProcAttr.Credential.Gid = uint32(gid)
    67  	cmd.SysProcAttr.Credential.Groups = gids
    68  
    69  	return nil
    70  }
    71  
    72  // configureResourceContainer configured the cgroups to be used to track pids
    73  // created by the executor
    74  func (e *UniversalExecutor) configureResourceContainer(pid int) error {
    75  	cfg := &configs.Config{
    76  		Cgroups: &configs.Cgroup{
    77  			Resources: &configs.Resources{},
    78  		},
    79  	}
    80  
    81  	// note: this was always here, but not used until cgroups v2 support
    82  	for _, device := range specconv.AllowedDevices {
    83  		cfg.Cgroups.Resources.Devices = append(cfg.Cgroups.Resources.Devices, &device.Rule)
    84  	}
    85  
    86  	lookup := func(env []string, name string) (result string) {
    87  		for _, s := range env {
    88  			if strings.HasPrefix(s, name+"=") {
    89  				result = strings.TrimLeft(s, name+"=")
    90  				return
    91  			}
    92  		}
    93  		return
    94  	}
    95  
    96  	if cgutil.UseV2 {
    97  		// in v2 we have the definitive cgroup; create and enter it
    98  
    99  		// use the task environment variables for determining the cgroup path -
   100  		// not ideal but plumbing the values directly requires grpc protobuf changes
   101  		parent := lookup(e.commandCfg.Env, taskenv.CgroupParent)
   102  		allocID := lookup(e.commandCfg.Env, taskenv.AllocID)
   103  		task := lookup(e.commandCfg.Env, taskenv.TaskName)
   104  		if parent == "" || allocID == "" || task == "" {
   105  			return fmt.Errorf(
   106  				"environment variables %s must be set",
   107  				strings.Join([]string{taskenv.CgroupParent, taskenv.AllocID, taskenv.TaskName}, ","),
   108  			)
   109  		}
   110  		scope := cgutil.CgroupScope(allocID, task)
   111  		path := filepath.Join("/", cgutil.GetCgroupParent(parent), scope)
   112  		cfg.Cgroups.Path = path
   113  		e.containment = resources.Contain(e.logger, cfg.Cgroups)
   114  		return e.containment.Apply(pid)
   115  
   116  	} else {
   117  		// in v1 create a freezer cgroup for use by containment
   118  
   119  		if err := cgutil.ConfigureBasicCgroups(cfg); err != nil {
   120  			// Log this error to help diagnose cases where nomad is run with too few
   121  			// permissions, but don't return an error. There is no separate check for
   122  			// cgroup creation permissions, so this may be the happy path.
   123  			e.logger.Warn("failed to create cgroup",
   124  				"docs", "https://www.nomadproject.io/docs/drivers/raw_exec.html#no_cgroups",
   125  				"error", err)
   126  			return nil
   127  		}
   128  		path := cfg.Cgroups.Path
   129  		e.logger.Trace("cgroup created, now need to apply", "path", path)
   130  		e.containment = resources.Contain(e.logger, cfg.Cgroups)
   131  		return e.containment.Apply(pid)
   132  	}
   133  }
   134  
   135  func (e *UniversalExecutor) getAllPids() (resources.PIDs, error) {
   136  	if e.containment == nil {
   137  		return getAllPidsByScanning()
   138  	}
   139  	return e.containment.GetPIDs(), nil
   140  }
   141  
   142  // withNetworkIsolation calls the passed function the network namespace `spec`
   143  func withNetworkIsolation(f func() error, spec *drivers.NetworkIsolationSpec) error {
   144  	if spec != nil && spec.Path != "" {
   145  		// Get a handle to the target network namespace
   146  		netNS, err := ns.GetNS(spec.Path)
   147  		if err != nil {
   148  			return err
   149  		}
   150  
   151  		// Start the container in the network namespace
   152  		return netNS.Do(func(ns.NetNS) error {
   153  			return f()
   154  		})
   155  	}
   156  	return f()
   157  }