Skip to content

Commit

Permalink
Implement namespace support for exceptions (#421)
Browse files Browse the repository at this point in the history
* Implement namespace support for exceptions

Signed-off-by: Markus Blaschke <mblaschke82@gmail.com>

* remove debug

Signed-off-by: Markus Blaschke <mblaschke82@gmail.com>

* Add documentation

Signed-off-by: Markus Blaschke <mblaschke82@gmail.com>

Co-authored-by: baderbuddy <bader@fairwinds.com>
  • Loading branch information
mblaschke and baderbuddy authored Oct 19, 2020
1 parent b27f619 commit 5bce1db
Show file tree
Hide file tree
Showing 8 changed files with 50 additions and 12 deletions.
9 changes: 8 additions & 1 deletion docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 6 additions & 0 deletions examples/config-full.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ exemptions:
rules:
- hostNetworkSet
- hostPortSet
- namespace: kube-system
controllerNames:
- my-network-controller
rules:
- hostNetworkSet
- hostPortSet

customChecks:
resourceLimits:
Expand Down
3 changes: 2 additions & 1 deletion examples/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ checks:
insecureCapabilities: warning

exemptions:
- controllerNames:
- namespace: kube-system
controllerNames:
- kube-apiserver
- kube-proxy
- kube-scheduler
Expand Down
1 change: 1 addition & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 6 additions & 1 deletion pkg/config/exemptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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
Expand Down
30 changes: 24 additions & 6 deletions pkg/config/exemptions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,42 @@ 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.")
}

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.")
}
2 changes: 1 addition & 1 deletion pkg/config/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/validator/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 5bce1db

Please sign in to comment.