Skip to content

Commit 9d3e8c8

Browse files
committed
default-validator: allow restricting namespace adjustment.
Implement configurable restriction of namespace adjustment in the default validator. Signed-off-by: Krisztian Litkey <[email protected]>
1 parent 1374e35 commit 9d3e8c8

File tree

2 files changed

+121
-0
lines changed

2 files changed

+121
-0
lines changed

pkg/adaptation/adaptation_suite_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1817,6 +1817,94 @@ var _ = Describe("Plugin container creation adjustments", func() {
18171817
})
18181818
})
18191819

1820+
When("the default validator is enabled and namespace adjustment is disabled", func() {
1821+
BeforeEach(func() {
1822+
s.Prepare(
1823+
&mockRuntime{
1824+
options: []nri.Option{
1825+
nri.WithDefaultValidator(
1826+
&validator.DefaultValidatorConfig{
1827+
Enable: true,
1828+
RejectNamespaceAdjustment: true,
1829+
},
1830+
),
1831+
},
1832+
},
1833+
&mockPlugin{idx: "00", name: "foo"},
1834+
&mockPlugin{idx: "10", name: "validator1"},
1835+
&mockPlugin{idx: "20", name: "validator2"},
1836+
)
1837+
})
1838+
1839+
It("should reject namespace adjustment", func() {
1840+
var (
1841+
create = func(_ *mockPlugin, _ *api.PodSandbox, ctr *api.Container) (*api.ContainerAdjustment, []*api.ContainerUpdate, error) {
1842+
a := &api.ContainerAdjustment{}
1843+
if ctr.GetName() == "ctr1" {
1844+
a.AddOrReplaceNamespace(
1845+
&api.LinuxNamespace{
1846+
Type: "cgroup",
1847+
Path: "/",
1848+
},
1849+
)
1850+
}
1851+
return a, nil, nil
1852+
}
1853+
1854+
validate = func(_ *mockPlugin, _ *api.ValidateContainerAdjustmentRequest) error {
1855+
return nil
1856+
}
1857+
1858+
runtime = s.runtime
1859+
plugins = s.plugins
1860+
ctx = context.Background()
1861+
1862+
pod = &api.PodSandbox{
1863+
Id: "pod0",
1864+
Name: "pod0",
1865+
Uid: "uid0",
1866+
Namespace: "default",
1867+
}
1868+
ctr0 = &api.Container{
1869+
Id: "ctr0",
1870+
PodSandboxId: "pod0",
1871+
Name: "ctr0",
1872+
State: api.ContainerState_CONTAINER_CREATED,
1873+
}
1874+
ctr1 = &api.Container{
1875+
Id: "ctr1",
1876+
PodSandboxId: "pod0",
1877+
Name: "ctr1",
1878+
State: api.ContainerState_CONTAINER_CREATED,
1879+
}
1880+
)
1881+
1882+
plugins[0].createContainer = create
1883+
plugins[1].validateAdjustment = validate
1884+
plugins[2].validateAdjustment = validate
1885+
1886+
s.Startup()
1887+
podReq := &api.RunPodSandboxRequest{Pod: pod}
1888+
Expect(runtime.RunPodSandbox(ctx, podReq)).To(Succeed())
1889+
1890+
ctrReq := &api.CreateContainerRequest{
1891+
Pod: pod,
1892+
Container: ctr0,
1893+
}
1894+
reply, err := runtime.CreateContainer(ctx, ctrReq)
1895+
Expect(reply).ToNot(BeNil())
1896+
Expect(err).To(BeNil())
1897+
1898+
ctrReq = &api.CreateContainerRequest{
1899+
Pod: pod,
1900+
Container: ctr1,
1901+
}
1902+
reply, err = runtime.CreateContainer(ctx, ctrReq)
1903+
Expect(err).ToNot(BeNil())
1904+
Expect(reply).To(BeNil())
1905+
})
1906+
})
1907+
18201908
When("the default validator is enabled with some required plugins", func() {
18211909
const AnnotationDomain = plugin.AnnotationDomain
18221910
BeforeEach(func() {

plugins/default-validator/default-validator.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ type DefaultValidatorConfig struct {
4444
// RejectCustomSeccompAdjustment fails validation if a custom seccomp policy (aka LOCALHOST)
4545
// is adjusted.
4646
RejectCustomSeccompAdjustment bool `yaml:"rejectCustomSeccompAdjustment" toml:"reject_custom_seccomp_adjustment"`
47+
// RejectNamespaceAdjustment fails validation if any plugin adjusts Linux namespaces.
48+
RejectNamespaceAdjustment bool `yaml:"rejectNamespaceAdjustment" toml:"reject_namespace_adjustment"`
4749
// RequiredPlugins list globally required plugins. These must be present
4850
// or otherwise validation will fail.
4951
// WARNING: This is a global setting and will affect all containers. In
@@ -102,6 +104,11 @@ func (v *DefaultValidator) ValidateContainerAdjustment(ctx context.Context, req
102104
return err
103105
}
104106

107+
if err := v.validateNamespaces(req); err != nil {
108+
log.Errorf(ctx, "rejecting adjustment: %v", err)
109+
return err
110+
}
111+
105112
if err := v.validateRequiredPlugins(req); err != nil {
106113
log.Errorf(ctx, "rejecting adjustment: %v", err)
107114
return err
@@ -169,6 +176,32 @@ func (v *DefaultValidator) validateSeccompPolicy(req *api.ValidateContainerAdjus
169176
return nil
170177
}
171178

179+
func (v *DefaultValidator) validateNamespaces(req *api.ValidateContainerAdjustmentRequest) error {
180+
if req.Adjust == nil {
181+
return nil
182+
}
183+
184+
if !v.cfg.RejectNamespaceAdjustment {
185+
return nil
186+
}
187+
188+
owners, claimed := req.Owners.NamespaceOwners(req.Container.Id)
189+
if !claimed {
190+
return nil
191+
}
192+
193+
offenders := "plugin(s) "
194+
sep := ""
195+
196+
for ns, plugin := range owners {
197+
offenders += sep + fmt.Sprintf("%q (namespace %q)", plugin, ns)
198+
sep = ", "
199+
}
200+
201+
return fmt.Errorf("%w: attempted restricted namespace adjustment by %s",
202+
ErrValidation, offenders)
203+
}
204+
172205
func (v *DefaultValidator) validateRequiredPlugins(req *api.ValidateContainerAdjustmentRequest) error {
173206
var (
174207
container = req.GetContainer().GetName()

0 commit comments

Comments
 (0)