github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/worker/runtime/user_namespace.go (about) 1 package runtime 2 3 import ( 4 "bufio" 5 "fmt" 6 "io" 7 "os" 8 ) 9 10 const ( 11 uidMap = "/proc/self/uid_map" 12 gidMap = "/proc/self/gid_map" 13 ) 14 15 type userNamespace struct{} 16 17 func NewUserNamespace() UserNamespace { 18 return &userNamespace{} 19 } 20 21 func (s *userNamespace) MaxValidIds() (uint32, uint32, error) { 22 maxValidUid, err := maxValidFromFile(uidMap) 23 if err != nil { 24 return 0, 0, err 25 } 26 maxValidGid, err := maxValidFromFile(gidMap) 27 if err != nil { 28 return 0, 0, err 29 } 30 return maxValidUid, maxValidGid, nil 31 } 32 33 func maxValidFromFile(fname string) (uint32, error) { 34 f, err := os.Open(uidMap) 35 if err != nil { 36 return 0, fmt.Errorf("open %s: %w", uidMap, err) 37 } 38 defer f.Close() 39 40 return MaxValid(f) 41 } 42 43 // MaxValid computes what the highest possible id in a permission map is. 44 // 45 // For example, given the following mapping from /proc/self/uid_map: 46 // 47 // 0 1001 10 48 // | | | 49 // | | max number of ids inside this mapping (3) 50 // | id outside (usually, the host) (2) 51 // id inside the container (1) 52 // 53 // it determines that the maximum valid user id in this mapping is 9. 54 // 55 // 56 // More information about semantics of `uid_map` and `gid_map` can be found in 57 // [user_namespaces], but here's a summary assuming the processes reading the 58 // file is in the same usernamespace as `$pid`: 59 // 60 // - each line specifies a 1:1 mapping of a range of contiguous user/group IDs 61 // between two user namespaces 62 // 63 // - (1) is the start of the range of ids in the user namespace of the 64 // process $pid 65 // - (2) is the start of the range of ids to which the user IDs specified 66 // in (1) map to in the parent user namespace 67 // - (3) is the length of the range of user/group IDs that is mapped 68 // between the two user namespaces 69 // 70 // where: 71 // i. (1), (2), and (3) are uint32, with (3) having to be > 0 72 // ii. the max number of lines is eiter 5 (linux <= 4.14) or 350 (linux > 73 // 4.14) 74 // iii. range of ids in each line cannot overlap with the ranges in any 75 // other lines 76 // iv. at least one line must exist 77 // 78 // 79 // [user_namespaces]: http://man7.org/linux/man-pages/man7/user_namespaces.7.html 80 // 81 func MaxValid(r io.Reader) (uint32, error) { 82 scanner := bufio.NewScanner(r) 83 84 var ( 85 inside, outside, size uint32 86 val uint32 87 lines uint32 88 ) 89 90 for scanner.Scan() { 91 _, err := fmt.Sscanf( 92 scanner.Text(), 93 "%d %d %d", 94 &inside, &outside, &size, 95 ) 96 if err != nil { 97 return 0, fmt.Errorf("scanf: %w", err) 98 } 99 100 val = maxUint(val, inside+size-1) 101 lines++ 102 } 103 104 err := scanner.Err() 105 if err != nil { 106 return 0, fmt.Errorf("scanning: %w", err) 107 } 108 109 if lines == 0 { 110 return 0, fmt.Errorf("empty reader") 111 } 112 113 return val, nil 114 } 115 116 func maxUint(a, b uint32) uint32 { 117 if a > b { 118 return a 119 } 120 121 return b 122 }