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 }