| package configs |
| |
| import ( |
| "errors" |
| "fmt" |
| "math" |
| ) |
| |
| var ( |
| errNoUIDMap = errors.New("user namespaces enabled, but no uid mappings found") |
| errNoGIDMap = errors.New("user namespaces enabled, but no gid mappings found") |
| ) |
| |
| // Please check https://man7.org/linux/man-pages/man2/personality.2.html for const details. |
| // https://raw.githubusercontent.com/torvalds/linux/master/include/uapi/linux/personality.h |
| const ( |
| PerLinux = 0x0000 |
| PerLinux32 = 0x0008 |
| ) |
| |
| type LinuxPersonality struct { |
| // Domain for the personality |
| // can only contain values "LINUX" and "LINUX32" |
| Domain int `json:"domain"` |
| } |
| |
| // HostUID gets the translated uid for the process on host which could be |
| // different when user namespaces are enabled. |
| func (c Config) HostUID(containerId int) (int, error) { |
| if c.Namespaces.Contains(NEWUSER) { |
| if len(c.UIDMappings) == 0 { |
| return -1, errNoUIDMap |
| } |
| id, found := c.hostIDFromMapping(int64(containerId), c.UIDMappings) |
| if !found { |
| return -1, fmt.Errorf("user namespaces enabled, but no mapping found for uid %d", containerId) |
| } |
| // If we are a 32-bit binary running on a 64-bit system, it's possible |
| // the mapped user is too large to store in an int, which means we |
| // cannot do the mapping. We can't just return an int64, because |
| // os.Setuid() takes an int. |
| if id > math.MaxInt { |
| return -1, fmt.Errorf("mapping for uid %d (host id %d) is larger than native integer size (%d)", containerId, id, math.MaxInt) |
| } |
| return int(id), nil |
| } |
| // Return unchanged id. |
| return containerId, nil |
| } |
| |
| // HostRootUID gets the root uid for the process on host which could be non-zero |
| // when user namespaces are enabled. |
| func (c Config) HostRootUID() (int, error) { |
| return c.HostUID(0) |
| } |
| |
| // HostGID gets the translated gid for the process on host which could be |
| // different when user namespaces are enabled. |
| func (c Config) HostGID(containerId int) (int, error) { |
| if c.Namespaces.Contains(NEWUSER) { |
| if len(c.GIDMappings) == 0 { |
| return -1, errNoGIDMap |
| } |
| id, found := c.hostIDFromMapping(int64(containerId), c.GIDMappings) |
| if !found { |
| return -1, fmt.Errorf("user namespaces enabled, but no mapping found for gid %d", containerId) |
| } |
| // If we are a 32-bit binary running on a 64-bit system, it's possible |
| // the mapped user is too large to store in an int, which means we |
| // cannot do the mapping. We can't just return an int64, because |
| // os.Setgid() takes an int. |
| if id > math.MaxInt { |
| return -1, fmt.Errorf("mapping for gid %d (host id %d) is larger than native integer size (%d)", containerId, id, math.MaxInt) |
| } |
| return int(id), nil |
| } |
| // Return unchanged id. |
| return containerId, nil |
| } |
| |
| // HostRootGID gets the root gid for the process on host which could be non-zero |
| // when user namespaces are enabled. |
| func (c Config) HostRootGID() (int, error) { |
| return c.HostGID(0) |
| } |
| |
| // Utility function that gets a host ID for a container ID from user namespace map |
| // if that ID is present in the map. |
| func (c Config) hostIDFromMapping(containerID int64, uMap []IDMap) (int64, bool) { |
| for _, m := range uMap { |
| if (containerID >= m.ContainerID) && (containerID <= (m.ContainerID + m.Size - 1)) { |
| hostID := m.HostID + (containerID - m.ContainerID) |
| return hostID, true |
| } |
| } |
| return -1, false |
| } |