From 5bce1db05e4590f36a001038301fb41528305d2d Mon Sep 17 00:00:00 2001 From: Markus Blaschke Date: Mon, 19 Oct 2020 14:45:45 +0200 Subject: [PATCH] Implement namespace support for exceptions (#421) * Implement namespace support for exceptions Signed-off-by: Markus Blaschke * remove debug Signed-off-by: Markus Blaschke * Add documentation Signed-off-by: Markus Blaschke Co-authored-by: baderbuddy --- docs/usage.md | 9 ++++++++- examples/config-full.yaml | 6 ++++++ examples/config.yaml | 3 ++- pkg/config/config.go | 1 + pkg/config/exemptions.go | 7 ++++++- pkg/config/exemptions_test.go | 30 ++++++++++++++++++++++++------ pkg/config/schema.go | 2 +- pkg/validator/schema.go | 4 ++-- 8 files changed, 50 insertions(+), 12 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index d1d293e0b..61ff76e88 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -71,9 +71,16 @@ To exempt a controller from all checks via annotations, use the annotation `pola kubectl annotate deployment my-deployment polaris.fairwinds.com/exempt=true ``` -To exempt a controller via the config, you have to specify a list of controller names and a list of rules, e.g. +To exempt a controller via the config, you have to specify a namespace (optional), a list of controller names and a list of rules, e.g. ```yaml exemptions: + # exception valid for kube-system namespace + - namespace: kube-system + controllerNames: + - dns-controller + rules: + - hostNetworkSet + # exception valid in all namespaces - controllerNames: - dns-controller rules: diff --git a/examples/config-full.yaml b/examples/config-full.yaml index 19d01d07a..c4553d0d1 100644 --- a/examples/config-full.yaml +++ b/examples/config-full.yaml @@ -35,6 +35,12 @@ exemptions: rules: - hostNetworkSet - hostPortSet + - namespace: kube-system + controllerNames: + - my-network-controller + rules: + - hostNetworkSet + - hostPortSet customChecks: resourceLimits: diff --git a/examples/config.yaml b/examples/config.yaml index cd3b1dba0..403fa3556 100644 --- a/examples/config.yaml +++ b/examples/config.yaml @@ -27,7 +27,8 @@ checks: insecureCapabilities: warning exemptions: - - controllerNames: + - namespace: kube-system + controllerNames: - kube-apiserver - kube-proxy - kube-scheduler diff --git a/pkg/config/config.go b/pkg/config/config.go index e697926c3..0137f849a 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -40,6 +40,7 @@ type Configuration struct { type Exemption struct { Rules []string `json:"rules"` ControllerNames []string `json:"controllerNames"` + Namespace string `json:"namespace"` } var configBox = (*packr.Box)(nil) diff --git a/pkg/config/exemptions.go b/pkg/config/exemptions.go index 317117975..2f4135257 100644 --- a/pkg/config/exemptions.go +++ b/pkg/config/exemptions.go @@ -5,7 +5,7 @@ import ( ) // IsActionable determines whether a check is actionable given the current configuration -func (conf Configuration) IsActionable(ruleID, controllerName string) bool { +func (conf Configuration) IsActionable(ruleID, namespace, controllerName string) bool { if severity, ok := conf.Checks[ruleID]; !ok || !severity.IsActionable() { return false } @@ -14,10 +14,15 @@ func (conf Configuration) IsActionable(ruleID, controllerName string) bool { } for _, example := range conf.Exemptions { + if example.Namespace != "" && example.Namespace != namespace { + continue + } + for _, rule := range example.Rules { if rule != ruleID { continue } + for _, controller := range example.ControllerNames { if strings.HasPrefix(controllerName, controller) { return false diff --git a/pkg/config/exemptions_test.go b/pkg/config/exemptions_test.go index ebffc12c4..cead8aab2 100644 --- a/pkg/config/exemptions_test.go +++ b/pkg/config/exemptions_test.go @@ -39,10 +39,19 @@ exemptions: - test ` +var confNamespaceTest = ` +checks: + ANY: warning +exemptions: + - namespace: kube-system + controllerNames: + - test +` + func TestInclusiveExemption(t *testing.T) { parsedConf, _ := Parse([]byte(confExemptTest)) - applicable := parsedConf.IsActionable("ANY", "test") - applicableOtherController := parsedConf.IsActionable("ANY", "other") + applicable := parsedConf.IsActionable("ANY", "test", "test") + applicableOtherController := parsedConf.IsActionable("ANY","test", "other") assert.False(t, applicable, "Expected all checks to be exempted when their controller is specified.") assert.True(t, applicableOtherController, "Expected checks to only be exempted when their controller is specified.") @@ -50,13 +59,22 @@ func TestInclusiveExemption(t *testing.T) { func TestIndividualRuleException(t *testing.T) { parsedConf, _ := Parse([]byte(confExemptRuleTest)) - applicable := parsedConf.IsActionable("ANY", "test") - applicableOtherRule := parsedConf.IsActionable("OTHER", "test") - applicableOtherRuleOtherController := parsedConf.IsActionable("OTHER", "other") - applicableRuleOtherController := parsedConf.IsActionable("ANY", "other") + applicable := parsedConf.IsActionable("ANY", "test", "test") + applicableOtherRule := parsedConf.IsActionable("OTHER","test", "test") + applicableOtherRuleOtherController := parsedConf.IsActionable("OTHER","test", "other") + applicableRuleOtherController := parsedConf.IsActionable("ANY","test", "other") assert.False(t, applicable, "Expected all checks to be exempted when their controller and rule are specified.") assert.True(t, applicableOtherRule, "Expected checks to only be exempted when their controller and rule are specified.") assert.True(t, applicableOtherRuleOtherController, "Expected checks to only be exempted when their controller and rule are specified.") assert.True(t, applicableRuleOtherController, "Expected checks to only be exempted when their controller and rule are specified.") } + +func TestNamespaceExemption(t *testing.T) { + parsedConf, _ := Parse([]byte(confNamespaceTest)) + applicable := parsedConf.IsActionable("ANY", "kube-system", "test") + applicableOtherController := parsedConf.IsActionable("ANY","default", "test") + + assert.False(t, applicable, "Expected all checks to be exempted when their namespace and controller is specified.") + assert.True(t, applicableOtherController, "Expected checks to only be exempted when their namespace and controller is specified.") +} \ No newline at end of file diff --git a/pkg/config/schema.go b/pkg/config/schema.go index 255741b10..0ef8b0557 100644 --- a/pkg/config/schema.go +++ b/pkg/config/schema.go @@ -152,7 +152,7 @@ func (check SchemaCheck) CheckObject(obj interface{}) (bool, error) { } // IsActionable decides if this check applies to a particular target -func (check SchemaCheck) IsActionable(target TargetKind, controllerType string, isInit bool) bool { +func (check SchemaCheck) IsActionable(target TargetKind, namespace, controllerType string, isInit bool) bool { if check.Target != target { return false } diff --git a/pkg/validator/schema.go b/pkg/validator/schema.go index bc18d67b6..17c38e466 100644 --- a/pkg/validator/schema.go +++ b/pkg/validator/schema.go @@ -86,10 +86,10 @@ func resolveCheck(conf *config.Configuration, checkID string, controller kube.Ge if !ok { return nil, fmt.Errorf("Check %s not found", checkID) } - if !conf.IsActionable(check.ID, controller.ObjectMeta.GetName()) { + if !conf.IsActionable(check.ID, controller.ObjectMeta.GetNamespace(), controller.ObjectMeta.GetName()) { return nil, nil } - if !check.IsActionable(target, controller.Kind, isInitContainer) { + if !check.IsActionable(target, controller.ObjectMeta.GetNamespace(), controller.Kind, isInitContainer) { return nil, nil } return &check, nil