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