diff options
author | Vijay Anusuri <vanusuri@mvista.com> | 2023-09-27 16:18:40 +0530 |
---|---|---|
committer | Bruce Ashfield <bruce.ashfield@gmail.com> | 2023-10-02 16:16:25 +0000 |
commit | 35c723774ee06b3c1831f00a2cbf25cbeae132e1 (patch) | |
tree | 6ba591bfaf2ad614ea6f3d5661ec2f69402cdf08 /recipes-containers/kubernetes/kubernetes/CVE-2021-25735-pre1.patch | |
parent | 0dbb8593fa38ac2a04fcac04ff3e35611e849824 (diff) | |
download | meta-virtualization-35c723774ee06b3c1831f00a2cbf25cbeae132e1.tar.gz |
kubernetes: Backport fix for CVE-2021-25735 and CVE-2021-25737
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>
Diffstat (limited to 'recipes-containers/kubernetes/kubernetes/CVE-2021-25735-pre1.patch')
-rw-r--r-- | recipes-containers/kubernetes/kubernetes/CVE-2021-25735-pre1.patch | 613 |
1 files changed, 613 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 @@ | |||
1 | From e612ebfdff22e4bd27ad8345f7c82f074bfedf26 Mon Sep 17 00:00:00 2001 | ||
2 | From: wojtekt <wojtekt@google.com> | ||
3 | Date: Tue, 26 Nov 2019 13:29:26 +0100 | ||
4 | Subject: [PATCH] Immutable field and validation | ||
5 | |||
6 | Upstream-Status: Backport [https://github.com/kubernetes/kubernetes/commit/e612ebfdff22e4bd27ad8345f7c82f074bfedf26] | ||
7 | CVE: CVE-2021-25735 #Dependency Patch1 | ||
8 | Signed-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 | |||
23 | diff --git a/pkg/apis/core/types.go b/pkg/apis/core/types.go | ||
24 | index 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. | ||
53 | diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go | ||
54 | index 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 | |||
96 | diff --git a/pkg/apis/core/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go | ||
97 | index 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 | |||
334 | diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go | ||
335 | index 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: | ||
359 | diff --git a/pkg/registry/core/configmap/strategy.go b/pkg/registry/core/configmap/strategy.go | ||
360 | index 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) | ||
422 | diff --git a/pkg/registry/core/secret/strategy.go b/pkg/registry/core/secret/strategy.go | ||
423 | index 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 | } | ||
473 | diff --git a/staging/src/k8s.io/api/core/v1/types.go b/staging/src/k8s.io/api/core/v1/types.go | ||
474 | index 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. | ||
507 | diff --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 | ||
508 | index 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 | } | ||
545 | diff --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 | ||
546 | index 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) | ||
560 | diff --git a/staging/src/k8s.io/kubectl/pkg/util/hash/hash.go b/staging/src/k8s.io/kubectl/pkg/util/hash/hash.go | ||
561 | index 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 | } | ||
599 | diff --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 | ||
600 | index 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) | ||