Skip to content

Commit bfb171d

Browse files
klihubmikebrow
andcommitted
default-validator: allow restricting seccomp policy adjustment.
Implement configurable restrictions for linux seccomp policy adjustment in the default validator. Co-authored-by: Mike Brown <brownwm@us.ibm.com> Signed-off-by: Krisztian Litkey <krisztian.litkey@intel.com>
1 parent b315f77 commit bfb171d

File tree

2 files changed

+188
-3
lines changed

2 files changed

+188
-3
lines changed

pkg/adaptation/adaptation_suite_test.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
"github.com/containerd/nri/pkg/api"
3939
"github.com/containerd/nri/pkg/plugin"
4040
validator "github.com/containerd/nri/plugins/default-validator/builtin"
41+
rspec "github.com/opencontainers/runtime-spec/specs-go"
4142
)
4243

4344
var _ = Describe("Configuration", func() {
@@ -558,6 +559,24 @@ var _ = Describe("Plugin container creation adjustments", func() {
558559

559560
case "cgroupspath":
560561
a.SetLinuxCgroupsPath("/" + plugin)
562+
563+
case "seccomp":
564+
a.SetLinuxSeccompPolicy(
565+
func() *api.LinuxSeccomp {
566+
seccomp := rspec.LinuxSeccomp{
567+
DefaultAction: rspec.ActAllow,
568+
ListenerPath: "/run/meshuggah-rocks.sock",
569+
Architectures: []rspec.Arch{},
570+
Flags: []rspec.LinuxSeccompFlag{},
571+
Syscalls: []rspec.LinuxSyscall{{
572+
Names: []string{"sched_getaffinity"},
573+
Action: rspec.ActNotify,
574+
Args: []rspec.LinuxSeccompArg{},
575+
}},
576+
}
577+
return api.FromOCILinuxSeccomp(&seccomp)
578+
}(),
579+
)
561580
}
562581

563582
return a, nil, nil
@@ -812,6 +831,26 @@ var _ = Describe("Plugin container creation adjustments", func() {
812831
},
813832
},
814833
),
834+
Entry("adjust seccomp policy", "seccomp",
835+
&api.ContainerAdjustment{
836+
Linux: &api.LinuxContainerAdjustment{
837+
SeccompPolicy: func() *api.LinuxSeccomp {
838+
seccomp := rspec.LinuxSeccomp{
839+
DefaultAction: rspec.ActAllow,
840+
ListenerPath: "/run/meshuggah-rocks.sock",
841+
Architectures: []rspec.Arch{},
842+
Flags: []rspec.LinuxSeccompFlag{},
843+
Syscalls: []rspec.LinuxSyscall{{
844+
Names: []string{"sched_getaffinity"},
845+
Action: rspec.ActNotify,
846+
Args: []rspec.LinuxSeccompArg{},
847+
}},
848+
}
849+
return api.FromOCILinuxSeccomp(&seccomp)
850+
}(),
851+
},
852+
},
853+
),
815854
)
816855
})
817856

@@ -1141,6 +1180,104 @@ var _ = Describe("Plugin container creation adjustments", func() {
11411180
})
11421181
})
11431182

1183+
When("the default validator is enabled and seccomp policy adjustment is disabled", func() {
1184+
BeforeEach(func() {
1185+
s.Prepare(
1186+
&mockRuntime{
1187+
options: []nri.Option{
1188+
nri.WithDefaultValidator(
1189+
&validator.DefaultValidatorConfig{
1190+
Enable: true,
1191+
RejectSeccompPolicy: true,
1192+
},
1193+
),
1194+
},
1195+
},
1196+
&mockPlugin{idx: "00", name: "foo"},
1197+
&mockPlugin{idx: "10", name: "validator1"},
1198+
&mockPlugin{idx: "20", name: "validator2"},
1199+
)
1200+
})
1201+
1202+
It("should reject OCI Hook injection", func() {
1203+
var (
1204+
create = func(_ *mockPlugin, _ *api.PodSandbox, ctr *api.Container) (*api.ContainerAdjustment, []*api.ContainerUpdate, error) {
1205+
a := &api.ContainerAdjustment{}
1206+
if ctr.GetName() == "ctr1" {
1207+
a.SetLinuxSeccompPolicy(
1208+
func() *api.LinuxSeccomp {
1209+
seccomp := rspec.LinuxSeccomp{
1210+
DefaultAction: rspec.ActAllow,
1211+
ListenerPath: "/run/meshuggah-rocks.sock",
1212+
Architectures: []rspec.Arch{},
1213+
Flags: []rspec.LinuxSeccompFlag{},
1214+
Syscalls: []rspec.LinuxSyscall{{
1215+
Names: []string{"sched_getaffinity"},
1216+
Action: rspec.ActNotify,
1217+
Args: []rspec.LinuxSeccompArg{},
1218+
}},
1219+
}
1220+
return api.FromOCILinuxSeccomp(&seccomp)
1221+
}(),
1222+
)
1223+
}
1224+
return a, nil, nil
1225+
}
1226+
1227+
validate = func(_ *mockPlugin, _ *api.ValidateContainerAdjustmentRequest) error {
1228+
return nil
1229+
}
1230+
1231+
runtime = s.runtime
1232+
plugins = s.plugins
1233+
ctx = context.Background()
1234+
1235+
pod = &api.PodSandbox{
1236+
Id: "pod0",
1237+
Name: "pod0",
1238+
Uid: "uid0",
1239+
Namespace: "default",
1240+
}
1241+
ctr0 = &api.Container{
1242+
Id: "ctr0",
1243+
PodSandboxId: "pod0",
1244+
Name: "ctr0",
1245+
State: api.ContainerState_CONTAINER_CREATED,
1246+
}
1247+
ctr1 = &api.Container{
1248+
Id: "ctr1",
1249+
PodSandboxId: "pod0",
1250+
Name: "ctr1",
1251+
State: api.ContainerState_CONTAINER_CREATED,
1252+
}
1253+
)
1254+
1255+
plugins[0].createContainer = create
1256+
plugins[1].validateAdjustment = validate
1257+
plugins[2].validateAdjustment = validate
1258+
1259+
s.Startup()
1260+
podReq := &api.RunPodSandboxRequest{Pod: pod}
1261+
Expect(runtime.RunPodSandbox(ctx, podReq)).To(Succeed())
1262+
1263+
ctrReq := &api.CreateContainerRequest{
1264+
Pod: pod,
1265+
Container: ctr0,
1266+
}
1267+
reply, err := runtime.CreateContainer(ctx, ctrReq)
1268+
Expect(reply).ToNot(BeNil())
1269+
Expect(err).To(BeNil())
1270+
1271+
ctrReq = &api.CreateContainerRequest{
1272+
Pod: pod,
1273+
Container: ctr1,
1274+
}
1275+
reply, err = runtime.CreateContainer(ctx, ctrReq)
1276+
Expect(err).ToNot(BeNil())
1277+
Expect(reply).To(BeNil())
1278+
})
1279+
})
1280+
11441281
When("the default validator is enabled with some required plugins", func() {
11451282
const AnnotationDomain = plugin.AnnotationDomain
11461283
BeforeEach(func() {

plugins/default-validator/default-validator.go

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,17 @@ import (
3333
type DefaultValidatorConfig struct {
3434
// Enable the default validator plugin.
3535
Enable bool `yaml:"enable" toml:"enable"`
36-
// RejectOCIHooks fails validation if any plugin injects OCI hooks.
37-
RejectOCIHooks bool `yaml:"rejectOCIHooks" toml:"reject_oci_hooks"`
36+
// RejectOCIHookAdjustment fails validation if OCI hooks are adjusted.
37+
RejectOCIHookAdjustment bool `yaml:"rejectOCIHookAdjustment" toml:"reject_oci_hook_adjustment"`
38+
// RejectRuntimeDefaultSeccompAdjustment fails validation if a runtime default seccomp
39+
// policy is adjusted.
40+
RejectRuntimeDefaultSeccompAdjustment bool `yaml:"rejectRuntimeDefaultSeccompAdjustment" toml:"reject_runtime_default_seccomp_adjustment"`
41+
// RejectUnconfinedSeccompAdjustment fails validation if an unconfined seccomp policy is
42+
// adjusted.
43+
RejectUnconfinedSeccompAdjustment bool `yaml:"rejectUnconfinedSeccompAdjustment" toml:"reject_unconfined_seccomp_adjustment"`
44+
// RejectCustomSeccompAdjustment fails validation if a custom seccomp policy (aka LOCALHOST)
45+
// is adjusted.
46+
RejectCustomSeccompAdjustment bool `yaml:"rejectCustomSeccompAdjustment" toml:"reject_custom_seccomp_adjustment"`
3847
// RequiredPlugins list globally required plugins. These must be present
3948
// or otherwise validation will fail.
4049
// WARNING: This is a global setting and will affect all containers. In
@@ -88,6 +97,11 @@ func (v *DefaultValidator) ValidateContainerAdjustment(ctx context.Context, req
8897
return err
8998
}
9099

100+
if err := v.validateSeccompPolicy(req); err != nil {
101+
log.Errorf(ctx, "rejecting adjustment: %v", err)
102+
return err
103+
}
104+
91105
if err := v.validateRequiredPlugins(req); err != nil {
92106
log.Errorf(ctx, "rejecting adjustment: %v", err)
93107
return err
@@ -101,7 +115,7 @@ func (v *DefaultValidator) validateOCIHooks(req *api.ValidateContainerAdjustment
101115
return nil
102116
}
103117

104-
if !v.cfg.RejectOCIHooks {
118+
if !v.cfg.RejectOCIHookAdjustment {
105119
return nil
106120
}
107121

@@ -121,6 +135,40 @@ func (v *DefaultValidator) validateOCIHooks(req *api.ValidateContainerAdjustment
121135
return fmt.Errorf("%w: %s attempted restricted OCI hook injection", ErrValidation, offender)
122136
}
123137

138+
func (v *DefaultValidator) validateSeccompPolicy(req *api.ValidateContainerAdjustmentRequest) error {
139+
if req.Adjust == nil {
140+
return nil
141+
}
142+
143+
owner, claimed := req.Owners.SeccompPolicyOwner(req.Container.Id)
144+
if !claimed {
145+
return nil
146+
}
147+
148+
profile := req.Container.GetLinux().GetSeccompProfile()
149+
switch {
150+
case profile == nil || profile.GetProfileType() == api.SecurityProfile_UNCONFINED:
151+
if v.cfg.RejectUnconfinedSeccompAdjustment {
152+
return fmt.Errorf("%w: plugin %s attempted restricted "+
153+
" unconfined seccomp policy adjustment", ErrValidation, owner)
154+
}
155+
156+
case profile.GetProfileType() == api.SecurityProfile_RUNTIME_DEFAULT:
157+
if v.cfg.RejectRuntimeDefaultSeccompAdjustment {
158+
return fmt.Errorf("%w: plugin %s attempted restricted "+
159+
"runtime default seccomp policy adjustment", ErrValidation, owner)
160+
}
161+
162+
case profile.GetProfileType() == api.SecurityProfile_LOCALHOST:
163+
if v.cfg.RejectCustomSeccompAdjustment {
164+
return fmt.Errorf("%w: plugin %s attempted restricted "+
165+
" custom seccomp policy adjustment", ErrValidation, owner)
166+
}
167+
}
168+
169+
return nil
170+
}
171+
124172
func (v *DefaultValidator) validateRequiredPlugins(req *api.ValidateContainerAdjustmentRequest) error {
125173
var (
126174
container = req.GetContainer().GetName()

0 commit comments

Comments
 (0)