github.com/hernad/nomad@v1.6.112/drivers/shared/capabilities/defaults.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package capabilities
     5  
     6  import (
     7  	"fmt"
     8  	"regexp"
     9  
    10  	"github.com/syndtr/gocapability/capability"
    11  )
    12  
    13  const (
    14  	// HCLSpecLiteral is an equivalent list to NomadDefaults, expressed as a literal
    15  	// HCL string for use in HCL config parsing.
    16  	HCLSpecLiteral = `["AUDIT_WRITE","CHOWN","DAC_OVERRIDE","FOWNER","FSETID","KILL","MKNOD","NET_BIND_SERVICE","SETFCAP","SETGID","SETPCAP","SETUID","SYS_CHROOT"]`
    17  )
    18  
    19  var (
    20  	extractLiteral = regexp.MustCompile(`([\w]+)`)
    21  )
    22  
    23  // NomadDefaults is the set of Linux capabilities that Nomad enables by
    24  // default. This list originates from what Docker enabled by default, but then
    25  // excludes NET_RAW for security reasons.
    26  //
    27  // This set is use in the as HCL configuration default, described by HCLSpecLiteral.
    28  func NomadDefaults() *Set {
    29  	return New(extractLiteral.FindAllString(HCLSpecLiteral, -1))
    30  }
    31  
    32  // DockerDefaults is a list of Linux capabilities enabled by Docker by default
    33  // and is used to compute the set of capabilities to add/drop given docker driver
    34  // configuration.
    35  //
    36  // https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
    37  func DockerDefaults() *Set {
    38  	defaults := NomadDefaults()
    39  	defaults.Add("NET_RAW")
    40  	return defaults
    41  }
    42  
    43  // Supported returns the set of capabilities supported by the operating system.
    44  //
    45  // This set will expand over time as new capabilities are introduced to the kernel
    46  // and the capability library is updated (which tends to happen to keep up with
    47  // run-container libraries).
    48  //
    49  // Defers to a library generated from
    50  // https://github.com/torvalds/linux/blob/master/include/uapi/linux/capability.h
    51  func Supported() *Set {
    52  	s := New(nil)
    53  
    54  	last := capability.CAP_LAST_CAP
    55  
    56  	// workaround for RHEL6 which has no /proc/sys/kernel/cap_last_cap
    57  	if last == capability.Cap(63) {
    58  		last = capability.CAP_BLOCK_SUSPEND
    59  	}
    60  
    61  	// accumulate every capability supported by this system
    62  	for _, c := range capability.List() {
    63  		if c > last {
    64  			continue
    65  		}
    66  		s.Add(c.String())
    67  	}
    68  
    69  	return s
    70  }
    71  
    72  // LegacySupported returns the historical set of capabilities used when a task is
    73  // configured to run as root using the exec task driver. Older versions of Nomad
    74  // always allowed the root user to make use of any capability. Now that the exec
    75  // task driver supports configuring the allowed capabilities, operators are
    76  // encouraged to explicitly opt-in to capabilities beyond this legacy set. We
    77  // maintain the legacy list here, because previous versions of Nomad deferred to
    78  // the capability.List library function, which adds new capabilities over time.
    79  //
    80  // https://github.com/hernad/nomad/blob/v1.0.4/vendor/github.com/syndtr/gocapability/capability/enum_gen.go#L88
    81  func LegacySupported() *Set {
    82  	return New([]string{
    83  		"CAP_CHOWN",
    84  		"CAP_DAC_OVERRIDE",
    85  		"CAP_DAC_READ_SEARCH",
    86  		"CAP_FOWNER",
    87  		"CAP_FSETID",
    88  		"CAP_KILL",
    89  		"CAP_SETGID",
    90  		"CAP_SETUID",
    91  		"CAP_SETPCAP",
    92  		"CAP_LINUX_IMMUTABLE",
    93  		"CAP_NET_BIND_SERVICE",
    94  		"CAP_NET_BROADCAST",
    95  		"CAP_NET_ADMIN",
    96  		"CAP_NET_RAW",
    97  		"CAP_IPC_LOCK",
    98  		"CAP_IPC_OWNER",
    99  		"CAP_SYS_MODULE",
   100  		"CAP_SYS_RAWIO",
   101  		"CAP_SYS_CHROOT",
   102  		"CAP_SYS_PTRACE",
   103  		"CAP_SYS_PACCT",
   104  		"CAP_SYS_ADMIN",
   105  		"CAP_SYS_BOOT",
   106  		"CAP_SYS_NICE",
   107  		"CAP_SYS_RESOURCE",
   108  		"CAP_SYS_TIME",
   109  		"CAP_SYS_TTY_CONFIG",
   110  		"CAP_MKNOD",
   111  		"CAP_LEASE",
   112  		"CAP_AUDIT_WRITE",
   113  		"CAP_AUDIT_CONTROL",
   114  		"CAP_SETFCAP",
   115  		"CAP_MAC_OVERRIDE",
   116  		"CAP_MAC_ADMIN",
   117  		"CAP_SYSLOG",
   118  		"CAP_WAKE_ALARM",
   119  		"CAP_BLOCK_SUSPEND",
   120  		"CAP_AUDIT_READ",
   121  	})
   122  }
   123  
   124  // Calculate the resulting set of linux capabilities to enable for a task, taking
   125  // into account:
   126  // - default capability basis
   127  // - driver allowable capabilities
   128  // - task capability drops
   129  // - task capability adds
   130  //
   131  // Nomad establishes a standard set of enabled capabilities allowed by the task
   132  // driver if allow_caps is not set. This is the same set that the task will be
   133  // enabled with by default if allow_caps does not further reduce permissions,
   134  // in which case the task capabilities will also be reduced accordingly.
   135  //
   136  // The task will drop any capabilities specified in cap_drop, and add back
   137  // capabilities specified in cap_add. The task will not be allowed to add capabilities
   138  // not set in the the allow_caps setting (which by default is the same as the basis).
   139  //
   140  // cap_add takes precedence over cap_drop, enabling the common pattern of dropping
   141  // all capabilities, then adding back the desired smaller set. e.g.
   142  //
   143  //	cap_drop = ["all"]
   144  //	cap_add = ["chown", "kill"]
   145  //
   146  // Note that the resulting capability names are upper-cased and prefixed with
   147  // "CAP_", which is the expected input for the exec/java driver implementation.
   148  func Calculate(basis *Set, allowCaps, capAdd, capDrop []string) ([]string, error) {
   149  	allow := New(allowCaps)
   150  	adds := New(capAdd)
   151  
   152  	// determine caps the task wants that are not allowed
   153  	missing := allow.Difference(adds)
   154  	if !missing.Empty() {
   155  		return nil, fmt.Errorf("driver does not allow the following capabilities: %s", missing)
   156  	}
   157  
   158  	// the realized enabled capabilities starts with what is allowed both by driver
   159  	// config AND is a member of the basis (i.e. nomad defaults)
   160  	result := basis.Intersect(allow)
   161  
   162  	// then remove capabilities the task explicitly drops
   163  	result.Remove(capDrop)
   164  
   165  	// then add back capabilities the task explicitly adds
   166  	return result.Union(adds).Slice(true), nil
   167  }
   168  
   169  // Delta calculates the set of capabilities that must be added and dropped relative
   170  // to a basis to achieve a desired result. The use case is that the docker driver
   171  // assumes a default set (DockerDefault), and we must calculate what to pass into
   172  // --cap-add and --cap-drop on container creation given the inputs of the docker
   173  // plugin config for allow_caps, and the docker task configuration for cap_add and
   174  // cap_drop. Note that the user provided cap_add and cap_drop settings are always
   175  // included, even if they are redundant with the basis (maintaining existing
   176  // behavior, working with existing tests).
   177  //
   178  // Note that the resulting capability names are lower-cased and not prefixed with
   179  // "CAP_", which is the existing style used with the docker driver implementation.
   180  func Delta(basis *Set, allowCaps, capAdd, capDrop []string) ([]string, []string, error) {
   181  	all := func(caps []string) bool {
   182  		for _, c := range caps {
   183  			if normalize(c) == "all" {
   184  				return true
   185  			}
   186  		}
   187  		return false
   188  	}
   189  
   190  	// set of caps allowed by driver
   191  	allow := New(allowCaps)
   192  
   193  	// determine caps the task wants that are not allowed
   194  	missing := allow.Difference(New(capAdd))
   195  	if !missing.Empty() {
   196  		return nil, nil, fmt.Errorf("driver does not allow the following capabilities: %s", missing)
   197  	}
   198  
   199  	// add what the task is asking for
   200  	add := New(capAdd).Slice(false)
   201  	if all(capAdd) {
   202  		add = []string{"all"}
   203  	}
   204  
   205  	// drop what the task removes plus whatever is in the basis that is not
   206  	// in the driver allow configuration
   207  	drop := New(allowCaps).Difference(basis).Union(New(capDrop)).Slice(false)
   208  	if all(capDrop) {
   209  		drop = []string{"all"}
   210  	}
   211  
   212  	return add, drop, nil
   213  }