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  }