IsInsideUserNamespace checks if a process is already running in a user namespace and also returns if the process has permissions to use setgroups in this user namespace.
(pid int)
| 23 | // user namespace and also returns if the process has permissions to use |
| 24 | // setgroups in this user namespace. |
| 25 | func IsInsideUserNamespace(pid int) (bool, bool) { |
| 26 | // default values returned in case of error |
| 27 | insideUserNs := false |
| 28 | setgroupsAllowed := false |
| 29 | |
| 30 | // can fail if the kernel doesn't support user namespace |
| 31 | r, err := os.Open(fmt.Sprintf("/proc/%d/uid_map", pid)) |
| 32 | if err != nil { |
| 33 | return insideUserNs, setgroupsAllowed |
| 34 | } |
| 35 | defer r.Close() |
| 36 | |
| 37 | scanner := bufio.NewScanner(r) |
| 38 | // we are interested only by the first line of |
| 39 | // uid_map which would give us the answer quickly |
| 40 | // based on the value of size field |
| 41 | if scanner.Scan() { |
| 42 | fields := strings.Fields(scanner.Text()) |
| 43 | |
| 44 | // trust values returned by procfs |
| 45 | size, _ := strconv.ParseUint(fields[2], 10, 32) |
| 46 | |
| 47 | // a size of 4294967295 means the process is running |
| 48 | // in the host user namespace |
| 49 | if uint32(size) == ^uint32(0) { |
| 50 | return insideUserNs, setgroupsAllowed |
| 51 | } |
| 52 | |
| 53 | // process is running inside user namespace |
| 54 | insideUserNs = true |
| 55 | |
| 56 | // should not fail if open call passed |
| 57 | d, err := os.ReadFile(fmt.Sprintf("/proc/%d/setgroups", pid)) |
| 58 | if err != nil { |
| 59 | return insideUserNs, setgroupsAllowed |
| 60 | } |
| 61 | setgroupsAllowed = string(d) == "allow\n" |
| 62 | } |
| 63 | |
| 64 | return insideUserNs, setgroupsAllowed |
| 65 | } |
| 66 | |
| 67 | // HostUID attempts to find the original host UID if the current |
| 68 | // process is root running inside a user namespace, and if not it |
no test coverage detected