github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/drivers/shared/capabilities/defaults.go (about)

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