Skip to content

Commit e53c7c1

Browse files
authored
Merge pull request #13628 from samuelkarp/prepare-2.2.5
[release/2.2] Prepare release notes for v2.2.5
2 parents 8bea48a + 2690310 commit e53c7c1

16 files changed

Lines changed: 851 additions & 111 deletions

client/container_opts.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,17 @@ import (
2121
"encoding/json"
2222
"errors"
2323
"fmt"
24+
"maps"
2425

2526
"github.com/containerd/containerd/v2/core/containers"
2627
"github.com/containerd/containerd/v2/core/content"
2728
"github.com/containerd/containerd/v2/core/images"
2829
"github.com/containerd/containerd/v2/core/snapshots"
30+
"github.com/containerd/containerd/v2/pkg/labels"
2931
"github.com/containerd/containerd/v2/pkg/namespaces"
3032
"github.com/containerd/containerd/v2/pkg/oci"
3133
"github.com/containerd/errdefs"
34+
"github.com/containerd/log"
3235
"github.com/containerd/typeurl/v2"
3336
"github.com/opencontainers/image-spec/identity"
3437
v1 "github.com/opencontainers/image-spec/specs-go/v1"
@@ -113,6 +116,10 @@ func WithContainerLabels(labels map[string]string) NewContainerOpts {
113116
// The existing labels are cleared as this is expected to be the first
114117
// operation in setting up a container's labels. Use WithAdditionalContainerLabels
115118
// to add/overwrite the existing image config labels.
119+
//
120+
// Image config labels in the namespaces reserved for containerd
121+
// (containerd.io/) and the CRI plugin (io.cri-containerd) are not copied
122+
// to the container.
116123
func WithImageConfigLabels(image Image) NewContainerOpts {
117124
return func(ctx context.Context, _ *Client, c *containers.Container) error {
118125
ic, err := image.Config(ctx)
@@ -138,6 +145,16 @@ func WithImageConfigLabels(image Image) NewContainerOpts {
138145
config = ociimage.Config
139146

140147
c.Labels = config.Labels
148+
// Labels in the containerd.io/* namespace are interpreted by containerd
149+
// itself, and labels in the io.cri-containerd.* namespace are interpreted
150+
// by the CRI plugin, so they are not copied from untrusted image configs.
151+
maps.DeleteFunc(c.Labels, func(k, _ string) bool {
152+
if labels.IsReserved(k) {
153+
log.G(ctx).Warnf("skipping image label %q: the label namespace is reserved for containerd; possible malicious image attempting to alter containerd behavior", k)
154+
return true
155+
}
156+
return false
157+
})
141158
return nil
142159
}
143160
}

client/container_opts_test.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package client
18+
19+
import (
20+
"context"
21+
"encoding/json"
22+
"testing"
23+
24+
"github.com/containerd/containerd/v2/core/containers"
25+
"github.com/containerd/containerd/v2/core/content"
26+
"github.com/opencontainers/go-digest"
27+
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
28+
"github.com/stretchr/testify/assert"
29+
"github.com/stretchr/testify/require"
30+
)
31+
32+
// fakeImage implements the subset of Image used by WithImageConfigLabels:
33+
// Config returns a descriptor with the config blob inlined in Data, so the
34+
// content store is never consulted.
35+
type fakeImage struct {
36+
Image
37+
config ocispec.Descriptor
38+
}
39+
40+
func (i fakeImage) Config(context.Context) (ocispec.Descriptor, error) {
41+
return i.config, nil
42+
}
43+
44+
func (i fakeImage) ContentStore() content.Store {
45+
return nil
46+
}
47+
48+
func TestWithImageConfigLabels(t *testing.T) {
49+
blob, err := json.Marshal(ocispec.Image{
50+
Config: ocispec.ImageConfig{
51+
Labels: map[string]string{
52+
"foo": "bar",
53+
"containerd.io/restart.policy": "always",
54+
"io.cri-containerd.kind": "sandbox",
55+
},
56+
},
57+
})
58+
require.NoError(t, err)
59+
60+
img := fakeImage{
61+
config: ocispec.Descriptor{
62+
MediaType: ocispec.MediaTypeImageConfig,
63+
Digest: digest.FromBytes(blob),
64+
Size: int64(len(blob)),
65+
Data: blob,
66+
},
67+
}
68+
69+
var c containers.Container
70+
require.NoError(t, WithImageConfigLabels(img)(t.Context(), nil, &c))
71+
72+
// labels in the namespaces reserved for containerd and the CRI plugin
73+
// are not copied from the image config
74+
assert.Equal(t, map[string]string{"foo": "bar"}, c.Labels)
75+
}

contrib/checkpoint/checkpoint-restore-cri-test.sh

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,15 @@ TESTDATA=testdata
5656
# shellcheck disable=SC2034
5757
export CONTAINERD_ADDRESS="$TESTDIR/c.sock"
5858
export CONTAINER_RUNTIME_ENDPOINT="unix:///${CONTAINERD_ADDRESS}"
59+
60+
# Generate crictl config file with 30s timeout
61+
export CRI_CONFIG_FILE="${TESTDIR}/crictl.yaml"
62+
cat <<EOF > "${CRI_CONFIG_FILE}"
63+
runtime-endpoint: unix://${CONTAINERD_ADDRESS}
64+
image-endpoint: unix://${CONTAINERD_ADDRESS}
65+
timeout: 30
66+
EOF
67+
5968
TEST_IMAGE=ghcr.io/containerd/alpine
6069

6170
function test_from_archive() {
@@ -69,9 +78,12 @@ function test_from_archive() {
6978
echo -n "--> Start pod: "
7079
pod_id=$(crictl runp "$POD_JSON")
7180
echo "$pod_id"
81+
CTR_JSON=$(mktemp)
82+
jq '.annotations = {"cdi.k8s.io/device":"gpu","safe.annotation":"true"}' "$TESTDATA"/container_sleep.json >"$CTR_JSON"
7283
echo -n "--> Create container: "
73-
ctr_id=$(crictl create "$pod_id" "$TESTDATA"/container_sleep.json "$POD_JSON")
84+
ctr_id=$(crictl create "$pod_id" "$CTR_JSON" "$POD_JSON")
7485
echo "$ctr_id"
86+
rm -f "$CTR_JSON"
7587
echo -n "--> Start container: "
7688
crictl start "$ctr_id"
7789
lines_before=$(crictl logs "$ctr_id" | wc -l)
@@ -108,9 +120,15 @@ function test_from_archive() {
108120
"should be larger than before checkpointing ($lines_before)"
109121
false
110122
fi
123+
echo "--> Verifying CDI annotation filtering on restore: "
124+
actual_annots=$(crictl inspect "$ctr_id" | jq -c '.status.annotations')
125+
if jq -e 'has("cdi.k8s.io/device") or (has("safe.annotation") | not)' <<<"$actual_annots" >/dev/null; then
126+
echo "error: CDI annotation was not filtered or safe annotation missing: $actual_annots"
127+
exit 1
128+
fi
111129
# Cleanup
112130
echo "--> Cleanup images: "
113-
crictl rmi "${TEST_IMAGE}" | sed 's/^/----> \t/'
131+
(crictl rmi "${TEST_IMAGE}" || true) | sed 's/^/----> \t/'
114132
echo -n "--> Verifying container rootfs: "
115133
crictl exec "$ctr_id" ls -la /root/testfile
116134
if crictl exec "$ctr_id" ls -la /etc/motd >/dev/null 2>&1; then
@@ -184,14 +202,16 @@ function test_from_oci() {
184202
echo "--> Cleanup images: "
185203
../../bin/ctr -n k8s.io images rm localhost/checkpoint-image:latest | sed 's/^/----> \t/'
186204
echo "--> Cleanup images: "
187-
crictl rmi "${TEST_IMAGE}" | sed 's/^/----> \t/'
205+
(crictl rmi "${TEST_IMAGE}" || true) | sed 's/^/----> \t/'
188206
echo "--> Deleting all pods: "
189207
crictl -t 5s rmp -fa | sed 's/^/----> \t/'
190208
SUCCESS=1
191209
}
192210

193211
cat >"${TESTDIR}/config.toml" <<EOF
194212
version = 3
213+
[plugins."io.containerd.cri.v1.runtime"]
214+
enable_cdi = false
195215
[plugins."io.containerd.cri.v1.runtime".containerd]
196216
default_runtime_name = "test-runtime"
197217
[plugins.'io.containerd.cri.v1.runtime'.containerd.runtimes.test-runtime]

internal/cri/labels/labels.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@
1616

1717
package labels
1818

19+
import (
20+
clabels "github.com/containerd/containerd/v2/pkg/labels"
21+
)
22+
1923
const (
2024
// criContainerdPrefix is common prefix for cri-containerd
21-
criContainerdPrefix = "io.cri-containerd"
25+
criContainerdPrefix = clabels.CRIContainerdPrefix
2226
// ImageLabelKey is the label key indicating the image is managed by cri plugin.
2327
ImageLabelKey = criContainerdPrefix + ".image"
2428
// ImageLabelValue is the label value indicating the image is managed by cri plugin.

0 commit comments

Comments
 (0)