github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+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  	"github.com/syndtr/gocapability/capability"
     9  )
    10  
    11  var capabilityList Capabilities
    12  
    13  func init() {
    14  	last := capability.CAP_LAST_CAP
    15  	// hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap
    16  	if last == capability.Cap(63) {
    17  		last = capability.CAP_BLOCK_SUSPEND
    18  	}
    19  	for _, cap := range capability.List() {
    20  		if cap > last {
    21  			continue
    22  		}
    23  		capabilityList = append(capabilityList,
    24  			&CapabilityMapping{
    25  				Key:   "CAP_" + strings.ToUpper(cap.String()),
    26  				Value: cap,
    27  			},
    28  		)
    29  	}
    30  }
    31  
    32  type (
    33  	// CapabilityMapping maps linux capability name to its value of capability.Cap type
    34  	// Capabilities is one of the security systems in Linux Security Module (LSM)
    35  	// framework provided by the kernel.
    36  	// For more details on capabilities, see http://man7.org/linux/man-pages/man7/capabilities.7.html
    37  	CapabilityMapping struct {
    38  		Key   string         `json:"key,omitempty"`
    39  		Value capability.Cap `json:"value,omitempty"`
    40  	}
    41  	// Capabilities contains all CapabilityMapping
    42  	Capabilities []*CapabilityMapping
    43  )
    44  
    45  // String returns <key> of CapabilityMapping
    46  func (c *CapabilityMapping) String() string {
    47  	return c.Key
    48  }
    49  
    50  // GetCapability returns CapabilityMapping which contains specific key
    51  func GetCapability(key string) *CapabilityMapping {
    52  	for _, capp := range capabilityList {
    53  		if capp.Key == key {
    54  			cpy := *capp
    55  			return &cpy
    56  		}
    57  	}
    58  	return nil
    59  }
    60  
    61  // GetAllCapabilities returns all of the capabilities
    62  func GetAllCapabilities() []string {
    63  	output := make([]string, len(capabilityList))
    64  	for i, capability := range capabilityList {
    65  		output[i] = capability.String()
    66  	}
    67  	return output
    68  }
    69  
    70  // inSlice tests whether a string is contained in a slice of strings or not.
    71  func inSlice(slice []string, s string) bool {
    72  	for _, ss := range slice {
    73  		if s == ss {
    74  			return true
    75  		}
    76  	}
    77  	return false
    78  }
    79  
    80  const allCapabilities = "ALL"
    81  
    82  // NormalizeLegacyCapabilities normalizes, and validates CapAdd/CapDrop capabilities
    83  // by upper-casing them, and adding a CAP_ prefix (if not yet present).
    84  //
    85  // This function also accepts the "ALL" magic-value, that's used by CapAdd/CapDrop.
    86  func NormalizeLegacyCapabilities(caps []string) ([]string, error) {
    87  	var normalized []string
    88  
    89  	valids := GetAllCapabilities()
    90  	for _, c := range caps {
    91  		c = strings.ToUpper(c)
    92  		if c == allCapabilities {
    93  			normalized = append(normalized, c)
    94  			continue
    95  		}
    96  		if !strings.HasPrefix(c, "CAP_") {
    97  			c = "CAP_" + c
    98  		}
    99  		if !inSlice(valids, c) {
   100  			return nil, errdefs.InvalidParameter(fmt.Errorf("unknown capability: %q", c))
   101  		}
   102  		normalized = append(normalized, c)
   103  	}
   104  	return normalized, nil
   105  }
   106  
   107  // ValidateCapabilities validates if caps only contains valid capabilities
   108  func ValidateCapabilities(caps []string) error {
   109  	valids := GetAllCapabilities()
   110  	for _, c := range caps {
   111  		if !inSlice(valids, c) {
   112  			return errdefs.InvalidParameter(fmt.Errorf("unknown capability: %q", c))
   113  		}
   114  	}
   115  	return nil
   116  }
   117  
   118  // TweakCapabilities tweaks capabilities by adding, dropping, or overriding
   119  // capabilities in the basics capabilities list.
   120  func TweakCapabilities(basics, adds, drops, capabilities []string, privileged bool) ([]string, error) {
   121  	switch {
   122  	case privileged:
   123  		// Privileged containers get all capabilities
   124  		return GetAllCapabilities(), nil
   125  	case capabilities != nil:
   126  		// Use custom set of capabilities
   127  		if err := ValidateCapabilities(capabilities); err != nil {
   128  			return nil, err
   129  		}
   130  		return capabilities, nil
   131  	case len(adds) == 0 && len(drops) == 0:
   132  		// Nothing to tweak; we're done
   133  		return basics, nil
   134  	}
   135  
   136  	capDrop, err := NormalizeLegacyCapabilities(drops)
   137  	if err != nil {
   138  		return nil, err
   139  	}
   140  	capAdd, err := NormalizeLegacyCapabilities(adds)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	var caps []string
   146  
   147  	switch {
   148  	case inSlice(capAdd, allCapabilities):
   149  		// Add all capabilities except ones on capDrop
   150  		for _, c := range GetAllCapabilities() {
   151  			if !inSlice(capDrop, c) {
   152  				caps = append(caps, c)
   153  			}
   154  		}
   155  	case inSlice(capDrop, allCapabilities):
   156  		// "Drop" all capabilities; use what's in capAdd instead
   157  		caps = capAdd
   158  	default:
   159  		// First drop some capabilities
   160  		for _, c := range basics {
   161  			if !inSlice(capDrop, c) {
   162  				caps = append(caps, c)
   163  			}
   164  		}
   165  		// Then add the list of capabilities from capAdd
   166  		caps = append(caps, capAdd...)
   167  	}
   168  	return caps, nil
   169  }