github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/configs/validate/rootless.go (about)

     1  package validate
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/opencontainers/runc/libcontainer/configs"
    10  )
    11  
    12  // rootlessEUIDCheck makes sure that the config can be applied when runc
    13  // is being executed as a non-root user (euid != 0) in the current user namespace.
    14  func rootlessEUIDCheck(config *configs.Config) error {
    15  	if !config.RootlessEUID {
    16  		return nil
    17  	}
    18  	if err := rootlessEUIDMappings(config); err != nil {
    19  		return err
    20  	}
    21  	if err := rootlessEUIDMount(config); err != nil {
    22  		return err
    23  	}
    24  
    25  	// XXX: We currently can't verify the user config at all, because
    26  	//      configs.Config doesn't store the user-related configs. So this
    27  	//      has to be verified by setupUser() in init_linux.go.
    28  
    29  	return nil
    30  }
    31  
    32  func rootlessEUIDMappings(config *configs.Config) error {
    33  	if !config.Namespaces.Contains(configs.NEWUSER) {
    34  		return errors.New("rootless container requires user namespaces")
    35  	}
    36  	// We only require mappings if we are not joining another userns.
    37  	if path := config.Namespaces.PathOf(configs.NEWUSER); path == "" {
    38  		if len(config.UIDMappings) == 0 {
    39  			return errors.New("rootless containers requires at least one UID mapping")
    40  		}
    41  		if len(config.GIDMappings) == 0 {
    42  			return errors.New("rootless containers requires at least one GID mapping")
    43  		}
    44  	}
    45  	return nil
    46  }
    47  
    48  // rootlessEUIDMount verifies that all mounts have valid uid=/gid= options,
    49  // i.e. their arguments has proper ID mappings.
    50  func rootlessEUIDMount(config *configs.Config) error {
    51  	// XXX: We could whitelist allowed devices at this point, but I'm not
    52  	//      convinced that's a good idea. The kernel is the best arbiter of
    53  	//      access control.
    54  
    55  	for _, mount := range config.Mounts {
    56  		// Check that the options list doesn't contain any uid= or gid= entries
    57  		// that don't resolve to root.
    58  		for _, opt := range strings.Split(mount.Data, ",") {
    59  			if str := strings.TrimPrefix(opt, "uid="); len(str) < len(opt) {
    60  				uid, err := strconv.Atoi(str)
    61  				if err != nil {
    62  					// Ignore unknown mount options.
    63  					continue
    64  				}
    65  				if _, err := config.HostUID(uid); err != nil {
    66  					return fmt.Errorf("cannot specify uid=%d mount option for rootless container: %w", uid, err)
    67  				}
    68  			}
    69  
    70  			if str := strings.TrimPrefix(opt, "gid="); len(str) < len(opt) {
    71  				gid, err := strconv.Atoi(str)
    72  				if err != nil {
    73  					// Ignore unknown mount options.
    74  					continue
    75  				}
    76  				if _, err := config.HostGID(gid); err != nil {
    77  					return fmt.Errorf("cannot specify gid=%d mount option for rootless container: %w", gid, err)
    78  				}
    79  			}
    80  		}
    81  	}
    82  
    83  	return nil
    84  }