diff options
22 files changed, 1814 insertions, 210 deletions
diff --git a/recipes-containers/containerd/containerd-opencontainers/0001-Add-build-option-GODEBUG-1.patch b/recipes-containers/containerd/containerd-opencontainers/0001-Add-build-option-GODEBUG-1.patch index 05c4f153..fec9ea63 100644 --- a/recipes-containers/containerd/containerd-opencontainers/0001-Add-build-option-GODEBUG-1.patch +++ b/recipes-containers/containerd/containerd-opencontainers/0001-Add-build-option-GODEBUG-1.patch @@ -33,7 +33,7 @@ index 4355395..4fb5d3b 100644 -GO_LDFLAGS=-ldflags '-s -w -X $(PKG)/version.Version=$(VERSION) -X $(PKG)/version.Revision=$(REVISION) -X $(PKG)/version.Package=$(PACKAGE) $(EXTRA_LDFLAGS)' -SHIM_GO_LDFLAGS=-ldflags '-s -w -X $(PKG)/version.Version=$(VERSION) -X $(PKG)/version.Revision=$(REVISION) -X $(PKG)/version.Package=$(PACKAGE) -extldflags "-static"' +GO_LDFLAGS=-ldflags '-X $(PKG)/version.Version=$(VERSION) -X $(PKG)/version.Revision=$(REVISION) -X $(PKG)/version.Package=$(PACKAGE) $(EXTRA_LDFLAGS)' -+SHIM_GO_LDFLAGS=-ldflags '-X $(PKG)/version.Version=$(VERSION) -X $(PKG)/version.Revision=$(REVISION) -X $(PKG)/version.Package=$(PACKAGE) -extldflags "-static" $(EXTRA_LDFLAGS)' ++SHIM_GO_LDFLAGS=-ldflags '-X $(PKG)/version.Version=$(VERSION) -X $(PKG)/version.Revision=$(REVISION) -X $(PKG)/version.Package=$(PACKAGE) -extldflags "-static $(EXTRA_EXTLDFLAGS)" $(EXTRA_LDFLAGS)' #Replaces ":" (*nix), ";" (windows) with newline for easy parsing GOPATHS=$(shell echo ${GOPATH} | tr ":" "\n" | tr ";" "\n") diff --git a/recipes-containers/containerd/files/0001-build-use-oe-provided-GO-and-flags.patch b/recipes-containers/containerd/containerd-opencontainers/0001-build-use-oe-provided-GO-and-flags.patch index d4d5973e..d4d5973e 100644 --- a/recipes-containers/containerd/files/0001-build-use-oe-provided-GO-and-flags.patch +++ b/recipes-containers/containerd/containerd-opencontainers/0001-build-use-oe-provided-GO-and-flags.patch diff --git a/recipes-containers/containerd/containerd-opencontainers_git.bb b/recipes-containers/containerd/containerd-opencontainers_git.bb index a52a5c35..5f697ed9 100644 --- a/recipes-containers/containerd/containerd-opencontainers_git.bb +++ b/recipes-containers/containerd/containerd-opencontainers_git.bb @@ -10,7 +10,16 @@ LIC_FILES_CHKSUM = "file://src/import/LICENSE;md5=1269f40c0d099c21a871163984590d CONTAINERD_VERSION = "v1.2.14" -EXTRA_OEMAKE += "GODEBUG=1" +# Work around internal error in gold triggered due to DWARF-5 by forcing to use bfd even with ld-is-gold: +# _/OE/lge/build/webosose/dunfell/BUILD/work/qemux86_64-webos-linux/containerd-opencontainers/v1.2.14+gitAUTOINC+3b3e9d5f62-r0/git/src/import/cmd/containerd-shim +# /OE/lge/build/webosose/dunfell/BUILD/work/qemux86_64-webos-linux/containerd-opencontainers/v1.2.14+gitAUTOINC+3b3e9d5f62-r0/recipe-sysroot-native/usr/lib/x86_64-webos-linux/go/pkg/tool/linux_amd64/link: running x86_64-webos-linux-gcc failed: exit status 1 +# /OE/lge/build/webosose/dunfell/BUILD/work/qemux86_64-webos-linux/containerd-opencontainers/v1.2.14+gitAUTOINC+3b3e9d5f62-r0/recipe-sysroot-native/usr/bin/x86_64-webos-linux/../../libexec/x86_64-webos-linux/gcc/x86_64-webos-linux/9.3.0/ld: internal error in read_header_prolog, at ../../gold/dwarf_reader.cc:1678 +# collect2: error: ld returned 1 exit status +# alternatively we can backport DWARF-5 support to binutils-2.34 used in dunfell like in: +# https://git.openembedded.org/openembedded-core/commit/?id=d07d4d739ae17787017f771dd2068fda0e836722 +EXTRA_EXTLDFLAGS = "${@bb.utils.contains('DISTRO_FEATURES', 'ld-is-gold', ' -fuse-ld=bfd ', '', d)}" + +EXTRA_OEMAKE += "GODEBUG=1 EXTRA_EXTLDFLAGS='${EXTRA_EXTLDFLAGS}'" PROVIDES += "virtual/containerd" RPROVIDES_${PN} = "virtual/containerd" diff --git a/recipes-containers/kubernetes/kubernetes/CVE-2020-8564.patch b/recipes-containers/kubernetes/kubernetes/CVE-2020-8564.patch new file mode 100644 index 00000000..9388f18d --- /dev/null +++ b/recipes-containers/kubernetes/kubernetes/CVE-2020-8564.patch @@ -0,0 +1,166 @@ +From b907f9e11892ddab1e71095e3d41bf76e63c3873 Mon Sep 17 00:00:00 2001 +From: Nikolaos Moraitis <nmoraiti@redhat.com> +Date: Fri, 11 Sep 2020 11:36:27 +0200 +Subject: [PATCH] avoid potential secret leaking while reading .dockercfg + +There are a lot of scenarios where an invalid .dockercfg file +will still contain secrets. This commit removes logging of the +contents to avoid any potential leaking and manages the actual error +by printing to the user the actual location of the invalid file. + +Signed-off-by: Nikolaos Moraitis <nmoraiti@redhat.com> + +Upstream-Status: Backport [https://github.com/kubernetes/kubernetes/commit/11793434dac97a49bfed0150b56ac63e5dc34634] +CVE: CVE-2020-8564 +Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> +--- + pkg/credentialprovider/config.go | 16 +++-- + pkg/credentialprovider/config_test.go | 93 +++++++++++++++++++++++++++ + 2 files changed, 102 insertions(+), 7 deletions(-) + +diff --git a/pkg/credentialprovider/config.go b/pkg/credentialprovider/config.go +index 377383aa903..b256bd8e7f0 100644 +--- a/src/import/pkg/credentialprovider/config.go ++++ b/src/import/pkg/credentialprovider/config.go +@@ -114,10 +114,14 @@ func ReadDockercfgFile(searchPaths []string) (cfg DockerConfig, err error) { + continue + } + cfg, err := readDockerConfigFileFromBytes(contents) +- if err == nil { +- klog.V(4).Infof("found .dockercfg at %s", absDockerConfigFileLocation) +- return cfg, nil ++ if err != nil { ++ klog.V(4).Infof("couldn't get the config from %q contents: %v", absDockerConfigFileLocation, err) ++ continue + } ++ ++ klog.V(4).Infof("found .dockercfg at %s", absDockerConfigFileLocation) ++ return cfg, nil ++ + } + return nil, fmt.Errorf("couldn't find valid .dockercfg after checking in %v", searchPaths) + } +@@ -224,8 +228,7 @@ func ReadDockerConfigFileFromUrl(url string, client *http.Client, header *http.H + + func readDockerConfigFileFromBytes(contents []byte) (cfg DockerConfig, err error) { + if err = json.Unmarshal(contents, &cfg); err != nil { +- klog.Errorf("while trying to parse blob %q: %v", contents, err) +- return nil, err ++ return nil, errors.New("error occurred while trying to unmarshal json") + } + return + } +@@ -233,8 +236,7 @@ func readDockerConfigFileFromBytes(contents []byte) (cfg DockerConfig, err error + func readDockerConfigJsonFileFromBytes(contents []byte) (cfg DockerConfig, err error) { + var cfgJson DockerConfigJson + if err = json.Unmarshal(contents, &cfgJson); err != nil { +- klog.Errorf("while trying to parse blob %q: %v", contents, err) +- return nil, err ++ return nil, errors.New("error occurred while trying to unmarshal json") + } + cfg = cfgJson.Auths + return +diff --git a/pkg/credentialprovider/config_test.go b/pkg/credentialprovider/config_test.go +index c310dc33dce..6974076984f 100644 +--- a/src/import/pkg/credentialprovider/config_test.go ++++ b/src/import/pkg/credentialprovider/config_test.go +@@ -304,3 +304,96 @@ func TestDockerConfigEntryJSONCompatibleEncode(t *testing.T) { + } + } + } ++ ++func TestReadDockerConfigFileFromBytes(t *testing.T) { ++ testCases := []struct { ++ id string ++ input []byte ++ expectedCfg DockerConfig ++ errorExpected bool ++ expectedErrorMsg string ++ }{ ++ { ++ id: "valid input, no error expected", ++ input: []byte(`{"http://foo.example.com":{"username": "foo", "password": "bar", "email": "foo@example.com"}}`), ++ expectedCfg: DockerConfig(map[string]DockerConfigEntry{ ++ "http://foo.example.com": { ++ Username: "foo", ++ Password: "bar", ++ Email: "foo@example.com", ++ }, ++ }), ++ }, ++ { ++ id: "invalid input, error expected", ++ input: []byte(`{"http://foo.example.com":{"username": "foo", "password": "bar", "email": "foo@example.com"`), ++ errorExpected: true, ++ expectedErrorMsg: "error occurred while trying to unmarshal json", ++ }, ++ } ++ ++ for _, tc := range testCases { ++ cfg, err := readDockerConfigFileFromBytes(tc.input) ++ if err != nil && !tc.errorExpected { ++ t.Fatalf("Error was not expected: %v", err) ++ } ++ if err != nil && tc.errorExpected { ++ if !reflect.DeepEqual(err.Error(), tc.expectedErrorMsg) { ++ t.Fatalf("Expected error message: `%s` got `%s`", tc.expectedErrorMsg, err.Error()) ++ } ++ } else { ++ if !reflect.DeepEqual(cfg, tc.expectedCfg) { ++ t.Fatalf("expected: %v got %v", tc.expectedCfg, cfg) ++ } ++ } ++ } ++} ++ ++func TestReadDockerConfigJSONFileFromBytes(t *testing.T) { ++ testCases := []struct { ++ id string ++ input []byte ++ expectedCfg DockerConfig ++ errorExpected bool ++ expectedErrorMsg string ++ }{ ++ { ++ id: "valid input, no error expected", ++ input: []byte(`{"auths": {"http://foo.example.com":{"username": "foo", "password": "bar", "email": "foo@example.com"}, "http://bar.example.com":{"username": "bar", "password": "baz", "email": "bar@example.com"}}}`), ++ expectedCfg: DockerConfig(map[string]DockerConfigEntry{ ++ "http://foo.example.com": { ++ Username: "foo", ++ Password: "bar", ++ Email: "foo@example.com", ++ }, ++ "http://bar.example.com": { ++ Username: "bar", ++ Password: "baz", ++ Email: "bar@example.com", ++ }, ++ }), ++ }, ++ { ++ id: "invalid input, error expected", ++ input: []byte(`{"auths": {"http://foo.example.com":{"username": "foo", "password": "bar", "email": "foo@example.com"}, "http://bar.example.com":{"username": "bar", "password": "baz", "email": "bar@example.com"`), ++ errorExpected: true, ++ expectedErrorMsg: "error occurred while trying to unmarshal json", ++ }, ++ } ++ ++ for _, tc := range testCases { ++ cfg, err := readDockerConfigJSONFileFromBytes(tc.input) ++ if err != nil && !tc.errorExpected { ++ t.Fatalf("Error was not expected: %v", err) ++ } ++ if err != nil && tc.errorExpected { ++ if !reflect.DeepEqual(err.Error(), tc.expectedErrorMsg) { ++ t.Fatalf("Expected error message: `%s` got `%s`", tc.expectedErrorMsg, err.Error()) ++ } ++ } else { ++ if !reflect.DeepEqual(cfg, tc.expectedCfg) { ++ t.Fatalf("expected: %v got %v", tc.expectedCfg, cfg) ++ } ++ } ++ } ++} +-- +2.25.1 + diff --git a/recipes-containers/kubernetes/kubernetes/CVE-2020-8565.patch b/recipes-containers/kubernetes/kubernetes/CVE-2020-8565.patch new file mode 100644 index 00000000..c3772e91 --- /dev/null +++ b/recipes-containers/kubernetes/kubernetes/CVE-2020-8565.patch @@ -0,0 +1,24 @@ +From f0f52255412cbc6834bd225a59608ebb4a0d399b Mon Sep 17 00:00:00 2001 +From: Sam Fowler <sfowler@redhat.com> +Date: Tue, 6 Oct 2020 11:10:38 +1000 +Subject: [PATCH] Mask bearer token in logs when logLevel >= 9 + +Upstream-Status: Backport [https://github.com/kubernetes/kubernetes/commit/f0f52255412cbc6834bd225a59608ebb4a0d399b] +CVE: CVE-2020-8565 +Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> +--- + staging/src/k8s.io/client-go/transport/round_trippers.go | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/staging/src/k8s.io/client-go/transport/round_trippers.go b/staging/src/k8s.io/client-go/transport/round_trippers.go +index a05208d924d3b..f4cfadbd3da8e 100644 +--- a/src/import/staging/src/k8s.io/client-go/transport/round_trippers.go ++++ b/src/import/staging/src/k8s.io/client-go/transport/round_trippers.go +@@ -340,6 +340,7 @@ func (r *requestInfo) toCurl() string { + headers := "" + for key, values := range r.RequestHeaders { + for _, value := range values { ++ value = maskValue(key, value) + headers += fmt.Sprintf(` -H %q`, fmt.Sprintf("%s: %s", key, value)) + } + } diff --git a/recipes-containers/kubernetes/kubernetes/CVE-2020-8566.patch b/recipes-containers/kubernetes/kubernetes/CVE-2020-8566.patch new file mode 100644 index 00000000..7ed812d5 --- /dev/null +++ b/recipes-containers/kubernetes/kubernetes/CVE-2020-8566.patch @@ -0,0 +1,67 @@ +From e91ec4fad3366d2dee020919f7c2a0d7b52fd3ea Mon Sep 17 00:00:00 2001 +From: Sam Fowler <sfowler@redhat.com> +Date: Fri, 2 Oct 2020 10:48:11 +1000 +Subject: [PATCH] Mask Ceph RBD adminSecrets in logs when logLevel >= 4 + +Upstream-Status: Backport [https://github.com/kubernetes/kubernetes/commit/e91ec4fad3366d2dee020919f7c2a0d7b52fd3ea] +CVE: CVE-2020-8566 +Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> +--- + pkg/volume/rbd/rbd_util.go | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/pkg/volume/rbd/rbd_util.go b/pkg/volume/rbd/rbd_util.go +index 85044e85fde..9593348de37 100644 +--- a/src/import/pkg/volume/rbd/rbd_util.go ++++ b/src/import/pkg/volume/rbd/rbd_util.go +@@ -592,9 +592,9 @@ func (util *RBDUtil) CreateImage(p *rbdVolumeProvisioner) (r *v1.RBDPersistentVo + volSz := fmt.Sprintf("%d", sz) + mon := util.kernelRBDMonitorsOpt(p.Mon) + if p.rbdMounter.imageFormat == rbdImageFormat2 { +- klog.V(4).Infof("rbd: create %s size %s format %s (features: %s) using mon %s, pool %s id %s key %s", p.rbdMounter.Image, volSz, p.rbdMounter.imageFormat, p.rbdMounter.imageFeatures, mon, p.rbdMounter.Pool, p.rbdMounter.adminId, p.rbdMounter.adminSecret) ++ klog.V(4).Infof("rbd: create %s size %s format %s (features: %s) using mon %s, pool %s id %s key <masked>", p.rbdMounter.Image, volSz, p.rbdMounter.imageFormat, p.rbdMounter.imageFeatures, mon, p.rbdMounter.Pool, p.rbdMounter.adminId) + } else { +- klog.V(4).Infof("rbd: create %s size %s format %s using mon %s, pool %s id %s key %s", p.rbdMounter.Image, volSz, p.rbdMounter.imageFormat, mon, p.rbdMounter.Pool, p.rbdMounter.adminId, p.rbdMounter.adminSecret) ++ klog.V(4).Infof("rbd: create %s size %s format %s using mon %s, pool %s id %s key <masked>", p.rbdMounter.Image, volSz, p.rbdMounter.imageFormat, mon, p.rbdMounter.Pool, p.rbdMounter.adminId) + } + args := []string{"create", p.rbdMounter.Image, "--size", volSz, "--pool", p.rbdMounter.Pool, "--id", p.rbdMounter.adminId, "-m", mon, "--key=" + p.rbdMounter.adminSecret, "--image-format", p.rbdMounter.imageFormat} + if p.rbdMounter.imageFormat == rbdImageFormat2 { +@@ -629,7 +629,7 @@ func (util *RBDUtil) DeleteImage(p *rbdVolumeDeleter) error { + } + // rbd rm. + mon := util.kernelRBDMonitorsOpt(p.rbdMounter.Mon) +- klog.V(4).Infof("rbd: rm %s using mon %s, pool %s id %s key %s", p.rbdMounter.Image, mon, p.rbdMounter.Pool, p.rbdMounter.adminId, p.rbdMounter.adminSecret) ++ klog.V(4).Infof("rbd: rm %s using mon %s, pool %s id %s key <masked>", p.rbdMounter.Image, mon, p.rbdMounter.Pool, p.rbdMounter.adminId) + output, err = p.exec.Command("rbd", + "rm", p.rbdMounter.Image, "--pool", p.rbdMounter.Pool, "--id", p.rbdMounter.adminId, "-m", mon, "--key="+p.rbdMounter.adminSecret).CombinedOutput() + if err == nil { +@@ -661,7 +661,7 @@ func (util *RBDUtil) ExpandImage(rbdExpander *rbdVolumeExpander, oldSize resourc + + // rbd resize. + mon := util.kernelRBDMonitorsOpt(rbdExpander.rbdMounter.Mon) +- klog.V(4).Infof("rbd: resize %s using mon %s, pool %s id %s key %s", rbdExpander.rbdMounter.Image, mon, rbdExpander.rbdMounter.Pool, rbdExpander.rbdMounter.adminId, rbdExpander.rbdMounter.adminSecret) ++ klog.V(4).Infof("rbd: resize %s using mon %s, pool %s id %s key <masked>", rbdExpander.rbdMounter.Image, mon, rbdExpander.rbdMounter.Pool, rbdExpander.rbdMounter.adminId) + output, err = rbdExpander.exec.Command("rbd", + "resize", rbdExpander.rbdMounter.Image, "--size", newVolSz, "--pool", rbdExpander.rbdMounter.Pool, "--id", rbdExpander.rbdMounter.adminId, "-m", mon, "--key="+rbdExpander.rbdMounter.adminSecret).CombinedOutput() + if err == nil { +@@ -703,7 +703,7 @@ func (util *RBDUtil) rbdInfo(b *rbdMounter) (int, error) { + // # image does not exist (exit=2) + // rbd: error opening image 1234: (2) No such file or directory + // +- klog.V(4).Infof("rbd: info %s using mon %s, pool %s id %s key %s", b.Image, mon, b.Pool, id, secret) ++ klog.V(4).Infof("rbd: info %s using mon %s, pool %s id %s key <masked>", b.Image, mon, b.Pool, id) + output, err = b.exec.Command("rbd", + "info", b.Image, "--pool", b.Pool, "-m", mon, "--id", id, "--key="+secret, "-k=/dev/null", "--format=json").CombinedOutput() + +@@ -766,7 +766,7 @@ func (util *RBDUtil) rbdStatus(b *rbdMounter) (bool, string, error) { + // # image does not exist (exit=2) + // rbd: error opening image kubernetes-dynamic-pvc-<UUID>: (2) No such file or directory + // +- klog.V(4).Infof("rbd: status %s using mon %s, pool %s id %s key %s", b.Image, mon, b.Pool, id, secret) ++ klog.V(4).Infof("rbd: status %s using mon %s, pool %s id %s key <masked>", b.Image, mon, b.Pool, id) + cmd, err = b.exec.Command("rbd", + "status", b.Image, "--pool", b.Pool, "-m", mon, "--id", id, "--key="+secret).CombinedOutput() + output = string(cmd) +-- +2.25.1 + 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 @@ +From e612ebfdff22e4bd27ad8345f7c82f074bfedf26 Mon Sep 17 00:00:00 2001 +From: wojtekt <wojtekt@google.com> +Date: Tue, 26 Nov 2019 13:29:26 +0100 +Subject: [PATCH] Immutable field and validation + +Upstream-Status: Backport [https://github.com/kubernetes/kubernetes/commit/e612ebfdff22e4bd27ad8345f7c82f074bfedf26] +CVE: CVE-2021-25735 #Dependency Patch1 +Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> +--- + pkg/apis/core/types.go | 12 + + pkg/apis/core/validation/validation.go | 24 +- + pkg/apis/core/validation/validation_test.go | 209 ++++++++++++++++-- + pkg/features/kube_features.go | 7 + + pkg/registry/core/configmap/strategy.go | 28 ++- + pkg/registry/core/secret/strategy.go | 17 ++ + staging/src/k8s.io/api/core/v1/types.go | 16 ++ + .../k8sdeps/transformer/hash/hash.go | 20 +- + .../k8sdeps/transformer/hash/hash_test.go | 4 +- + .../src/k8s.io/kubectl/pkg/util/hash/hash.go | 20 +- + .../k8s.io/kubectl/pkg/util/hash/hash_test.go | 4 +- + 11 files changed, 322 insertions(+), 39 deletions(-) + +diff --git a/pkg/apis/core/types.go b/pkg/apis/core/types.go +index 74d22ae973e87..c5ada193effc4 100644 +--- a/src/import/pkg/apis/core/types.go ++++ b/src/import/pkg/apis/core/types.go +@@ -4735,6 +4735,12 @@ type Secret struct { + // +optional + metav1.ObjectMeta + ++ // Immutable field, if set, ensures that data stored in the Secret cannot ++ // be updated (only object metadata can be modified). ++ // This is an alpha field enabled by ImmutableEphemeralVolumes feature gate. ++ // +optional ++ Immutable *bool ++ + // Data contains the secret data. Each key must consist of alphanumeric + // characters, '-', '_' or '.'. The serialized form of the secret data is a + // base64 encoded string, representing the arbitrary (possibly non-string) +@@ -4857,6 +4863,12 @@ type ConfigMap struct { + // +optional + metav1.ObjectMeta + ++ // Immutable field, if set, ensures that data stored in the ConfigMap cannot ++ // be updated (only object metadata can be modified). ++ // This is an alpha field enabled by ImmutableEphemeralVolumes feature gate. ++ // +optional ++ Immutable *bool ++ + // Data contains the configuration data. + // Each key must consist of alphanumeric characters, '-', '_' or '.'. + // Values with non-UTF-8 byte sequences must use the BinaryData field. +diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go +index 4ad241c745b7d..8e3cfd9d9e423 100644 +--- a/src/import/pkg/apis/core/validation/validation.go ++++ b/src/import/pkg/apis/core/validation/validation.go +@@ -5005,6 +5005,16 @@ func ValidateSecretUpdate(newSecret, oldSecret *core.Secret) field.ErrorList { + } + + allErrs = append(allErrs, ValidateImmutableField(newSecret.Type, oldSecret.Type, field.NewPath("type"))...) ++ if oldSecret.Immutable != nil && *oldSecret.Immutable { ++ if !reflect.DeepEqual(newSecret.Immutable, oldSecret.Immutable) { ++ allErrs = append(allErrs, field.Forbidden(field.NewPath("immutable"), "field is immutable when `immutable` is set")) ++ } ++ if !reflect.DeepEqual(newSecret.Data, oldSecret.Data) { ++ allErrs = append(allErrs, field.Forbidden(field.NewPath("data"), "field is immutable when `immutable` is set")) ++ } ++ // We don't validate StringData, as it was already converted back to Data ++ // before validation is happening. ++ } + + allErrs = append(allErrs, ValidateSecret(newSecret)...) + return allErrs +@@ -5051,8 +5061,20 @@ func ValidateConfigMap(cfg *core.ConfigMap) field.ErrorList { + func ValidateConfigMapUpdate(newCfg, oldCfg *core.ConfigMap) field.ErrorList { + allErrs := field.ErrorList{} + allErrs = append(allErrs, ValidateObjectMetaUpdate(&newCfg.ObjectMeta, &oldCfg.ObjectMeta, field.NewPath("metadata"))...) +- allErrs = append(allErrs, ValidateConfigMap(newCfg)...) + ++ if oldCfg.Immutable != nil && *oldCfg.Immutable { ++ if !reflect.DeepEqual(newCfg.Immutable, oldCfg.Immutable) { ++ allErrs = append(allErrs, field.Forbidden(field.NewPath("immutable"), "field is immutable when `immutable` is set")) ++ } ++ if !reflect.DeepEqual(newCfg.Data, oldCfg.Data) { ++ allErrs = append(allErrs, field.Forbidden(field.NewPath("data"), "field is immutable when `immutable` is set")) ++ } ++ if !reflect.DeepEqual(newCfg.BinaryData, oldCfg.BinaryData) { ++ allErrs = append(allErrs, field.Forbidden(field.NewPath("binaryData"), "field is immutable when `immutable` is set")) ++ } ++ } ++ ++ allErrs = append(allErrs, ValidateConfigMap(newCfg)...) + return allErrs + } + +diff --git a/pkg/apis/core/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go +index 8ba68da00fe05..de8c1d49fc196 100644 +--- a/src/import/pkg/apis/core/validation/validation_test.go ++++ b/src/import/pkg/apis/core/validation/validation_test.go +@@ -13117,6 +13117,104 @@ func TestValidateSecret(t *testing.T) { + } + } + ++func TestValidateSecretUpdate(t *testing.T) { ++ validSecret := func() core.Secret { ++ return core.Secret{ ++ ObjectMeta: metav1.ObjectMeta{ ++ Name: "foo", ++ Namespace: "bar", ++ ResourceVersion: "20", ++ }, ++ Data: map[string][]byte{ ++ "data-1": []byte("bar"), ++ }, ++ } ++ } ++ ++ falseVal := false ++ trueVal := true ++ ++ secret := validSecret() ++ immutableSecret := validSecret() ++ immutableSecret.Immutable = &trueVal ++ mutableSecret := validSecret() ++ mutableSecret.Immutable = &falseVal ++ ++ secretWithData := validSecret() ++ secretWithData.Data["data-2"] = []byte("baz") ++ immutableSecretWithData := validSecret() ++ immutableSecretWithData.Immutable = &trueVal ++ immutableSecretWithData.Data["data-2"] = []byte("baz") ++ ++ secretWithChangedData := validSecret() ++ secretWithChangedData.Data["data-1"] = []byte("foo") ++ immutableSecretWithChangedData := validSecret() ++ immutableSecretWithChangedData.Immutable = &trueVal ++ immutableSecretWithChangedData.Data["data-1"] = []byte("foo") ++ ++ tests := []struct { ++ name string ++ oldSecret core.Secret ++ newSecret core.Secret ++ valid bool ++ }{ ++ { ++ name: "mark secret immutable", ++ oldSecret: secret, ++ newSecret: immutableSecret, ++ valid: true, ++ }, ++ { ++ name: "revert immutable secret", ++ oldSecret: immutableSecret, ++ newSecret: secret, ++ valid: false, ++ }, ++ { ++ name: "makr immutable secret mutable", ++ oldSecret: immutableSecret, ++ newSecret: mutableSecret, ++ valid: false, ++ }, ++ { ++ name: "add data in secret", ++ oldSecret: secret, ++ newSecret: secretWithData, ++ valid: true, ++ }, ++ { ++ name: "add data in immutable secret", ++ oldSecret: immutableSecret, ++ newSecret: immutableSecretWithData, ++ valid: false, ++ }, ++ { ++ name: "change data in secret", ++ oldSecret: secret, ++ newSecret: secretWithChangedData, ++ valid: true, ++ }, ++ { ++ name: "change data in immutable secret", ++ oldSecret: immutableSecret, ++ newSecret: immutableSecretWithChangedData, ++ valid: false, ++ }, ++ } ++ ++ for _, tc := range tests { ++ t.Run(tc.name, func(t *testing.T) { ++ errs := ValidateSecretUpdate(&tc.newSecret, &tc.oldSecret) ++ if tc.valid && len(errs) > 0 { ++ t.Errorf("Unexpected error: %v", errs) ++ } ++ if !tc.valid && len(errs) == 0 { ++ t.Errorf("Unexpected lack of error") ++ } ++ }) ++ } ++} ++ + func TestValidateDockerConfigSecret(t *testing.T) { + validDockerSecret := func() core.Secret { + return core.Secret{ +@@ -13731,40 +13829,105 @@ func TestValidateConfigMapUpdate(t *testing.T) { + Data: data, + } + } ++ validConfigMap := func() core.ConfigMap { ++ return newConfigMap("1", "validname", "validdns", map[string]string{"key": "value"}) ++ } + +- var ( +- validConfigMap = newConfigMap("1", "validname", "validns", map[string]string{"key": "value"}) +- noVersion = newConfigMap("", "validname", "validns", map[string]string{"key": "value"}) +- ) ++ falseVal := false ++ trueVal := true ++ ++ configMap := validConfigMap() ++ immutableConfigMap := validConfigMap() ++ immutableConfigMap.Immutable = &trueVal ++ mutableConfigMap := validConfigMap() ++ mutableConfigMap.Immutable = &falseVal ++ ++ configMapWithData := validConfigMap() ++ configMapWithData.Data["key-2"] = "value-2" ++ immutableConfigMapWithData := validConfigMap() ++ immutableConfigMapWithData.Immutable = &trueVal ++ immutableConfigMapWithData.Data["key-2"] = "value-2" ++ ++ configMapWithChangedData := validConfigMap() ++ configMapWithChangedData.Data["key"] = "foo" ++ immutableConfigMapWithChangedData := validConfigMap() ++ immutableConfigMapWithChangedData.Immutable = &trueVal ++ immutableConfigMapWithChangedData.Data["key"] = "foo" ++ ++ noVersion := newConfigMap("", "validname", "validns", map[string]string{"key": "value"}) + + cases := []struct { +- name string +- newCfg core.ConfigMap +- oldCfg core.ConfigMap +- isValid bool ++ name string ++ newCfg core.ConfigMap ++ oldCfg core.ConfigMap ++ valid bool + }{ + { +- name: "valid", +- newCfg: validConfigMap, +- oldCfg: validConfigMap, +- isValid: true, ++ name: "valid", ++ newCfg: configMap, ++ oldCfg: configMap, ++ valid: true, + }, + { +- name: "invalid", +- newCfg: noVersion, +- oldCfg: validConfigMap, +- isValid: false, ++ name: "invalid", ++ newCfg: noVersion, ++ oldCfg: configMap, ++ valid: false, ++ }, ++ { ++ name: "mark configmap immutable", ++ oldCfg: configMap, ++ newCfg: immutableConfigMap, ++ valid: true, ++ }, ++ { ++ name: "revert immutable configmap", ++ oldCfg: immutableConfigMap, ++ newCfg: configMap, ++ valid: false, ++ }, ++ { ++ name: "mark immutable configmap mutable", ++ oldCfg: immutableConfigMap, ++ newCfg: mutableConfigMap, ++ valid: false, ++ }, ++ { ++ name: "add data in configmap", ++ oldCfg: configMap, ++ newCfg: configMapWithData, ++ valid: true, ++ }, ++ { ++ name: "add data in immutable configmap", ++ oldCfg: immutableConfigMap, ++ newCfg: immutableConfigMapWithData, ++ valid: false, ++ }, ++ { ++ name: "change data in configmap", ++ oldCfg: configMap, ++ newCfg: configMapWithChangedData, ++ valid: true, ++ }, ++ { ++ name: "change data in immutable configmap", ++ oldCfg: immutableConfigMap, ++ newCfg: immutableConfigMapWithChangedData, ++ valid: false, + }, + } + + for _, tc := range cases { +- errs := ValidateConfigMapUpdate(&tc.newCfg, &tc.oldCfg) +- if tc.isValid && len(errs) > 0 { +- t.Errorf("%v: unexpected error: %v", tc.name, errs) +- } +- if !tc.isValid && len(errs) == 0 { +- t.Errorf("%v: unexpected non-error", tc.name) +- } ++ t.Run(tc.name, func(t *testing.T) { ++ errs := ValidateConfigMapUpdate(&tc.newCfg, &tc.oldCfg) ++ if tc.valid && len(errs) > 0 { ++ t.Errorf("Unexpected error: %v", errs) ++ } ++ if !tc.valid && len(errs) == 0 { ++ t.Errorf("Unexpected lack of error") ++ } ++ }) + } + } + +diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go +index 309dbb2955663..00da711112d71 100644 +--- a/src/import/pkg/features/kube_features.go ++++ b/src/import/pkg/features/kube_features.go +@@ -548,6 +548,12 @@ const ( + // + // Enables topology aware service routing + ServiceTopology featuregate.Feature = "ServiceTopology" ++ ++ // owner: @wojtek-t ++ // alpha: v1.18 ++ // ++ // Enables a feature to make secrets and configmaps data immutable. ++ ImmutableEphemeralVolumes featuregate.Feature = "ImmutableEphemeralVolumes" + ) + + func init() { +@@ -634,6 +640,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS + AllowInsecureBackendProxy: {Default: true, PreRelease: featuregate.Beta}, + PodDisruptionBudget: {Default: true, PreRelease: featuregate.Beta}, + ServiceTopology: {Default: false, PreRelease: featuregate.Alpha}, ++ ImmutableEphemeralVolumes: {Default: false, PreRelease: featuregate.Alpha}, + + // inherited features from generic apiserver, relisted here to get a conflict if it is changed + // unintentionally on either side: +diff --git a/pkg/registry/core/configmap/strategy.go b/pkg/registry/core/configmap/strategy.go +index 4f8bf42e3bd60..d592c181c0c2e 100644 +--- a/src/import/pkg/registry/core/configmap/strategy.go ++++ b/src/import/pkg/registry/core/configmap/strategy.go +@@ -28,9 +28,11 @@ import ( + "k8s.io/apiserver/pkg/registry/rest" + pkgstorage "k8s.io/apiserver/pkg/storage" + "k8s.io/apiserver/pkg/storage/names" ++ utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/kubernetes/pkg/api/legacyscheme" + api "k8s.io/kubernetes/pkg/apis/core" + "k8s.io/kubernetes/pkg/apis/core/validation" ++ "k8s.io/kubernetes/pkg/features" + ) + + // strategy implements behavior for ConfigMap objects +@@ -54,7 +56,8 @@ func (strategy) NamespaceScoped() bool { + } + + func (strategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { +- _ = obj.(*api.ConfigMap) ++ configMap := obj.(*api.ConfigMap) ++ dropDisabledFields(configMap, nil) + } + + func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { +@@ -72,12 +75,9 @@ func (strategy) AllowCreateOnUpdate() bool { + } + + func (strategy) PrepareForUpdate(ctx context.Context, newObj, oldObj runtime.Object) { +- _ = oldObj.(*api.ConfigMap) +- _ = newObj.(*api.ConfigMap) +-} +- +-func (strategy) AllowUnconditionalUpdate() bool { +- return true ++ oldConfigMap := oldObj.(*api.ConfigMap) ++ newConfigMap := newObj.(*api.ConfigMap) ++ dropDisabledFields(newConfigMap, oldConfigMap) + } + + func (strategy) ValidateUpdate(ctx context.Context, newObj, oldObj runtime.Object) field.ErrorList { +@@ -86,6 +86,20 @@ func (strategy) ValidateUpdate(ctx context.Context, newObj, oldObj runtime.Objec + return validation.ValidateConfigMapUpdate(newCfg, oldCfg) + } + ++func isImmutableInUse(configMap *api.ConfigMap) bool { ++ return configMap != nil && configMap.Immutable != nil ++} ++ ++func dropDisabledFields(configMap *api.ConfigMap, oldConfigMap *api.ConfigMap) { ++ if !utilfeature.DefaultFeatureGate.Enabled(features.ImmutableEphemeralVolumes) && !isImmutableInUse(oldConfigMap) { ++ configMap.Immutable = nil ++ } ++} ++ ++func (strategy) AllowUnconditionalUpdate() bool { ++ return true ++} ++ + // GetAttrs returns labels and fields of a given object for filtering purposes. + func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) { + configMap, ok := obj.(*api.ConfigMap) +diff --git a/pkg/registry/core/secret/strategy.go b/pkg/registry/core/secret/strategy.go +index 1701805065e6c..0d5908d8975f1 100644 +--- a/src/import/pkg/registry/core/secret/strategy.go ++++ b/src/import/pkg/registry/core/secret/strategy.go +@@ -29,9 +29,11 @@ import ( + "k8s.io/apiserver/pkg/registry/rest" + pkgstorage "k8s.io/apiserver/pkg/storage" + "k8s.io/apiserver/pkg/storage/names" ++ utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/kubernetes/pkg/api/legacyscheme" + api "k8s.io/kubernetes/pkg/apis/core" + "k8s.io/kubernetes/pkg/apis/core/validation" ++ "k8s.io/kubernetes/pkg/features" + ) + + // strategy implements behavior for Secret objects +@@ -53,6 +55,8 @@ func (strategy) NamespaceScoped() bool { + } + + func (strategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { ++ secret := obj.(*api.Secret) ++ dropDisabledFields(secret, nil) + } + + func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { +@@ -67,12 +71,25 @@ func (strategy) AllowCreateOnUpdate() bool { + } + + func (strategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { ++ newSecret := obj.(*api.Secret) ++ oldSecret := old.(*api.Secret) ++ dropDisabledFields(newSecret, oldSecret) + } + + func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { + return validation.ValidateSecretUpdate(obj.(*api.Secret), old.(*api.Secret)) + } + ++func isImmutableInUse(secret *api.Secret) bool { ++ return secret != nil && secret.Immutable != nil ++} ++ ++func dropDisabledFields(secret *api.Secret, oldSecret *api.Secret) { ++ if !utilfeature.DefaultFeatureGate.Enabled(features.ImmutableEphemeralVolumes) && !isImmutableInUse(oldSecret) { ++ secret.Immutable = nil ++ } ++} ++ + func (strategy) AllowUnconditionalUpdate() bool { + return true + } +diff --git a/staging/src/k8s.io/api/core/v1/types.go b/staging/src/k8s.io/api/core/v1/types.go +index a78372aeaffa7..1e3aa51730427 100644 +--- a/src/import/staging/src/k8s.io/api/core/v1/types.go ++++ b/src/import/staging/src/k8s.io/api/core/v1/types.go +@@ -5424,6 +5424,14 @@ type Secret struct { + // +optional + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + ++ // Immutable, if set to true, ensures that data stored in the Secret cannot ++ // be updated (only object metadata can be modified). ++ // If not set to true, the field can be modified at any time. ++ // Defaulted to nil. ++ // This is an alpha field enabled by ImmutableEphemeralVolumes feature gate. ++ // +optional ++ Immutable *bool `json:"immutable,omitempty"` ++ + // Data contains the secret data. Each key must consist of alphanumeric + // characters, '-', '_' or '.'. The serialized form of the secret data is a + // base64 encoded string, representing the arbitrary (possibly non-string) +@@ -5557,6 +5565,14 @@ type ConfigMap struct { + // +optional + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + ++ // Immutable, if set to true, ensures that data stored in the ConfigMap cannot ++ // be updated (only object metadata can be modified). ++ // If not set to true, the field can be modified at any time. ++ // Defaulted to nil. ++ // This is an alpha field enabled by ImmutableEphemeralVolumes feature gate. ++ // +optional ++ Immutable *bool `json:"immutable,omitempty"` ++ + // Data contains the configuration data. + // Each key must consist of alphanumeric characters, '-', '_' or '.'. + // Values with non-UTF-8 byte sequences must use the BinaryData field. +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 +index 17e24ff3e6443..85bf1e731c3fb 100644 +--- a/src/import/staging/src/k8s.io/cli-runtime/pkg/kustomize/k8sdeps/transformer/hash/hash.go ++++ b/src/import/staging/src/k8s.io/cli-runtime/pkg/kustomize/k8sdeps/transformer/hash/hash.go +@@ -90,7 +90,14 @@ func SecretHash(sec *v1.Secret) (string, error) { + // Data, Kind, and Name are taken into account. + func encodeConfigMap(cm *v1.ConfigMap) (string, error) { + // json.Marshal sorts the keys in a stable order in the encoding +- m := map[string]interface{}{"kind": "ConfigMap", "name": cm.Name, "data": cm.Data} ++ m := map[string]interface{}{ ++ "kind": "ConfigMap", ++ "name": cm.Name, ++ "data": cm.Data, ++ } ++ if cm.Immutable != nil { ++ m["immutable"] = *cm.Immutable ++ } + if len(cm.BinaryData) > 0 { + m["binaryData"] = cm.BinaryData + } +@@ -105,7 +112,16 @@ func encodeConfigMap(cm *v1.ConfigMap) (string, error) { + // Data, Kind, Name, and Type are taken into account. + func encodeSecret(sec *v1.Secret) (string, error) { + // json.Marshal sorts the keys in a stable order in the encoding +- data, err := json.Marshal(map[string]interface{}{"kind": "Secret", "type": sec.Type, "name": sec.Name, "data": sec.Data}) ++ m := map[string]interface{}{ ++ "kind": "Secret", ++ "type": sec.Type, ++ "name": sec.Name, ++ "data": sec.Data, ++ } ++ if sec.Immutable != nil { ++ m["immutable"] = *sec.Immutable ++ } ++ data, err := json.Marshal(m) + if err != nil { + return "", err + } +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 +index 2d336f35a824e..144fe444e4cac 100644 +--- a/src/import/staging/src/k8s.io/cli-runtime/pkg/kustomize/k8sdeps/transformer/hash/hash_test.go ++++ b/src/import/staging/src/k8s.io/cli-runtime/pkg/kustomize/k8sdeps/transformer/hash/hash_test.go +@@ -178,8 +178,8 @@ not their metadata (e.g. the Data of a ConfigMap, but nothing in ObjectMeta). + obj interface{} + expect int + }{ +- {"ConfigMap", v1.ConfigMap{}, 4}, +- {"Secret", v1.Secret{}, 5}, ++ {"ConfigMap", v1.ConfigMap{}, 5}, ++ {"Secret", v1.Secret{}, 6}, + } + for _, c := range cases { + val := reflect.ValueOf(c.obj) +diff --git a/staging/src/k8s.io/kubectl/pkg/util/hash/hash.go b/staging/src/k8s.io/kubectl/pkg/util/hash/hash.go +index de0036245d2f1..1b20f384b7098 100644 +--- a/src/import/staging/src/k8s.io/kubectl/pkg/util/hash/hash.go ++++ b/src/import/staging/src/k8s.io/kubectl/pkg/util/hash/hash.go +@@ -56,7 +56,14 @@ func SecretHash(sec *v1.Secret) (string, error) { + // Data, Kind, and Name are taken into account. + func encodeConfigMap(cm *v1.ConfigMap) (string, error) { + // json.Marshal sorts the keys in a stable order in the encoding +- m := map[string]interface{}{"kind": "ConfigMap", "name": cm.Name, "data": cm.Data} ++ m := map[string]interface{}{ ++ "kind": "ConfigMap", ++ "name": cm.Name, ++ "data": cm.Data, ++ } ++ if cm.Immutable != nil { ++ m["immutable"] = *cm.Immutable ++ } + if len(cm.BinaryData) > 0 { + m["binaryData"] = cm.BinaryData + } +@@ -70,8 +77,17 @@ func encodeConfigMap(cm *v1.ConfigMap) (string, error) { + // encodeSecret encodes a Secret. + // Data, Kind, Name, and Type are taken into account. + func encodeSecret(sec *v1.Secret) (string, error) { ++ m := map[string]interface{}{ ++ "kind": "Secret", ++ "type": sec.Type, ++ "name": sec.Name, ++ "data": sec.Data, ++ } ++ if sec.Immutable != nil { ++ m["immutable"] = *sec.Immutable ++ } + // json.Marshal sorts the keys in a stable order in the encoding +- data, err := json.Marshal(map[string]interface{}{"kind": "Secret", "type": sec.Type, "name": sec.Name, "data": sec.Data}) ++ data, err := json.Marshal(m) + if err != nil { + return "", err + } +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 +index f527a98a2026c..455459c3b3df5 100644 +--- a/src/import/staging/src/k8s.io/kubectl/pkg/util/hash/hash_test.go ++++ b/src/import/staging/src/k8s.io/kubectl/pkg/util/hash/hash_test.go +@@ -164,8 +164,8 @@ not their metadata (e.g. the Data of a ConfigMap, but nothing in ObjectMeta). + obj interface{} + expect int + }{ +- {"ConfigMap", v1.ConfigMap{}, 4}, +- {"Secret", v1.Secret{}, 5}, ++ {"ConfigMap", v1.ConfigMap{}, 5}, ++ {"Secret", v1.Secret{}, 6}, + } + for _, c := range cases { + 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 @@ +From 7d4efe7ad8cf06c0c1d6092cf1f77964edb8016f Mon Sep 17 00:00:00 2001 +From: David Eads <deads@redhat.com> +Date: Fri, 29 Jan 2021 13:47:31 -0500 +Subject: [PATCH 1/8] tweak validation to avoid mutation + +Upstream-Status: Backport [https://github.com/kubernetes/kubernetes/commit/d57f0641d60b73934ebc2cdf4b6a63182217d10c] +CVE: CVE-2021-25735 +Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> +--- + pkg/apis/core/validation/validation.go | 46 +++++++++----------------- + 1 file changed, 15 insertions(+), 31 deletions(-) + +diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go +index 8e3cfd9d9e4..89e5b5811c4 100644 +--- a/src/import/pkg/apis/core/validation/validation.go ++++ b/src/import/pkg/apis/core/validation/validation.go +@@ -29,8 +29,6 @@ import ( + "unicode" + "unicode/utf8" + +- "k8s.io/klog" +- + "k8s.io/api/core/v1" + apiequality "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/resource" +@@ -4530,11 +4528,8 @@ func ValidateNodeUpdate(node, oldNode *core.Node) field.ErrorList { + addresses[address] = true + } + +- if len(oldNode.Spec.PodCIDRs) == 0 { +- // Allow the controller manager to assign a CIDR to a node if it doesn't have one. +- //this is a no op for a string slice. +- oldNode.Spec.PodCIDRs = node.Spec.PodCIDRs +- } else { ++ // Allow the controller manager to assign a CIDR to a node if it doesn't have one. ++ if len(oldNode.Spec.PodCIDRs) > 0 { + // compare the entire slice + if len(oldNode.Spec.PodCIDRs) != len(node.Spec.PodCIDRs) { + allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "podCIDRs"), "node updates may not change podCIDR except from \"\" to valid")) +@@ -4548,46 +4543,35 @@ func ValidateNodeUpdate(node, oldNode *core.Node) field.ErrorList { + } + + // Allow controller manager updating provider ID when not set +- if len(oldNode.Spec.ProviderID) == 0 { +- oldNode.Spec.ProviderID = node.Spec.ProviderID +- } else { +- if oldNode.Spec.ProviderID != node.Spec.ProviderID { +- allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "providerID"), "node updates may not change providerID except from \"\" to valid")) +- } ++ if len(oldNode.Spec.ProviderID) > 0 && oldNode.Spec.ProviderID != node.Spec.ProviderID { ++ allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "providerID"), "node updates may not change providerID except from \"\" to valid")) + } + + if node.Spec.ConfigSource != nil { + allErrs = append(allErrs, validateNodeConfigSourceSpec(node.Spec.ConfigSource, field.NewPath("spec", "configSource"))...) + } +- oldNode.Spec.ConfigSource = node.Spec.ConfigSource + if node.Status.Config != nil { + allErrs = append(allErrs, validateNodeConfigStatus(node.Status.Config, field.NewPath("status", "config"))...) + } +- oldNode.Status.Config = node.Status.Config +- +- // TODO: move reset function to its own location +- // Ignore metadata changes now that they have been tested +- oldNode.ObjectMeta = node.ObjectMeta +- // Allow users to update capacity +- oldNode.Status.Capacity = node.Status.Capacity +- // Allow users to unschedule node +- oldNode.Spec.Unschedulable = node.Spec.Unschedulable +- // Clear status +- oldNode.Status = node.Status + + // update taints + if len(node.Spec.Taints) > 0 { + allErrs = append(allErrs, validateNodeTaints(node.Spec.Taints, fldPath.Child("taints"))...) + } +- oldNode.Spec.Taints = node.Spec.Taints + +- // We made allowed changes to oldNode, and now we compare oldNode to node. Any remaining differences indicate changes to protected fields. +- // TODO: Add a 'real' error type for this error and provide print actual diffs. +- if !apiequality.Semantic.DeepEqual(oldNode, node) { +- klog.V(4).Infof("Update failed validation %#v vs %#v", oldNode, node) +- 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)")) ++ if node.Spec.DoNotUseExternalID != oldNode.Spec.DoNotUseExternalID { ++ allErrs = append(allErrs, field.Forbidden(field.NewPath("spec", "externalID"), "may not be updated")) + } + ++ // status and metadata are allowed change (barring restrictions above), so separately test spec field. ++ // spec only has a few fields, so check the ones we don't allow changing ++ // 1. PodCIDRs - immutable after first set - checked above ++ // 2. ProviderID - immutable after first set - checked above ++ // 3. Unschedulable - allowed to change ++ // 4. Taints - allowed to change ++ // 5. ConfigSource - allowed to change (and checked above) ++ // 6. DoNotUseExternalID - immutable - checked above ++ + return allErrs + } + +-- +2.25.1 + + +From 0ef8605f4535713f17ede4bcf162ad513cbf6900 Mon Sep 17 00:00:00 2001 +From: David Eads <deads@redhat.com> +Date: Mon, 15 Feb 2021 16:21:42 -0500 +Subject: [PATCH 2/8] remove unnecessary mutations in validation + +These mutations are already done in the strategy +--- + pkg/apis/core/validation/validation.go | 22 ++-------------------- + 1 file changed, 2 insertions(+), 20 deletions(-) + +diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go +index 89e5b5811c4..3381f2a37c2 100644 +--- a/src/import/pkg/apis/core/validation/validation.go ++++ b/src/import/pkg/apis/core/validation/validation.go +@@ -1874,13 +1874,11 @@ func ValidatePersistentVolumeUpdate(newPv, oldPv *core.PersistentVolume) field.E + } + + // ValidatePersistentVolumeStatusUpdate tests to see if the status update is legal for an end user to make. +-// newPv is updated with fields that cannot be changed. + func ValidatePersistentVolumeStatusUpdate(newPv, oldPv *core.PersistentVolume) field.ErrorList { + allErrs := ValidateObjectMetaUpdate(&newPv.ObjectMeta, &oldPv.ObjectMeta, field.NewPath("metadata")) + if len(newPv.ResourceVersion) == 0 { + allErrs = append(allErrs, field.Required(field.NewPath("resourceVersion"), "")) + } +- newPv.Spec = oldPv.Spec + return allErrs + } + +@@ -2026,7 +2024,6 @@ func ValidatePersistentVolumeClaimStatusUpdate(newPvc, oldPvc *core.PersistentVo + for r, qty := range newPvc.Status.Capacity { + allErrs = append(allErrs, validateBasicResource(qty, capPath.Key(string(r)))...) + } +- newPvc.Spec = oldPvc.Spec + return allErrs + } + +@@ -3795,8 +3792,7 @@ func ValidateContainerStateTransition(newStatuses, oldStatuses []core.ContainerS + return allErrs + } + +-// ValidatePodStatusUpdate tests to see if the update is legal for an end user to make. newPod is updated with fields +-// that cannot be changed. ++// ValidatePodStatusUpdate tests to see if the update is legal for an end user to make. + func ValidatePodStatusUpdate(newPod, oldPod *core.Pod) field.ErrorList { + fldPath := field.NewPath("metadata") + allErrs := ValidateObjectMetaUpdate(&newPod.ObjectMeta, &oldPod.ObjectMeta, fldPath) +@@ -3819,9 +3815,6 @@ func ValidatePodStatusUpdate(newPod, oldPod *core.Pod) field.ErrorList { + allErrs = append(allErrs, ValidateContainerStateTransition(newPod.Status.ContainerStatuses, oldPod.Status.ContainerStatuses, fldPath.Child("containerStatuses"), oldPod.Spec.RestartPolicy)...) + allErrs = append(allErrs, ValidateContainerStateTransition(newPod.Status.InitContainerStatuses, oldPod.Status.InitContainerStatuses, fldPath.Child("initContainerStatuses"), oldPod.Spec.RestartPolicy)...) + +- // For status update we ignore changes to pod spec. +- newPod.Spec = oldPod.Spec +- + return allErrs + } + +@@ -5287,7 +5280,6 @@ func ValidateResourceQuantityValue(resource string, value resource.Quantity, fld + } + + // ValidateResourceQuotaUpdate tests to see if the update is legal for an end user to make. +-// newResourceQuota is updated with fields that cannot be changed. + func ValidateResourceQuotaUpdate(newResourceQuota, oldResourceQuota *core.ResourceQuota) field.ErrorList { + allErrs := ValidateObjectMetaUpdate(&newResourceQuota.ObjectMeta, &oldResourceQuota.ObjectMeta, field.NewPath("metadata")) + allErrs = append(allErrs, ValidateResourceQuotaSpec(&newResourceQuota.Spec, field.NewPath("spec"))...) +@@ -5306,12 +5298,10 @@ func ValidateResourceQuotaUpdate(newResourceQuota, oldResourceQuota *core.Resour + allErrs = append(allErrs, field.Invalid(fldPath, newResourceQuota.Spec.Scopes, fieldImmutableErrorMsg)) + } + +- newResourceQuota.Status = oldResourceQuota.Status + return allErrs + } + + // ValidateResourceQuotaStatusUpdate tests to see if the status update is legal for an end user to make. +-// newResourceQuota is updated with fields that cannot be changed. + func ValidateResourceQuotaStatusUpdate(newResourceQuota, oldResourceQuota *core.ResourceQuota) field.ErrorList { + allErrs := ValidateObjectMetaUpdate(&newResourceQuota.ObjectMeta, &oldResourceQuota.ObjectMeta, field.NewPath("metadata")) + if len(newResourceQuota.ResourceVersion) == 0 { +@@ -5329,7 +5319,6 @@ func ValidateResourceQuotaStatusUpdate(newResourceQuota, oldResourceQuota *core. + allErrs = append(allErrs, ValidateResourceQuotaResourceName(string(k), resPath)...) + allErrs = append(allErrs, ValidateResourceQuantityValue(string(k), v, resPath)...) + } +- newResourceQuota.Spec = oldResourceQuota.Spec + return allErrs + } + +@@ -5362,19 +5351,14 @@ func validateKubeFinalizerName(stringValue string, fldPath *field.Path) field.Er + } + + // ValidateNamespaceUpdate tests to make sure a namespace update can be applied. +-// newNamespace is updated with fields that cannot be changed + func ValidateNamespaceUpdate(newNamespace *core.Namespace, oldNamespace *core.Namespace) field.ErrorList { + allErrs := ValidateObjectMetaUpdate(&newNamespace.ObjectMeta, &oldNamespace.ObjectMeta, field.NewPath("metadata")) +- newNamespace.Spec.Finalizers = oldNamespace.Spec.Finalizers +- newNamespace.Status = oldNamespace.Status + return allErrs + } + +-// ValidateNamespaceStatusUpdate tests to see if the update is legal for an end user to make. newNamespace is updated with fields +-// that cannot be changed. ++// ValidateNamespaceStatusUpdate tests to see if the update is legal for an end user to make. + func ValidateNamespaceStatusUpdate(newNamespace, oldNamespace *core.Namespace) field.ErrorList { + allErrs := ValidateObjectMetaUpdate(&newNamespace.ObjectMeta, &oldNamespace.ObjectMeta, field.NewPath("metadata")) +- newNamespace.Spec = oldNamespace.Spec + if newNamespace.DeletionTimestamp.IsZero() { + if newNamespace.Status.Phase != core.NamespaceActive { + allErrs = append(allErrs, field.Invalid(field.NewPath("status", "Phase"), newNamespace.Status.Phase, "may only be 'Active' if `deletionTimestamp` is empty")) +@@ -5388,7 +5372,6 @@ func ValidateNamespaceStatusUpdate(newNamespace, oldNamespace *core.Namespace) f + } + + // ValidateNamespaceFinalizeUpdate tests to see if the update is legal for an end user to make. +-// newNamespace is updated with fields that cannot be changed. + func ValidateNamespaceFinalizeUpdate(newNamespace, oldNamespace *core.Namespace) field.ErrorList { + allErrs := ValidateObjectMetaUpdate(&newNamespace.ObjectMeta, &oldNamespace.ObjectMeta, field.NewPath("metadata")) + +@@ -5397,7 +5380,6 @@ func ValidateNamespaceFinalizeUpdate(newNamespace, oldNamespace *core.Namespace) + idxPath := fldPath.Index(i) + allErrs = append(allErrs, validateFinalizerName(string(newNamespace.Spec.Finalizers[i]), idxPath)...) + } +- newNamespace.Status = oldNamespace.Status + return allErrs + } + +-- +2.25.1 + + +From 198ac41f97e11140b634274e34f0102e33806145 Mon Sep 17 00:00:00 2001 +From: David Eads <deads@redhat.com> +Date: Mon, 15 Feb 2021 16:55:41 -0500 +Subject: [PATCH 3/8] move secret mutation from validation to prepareforupdate + +--- + pkg/apis/core/validation/validation.go | 4 ---- + pkg/registry/core/secret/strategy.go | 6 ++++++ + 2 files changed, 6 insertions(+), 4 deletions(-) + +diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go +index 3381f2a37c2..9775b268e90 100644 +--- a/src/import/pkg/apis/core/validation/validation.go ++++ b/src/import/pkg/apis/core/validation/validation.go +@@ -4977,10 +4977,6 @@ func ValidateSecret(secret *core.Secret) field.ErrorList { + func ValidateSecretUpdate(newSecret, oldSecret *core.Secret) field.ErrorList { + allErrs := ValidateObjectMetaUpdate(&newSecret.ObjectMeta, &oldSecret.ObjectMeta, field.NewPath("metadata")) + +- if len(newSecret.Type) == 0 { +- newSecret.Type = oldSecret.Type +- } +- + allErrs = append(allErrs, ValidateImmutableField(newSecret.Type, oldSecret.Type, field.NewPath("type"))...) + if oldSecret.Immutable != nil && *oldSecret.Immutable { + if !reflect.DeepEqual(newSecret.Immutable, oldSecret.Immutable) { +diff --git a/pkg/registry/core/secret/strategy.go b/pkg/registry/core/secret/strategy.go +index 0d5908d8975..aad00387ac1 100644 +--- a/src/import/pkg/registry/core/secret/strategy.go ++++ b/src/import/pkg/registry/core/secret/strategy.go +@@ -73,6 +73,12 @@ func (strategy) AllowCreateOnUpdate() bool { + func (strategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { + newSecret := obj.(*api.Secret) + oldSecret := old.(*api.Secret) ++ ++ // this is weird, but consistent with what the validatedUpdate function used to do. ++ if len(newSecret.Type) == 0 { ++ newSecret.Type = oldSecret.Type ++ } ++ + dropDisabledFields(newSecret, oldSecret) + } + +-- +2.25.1 + + +From 7973d58ea8fe93c2be920a766c7c5d6a4a2529e6 Mon Sep 17 00:00:00 2001 +From: David Eads <deads@redhat.com> +Date: Mon, 15 Feb 2021 17:18:11 -0500 +Subject: [PATCH 4/8] add markers for inspected validation mutation hits + +--- + pkg/apis/core/validation/validation.go | 10 +++++----- + .../pkg/apis/apiextensions/validation/validation.go | 2 +- + 2 files changed, 6 insertions(+), 6 deletions(-) + +diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go +index 9775b268e90..6f634db468e 100644 +--- a/src/import/pkg/apis/core/validation/validation.go ++++ b/src/import/pkg/apis/core/validation/validation.go +@@ -1953,7 +1953,7 @@ func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *core.PersistentVolumeCl + // Claims are immutable in order to enforce quota, range limits, etc. without gaming the system. + if len(oldPvc.Spec.VolumeName) == 0 { + // volumeName changes are allowed once. +- oldPvcClone.Spec.VolumeName = newPvcClone.Spec.VolumeName ++ oldPvcClone.Spec.VolumeName = newPvcClone.Spec.VolumeName // +k8s:verify-mutation:reason=clone + } + + if validateStorageClassUpgrade(oldPvcClone.Annotations, newPvcClone.Annotations, +@@ -1969,7 +1969,7 @@ func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *core.PersistentVolumeCl + if utilfeature.DefaultFeatureGate.Enabled(features.ExpandPersistentVolumes) { + // lets make sure storage values are same. + if newPvc.Status.Phase == core.ClaimBound && newPvcClone.Spec.Resources.Requests != nil { +- newPvcClone.Spec.Resources.Requests["storage"] = oldPvc.Spec.Resources.Requests["storage"] ++ newPvcClone.Spec.Resources.Requests["storage"] = oldPvc.Spec.Resources.Requests["storage"] // +k8s:verify-mutation:reason=clone + } + + oldSize := oldPvc.Spec.Resources.Requests["storage"] +@@ -2317,13 +2317,13 @@ func GetVolumeMountMap(mounts []core.VolumeMount) map[string]string { + } + + func GetVolumeDeviceMap(devices []core.VolumeDevice) map[string]string { +- voldevices := make(map[string]string) ++ volDevices := make(map[string]string) + + for _, dev := range devices { +- voldevices[dev.Name] = dev.DevicePath ++ volDevices[dev.Name] = dev.DevicePath + } + +- return voldevices ++ return volDevices + } + + func ValidateVolumeMounts(mounts []core.VolumeMount, voldevices map[string]string, volumes map[string]core.VolumeSource, container *core.Container, fldPath *field.Path) field.ErrorList { +diff --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 +index f570dd82a4b..2bc72643c85 100644 +--- a/src/import/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go ++++ b/src/import/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go +@@ -1304,7 +1304,7 @@ func validateAPIApproval(newCRD, oldCRD *apiextensions.CustomResourceDefinition, + var oldApprovalState *apihelpers.APIApprovalState + if oldCRD != nil { + t, _ := apihelpers.GetAPIApprovalState(oldCRD.Annotations) +- oldApprovalState = &t ++ oldApprovalState = &t // +k8s:verify-mutation:reason=clone + } + newApprovalState, reason := apihelpers.GetAPIApprovalState(newCRD.Annotations) + +-- +2.25.1 + + +From 0b8dcbecdc093829aaccee7bf541ef8cae7f3848 Mon Sep 17 00:00:00 2001 +From: David Eads <deads@redhat.com> +Date: Mon, 15 Feb 2021 17:33:34 -0500 +Subject: [PATCH 5/8] remove pod toleration toleration seconds mutation + +--- + pkg/apis/core/validation/validation.go | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go +index 6f634db468e..4226047775b 100644 +--- a/src/import/pkg/apis/core/validation/validation.go ++++ b/src/import/pkg/apis/core/validation/validation.go +@@ -2967,10 +2967,11 @@ func validateOnlyAddedTolerations(newTolerations []core.Toleration, oldToleratio + allErrs := field.ErrorList{} + for _, old := range oldTolerations { + found := false +- old.TolerationSeconds = nil +- for _, new := range newTolerations { +- new.TolerationSeconds = nil +- if reflect.DeepEqual(old, new) { ++ oldTolerationClone := old.DeepCopy() ++ for _, newToleration := range newTolerations { ++ // assign to our clone before doing a deep equal so we can allow tolerationseconds to change. ++ oldTolerationClone.TolerationSeconds = newToleration.TolerationSeconds // +k8s:verify-mutation:reason=clone ++ if reflect.DeepEqual(*oldTolerationClone, newToleration) { + found = true + break + } +@@ -3730,6 +3731,9 @@ func ValidatePodUpdate(newPod, oldPod *core.Pod) field.ErrorList { + allErrs = append(allErrs, field.Invalid(specPath.Child("activeDeadlineSeconds"), newPod.Spec.ActiveDeadlineSeconds, "must not update from a positive integer to nil value")) + } + ++ // Allow only additions to tolerations updates. ++ allErrs = append(allErrs, validateOnlyAddedTolerations(newPod.Spec.Tolerations, oldPod.Spec.Tolerations, specPath.Child("tolerations"))...) ++ + // handle updateable fields by munging those fields prior to deep equal comparison. + mungedPod := *newPod + // munge spec.containers[*].image +@@ -3753,10 +3757,6 @@ func ValidatePodUpdate(newPod, oldPod *core.Pod) field.ErrorList { + mungedPod.Spec.ActiveDeadlineSeconds = &activeDeadlineSeconds + } + +- // Allow only additions to tolerations updates. +- mungedPod.Spec.Tolerations = oldPod.Spec.Tolerations +- allErrs = append(allErrs, validateOnlyAddedTolerations(newPod.Spec.Tolerations, oldPod.Spec.Tolerations, specPath.Child("tolerations"))...) +- + if !apiequality.Semantic.DeepEqual(mungedPod.Spec, oldPod.Spec) { + // This diff isn't perfect, but it's a helluva lot better an "I'm not going to tell you what the difference is". + //TODO: Pinpoint the specific field that causes the invalid error after we have strategic merge diff +-- +2.25.1 + + +From db5696ebe654a487c0216bd0646ffb9872bac39a Mon Sep 17 00:00:00 2001 +From: David Eads <deads@redhat.com> +Date: Mon, 15 Feb 2021 17:43:57 -0500 +Subject: [PATCH 6/8] full deepcopy on munged pod spec + +--- + pkg/apis/core/validation/validation.go | 30 ++++++++++++++++---------- + 1 file changed, 19 insertions(+), 11 deletions(-) + +diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go +index 4226047775b..d6eb9fe56f4 100644 +--- a/src/import/pkg/apis/core/validation/validation.go ++++ b/src/import/pkg/apis/core/validation/validation.go +@@ -3734,33 +3734,41 @@ func ValidatePodUpdate(newPod, oldPod *core.Pod) field.ErrorList { + // Allow only additions to tolerations updates. + allErrs = append(allErrs, validateOnlyAddedTolerations(newPod.Spec.Tolerations, oldPod.Spec.Tolerations, specPath.Child("tolerations"))...) + ++ // the last thing to check is pod spec equality. If the pod specs are equal, then we can simply return the errors we have ++ // so far and save the cost of a deep copy. ++ if apiequality.Semantic.DeepEqual(newPod.Spec, oldPod.Spec) { ++ return allErrs ++ } ++ + // handle updateable fields by munging those fields prior to deep equal comparison. +- mungedPod := *newPod ++ mungedPodSpec := *newPod.Spec.DeepCopy() + // munge spec.containers[*].image + var newContainers []core.Container +- for ix, container := range mungedPod.Spec.Containers { +- container.Image = oldPod.Spec.Containers[ix].Image ++ for ix, container := range mungedPodSpec.Containers { ++ container.Image = oldPod.Spec.Containers[ix].Image // +k8s:verify-mutation:reason=clone + newContainers = append(newContainers, container) + } +- mungedPod.Spec.Containers = newContainers ++ mungedPodSpec.Containers = newContainers + // munge spec.initContainers[*].image + var newInitContainers []core.Container +- for ix, container := range mungedPod.Spec.InitContainers { +- container.Image = oldPod.Spec.InitContainers[ix].Image ++ for ix, container := range mungedPodSpec.InitContainers { ++ container.Image = oldPod.Spec.InitContainers[ix].Image // +k8s:verify-mutation:reason=clone + newInitContainers = append(newInitContainers, container) + } +- mungedPod.Spec.InitContainers = newInitContainers ++ mungedPodSpec.InitContainers = newInitContainers + // munge spec.activeDeadlineSeconds +- mungedPod.Spec.ActiveDeadlineSeconds = nil ++ mungedPodSpec.ActiveDeadlineSeconds = nil + if oldPod.Spec.ActiveDeadlineSeconds != nil { + activeDeadlineSeconds := *oldPod.Spec.ActiveDeadlineSeconds +- mungedPod.Spec.ActiveDeadlineSeconds = &activeDeadlineSeconds ++ mungedPodSpec.ActiveDeadlineSeconds = &activeDeadlineSeconds + } ++ // tolerations are checked before the deep copy, so munge those too ++ mungedPodSpec.Tolerations = oldPod.Spec.Tolerations // +k8s:verify-mutation:reason=clone + +- if !apiequality.Semantic.DeepEqual(mungedPod.Spec, oldPod.Spec) { ++ if !apiequality.Semantic.DeepEqual(mungedPodSpec, oldPod.Spec) { + // This diff isn't perfect, but it's a helluva lot better an "I'm not going to tell you what the difference is". + //TODO: Pinpoint the specific field that causes the invalid error after we have strategic merge diff +- specDiff := diff.ObjectDiff(mungedPod.Spec, oldPod.Spec) ++ specDiff := diff.ObjectDiff(mungedPodSpec, oldPod.Spec) + 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))) + } + +-- +2.25.1 + + +From 888666d4d5b96328f7e9d96c0946e9d7c8222f81 Mon Sep 17 00:00:00 2001 +From: David Eads <deads@redhat.com> +Date: Wed, 17 Feb 2021 10:51:38 -0500 +Subject: [PATCH 7/8] deepcopy statefulsets + +--- + pkg/apis/apps/validation/validation.go | 20 +++++++------------- + 1 file changed, 7 insertions(+), 13 deletions(-) + +diff --git a/pkg/apis/apps/validation/validation.go b/pkg/apis/apps/validation/validation.go +index 6ac73cb6b7e..03e0d2024dd 100644 +--- a/src/import/pkg/apis/apps/validation/validation.go ++++ b/src/import/pkg/apis/apps/validation/validation.go +@@ -144,21 +144,15 @@ func ValidateStatefulSet(statefulSet *apps.StatefulSet) field.ErrorList { + func ValidateStatefulSetUpdate(statefulSet, oldStatefulSet *apps.StatefulSet) field.ErrorList { + allErrs := apivalidation.ValidateObjectMetaUpdate(&statefulSet.ObjectMeta, &oldStatefulSet.ObjectMeta, field.NewPath("metadata")) + +- restoreReplicas := statefulSet.Spec.Replicas +- statefulSet.Spec.Replicas = oldStatefulSet.Spec.Replicas +- +- restoreTemplate := statefulSet.Spec.Template +- statefulSet.Spec.Template = oldStatefulSet.Spec.Template +- +- restoreStrategy := statefulSet.Spec.UpdateStrategy +- statefulSet.Spec.UpdateStrategy = oldStatefulSet.Spec.UpdateStrategy +- +- if !apiequality.Semantic.DeepEqual(statefulSet.Spec, oldStatefulSet.Spec) { ++ // statefulset updates aren't super common and general updates are likely to be touching spec, so we'll do this ++ // deep copy right away. This avoids mutating our inputs ++ newStatefulSetClone := statefulSet.DeepCopy() ++ newStatefulSetClone.Spec.Replicas = oldStatefulSet.Spec.Replicas // +k8s:verify-mutation:reason=clone ++ newStatefulSetClone.Spec.Template = oldStatefulSet.Spec.Template // +k8s:verify-mutation:reason=clone ++ newStatefulSetClone.Spec.UpdateStrategy = oldStatefulSet.Spec.UpdateStrategy // +k8s:verify-mutation:reason=clone ++ if !apiequality.Semantic.DeepEqual(newStatefulSetClone.Spec, oldStatefulSet.Spec) { + allErrs = append(allErrs, field.Forbidden(field.NewPath("spec"), "updates to statefulset spec for fields other than 'replicas', 'template', and 'updateStrategy' are forbidden")) + } +- statefulSet.Spec.Replicas = restoreReplicas +- statefulSet.Spec.Template = restoreTemplate +- statefulSet.Spec.UpdateStrategy = restoreStrategy + + allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(statefulSet.Spec.Replicas), field.NewPath("spec", "replicas"))...) + return allErrs +-- +2.25.1 + + +From bfbe634654ae1ac86033b69703ed78ade5d605ea Mon Sep 17 00:00:00 2001 +From: David Eads <deads@redhat.com> +Date: Wed, 17 Mar 2021 09:12:42 -0400 +Subject: [PATCH 8/8] bazel + +--- + pkg/apis/core/validation/BUILD | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/pkg/apis/core/validation/BUILD b/pkg/apis/core/validation/BUILD +index 2db631180e6..00649a3a52c 100644 +--- a/src/import/pkg/apis/core/validation/BUILD ++++ b/src/import/pkg/apis/core/validation/BUILD +@@ -40,7 +40,6 @@ go_library( + "//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", + "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library", +- "//vendor/k8s.io/klog:go_default_library", + "//vendor/k8s.io/utils/net:go_default_library", + ], + ) +-- +2.25.1 + 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 @@ +From 901e8e07e1f031456ecd7fefce965aaa05916825 Mon Sep 17 00:00:00 2001 +From: Rob Scott <robertjscott@google.com> +Date: Fri, 9 Apr 2021 15:24:17 -0700 +Subject: [PATCH] Updating EndpointSlice validation to match Endpoints + validation + +Upstream-Status: Backport [https://github.com/kubernetes/kubernetes/commit/901e8e07e1f031456ecd7fefce965aaa05916825] +CVE: CVE-2021-25737 +Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> +--- + pkg/apis/core/validation/validation.go | 18 ++++++---- + pkg/apis/discovery/validation/validation.go | 2 ++ + .../discovery/validation/validation_test.go | 34 +++++++++++++++++-- + 3 files changed, 45 insertions(+), 9 deletions(-) + +diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go +index 3daeb139d590d..c65cdd40f9061 100644 +--- a/src/import/pkg/apis/core/validation/validation.go ++++ b/src/import/pkg/apis/core/validation/validation.go +@@ -4014,7 +4014,7 @@ func ValidateService(service *core.Service, allowAppProtocol bool) field.ErrorLi + allErrs = append(allErrs, field.Invalid(idxPath, ip, msgs[i])) + } + } else { +- allErrs = append(allErrs, validateNonSpecialIP(ip, idxPath)...) ++ allErrs = append(allErrs, ValidateNonSpecialIP(ip, idxPath)...) + } + } + +@@ -5572,15 +5572,19 @@ func validateEndpointAddress(address *core.EndpointAddress, fldPath *field.Path) + allErrs = append(allErrs, field.Invalid(fldPath.Child("nodeName"), *address.NodeName, msg)) + } + } +- allErrs = append(allErrs, validateNonSpecialIP(address.IP, fldPath.Child("ip"))...) ++ allErrs = append(allErrs, ValidateNonSpecialIP(address.IP, fldPath.Child("ip"))...) + return allErrs + } + +-func validateNonSpecialIP(ipAddress string, fldPath *field.Path) field.ErrorList { +- // We disallow some IPs as endpoints or external-ips. Specifically, +- // unspecified and loopback addresses are nonsensical and link-local +- // addresses tend to be used for node-centric purposes (e.g. metadata +- // service). ++// ValidateNonSpecialIP is used to validate Endpoints, EndpointSlices, and ++// external IPs. Specifically, this disallows unspecified and loopback addresses ++// are nonsensical and link-local addresses tend to be used for node-centric ++// purposes (e.g. metadata service). ++// ++// IPv6 references ++// - https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml ++// - https://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xhtml ++func ValidateNonSpecialIP(ipAddress string, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + ip := net.ParseIP(ipAddress) + if ip == nil { +diff --git a/pkg/apis/discovery/validation/validation.go b/pkg/apis/discovery/validation/validation.go +index 810f2ca124d57..3aa5128359d7f 100644 +--- a/src/import/pkg/apis/discovery/validation/validation.go ++++ b/src/import/pkg/apis/discovery/validation/validation.go +@@ -103,8 +103,10 @@ func validateEndpoints(endpoints []discovery.Endpoint, addrType discovery.Addres + } + case discovery.AddressTypeIPv4: + allErrs = append(allErrs, validation.IsValidIPv4Address(addressPath.Index(i), address)...) ++ allErrs = append(allErrs, apivalidation.ValidateNonSpecialIP(address, addressPath.Index(i))...) + case discovery.AddressTypeIPv6: + allErrs = append(allErrs, validation.IsValidIPv6Address(addressPath.Index(i), address)...) ++ allErrs = append(allErrs, apivalidation.ValidateNonSpecialIP(address, addressPath.Index(i))...) + case discovery.AddressTypeFQDN: + allErrs = append(allErrs, validation.IsFullyQualifiedDomainName(addressPath.Index(i), address)...) + } +diff --git a/pkg/apis/discovery/validation/validation_test.go b/pkg/apis/discovery/validation/validation_test.go +index 060545f93ab31..3c8a5465128a9 100644 +--- a/src/import/pkg/apis/discovery/validation/validation_test.go ++++ b/src/import/pkg/apis/discovery/validation/validation_test.go +@@ -390,7 +390,7 @@ func TestValidateEndpointSlice(t *testing.T) { + }, + }, + "bad-ipv4": { +- expectedErrors: 2, ++ expectedErrors: 3, + endpointSlice: &discovery.EndpointSlice{ + ObjectMeta: standardMeta, + AddressType: discovery.AddressTypeIPv4, +@@ -405,7 +405,7 @@ func TestValidateEndpointSlice(t *testing.T) { + }, + }, + "bad-ipv6": { +- expectedErrors: 2, ++ expectedErrors: 4, + endpointSlice: &discovery.EndpointSlice{ + ObjectMeta: standardMeta, + AddressType: discovery.AddressTypeIPv6, +@@ -454,6 +454,36 @@ func TestValidateEndpointSlice(t *testing.T) { + expectedErrors: 3, + endpointSlice: &discovery.EndpointSlice{}, + }, ++ "special-ipv4": { ++ expectedErrors: 1, ++ endpointSlice: &discovery.EndpointSlice{ ++ ObjectMeta: standardMeta, ++ AddressType: discovery.AddressTypeIPv4, ++ Ports: []discovery.EndpointPort{{ ++ Name: utilpointer.StringPtr("http"), ++ Protocol: protocolPtr(api.ProtocolTCP), ++ }}, ++ Endpoints: []discovery.Endpoint{{ ++ Addresses: []string{"127.0.0.1"}, ++ Hostname: utilpointer.StringPtr("valid-123"), ++ }}, ++ }, ++ }, ++ "special-ipv6": { ++ expectedErrors: 1, ++ endpointSlice: &discovery.EndpointSlice{ ++ ObjectMeta: standardMeta, ++ AddressType: discovery.AddressTypeIPv6, ++ Ports: []discovery.EndpointPort{{ ++ Name: utilpointer.StringPtr("http"), ++ Protocol: protocolPtr(api.ProtocolTCP), ++ }}, ++ Endpoints: []discovery.Endpoint{{ ++ Addresses: []string{"fe80::9656:d028:8652:66b6"}, ++ Hostname: utilpointer.StringPtr("valid-123"), ++ }}, ++ }, ++ }, + } + + for name, testCase := range testCases { diff --git a/recipes-containers/kubernetes/kubernetes_git.bb b/recipes-containers/kubernetes/kubernetes_git.bb index 8c286e23..be3d7dbe 100644 --- a/recipes-containers/kubernetes/kubernetes_git.bb +++ b/recipes-containers/kubernetes/kubernetes_git.bb @@ -11,6 +11,12 @@ SRCREV_kubernetes = "f45fc1861acab22eb6a4697e3fb831e85ef5ff9c" SRC_URI = "git://github.com/kubernetes/kubernetes.git;branch=release-1.17;name=kubernetes;protocol=https \ file://0001-hack-lib-golang.sh-use-CC-from-environment.patch \ file://0001-cross-don-t-build-tests-by-default.patch \ + file://CVE-2020-8564.patch \ + file://CVE-2020-8565.patch \ + file://CVE-2020-8566.patch \ + file://CVE-2021-25735-pre1.patch \ + file://CVE-2021-25735.patch \ + file://CVE-2021-25737.patch \ " DEPENDS += "rsync-native \ diff --git a/recipes-containers/lxc/files/0001-Patching-an-incoming-CVE-CVE-2022-47952.patch b/recipes-containers/lxc/files/0001-Patching-an-incoming-CVE-CVE-2022-47952.patch new file mode 100644 index 00000000..d5a02f40 --- /dev/null +++ b/recipes-containers/lxc/files/0001-Patching-an-incoming-CVE-CVE-2022-47952.patch @@ -0,0 +1,76 @@ +From 1b0469530d7a38b8f8990e114b52530d1bf7f3b8 Mon Sep 17 00:00:00 2001 +From: Maher Azzouzi <maherazz04@gmail.com> +Date: Sun, 25 Dec 2022 13:50:25 +0100 +Subject: [PATCH] Patching an incoming CVE (CVE-2022-47952) + +lxc-user-nic in lxc through 5.0.1 is installed setuid root, and may +allow local users to infer whether any file exists, even within a +protected directory tree, because "Failed to open" often indicates +that a file does not exist, whereas "does not refer to a network +namespace path" often indicates that a file exists. NOTE: this is +different from CVE-2018-6556 because the CVE-2018-6556 fix design was +based on the premise that "we will report back to the user that the +open() failed but the user has no way of knowing why it failed"; +however, in many realistic cases, there are no plausible reasons for +failing except that the file does not exist. + +PoC: +> % ls /l +> ls: cannot open directory '/l': Permission denied +> % /usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic delete lol lol /l/h/tt h h +> cmd/lxc_user_nic.c: 1096: main: Failed to open "/l/h/tt" <----- file does not exist. +> % /usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic delete lol lol /l/h/t h h +> cmd/lxc_user_nic.c: 1101: main: Path "/l/h/t" does not refer to a network namespace path <---- file exist! + +Upstream-Status: Backport from https://github.com/lxc/lxc/commit/1b0469530d7a38b8f8990e114b52530d1bf7f3b8 +CVE: CVE-2022-47952 + +Signed-off-by: MaherAzzouzi <maherazz04@gmail.com> +Acked-by: Serge Hallyn <serge@hallyn.com> +Signed-off-by: Xiangyu Chen <xiangyu.chen@windriver.com> +--- + src/lxc/cmd/lxc_user_nic.c | 15 ++++++--------- + 1 file changed, 6 insertions(+), 9 deletions(-) + +diff --git a/src/lxc/cmd/lxc_user_nic.c b/src/lxc/cmd/lxc_user_nic.c +index a91e2259d..69bc6f17d 100644 +--- a/src/lxc/cmd/lxc_user_nic.c ++++ b/src/lxc/cmd/lxc_user_nic.c +@@ -1085,20 +1085,17 @@ int main(int argc, char *argv[]) + } else if (request == LXC_USERNIC_DELETE) { + char opath[LXC_PROC_PID_FD_LEN]; + +- /* Open the path with O_PATH which will not trigger an actual +- * open(). Don't report an errno to the caller to not leak +- * information whether the path exists or not. +- * When stracing setuid is stripped so this is not a concern +- * either. +- */ ++ // Keep in mind CVE-2022-47952: It's crucial not to leak any ++ // information whether open() succeeded of failed. ++ + netns_fd = open(args.pid, O_PATH | O_CLOEXEC); + if (netns_fd < 0) { +- usernic_error("Failed to open \"%s\"\n", args.pid); ++ usernic_error("Failed while opening netns file for \"%s\"\n", args.pid); + _exit(EXIT_FAILURE); + } + + if (!fhas_fs_type(netns_fd, NSFS_MAGIC)) { +- usernic_error("Path \"%s\" does not refer to a network namespace path\n", args.pid); ++ usernic_error("Failed while opening netns file for \"%s\"\n", args.pid); + close(netns_fd); + _exit(EXIT_FAILURE); + } +@@ -1112,7 +1109,7 @@ int main(int argc, char *argv[]) + /* Now get an fd that we can use in setns() calls. */ + ret = open(opath, O_RDONLY | O_CLOEXEC); + if (ret < 0) { +- CMD_SYSERROR("Failed to open \"%s\"\n", args.pid); ++ CMD_SYSERROR("Failed while opening netns file for \"%s\"\n", args.pid); + close(netns_fd); + _exit(EXIT_FAILURE); + } +-- +2.34.1 + diff --git a/recipes-containers/lxc/files/commands-fix-check-for-seccomp-notify-support.patch b/recipes-containers/lxc/files/commands-fix-check-for-seccomp-notify-support.patch deleted file mode 100644 index 391af381..00000000 --- a/recipes-containers/lxc/files/commands-fix-check-for-seccomp-notify-support.patch +++ /dev/null @@ -1,44 +0,0 @@ -From a342b11fedb3010630de4909ca707ebdc0862060 Mon Sep 17 00:00:00 2001 -From: Eneas U de Queiroz <cotequeiroz@gmail.com> -Date: Fri, 25 Dec 2020 13:54:14 -0300 -Subject: [PATCH] commands: fix check for seccomp notify support - -Use HAVE_SECCOMP_NOTIFY instead of HAVE_DECL_SECCOMP_NOTIFY_FD. -Currently the latter will be true if the declaration is found by -configure, even if 'configure --disable-seccomp' is used. - -HAVE_SECCOMP_NOTIFY is defined in lxcseccomp.h if both HAVE_SECCOMP and -HAVE_DECL_SECCOMP_NOTIFY_FD are true, which is the correct behavior. - -Upstream-status: submitted https://github.com/lxc/lxc/pull/3623 - -Signed-off-by: Eneas U de Queiroz <cotequeiroz@gmail.com> ---- - src/lxc/commands.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/lxc/commands.c b/src/lxc/commands.c -index a9a03ca2c..37d1abcef 100644 ---- a/src/lxc/commands.c -+++ b/src/lxc/commands.c -@@ -501,7 +501,7 @@ static int lxc_cmd_get_devpts_fd_callback(int fd, struct lxc_cmd_req *req, - - int lxc_cmd_get_seccomp_notify_fd(const char *name, const char *lxcpath) - { --#if HAVE_DECL_SECCOMP_NOTIFY_FD -+#ifdef HAVE_SECCOMP_NOTIFY - int ret, stopped; - struct lxc_cmd_rr cmd = { - .req = { -@@ -526,7 +526,7 @@ static int lxc_cmd_get_seccomp_notify_fd_callback(int fd, struct lxc_cmd_req *re - struct lxc_handler *handler, - struct lxc_epoll_descr *descr) - { --#if HAVE_DECL_SECCOMP_NOTIFY_FD -+#ifdef HAVE_SECCOMP_NOTIFY - struct lxc_cmd_rsp rsp = { - .ret = 0, - }; --- -2.17.1 - diff --git a/recipes-containers/lxc/files/configure-skip-libseccomp-tests-if-it-is-disabled.patch b/recipes-containers/lxc/files/configure-skip-libseccomp-tests-if-it-is-disabled.patch deleted file mode 100644 index 43c91bab..00000000 --- a/recipes-containers/lxc/files/configure-skip-libseccomp-tests-if-it-is-disabled.patch +++ /dev/null @@ -1,53 +0,0 @@ -From 67cd8bde2d46983df8fa9f647e9fc0b96370ec29 Mon Sep 17 00:00:00 2001 -From: Eneas U de Queiroz <cotequeiroz@gmail.com> -Date: Sat, 16 Jan 2021 13:54:07 -0300 -Subject: [PATCH] configure: skip libseccomp tests if it is disabled - -Move the block checking for libseccomp api compatibility inside -AM_COND_IF([ENABLE_SECCOMP] ... ). - -Upstream-Status: submitted [https://github.com/lxc/lxc/pull/3623] - -Signed-off-by: Eneas U de Queiroz <cotequeiroz@gmail.com> ---- - configure.ac | 17 ++++++++--------- - 1 file changed, 8 insertions(+), 9 deletions(-) - -diff --git a/configure.ac b/configure.ac -index f58487f5d..ce6363136 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -312,6 +312,14 @@ AM_COND_IF([ENABLE_SECCOMP], - AC_CHECK_LIB([seccomp], [seccomp_init],[],[AC_MSG_ERROR([You must install the seccomp development package in order to compile lxc])]) - AC_SUBST([SECCOMP_LIBS], [-lseccomp]) - ]) -+ # HAVE_SCMP_FILTER_CTX=1 will tell us we have libseccomp api >= 1.0.0 -+ OLD_CFLAGS="$CFLAGS" -+ CFLAGS="$CFLAGS $SECCOMP_CFLAGS" -+ AC_CHECK_TYPES([scmp_filter_ctx], [], [], [[#include <seccomp.h>]]) -+ AC_CHECK_DECLS([seccomp_notify_fd], [], [], [[#include <seccomp.h>]]) -+ AC_CHECK_TYPES([struct seccomp_notif_sizes], [], [], [[#include <seccomp.h>]]) -+ AC_CHECK_DECLS([seccomp_syscall_resolve_name_arch], [], [], [[#include <seccomp.h>]]) -+ CFLAGS="$OLD_CFLAGS" - ]) - - AC_MSG_CHECKING(for static libcap) -@@ -359,15 +367,6 @@ AM_COND_IF([ENABLE_CAP], - AC_CHECK_LIB(cap,cap_get_file, AC_DEFINE(LIBCAP_SUPPORTS_FILE_CAPABILITIES,1,[Have cap_get_file]),[],[]) - AC_SUBST([CAP_LIBS], [-lcap])]) - --# HAVE_SCMP_FILTER_CTX=1 will tell us we have libseccomp api >= 1.0.0 --OLD_CFLAGS="$CFLAGS" --CFLAGS="$CFLAGS $SECCOMP_CFLAGS" --AC_CHECK_TYPES([scmp_filter_ctx], [], [], [[#include <seccomp.h>]]) --AC_CHECK_DECLS([seccomp_notify_fd], [], [], [[#include <seccomp.h>]]) --AC_CHECK_TYPES([struct seccomp_notif_sizes], [], [], [[#include <seccomp.h>]]) --AC_CHECK_DECLS([seccomp_syscall_resolve_name_arch], [], [], [[#include <seccomp.h>]]) --CFLAGS="$OLD_CFLAGS" -- - AC_CHECK_HEADERS([linux/bpf.h], [ - AC_CHECK_TYPES([struct bpf_cgroup_dev_ctx], [], [], [[#include <linux/bpf.h>]]) - ], [], []) --- -2.17.1 - diff --git a/recipes-containers/lxc/files/templates-use-curl-instead-of-wget.patch b/recipes-containers/lxc/files/templates-use-curl-instead-of-wget.patch index 156df82f..f06e5969 100644 --- a/recipes-containers/lxc/files/templates-use-curl-instead-of-wget.patch +++ b/recipes-containers/lxc/files/templates-use-curl-instead-of-wget.patch @@ -1,4 +1,4 @@ -From 07890dd8ffdcd08b7be1ddbd9f56ac55482c76bb Mon Sep 17 00:00:00 2001 +From 1db2db7783bd7ec2aa1da86e640019891634c659 Mon Sep 17 00:00:00 2001 From: Joakim Roubert <joakimr@axis.com> Date: Fri, 16 Aug 2019 07:52:48 +0200 Subject: [PATCH] Use curl instead of wget @@ -7,16 +7,16 @@ When curl's MIT license is preferable to wget's GPLv3. Change-Id: I4684ae7569704514fdcc63e0655c556efcaf44f8 Signed-off-by: Joakim Roubert <joakimr@axis.com> - +Signed-off-by: Yanfei Xu <yanfei.xu@windriver.com> --- templates/lxc-download.in | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) -diff --git a/templates/lxc-download.in b/templates/lxc-download.in -index d7e6128..8a4b567 100644 ---- a/templates/lxc-download.in -+++ b/templates/lxc-download.in -@@ -74,9 +74,9 @@ cleanup() { +Index: git/templates/lxc-download.in +=================================================================== +--- git.orig/templates/lxc-download.in ++++ git/templates/lxc-download.in +@@ -59,9 +59,9 @@ fi } @@ -28,18 +28,19 @@ index d7e6128..8a4b567 100644 return 0 fi done -@@ -85,8 +85,8 @@ wget_wrapper() { +@@ -70,8 +70,9 @@ } download_file() { -- if ! wget_wrapper -T 30 -q "https://${DOWNLOAD_SERVER}/$1" -O "$2" >/dev/null 2>&1; then -- if ! wget_wrapper -T 30 -q "http://${DOWNLOAD_SERVER}/$1" -O "$2" >/dev/null 2>&1; then -+ if ! curl_wrapper -m 30 -s "https://${DOWNLOAD_SERVER}/$1" -o "$2" >/dev/null 2>&1; then -+ if ! curl_wrapper -m 30 -s "http://${DOWNLOAD_SERVER}/$1" -o "$2" >/dev/null 2>&1; then - if [ "$3" = "noexit" ]; then - return 1 - else -@@ -271,7 +271,7 @@ while :; do +- if ! wget_wrapper --user-agent="lxc/@PACKAGE_VERSION@ compat:${DOWNLOAD_COMPAT_LEVEL}" -T 30 -q "https://${DOWNLOAD_SERVER}/$1" -O "$2" >/dev/null 2>&1; then +- if [ "$3" = "noexit" ]; then ++ if ! curl_wrapper --user-agent="lxc/@PACKAGE_VERSION@ compat:${DOWNLOAD_COMPAT_LEVEL}" -m 30 -s "https://${DOWNLOAD_SERVER}/$1" -o "$2" >/dev/null 2>&1; then ++ if ! curl_wrapper --user-agent="lxc/@PACKAGE_VERSION@ compat:${DOWNLOAD_COMPAT_LEVEL}" -m 30 -s "http://${DOWNLOAD_SERVER}/$1" -o "$2" >/dev/null 2>&1; then ++ if [ "$3" = "noexit" ]; then + return 1 + else + echo "ERROR: Failed to download https://${DOWNLOAD_SERVER}/$1" 1>&2 +@@ -176,7 +177,7 @@ done # Check for required binaries diff --git a/recipes-containers/lxc/files/tests-add-no-validate-when-using-download-template.patch b/recipes-containers/lxc/files/tests-add-no-validate-when-using-download-template.patch deleted file mode 100644 index 8caeb2ba..00000000 --- a/recipes-containers/lxc/files/tests-add-no-validate-when-using-download-template.patch +++ /dev/null @@ -1,85 +0,0 @@ -From 1c2506434e744d8c6a86e42c9d8bae4cde7553f6 Mon Sep 17 00:00:00 2001 -From: Mark Asselstine <mark.asselstine@windriver.com> -Date: Thu, 31 May 2018 15:14:26 -0400 -Subject: [PATCH] tests: add '--no-validate' when using download template - -We are usually running the ptests with core-image-minimal which has no -mechanism to validate the downloads. Validation isn't really of -interest to this test at any rate so simply add '--no-validate' to -avoid failing due to no GPG validation. - -Signed-off-by: Mark Asselstine <mark.asselstine@windriver.com> - ---- - src/tests/lxc-test-apparmor-mount | 2 +- - src/tests/lxc-test-autostart | 2 +- - src/tests/lxc-test-no-new-privs | 2 +- - src/tests/lxc-test-unpriv | 2 +- - src/tests/lxc-test-usernic.in | 2 +- - 5 files changed, 5 insertions(+), 5 deletions(-) - -diff --git a/src/tests/lxc-test-apparmor-mount b/src/tests/lxc-test-apparmor-mount -index d21c948..9e1969b 100755 ---- a/src/tests/lxc-test-apparmor-mount -+++ b/src/tests/lxc-test-apparmor-mount -@@ -169,7 +169,7 @@ if [ -f /etc/lsb-release ]; then - done - fi - --run_cmd lxc-create -t download -n $cname -- -d ubuntu -r $release -a $ARCH -+run_cmd lxc-create -t download -n $cname -- --no-validate -d ubuntu -r $release -a $ARCH - - echo "test default confined container" - run_cmd lxc-start -n $cname -d -lDEBUG -o "$logfile" -diff --git a/src/tests/lxc-test-autostart b/src/tests/lxc-test-autostart -index e5b651b..d15b79b 100755 ---- a/src/tests/lxc-test-autostart -+++ b/src/tests/lxc-test-autostart -@@ -55,7 +55,7 @@ if [ -f /etc/lsb-release ]; then - done - fi - --lxc-create -t download -n $CONTAINER_NAME -B dir -- -d ubuntu -r $release -a $ARCH -+lxc-create -t download -n $CONTAINER_NAME -B dir -- --no-validate -d ubuntu -r $release -a $ARCH - CONTAINER_PATH=$(dirname $(lxc-info -n $CONTAINER_NAME -c lxc.rootfs.path -H) | sed -e 's/dir://') - cp $CONTAINER_PATH/config $CONTAINER_PATH/config.bak - -diff --git a/src/tests/lxc-test-no-new-privs b/src/tests/lxc-test-no-new-privs -index 8642992..e72bdf0 100755 ---- a/src/tests/lxc-test-no-new-privs -+++ b/src/tests/lxc-test-no-new-privs -@@ -47,7 +47,7 @@ if type dpkg >/dev/null 2>&1; then - ARCH=$(dpkg --print-architecture) - fi - --lxc-create -t download -n c1 -- -d ubuntu -r xenial -a $ARCH -+lxc-create -t download -n c1 -- --no-validate -d ubuntu -r xenial -a $ARCH - echo "lxc.no_new_privs = 1" >> /var/lib/lxc/c1/config - - lxc-start -n c1 -diff --git a/src/tests/lxc-test-unpriv b/src/tests/lxc-test-unpriv -index 16ff12d..0958d48 100755 ---- a/src/tests/lxc-test-unpriv -+++ b/src/tests/lxc-test-unpriv -@@ -173,7 +173,7 @@ run_cmd mkdir -p $HDIR/.cache/lxc - cp -R /var/cache/lxc/download $HDIR/.cache/lxc && \ - chown -R $TUSER: $HDIR/.cache/lxc - --run_cmd lxc-create -t download -n c1 -- -d ubuntu -r $release -a $ARCH -+run_cmd lxc-create -t download -n c1 -- --no-validate -d ubuntu -r $release -a $ARCH - - # Make sure we can start it - twice - -diff --git a/src/tests/lxc-test-usernic.in b/src/tests/lxc-test-usernic.in -index 3e35008..f489286 100755 ---- a/src/tests/lxc-test-usernic.in -+++ b/src/tests/lxc-test-usernic.in -@@ -146,7 +146,7 @@ if [ -f /etc/lsb-release ]; then - fi - - # Create three containers --run_cmd "lxc-create -t download -n b1 -- -d ubuntu -r $release -a $ARCH" -+run_cmd "lxc-create -t download -n b1 -- --no-validate -d ubuntu -r $release -a $ARCH" - run_cmd "lxc-start -n b1 -d" - p1=$(run_cmd "lxc-info -n b1 -p -H") - diff --git a/recipes-containers/lxc/lxc_4.0.6.bb b/recipes-containers/lxc/lxc_git.bb index c9bf3d09..29fcb04a 100644 --- a/recipes-containers/lxc/lxc_4.0.6.bb +++ b/recipes-containers/lxc/lxc_git.bb @@ -36,7 +36,7 @@ RDEPENDS_${PN}-ptest += "file make gmp nettle gnutls bash libgcc" RDEPENDS_${PN}-networking += "iptables" -SRC_URI = "http://linuxcontainers.org/downloads/${BPN}/${BPN}-${PV}.tar.gz \ +SRC_URI = "git://github.com/lxc/lxc.git;branch=stable-4.0 \ file://lxc-1.0.0-disable-udhcp-from-busybox-template.patch \ file://run-ptest \ file://lxc-fix-B-S.patch \ @@ -46,19 +46,15 @@ SRC_URI = "http://linuxcontainers.org/downloads/${BPN}/${BPN}-${PV}.tar.gz \ file://template-make-busybox-template-compatible-with-core-.patch \ file://templates-use-curl-instead-of-wget.patch \ file://tests-our-init-is-not-busybox.patch \ - file://tests-add-no-validate-when-using-download-template.patch \ file://dnsmasq.conf \ file://lxc-net \ - file://configure-skip-libseccomp-tests-if-it-is-disabled.patch \ - file://commands-fix-check-for-seccomp-notify-support.patch \ + file://0001-Patching-an-incoming-CVE-CVE-2022-47952.patch \ " -SRC_URI[md5sum] = "732571c7cb4ab845068afb227bf35256" -SRC_URI[sha256sum] = "9165dabc0bb6ef7f2fda2009aee90b20fbefe77ed8008347e9f06048eba1e463" +SRCREV = "5ba5725cb4a210c25707beeca64fde5f561d1c71" +PV = "4.0.12+git${SRCPV}" - - -S = "${WORKDIR}/${BPN}-${PV}" +S = "${WORKDIR}/git" # Let's not configure for the host distro. # @@ -74,6 +70,7 @@ EXTRA_OECONF += "--enable-log-src-basename --disable-werror" PACKAGECONFIG ??= "templates \ ${@bb.utils.contains('DISTRO_FEATURES', 'systemd', 'systemd', '', d)} \ ${@bb.utils.contains('DISTRO_FEATURES', 'selinux', 'selinux', '', d)} \ + ${@bb.utils.contains('DISTRO_FEATURES', 'seccomp', 'seccomp', '', d)} \ " PACKAGECONFIG[doc] = "--enable-doc --enable-api-docs,--disable-doc --disable-api-docs,," PACKAGECONFIG[rpath] = "--enable-rpath,--disable-rpath,," diff --git a/recipes-extended/libvirt/libvirt/CVE-2021-3667.patch b/recipes-extended/libvirt/libvirt/CVE-2021-3667.patch new file mode 100644 index 00000000..b99c5da0 --- /dev/null +++ b/recipes-extended/libvirt/libvirt/CVE-2021-3667.patch @@ -0,0 +1,32 @@ +From 494fc57f60af732a72b02c984527878ea7a016dc Mon Sep 17 00:00:00 2001 +From: Hitendra Prajapati <hprajapati@mvista.com> +Date: Mon, 20 Jun 2022 12:40:39 +0530 +Subject: [PATCH] CVE-2021-3667 + +Upstream-Status: Backport [https://gitlab.com/libvirt/libvirt/-/commit/447f69dec47e1b0bd15ecd7cd49a9fd3b050fb87] +CVE: CVE-2021-3667 +Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> + +--- + src/storage/storage_driver.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c +index 7e59335..1f1225d 100644 +--- a/src/storage/storage_driver.c ++++ b/src/storage/storage_driver.c +@@ -1741,8 +1741,10 @@ storagePoolLookupByTargetPath(virConnectPtr conn, + storagePoolLookupByTargetPathCallback, + cleanpath))) { + def = virStoragePoolObjGetDef(obj); +- if (virStoragePoolLookupByTargetPathEnsureACL(conn, def) < 0) ++ if (virStoragePoolLookupByTargetPathEnsureACL(conn, def) < 0) { ++ virStoragePoolObjEndAPI(&obj); + return NULL; ++ } + + pool = virGetStoragePool(conn, def->name, def->uuid, NULL, NULL); + virStoragePoolObjEndAPI(&obj); +-- +2.25.1 + diff --git a/recipes-extended/libvirt/libvirt/CVE-2021-3975.patch b/recipes-extended/libvirt/libvirt/CVE-2021-3975.patch new file mode 100644 index 00000000..1d69d526 --- /dev/null +++ b/recipes-extended/libvirt/libvirt/CVE-2021-3975.patch @@ -0,0 +1,44 @@ +From ffeb79e085a63f9917fc52a809a69e31e5cfd9b6 Mon Sep 17 00:00:00 2001 +From: Hitendra Prajapati <hprajapati@mvista.com> +Date: Tue, 13 Sep 2022 10:35:43 +0530 +Subject: [PATCH] CVE-2021-3975 + +Upstream-Status: Backport [https://github.com/libvirt/libvirt/commit/1ac703a7d0789e46833f4013a3876c2e3af18ec7] +CVE: CVE-2021-3975 +Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> + +qemu: Add missing lock in qemuProcessHandleMonitorEOF + +qemuMonitorUnregister will be called in multiple threads (e.g. threads +in rpc worker pool and the vm event thread). In some cases, it isn't +protected by the monitor lock, which may lead to call g_source_unref +more than one time and a use-after-free problem eventually. + +Add the missing lock in qemuProcessHandleMonitorEOF (which is the only +position missing lock of monitor I found). + +Suggested-by: Michal Privoznik <mprivozn@redhat.com> +Signed-off-by: Peng Liang <liangpeng10@huawei.com> +Signed-off-by: Michal Privoznik <mprivozn@redhat.com> +Reviewed-by: Michal Privoznik <mprivozn@redhat.com> +--- + src/qemu/qemu_process.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c +index d903505..0d6f14c 100644 +--- a/src/qemu/qemu_process.c ++++ b/src/qemu/qemu_process.c +@@ -313,7 +313,9 @@ qemuProcessHandleMonitorEOF(qemuMonitorPtr mon, + /* We don't want this EOF handler to be called over and over while the + * thread is waiting for a job. + */ ++ virObjectLock(mon); + qemuMonitorUnregister(mon); ++ virObjectUnlock(mon); + + /* We don't want any cleanup from EOF handler (or any other + * thread) to enter qemu namespace. */ +-- +2.25.1 + diff --git a/recipes-extended/libvirt/libvirt/CVE-2022-0897.patch b/recipes-extended/libvirt/libvirt/CVE-2022-0897.patch new file mode 100644 index 00000000..81e3194c --- /dev/null +++ b/recipes-extended/libvirt/libvirt/CVE-2022-0897.patch @@ -0,0 +1,57 @@ +From 949548615761737bccc0046ae30b1b7cdf50ec39 Mon Sep 17 00:00:00 2001 +From: Hitendra Prajapati <hprajapati@mvista.com> +Date: Mon, 27 Jun 2022 11:13:53 +0530 +Subject: [PATCH] CVE-2022-0897 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Upstream-Status: Backport [https://gitlab.com/libvirt/libvirt/-/commit/a4947e8f63c3e6b7b067b444f3d6cf674c0d7f36] +CVE: CVE-2022-0897 +Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> + +nwfilter: fix crash when counting number of network filters + +The virNWFilterObjListNumOfNWFilters method iterates over the +driver->nwfilters, accessing virNWFilterObj instances. As such +it needs to be protected against concurrent modification of +the driver->nwfilters object. + +This API allows unprivileged users to connect, so users with +read-only access to libvirt can cause a denial of service +crash if they are able to race with a call of virNWFilterUndefine. +Since network filters are usually statically defined, this is +considered a low severity problem. + +This is assigned CVE-2022-0897. +Reviewed-by: Eric Blake's avatarEric Blake <eblake@redhat.com> +Signed-off-by: Daniel P. Berrangé's avatarDaniel P. Berrangé <berrange@redhat.com> +--- + src/nwfilter/nwfilter_driver.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/src/nwfilter/nwfilter_driver.c b/src/nwfilter/nwfilter_driver.c +index 1c40772..27500d1 100644 +--- a/src/nwfilter/nwfilter_driver.c ++++ b/src/nwfilter/nwfilter_driver.c +@@ -514,11 +514,15 @@ nwfilterLookupByName(virConnectPtr conn, + static int + nwfilterConnectNumOfNWFilters(virConnectPtr conn) + { ++ int ret; + if (virConnectNumOfNWFiltersEnsureACL(conn) < 0) + return -1; + +- return virNWFilterObjListNumOfNWFilters(driver->nwfilters, conn, +- virConnectNumOfNWFiltersCheckACL); ++ nwfilterDriverLock(); ++ ret = virNWFilterObjListNumOfNWFilters(driver->nwfilters, conn, ++ virConnectNumOfNWFiltersCheckACL); ++ nwfilterDriverUnlock(); ++ return ret; + } + + +-- +2.25.1 + diff --git a/recipes-extended/libvirt/libvirt_6.1.0.bb b/recipes-extended/libvirt/libvirt_6.1.0.bb index d4978b38..803f7878 100644 --- a/recipes-extended/libvirt/libvirt_6.1.0.bb +++ b/recipes-extended/libvirt/libvirt_6.1.0.bb @@ -46,8 +46,10 @@ SRC_URI = "http://libvirt.org/sources/libvirt-${PV}.tar.xz;name=libvirt \ file://CVE-2020-25637_3.patch \ file://CVE-2020-25637_4.patch \ file://CVE-2021-3631.patch \ + file://CVE-2021-3667.patch \ + file://CVE-2022-0897.patch \ + file://CVE-2021-3975.patch \ " - SRC_URI[libvirt.md5sum] = "a870e63f20fac2ccf98e716d05256145" SRC_URI[libvirt.sha256sum] = "167c185be45560e73dd3e14ed375778b555c01455192de2dafc4d0f74fabebc0" diff --git a/recipes-networking/openvswitch/openvswitch-git/CVE-2021-3905.patch b/recipes-networking/openvswitch/openvswitch-git/CVE-2021-3905.patch new file mode 100644 index 00000000..beff3944 --- /dev/null +++ b/recipes-networking/openvswitch/openvswitch-git/CVE-2021-3905.patch @@ -0,0 +1,28 @@ +From 9f66c8c98f1eb55e0fb536bfaf7afaecda136b0a Mon Sep 17 00:00:00 2001 +From: Hitendra Prajapati <hprajapati@mvista.com> +Date: Tue, 20 Sep 2022 10:06:04 +0530 +Subject: [PATCH] CVE-2021-3905 + +Upstream-Status: Backport [https://github.com/openvswitch/ovs/commit/803ed12e31b0377c37d7aa8c94b3b92f2081e349] +CVE: CVE-2021-3905 +Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> +--- + lib/ipf.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/lib/ipf.c b/lib/ipf.c +index 446e89d13..66203f642 100644 +--- a/lib/ipf.c ++++ b/lib/ipf.c +@@ -945,6 +945,8 @@ ipf_extract_frags_from_batch(struct ipf *ipf, struct dp_packet_batch *pb, + if (!ipf_handle_frag(ipf, pkt, dl_type, zone, now, hash_basis, + pb->do_not_steal)) { + dp_packet_batch_refill(pb, pkt, pb_idx); ++ } else { ++ dp_packet_delete(pkt); + } + ovs_mutex_unlock(&ipf->ipf_lock); + } else { +-- +2.25.1 + diff --git a/recipes-networking/openvswitch/openvswitch_git.bb b/recipes-networking/openvswitch/openvswitch_git.bb index 86144196..56a9c25f 100644 --- a/recipes-networking/openvswitch/openvswitch_git.bb +++ b/recipes-networking/openvswitch/openvswitch_git.bb @@ -31,6 +31,7 @@ SRC_URI = "file://openvswitch-switch \ file://python-switch-remaining-scripts-to-use-python3.patch \ file://systemd-update-tool-paths.patch \ file://systemd-create-runtime-dirs.patch \ + file://CVE-2021-3905.patch \ " LIC_FILES_CHKSUM = "file://LICENSE;md5=1ce5d23a6429dff345518758f13aaeab" |