|
| 1 | +--- |
| 2 | +title: AppArmor to GA |
| 3 | +authors: |
| 4 | + - "@saschagrunert" |
| 5 | + - "@tallclair" |
| 6 | +owning-sig: sig-node |
| 7 | +participating-sigs: |
| 8 | + - sig-api-machinery |
| 9 | + - sig-auth |
| 10 | +reviewers: |
| 11 | + - TBD |
| 12 | +approvers: |
| 13 | + - TBD |
| 14 | +editor: TBD |
| 15 | +creation-date: 2020-01-10 |
| 16 | +status: provisional |
| 17 | +--- |
| 18 | + |
| 19 | +# AppArmor to GA |
| 20 | + |
| 21 | +## Table of Contents |
| 22 | + |
| 23 | +<!-- toc --> |
| 24 | + |
| 25 | +- [Release Signoff Checklist](#release-signoff-checklist) |
| 26 | +- [Summary](#summary) |
| 27 | +- [Motivation](#motivation) |
| 28 | + - [Goals](#goals) |
| 29 | + - [Non-Goals](#non-goals) |
| 30 | +- [Proposal](#proposal) |
| 31 | + - [API](#api) |
| 32 | + - [Pod API](#pod-api) |
| 33 | + - [PodSecurityPolicy API](#podsecuritypolicy-api) |
| 34 | +- [Design Details](#design-details) |
| 35 | + - [Version Skew Strategy](#version-skew-strategy) |
| 36 | + - [Pod Creation](#pod-creation) |
| 37 | + - [Pod Update](#pod-update) |
| 38 | + - [PodSecurityPolicy Creation](#podsecuritypolicy-creation) |
| 39 | + - [PodSecurityPolicy Update](#podsecuritypolicy-update) |
| 40 | + - [PodSecurityPolicy Enforcement](#podsecuritypolicy-enforcement) |
| 41 | + - [PodTemplates](#podtemplates) |
| 42 | + - [Upgrade / Downgrade](#upgrade--downgrade) |
| 43 | + - [Test Plan](#test-plan) |
| 44 | + - [Graduation Criteria](#graduation-criteria) |
| 45 | + - [Upgrade / Downgrade Strategy](#upgrade--downgrade-strategy) |
| 46 | +- [Implementation History](#implementation-history) |
| 47 | +- [Drawbacks](#drawbacks) |
| 48 | + <!-- /toc --> |
| 49 | + |
| 50 | +## Release Signoff Checklist |
| 51 | + |
| 52 | +**ACTION REQUIRED:** In order to merge code into a release, there must be an |
| 53 | +issue in [kubernetes/enhancements] referencing this KEP and targeting a release |
| 54 | +milestone **before [Enhancement |
| 55 | +Freeze](https://github.com/kubernetes/sig-release/tree/master/releases) of the |
| 56 | +targeted release**. |
| 57 | + |
| 58 | +For enhancements that make changes to code or processes/procedures in core |
| 59 | +Kubernetes i.e., [kubernetes/kubernetes], we require the following Release |
| 60 | +Signoff checklist to be completed. |
| 61 | + |
| 62 | +Check these off as they are completed for the Release Team to track. These |
| 63 | +checklist items _must_ be updated for the enhancement to be released. |
| 64 | + |
| 65 | +- [ ] kubernetes/enhancements issue in release milestone, which links to KEP |
| 66 | + (this should be a link to the KEP location in kubernetes/enhancements, not the |
| 67 | + initial KEP PR) |
| 68 | +- [ ] KEP approvers have set the KEP status to `implementable` |
| 69 | +- [ ] Design details are appropriately documented |
| 70 | +- [ ] Test plan is in place, giving consideration to SIG Architecture and SIG |
| 71 | + Testing input |
| 72 | +- [ ] Graduation criteria is in place |
| 73 | +- [ ] "Implementation History" section is up-to-date for milestone |
| 74 | +- [ ] User-facing documentation has been created in [kubernetes/website], for |
| 75 | + publication to [kubernetes.io] |
| 76 | +- [ ] Supporting documentation e.g., additional design documents, links to |
| 77 | + mailing list discussions/SIG meetings, relevant PRs/issues, release notes |
| 78 | + |
| 79 | +**Note:** Any PRs to move a KEP to `implementable` or significant changes once |
| 80 | +it is marked `implementable` should be approved by each of the KEP approvers. If |
| 81 | +any of those approvers is no longer appropriate than changes to that list should |
| 82 | +be approved by the remaining approvers and/or the owning SIG (or SIG-arch for |
| 83 | +cross cutting KEPs). |
| 84 | + |
| 85 | +**Note:** This checklist is iterative and should be reviewed and updated every |
| 86 | +time this enhancement is being considered for a milestone. |
| 87 | + |
| 88 | +[kubernetes.io]: https://kubernetes.io/ |
| 89 | +[kubernetes/enhancements]: https://github.com/kubernetes/enhancements/issues |
| 90 | +[kubernetes/kubernetes]: https://github.com/kubernetes/kubernetes |
| 91 | +[kubernetes/website]: https://github.com/kubernetes/website |
| 92 | + |
| 93 | +## Summary |
| 94 | + |
| 95 | +This is a proposal to upgrade the AppArmor annotation on pods & pod security |
| 96 | +policies (PSP) to a dedicated field, and mark the feature as GA. This proposal |
| 97 | +aims to do the _bare minimum_ to clean up the feature, without blocking future |
| 98 | +enhancements. |
| 99 | + |
| 100 | +## Motivation |
| 101 | + |
| 102 | +AppArmor support has been added with Kubernetes v1.14 and is already in beta. |
| 103 | +Profiles have to be available on each node whereas the container runtime ensures |
| 104 | +that the profile is loaded when specified at pod or PSP level. Profiles |
| 105 | +can be specified per-container via the pod’s metadata annotation: |
| 106 | + |
| 107 | +``` |
| 108 | +container.apparmor.security.beta.kubernetes.io/<container_name>: {unconfined,runtime/default,localhost/<profile>} |
| 109 | +``` |
| 110 | + |
| 111 | +The feature has been more or less unchanged ever since. Also note that the |
| 112 | +addition predates feature gates or our modern concept of feature lifecycle. So, |
| 113 | +even though the annotations include `beta` in the key, this is entirely useable |
| 114 | +on any production GA cluster. |
| 115 | + |
| 116 | +The main motivation behind this KEP is to promote the AppArmor feature to GA. |
| 117 | + |
| 118 | +_NOTE: Seccomp is in a very similar state, but with some subtle differences. |
| 119 | +Promoting Seccomp to GA will be covered by a [separate |
| 120 | +KEP][https://github.com/kubernetes/enhancements/pull/1148]._ |
| 121 | + |
| 122 | +### Goals |
| 123 | + |
| 124 | +- Declare AppArmor as GA |
| 125 | +- Fully document and formally spec the feature support |
| 126 | +- Add equivalent API fields to replace AppArmor annotations |
| 127 | +- Deprecate the AppArmor annotations |
| 128 | + |
| 129 | +### Non-Goals |
| 130 | + |
| 131 | +This KEP proposes the absolute minimum to get AppArmor to GA, therefore all |
| 132 | +functional enhancements are out of scope, including: |
| 133 | + |
| 134 | +- Defining any standard "Kubernetes branded" AppArmor profiles |
| 135 | +- Formally specifying the AppArmor profile format in Kubernetes |
| 136 | +- Providing mechanisms for loading profiles from outside the of the node |
| 137 | +- Changing the semantics around AppArmor support |
| 138 | +- Windows support |
| 139 | + |
| 140 | +## Proposal |
| 141 | + |
| 142 | +AppArmor is not available on every Linux distribution. Beside this, container |
| 143 | +runtimes have AppArmor as compile-time feature which may be disabled as well. |
| 144 | +With the GA API we do not change the error handling and behave exactly the same |
| 145 | +as the current error propagation paths. |
| 146 | + |
| 147 | +### API |
| 148 | + |
| 149 | +The AppArmor API will be functionally equivalent to the current beta API. This |
| 150 | +includes the Pod API, which specifies what profile the containers run with, and |
| 151 | +the `PodSecurityPolicy` API which specifies allowed profiles & a default |
| 152 | +profile. |
| 153 | + |
| 154 | +#### Pod API |
| 155 | + |
| 156 | +The Pod AppArmor API is generally immutable, except in `PodTemplates`. |
| 157 | + |
| 158 | +```go |
| 159 | +type PodSecurityContext struct { |
| 160 | + ... |
| 161 | + // The AppArmor options to use by the containers in this pod. |
| 162 | + // +optional |
| 163 | + AppArmor *AppArmorProfile |
| 164 | + ... |
| 165 | +} |
| 166 | + |
| 167 | +type SecurityContext struct { |
| 168 | + ... |
| 169 | + // The AppArmor options to use by this container. If AppArmor options are |
| 170 | + // provided at both the pod & container level, the container options |
| 171 | + // override the pod options. |
| 172 | + // +optional |
| 173 | + AppArmor *AppArmorProfile |
| 174 | + ... |
| 175 | +} |
| 176 | + |
| 177 | +type AppArmorProfileType string |
| 178 | + |
| 179 | +const ( |
| 180 | + AppArmorProfileUnconfined AppArmorProfileType = "Unconfined" |
| 181 | + AppArmorProfileDefault AppArmorProfileType = "Default" |
| 182 | + AppArmorProfileLocalhost AppArmorProfileType = "Localhost" |
| 183 | +) |
| 184 | + |
| 185 | +// Only one profile source may be set. |
| 186 | +// +union |
| 187 | +type AppArmorProfile struct { |
| 188 | + // +unionDescriminator |
| 189 | + Type AppArmorProfileType |
| 190 | + |
| 191 | + // Load a profile defined on the node. |
| 192 | + // The profile must be available on the node to work. |
| 193 | + // The length of the profile is limited to 253 characters. |
| 194 | + // The `Type` in this struct has to be set to `AppArmorProfileLocalhost`. |
| 195 | + // +optional |
| 196 | + LocalhostProfile *string |
| 197 | +} |
| 198 | +``` |
| 199 | + |
| 200 | +This API makes the options more explicit and leaves room for new profile sources |
| 201 | +to be added in the future (e.g. Kubernetes predefined profiles or ConfigMap |
| 202 | +profiles). The AppArmor options structure leaves room for future extensions, |
| 203 | +such as defining the behavior when a profile cannot be set. |
| 204 | + |
| 205 | +#### PodSecurityPolicy API |
| 206 | + |
| 207 | +```go |
| 208 | +type PodSecurityPolicySpec struct { |
| 209 | + ... |
| 210 | + // AppArmor is the strategy that will dictate allowable and default AppArmor |
| 211 | + // profiles for the container. |
| 212 | + // +optional |
| 213 | + AppArmor *ApparmorStrategyOptions |
| 214 | + ... |
| 215 | +} |
| 216 | + |
| 217 | +type AppArmorStrategyOptions struct { |
| 218 | + // The default profile to set on the pod, if none is specified. |
| 219 | + // The default MUST be allowed by the allowedProfiles. |
| 220 | + // +optional |
| 221 | + DefaultProfile *v1.AppArmorProfile |
| 222 | + |
| 223 | + // The set of profiles that may be set on the pod or containers. |
| 224 | + // If unspecified, AppArmor profiles are unrestricted by this policy. |
| 225 | + // +optional |
| 226 | + AllowedProfiles *AppArmorProfileSet |
| 227 | +} |
| 228 | + |
| 229 | +// A set of AppArmor profiles. This struct should be a plural of |
| 230 | +// `v1.AppArmorProfile`. |
| 231 | +// All values are optional, and an unspecified field excludes all profiles of |
| 232 | +// that type from the set. |
| 233 | +type AppArmorProfileSet struct { |
| 234 | + // The allowed AppArmor profile types. |
| 235 | + // +optional |
| 236 | + Types []AppArmorProfileType |
| 237 | + |
| 238 | + // The allowed runtimeProfiles. A value of '*' allows all runtimeProfiles. |
| 239 | + // +optional |
| 240 | + RuntimeProfiles []string |
| 241 | + |
| 242 | + // The allowed localhostProfiles. Values may end in '*' to include all |
| 243 | + // localhostProfiles with a prefix. |
| 244 | + // +optional |
| 245 | + LocalhostProfiles []string |
| 246 | +} |
| 247 | +``` |
| 248 | + |
| 249 | +## Design Details |
| 250 | + |
| 251 | +### Version Skew Strategy |
| 252 | + |
| 253 | +All API skew is resolved in the API server. New Kubelets will only use the |
| 254 | +AppArmor values specified in the fields and ignore the annotations therefore. |
| 255 | + |
| 256 | +#### Pod Creation |
| 257 | + |
| 258 | +If no AppArmor annotations or fields are specified, no action is necessary. |
| 259 | + |
| 260 | +If _only_ AppArmor fields are specified, add the corresponding annotations. This |
| 261 | +ensures that the fields are enforced even if the node version trails the API |
| 262 | +version. Since [we |
| 263 | +support](https://kubernetes.io/docs/setup/release/version-skew-policy) up to 2 |
| 264 | +minor releases of version skew between the master and node, this behavior will |
| 265 | +be removed in version N+4 (where N is the release with the upgraded AppArmor |
| 266 | +support). |
| 267 | + |
| 268 | +If _only_ AppArmor annotations are specified, copy the values into the |
| 269 | +corresponding fields. This ensures that existing applications continue to |
| 270 | +enforce AppArmor, and prevents the kubelet from needing to resolve annotations & |
| 271 | +fields. |
| 272 | + |
| 273 | +If both AppArmor annotations _and_ fields are specified, the values MUST match. |
| 274 | +This will be enforced in API validation. |
| 275 | + |
| 276 | +To raise awareness of annotation usage (in case of old automation), an |
| 277 | +additional warning annotation will be added when a pod is created with an |
| 278 | +AppArmor annotation: |
| 279 | + |
| 280 | +``` |
| 281 | +warning.kubernetes.io/apparmor: "AppArmor set through annotations. Support will be dropped in v1.22" |
| 282 | +``` |
| 283 | + |
| 284 | +#### Pod Update |
| 285 | + |
| 286 | +The AppArmor fields on a pod are immutable. |
| 287 | + |
| 288 | +The behavior on annotation update is currently ill-defined: the annotation |
| 289 | +update is allowed, but the new value will not be used until the container is |
| 290 | +restarted. There is no way to tell (from the API) what value a container is |
| 291 | +using. |
| 292 | + |
| 293 | +Therefore, AppArmor annotation updates will be ignored. This maintains backwards |
| 294 | +API compatibility (no tightening validation), and makes a small stabilizing |
| 295 | +change to behavior (new Kubelets will ignore the update). |
| 296 | + |
| 297 | +#### PodSecurityPolicy Creation |
| 298 | + |
| 299 | +Unlike with pods, PodSecurityPolicy AppArmor annotations and fields are _not_ |
| 300 | +synced. |
| 301 | + |
| 302 | +If only AppArmor annotations or fields are specified, no action is necessary. |
| 303 | +The set value is used when applying the PodSecurityPolicy. |
| 304 | + |
| 305 | +If both AppArmor annotations _and_ fields are specified, the values MUST match. |
| 306 | +This will be enforced in API validation. |
| 307 | + |
| 308 | +#### PodSecurityPolicy Update |
| 309 | + |
| 310 | +PodSecurityPolicy AppArmor fields are mutable. On an update, the same rules are |
| 311 | +applied as for creation, ignoring the old values. |
| 312 | + |
| 313 | +If only AppArmor annotations or fields are specified in the updated PSP, no |
| 314 | +action is necessary, and the specified values are used. |
| 315 | + |
| 316 | +If both AppArmor annotations _and_ fields are specified in the updated PSP, the |
| 317 | +values MUST match. |
| 318 | + |
| 319 | +#### PodSecurityPolicy Enforcement |
| 320 | + |
| 321 | +The PodSecurityPolicy admission controller must continue to check the PSP object |
| 322 | +for annotations, as well as for fields. |
| 323 | + |
| 324 | +When setting default profiles, PSP only needs to set the field. The API |
| 325 | +machinery will handle setting the annotation as necessary. |
| 326 | + |
| 327 | +When enforcing allowed profiles, the PSP should check BOTH the annotations & |
| 328 | +fields. In most cases, they should be consistent. On pod update, the AppArmor |
| 329 | +annotations may differ from the fields. In that case, the PSP enforcement should |
| 330 | +check both values as the effective value depends on the node version running the |
| 331 | +pod. |
| 332 | + |
| 333 | +#### PodTemplates |
| 334 | + |
| 335 | +PodTemplates (e.g. ReplaceSets, Deployments, StatefulSets, etc.) will be |
| 336 | +ignored. The field/annotation resolution will happen on template instantiation. |
| 337 | + |
| 338 | +However, to raise awareness of existing controllers using the AppArmor |
| 339 | +annotations that need to be migrated, the same warning annotation will be added |
| 340 | +to the controller as for pods: |
| 341 | + |
| 342 | +``` |
| 343 | +warning.kubernetes.io/apparmor: "AppArmor set through annotations. Support will be dropped in v1.22" |
| 344 | +``` |
| 345 | + |
| 346 | +#### Upgrade / Downgrade |
| 347 | + |
| 348 | +Nodes do not currently support in-place upgrades, so pods will be recreated on |
| 349 | +node upgrade and downgrade. No special handling or consideration is needed to |
| 350 | +support this. |
| 351 | + |
| 352 | +On the API server side, we've already taken version skew in HA clusters into |
| 353 | +account. The same precautions make upgrade & downgrade handling a non-issue. |
| 354 | + |
| 355 | +### Test Plan |
| 356 | + |
| 357 | +AppArmor already has [e2e tests][https://github.com/kubernetes/kubernetes/blob/6596a14/test/e2e_node/apparmor_test.go], |
| 358 | +but the tests are guarded by the `[Feature:AppArmor]` tag and not run in the |
| 359 | +standard test suites. |
| 360 | + |
| 361 | +Prior to being marked GA, the feature tag will be removed from the AppArmor |
| 362 | +tests, and the tests will be migrated to the new fields API. Tests will be |
| 363 | +tagged as `[LinuxOnly]`. |
| 364 | + |
| 365 | +New tests will be added covering the annotation/field conflict cases described |
| 366 | +under [Version Skew Strategy](#version-skew-strategy). |
| 367 | + |
| 368 | +Test coverage for localhost profiles will be added as well. |
| 369 | + |
| 370 | +### Graduation Criteria |
| 371 | + |
| 372 | +_This section is excluded, as it is the subject of the entire proposal._ |
| 373 | + |
| 374 | +### Upgrade / Downgrade Strategy |
| 375 | + |
| 376 | +See [Version Skew Strategy](#version-skew-strategy). |
| 377 | + |
| 378 | +## Implementation History |
| 379 | + |
| 380 | +- 2020-01-10: Initial KEP |
| 381 | + |
| 382 | +## Drawbacks |
| 383 | + |
| 384 | +Promoting AppArmor as-is to GA may be seen as "blessing" the current |
| 385 | +functionality, and make it harder to make some of the enhancements listed under |
| 386 | +[Non-Goals](#non-goals). Since the current behavior is unguarded, I think we |
| 387 | +already need to treat the behavior as GA. |
0 commit comments