@@ -30,20 +30,12 @@ import (
3030 yaml "gopkg.in/yaml.v3"
3131)
3232
33- type DefaultValidatorConfig struct {
33+ type DefaultConfig struct {
3434 // Enable the default validator plugin.
3535 Enable bool `yaml:"enable" toml:"enable"`
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"`
36+ * Config
37+ // Overrides provide per-role overrides to the default configuration.
38+ Overrides map [string ]* Config `yaml:"overrides" toml:"overrides"`
4739 // RequiredPlugins list globally required plugins. These must be present
4840 // or otherwise validation will fail.
4941 // WARNING: This is a global setting and will affect all containers. In
@@ -59,9 +51,24 @@ type DefaultValidatorConfig struct {
5951 TolerateMissingAnnotation string `yaml:"tolerateMissingPluginsAnnotation" toml:"tolerate_missing_plugins_annotation"`
6052}
6153
54+ // Config provides validation defaults or per role overrides.
55+ type Config struct {
56+ // RejectOCIHookAdjustment fails validation if OCI hooks are adjusted.
57+ RejectOCIHookAdjustment * bool `yaml:"rejectOCIHookAdjustment" toml:"reject_oci_hook_adjustment"`
58+ // RejectRuntimeDefaultSeccompAdjustment fails validation if a runtime default seccomp
59+ // policy is adjusted.
60+ RejectRuntimeDefaultSeccompAdjustment * bool `yaml:"rejectRuntimeDefaultSeccompAdjustment" toml:"reject_runtime_default_seccomp_adjustment"`
61+ // RejectUnconfinedSeccompAdjustment fails validation if an unconfined seccomp policy is
62+ // adjusted.
63+ RejectUnconfinedSeccompAdjustment * bool `yaml:"rejectUnconfinedSeccompAdjustment" toml:"reject_unconfined_seccomp_adjustment"`
64+ // RejectCustomSeccompAdjustment fails validation if a custom seccomp policy (aka LOCALHOST)
65+ // is adjusted.
66+ RejectCustomSeccompAdjustment * bool `yaml:"rejectCustomSeccompAdjustment" toml:"reject_custom_seccomp_adjustment"`
67+ }
68+
6269// DefaultValidator implements default validation.
6370type DefaultValidator struct {
64- cfg DefaultValidatorConfig
71+ cfg DefaultConfig
6572}
6673
6774const (
@@ -75,12 +82,12 @@ var (
7582)
7683
7784// NewDefaultValidator creates a new instance of the validator.
78- func NewDefaultValidator (cfg * DefaultValidatorConfig ) * DefaultValidator {
85+ func NewDefaultValidator (cfg * DefaultConfig ) * DefaultValidator {
7986 return & DefaultValidator {cfg : * cfg }
8087}
8188
8289// SetConfig sets new configuration for the validator.
83- func (v * DefaultValidator ) SetConfig (cfg * DefaultValidatorConfig ) {
90+ func (v * DefaultValidator ) SetConfig (cfg * DefaultConfig ) {
8491 if cfg == nil {
8592 return
8693 }
@@ -92,50 +99,64 @@ func (v *DefaultValidator) ValidateContainerAdjustment(ctx context.Context, req
9299 log .Debugf (ctx , "Validating adjustment of container %s/%s/%s" ,
93100 req .GetPod ().GetNamespace (), req .GetPod ().GetName (), req .GetContainer ().GetName ())
94101
95- if err := v .validateOCIHooks (req ); err != nil {
102+ plugins := req .GetPluginMap ()
103+
104+ if err := v .validateOCIHooks (req , plugins ); err != nil {
96105 log .Errorf (ctx , "rejecting adjustment: %v" , err )
97106 return err
98107 }
99108
100- if err := v .validateSeccompPolicy (req ); err != nil {
109+ if err := v .validateSeccompPolicy (req , plugins ); err != nil {
101110 log .Errorf (ctx , "rejecting adjustment: %v" , err )
102111 return err
103112 }
104113
105- if err := v .validateRequiredPlugins (req ); err != nil {
114+ if err := v .validateRequiredPlugins (req , plugins ); err != nil {
106115 log .Errorf (ctx , "rejecting adjustment: %v" , err )
107116 return err
108117 }
109118
110119 return nil
111120}
112121
113- func (v * DefaultValidator ) validateOCIHooks (req * api.ValidateContainerAdjustmentRequest ) error {
122+ func (v * DefaultValidator ) validateOCIHooks (req * api.ValidateContainerAdjustmentRequest , plugins map [ string ] * api. PluginInstance ) error {
114123 if req .Adjust == nil {
115124 return nil
116125 }
117126
118- if ! v .cfg .RejectOCIHookAdjustment {
127+ owners , claimed := req .Owners .HooksOwner (req .Container .Id )
128+ if ! claimed {
119129 return nil
120130 }
121131
122- owners , claimed := req .Owners .HooksOwner (req .Container .Id )
123- if ! claimed {
132+ defaults := v .cfg .Config
133+ rejected := []string {}
134+
135+ for _ , p := range strings .Split (owners , "," ) {
136+ if instance , ok := plugins [p ]; ok {
137+ cfg := v .cfg .GetConfig (instance .GetRole ())
138+ if cfg .DenyOCIHookInjection (defaults ) {
139+ rejected = append (rejected , p )
140+ }
141+ }
142+ }
143+
144+ if len (rejected ) == 0 {
124145 return nil
125146 }
126147
127148 offender := ""
128149
129- if ! strings . Contains ( owners , "," ) {
130- offender = fmt .Sprintf ("plugin %q" , owners )
150+ if len ( rejected ) == 1 {
151+ offender = fmt .Sprintf ("plugin %q" , rejected [ 0 ] )
131152 } else {
132- offender = fmt .Sprintf ("plugins %q" , owners )
153+ offender = fmt .Sprintf ("plugins %q" , strings . Join ( rejected , "," ) )
133154 }
134155
135156 return fmt .Errorf ("%w: %s attempted restricted OCI hook injection" , ErrValidation , offender )
136157}
137158
138- func (v * DefaultValidator ) validateSeccompPolicy (req * api.ValidateContainerAdjustmentRequest ) error {
159+ func (v * DefaultValidator ) validateSeccompPolicy (req * api.ValidateContainerAdjustmentRequest , plugins map [ string ] * api. PluginInstance ) error {
139160 if req .Adjust == nil {
140161 return nil
141162 }
@@ -145,22 +166,31 @@ func (v *DefaultValidator) validateSeccompPolicy(req *api.ValidateContainerAdjus
145166 return nil
146167 }
147168
169+ var (
170+ cfg * Config
171+ defaults = v .cfg .Config
172+ )
173+
174+ if instance , ok := plugins [owner ]; ok {
175+ cfg = v .cfg .GetConfig (instance .GetRole ())
176+ }
177+
148178 profile := req .Container .GetLinux ().GetSeccompProfile ()
149179 switch {
150180 case profile == nil || profile .GetProfileType () == api .SecurityProfile_UNCONFINED :
151- if v . cfg .RejectUnconfinedSeccompAdjustment {
181+ if cfg .DenyUnconfinedSeccompAdjustment ( defaults ) {
152182 return fmt .Errorf ("%w: plugin %s attempted restricted " +
153183 " unconfined seccomp policy adjustment" , ErrValidation , owner )
154184 }
155185
156186 case profile .GetProfileType () == api .SecurityProfile_RUNTIME_DEFAULT :
157- if v . cfg .RejectRuntimeDefaultSeccompAdjustment {
187+ if cfg .DenyRuntimeDefaultSeccompAdjustment ( defaults ) {
158188 return fmt .Errorf ("%w: plugin %s attempted restricted " +
159189 "runtime default seccomp policy adjustment" , ErrValidation , owner )
160190 }
161191
162192 case profile .GetProfileType () == api .SecurityProfile_LOCALHOST :
163- if v . cfg .RejectCustomSeccompAdjustment {
193+ if cfg .DenyCustomSeccompAdjustment ( defaults ) {
164194 return fmt .Errorf ("%w: plugin %s attempted restricted " +
165195 " custom seccomp policy adjustment" , ErrValidation , owner )
166196 }
@@ -169,7 +199,7 @@ func (v *DefaultValidator) validateSeccompPolicy(req *api.ValidateContainerAdjus
169199 return nil
170200}
171201
172- func (v * DefaultValidator ) validateRequiredPlugins (req * api.ValidateContainerAdjustmentRequest ) error {
202+ func (v * DefaultValidator ) validateRequiredPlugins (req * api.ValidateContainerAdjustmentRequest , plugins map [ string ] * api. PluginInstance ) error {
173203 var (
174204 container = req .GetContainer ().GetName ()
175205 required = slices .Clone (v .cfg .RequiredPlugins )
@@ -200,7 +230,6 @@ func (v *DefaultValidator) validateRequiredPlugins(req *api.ValidateContainerAdj
200230 return nil
201231 }
202232
203- plugins := req .GetPluginMap ()
204233 missing := []string {}
205234
206235 for _ , r := range required {
@@ -223,3 +252,56 @@ func (v *DefaultValidator) validateRequiredPlugins(req *api.ValidateContainerAdj
223252
224253 return fmt .Errorf ("%w: %s not present" , ErrValidation , offender )
225254}
255+
256+ // GetConfig returns overrides for the named role if it exists in the
257+ // configuration.
258+ func (cfg * DefaultConfig ) GetConfig (role string ) * Config {
259+ if cfg == nil || cfg .Overrides == nil {
260+ return nil
261+ }
262+ return cfg .Overrides [role ]
263+ }
264+
265+ // DenyOCIHookInjection checks whether OCI hook injection should be denied
266+ // based on the configuration, using an optional fallbak configuration if
267+ // this one is nil or omits configuration.
268+ func (cfg * Config ) DenyOCIHookInjection (fallback * Config ) bool {
269+ if cfg != nil && cfg .RejectOCIHookAdjustment != nil {
270+ return * cfg .RejectOCIHookAdjustment
271+ }
272+
273+ return fallback != nil && fallback .DenyOCIHookInjection (nil )
274+ }
275+
276+ // DenyUnconfinedSeccompAdjustment checks whether adjustment of an unconfined
277+ // seccomp policy should be denied based on the configuration, using an optional
278+ // fallback configuration if this one is nil or omits configuration.
279+ func (cfg * Config ) DenyUnconfinedSeccompAdjustment (fallback * Config ) bool {
280+ if cfg != nil && cfg .RejectUnconfinedSeccompAdjustment != nil {
281+ return * cfg .RejectUnconfinedSeccompAdjustment
282+ }
283+
284+ return fallback != nil && fallback .DenyUnconfinedSeccompAdjustment (nil )
285+ }
286+
287+ // DenyRuntimeDefaultSeccompAdjustment checks whether adjustment of a runtime
288+ // default seccomp policy should be denied based on the configuration, using an
289+ // optional fallback configuration if this one is nil or omits configuration.
290+ func (cfg * Config ) DenyRuntimeDefaultSeccompAdjustment (fallback * Config ) bool {
291+ if cfg != nil && cfg .RejectRuntimeDefaultSeccompAdjustment != nil {
292+ return * cfg .RejectRuntimeDefaultSeccompAdjustment
293+ }
294+
295+ return fallback != nil && fallback .DenyRuntimeDefaultSeccompAdjustment (nil )
296+ }
297+
298+ // DenyCustomSeccompAdjustment checks whether adjustment of a custom (localhost)
299+ // seccomp policy should be denied based on the configuration, using an optional
300+ // fallback configuration if this one is nil or omits configuration.
301+ func (cfg * Config ) DenyCustomSeccompAdjustment (fallback * Config ) bool {
302+ if cfg != nil && cfg .RejectCustomSeccompAdjustment != nil {
303+ return * cfg .RejectCustomSeccompAdjustment
304+ }
305+
306+ return fallback != nil && fallback .DenyCustomSeccompAdjustment (nil )
307+ }
0 commit comments