github.com/rigado/snapd@v2.42.5-go-mod+incompatible/interfaces/builtin/kubernetes_support.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017-2018 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package builtin 21 22 import ( 23 "fmt" 24 "strings" 25 26 "github.com/snapcore/snapd/interfaces" 27 "github.com/snapcore/snapd/interfaces/apparmor" 28 "github.com/snapcore/snapd/interfaces/kmod" 29 "github.com/snapcore/snapd/interfaces/seccomp" 30 "github.com/snapcore/snapd/interfaces/udev" 31 "github.com/snapcore/snapd/snap" 32 ) 33 34 const kubernetesSupportSummary = `allows operating as the Kubernetes service` 35 36 const kubernetesSupportBaseDeclarationPlugs = ` 37 kubernetes-support: 38 allow-installation: false 39 deny-auto-connection: true 40 ` 41 42 const kubernetesSupportBaseDeclarationSlots = ` 43 kubernetes-support: 44 allow-installation: 45 slot-snap-type: 46 - core 47 deny-auto-connection: true 48 ` 49 50 const kubernetesSupportConnectedPlugAppArmorCommon = ` 51 # Common rules for running as a kubernetes node 52 53 # reading cgroups 54 capability sys_resource, 55 /sys/fs/cgroup/{,**} r, 56 57 # Allow adjusting the OOM score for containers. Note, this allows adjusting for 58 # all processes, not just containers. 59 @{PROC}/@{pid}/oom_score_adj rw, 60 @{PROC}/sys/vm/overcommit_memory rw, 61 /sys/kernel/mm/hugepages/{,**} r, 62 /sys/kernel/mm/transparent_hugepage/{,**} r, 63 64 capability dac_override, 65 66 /usr/bin/systemd-run Cxr -> systemd_run, 67 profile systemd_run (attach_disconnected,mediate_deleted) { 68 # Common rules for kubernetes use of systemd_run 69 #include <abstractions/base> 70 71 /{,usr/}bin/systemd-run rm, 72 owner @{PROC}/@{pid}/stat r, 73 owner @{PROC}/@{pid}/environ r, 74 @{PROC}/cmdline r, 75 @{PROC}/sys/kernel/osrelease r, 76 @{PROC}/1/sched r, 77 78 # setsockopt() 79 capability net_admin, 80 81 # ptrace 'trace' is coarse and not required for using the systemd private 82 # socket, and while the child profile omits 'capability sys_ptrace', skip 83 # for now since it isn't strictly required. 84 ptrace read peer=unconfined, 85 deny ptrace trace peer=unconfined, 86 /run/systemd/private rw, 87 88 /{,usr/}bin/true ixr, 89 @{INSTALL_DIR}/{@{SNAP_NAME},@{SNAP_INSTANCE_NAME}}/@{SNAP_REVISION}/{,usr/}bin/true ixr, 90 ###KUBERNETES_SUPPORT_SYSTEMD_RUN### 91 } 92 ` 93 94 const kubernetesSupportConnectedPlugAppArmorKubelet = ` 95 # Allow running as the kubelet service 96 97 # Ideally this would be snap-specific 98 /run/dockershim.sock rw, 99 100 # Ideally this would be snap-specific (it could if the control plane was a 101 # snap), but in deployments where the control plane is not a snap, it will tell 102 # flannel to use this path. 103 /run/flannel/{,**} rw, 104 /run/flannel/** k, 105 106 # allow managing pods' cgroups 107 /sys/fs/cgroup/*/kubepods/{,**} rw, 108 109 # Allow tracing our own processes. Note, this allows seccomp sandbox escape on 110 # kernels < 4.8 111 capability sys_ptrace, 112 ptrace (trace) peer=snap.@{SNAP_NAME}.*, 113 114 # Allow ptracing other processes (as part of ps-style process lookups). Note, 115 # the peer needs a corresponding tracedby rule. As a special case, disallow 116 # ptracing unconfined. 117 ptrace (trace), 118 deny ptrace (trace) peer=unconfined, 119 120 @{PROC}/[0-9]*/attr/ r, 121 @{PROC}/[0-9]*/fdinfo/ r, 122 @{PROC}/[0-9]*/map_files/ r, 123 @{PROC}/[0-9]*/ns/ r, 124 125 # kubernetes will verify and set panic and panic_on_oops to values it considers 126 # sane 127 @{PROC}/sys/kernel/panic w, 128 @{PROC}/sys/kernel/panic_on_oops w, 129 @{PROC}/sys/kernel/keys/root_maxbytes r, 130 @{PROC}/sys/kernel/keys/root_maxkeys r, 131 132 /dev/kmsg r, 133 134 # kubelet calls out to systemd-run for some mounts, but not all of them and not 135 # unmounts... 136 capability sys_admin, 137 mount /var/snap/@{SNAP_NAME}/common/{,**} -> /var/snap/@{SNAP_NAME}/common/{,**}, 138 mount options=(rw, rshared) -> /var/snap/@{SNAP_NAME}/common/{,**}, 139 140 /{,usr/}bin/mount ixr, 141 /{,usr/}bin/umount ixr, 142 deny /run/mount/utab rw, 143 umount /var/snap/@{SNAP_INSTANCE_NAME}/common/**, 144 ` 145 146 const kubernetesSupportConnectedPlugAppArmorKubeletSystemdRun = ` 147 # kubelet mount rules 148 capability sys_admin, 149 /{,usr/}bin/mount ixr, 150 mount fstype="tmpfs" tmpfs -> /var/snap/@{SNAP_INSTANCE_NAME}/common/**, 151 deny /run/mount/utab rw, 152 ` 153 154 const kubernetesSupportConnectedPlugSeccompKubelet = ` 155 # Allow running as the kubelet service 156 mount 157 umount 158 umount2 159 160 unshare 161 setns - CLONE_NEWNET 162 ` 163 164 var kubernetesSupportConnectedPlugUDevKubelet = []string{ 165 `KERNEL=="kmsg"`, 166 } 167 168 const kubernetesSupportConnectedPlugAppArmorKubeproxy = ` 169 # Allow running as the kubeproxy service 170 171 # managing our own cgroup 172 /sys/fs/cgroup/*/kube-proxy/{,**} rw, 173 174 # Allow reading the state of modules kubernetes needs 175 /sys/module/libcrc32c/initstate r, 176 /sys/module/llc/initstate r, 177 /sys/module/stp/initstate r, 178 /sys/module/ip_vs/initstate r, 179 /sys/module/ip_vs_rr/initstate r, 180 /sys/module/ip_vs_sh/initstate r, 181 /sys/module/ip_vs_wrr/initstate r, 182 ` 183 184 var kubernetesSupportConnectedPlugKmodKubeProxy = []string{ 185 `ip_vs_rr`, 186 `ip_vs_sh`, 187 `ip_vs_wrr`, 188 `libcrc32c`, 189 `llc`, 190 `stp`, 191 } 192 193 type kubernetesSupportInterface struct { 194 commonInterface 195 } 196 197 func k8sFlavor(plug *interfaces.ConnectedPlug) string { 198 var flavor string 199 _ = plug.Attr("flavor", &flavor) 200 return flavor 201 } 202 203 func (iface *kubernetesSupportInterface) AppArmorConnectedPlug(spec *apparmor.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { 204 snippet := kubernetesSupportConnectedPlugAppArmorCommon 205 systemd_run_extra := "" 206 207 switch k8sFlavor(plug) { 208 case "kubelet": 209 systemd_run_extra = kubernetesSupportConnectedPlugAppArmorKubeletSystemdRun 210 snippet += kubernetesSupportConnectedPlugAppArmorKubelet 211 spec.SetUsesPtraceTrace() 212 case "kubeproxy": 213 snippet += kubernetesSupportConnectedPlugAppArmorKubeproxy 214 default: 215 systemd_run_extra = kubernetesSupportConnectedPlugAppArmorKubeletSystemdRun 216 snippet += kubernetesSupportConnectedPlugAppArmorKubelet 217 snippet += kubernetesSupportConnectedPlugAppArmorKubeproxy 218 spec.SetUsesPtraceTrace() 219 } 220 221 old := "###KUBERNETES_SUPPORT_SYSTEMD_RUN###" 222 spec.AddSnippet(strings.Replace(snippet, old, systemd_run_extra, -1)) 223 return nil 224 } 225 226 func (iface *kubernetesSupportInterface) SecCompConnectedPlug(spec *seccomp.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { 227 flavor := k8sFlavor(plug) 228 if flavor == "kubelet" || flavor == "" { 229 snippet := kubernetesSupportConnectedPlugSeccompKubelet 230 spec.AddSnippet(snippet) 231 } 232 return nil 233 } 234 235 func (iface *kubernetesSupportInterface) UDevConnectedPlug(spec *udev.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { 236 flavor := k8sFlavor(plug) 237 if flavor == "kubelet" || flavor == "" { 238 for _, rule := range kubernetesSupportConnectedPlugUDevKubelet { 239 spec.TagDevice(rule) 240 } 241 } 242 return nil 243 } 244 245 func (iface *kubernetesSupportInterface) KModConnectedPlug(spec *kmod.Specification, plug *interfaces.ConnectedPlug, slot *interfaces.ConnectedSlot) error { 246 flavor := k8sFlavor(plug) 247 if flavor == "kubeproxy" || flavor == "" { 248 for _, m := range kubernetesSupportConnectedPlugKmodKubeProxy { 249 if err := spec.AddModule(m); err != nil { 250 return err 251 } 252 } 253 } 254 return nil 255 } 256 257 func (iface *kubernetesSupportInterface) BeforePreparePlug(plug *snap.PlugInfo) error { 258 // It's fine if flavor isn't specified, but if it is, it needs to be 259 // either "kubelet" or "kubeproxy" 260 if t, ok := plug.Attrs["flavor"]; ok && t != "kubelet" && t != "kubeproxy" { 261 return fmt.Errorf(`kubernetes-support plug requires "flavor" to be either "kubelet" or "kubeproxy"`) 262 } 263 264 return nil 265 } 266 267 func init() { 268 registerIface(&kubernetesSupportInterface{commonInterface{ 269 name: "kubernetes-support", 270 summary: kubernetesSupportSummary, 271 implicitOnClassic: true, 272 implicitOnCore: true, 273 baseDeclarationPlugs: kubernetesSupportBaseDeclarationPlugs, 274 baseDeclarationSlots: kubernetesSupportBaseDeclarationSlots, 275 }}) 276 }