github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/drivers/shared/executor/executor_universal_linux.go (about) 1 package executor 2 3 import ( 4 "fmt" 5 "os" 6 "os/user" 7 "strconv" 8 "syscall" 9 10 "github.com/containernetworking/plugins/pkg/ns" 11 multierror "github.com/hashicorp/go-multierror" 12 "github.com/hashicorp/nomad/helper" 13 "github.com/hashicorp/nomad/plugins/drivers" 14 "github.com/opencontainers/runc/libcontainer/cgroups" 15 cgroupFs "github.com/opencontainers/runc/libcontainer/cgroups/fs" 16 lconfigs "github.com/opencontainers/runc/libcontainer/configs" 17 ) 18 19 // runAs takes a user id as a string and looks up the user, and sets the command 20 // to execute as that user. 21 func (e *UniversalExecutor) runAs(userid string) error { 22 u, err := user.Lookup(userid) 23 if err != nil { 24 return fmt.Errorf("Failed to identify user %v: %v", userid, err) 25 } 26 27 // Get the groups the user is a part of 28 gidStrings, err := u.GroupIds() 29 if err != nil { 30 return fmt.Errorf("Unable to lookup user's group membership: %v", err) 31 } 32 33 gids := make([]uint32, len(gidStrings)) 34 for _, gidString := range gidStrings { 35 u, err := strconv.Atoi(gidString) 36 if err != nil { 37 return fmt.Errorf("Unable to convert user's group to int %s: %v", gidString, err) 38 } 39 40 gids = append(gids, uint32(u)) 41 } 42 43 // Convert the uid and gid 44 uid, err := strconv.ParseUint(u.Uid, 10, 32) 45 if err != nil { 46 return fmt.Errorf("Unable to convert userid to uint32: %s", err) 47 } 48 gid, err := strconv.ParseUint(u.Gid, 10, 32) 49 if err != nil { 50 return fmt.Errorf("Unable to convert groupid to uint32: %s", err) 51 } 52 53 // Set the command to run as that user and group. 54 if e.childCmd.SysProcAttr == nil { 55 e.childCmd.SysProcAttr = &syscall.SysProcAttr{} 56 } 57 if e.childCmd.SysProcAttr.Credential == nil { 58 e.childCmd.SysProcAttr.Credential = &syscall.Credential{} 59 } 60 e.childCmd.SysProcAttr.Credential.Uid = uint32(uid) 61 e.childCmd.SysProcAttr.Credential.Gid = uint32(gid) 62 e.childCmd.SysProcAttr.Credential.Groups = gids 63 64 e.logger.Debug("setting process user", "user", uid, "group", gid, "additional_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 := &lconfigs.Config{ 73 Cgroups: &lconfigs.Cgroup{ 74 Resources: &lconfigs.Resources{ 75 AllowAllDevices: helper.BoolToPtr(true), 76 }, 77 }, 78 } 79 80 err := configureBasicCgroups(cfg) 81 if err != nil { 82 // Log this error to help diagnose cases where nomad is run with too few 83 // permissions, but don't return an error. There is no separate check for 84 // cgroup creation permissions, so this may be the happy path. 85 e.logger.Warn("failed to create cgroup", 86 "docs", "https://www.nomadproject.io/docs/drivers/raw_exec.html#no_cgroups", 87 "error", err) 88 return nil 89 } 90 e.resConCtx.groups = cfg.Cgroups 91 return cgroups.EnterPid(cfg.Cgroups.Paths, pid) 92 } 93 94 func (e *UniversalExecutor) getAllPids() (map[int]*nomadPid, error) { 95 if e.resConCtx.isEmpty() { 96 return getAllPidsByScanning() 97 } else { 98 return e.resConCtx.getAllPidsByCgroup() 99 } 100 } 101 102 // DestroyCgroup kills all processes in the cgroup and removes the cgroup 103 // configuration from the host. This function is idempotent. 104 func DestroyCgroup(groups *lconfigs.Cgroup, executorPid int) error { 105 mErrs := new(multierror.Error) 106 if groups == nil { 107 return fmt.Errorf("Can't destroy: cgroup configuration empty") 108 } 109 110 // Move the executor into the global cgroup so that the task specific 111 // cgroup can be destroyed. 112 path, err := cgroups.GetInitCgroupPath("freezer") 113 if err != nil { 114 return err 115 } 116 117 if err := cgroups.EnterPid(map[string]string{"freezer": path}, executorPid); err != nil { 118 return err 119 } 120 121 // Freeze the Cgroup so that it can not continue to fork/exec. 122 groups.Resources.Freezer = lconfigs.Frozen 123 freezer := cgroupFs.FreezerGroup{} 124 if err := freezer.Set(groups.Paths[freezer.Name()], groups); err != nil { 125 return err 126 } 127 128 var procs []*os.Process 129 pids, err := cgroups.GetAllPids(groups.Paths[freezer.Name()]) 130 if err != nil { 131 multierror.Append(mErrs, fmt.Errorf("error getting pids: %v", err)) 132 133 // Unfreeze the cgroup. 134 groups.Resources.Freezer = lconfigs.Thawed 135 freezer := cgroupFs.FreezerGroup{} 136 if err := freezer.Set(groups.Paths[freezer.Name()], groups); err != nil { 137 multierror.Append(mErrs, fmt.Errorf("failed to unfreeze cgroup: %v", err)) 138 return mErrs.ErrorOrNil() 139 } 140 } 141 142 // Kill the processes in the cgroup 143 for _, pid := range pids { 144 proc, err := os.FindProcess(pid) 145 if err != nil { 146 multierror.Append(mErrs, fmt.Errorf("error finding process %v: %v", pid, err)) 147 continue 148 } 149 150 procs = append(procs, proc) 151 if e := proc.Kill(); e != nil { 152 multierror.Append(mErrs, fmt.Errorf("error killing process %v: %v", pid, e)) 153 } 154 } 155 156 // Unfreeze the cgroug so we can wait. 157 groups.Resources.Freezer = lconfigs.Thawed 158 if err := freezer.Set(groups.Paths[freezer.Name()], groups); err != nil { 159 multierror.Append(mErrs, fmt.Errorf("failed to unfreeze cgroup: %v", err)) 160 return mErrs.ErrorOrNil() 161 } 162 163 // Wait on the killed processes to ensure they are cleaned up. 164 for _, proc := range procs { 165 // Don't capture the error because we expect this to fail for 166 // processes we didn't fork. 167 proc.Wait() 168 } 169 170 // Remove the cgroup. 171 if err := cgroups.RemovePaths(groups.Paths); err != nil { 172 multierror.Append(mErrs, fmt.Errorf("failed to delete the cgroup directories: %v", err)) 173 } 174 return mErrs.ErrorOrNil() 175 } 176 177 // withNetworkIsolation calls the passed function the network namespace `spec` 178 func withNetworkIsolation(f func() error, spec *drivers.NetworkIsolationSpec) error { 179 if spec != nil && spec.Path != "" { 180 // Get a handle to the target network namespace 181 netns, err := ns.GetNS(spec.Path) 182 if err != nil { 183 return err 184 } 185 186 // Start the container in the network namespace 187 return netns.Do(func(ns.NetNS) error { 188 return f() 189 }) 190 } 191 192 return f() 193 }