blob: ad6f57ae606c7ca42bb739b8b3988310aba72ecc [file] [log] [blame] [edit]
/*
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 sputil
import (
"context"
"testing"
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
"github.com/containerd/containerd/v2/contrib/seccomp"
"github.com/containerd/containerd/v2/internal/cri/util"
"github.com/containerd/containerd/v2/pkg/oci"
)
func TestGenerateSeccompSecurityProfileSpecOpts(t *testing.T) {
for _, test := range []struct {
desc string
profile string
privileged bool
disable bool
specOpts oci.SpecOpts
expectErr bool
defaultProfile string
sp *runtime.SecurityProfile
}{
{
desc: "should return error if seccomp is specified when seccomp is not supported",
profile: runtimeDefault,
disable: true,
expectErr: true,
},
{
desc: "should not return error if seccomp is not specified when seccomp is not supported",
profile: "",
disable: true,
},
{
desc: "should not return error if seccomp is unconfined when seccomp is not supported",
profile: unconfinedProfile,
disable: true,
},
{
desc: "should not set seccomp when privileged is true",
profile: seccompDefaultProfile,
privileged: true,
},
{
desc: "should not set seccomp when seccomp is unconfined",
profile: unconfinedProfile,
},
{
desc: "should not set seccomp when seccomp is not specified",
profile: "",
},
{
desc: "should set default seccomp when seccomp is runtime/default",
profile: runtimeDefault,
specOpts: seccomp.WithDefaultProfile(),
},
{
desc: "should set default seccomp when seccomp is docker/default",
profile: dockerDefault,
specOpts: seccomp.WithDefaultProfile(),
},
{
desc: "should set specified profile when local profile is specified",
profile: profileNamePrefix + "test-profile",
specOpts: seccomp.WithProfile("test-profile"),
},
{
desc: "should use default profile when seccomp is empty",
defaultProfile: profileNamePrefix + "test-profile",
specOpts: seccomp.WithProfile("test-profile"),
},
{
desc: "should fallback to docker/default when seccomp is empty and default is runtime/default",
defaultProfile: runtimeDefault,
specOpts: seccomp.WithDefaultProfile(),
},
//-----------------------------------------------
// now buckets for the SecurityProfile variants
//-----------------------------------------------
{
desc: "sp should return error if seccomp is specified when seccomp is not supported",
disable: true,
expectErr: true,
sp: &runtime.SecurityProfile{
ProfileType: runtime.SecurityProfile_RuntimeDefault,
},
},
{
desc: "sp should not return error if seccomp is unconfined when seccomp is not supported",
disable: true,
sp: &runtime.SecurityProfile{
ProfileType: runtime.SecurityProfile_Unconfined,
},
},
{
desc: "sp should not set seccomp when privileged is true",
privileged: true,
sp: &runtime.SecurityProfile{
ProfileType: runtime.SecurityProfile_RuntimeDefault,
},
},
{
desc: "sp should not set seccomp when seccomp is unconfined",
sp: &runtime.SecurityProfile{
ProfileType: runtime.SecurityProfile_Unconfined,
},
},
{
desc: "sp should not set seccomp when seccomp is not specified",
},
{
desc: "sp should set default seccomp when seccomp is runtime/default",
specOpts: seccomp.WithDefaultProfile(),
sp: &runtime.SecurityProfile{
ProfileType: runtime.SecurityProfile_RuntimeDefault,
},
},
{
desc: "sp should set specified profile when local profile is specified",
specOpts: seccomp.WithProfile("test-profile"),
sp: &runtime.SecurityProfile{
ProfileType: runtime.SecurityProfile_Localhost,
LocalhostRef: profileNamePrefix + "test-profile",
},
},
{
desc: "sp should set specified profile when local profile is specified even without prefix",
specOpts: seccomp.WithProfile("test-profile"),
sp: &runtime.SecurityProfile{
ProfileType: runtime.SecurityProfile_Localhost,
LocalhostRef: "test-profile",
},
},
{
desc: "sp should return error if specified profile is invalid",
expectErr: true,
sp: &runtime.SecurityProfile{
ProfileType: runtime.SecurityProfile_RuntimeDefault,
LocalhostRef: "test-profile",
},
},
} {
t.Run(test.desc, func(t *testing.T) {
ssp := test.sp
csp, err := GenerateSeccompSecurityProfile(
test.profile,
test.defaultProfile)
if err != nil {
if test.expectErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
} else {
if ssp == nil {
ssp = csp
}
specOpts, err := GenerateSeccompSpecOpts(ssp, test.privileged, !test.disable)
if test.expectErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
if test.specOpts == nil && specOpts == nil {
return
}
if test.specOpts == nil || specOpts == nil {
t.Fatalf("unexpected nil specOpts, expected nil: %t, actual nil: %t", test.specOpts == nil, specOpts == nil)
}
// `specOpts` for seccomp only uses/modifies `*specs.Spec`, not
// `oci.Client` or `*containers.Container`, so let's construct a
// `*specs.Spec` and compare if the results are the same.
expected := runtimespec.Spec{
Linux: &runtimespec.Linux{},
Process: &runtimespec.Process{
Capabilities: &runtimespec.LinuxCapabilities{
// This is to ensure the test covers logic in
// `contrib/seccomp.DefaultProfile` as much as possible
Bounding: []string{
"CAP_DAC_READ_SEARCH",
"CAP_SYS_ADMIN",
"CAP_SYS_BOOT",
"CAP_SYS_CHROOT",
"CAP_SYS_MODULE",
"CAP_SYS_PACCT",
"CAP_SYS_PTRACE",
"CAP_SYS_RAWIO",
"CAP_SYS_TIME",
"CAP_SYS_TTY_CONFIG",
"CAP_SYS_NICE",
"CAP_SYSLOG",
"CAP_BPF",
"CAP_PERFMON",
},
},
},
}
var actual runtimespec.Spec
err := util.DeepCopy(&actual, &expected)
assert.NoError(t, err)
test.specOpts(context.TODO(), nil, nil, &expected)
specOpts(context.TODO(), nil, nil, &actual)
assert.Equal(t, expected, actual)
}
}
})
}
}