Files
containerd/client/container_opts_test.go
Ben Cressey 0ec1af4cae Do not propagate reserved labels from image configs
Image config labels are copied onto the container by both the CRI
plugin (BuildLabels) and the client's WithImageConfigLabels option
used by `ctr run`. Labels in the containerd.io/* namespace are
interpreted by containerd itself and labels in the io.cri-containerd*
namespace are interpreted by the CRI plugin. An image config is not a
trusted source for labels in either namespace.

Skip labels in both reserved namespaces when copying labels from an
image config to a container, and warn about each label skipped: an
image that tries to set them may be attempting to alter containerd
behavior. Oversized image labels are already skipped this way by
the CRI plugin.

Labels set explicitly by clients, for example via `ctr run --label`
or in the CRI request, are unaffected.

Verified with the CRI plugin and with `ctr run` against an image
whose config carries labels like these: the labels are no longer
present on the created container and a warning is logged for each.

Assisted-by: Claude Code
Signed-off-by: Ben Cressey <ben@cressey.org>
Signed-off-by: Samuel Karp <samuelkarp@google.com>
2026-06-10 13:18:24 -07:00

76 lines
2.1 KiB
Go

/*
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 client
import (
"context"
"encoding/json"
"testing"
"github.com/containerd/containerd/v2/core/containers"
"github.com/containerd/containerd/v2/core/content"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// fakeImage implements the subset of Image used by WithImageConfigLabels:
// Config returns a descriptor with the config blob inlined in Data, so the
// content store is never consulted.
type fakeImage struct {
Image
config ocispec.Descriptor
}
func (i fakeImage) Config(context.Context) (ocispec.Descriptor, error) {
return i.config, nil
}
func (i fakeImage) ContentStore() content.Store {
return nil
}
func TestWithImageConfigLabels(t *testing.T) {
blob, err := json.Marshal(ocispec.Image{
Config: ocispec.ImageConfig{
Labels: map[string]string{
"foo": "bar",
"containerd.io/restart.policy": "always",
"io.cri-containerd.kind": "sandbox",
},
},
})
require.NoError(t, err)
img := fakeImage{
config: ocispec.Descriptor{
MediaType: ocispec.MediaTypeImageConfig,
Digest: digest.FromBytes(blob),
Size: int64(len(blob)),
Data: blob,
},
}
var c containers.Container
require.NoError(t, WithImageConfigLabels(img)(t.Context(), nil, &c))
// labels in the namespaces reserved for containerd and the CRI plugin
// are not copied from the image config
assert.Equal(t, map[string]string{"foo": "bar"}, c.Labels)
}