github.com/jfrazelle/docker@v1.1.2-0.20210712172922-bf78e25fe508/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  	if last > capability.CAP_AUDIT_READ {
    20  		// Prevents docker from setting CAP_PERFMON, CAP_BPF, and CAP_CHECKPOINT_RESTORE
    21  		// capabilities on privileged (or CAP_ALL) containers on Kernel 5.8 and up.
    22  		// While these kernels support these capabilities, the current release of
    23  		// runc ships with an older version of /gocapability/capability, and does
    24  		// not know about them, causing an error to be produced.
    25  		//
    26  		// FIXME remove once https://github.com/opencontainers/runc/commit/6dfbe9b80707b1ca188255e8def15263348e0f9a
    27  		//       is included in a runc release and once we stop supporting containerd 1.3.x
    28  		//       (which ships with runc v1.0.0-rc92)
    29  		last = capability.CAP_AUDIT_READ
    30  	}
    31  	for _, cap := range capability.List() {
    32  		if cap > last {
    33  			continue
    34  		}
    35  		capabilityList = append(capabilityList,
    36  			&CapabilityMapping{
    37  				Key:   "CAP_" + strings.ToUpper(cap.String()),
    38  				Value: cap,
    39  			},
    40  		)
    41  	}
    42  }
    43  
    44  type (
    45  	// CapabilityMapping maps linux capability name to its value of capability.Cap type
    46  	// Capabilities is one of the security systems in Linux Security Module (LSM)
    47  	// framework provided by the kernel.
    48  	// For more details on capabilities, see http://man7.org/linux/man-pages/man7/capabilities.7.html
    49  	CapabilityMapping struct {
    50  		Key   string         `json:"key,omitempty"`
    51  		Value capability.Cap `json:"value,omitempty"`
    52  	}
    53  	// Capabilities contains all CapabilityMapping
    54  	Capabilities []*CapabilityMapping
    55  )
    56  
    57  // String returns <key> of CapabilityMapping
    58  func (c *CapabilityMapping) String() string {
    59  	return c.Key
    60  }
    61  
    62  // GetAllCapabilities returns all of the capabilities
    63  func GetAllCapabilities() []string {
    64  	output := make([]string, len(capabilityList))
    65  	for i, capability := range capabilityList {
    66  		output[i] = capability.String()
    67  	}
    68  	return output
    69  }
    70  
    71  // inSlice tests whether a string is contained in a slice of strings or not.
    72  func inSlice(slice []string, s string) bool {
    73  	for _, ss := range slice {
    74  		if s == ss {
    75  			return true
    76  		}
    77  	}
    78  	return false
    79  }
    80  
    81  const allCapabilities = "ALL"
    82  
    83  // NormalizeLegacyCapabilities normalizes, and validates CapAdd/CapDrop capabilities
    84  // by upper-casing them, and adding a CAP_ prefix (if not yet present).
    85  //
    86  // This function also accepts the "ALL" magic-value, that's used by CapAdd/CapDrop.
    87  func NormalizeLegacyCapabilities(caps []string) ([]string, error) {
    88  	var normalized []string
    89  
    90  	valids := GetAllCapabilities()
    91  	for _, c := range caps {
    92  		c = strings.ToUpper(c)
    93  		if c == allCapabilities {
    94  			normalized = append(normalized, c)
    95  			continue
    96  		}
    97  		if !strings.HasPrefix(c, "CAP_") {
    98  			c = "CAP_" + c
    99  		}
   100  		if !inSlice(valids, c) {
   101  			return nil, errdefs.InvalidParameter(fmt.Errorf("unknown capability: %q", c))
   102  		}
   103  		normalized = append(normalized, c)
   104  	}
   105  	return normalized, nil
   106  }
   107  
   108  // TweakCapabilities tweaks capabilities by adding, dropping, or overriding
   109  // capabilities in the basics capabilities list.
   110  func TweakCapabilities(basics, adds, drops []string, privileged bool) ([]string, error) {
   111  	switch {
   112  	case privileged:
   113  		// Privileged containers get all capabilities
   114  		return GetAllCapabilities(), nil
   115  	case len(adds) == 0 && len(drops) == 0:
   116  		// Nothing to tweak; we're done
   117  		return basics, nil
   118  	}
   119  
   120  	capDrop, err := NormalizeLegacyCapabilities(drops)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  	capAdd, err := NormalizeLegacyCapabilities(adds)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  
   129  	var caps []string
   130  
   131  	switch {
   132  	case inSlice(capAdd, allCapabilities):
   133  		// Add all capabilities except ones on capDrop
   134  		for _, c := range GetAllCapabilities() {
   135  			if !inSlice(capDrop, c) {
   136  				caps = append(caps, c)
   137  			}
   138  		}
   139  	case inSlice(capDrop, allCapabilities):
   140  		// "Drop" all capabilities; use what's in capAdd instead
   141  		caps = capAdd
   142  	default:
   143  		// First drop some capabilities
   144  		for _, c := range basics {
   145  			if !inSlice(capDrop, c) {
   146  				caps = append(caps, c)
   147  			}
   148  		}
   149  		// Then add the list of capabilities from capAdd
   150  		caps = append(caps, capAdd...)
   151  	}
   152  	return caps, nil
   153  }