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 }