summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVijay Anusuri <vanusuri@mvista.com>2023-09-27 16:18:40 +0530
committerBruce Ashfield <bruce.ashfield@gmail.com>2023-10-02 16:16:25 +0000
commit35c723774ee06b3c1831f00a2cbf25cbeae132e1 (patch)
tree6ba591bfaf2ad614ea6f3d5661ec2f69402cdf08
parent0dbb8593fa38ac2a04fcac04ff3e35611e849824 (diff)
downloadmeta-virtualization-dunfell.tar.gz
kubernetes: Backport fix for CVE-2021-25735 and CVE-2021-25737dunfell
Upstream-commit: https://github.com/kubernetes/kubernetes/commit/e612ebfdff22e4bd27ad8345f7c82f074bfedf26 & https://github.com/kubernetes/kubernetes/commit/d57f0641d60b73934ebc2cdf4b6a63182217d10c & https://github.com/kubernetes/kubernetes/commit/901e8e07e1f031456ecd7fefce965aaa05916825 Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
-rw-r--r--recipes-containers/kubernetes/kubernetes/CVE-2021-25735-pre1.patch613
-rw-r--r--recipes-containers/kubernetes/kubernetes/CVE-2021-25735.patch535
-rw-r--r--recipes-containers/kubernetes/kubernetes/CVE-2021-25737.patch128
-rw-r--r--recipes-containers/kubernetes/kubernetes_git.bb3
4 files changed, 1279 insertions, 0 deletions
diff --git a/recipes-containers/kubernetes/kubernetes/CVE-2021-25735-pre1.patch b/recipes-containers/kubernetes/kubernetes/CVE-2021-25735-pre1.patch
new file mode 100644
index 00000000..2066188a
--- /dev/null
+++ b/recipes-containers/kubernetes/kubernetes/CVE-2021-25735-pre1.patch
@@ -0,0 +1,613 @@
1From e612ebfdff22e4bd27ad8345f7c82f074bfedf26 Mon Sep 17 00:00:00 2001
2From: wojtekt <wojtekt@google.com>
3Date: Tue, 26 Nov 2019 13:29:26 +0100
4Subject: [PATCH] Immutable field and validation
5
6Upstream-Status: Backport [https://github.com/kubernetes/kubernetes/commit/e612ebfdff22e4bd27ad8345f7c82f074bfedf26]
7CVE: CVE-2021-25735 #Dependency Patch1
8Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
9---
10 pkg/apis/core/types.go | 12 +
11 pkg/apis/core/validation/validation.go | 24 +-
12 pkg/apis/core/validation/validation_test.go | 209 ++++++++++++++++--
13 pkg/features/kube_features.go | 7 +
14 pkg/registry/core/configmap/strategy.go | 28 ++-
15 pkg/registry/core/secret/strategy.go | 17 ++
16 staging/src/k8s.io/api/core/v1/types.go | 16 ++
17 .../k8sdeps/transformer/hash/hash.go | 20 +-
18 .../k8sdeps/transformer/hash/hash_test.go | 4 +-
19 .../src/k8s.io/kubectl/pkg/util/hash/hash.go | 20 +-
20 .../k8s.io/kubectl/pkg/util/hash/hash_test.go | 4 +-
21 11 files changed, 322 insertions(+), 39 deletions(-)
22
23diff --git a/pkg/apis/core/types.go b/pkg/apis/core/types.go
24index 74d22ae973e87..c5ada193effc4 100644
25--- a/src/import/pkg/apis/core/types.go
26+++ b/src/import/pkg/apis/core/types.go
27@@ -4735,6 +4735,12 @@ type Secret struct {
28 // +optional
29 metav1.ObjectMeta
30
31+ // Immutable field, if set, ensures that data stored in the Secret cannot
32+ // be updated (only object metadata can be modified).
33+ // This is an alpha field enabled by ImmutableEphemeralVolumes feature gate.
34+ // +optional
35+ Immutable *bool
36+
37 // Data contains the secret data. Each key must consist of alphanumeric
38 // characters, '-', '_' or '.'. The serialized form of the secret data is a
39 // base64 encoded string, representing the arbitrary (possibly non-string)
40@@ -4857,6 +4863,12 @@ type ConfigMap struct {
41 // +optional
42 metav1.ObjectMeta
43
44+ // Immutable field, if set, ensures that data stored in the ConfigMap cannot
45+ // be updated (only object metadata can be modified).
46+ // This is an alpha field enabled by ImmutableEphemeralVolumes feature gate.
47+ // +optional
48+ Immutable *bool
49+
50 // Data contains the configuration data.
51 // Each key must consist of alphanumeric characters, '-', '_' or '.'.
52 // Values with non-UTF-8 byte sequences must use the BinaryData field.
53diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go
54index 4ad241c745b7d..8e3cfd9d9e423 100644
55--- a/src/import/pkg/apis/core/validation/validation.go
56+++ b/src/import/pkg/apis/core/validation/validation.go
57@@ -5005,6 +5005,16 @@ func ValidateSecretUpdate(newSecret, oldSecret *core.Secret) field.ErrorList {
58 }
59
60 allErrs = append(allErrs, ValidateImmutableField(newSecret.Type, oldSecret.Type, field.NewPath("type"))...)
61+ if oldSecret.Immutable != nil && *oldSecret.Immutable {
62+ if !reflect.DeepEqual(newSecret.Immutable, oldSecret.Immutable) {
63+ allErrs = append(allErrs, field.Forbidden(field.NewPath("immutable"), "field is immutable when `immutable` is set"))
64+ }
65+ if !reflect.DeepEqual(newSecret.Data, oldSecret.Data) {
66+ allErrs = append(allErrs, field.Forbidden(field.NewPath("data"), "field is immutable when `immutable` is set"))
67+ }
68+ // We don't validate StringData, as it was already converted back to Data
69+ // before validation is happening.
70+ }
71
72 allErrs = append(allErrs, ValidateSecret(newSecret)...)
73 return allErrs
74@@ -5051,8 +5061,20 @@ func ValidateConfigMap(cfg *core.ConfigMap) field.ErrorList {
75 func ValidateConfigMapUpdate(newCfg, oldCfg *core.ConfigMap) field.ErrorList {
76 allErrs := field.ErrorList{}
77 allErrs = append(allErrs, ValidateObjectMetaUpdate(&newCfg.ObjectMeta, &oldCfg.ObjectMeta, field.NewPath("metadata"))...)
78- allErrs = append(allErrs, ValidateConfigMap(newCfg)...)
79
80+ if oldCfg.Immutable != nil && *oldCfg.Immutable {
81+ if !reflect.DeepEqual(newCfg.Immutable, oldCfg.Immutable) {
82+ allErrs = append(allErrs, field.Forbidden(field.NewPath("immutable"), "field is immutable when `immutable` is set"))
83+ }
84+ if !reflect.DeepEqual(newCfg.Data, oldCfg.Data) {
85+ allErrs = append(allErrs, field.Forbidden(field.NewPath("data"), "field is immutable when `immutable` is set"))
86+ }
87+ if !reflect.DeepEqual(newCfg.BinaryData, oldCfg.BinaryData) {
88+ allErrs = append(allErrs, field.Forbidden(field.NewPath("binaryData"), "field is immutable when `immutable` is set"))
89+ }
90+ }
91+
92+ allErrs = append(allErrs, ValidateConfigMap(newCfg)...)
93 return allErrs
94 }
95
96diff --git a/pkg/apis/core/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go
97index 8ba68da00fe05..de8c1d49fc196 100644
98--- a/src/import/pkg/apis/core/validation/validation_test.go
99+++ b/src/import/pkg/apis/core/validation/validation_test.go
100@@ -13117,6 +13117,104 @@ func TestValidateSecret(t *testing.T) {
101 }
102 }
103
104+func TestValidateSecretUpdate(t *testing.T) {
105+ validSecret := func() core.Secret {
106+ return core.Secret{
107+ ObjectMeta: metav1.ObjectMeta{
108+ Name: "foo",
109+ Namespace: "bar",
110+ ResourceVersion: "20",
111+ },
112+ Data: map[string][]byte{
113+ "data-1": []byte("bar"),
114+ },
115+ }
116+ }
117+
118+ falseVal := false
119+ trueVal := true
120+
121+ secret := validSecret()
122+ immutableSecret := validSecret()
123+ immutableSecret.Immutable = &trueVal
124+ mutableSecret := validSecret()
125+ mutableSecret.Immutable = &falseVal
126+
127+ secretWithData := validSecret()
128+ secretWithData.Data["data-2"] = []byte("baz")
129+ immutableSecretWithData := validSecret()
130+ immutableSecretWithData.Immutable = &trueVal
131+ immutableSecretWithData.Data["data-2"] = []byte("baz")
132+
133+ secretWithChangedData := validSecret()
134+ secretWithChangedData.Data["data-1"] = []byte("foo")
135+ immutableSecretWithChangedData := validSecret()
136+ immutableSecretWithChangedData.Immutable = &trueVal
137+ immutableSecretWithChangedData.Data["data-1"] = []byte("foo")
138+
139+ tests := []struct {
140+ name string
141+ oldSecret core.Secret
142+ newSecret core.Secret
143+ valid bool
144+ }{
145+ {
146+ name: "mark secret immutable",
147+ oldSecret: secret,
148+ newSecret: immutableSecret,
149+ valid: true,
150+ },
151+ {
152+ name: "revert immutable secret",
153+ oldSecret: immutableSecret,
154+ newSecret: secret,
155+ valid: false,
156+ },
157+ {
158+ name: "makr immutable secret mutable",
159+ oldSecret: immutableSecret,
160+ newSecret: mutableSecret,
161+ valid: false,
162+ },
163+ {
164+ name: "add data in secret",
165+ oldSecret: secret,
166+ newSecret: secretWithData,
167+ valid: true,
168+ },
169+ {
170+ name: "add data in immutable secret",
171+ oldSecret: immutableSecret,
172+ newSecret: immutableSecretWithData,
173+ valid: false,
174+ },
175+ {
176+ name: "change data in secret",
177+ oldSecret: secret,
178+ newSecret: secretWithChangedData,
179+ valid: true,
180+ },
181+ {
182+ name: "change data in immutable secret",
183+ oldSecret: immutableSecret,
184+ newSecret: immutableSecretWithChangedData,
185+ valid: false,
186+ },
187+ }
188+
189+ for _, tc := range tests {
190+ t.Run(tc.name, func(t *testing.T) {
191+ errs := ValidateSecretUpdate(&tc.newSecret, &tc.oldSecret)
192+ if tc.valid && len(errs) > 0 {
193+ t.Errorf("Unexpected error: %v", errs)
194+ }
195+ if !tc.valid && len(errs) == 0 {
196+ t.Errorf("Unexpected lack of error")
197+ }
198+ })
199+ }
200+}
201+
202 func TestValidateDockerConfigSecret(t *testing.T) {
203 validDockerSecret := func() core.Secret {
204 return core.Secret{
205@@ -13731,40 +13829,105 @@ func TestValidateConfigMapUpdate(t *testing.T) {
206 Data: data,
207 }
208 }
209+ validConfigMap := func() core.ConfigMap {
210+ return newConfigMap("1", "validname", "validdns", map[string]string{"key": "value"})
211+ }
212
213- var (
214- validConfigMap = newConfigMap("1", "validname", "validns", map[string]string{"key": "value"})
215- noVersion = newConfigMap("", "validname", "validns", map[string]string{"key": "value"})
216- )
217+ falseVal := false
218+ trueVal := true
219+
220+ configMap := validConfigMap()
221+ immutableConfigMap := validConfigMap()
222+ immutableConfigMap.Immutable = &trueVal
223+ mutableConfigMap := validConfigMap()
224+ mutableConfigMap.Immutable = &falseVal
225+
226+ configMapWithData := validConfigMap()
227+ configMapWithData.Data["key-2"] = "value-2"
228+ immutableConfigMapWithData := validConfigMap()
229+ immutableConfigMapWithData.Immutable = &trueVal
230+ immutableConfigMapWithData.Data["key-2"] = "value-2"
231+
232+ configMapWithChangedData := validConfigMap()
233+ configMapWithChangedData.Data["key"] = "foo"
234+ immutableConfigMapWithChangedData := validConfigMap()
235+ immutableConfigMapWithChangedData.Immutable = &trueVal
236+ immutableConfigMapWithChangedData.Data["key"] = "foo"
237+
238+ noVersion := newConfigMap("", "validname", "validns", map[string]string{"key": "value"})
239
240 cases := []struct {
241- name string
242- newCfg core.ConfigMap
243- oldCfg core.ConfigMap
244- isValid bool
245+ name string
246+ newCfg core.ConfigMap
247+ oldCfg core.ConfigMap
248+ valid bool
249 }{
250 {
251- name: "valid",
252- newCfg: validConfigMap,
253- oldCfg: validConfigMap,
254- isValid: true,
255+ name: "valid",
256+ newCfg: configMap,
257+ oldCfg: configMap,
258+ valid: true,
259 },
260 {
261- name: "invalid",
262- newCfg: noVersion,
263- oldCfg: validConfigMap,
264- isValid: false,
265+ name: "invalid",
266+ newCfg: noVersion,
267+ oldCfg: configMap,
268+ valid: false,
269+ },
270+ {
271+ name: "mark configmap immutable",
272+ oldCfg: configMap,
273+ newCfg: immutableConfigMap,
274+ valid: true,
275+ },
276+ {
277+ name: "revert immutable configmap",
278+ oldCfg: immutableConfigMap,
279+ newCfg: configMap,
280+ valid: false,
281+ },
282+ {
283+ name: "mark immutable configmap mutable",
284+ oldCfg: immutableConfigMap,
285+ newCfg: mutableConfigMap,
286+ valid: false,
287+ },
288+ {
289+ name: "add data in configmap",
290+ oldCfg: configMap,
291+ newCfg: configMapWithData,
292+ valid: true,
293+ },
294+ {
295+ name: "add data in immutable configmap",
296+ oldCfg: immutableConfigMap,
297+ newCfg: immutableConfigMapWithData,
298+ valid: false,
299+ },
300+ {
301+ name: "change data in configmap",
302+ oldCfg: configMap,
303+ newCfg: configMapWithChangedData,
304+ valid: true,
305+ },
306+ {
307+ name: "change data in immutable configmap",
308+ oldCfg: immutableConfigMap,
309+ newCfg: immutableConfigMapWithChangedData,
310+ valid: false,
311 },
312 }
313
314 for _, tc := range cases {
315- errs := ValidateConfigMapUpdate(&tc.newCfg, &tc.oldCfg)
316- if tc.isValid && len(errs) > 0 {
317- t.Errorf("%v: unexpected error: %v", tc.name, errs)
318- }
319- if !tc.isValid && len(errs) == 0 {
320- t.Errorf("%v: unexpected non-error", tc.name)
321- }
322+ t.Run(tc.name, func(t *testing.T) {
323+ errs := ValidateConfigMapUpdate(&tc.newCfg, &tc.oldCfg)
324+ if tc.valid && len(errs) > 0 {
325+ t.Errorf("Unexpected error: %v", errs)
326+ }
327+ if !tc.valid && len(errs) == 0 {
328+ t.Errorf("Unexpected lack of error")
329+ }
330+ })
331 }
332 }
333
334diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go
335index 309dbb2955663..00da711112d71 100644
336--- a/src/import/pkg/features/kube_features.go
337+++ b/src/import/pkg/features/kube_features.go
338@@ -548,6 +548,12 @@ const (
339 //
340 // Enables topology aware service routing
341 ServiceTopology featuregate.Feature = "ServiceTopology"
342+
343+ // owner: @wojtek-t
344+ // alpha: v1.18
345+ //
346+ // Enables a feature to make secrets and configmaps data immutable.
347+ ImmutableEphemeralVolumes featuregate.Feature = "ImmutableEphemeralVolumes"
348 )
349
350 func init() {
351@@ -634,6 +640,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
352 AllowInsecureBackendProxy: {Default: true, PreRelease: featuregate.Beta},
353 PodDisruptionBudget: {Default: true, PreRelease: featuregate.Beta},
354 ServiceTopology: {Default: false, PreRelease: featuregate.Alpha},
355+ ImmutableEphemeralVolumes: {Default: false, PreRelease: featuregate.Alpha},
356
357 // inherited features from generic apiserver, relisted here to get a conflict if it is changed
358 // unintentionally on either side:
359diff --git a/pkg/registry/core/configmap/strategy.go b/pkg/registry/core/configmap/strategy.go
360index 4f8bf42e3bd60..d592c181c0c2e 100644
361--- a/src/import/pkg/registry/core/configmap/strategy.go
362+++ b/src/import/pkg/registry/core/configmap/strategy.go
363@@ -28,9 +28,11 @@ import (
364 "k8s.io/apiserver/pkg/registry/rest"
365 pkgstorage "k8s.io/apiserver/pkg/storage"
366 "k8s.io/apiserver/pkg/storage/names"
367+ utilfeature "k8s.io/apiserver/pkg/util/feature"
368 "k8s.io/kubernetes/pkg/api/legacyscheme"
369 api "k8s.io/kubernetes/pkg/apis/core"
370 "k8s.io/kubernetes/pkg/apis/core/validation"
371+ "k8s.io/kubernetes/pkg/features"
372 )
373
374 // strategy implements behavior for ConfigMap objects
375@@ -54,7 +56,8 @@ func (strategy) NamespaceScoped() bool {
376 }
377
378 func (strategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
379- _ = obj.(*api.ConfigMap)
380+ configMap := obj.(*api.ConfigMap)
381+ dropDisabledFields(configMap, nil)
382 }
383
384 func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
385@@ -72,12 +75,9 @@ func (strategy) AllowCreateOnUpdate() bool {
386 }
387
388 func (strategy) PrepareForUpdate(ctx context.Context, newObj, oldObj runtime.Object) {
389- _ = oldObj.(*api.ConfigMap)
390- _ = newObj.(*api.ConfigMap)
391-}
392-
393-func (strategy) AllowUnconditionalUpdate() bool {
394- return true
395+ oldConfigMap := oldObj.(*api.ConfigMap)
396+ newConfigMap := newObj.(*api.ConfigMap)
397+ dropDisabledFields(newConfigMap, oldConfigMap)
398 }
399
400 func (strategy) ValidateUpdate(ctx context.Context, newObj, oldObj runtime.Object) field.ErrorList {
401@@ -86,6 +86,20 @@ func (strategy) ValidateUpdate(ctx context.Context, newObj, oldObj runtime.Objec
402 return validation.ValidateConfigMapUpdate(newCfg, oldCfg)
403 }
404
405+func isImmutableInUse(configMap *api.ConfigMap) bool {
406+ return configMap != nil && configMap.Immutable != nil
407+}
408+
409+func dropDisabledFields(configMap *api.ConfigMap, oldConfigMap *api.ConfigMap) {
410+ if !utilfeature.DefaultFeatureGate.Enabled(features.ImmutableEphemeralVolumes) && !isImmutableInUse(oldConfigMap) {
411+ configMap.Immutable = nil
412+ }
413+}
414+
415+func (strategy) AllowUnconditionalUpdate() bool {
416+ return true
417+}
418+
419 // GetAttrs returns labels and fields of a given object for filtering purposes.
420 func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
421 configMap, ok := obj.(*api.ConfigMap)
422diff --git a/pkg/registry/core/secret/strategy.go b/pkg/registry/core/secret/strategy.go
423index 1701805065e6c..0d5908d8975f1 100644
424--- a/src/import/pkg/registry/core/secret/strategy.go
425+++ b/src/import/pkg/registry/core/secret/strategy.go
426@@ -29,9 +29,11 @@ import (
427 "k8s.io/apiserver/pkg/registry/rest"
428 pkgstorage "k8s.io/apiserver/pkg/storage"
429 "k8s.io/apiserver/pkg/storage/names"
430+ utilfeature "k8s.io/apiserver/pkg/util/feature"
431 "k8s.io/kubernetes/pkg/api/legacyscheme"
432 api "k8s.io/kubernetes/pkg/apis/core"
433 "k8s.io/kubernetes/pkg/apis/core/validation"
434+ "k8s.io/kubernetes/pkg/features"
435 )
436
437 // strategy implements behavior for Secret objects
438@@ -53,6 +55,8 @@ func (strategy) NamespaceScoped() bool {
439 }
440
441 func (strategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
442+ secret := obj.(*api.Secret)
443+ dropDisabledFields(secret, nil)
444 }
445
446 func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
447@@ -67,12 +71,25 @@ func (strategy) AllowCreateOnUpdate() bool {
448 }
449
450 func (strategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
451+ newSecret := obj.(*api.Secret)
452+ oldSecret := old.(*api.Secret)
453+ dropDisabledFields(newSecret, oldSecret)
454 }
455
456 func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
457 return validation.ValidateSecretUpdate(obj.(*api.Secret), old.(*api.Secret))
458 }
459
460+func isImmutableInUse(secret *api.Secret) bool {
461+ return secret != nil && secret.Immutable != nil
462+}
463+
464+func dropDisabledFields(secret *api.Secret, oldSecret *api.Secret) {
465+ if !utilfeature.DefaultFeatureGate.Enabled(features.ImmutableEphemeralVolumes) && !isImmutableInUse(oldSecret) {
466+ secret.Immutable = nil
467+ }
468+}
469+
470 func (strategy) AllowUnconditionalUpdate() bool {
471 return true
472 }
473diff --git a/staging/src/k8s.io/api/core/v1/types.go b/staging/src/k8s.io/api/core/v1/types.go
474index a78372aeaffa7..1e3aa51730427 100644
475--- a/src/import/staging/src/k8s.io/api/core/v1/types.go
476+++ b/src/import/staging/src/k8s.io/api/core/v1/types.go
477@@ -5424,6 +5424,14 @@ type Secret struct {
478 // +optional
479 metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
480
481+ // Immutable, if set to true, ensures that data stored in the Secret cannot
482+ // be updated (only object metadata can be modified).
483+ // If not set to true, the field can be modified at any time.
484+ // Defaulted to nil.
485+ // This is an alpha field enabled by ImmutableEphemeralVolumes feature gate.
486+ // +optional
487+ Immutable *bool `json:"immutable,omitempty"`
488+
489 // Data contains the secret data. Each key must consist of alphanumeric
490 // characters, '-', '_' or '.'. The serialized form of the secret data is a
491 // base64 encoded string, representing the arbitrary (possibly non-string)
492@@ -5557,6 +5565,14 @@ type ConfigMap struct {
493 // +optional
494 metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
495
496+ // Immutable, if set to true, ensures that data stored in the ConfigMap cannot
497+ // be updated (only object metadata can be modified).
498+ // If not set to true, the field can be modified at any time.
499+ // Defaulted to nil.
500+ // This is an alpha field enabled by ImmutableEphemeralVolumes feature gate.
501+ // +optional
502+ Immutable *bool `json:"immutable,omitempty"`
503+
504 // Data contains the configuration data.
505 // Each key must consist of alphanumeric characters, '-', '_' or '.'.
506 // Values with non-UTF-8 byte sequences must use the BinaryData field.
507diff --git a/staging/src/k8s.io/cli-runtime/pkg/kustomize/k8sdeps/transformer/hash/hash.go b/staging/src/k8s.io/cli-runtime/pkg/kustomize/k8sdeps/transformer/hash/hash.go
508index 17e24ff3e6443..85bf1e731c3fb 100644
509--- a/src/import/staging/src/k8s.io/cli-runtime/pkg/kustomize/k8sdeps/transformer/hash/hash.go
510+++ b/src/import/staging/src/k8s.io/cli-runtime/pkg/kustomize/k8sdeps/transformer/hash/hash.go
511@@ -90,7 +90,14 @@ func SecretHash(sec *v1.Secret) (string, error) {
512 // Data, Kind, and Name are taken into account.
513 func encodeConfigMap(cm *v1.ConfigMap) (string, error) {
514 // json.Marshal sorts the keys in a stable order in the encoding
515- m := map[string]interface{}{"kind": "ConfigMap", "name": cm.Name, "data": cm.Data}
516+ m := map[string]interface{}{
517+ "kind": "ConfigMap",
518+ "name": cm.Name,
519+ "data": cm.Data,
520+ }
521+ if cm.Immutable != nil {
522+ m["immutable"] = *cm.Immutable
523+ }
524 if len(cm.BinaryData) > 0 {
525 m["binaryData"] = cm.BinaryData
526 }
527@@ -105,7 +112,16 @@ func encodeConfigMap(cm *v1.ConfigMap) (string, error) {
528 // Data, Kind, Name, and Type are taken into account.
529 func encodeSecret(sec *v1.Secret) (string, error) {
530 // json.Marshal sorts the keys in a stable order in the encoding
531- data, err := json.Marshal(map[string]interface{}{"kind": "Secret", "type": sec.Type, "name": sec.Name, "data": sec.Data})
532+ m := map[string]interface{}{
533+ "kind": "Secret",
534+ "type": sec.Type,
535+ "name": sec.Name,
536+ "data": sec.Data,
537+ }
538+ if sec.Immutable != nil {
539+ m["immutable"] = *sec.Immutable
540+ }
541+ data, err := json.Marshal(m)
542 if err != nil {
543 return "", err
544 }
545diff --git a/staging/src/k8s.io/cli-runtime/pkg/kustomize/k8sdeps/transformer/hash/hash_test.go b/staging/src/k8s.io/cli-runtime/pkg/kustomize/k8sdeps/transformer/hash/hash_test.go
546index 2d336f35a824e..144fe444e4cac 100644
547--- a/src/import/staging/src/k8s.io/cli-runtime/pkg/kustomize/k8sdeps/transformer/hash/hash_test.go
548+++ b/src/import/staging/src/k8s.io/cli-runtime/pkg/kustomize/k8sdeps/transformer/hash/hash_test.go
549@@ -178,8 +178,8 @@ not their metadata (e.g. the Data of a ConfigMap, but nothing in ObjectMeta).
550 obj interface{}
551 expect int
552 }{
553- {"ConfigMap", v1.ConfigMap{}, 4},
554- {"Secret", v1.Secret{}, 5},
555+ {"ConfigMap", v1.ConfigMap{}, 5},
556+ {"Secret", v1.Secret{}, 6},
557 }
558 for _, c := range cases {
559 val := reflect.ValueOf(c.obj)
560diff --git a/staging/src/k8s.io/kubectl/pkg/util/hash/hash.go b/staging/src/k8s.io/kubectl/pkg/util/hash/hash.go
561index de0036245d2f1..1b20f384b7098 100644
562--- a/src/import/staging/src/k8s.io/kubectl/pkg/util/hash/hash.go
563+++ b/src/import/staging/src/k8s.io/kubectl/pkg/util/hash/hash.go
564@@ -56,7 +56,14 @@ func SecretHash(sec *v1.Secret) (string, error) {
565 // Data, Kind, and Name are taken into account.
566 func encodeConfigMap(cm *v1.ConfigMap) (string, error) {
567 // json.Marshal sorts the keys in a stable order in the encoding
568- m := map[string]interface{}{"kind": "ConfigMap", "name": cm.Name, "data": cm.Data}
569+ m := map[string]interface{}{
570+ "kind": "ConfigMap",
571+ "name": cm.Name,
572+ "data": cm.Data,
573+ }
574+ if cm.Immutable != nil {
575+ m["immutable"] = *cm.Immutable
576+ }
577 if len(cm.BinaryData) > 0 {
578 m["binaryData"] = cm.BinaryData
579 }
580@@ -70,8 +77,17 @@ func encodeConfigMap(cm *v1.ConfigMap) (string, error) {
581 // encodeSecret encodes a Secret.
582 // Data, Kind, Name, and Type are taken into account.
583 func encodeSecret(sec *v1.Secret) (string, error) {
584+ m := map[string]interface{}{
585+ "kind": "Secret",
586+ "type": sec.Type,
587+ "name": sec.Name,
588+ "data": sec.Data,
589+ }
590+ if sec.Immutable != nil {
591+ m["immutable"] = *sec.Immutable
592+ }
593 // json.Marshal sorts the keys in a stable order in the encoding
594- data, err := json.Marshal(map[string]interface{}{"kind": "Secret", "type": sec.Type, "name": sec.Name, "data": sec.Data})
595+ data, err := json.Marshal(m)
596 if err != nil {
597 return "", err
598 }
599diff --git a/staging/src/k8s.io/kubectl/pkg/util/hash/hash_test.go b/staging/src/k8s.io/kubectl/pkg/util/hash/hash_test.go
600index f527a98a2026c..455459c3b3df5 100644
601--- a/src/import/staging/src/k8s.io/kubectl/pkg/util/hash/hash_test.go
602+++ b/src/import/staging/src/k8s.io/kubectl/pkg/util/hash/hash_test.go
603@@ -164,8 +164,8 @@ not their metadata (e.g. the Data of a ConfigMap, but nothing in ObjectMeta).
604 obj interface{}
605 expect int
606 }{
607- {"ConfigMap", v1.ConfigMap{}, 4},
608- {"Secret", v1.Secret{}, 5},
609+ {"ConfigMap", v1.ConfigMap{}, 5},
610+ {"Secret", v1.Secret{}, 6},
611 }
612 for _, c := range cases {
613 val := reflect.ValueOf(c.obj)
diff --git a/recipes-containers/kubernetes/kubernetes/CVE-2021-25735.patch b/recipes-containers/kubernetes/kubernetes/CVE-2021-25735.patch
new file mode 100644
index 00000000..dce50f3e
--- /dev/null
+++ b/recipes-containers/kubernetes/kubernetes/CVE-2021-25735.patch
@@ -0,0 +1,535 @@
1From 7d4efe7ad8cf06c0c1d6092cf1f77964edb8016f Mon Sep 17 00:00:00 2001
2From: David Eads <deads@redhat.com>
3Date: Fri, 29 Jan 2021 13:47:31 -0500
4Subject: [PATCH 1/8] tweak validation to avoid mutation
5
6Upstream-Status: Backport [https://github.com/kubernetes/kubernetes/commit/d57f0641d60b73934ebc2cdf4b6a63182217d10c]
7CVE: CVE-2021-25735
8Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
9---
10 pkg/apis/core/validation/validation.go | 46 +++++++++-----------------
11 1 file changed, 15 insertions(+), 31 deletions(-)
12
13diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go
14index 8e3cfd9d9e4..89e5b5811c4 100644
15--- a/src/import/pkg/apis/core/validation/validation.go
16+++ b/src/import/pkg/apis/core/validation/validation.go
17@@ -29,8 +29,6 @@ import (
18 "unicode"
19 "unicode/utf8"
20
21- "k8s.io/klog"
22-
23 "k8s.io/api/core/v1"
24 apiequality "k8s.io/apimachinery/pkg/api/equality"
25 "k8s.io/apimachinery/pkg/api/resource"
26@@ -4530,11 +4528,8 @@ func ValidateNodeUpdate(node, oldNode *core.Node) field.ErrorList {
27 addresses[address] = true
28 }
29
30- if len(oldNode.Spec.PodCIDRs) == 0 {
31- // Allow the controller manager to assign a CIDR to a node if it doesn't have one.
32- //this is a no op for a string slice.
33- oldNode.Spec.PodCIDRs = node.Spec.PodCIDRs
34- } else {
35+ // Allow the controller manager to assign a CIDR to a node if it doesn't have one.
36+ if len(oldNode.Spec.PodCIDRs) > 0 {
37 // compare the entire slice
38 if len(oldNode.Spec.PodCIDRs) != len(node.Spec.PodCIDRs) {
39 allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "podCIDRs"), "node updates may not change podCIDR except from \"\" to valid"))
40@@ -4548,46 +4543,35 @@ func ValidateNodeUpdate(node, oldNode *core.Node) field.ErrorList {
41 }
42
43 // Allow controller manager updating provider ID when not set
44- if len(oldNode.Spec.ProviderID) == 0 {
45- oldNode.Spec.ProviderID = node.Spec.ProviderID
46- } else {
47- if oldNode.Spec.ProviderID != node.Spec.ProviderID {
48- allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "providerID"), "node updates may not change providerID except from \"\" to valid"))
49- }
50+ if len(oldNode.Spec.ProviderID) > 0 && oldNode.Spec.ProviderID != node.Spec.ProviderID {
51+ allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "providerID"), "node updates may not change providerID except from \"\" to valid"))
52 }
53
54 if node.Spec.ConfigSource != nil {
55 allErrs = append(allErrs, validateNodeConfigSourceSpec(node.Spec.ConfigSource, field.NewPath("spec", "configSource"))...)
56 }
57- oldNode.Spec.ConfigSource = node.Spec.ConfigSource
58 if node.Status.Config != nil {
59 allErrs = append(allErrs, validateNodeConfigStatus(node.Status.Config, field.NewPath("status", "config"))...)
60 }
61- oldNode.Status.Config = node.Status.Config
62-
63- // TODO: move reset function to its own location
64- // Ignore metadata changes now that they have been tested
65- oldNode.ObjectMeta = node.ObjectMeta
66- // Allow users to update capacity
67- oldNode.Status.Capacity = node.Status.Capacity
68- // Allow users to unschedule node
69- oldNode.Spec.Unschedulable = node.Spec.Unschedulable
70- // Clear status
71- oldNode.Status = node.Status
72
73 // update taints
74 if len(node.Spec.Taints) > 0 {
75 allErrs = append(allErrs, validateNodeTaints(node.Spec.Taints, fldPath.Child("taints"))...)
76 }
77- oldNode.Spec.Taints = node.Spec.Taints
78
79- // We made allowed changes to oldNode, and now we compare oldNode to node. Any remaining differences indicate changes to protected fields.
80- // TODO: Add a 'real' error type for this error and provide print actual diffs.
81- if !apiequality.Semantic.DeepEqual(oldNode, node) {
82- klog.V(4).Infof("Update failed validation %#v vs %#v", oldNode, node)
83- allErrs = append(allErrs, field.Forbidden(field.NewPath(""), "node updates may only change labels, taints, or capacity (or configSource, if the DynamicKubeletConfig feature gate is enabled)"))
84+ if node.Spec.DoNotUseExternalID != oldNode.Spec.DoNotUseExternalID {
85+ allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "externalID"), "may not be updated"))
86 }
87
88+ // status and metadata are allowed change (barring restrictions above), so separately test spec field.
89+ // spec only has a few fields, so check the ones we don't allow changing
90+ // 1. PodCIDRs - immutable after first set - checked above
91+ // 2. ProviderID - immutable after first set - checked above
92+ // 3. Unschedulable - allowed to change
93+ // 4. Taints - allowed to change
94+ // 5. ConfigSource - allowed to change (and checked above)
95+ // 6. DoNotUseExternalID - immutable - checked above
96+
97 return allErrs
98 }
99
100--
1012.25.1
102
103
104From 0ef8605f4535713f17ede4bcf162ad513cbf6900 Mon Sep 17 00:00:00 2001
105From: David Eads <deads@redhat.com>
106Date: Mon, 15 Feb 2021 16:21:42 -0500
107Subject: [PATCH 2/8] remove unnecessary mutations in validation
108
109These mutations are already done in the strategy
110---
111 pkg/apis/core/validation/validation.go | 22 ++--------------------
112 1 file changed, 2 insertions(+), 20 deletions(-)
113
114diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go
115index 89e5b5811c4..3381f2a37c2 100644
116--- a/src/import/pkg/apis/core/validation/validation.go
117+++ b/src/import/pkg/apis/core/validation/validation.go
118@@ -1874,13 +1874,11 @@ func ValidatePersistentVolumeUpdate(newPv, oldPv *core.PersistentVolume) field.E
119 }
120
121 // ValidatePersistentVolumeStatusUpdate tests to see if the status update is legal for an end user to make.
122-// newPv is updated with fields that cannot be changed.
123 func ValidatePersistentVolumeStatusUpdate(newPv, oldPv *core.PersistentVolume) field.ErrorList {
124 allErrs := ValidateObjectMetaUpdate(&newPv.ObjectMeta, &oldPv.ObjectMeta, field.NewPath("metadata"))
125 if len(newPv.ResourceVersion) == 0 {
126 allErrs = append(allErrs, field.Required(field.NewPath("resourceVersion"), ""))
127 }
128- newPv.Spec = oldPv.Spec
129 return allErrs
130 }
131
132@@ -2026,7 +2024,6 @@ func ValidatePersistentVolumeClaimStatusUpdate(newPvc, oldPvc *core.PersistentVo
133 for r, qty := range newPvc.Status.Capacity {
134 allErrs = append(allErrs, validateBasicResource(qty, capPath.Key(string(r)))...)
135 }
136- newPvc.Spec = oldPvc.Spec
137 return allErrs
138 }
139
140@@ -3795,8 +3792,7 @@ func ValidateContainerStateTransition(newStatuses, oldStatuses []core.ContainerS
141 return allErrs
142 }
143
144-// ValidatePodStatusUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields
145-// that cannot be changed.
146+// ValidatePodStatusUpdate tests to see if the update is legal for an end user to make.
147 func ValidatePodStatusUpdate(newPod, oldPod *core.Pod) field.ErrorList {
148 fldPath := field.NewPath("metadata")
149 allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath)
150@@ -3819,9 +3815,6 @@ func ValidatePodStatusUpdate(newPod, oldPod *core.Pod) field.ErrorList {
151 allErrs = append(allErrs, ValidateContainerStateTransition(newPod.Status.ContainerStatuses, oldPod.Status.ContainerStatuses, fldPath.Child("containerStatuses"), oldPod.Spec.RestartPolicy)...)
152 allErrs = append(allErrs, ValidateContainerStateTransition(newPod.Status.InitContainerStatuses, oldPod.Status.InitContainerStatuses, fldPath.Child("initContainerStatuses"), oldPod.Spec.RestartPolicy)...)
153
154- // For status update we ignore changes to pod spec.
155- newPod.Spec = oldPod.Spec
156-
157 return allErrs
158 }
159
160@@ -5287,7 +5280,6 @@ func ValidateResourceQuantityValue(resource string, value resource.Quantity, fld
161 }
162
163 // ValidateResourceQuotaUpdate tests to see if the update is legal for an end user to make.
164-// newResourceQuota is updated with fields that cannot be changed.
165 func ValidateResourceQuotaUpdate(newResourceQuota, oldResourceQuota *core.ResourceQuota) field.ErrorList {
166 allErrs := ValidateObjectMetaUpdate(&newResourceQuota.ObjectMeta, &oldResourceQuota.ObjectMeta, field.NewPath("metadata"))
167 allErrs = append(allErrs, ValidateResourceQuotaSpec(&newResourceQuota.Spec, field.NewPath("spec"))...)
168@@ -5306,12 +5298,10 @@ func ValidateResourceQuotaUpdate(newResourceQuota, oldResourceQuota *core.Resour
169 allErrs = append(allErrs, field.Invalid(fldPath, newResourceQuota.Spec.Scopes, fieldImmutableErrorMsg))
170 }
171
172- newResourceQuota.Status = oldResourceQuota.Status
173 return allErrs
174 }
175
176 // ValidateResourceQuotaStatusUpdate tests to see if the status update is legal for an end user to make.
177-// newResourceQuota is updated with fields that cannot be changed.
178 func ValidateResourceQuotaStatusUpdate(newResourceQuota, oldResourceQuota *core.ResourceQuota) field.ErrorList {
179 allErrs := ValidateObjectMetaUpdate(&newResourceQuota.ObjectMeta, &oldResourceQuota.ObjectMeta, field.NewPath("metadata"))
180 if len(newResourceQuota.ResourceVersion) == 0 {
181@@ -5329,7 +5319,6 @@ func ValidateResourceQuotaStatusUpdate(newResourceQuota, oldResourceQuota *core.
182 allErrs = append(allErrs, ValidateResourceQuotaResourceName(string(k), resPath)...)
183 allErrs = append(allErrs, ValidateResourceQuantityValue(string(k), v, resPath)...)
184 }
185- newResourceQuota.Spec = oldResourceQuota.Spec
186 return allErrs
187 }
188
189@@ -5362,19 +5351,14 @@ func validateKubeFinalizerName(stringValue string, fldPath *field.Path) field.Er
190 }
191
192 // ValidateNamespaceUpdate tests to make sure a namespace update can be applied.
193-// newNamespace is updated with fields that cannot be changed
194 func ValidateNamespaceUpdate(newNamespace *core.Namespace, oldNamespace *core.Namespace) field.ErrorList {
195 allErrs := ValidateObjectMetaUpdate(&newNamespace.ObjectMeta, &oldNamespace.ObjectMeta, field.NewPath("metadata"))
196- newNamespace.Spec.Finalizers = oldNamespace.Spec.Finalizers
197- newNamespace.Status = oldNamespace.Status
198 return allErrs
199 }
200
201-// ValidateNamespaceStatusUpdate tests to see if the update is legal for an end user to make. newNamespace is updated with fields
202-// that cannot be changed.
203+// ValidateNamespaceStatusUpdate tests to see if the update is legal for an end user to make.
204 func ValidateNamespaceStatusUpdate(newNamespace, oldNamespace *core.Namespace) field.ErrorList {
205 allErrs := ValidateObjectMetaUpdate(&newNamespace.ObjectMeta, &oldNamespace.ObjectMeta, field.NewPath("metadata"))
206- newNamespace.Spec = oldNamespace.Spec
207 if newNamespace.DeletionTimestamp.IsZero() {
208 if newNamespace.Status.Phase != core.NamespaceActive {
209 allErrs = append(allErrs, field.Invalid(field.NewPath("status", "Phase"), newNamespace.Status.Phase, "may only be 'Active' if `deletionTimestamp` is empty"))
210@@ -5388,7 +5372,6 @@ func ValidateNamespaceStatusUpdate(newNamespace, oldNamespace *core.Namespace) f
211 }
212
213 // ValidateNamespaceFinalizeUpdate tests to see if the update is legal for an end user to make.
214-// newNamespace is updated with fields that cannot be changed.
215 func ValidateNamespaceFinalizeUpdate(newNamespace, oldNamespace *core.Namespace) field.ErrorList {
216 allErrs := ValidateObjectMetaUpdate(&newNamespace.ObjectMeta, &oldNamespace.ObjectMeta, field.NewPath("metadata"))
217
218@@ -5397,7 +5380,6 @@ func ValidateNamespaceFinalizeUpdate(newNamespace, oldNamespace *core.Namespace)
219 idxPath := fldPath.Index(i)
220 allErrs = append(allErrs, validateFinalizerName(string(newNamespace.Spec.Finalizers[i]), idxPath)...)
221 }
222- newNamespace.Status = oldNamespace.Status
223 return allErrs
224 }
225
226--
2272.25.1
228
229
230From 198ac41f97e11140b634274e34f0102e33806145 Mon Sep 17 00:00:00 2001
231From: David Eads <deads@redhat.com>
232Date: Mon, 15 Feb 2021 16:55:41 -0500
233Subject: [PATCH 3/8] move secret mutation from validation to prepareforupdate
234
235---
236 pkg/apis/core/validation/validation.go | 4 ----
237 pkg/registry/core/secret/strategy.go | 6 ++++++
238 2 files changed, 6 insertions(+), 4 deletions(-)
239
240diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go
241index 3381f2a37c2..9775b268e90 100644
242--- a/src/import/pkg/apis/core/validation/validation.go
243+++ b/src/import/pkg/apis/core/validation/validation.go
244@@ -4977,10 +4977,6 @@ func ValidateSecret(secret *core.Secret) field.ErrorList {
245 func ValidateSecretUpdate(newSecret, oldSecret *core.Secret) field.ErrorList {
246 allErrs := ValidateObjectMetaUpdate(&newSecret.ObjectMeta, &oldSecret.ObjectMeta, field.NewPath("metadata"))
247
248- if len(newSecret.Type) == 0 {
249- newSecret.Type = oldSecret.Type
250- }
251-
252 allErrs = append(allErrs, ValidateImmutableField(newSecret.Type, oldSecret.Type, field.NewPath("type"))...)
253 if oldSecret.Immutable != nil && *oldSecret.Immutable {
254 if !reflect.DeepEqual(newSecret.Immutable, oldSecret.Immutable) {
255diff --git a/pkg/registry/core/secret/strategy.go b/pkg/registry/core/secret/strategy.go
256index 0d5908d8975..aad00387ac1 100644
257--- a/src/import/pkg/registry/core/secret/strategy.go
258+++ b/src/import/pkg/registry/core/secret/strategy.go
259@@ -73,6 +73,12 @@ func (strategy) AllowCreateOnUpdate() bool {
260 func (strategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
261 newSecret := obj.(*api.Secret)
262 oldSecret := old.(*api.Secret)
263+
264+ // this is weird, but consistent with what the validatedUpdate function used to do.
265+ if len(newSecret.Type) == 0 {
266+ newSecret.Type = oldSecret.Type
267+ }
268+
269 dropDisabledFields(newSecret, oldSecret)
270 }
271
272--
2732.25.1
274
275
276From 7973d58ea8fe93c2be920a766c7c5d6a4a2529e6 Mon Sep 17 00:00:00 2001
277From: David Eads <deads@redhat.com>
278Date: Mon, 15 Feb 2021 17:18:11 -0500
279Subject: [PATCH 4/8] add markers for inspected validation mutation hits
280
281---
282 pkg/apis/core/validation/validation.go | 10 +++++-----
283 .../pkg/apis/apiextensions/validation/validation.go | 2 +-
284 2 files changed, 6 insertions(+), 6 deletions(-)
285
286diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go
287index 9775b268e90..6f634db468e 100644
288--- a/src/import/pkg/apis/core/validation/validation.go
289+++ b/src/import/pkg/apis/core/validation/validation.go
290@@ -1953,7 +1953,7 @@ func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *core.PersistentVolumeCl
291 // Claims are immutable in order to enforce quota, range limits, etc. without gaming the system.
292 if len(oldPvc.Spec.VolumeName) == 0 {
293 // volumeName changes are allowed once.
294- oldPvcClone.Spec.VolumeName = newPvcClone.Spec.VolumeName
295+ oldPvcClone.Spec.VolumeName = newPvcClone.Spec.VolumeName // +k8s:verify-mutation:reason=clone
296 }
297
298 if validateStorageClassUpgrade(oldPvcClone.Annotations, newPvcClone.Annotations,
299@@ -1969,7 +1969,7 @@ func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *core.PersistentVolumeCl
300 if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) {
301 // lets make sure storage values are same.
302 if newPvc.Status.Phase == core.ClaimBound && newPvcClone.Spec.Resources.Requests != nil {
303- newPvcClone.Spec.Resources.Requests["storage"] = oldPvc.Spec.Resources.Requests["storage"]
304+ newPvcClone.Spec.Resources.Requests["storage"] = oldPvc.Spec.Resources.Requests["storage"] // +k8s:verify-mutation:reason=clone
305 }
306
307 oldSize := oldPvc.Spec.Resources.Requests["storage"]
308@@ -2317,13 +2317,13 @@ func GetVolumeMountMap(mounts []core.VolumeMount) map[string]string {
309 }
310
311 func GetVolumeDeviceMap(devices []core.VolumeDevice) map[string]string {
312- voldevices := make(map[string]string)
313+ volDevices := make(map[string]string)
314
315 for _, dev := range devices {
316- voldevices[dev.Name] = dev.DevicePath
317+ volDevices[dev.Name] = dev.DevicePath
318 }
319
320- return voldevices
321+ return volDevices
322 }
323
324 func ValidateVolumeMounts(mounts []core.VolumeMount, voldevices map[string]string, volumes map[string]core.VolumeSource, container *core.Container, fldPath *field.Path) field.ErrorList {
325diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go
326index f570dd82a4b..2bc72643c85 100644
327--- a/src/import/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go
328+++ b/src/import/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go
329@@ -1304,7 +1304,7 @@ func validateAPIApproval(newCRD, oldCRD *apiextensions.CustomResourceDefinition,
330 var oldApprovalState *apihelpers.APIApprovalState
331 if oldCRD != nil {
332 t, _ := apihelpers.GetAPIApprovalState(oldCRD.Annotations)
333- oldApprovalState = &t
334+ oldApprovalState = &t // +k8s:verify-mutation:reason=clone
335 }
336 newApprovalState, reason := apihelpers.GetAPIApprovalState(newCRD.Annotations)
337
338--
3392.25.1
340
341
342From 0b8dcbecdc093829aaccee7bf541ef8cae7f3848 Mon Sep 17 00:00:00 2001
343From: David Eads <deads@redhat.com>
344Date: Mon, 15 Feb 2021 17:33:34 -0500
345Subject: [PATCH 5/8] remove pod toleration toleration seconds mutation
346
347---
348 pkg/apis/core/validation/validation.go | 16 ++++++++--------
349 1 file changed, 8 insertions(+), 8 deletions(-)
350
351diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go
352index 6f634db468e..4226047775b 100644
353--- a/src/import/pkg/apis/core/validation/validation.go
354+++ b/src/import/pkg/apis/core/validation/validation.go
355@@ -2967,10 +2967,11 @@ func validateOnlyAddedTolerations(newTolerations []core.Toleration, oldToleratio
356 allErrs := field.ErrorList{}
357 for _, old := range oldTolerations {
358 found := false
359- old.TolerationSeconds = nil
360- for _, new := range newTolerations {
361- new.TolerationSeconds = nil
362- if reflect.DeepEqual(old, new) {
363+ oldTolerationClone := old.DeepCopy()
364+ for _, newToleration := range newTolerations {
365+ // assign to our clone before doing a deep equal so we can allow tolerationseconds to change.
366+ oldTolerationClone.TolerationSeconds = newToleration.TolerationSeconds // +k8s:verify-mutation:reason=clone
367+ if reflect.DeepEqual(*oldTolerationClone, newToleration) {
368 found = true
369 break
370 }
371@@ -3730,6 +3731,9 @@ func ValidatePodUpdate(newPod, oldPod *core.Pod) field.ErrorList {
372 allErrs = append(allErrs, field.Invalid(specPath.Child("activeDeadlineSeconds"), newPod.Spec.ActiveDeadlineSeconds, "must not update from a positive integer to nil value"))
373 }
374
375+ // Allow only additions to tolerations updates.
376+ allErrs = append(allErrs, validateOnlyAddedTolerations(newPod.Spec.Tolerations, oldPod.Spec.Tolerations, specPath.Child("tolerations"))...)
377+
378 // handle updateable fields by munging those fields prior to deep equal comparison.
379 mungedPod := *newPod
380 // munge spec.containers[*].image
381@@ -3753,10 +3757,6 @@ func ValidatePodUpdate(newPod, oldPod *core.Pod) field.ErrorList {
382 mungedPod.Spec.ActiveDeadlineSeconds = &activeDeadlineSeconds
383 }
384
385- // Allow only additions to tolerations updates.
386- mungedPod.Spec.Tolerations = oldPod.Spec.Tolerations
387- allErrs = append(allErrs, validateOnlyAddedTolerations(newPod.Spec.Tolerations, oldPod.Spec.Tolerations, specPath.Child("tolerations"))...)
388-
389 if !apiequality.Semantic.DeepEqual(mungedPod.Spec, oldPod.Spec) {
390 // This diff isn't perfect, but it's a helluva lot better an "I'm not going to tell you what the difference is".
391 //TODO: Pinpoint the specific field that causes the invalid error after we have strategic merge diff
392--
3932.25.1
394
395
396From db5696ebe654a487c0216bd0646ffb9872bac39a Mon Sep 17 00:00:00 2001
397From: David Eads <deads@redhat.com>
398Date: Mon, 15 Feb 2021 17:43:57 -0500
399Subject: [PATCH 6/8] full deepcopy on munged pod spec
400
401---
402 pkg/apis/core/validation/validation.go | 30 ++++++++++++++++----------
403 1 file changed, 19 insertions(+), 11 deletions(-)
404
405diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go
406index 4226047775b..d6eb9fe56f4 100644
407--- a/src/import/pkg/apis/core/validation/validation.go
408+++ b/src/import/pkg/apis/core/validation/validation.go
409@@ -3734,33 +3734,41 @@ func ValidatePodUpdate(newPod, oldPod *core.Pod) field.ErrorList {
410 // Allow only additions to tolerations updates.
411 allErrs = append(allErrs, validateOnlyAddedTolerations(newPod.Spec.Tolerations, oldPod.Spec.Tolerations, specPath.Child("tolerations"))...)
412
413+ // the last thing to check is pod spec equality. If the pod specs are equal, then we can simply return the errors we have
414+ // so far and save the cost of a deep copy.
415+ if apiequality.Semantic.DeepEqual(newPod.Spec, oldPod.Spec) {
416+ return allErrs
417+ }
418+
419 // handle updateable fields by munging those fields prior to deep equal comparison.
420- mungedPod := *newPod
421+ mungedPodSpec := *newPod.Spec.DeepCopy()
422 // munge spec.containers[*].image
423 var newContainers []core.Container
424- for ix, container := range mungedPod.Spec.Containers {
425- container.Image = oldPod.Spec.Containers[ix].Image
426+ for ix, container := range mungedPodSpec.Containers {
427+ container.Image = oldPod.Spec.Containers[ix].Image // +k8s:verify-mutation:reason=clone
428 newContainers = append(newContainers, container)
429 }
430- mungedPod.Spec.Containers = newContainers
431+ mungedPodSpec.Containers = newContainers
432 // munge spec.initContainers[*].image
433 var newInitContainers []core.Container
434- for ix, container := range mungedPod.Spec.InitContainers {
435- container.Image = oldPod.Spec.InitContainers[ix].Image
436+ for ix, container := range mungedPodSpec.InitContainers {
437+ container.Image = oldPod.Spec.InitContainers[ix].Image // +k8s:verify-mutation:reason=clone
438 newInitContainers = append(newInitContainers, container)
439 }
440- mungedPod.Spec.InitContainers = newInitContainers
441+ mungedPodSpec.InitContainers = newInitContainers
442 // munge spec.activeDeadlineSeconds
443- mungedPod.Spec.ActiveDeadlineSeconds = nil
444+ mungedPodSpec.ActiveDeadlineSeconds = nil
445 if oldPod.Spec.ActiveDeadlineSeconds != nil {
446 activeDeadlineSeconds := *oldPod.Spec.ActiveDeadlineSeconds
447- mungedPod.Spec.ActiveDeadlineSeconds = &activeDeadlineSeconds
448+ mungedPodSpec.ActiveDeadlineSeconds = &activeDeadlineSeconds
449 }
450+ // tolerations are checked before the deep copy, so munge those too
451+ mungedPodSpec.Tolerations = oldPod.Spec.Tolerations // +k8s:verify-mutation:reason=clone
452
453- if !apiequality.Semantic.DeepEqual(mungedPod.Spec, oldPod.Spec) {
454+ if !apiequality.Semantic.DeepEqual(mungedPodSpec, oldPod.Spec) {
455 // This diff isn't perfect, but it's a helluva lot better an "I'm not going to tell you what the difference is".
456 //TODO: Pinpoint the specific field that causes the invalid error after we have strategic merge diff
457- specDiff := diff.ObjectDiff(mungedPod.Spec, oldPod.Spec)
458+ specDiff := diff.ObjectDiff(mungedPodSpec, oldPod.Spec)
459 allErrs = append(allErrs, field.Forbidden(specPath, fmt.Sprintf("pod updates may not change fields other than `spec.containers[*].image`, `spec.initContainers[*].image`, `spec.activeDeadlineSeconds` or `spec.tolerations` (only additions to existing tolerations)\n%v", specDiff)))
460 }
461
462--
4632.25.1
464
465
466From 888666d4d5b96328f7e9d96c0946e9d7c8222f81 Mon Sep 17 00:00:00 2001
467From: David Eads <deads@redhat.com>
468Date: Wed, 17 Feb 2021 10:51:38 -0500
469Subject: [PATCH 7/8] deepcopy statefulsets
470
471---
472 pkg/apis/apps/validation/validation.go | 20 +++++++-------------
473 1 file changed, 7 insertions(+), 13 deletions(-)
474
475diff --git a/pkg/apis/apps/validation/validation.go b/pkg/apis/apps/validation/validation.go
476index 6ac73cb6b7e..03e0d2024dd 100644
477--- a/src/import/pkg/apis/apps/validation/validation.go
478+++ b/src/import/pkg/apis/apps/validation/validation.go
479@@ -144,21 +144,15 @@ func ValidateStatefulSet(statefulSet *apps.StatefulSet) field.ErrorList {
480 func ValidateStatefulSetUpdate(statefulSet, oldStatefulSet *apps.StatefulSet) field.ErrorList {
481 allErrs := apivalidation.ValidateObjectMetaUpdate(&statefulSet.ObjectMeta, &oldStatefulSet.ObjectMeta, field.NewPath("metadata"))
482
483- restoreReplicas := statefulSet.Spec.Replicas
484- statefulSet.Spec.Replicas = oldStatefulSet.Spec.Replicas
485-
486- restoreTemplate := statefulSet.Spec.Template
487- statefulSet.Spec.Template = oldStatefulSet.Spec.Template
488-
489- restoreStrategy := statefulSet.Spec.UpdateStrategy
490- statefulSet.Spec.UpdateStrategy = oldStatefulSet.Spec.UpdateStrategy
491-
492- if !apiequality.Semantic.DeepEqual(statefulSet.Spec, oldStatefulSet.Spec) {
493+ // statefulset updates aren't super common and general updates are likely to be touching spec, so we'll do this
494+ // deep copy right away. This avoids mutating our inputs
495+ newStatefulSetClone := statefulSet.DeepCopy()
496+ newStatefulSetClone.Spec.Replicas = oldStatefulSet.Spec.Replicas // +k8s:verify-mutation:reason=clone
497+ newStatefulSetClone.Spec.Template = oldStatefulSet.Spec.Template // +k8s:verify-mutation:reason=clone
498+ newStatefulSetClone.Spec.UpdateStrategy = oldStatefulSet.Spec.UpdateStrategy // +k8s:verify-mutation:reason=clone
499+ if !apiequality.Semantic.DeepEqual(newStatefulSetClone.Spec, oldStatefulSet.Spec) {
500 allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "updates to statefulset spec for fields other than 'replicas', 'template', and 'updateStrategy' are forbidden"))
501 }
502- statefulSet.Spec.Replicas = restoreReplicas
503- statefulSet.Spec.Template = restoreTemplate
504- statefulSet.Spec.UpdateStrategy = restoreStrategy
505
506 allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(statefulSet.Spec.Replicas), field.NewPath("spec", "replicas"))...)
507 return allErrs
508--
5092.25.1
510
511
512From bfbe634654ae1ac86033b69703ed78ade5d605ea Mon Sep 17 00:00:00 2001
513From: David Eads <deads@redhat.com>
514Date: Wed, 17 Mar 2021 09:12:42 -0400
515Subject: [PATCH 8/8] bazel
516
517---
518 pkg/apis/core/validation/BUILD | 1 -
519 1 file changed, 1 deletion(-)
520
521diff --git a/pkg/apis/core/validation/BUILD b/pkg/apis/core/validation/BUILD
522index 2db631180e6..00649a3a52c 100644
523--- a/src/import/pkg/apis/core/validation/BUILD
524+++ b/src/import/pkg/apis/core/validation/BUILD
525@@ -40,7 +40,6 @@ go_library(
526 "//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library",
527 "//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
528 "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
529- "//vendor/k8s.io/klog:go_default_library",
530 "//vendor/k8s.io/utils/net:go_default_library",
531 ],
532 )
533--
5342.25.1
535
diff --git a/recipes-containers/kubernetes/kubernetes/CVE-2021-25737.patch b/recipes-containers/kubernetes/kubernetes/CVE-2021-25737.patch
new file mode 100644
index 00000000..d1a97971
--- /dev/null
+++ b/recipes-containers/kubernetes/kubernetes/CVE-2021-25737.patch
@@ -0,0 +1,128 @@
1From 901e8e07e1f031456ecd7fefce965aaa05916825 Mon Sep 17 00:00:00 2001
2From: Rob Scott <robertjscott@google.com>
3Date: Fri, 9 Apr 2021 15:24:17 -0700
4Subject: [PATCH] Updating EndpointSlice validation to match Endpoints
5 validation
6
7Upstream-Status: Backport [https://github.com/kubernetes/kubernetes/commit/901e8e07e1f031456ecd7fefce965aaa05916825]
8CVE: CVE-2021-25737
9Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
10---
11 pkg/apis/core/validation/validation.go | 18 ++++++----
12 pkg/apis/discovery/validation/validation.go | 2 ++
13 .../discovery/validation/validation_test.go | 34 +++++++++++++++++--
14 3 files changed, 45 insertions(+), 9 deletions(-)
15
16diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go
17index 3daeb139d590d..c65cdd40f9061 100644
18--- a/src/import/pkg/apis/core/validation/validation.go
19+++ b/src/import/pkg/apis/core/validation/validation.go
20@@ -4014,7 +4014,7 @@ func ValidateService(service *core.Service, allowAppProtocol bool) field.ErrorLi
21 allErrs = append(allErrs, field.Invalid(idxPath, ip, msgs[i]))
22 }
23 } else {
24- allErrs = append(allErrs, validateNonSpecialIP(ip, idxPath)...)
25+ allErrs = append(allErrs, ValidateNonSpecialIP(ip, idxPath)...)
26 }
27 }
28
29@@ -5572,15 +5572,19 @@ func validateEndpointAddress(address *core.EndpointAddress, fldPath *field.Path)
30 allErrs = append(allErrs, field.Invalid(fldPath.Child("nodeName"), *address.NodeName, msg))
31 }
32 }
33- allErrs = append(allErrs, validateNonSpecialIP(address.IP, fldPath.Child("ip"))...)
34+ allErrs = append(allErrs, ValidateNonSpecialIP(address.IP, fldPath.Child("ip"))...)
35 return allErrs
36 }
37
38-func validateNonSpecialIP(ipAddress string, fldPath *field.Path) field.ErrorList {
39- // We disallow some IPs as endpoints or external-ips. Specifically,
40- // unspecified and loopback addresses are nonsensical and link-local
41- // addresses tend to be used for node-centric purposes (e.g. metadata
42- // service).
43+// ValidateNonSpecialIP is used to validate Endpoints, EndpointSlices, and
44+// external IPs. Specifically, this disallows unspecified and loopback addresses
45+// are nonsensical and link-local addresses tend to be used for node-centric
46+// purposes (e.g. metadata service).
47+//
48+// IPv6 references
49+// - https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
50+// - https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml
51+func ValidateNonSpecialIP(ipAddress string, fldPath *field.Path) field.ErrorList {
52 allErrs := field.ErrorList{}
53 ip := net.ParseIP(ipAddress)
54 if ip == nil {
55diff --git a/pkg/apis/discovery/validation/validation.go b/pkg/apis/discovery/validation/validation.go
56index 810f2ca124d57..3aa5128359d7f 100644
57--- a/src/import/pkg/apis/discovery/validation/validation.go
58+++ b/src/import/pkg/apis/discovery/validation/validation.go
59@@ -103,8 +103,10 @@ func validateEndpoints(endpoints []discovery.Endpoint, addrType discovery.Addres
60 }
61 case discovery.AddressTypeIPv4:
62 allErrs = append(allErrs, validation.IsValidIPv4Address(addressPath.Index(i), address)...)
63+ allErrs = append(allErrs, apivalidation.ValidateNonSpecialIP(address, addressPath.Index(i))...)
64 case discovery.AddressTypeIPv6:
65 allErrs = append(allErrs, validation.IsValidIPv6Address(addressPath.Index(i), address)...)
66+ allErrs = append(allErrs, apivalidation.ValidateNonSpecialIP(address, addressPath.Index(i))...)
67 case discovery.AddressTypeFQDN:
68 allErrs = append(allErrs, validation.IsFullyQualifiedDomainName(addressPath.Index(i), address)...)
69 }
70diff --git a/pkg/apis/discovery/validation/validation_test.go b/pkg/apis/discovery/validation/validation_test.go
71index 060545f93ab31..3c8a5465128a9 100644
72--- a/src/import/pkg/apis/discovery/validation/validation_test.go
73+++ b/src/import/pkg/apis/discovery/validation/validation_test.go
74@@ -390,7 +390,7 @@ func TestValidateEndpointSlice(t *testing.T) {
75 },
76 },
77 "bad-ipv4": {
78- expectedErrors: 2,
79+ expectedErrors: 3,
80 endpointSlice: &discovery.EndpointSlice{
81 ObjectMeta: standardMeta,
82 AddressType: discovery.AddressTypeIPv4,
83@@ -405,7 +405,7 @@ func TestValidateEndpointSlice(t *testing.T) {
84 },
85 },
86 "bad-ipv6": {
87- expectedErrors: 2,
88+ expectedErrors: 4,
89 endpointSlice: &discovery.EndpointSlice{
90 ObjectMeta: standardMeta,
91 AddressType: discovery.AddressTypeIPv6,
92@@ -454,6 +454,36 @@ func TestValidateEndpointSlice(t *testing.T) {
93 expectedErrors: 3,
94 endpointSlice: &discovery.EndpointSlice{},
95 },
96+ "special-ipv4": {
97+ expectedErrors: 1,
98+ endpointSlice: &discovery.EndpointSlice{
99+ ObjectMeta: standardMeta,
100+ AddressType: discovery.AddressTypeIPv4,
101+ Ports: []discovery.EndpointPort{{
102+ Name: utilpointer.StringPtr("http"),
103+ Protocol: protocolPtr(api.ProtocolTCP),
104+ }},
105+ Endpoints: []discovery.Endpoint{{
106+ Addresses: []string{"127.0.0.1"},
107+ Hostname: utilpointer.StringPtr("valid-123"),
108+ }},
109+ },
110+ },
111+ "special-ipv6": {
112+ expectedErrors: 1,
113+ endpointSlice: &discovery.EndpointSlice{
114+ ObjectMeta: standardMeta,
115+ AddressType: discovery.AddressTypeIPv6,
116+ Ports: []discovery.EndpointPort{{
117+ Name: utilpointer.StringPtr("http"),
118+ Protocol: protocolPtr(api.ProtocolTCP),
119+ }},
120+ Endpoints: []discovery.Endpoint{{
121+ Addresses: []string{"fe80::9656:d028:8652:66b6"},
122+ Hostname: utilpointer.StringPtr("valid-123"),
123+ }},
124+ },
125+ },
126 }
127
128 for name, testCase := range testCases {
diff --git a/recipes-containers/kubernetes/kubernetes_git.bb b/recipes-containers/kubernetes/kubernetes_git.bb
index 2b0bfb7a..be3d7dbe 100644
--- a/recipes-containers/kubernetes/kubernetes_git.bb
+++ b/recipes-containers/kubernetes/kubernetes_git.bb
@@ -14,6 +14,9 @@ SRC_URI = "git://github.com/kubernetes/kubernetes.git;branch=release-1.17;name=k
14 file://CVE-2020-8564.patch \ 14 file://CVE-2020-8564.patch \
15 file://CVE-2020-8565.patch \ 15 file://CVE-2020-8565.patch \
16 file://CVE-2020-8566.patch \ 16 file://CVE-2020-8566.patch \
17 file://CVE-2021-25735-pre1.patch \
18 file://CVE-2021-25735.patch \
19 file://CVE-2021-25737.patch \
17 " 20 "
18 21
19DEPENDS += "rsync-native \ 22DEPENDS += "rsync-native \