Skip to content

Commit

Permalink
Merge pull request #1834 from cbgbt/1.4.x
Browse files Browse the repository at this point in the history
Prepare for 1.4.1 Release
  • Loading branch information
cbgbt authored Nov 17, 2021
2 parents 4236070 + dc87d83 commit 1865c47
Show file tree
Hide file tree
Showing 14 changed files with 859 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v2
- run: rustup toolchain install 1.56.0 && rustup default 1.56.0
- run: rustup toolchain install 1.56.1 && rustup default 1.56.1
- run: cargo install --version 0.30.0 cargo-make
- run: cargo make -e BUILDSYS_VARIANT=${{ matrix.variant }} unit-tests
- run: cargo make -e BUILDSYS_VARIANT=${{ matrix.variant }} check-fmt
Expand Down
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
# v1.4.1 (2021-11-18)

## Security Fixes

* Apply patches to docker and containerd for CVE-2021-41190 ([#1832], [#1833])

## Build Changes

* Update Bottlerocket SDK to 0.23.1 ([#1831])

[#1831]: https://github.com/bottlerocket-os/bottlerocket/pull/1831
[#1832]: https://github.com/bottlerocket-os/bottlerocket/pull/1832
[#1833]: https://github.com/bottlerocket-os/bottlerocket/pull/1833


# v1.4.0 (2021-11-12)

## OS Changes
Expand Down
2 changes: 1 addition & 1 deletion Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ BUILDSYS_NAME = "bottlerocket"
# "Bottlerocket Remix by ${CORP}" or "${CORP}'s Bottlerocket Remix"
BUILDSYS_PRETTY_NAME = "Bottlerocket OS"
# SDK version used for building
BUILDSYS_SDK_VERSION="v0.23.0"
BUILDSYS_SDK_VERSION="v0.23.1"
# Site for fetching the SDK
BUILDSYS_REGISTRY="public.ecr.aws/bottlerocket"

Expand Down
3 changes: 2 additions & 1 deletion Release.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = "1.4.0"
version = "1.4.1"

[migrations]
"(0.3.1, 0.3.2)" = ["migrate_v0.3.2_admin-container-v0-5-0.lz4"]
Expand Down Expand Up @@ -77,3 +77,4 @@ version = "1.4.0"
"(1.3.0, 1.4.0)" = [
"migrate_v1.4.0_registry-mirror-representation.lz4",
]
"(1.4.0, 1.4.1)" = []
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
From 833407fbff446771e26d6a381897f2c7ae24677e Mon Sep 17 00:00:00 2001
From: Samuel Karp <[email protected]>
Date: Wed, 20 Oct 2021 14:43:16 -0700
Subject: [PATCH 1/2] images: validate document type before unmarshal

Signed-off-by: Samuel Karp <[email protected]>
(cherry picked from commit eb9ba7ed8d46d48fb22362f9d91fff6fb837e37e)
Signed-off-by: Samuel Karp <[email protected]>
---
images/image.go | 55 +++++++++++++++++++
images/image_test.go | 127 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 182 insertions(+)
create mode 100644 images/image_test.go

diff --git a/images/image.go b/images/image.go
index 27384c16d..2e5cd61c9 100644
--- a/images/image.go
+++ b/images/image.go
@@ -19,6 +19,7 @@ package images
import (
"context"
"encoding/json"
+ "fmt"
"sort"
"time"

@@ -154,6 +155,10 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc
return nil, err
}

+ if err := validateMediaType(p, desc.MediaType); err != nil {
+ return nil, errors.Wrapf(err, "manifest: invalid desc %s", desc.Digest)
+ }
+
var manifest ocispec.Manifest
if err := json.Unmarshal(p, &manifest); err != nil {
return nil, err
@@ -194,6 +199,10 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc
return nil, err
}

+ if err := validateMediaType(p, desc.MediaType); err != nil {
+ return nil, errors.Wrapf(err, "manifest: invalid desc %s", desc.Digest)
+ }
+
var idx ocispec.Index
if err := json.Unmarshal(p, &idx); err != nil {
return nil, err
@@ -336,6 +345,10 @@ func Children(ctx context.Context, provider content.Provider, desc ocispec.Descr
return nil, err
}

+ if err := validateMediaType(p, desc.MediaType); err != nil {
+ return nil, errors.Wrapf(err, "children: invalid desc %s", desc.Digest)
+ }
+
// TODO(stevvooe): We just assume oci manifest, for now. There may be
// subtle differences from the docker version.
var manifest ocispec.Manifest
@@ -351,6 +364,10 @@ func Children(ctx context.Context, provider content.Provider, desc ocispec.Descr
return nil, err
}

+ if err := validateMediaType(p, desc.MediaType); err != nil {
+ return nil, errors.Wrapf(err, "children: invalid desc %s", desc.Digest)
+ }
+
var index ocispec.Index
if err := json.Unmarshal(p, &index); err != nil {
return nil, err
@@ -368,6 +385,44 @@ func Children(ctx context.Context, provider content.Provider, desc ocispec.Descr
return descs, nil
}

+// unknownDocument represents a manifest, manifest list, or index that has not
+// yet been validated.
+type unknownDocument struct {
+ MediaType string `json:"mediaType,omitempty"`
+ Config json.RawMessage `json:"config,omitempty"`
+ Layers json.RawMessage `json:"layers,omitempty"`
+ Manifests json.RawMessage `json:"manifests,omitempty"`
+ FSLayers json.RawMessage `json:"fsLayers,omitempty"` // schema 1
+}
+
+// validateMediaType returns an error if the byte slice is invalid JSON or if
+// the media type identifies the blob as one format but it contains elements of
+// another format.
+func validateMediaType(b []byte, mt string) error {
+ var doc unknownDocument
+ if err := json.Unmarshal(b, &doc); err != nil {
+ return err
+ }
+ if len(doc.FSLayers) != 0 {
+ return fmt.Errorf("media-type: schema 1 not supported")
+ }
+ switch mt {
+ case MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
+ if len(doc.Manifests) != 0 ||
+ doc.MediaType == MediaTypeDockerSchema2ManifestList ||
+ doc.MediaType == ocispec.MediaTypeImageIndex {
+ return fmt.Errorf("media-type: expected manifest but found index (%s)", mt)
+ }
+ case MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
+ if len(doc.Config) != 0 || len(doc.Layers) != 0 ||
+ doc.MediaType == MediaTypeDockerSchema2Manifest ||
+ doc.MediaType == ocispec.MediaTypeImageManifest {
+ return fmt.Errorf("media-type: expected index but found manifest (%s)", mt)
+ }
+ }
+ return nil
+}
+
// RootFS returns the unpacked diffids that make up and images rootfs.
//
// These are used to verify that a set of layers unpacked to the expected
diff --git a/images/image_test.go b/images/image_test.go
new file mode 100644
index 000000000..87c84ab05
--- /dev/null
+++ b/images/image_test.go
@@ -0,0 +1,127 @@
+/*
+ Copyright The containerd Authors.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+package images
+
+import (
+ "encoding/json"
+ "testing"
+
+ ocispec "github.com/opencontainers/image-spec/specs-go/v1"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestValidateMediaType(t *testing.T) {
+ docTests := []struct {
+ mt string
+ index bool
+ }{
+ {MediaTypeDockerSchema2Manifest, false},
+ {ocispec.MediaTypeImageManifest, false},
+ {MediaTypeDockerSchema2ManifestList, true},
+ {ocispec.MediaTypeImageIndex, true},
+ }
+ for _, tc := range docTests {
+ t.Run("manifest-"+tc.mt, func(t *testing.T) {
+ manifest := ocispec.Manifest{
+ Config: ocispec.Descriptor{Size: 1},
+ Layers: []ocispec.Descriptor{{Size: 2}},
+ }
+ b, err := json.Marshal(manifest)
+ require.NoError(t, err, "failed to marshal manifest")
+
+ err = validateMediaType(b, tc.mt)
+ if tc.index {
+ assert.Error(t, err, "manifest should not be a valid index")
+ } else {
+ assert.NoError(t, err, "manifest should be valid")
+ }
+ })
+ t.Run("index-"+tc.mt, func(t *testing.T) {
+ index := ocispec.Index{
+ Manifests: []ocispec.Descriptor{{Size: 1}},
+ }
+ b, err := json.Marshal(index)
+ require.NoError(t, err, "failed to marshal index")
+
+ err = validateMediaType(b, tc.mt)
+ if tc.index {
+ assert.NoError(t, err, "index should be valid")
+ } else {
+ assert.Error(t, err, "index should not be a valid manifest")
+ }
+ })
+ }
+
+ mtTests := []struct {
+ mt string
+ valid []string
+ invalid []string
+ }{{
+ MediaTypeDockerSchema2Manifest,
+ []string{MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest},
+ []string{MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex},
+ }, {
+ ocispec.MediaTypeImageManifest,
+ []string{MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest},
+ []string{MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex},
+ }, {
+ MediaTypeDockerSchema2ManifestList,
+ []string{MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex},
+ []string{MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest},
+ }, {
+ ocispec.MediaTypeImageIndex,
+ []string{MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex},
+ []string{MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest},
+ }}
+ for _, tc := range mtTests {
+ for _, v := range tc.valid {
+ t.Run("valid-"+tc.mt+"-"+v, func(t *testing.T) {
+ doc := struct {
+ MediaType string `json:"mediaType"`
+ }{MediaType: v}
+ b, err := json.Marshal(doc)
+ require.NoError(t, err, "failed to marshal document")
+
+ err = validateMediaType(b, tc.mt)
+ assert.NoError(t, err, "document should be valid")
+ })
+ }
+ for _, iv := range tc.invalid {
+ t.Run("invalid-"+tc.mt+"-"+iv, func(t *testing.T) {
+ doc := struct {
+ MediaType string `json:"mediaType"`
+ }{MediaType: iv}
+ b, err := json.Marshal(doc)
+ require.NoError(t, err, "failed to marshal document")
+
+ err = validateMediaType(b, tc.mt)
+ assert.Error(t, err, "document should not be valid")
+ })
+ }
+ }
+ t.Run("schema1", func(t *testing.T) {
+ doc := struct {
+ FSLayers []string `json:"fsLayers"`
+ }{FSLayers: []string{"1"}}
+ b, err := json.Marshal(doc)
+ require.NoError(t, err, "failed to marshal document")
+
+ err = validateMediaType(b, "")
+ assert.Error(t, err, "document should not be valid")
+ })
+}
--
2.33.1

42 changes: 42 additions & 0 deletions packages/containerd/0005-schema1-reject-ambiguous-documents.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
From 15d8c03e3260953cc560223b42426e8b67dde93c Mon Sep 17 00:00:00 2001
From: Samuel Karp <[email protected]>
Date: Mon, 15 Nov 2021 12:00:01 -0800
Subject: [PATCH 2/2] schema1: reject ambiguous documents

Signed-off-by: Samuel Karp <[email protected]>
(cherry picked from commit 70c88f507579277ab7af23b06666e3b57d4b4f2d)
Signed-off-by: Samuel Karp <[email protected]>
---
remotes/docker/schema1/converter.go | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/remotes/docker/schema1/converter.go b/remotes/docker/schema1/converter.go
index 8314c01d5..f15a9acf3 100644
--- a/remotes/docker/schema1/converter.go
+++ b/remotes/docker/schema1/converter.go
@@ -256,6 +256,9 @@ func (c *Converter) fetchManifest(ctx context.Context, desc ocispec.Descriptor)
if err := json.Unmarshal(b, &m); err != nil {
return err
}
+ if len(m.Manifests) != 0 || len(m.Layers) != 0 {
+ return errors.New("converter: expected schema1 document but found extra keys")
+ }
c.pulledManifest = &m

return nil
@@ -472,8 +475,10 @@ type history struct {
}

type manifest struct {
- FSLayers []fsLayer `json:"fsLayers"`
- History []history `json:"history"`
+ FSLayers []fsLayer `json:"fsLayers"`
+ History []history `json:"history"`
+ Layers json.RawMessage `json:"layers,omitempty"` // OCI manifest
+ Manifests json.RawMessage `json:"manifests,omitempty"` // OCI index
}

type v1History struct {
--
2.33.1

4 changes: 4 additions & 0 deletions packages/containerd/containerd.spec
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ Patch2001: 0001-v2-runtime-reduce-permissions-for-bundle-dir.patch
Patch2002: 0002-v1-runtime-reduce-permissions-for-bundle-dir.patch
Patch2003: 0003-btrfs-reduce-permissions-on-plugin-directories.patch

# CVE-2021-41190
Patch2004: 0004-images-validate-document-type-before-unmarshal.patch
Patch2005: 0005-schema1-reject-ambiguous-documents.patch

BuildRequires: git
BuildRequires: %{_cross_os}glibc-devel
Requires: %{_cross_os}runc
Expand Down
Loading

0 comments on commit 1865c47

Please sign in to comment.