github.com/moby/docker@v26.1.3+incompatible/oci/caps/utils.go (about)

     1  package caps // import "github.com/docker/docker/oci/caps"
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/docker/docker/errdefs"
     8  )
     9  
    10  var (
    11  	allCaps []string
    12  
    13  	// knownCapabilities is a map of all known capabilities, using capability
    14  	// name as index. Nil values indicate that the capability is known, but either
    15  	// not supported by the Kernel, or not available in the current environment,
    16  	// for example, when running Docker-in-Docker with restricted capabilities.
    17  	//
    18  	// Capabilities are one of the security systems in Linux Security Module (LSM)
    19  	// framework provided by the kernel.
    20  	// For more details on capabilities, see http://man7.org/linux/man-pages/man7/capabilities.7.html
    21  	knownCaps map[string]*struct{}
    22  )
    23  
    24  // GetAllCapabilities returns all capabilities that are availeble in the current
    25  // environment.
    26  func GetAllCapabilities() []string {
    27  	initCaps()
    28  	return allCaps
    29  }
    30  
    31  // knownCapabilities returns a map of all known capabilities, using capability
    32  // name as index. Nil values indicate that the capability is known, but either
    33  // not supported by the Kernel, or not available in the current environment, for
    34  // example, when running Docker-in-Docker with restricted capabilities.
    35  func knownCapabilities() map[string]*struct{} {
    36  	initCaps()
    37  	return knownCaps
    38  }
    39  
    40  // inSlice tests whether a string is contained in a slice of strings or not.
    41  func inSlice(slice []string, s string) bool {
    42  	for _, ss := range slice {
    43  		if s == ss {
    44  			return true
    45  		}
    46  	}
    47  	return false
    48  }
    49  
    50  const allCapabilities = "ALL"
    51  
    52  // NormalizeLegacyCapabilities normalizes, and validates CapAdd/CapDrop capabilities
    53  // by upper-casing them, and adding a CAP_ prefix (if not yet present).
    54  //
    55  // This function also accepts the "ALL" magic-value, that's used by CapAdd/CapDrop.
    56  func NormalizeLegacyCapabilities(caps []string) ([]string, error) {
    57  	var (
    58  		normalized     []string
    59  		capabilityList = knownCapabilities()
    60  	)
    61  
    62  	for _, c := range caps {
    63  		c = strings.ToUpper(c)
    64  		if c == allCapabilities {
    65  			normalized = append(normalized, c)
    66  			continue
    67  		}
    68  		if !strings.HasPrefix(c, "CAP_") {
    69  			c = "CAP_" + c
    70  		}
    71  		if v, ok := capabilityList[c]; !ok {
    72  			return nil, errdefs.InvalidParameter(fmt.Errorf("unknown capability: %q", c))
    73  		} else if v == nil {
    74  			return nil, errdefs.InvalidParameter(fmt.Errorf("capability not supported by your kernel or not available in the current environment: %q", c))
    75  		}
    76  		normalized = append(normalized, c)
    77  	}
    78  	return normalized, nil
    79  }
    80  
    81  // TweakCapabilities tweaks capabilities by adding, dropping, or overriding
    82  // capabilities in the basics capabilities list.
    83  func TweakCapabilities(basics, adds, drops []string, privileged bool) ([]string, error) {
    84  	switch {
    85  	case privileged:
    86  		// Privileged containers get all capabilities
    87  		return GetAllCapabilities(), nil
    88  	case len(adds) == 0 && len(drops) == 0:
    89  		// Nothing to tweak; we're done
    90  		return basics, nil
    91  	}
    92  
    93  	capDrop, err := NormalizeLegacyCapabilities(drops)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	capAdd, err := NormalizeLegacyCapabilities(adds)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	var caps []string
   103  
   104  	switch {
   105  	case inSlice(capAdd, allCapabilities):
   106  		// Add all capabilities except ones on capDrop
   107  		for _, c := range GetAllCapabilities() {
   108  			if !inSlice(capDrop, c) {
   109  				caps = append(caps, c)
   110  			}
   111  		}
   112  	case inSlice(capDrop, allCapabilities):
   113  		// "Drop" all capabilities; use what's in capAdd instead
   114  		caps = capAdd
   115  	default:
   116  		// First drop some capabilities
   117  		for _, c := range basics {
   118  			if !inSlice(capDrop, c) {
   119  				caps = append(caps, c)
   120  			}
   121  		}
   122  		// Then add the list of capabilities from capAdd
   123  		caps = append(caps, capAdd...)
   124  	}
   125  	return caps, nil
   126  }