From 3a2fb3584b291b7a67f38b57da17b50d07d9e1d9 Mon Sep 17 00:00:00 2001 From: skatika Date: Wed, 16 Dec 2020 15:52:48 -0500 Subject: [PATCH 01/14] Refactor common code --- pkg/config/exemptions.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pkg/config/exemptions.go b/pkg/config/exemptions.go index 2f413525..01829bf9 100644 --- a/pkg/config/exemptions.go +++ b/pkg/config/exemptions.go @@ -18,18 +18,16 @@ func (conf Configuration) IsActionable(ruleID, namespace, controllerName string) continue } + checkIfActionable := false for _, rule := range example.Rules { if rule != ruleID { continue } - - for _, controller := range example.ControllerNames { - if strings.HasPrefix(controllerName, controller) { - return false - } - } + checkIfActionable = true + break } - if len(example.Rules) == 0 { + + if len(example.Rules) == 0 || checkIfActionable { for _, controller := range example.ControllerNames { if strings.HasPrefix(controllerName, controller) { return false From ca6e4b43e4946d25f04486d8187813a75c52ef46 Mon Sep 17 00:00:00 2001 From: skatika Date: Wed, 16 Dec 2020 15:53:22 -0500 Subject: [PATCH 02/14] Rename to receivers to same name --- pkg/config/config.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 0137f849..4a8bce93 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -40,7 +40,7 @@ type Configuration struct { type Exemption struct { Rules []string `json:"rules"` ControllerNames []string `json:"controllerNames"` - Namespace string `json:"namespace"` + Namespace string `json:"namespace"` } var configBox = (*packr.Box)(nil) @@ -102,8 +102,8 @@ func Parse(rawBytes []byte) (Configuration, error) { } // Validate checks if a config is valid -func (c Configuration) Validate() error { - if len(c.Checks) == 0 { +func (conf Configuration) Validate() error { + if len(conf.Checks) == 0 { return errors.New("No checks were enabled") } return nil From 9dd7f0947a6cda7c6ef78792fce23b34bf0a7db9 Mon Sep 17 00:00:00 2001 From: skatika Date: Wed, 16 Dec 2020 17:15:09 -0500 Subject: [PATCH 03/14] Add a comma --- docs-md/customization/exemptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs-md/customization/exemptions.md b/docs-md/customization/exemptions.md index 8e6d28a8..9a755ac7 100644 --- a/docs-md/customization/exemptions.md +++ b/docs-md/customization/exemptions.md @@ -18,7 +18,7 @@ kubectl annotate deployment my-deployment polaris.fairwinds.com/cpuRequestsMissi ## Config -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. +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: # exemption valid for kube-system namespace From e57668fc7511ea60def3c68d212a369f44b36af8 Mon Sep 17 00:00:00 2001 From: skatika Date: Wed, 16 Dec 2020 17:17:43 -0500 Subject: [PATCH 04/14] Fix typos --- pkg/validator/controller.go | 4 ++-- pkg/validator/pod_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/validator/controller.go b/pkg/validator/controller.go index 35024f7e..a2077177 100644 --- a/pkg/validator/controller.go +++ b/pkg/validator/controller.go @@ -54,14 +54,14 @@ func ValidateController(ctx context.Context, conf *conf.Configuration, controlle func ValidateControllers(ctx context.Context, config *conf.Configuration, kubeResources *kube.ResourceProvider) ([]ControllerResult, error) { controllersToAudit := kubeResources.Controllers - results := []ControllerResult{} + var results []ControllerResult for _, controller := range controllersToAudit { if !config.DisallowExemptions && hasExemptionAnnotation(controller) { continue } result, err := ValidateController(ctx, config, controller) if err != nil { - logrus.Warn("An error occured validating controller:", err) + logrus.Warn("An error occurred validating controller:", err) return nil, err } results = append(results, result) diff --git a/pkg/validator/pod_test.go b/pkg/validator/pod_test.go index bfbc22ab..c72f6443 100644 --- a/pkg/validator/pod_test.go +++ b/pkg/validator/pod_test.go @@ -100,7 +100,7 @@ func TestInvalidIPCPod(t *testing.T) { assert.EqualValues(t, expectedResults, actualPodResult.Results) } -func TestInvalidNeworkPod(t *testing.T) { +func TestInvalidNetworkPod(t *testing.T) { c := conf.Configuration{ Checks: map[string]conf.Severity{ "hostNetworkSet": conf.SeverityWarning, @@ -185,7 +185,7 @@ func TestExemption(t *testing.T) { "hostPortSet": conf.SeverityDanger, }, Exemptions: []conf.Exemption{ - conf.Exemption{ + { Rules: []string{"hostIPCSet"}, ControllerNames: []string{"foo"}, }, From 272e06bbec171826c13c736f4c243d3b8e168149 Mon Sep 17 00:00:00 2001 From: skatika Date: Wed, 16 Dec 2020 17:21:50 -0500 Subject: [PATCH 05/14] Add ContainerNames to Exemption struct --- pkg/config/config.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 4a8bce93..9f7d4b87 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -23,7 +23,7 @@ import ( "net/http" "strings" - packr "github.com/gobuffalo/packr/v2" + "github.com/gobuffalo/packr/v2" "k8s.io/apimachinery/pkg/util/yaml" ) @@ -40,6 +40,7 @@ type Configuration struct { type Exemption struct { Rules []string `json:"rules"` ControllerNames []string `json:"controllerNames"` + ContainerNames []string `json:"containerNames"` Namespace string `json:"namespace"` } @@ -59,14 +60,14 @@ func ParseFile(path string) (Configuration, error) { if path == "" { rawBytes, err = getConfigBox().Find("config.yaml") } else if strings.HasPrefix(path, "https://") || strings.HasPrefix(path, "http://") { - //path is a url + // path is a url response, err2 := http.Get(path) if err2 != nil { return Configuration{}, err2 } rawBytes, err = ioutil.ReadAll(response.Body) } else { - //path is local + // path is local rawBytes, err = ioutil.ReadFile(path) } if err != nil { From fdd30717e51915cb0bb535c9ba516479f97bdf20 Mon Sep 17 00:00:00 2001 From: skatika Date: Thu, 17 Dec 2020 09:54:29 -0500 Subject: [PATCH 06/14] Remove unused parameter --- cmd/polaris/audit.go | 2 +- go.sum | 20 -------------------- pkg/dashboard/dashboard.go | 4 ++-- pkg/validator/container.go | 14 ++++++-------- pkg/validator/container_test.go | 15 +++++++-------- pkg/validator/controller.go | 11 +++++------ pkg/validator/controller_test.go | 15 +++++++-------- pkg/validator/fullaudit.go | 5 ++--- pkg/validator/fullaudit_test.go | 2 +- pkg/validator/pod.go | 8 +++----- pkg/validator/pod_test.go | 10 +++++----- pkg/validator/schema.go | 7 +++---- pkg/validator/schema_test.go | 5 ++--- pkg/webhook/webhook.go | 2 +- test/checks_test.go | 3 +-- 15 files changed, 46 insertions(+), 77 deletions(-) diff --git a/cmd/polaris/audit.go b/cmd/polaris/audit.go index aa00435b..3b7d6611 100644 --- a/cmd/polaris/audit.go +++ b/cmd/polaris/audit.go @@ -80,7 +80,7 @@ func runAndReportAudit(ctx context.Context, c conf.Configuration, auditPath, wor logrus.Errorf("Error fetching Kubernetes resources %v", err) os.Exit(1) } - auditData, err := validator.RunAudit(ctx, c, k) + auditData, err := validator.RunAudit(c, k) if err != nil { logrus.Errorf("Error while running audit on resources: %v", err) diff --git a/go.sum b/go.sum index 251ffffb..1f87039f 100644 --- a/go.sum +++ b/go.sum @@ -231,8 +231,6 @@ github.com/gobuffalo/logger v1.0.3 h1:YaXOTHNPCvkqqA7w05A4v0k2tCdpr+sgFlgINbQ6gq github.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM= github.com/gobuffalo/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM= github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI= -github.com/gobuffalo/packr/v2 v2.8.0 h1:IULGd15bQL59ijXLxEvA5wlMxsmx/ZkQv9T282zNVIY= -github.com/gobuffalo/packr/v2 v2.8.0/go.mod h1:PDk2k3vGevNE3SwVyVRgQCCXETC9SaONCNSXT1Q8M1g= github.com/gobuffalo/packr/v2 v2.8.1 h1:tkQpju6i3EtMXJ9uoF5GT6kB+LMTimDWD8Xvbz6zDVA= github.com/gobuffalo/packr/v2 v2.8.1/go.mod h1:c/PLlOuTU+p3SybaJATW3H6lX/iK7xEz5OeMf+NnJpg= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= @@ -372,7 +370,6 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/karrick/godirwalk v1.15.3/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/karrick/godirwalk v1.15.8/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= @@ -555,8 +552,6 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.1 h1:KfztREH0tPxJJ+geloSLaAkaPkr4ki2Er5quFV1TDo4= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= @@ -994,27 +989,18 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.18.4/go.mod h1:lOIQAKYgai1+vz9J7YcDZwC26Z0zQewYOGWdyIPUUQ4= k8s.io/api v0.18.6/go.mod h1:eeyxr+cwCjMdLAmr2W3RyDI0VvTawSg/3RFFBEnmZGI= k8s.io/api v0.18.8 h1:aIKUzJPb96f3fKec2lxtY7acZC9gQNDLVhfSGpxBAC4= k8s.io/api v0.18.8/go.mod h1:d/CXqwWv+Z2XEG1LgceeDmHQwpUJhROPx16SlxJgERY= -k8s.io/apiextensions-apiserver v0.18.4 h1:Y3HGERmS8t9u12YNUFoOISqefaoGRuTc43AYCLzWmWE= -k8s.io/apiextensions-apiserver v0.18.4/go.mod h1:NYeyeYq4SIpFlPxSAB6jHPIdvu3hL0pc36wuRChybio= k8s.io/apiextensions-apiserver v0.18.6 h1:vDlk7cyFsDyfwn2rNAO2DbmUbvXy5yT5GE3rrqOzaMo= k8s.io/apiextensions-apiserver v0.18.6/go.mod h1:lv89S7fUysXjLZO7ke783xOwVTm6lKizADfvUM/SS/M= -k8s.io/apimachinery v0.18.4/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= k8s.io/apimachinery v0.18.6/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= k8s.io/apimachinery v0.18.8 h1:jimPrycCqgx2QPearX3to1JePz7wSbVLq+7PdBTTwQ0= k8s.io/apimachinery v0.18.8/go.mod h1:6sQd+iHEqmOtALqOFjSWp2KZ9F0wlU/nWm0ZgsYWMig= -k8s.io/apiserver v0.18.4/go.mod h1:q+zoFct5ABNnYkGIaGQ3bcbUNdmPyOCoEBcg51LChY8= k8s.io/apiserver v0.18.6/go.mod h1:Zt2XvTHuaZjBz6EFYzpp+X4hTmgWGy8AthNVnTdm3Wg= -k8s.io/client-go v0.18.4 h1:un55V1Q/B3JO3A76eS0kUSywgGK/WR3BQ8fHQjNa6Zc= -k8s.io/client-go v0.18.4/go.mod h1:f5sXwL4yAZRkAtzOxRWUhA/N8XzGCb+nPZI8PfobZ9g= k8s.io/client-go v0.18.6 h1:I+oWqJbibLSGsZj8Xs8F0aWVXJVIoUHWaaJV3kUN/Zw= k8s.io/client-go v0.18.6/go.mod h1:/fwtGLjYMS1MaM5oi+eXhKwG+1UHidUEXRh6cNsdO0Q= -k8s.io/code-generator v0.18.4/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= k8s.io/code-generator v0.18.6/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c= -k8s.io/component-base v0.18.4/go.mod h1:7jr/Ef5PGmKwQhyAz/pjByxJbC58mhKAhiaDu0vXfPk= k8s.io/component-base v0.18.6/go.mod h1:knSVsibPR5K6EW2XOjEHik6sdU5nCvKMrzMt2D4In14= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200114144118-36b2048a9120/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= @@ -1035,12 +1021,6 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT7lCHcxMU+mDHEm+nx46H4zuuHZkDP6icnhu0= -sigs.k8s.io/controller-runtime v0.6.1 h1:LcK2+nk0kmaOnKGN+vBcWHqY5WDJNJNB/c5pW+sU8fc= -sigs.k8s.io/controller-runtime v0.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gEORz0efEja7A= -sigs.k8s.io/controller-runtime v0.6.2 h1:jkAnfdTYBpFwlmBn3pS5HFO06SfxvnTZ1p5PeEF/zAA= -sigs.k8s.io/controller-runtime v0.6.2/go.mod h1:vhcq/rlnENJ09SIRp3EveTaZ0yqH526hjf9iJdbUJ/E= -sigs.k8s.io/controller-runtime v0.6.3 h1:SBbr+inLPEKhvlJtrvDcwIpm+uhDvp63Bl72xYJtoOE= -sigs.k8s.io/controller-runtime v0.6.3/go.mod h1:WlZNXcM0++oyaQt4B7C2lEE5JYRs8vJUzRP4N4JpdAY= sigs.k8s.io/controller-runtime v0.6.4 h1:4013CKsBs5bEqo+LevzDett+LLxag/FjQWG94nVZ/9g= sigs.k8s.io/controller-runtime v0.6.4/go.mod h1:WlZNXcM0++oyaQt4B7C2lEE5JYRs8vJUzRP4N4JpdAY= sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= diff --git a/pkg/dashboard/dashboard.go b/pkg/dashboard/dashboard.go index 64c7c302..eaacb778 100644 --- a/pkg/dashboard/dashboard.go +++ b/pkg/dashboard/dashboard.go @@ -191,7 +191,7 @@ func GetRouter(c config.Configuration, auditPath string, port int, basePath stri return } - auditDataObj, err := validator.RunAudit(r.Context(), adjustedConf, k) + auditDataObj, err := validator.RunAudit(adjustedConf, k) if err != nil { http.Error(w, "Error Fetching Deployments", http.StatusInternalServerError) return @@ -224,7 +224,7 @@ func GetRouter(c config.Configuration, auditPath string, port int, basePath stri return } - auditData, err := validator.RunAudit(r.Context(), adjustedConf, k) + auditData, err := validator.RunAudit(adjustedConf, k) if err != nil { logrus.Errorf("Error getting audit data: %v", err) http.Error(w, "Error running audit", 500) diff --git a/pkg/validator/container.go b/pkg/validator/container.go index 44f138a2..1fa8b387 100644 --- a/pkg/validator/container.go +++ b/pkg/validator/container.go @@ -15,8 +15,6 @@ package validator import ( - "context" - "github.com/fairwindsops/polaris/pkg/config" "github.com/fairwindsops/polaris/pkg/kube" @@ -24,8 +22,8 @@ import ( ) // ValidateContainer validates a single container from a given controller -func ValidateContainer(ctx context.Context, conf *config.Configuration, controller kube.GenericWorkload, container *corev1.Container, isInit bool) (ContainerResult, error) { - results, err := applyContainerSchemaChecks(ctx, conf, controller, container, isInit) +func ValidateContainer(conf *config.Configuration, controller kube.GenericWorkload, container *corev1.Container, isInit bool) (ContainerResult, error) { + results, err := applyContainerSchemaChecks(conf, controller, container, isInit) if err != nil { return ContainerResult{}, err } @@ -39,18 +37,18 @@ func ValidateContainer(ctx context.Context, conf *config.Configuration, controll } // ValidateAllContainers validates both init and regular containers -func ValidateAllContainers(ctx context.Context, conf *config.Configuration, controller kube.GenericWorkload) ([]ContainerResult, error) { - results := []ContainerResult{} +func ValidateAllContainers(conf *config.Configuration, controller kube.GenericWorkload) ([]ContainerResult, error) { + var results []ContainerResult pod := controller.PodSpec for _, container := range pod.InitContainers { - result, err := ValidateContainer(ctx, conf, controller, &container, true) + result, err := ValidateContainer(conf, controller, &container, true) if err != nil { return nil, err } results = append(results, result) } for _, container := range pod.Containers { - result, err := ValidateContainer(ctx, conf, controller, &container, false) + result, err := ValidateContainer(conf, controller, &container, false) if err != nil { return nil, err } diff --git a/pkg/validator/container_test.go b/pkg/validator/container_test.go index 4ab651b1..a75526b7 100644 --- a/pkg/validator/container_test.go +++ b/pkg/validator/container_test.go @@ -15,7 +15,6 @@ package validator import ( - "context" "fmt" "testing" @@ -69,7 +68,7 @@ func testValidateWithWorkload(t *testing.T, container *corev1.Container, resourc parsedConf, err := conf.Parse([]byte(*resourceConf)) assert.NoError(t, err, "Expected no error when parsing config") - results, err := applyContainerSchemaChecks(context.Background(), &parsedConf, workload, container, false) + results, err := applyContainerSchemaChecks(&parsedConf, workload, container, false) if err != nil { panic(err) } @@ -90,7 +89,7 @@ func TestValidateResourcesEmptyConfig(t *testing.T) { Name: "Empty", } - results, err := applyContainerSchemaChecks(context.Background(), &conf.Configuration{}, getEmptyWorkload(t, ""), container, false) + results, err := applyContainerSchemaChecks(&conf.Configuration{}, getEmptyWorkload(t, ""), container, false) if err != nil { panic(err) } @@ -187,7 +186,7 @@ func TestValidateHealthChecks(t *testing.T) { for idx, tt := range testCases { t.Run(tt.name, func(t *testing.T) { controller := getEmptyWorkload(t, "") - results, err := applyContainerSchemaChecks(context.Background(), &conf.Configuration{Checks: tt.probes}, controller, tt.container, tt.isInit) + results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.probes}, controller, tt.container, tt.isInit) if err != nil { panic(err) } @@ -301,7 +300,7 @@ func TestValidateImage(t *testing.T) { for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { controller := getEmptyWorkload(t, "") - results, err := applyContainerSchemaChecks(context.Background(), &conf.Configuration{Checks: tt.image}, controller, tt.container, false) + results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.image}, controller, tt.container, false) if err != nil { panic(err) } @@ -418,7 +417,7 @@ func TestValidateNetworking(t *testing.T) { for _, tt := range testCases { t.Run(tt.name, func(t *testing.T) { controller := getEmptyWorkload(t, "") - results, err := applyContainerSchemaChecks(context.Background(), &conf.Configuration{Checks: tt.networkConf}, controller, tt.container, false) + results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.networkConf}, controller, tt.container, false) if err != nil { panic(err) } @@ -923,7 +922,7 @@ func TestValidateSecurity(t *testing.T) { t.Run(tt.name, func(t *testing.T) { workload, err := kube.NewGenericWorkloadFromPod(corev1.Pod{Spec: *tt.pod}, nil) assert.NoError(t, err) - results, err := applyContainerSchemaChecks(context.Background(), &conf.Configuration{Checks: tt.securityConf}, workload, tt.container, false) + results, err := applyContainerSchemaChecks(&conf.Configuration{Checks: tt.securityConf}, workload, tt.container, false) if err != nil { panic(err) } @@ -1068,7 +1067,7 @@ func TestValidateRunAsRoot(t *testing.T) { t.Run(tt.name, func(t *testing.T) { workload, err := kube.NewGenericWorkloadFromPod(corev1.Pod{Spec: *tt.pod}, nil) assert.NoError(t, err) - results, err := applyContainerSchemaChecks(context.Background(), &config, workload, tt.container, false) + results, err := applyContainerSchemaChecks(&config, workload, tt.container, false) if err != nil { panic(err) } diff --git a/pkg/validator/controller.go b/pkg/validator/controller.go index a2077177..90b4a071 100644 --- a/pkg/validator/controller.go +++ b/pkg/validator/controller.go @@ -15,7 +15,6 @@ package validator import ( - "context" "strings" "github.com/sirupsen/logrus" @@ -27,13 +26,13 @@ import ( const exemptionAnnotationKey = "polaris.fairwinds.com/exempt" // ValidateController validates a single controller, returns a ControllerResult. -func ValidateController(ctx context.Context, conf *conf.Configuration, controller kube.GenericWorkload) (ControllerResult, error) { - podResult, err := ValidatePod(ctx, conf, controller) +func ValidateController(conf *conf.Configuration, controller kube.GenericWorkload) (ControllerResult, error) { + podResult, err := ValidatePod(conf, controller) if err != nil { return ControllerResult{}, err } - controllerResult, err := applyControllerSchemaChecks(ctx, conf, controller) + controllerResult, err := applyControllerSchemaChecks(conf, controller) if err != nil { return ControllerResult{}, err } @@ -51,7 +50,7 @@ func ValidateController(ctx context.Context, conf *conf.Configuration, controlle // ValidateControllers validates that each deployment conforms to the Polaris config, // builds a list of ResourceResults organized by namespace. -func ValidateControllers(ctx context.Context, config *conf.Configuration, kubeResources *kube.ResourceProvider) ([]ControllerResult, error) { +func ValidateControllers(config *conf.Configuration, kubeResources *kube.ResourceProvider) ([]ControllerResult, error) { controllersToAudit := kubeResources.Controllers var results []ControllerResult @@ -59,7 +58,7 @@ func ValidateControllers(ctx context.Context, config *conf.Configuration, kubeRe if !config.DisallowExemptions && hasExemptionAnnotation(controller) { continue } - result, err := ValidateController(ctx, config, controller) + result, err := ValidateController(config, controller) if err != nil { logrus.Warn("An error occurred validating controller:", err) return nil, err diff --git a/pkg/validator/controller_test.go b/pkg/validator/controller_test.go index 8bfed505..54231148 100644 --- a/pkg/validator/controller_test.go +++ b/pkg/validator/controller_test.go @@ -15,7 +15,6 @@ package validator import ( - "context" "testing" "github.com/stretchr/testify/assert" @@ -47,7 +46,7 @@ func TestValidateController(t *testing.T) { "hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "danger", Category: "Security"}, } - actualResult, err := ValidateController(context.Background(), &c, deployment) + actualResult, err := ValidateController(&c, deployment) if err != nil { panic(err) } @@ -82,7 +81,7 @@ func TestControllerLevelChecks(t *testing.T) { for _, controller := range resources.Controllers { if controller.Kind == "Deployment" && controller.ObjectMeta.GetName() == "test-deployment" { - actualResult, err := ValidateController(context.Background(), &c, controller) + actualResult, err := ValidateController(&c, controller) if err != nil { panic(err) } @@ -117,7 +116,7 @@ func TestSkipHealthChecks(t *testing.T) { "readinessProbeMissing": {ID: "readinessProbeMissing", Message: "Readiness probe should be configured", Success: false, Severity: "danger", Category: "Reliability"}, "livenessProbeMissing": {ID: "livenessProbeMissing", Message: "Liveness probe should be configured", Success: false, Severity: "warning", Category: "Reliability"}, } - actualResult, err := ValidateController(context.Background(), &c, deployment) + actualResult, err := ValidateController(&c, deployment) if err != nil { panic(err) } @@ -136,7 +135,7 @@ func TestSkipHealthChecks(t *testing.T) { Dangers: uint(0), } expectedResults = ResultSet{} - actualResult, err = ValidateController(context.Background(), &c, job) + actualResult, err = ValidateController(&c, job) if err != nil { panic(err) } @@ -154,7 +153,7 @@ func TestSkipHealthChecks(t *testing.T) { Dangers: uint(0), } expectedResults = ResultSet{} - actualResult, err = ValidateController(context.Background(), &c, cronjob) + actualResult, err = ValidateController(&c, cronjob) if err != nil { panic(err) } @@ -184,7 +183,7 @@ func TestControllerExemptions(t *testing.T) { Warnings: uint(1), Dangers: uint(1), } - actualResults, err := ValidateControllers(context.Background(), &c, resources) + actualResults, err := ValidateControllers(&c, resources) if err != nil { panic(err) } @@ -195,7 +194,7 @@ func TestControllerExemptions(t *testing.T) { resources.Controllers[0].ObjectMeta.SetAnnotations(map[string]string{ exemptionAnnotationKey: "true", }) - actualResults, err = ValidateControllers(context.Background(), &c, resources) + actualResults, err = ValidateControllers(&c, resources) if err != nil { panic(err) } diff --git a/pkg/validator/fullaudit.go b/pkg/validator/fullaudit.go index c7cccc91..1fe4b020 100644 --- a/pkg/validator/fullaudit.go +++ b/pkg/validator/fullaudit.go @@ -2,7 +2,6 @@ package validator import ( "bytes" - "context" "fmt" "io" "io/ioutil" @@ -17,13 +16,13 @@ import ( ) // RunAudit runs a full Polaris audit and returns an AuditData object -func RunAudit(ctx context.Context, config conf.Configuration, kubeResources *kube.ResourceProvider) (AuditData, error) { +func RunAudit(config conf.Configuration, kubeResources *kube.ResourceProvider) (AuditData, error) { displayName := config.DisplayName if displayName == "" { displayName = kubeResources.SourceName } - results, err := ValidateControllers(ctx, &config, kubeResources) + results, err := ValidateControllers(&config, kubeResources) if err != nil { return AuditData{}, err } diff --git a/pkg/validator/fullaudit_test.go b/pkg/validator/fullaudit_test.go index 63690da8..943cdd2a 100644 --- a/pkg/validator/fullaudit_test.go +++ b/pkg/validator/fullaudit_test.go @@ -32,7 +32,7 @@ func TestGetTemplateData(t *testing.T) { Dangers: uint(1), } - actualAudit, err := RunAudit(context.Background(), c, resources) + actualAudit, err := RunAudit(c, resources) assert.Equal(t, err, nil, "error should be nil") diff --git a/pkg/validator/pod.go b/pkg/validator/pod.go index e159cd75..6bef7b91 100644 --- a/pkg/validator/pod.go +++ b/pkg/validator/pod.go @@ -15,15 +15,13 @@ package validator import ( - "context" - "github.com/fairwindsops/polaris/pkg/config" "github.com/fairwindsops/polaris/pkg/kube" ) // ValidatePod validates that each pod conforms to the Polaris config, returns a ResourceResult. -func ValidatePod(ctx context.Context, conf *config.Configuration, controller kube.GenericWorkload) (PodResult, error) { - podResults, err := applyPodSchemaChecks(ctx, conf, controller) +func ValidatePod(conf *config.Configuration, controller kube.GenericWorkload) (PodResult, error) { + podResults, err := applyPodSchemaChecks(conf, controller) if err != nil { return PodResult{}, err } @@ -32,7 +30,7 @@ func ValidatePod(ctx context.Context, conf *config.Configuration, controller kub ContainerResults: []ContainerResult{}, } - pRes.ContainerResults, err = ValidateAllContainers(ctx, conf, controller) + pRes.ContainerResults, err = ValidateAllContainers(conf, controller) if err != nil { return pRes, err } diff --git a/pkg/validator/pod_test.go b/pkg/validator/pod_test.go index c72f6443..6d04345c 100644 --- a/pkg/validator/pod_test.go +++ b/pkg/validator/pod_test.go @@ -53,7 +53,7 @@ func TestValidatePod(t *testing.T) { "hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "danger", Category: "Security"}, } - actualPodResult, err := ValidatePod(context.Background(), &c, deployment) + actualPodResult, err := ValidatePod(&c, deployment) if err != nil { panic(err) } @@ -90,7 +90,7 @@ func TestInvalidIPCPod(t *testing.T) { "hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "danger", Category: "Security"}, } - actualPodResult, err := ValidatePod(context.Background(), &c, workload) + actualPodResult, err := ValidatePod(&c, workload) if err != nil { panic(err) } @@ -128,7 +128,7 @@ func TestInvalidNetworkPod(t *testing.T) { "hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "danger", Category: "Security"}, } - actualPodResult, err := ValidatePod(context.Background(), &c, workload) + actualPodResult, err := ValidatePod(&c, workload) if err != nil { panic(err) } @@ -166,7 +166,7 @@ func TestInvalidPIDPod(t *testing.T) { "hostNetworkSet": {ID: "hostNetworkSet", Message: "Host network is not configured", Success: true, Severity: "warning", Category: "Security"}, } - actualPodResult, err := ValidatePod(context.Background(), &c, workload) + actualPodResult, err := ValidatePod(&c, workload) if err != nil { panic(err) } @@ -211,7 +211,7 @@ func TestExemption(t *testing.T) { "hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "danger", Category: "Security"}, } - actualPodResult, err := ValidatePod(context.Background(), &c, workload) + actualPodResult, err := ValidatePod(&c, workload) if err != nil { panic(err) } diff --git a/pkg/validator/schema.go b/pkg/validator/schema.go index 17c38e46..6d1e5a89 100644 --- a/pkg/validator/schema.go +++ b/pkg/validator/schema.go @@ -2,7 +2,6 @@ package validator import ( "bytes" - "context" "fmt" "io" "sort" @@ -114,7 +113,7 @@ func getExemptKey(checkID string) string { return fmt.Sprintf("polaris.fairwinds.com/%s-exempt", checkID) } -func applyPodSchemaChecks(ctx context.Context, conf *config.Configuration, controller kube.GenericWorkload) (ResultSet, error) { +func applyPodSchemaChecks(conf *config.Configuration, controller kube.GenericWorkload) (ResultSet, error) { results := ResultSet{} checkIDs := getSortedKeys(conf.Checks) objectAnnotations := controller.ObjectMeta.GetAnnotations() @@ -139,7 +138,7 @@ func applyPodSchemaChecks(ctx context.Context, conf *config.Configuration, contr return results, nil } -func applyControllerSchemaChecks(ctx context.Context, conf *config.Configuration, controller kube.GenericWorkload) (ResultSet, error) { +func applyControllerSchemaChecks(conf *config.Configuration, controller kube.GenericWorkload) (ResultSet, error) { results := ResultSet{} checkIDs := getSortedKeys(conf.Checks) objectAnnotations := controller.ObjectMeta.GetAnnotations() @@ -164,7 +163,7 @@ func applyControllerSchemaChecks(ctx context.Context, conf *config.Configuration return results, nil } -func applyContainerSchemaChecks(ctx context.Context, conf *config.Configuration, controller kube.GenericWorkload, container *corev1.Container, isInit bool) (ResultSet, error) { +func applyContainerSchemaChecks(conf *config.Configuration, controller kube.GenericWorkload, container *corev1.Container, isInit bool) (ResultSet, error) { results := ResultSet{} checkIDs := getSortedKeys(conf.Checks) objectAnnotations := controller.ObjectMeta.GetAnnotations() diff --git a/pkg/validator/schema_test.go b/pkg/validator/schema_test.go index 303357fd..36ef40fd 100644 --- a/pkg/validator/schema_test.go +++ b/pkg/validator/schema_test.go @@ -1,7 +1,6 @@ package validator import ( - "context" "testing" conf "github.com/fairwindsops/polaris/pkg/config" @@ -144,14 +143,14 @@ func TestValidateResourcesInit(t *testing.T) { parsedConf, err := conf.Parse([]byte(resourceConfRanges)) assert.NoError(t, err, "Expected no error when parsing config") - results, err := applyContainerSchemaChecks(context.Background(), &parsedConf, controller, emptyContainer, false) + results, err := applyContainerSchemaChecks(&parsedConf, controller, emptyContainer, false) if err != nil { panic(err) } assert.Equal(t, uint(1), results.GetSummary().Dangers) assert.Equal(t, uint(1), results.GetSummary().Warnings) - results, err = applyContainerSchemaChecks(context.Background(), &parsedConf, controller, emptyContainer, true) + results, err = applyContainerSchemaChecks(&parsedConf, controller, emptyContainer, true) if err != nil { panic(err) } diff --git a/pkg/webhook/webhook.go b/pkg/webhook/webhook.go index 9a11ade5..0ab79e9b 100644 --- a/pkg/webhook/webhook.go +++ b/pkg/webhook/webhook.go @@ -104,7 +104,7 @@ func (v *Validator) handleInternal(ctx context.Context, req admission.Request) ( return nil, err } controller.Kind = req.AdmissionRequest.Kind.Kind - controllerResult, err := validator.ValidateController(ctx, &v.Config, controller) + controllerResult, err := validator.ValidateController(&v.Config, controller) if err != nil { return nil, err } diff --git a/test/checks_test.go b/test/checks_test.go index ac74430b..a7d46c85 100644 --- a/test/checks_test.go +++ b/test/checks_test.go @@ -1,7 +1,6 @@ package test import ( - "context" "io/ioutil" "path/filepath" "runtime" @@ -57,7 +56,7 @@ func TestChecks(t *testing.T) { assert.NoError(t, err) c, err := config.Parse([]byte("checks:\n " + tc.check + ": danger")) assert.NoError(t, err) - result, err := validator.ValidateController(context.Background(), &c, *workload) + result, err := validator.ValidateController(&c, *workload) assert.NoError(t, err) summary := result.GetSummary() if tc.failure { From dd2976794a7a0b1a3387085bf49584de5e17a054 Mon Sep 17 00:00:00 2001 From: skatika Date: Fri, 18 Dec 2020 09:50:04 -0500 Subject: [PATCH 07/14] Implement namespace and container exemptions. Also refactoring according to gofmt --- README.md | 2 +- cmd/polaris/audit.go | 3 +- deploy/dashboard.yaml | 2 +- deploy/webhook.yaml | 2 +- docs-md/contributing.md | 3 +- docs-md/customization/exemptions.md | 17 +- pkg/config/exemptions.go | 37 +++-- pkg/config/exemptions_test.go | 245 ++++++++++++++++++++++------ pkg/config/schema.go | 2 +- pkg/dashboard/dashboard.go | 6 +- pkg/validator/container_test.go | 3 +- pkg/validator/controller.go | 3 +- pkg/validator/controller_test.go | 12 +- pkg/validator/fullaudit_test.go | 3 +- pkg/validator/pod_test.go | 15 +- pkg/validator/schema.go | 19 ++- pkg/validator/schema_test.go | 3 +- pkg/webhook/webhook.go | 7 +- test/checks_test.go | 5 +- 19 files changed, 290 insertions(+), 99 deletions(-) diff --git a/README.md b/README.md index 92636097..220d6b66 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

Best Practices for Kubernetes Workload Configuration

- + diff --git a/cmd/polaris/audit.go b/cmd/polaris/audit.go index 3b7d6611..0b14ba7b 100644 --- a/cmd/polaris/audit.go +++ b/cmd/polaris/audit.go @@ -80,7 +80,8 @@ func runAndReportAudit(ctx context.Context, c conf.Configuration, auditPath, wor logrus.Errorf("Error fetching Kubernetes resources %v", err) os.Exit(1) } - auditData, err := validator.RunAudit(c, k) + var auditData validator.AuditData + auditData, err = validator.RunAudit(c, k) if err != nil { logrus.Errorf("Error while running audit on resources: %v", err) diff --git a/deploy/dashboard.yaml b/deploy/dashboard.yaml index d67acde0..c51ab8c4 100644 --- a/deploy/dashboard.yaml +++ b/deploy/dashboard.yaml @@ -109,7 +109,7 @@ spec: - command: - polaris - dashboard - image: 'quay.io/fairwinds/polaris:2.0' + image: 'quay.io/fairwinds/polaris:3.0' imagePullPolicy: 'Always' name: dashboard ports: diff --git a/deploy/webhook.yaml b/deploy/webhook.yaml index 1cbfdafb..38b00b81 100644 --- a/deploy/webhook.yaml +++ b/deploy/webhook.yaml @@ -109,7 +109,7 @@ spec: command: - polaris - webhook - image: 'quay.io/fairwinds/polaris:2.0' + image: 'quay.io/fairwinds/polaris:3.0' imagePullPolicy: 'Always' ports: - containerPort: 9876 diff --git a/docs-md/contributing.md b/docs-md/contributing.md index 80ae2a1d..38310306 100644 --- a/docs-md/contributing.md +++ b/docs-md/contributing.md @@ -50,7 +50,7 @@ Each new pull request should: - Reference any related issues - Add tests that show the issues have been solved - Pass existing tests and linting -- Contain a clear indication of if they're ready for review or a work in progress +- Contain a clear indication of if they're ready for review, or a work in progress - Be up to date and/or rebased on the master branch ## Creating a new release @@ -104,4 +104,3 @@ The steps are: 3. Make sure CircleCI runs successfully for the new tag - this will push images to quay.io and create a release in GitHub 1. If CircleCI fails, check with Codeowners ASAP 4. Create and merge a PR for your changes to the Helm chart - diff --git a/docs-md/customization/exemptions.md b/docs-md/customization/exemptions.md index 9a755ac7..c774d867 100644 --- a/docs-md/customization/exemptions.md +++ b/docs-md/customization/exemptions.md @@ -3,7 +3,10 @@ Sometimes a workload really does need to do things that Polaris considers insecu many of the `kube-system` workloads need to run as root, or need access to the host network. In these cases, we can add **exemptions** to allow the workload to pass Polaris checks. -Exemptions can be added two ways: by annotating a controller, or editing the Polaris config. +Exemptions can be added in a few different ways: + - Namespace: By annotating a controller, or editing the Polaris config. + - Controller: By editing the Polaris config. + - Container: By editing the Polaris config. ## Annotations To exempt a controller from all checks via annotations, use the annotation `polaris.fairwinds.com/exempt=true`, e.g. @@ -18,19 +21,25 @@ kubectl annotate deployment my-deployment polaris.fairwinds.com/cpuRequestsMissi ## Config -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. +You can add exemptions by using a combination of namespace, controller names, and container names via the config. You have to specify a list of rules and at least one of the following: a namespace, a list of controller names, or a list of container names, e.g. ```yaml exemptions: - # exemption valid for kube-system namespace + # exemption valid in kube-system namespace, dns-controller controller for all containers - namespace: kube-system controllerNames: - dns-controller rules: - hostNetworkSet - # exemption valid in all namespaces + # exemption valid in all namespaces for dns-controller controller for all containers - controllerNames: - dns-controller rules: - hostNetworkSet + # exemption valid in kube-system namespace and all controllers for coredns container + - namespace: kube-system + - containerNames: + - coredns + rules: + - hostNetworkSet ``` diff --git a/pkg/config/exemptions.go b/pkg/config/exemptions.go index 01829bf9..1ae61984 100644 --- a/pkg/config/exemptions.go +++ b/pkg/config/exemptions.go @@ -5,35 +5,48 @@ import ( ) // IsActionable determines whether a check is actionable given the current configuration -func (conf Configuration) IsActionable(ruleID, namespace, controllerName string) bool { +func (conf Configuration) IsActionable(ruleID, namespace, controllerName, containerName string) bool { if severity, ok := conf.Checks[ruleID]; !ok || !severity.IsActionable() { return false } if conf.DisallowExemptions { return true } - - for _, example := range conf.Exemptions { - if example.Namespace != "" && example.Namespace != namespace { + for _, exemption := range conf.Exemptions { + if exemption.Namespace != "" && exemption.Namespace != namespace { continue } - checkIfActionable := false - for _, rule := range example.Rules { + checkIfRuleMatches := false + for _, rule := range exemption.Rules { if rule != ruleID { continue } - checkIfActionable = true + checkIfRuleMatches = true break } - if len(example.Rules) == 0 || checkIfActionable { - for _, controller := range example.ControllerNames { - if strings.HasPrefix(controllerName, controller) { - return false - } + if len(exemption.Rules) == 0 || checkIfRuleMatches { + if !isExemptionCheckMatched(exemption.ControllerNames, controllerName) { + continue + } + if isExemptionCheckMatched(exemption.ContainerNames, containerName) { + return false } } } return true } + +func isExemptionCheckMatched(arr []string, predicate string) bool { + if len(arr) == 0 { + return true + } + + for _, container := range arr { + if strings.HasPrefix(predicate, container) { + return true + } + } + return false +} diff --git a/pkg/config/exemptions_test.go b/pkg/config/exemptions_test.go index cead8aab..82a0ad36 100644 --- a/pkg/config/exemptions_test.go +++ b/pkg/config/exemptions_test.go @@ -20,61 +20,208 @@ import ( "github.com/stretchr/testify/assert" ) -var confExemptRuleTest = ` +var confContainerTest = ` checks: - ANY: warning - OTHER: warning + multipleReplicasForDeployment: warning + priorityClassNotSet: warning + pullPolicyNotAlways: warning exemptions: - - controllerNames: - - test + - namespace: prometheus rules: - - ANY -` - -var confExemptTest = ` -checks: - ANY: warning -exemptions: - - controllerNames: - - test -` - -var confNamespaceTest = ` -checks: - ANY: warning -exemptions: + - multipleReplicasForDeployment + - controllerNames: + - controller2 + rules: + - multipleReplicasForDeployment - namespace: kube-system controllerNames: - - test + - controller3 + rules: + - multipleReplicasForDeployment + - containerNames: + - container41 + - container42 + rules: + - multipleReplicasForDeployment + - namespace: kube-system + containerNames: + - container51 + - container52 + rules: + - multipleReplicasForDeployment + - controllerNames: + - controller6 + containerNames: + - container61 + - container62 + rules: + - multipleReplicasForDeployment + - namespace: kube-system + controllerNames: + - controller7 + containerNames: + - container71 + - container72 + rules: + - multipleReplicasForDeployment + - priorityClassNotSet ` -func TestInclusiveExemption(t *testing.T) { - parsedConf, _ := Parse([]byte(confExemptTest)) - 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", "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") + parsedConf, err := Parse([]byte(confContainerTest)) + assert.NoError(t, err) - 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 + actionable := parsedConf.IsActionable("multipleReplicasForDeployment", "prometheus", "", "") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "prometheus", "controller1", "container11") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "prometheus", "", "container11") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "prometheus", "controller1", "") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "", "") + assert.True(t, actionable) +} + +func TestControllerExemption(t *testing.T) { + parsedConf, err := Parse([]byte(confContainerTest)) + assert.NoError(t, err) + + actionable := parsedConf.IsActionable("multipleReplicasForDeployment", "", "controller2", "") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "controller2", "container21") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "prometheus", "controller2", "container21") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "prometheus", "controller2", "") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "controller3", "") + assert.True(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller3", "") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller3", "container31") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller4", "") + assert.True(t, actionable) +} + +func TestOnlyContainerExemption(t *testing.T) { + parsedConf, err := Parse([]byte(confContainerTest)) + assert.NoError(t, err) + + actionable := parsedConf.IsActionable("multipleReplicasForDeployment", "", "", "container41") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "", "container42") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "controller4", "container41") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "", "container41") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller4", "container41") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "", "container51") + assert.True(t, actionable) +} + +func TestNamespaceAndContainerExemption(t *testing.T) { + parsedConf, err := Parse([]byte(confContainerTest)) + assert.NoError(t, err) + + actionable := parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "", "container51") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("priorityClassNotSet", "kube-system", "", "container51") + assert.True(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller5", "container51") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller5", "") + assert.True(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "insights-agent", "", "container51") + assert.True(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "", "container51") + assert.True(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "controller5", "container51") + assert.True(t, actionable) +} + +func TestControllerAndContainerExemption(t *testing.T) { + parsedConf, err := Parse([]byte(confContainerTest)) + assert.NoError(t, err) + + actionable := parsedConf.IsActionable("multipleReplicasForDeployment", "", "controller6", "container61") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("priorityClassNotSet", "", "controller6", "container61") + assert.True(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller6", "container61") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller6", "") + assert.True(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "controller7", "container61") + assert.True(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "", "container61") + assert.True(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "", "container61") + assert.True(t, actionable) +} + +func TestContainerExemption(t *testing.T) { + parsedConf, err := Parse([]byte(confContainerTest)) + assert.NoError(t, err) + + actionable := parsedConf.IsActionable("multipleReplicasForDeployment", "", "", "container71") + assert.True(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "", "container71") + assert.True(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "", "controller7", "container71") + assert.True(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller7", "") + assert.True(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller7", "container71") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "insights-agent", "controller7", "container71") + assert.True(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller6", "container71") + assert.True(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "controller7", "container61") + assert.True(t, actionable) + + actionable = parsedConf.IsActionable("priorityClassNotSet", "kube-system", "controller7", "container71") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("pullPolicyNotAlways", "kube-system", "controller8", "container71") + assert.True(t, actionable) +} diff --git a/pkg/config/schema.go b/pkg/config/schema.go index 0ef8b055..255741b1 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, namespace, controllerType string, isInit bool) bool { +func (check SchemaCheck) IsActionable(target TargetKind, controllerType string, isInit bool) bool { if check.Target != target { return false } diff --git a/pkg/dashboard/dashboard.go b/pkg/dashboard/dashboard.go index eaacb778..ba59a0b5 100644 --- a/pkg/dashboard/dashboard.go +++ b/pkg/dashboard/dashboard.go @@ -191,7 +191,8 @@ func GetRouter(c config.Configuration, auditPath string, port int, basePath stri return } - auditDataObj, err := validator.RunAudit(adjustedConf, k) + var auditDataObj validator.AuditData + auditDataObj, err = validator.RunAudit(adjustedConf, k) if err != nil { http.Error(w, "Error Fetching Deployments", http.StatusInternalServerError) return @@ -224,7 +225,8 @@ func GetRouter(c config.Configuration, auditPath string, port int, basePath stri return } - auditData, err := validator.RunAudit(adjustedConf, k) + var auditData validator.AuditData + auditData, err = validator.RunAudit(adjustedConf, k) if err != nil { logrus.Errorf("Error getting audit data: %v", err) http.Error(w, "Error running audit", 500) diff --git a/pkg/validator/container_test.go b/pkg/validator/container_test.go index a75526b7..f455ebc7 100644 --- a/pkg/validator/container_test.go +++ b/pkg/validator/container_test.go @@ -68,7 +68,8 @@ func testValidateWithWorkload(t *testing.T, container *corev1.Container, resourc parsedConf, err := conf.Parse([]byte(*resourceConf)) assert.NoError(t, err, "Expected no error when parsing config") - results, err := applyContainerSchemaChecks(&parsedConf, workload, container, false) + var results ResultSet + results, err = applyContainerSchemaChecks(&parsedConf, workload, container, false) if err != nil { panic(err) } diff --git a/pkg/validator/controller.go b/pkg/validator/controller.go index 90b4a071..68016c9a 100644 --- a/pkg/validator/controller.go +++ b/pkg/validator/controller.go @@ -32,7 +32,8 @@ func ValidateController(conf *conf.Configuration, controller kube.GenericWorkloa return ControllerResult{}, err } - controllerResult, err := applyControllerSchemaChecks(conf, controller) + var controllerResult ResultSet + controllerResult, err = applyControllerSchemaChecks(conf, controller) if err != nil { return ControllerResult{}, err } diff --git a/pkg/validator/controller_test.go b/pkg/validator/controller_test.go index 54231148..b116871e 100644 --- a/pkg/validator/controller_test.go +++ b/pkg/validator/controller_test.go @@ -46,7 +46,8 @@ func TestValidateController(t *testing.T) { "hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "danger", Category: "Security"}, } - actualResult, err := ValidateController(&c, deployment) + var actualResult ControllerResult + actualResult, err = ValidateController(&c, deployment) if err != nil { panic(err) } @@ -81,7 +82,8 @@ func TestControllerLevelChecks(t *testing.T) { for _, controller := range resources.Controllers { if controller.Kind == "Deployment" && controller.ObjectMeta.GetName() == "test-deployment" { - actualResult, err := ValidateController(&c, controller) + var actualResult ControllerResult + actualResult, err = ValidateController(&c, controller) if err != nil { panic(err) } @@ -116,7 +118,8 @@ func TestSkipHealthChecks(t *testing.T) { "readinessProbeMissing": {ID: "readinessProbeMissing", Message: "Readiness probe should be configured", Success: false, Severity: "danger", Category: "Reliability"}, "livenessProbeMissing": {ID: "livenessProbeMissing", Message: "Liveness probe should be configured", Success: false, Severity: "warning", Category: "Reliability"}, } - actualResult, err := ValidateController(&c, deployment) + var actualResult ControllerResult + actualResult, err = ValidateController(&c, deployment) if err != nil { panic(err) } @@ -183,7 +186,8 @@ func TestControllerExemptions(t *testing.T) { Warnings: uint(1), Dangers: uint(1), } - actualResults, err := ValidateControllers(&c, resources) + var actualResults []ControllerResult + actualResults, err = ValidateControllers(&c, resources) if err != nil { panic(err) } diff --git a/pkg/validator/fullaudit_test.go b/pkg/validator/fullaudit_test.go index 943cdd2a..88441315 100644 --- a/pkg/validator/fullaudit_test.go +++ b/pkg/validator/fullaudit_test.go @@ -32,7 +32,8 @@ func TestGetTemplateData(t *testing.T) { Dangers: uint(1), } - actualAudit, err := RunAudit(c, resources) + var actualAudit AuditData + actualAudit, err = RunAudit(c, resources) assert.Equal(t, err, nil, "error should be nil") diff --git a/pkg/validator/pod_test.go b/pkg/validator/pod_test.go index 6d04345c..e2b18684 100644 --- a/pkg/validator/pod_test.go +++ b/pkg/validator/pod_test.go @@ -53,7 +53,8 @@ func TestValidatePod(t *testing.T) { "hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "danger", Category: "Security"}, } - actualPodResult, err := ValidatePod(&c, deployment) + var actualPodResult PodResult + actualPodResult, err = ValidatePod(&c, deployment) if err != nil { panic(err) } @@ -90,7 +91,8 @@ func TestInvalidIPCPod(t *testing.T) { "hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "danger", Category: "Security"}, } - actualPodResult, err := ValidatePod(&c, workload) + var actualPodResult PodResult + actualPodResult, err = ValidatePod(&c, workload) if err != nil { panic(err) } @@ -128,7 +130,8 @@ func TestInvalidNetworkPod(t *testing.T) { "hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "danger", Category: "Security"}, } - actualPodResult, err := ValidatePod(&c, workload) + var actualPodResult PodResult + actualPodResult, err = ValidatePod(&c, workload) if err != nil { panic(err) } @@ -166,7 +169,8 @@ func TestInvalidPIDPod(t *testing.T) { "hostNetworkSet": {ID: "hostNetworkSet", Message: "Host network is not configured", Success: true, Severity: "warning", Category: "Security"}, } - actualPodResult, err := ValidatePod(&c, workload) + var actualPodResult PodResult + actualPodResult, err = ValidatePod(&c, workload) if err != nil { panic(err) } @@ -211,7 +215,8 @@ func TestExemption(t *testing.T) { "hostPIDSet": {ID: "hostPIDSet", Message: "Host PID is not configured", Success: true, Severity: "danger", Category: "Security"}, } - actualPodResult, err := ValidatePod(&c, workload) + var actualPodResult PodResult + actualPodResult, err = ValidatePod(&c, workload) if err != nil { panic(err) } diff --git a/pkg/validator/schema.go b/pkg/validator/schema.go index 6d1e5a89..9b1ab655 100644 --- a/pkg/validator/schema.go +++ b/pkg/validator/schema.go @@ -7,7 +7,7 @@ import ( "sort" "strings" - packr "github.com/gobuffalo/packr/v2" + "github.com/gobuffalo/packr/v2" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/yaml" @@ -77,7 +77,7 @@ func parseCheck(rawBytes []byte) (config.SchemaCheck, error) { } } -func resolveCheck(conf *config.Configuration, checkID string, controller kube.GenericWorkload, target config.TargetKind, isInitContainer bool) (*config.SchemaCheck, error) { +func resolveCheck(conf *config.Configuration, checkID string, controller kube.GenericWorkload, container *corev1.Container, target config.TargetKind, isInitContainer bool) (*config.SchemaCheck, error) { check, ok := conf.CustomChecks[checkID] if !ok { check, ok = builtInChecks[checkID] @@ -85,10 +85,15 @@ 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.GetNamespace(), controller.ObjectMeta.GetName()) { + + containerName := "" + if container != nil { + containerName = container.Name + } + if !conf.IsActionable(check.ID, controller.ObjectMeta.GetNamespace(), controller.ObjectMeta.GetName(), containerName) { return nil, nil } - if !check.IsActionable(target, controller.ObjectMeta.GetNamespace(), controller.Kind, isInitContainer) { + if !check.IsActionable(target, controller.Kind, isInitContainer) { return nil, nil } return &check, nil @@ -122,7 +127,7 @@ func applyPodSchemaChecks(conf *config.Configuration, controller kube.GenericWor if strings.ToLower(exemptValue) == "true" { continue } - check, err := resolveCheck(conf, checkID, controller, config.TargetPod, false) + check, err := resolveCheck(conf, checkID, controller, nil, config.TargetPod, false) if err != nil { return nil, err @@ -147,7 +152,7 @@ func applyControllerSchemaChecks(conf *config.Configuration, controller kube.Gen if strings.ToLower(exemptValue) == "true" { continue } - check, err := resolveCheck(conf, checkID, controller, config.TargetController, false) + check, err := resolveCheck(conf, checkID, controller, nil, config.TargetController, false) if err != nil { return nil, err @@ -172,7 +177,7 @@ func applyContainerSchemaChecks(conf *config.Configuration, controller kube.Gene if strings.ToLower(exemptValue) == "true" { continue } - check, err := resolveCheck(conf, checkID, controller, config.TargetContainer, isInit) + check, err := resolveCheck(conf, checkID, controller, container, config.TargetContainer, isInit) if err != nil { return nil, err } else if check == nil { diff --git a/pkg/validator/schema_test.go b/pkg/validator/schema_test.go index 36ef40fd..bdea8e99 100644 --- a/pkg/validator/schema_test.go +++ b/pkg/validator/schema_test.go @@ -143,7 +143,8 @@ func TestValidateResourcesInit(t *testing.T) { parsedConf, err := conf.Parse([]byte(resourceConfRanges)) assert.NoError(t, err, "Expected no error when parsing config") - results, err := applyContainerSchemaChecks(&parsedConf, controller, emptyContainer, false) + var results ResultSet + results, err = applyContainerSchemaChecks(&parsedConf, controller, emptyContainer, false) if err != nil { panic(err) } diff --git a/pkg/webhook/webhook.go b/pkg/webhook/webhook.go index 0ab79e9b..c0afcd34 100644 --- a/pkg/webhook/webhook.go +++ b/pkg/webhook/webhook.go @@ -82,7 +82,7 @@ func GetObjectFromRawRequest(raw []byte) (corev1.Pod, interface{}, error) { return pod, originalObject, err } -func (v *Validator) handleInternal(ctx context.Context, req admission.Request) (*validator.PodResult, error) { +func (v *Validator) handleInternal(req admission.Request) (*validator.PodResult, error) { pod := corev1.Pod{} var originalObject interface{} var err error @@ -104,7 +104,8 @@ func (v *Validator) handleInternal(ctx context.Context, req admission.Request) ( return nil, err } controller.Kind = req.AdmissionRequest.Kind.Kind - controllerResult, err := validator.ValidateController(&v.Config, controller) + var controllerResult validator.ControllerResult + controllerResult, err = validator.ValidateController(&v.Config, controller) if err != nil { return nil, err } @@ -114,7 +115,7 @@ func (v *Validator) handleInternal(ctx context.Context, req admission.Request) ( // Handle for Validator to run validation checks. func (v *Validator) Handle(ctx context.Context, req admission.Request) admission.Response { logrus.Info("Starting request") - podResult, err := v.handleInternal(ctx, req) + podResult, err := v.handleInternal(req) if err != nil { logrus.Errorf("Error validating request: %v", err) return admission.Errored(http.StatusBadRequest, err) diff --git a/test/checks_test.go b/test/checks_test.go index a7d46c85..3f4ce9db 100644 --- a/test/checks_test.go +++ b/test/checks_test.go @@ -14,7 +14,7 @@ import ( "github.com/fairwindsops/polaris/pkg/validator" ) -var testCases = []testCase{} +var testCases []testCase type testCase struct { check string @@ -56,7 +56,8 @@ func TestChecks(t *testing.T) { assert.NoError(t, err) c, err := config.Parse([]byte("checks:\n " + tc.check + ": danger")) assert.NoError(t, err) - result, err := validator.ValidateController(&c, *workload) + var result validator.ControllerResult + result, err = validator.ValidateController(&c, *workload) assert.NoError(t, err) summary := result.GetSummary() if tc.failure { From 5fd9e014932af4846373374da3df9a1fe3afc926 Mon Sep 17 00:00:00 2001 From: skatika Date: Fri, 18 Dec 2020 10:00:18 -0500 Subject: [PATCH 08/14] Update version and changelog --- README.md | 2 +- docs-md/changelog.md | 3 +++ main.go | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index badaaebd..220d6b66 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

Best Practices for Kubernetes Workload Configuration

- + diff --git a/docs-md/changelog.md b/docs-md/changelog.md index 5306918c..749947c9 100644 --- a/docs-md/changelog.md +++ b/docs-md/changelog.md @@ -1,6 +1,9 @@ --- sidebarDepth: 0 --- +## 3.1.0 +* Add ability for exemptions for namespaces and containers + ## 3.0.0 * **Breaking** - fixed inconsistency in how controller-level checks are handled Custom checks with `target: Controller` should remove `Object` from the top-level of the diff --git a/main.go b/main.go index 67fde74e..0941c39d 100644 --- a/main.go +++ b/main.go @@ -20,7 +20,7 @@ import ( const ( // Version represents the current release version of Polaris - Version = "3.0.0" + Version = "3.1.0" ) func main() { From 564803c9f8e1a9243b67565f3a88acac068a3790 Mon Sep 17 00:00:00 2001 From: skatika Date: Tue, 22 Dec 2020 14:10:15 -0500 Subject: [PATCH 09/14] Fix instructions --- docs-md/customization/exemptions.md | 4 ++-- pkg/validator/container_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs-md/customization/exemptions.md b/docs-md/customization/exemptions.md index c774d867..b6ea5117 100644 --- a/docs-md/customization/exemptions.md +++ b/docs-md/customization/exemptions.md @@ -4,8 +4,8 @@ many of the `kube-system` workloads need to run as root, or need access to the h cases, we can add **exemptions** to allow the workload to pass Polaris checks. Exemptions can be added in a few different ways: - - Namespace: By annotating a controller, or editing the Polaris config. - - Controller: By editing the Polaris config. + - Namespace: By editing the Polaris config. + - Controller: By annotating a controller, or editing the Polaris config. - Container: By editing the Polaris config. ## Annotations diff --git a/pkg/validator/container_test.go b/pkg/validator/container_test.go index f455ebc7..470f157c 100644 --- a/pkg/validator/container_test.go +++ b/pkg/validator/container_test.go @@ -927,7 +927,7 @@ func TestValidateSecurity(t *testing.T) { if err != nil { panic(err) } - messages := []ResultMessage{} + var messages []ResultMessage for _, msg := range results { messages = append(messages, msg) } From 54aacb786f7fc2651caae153cf9a240087b1a73a Mon Sep 17 00:00:00 2001 From: skatika Date: Tue, 22 Dec 2020 14:14:55 -0500 Subject: [PATCH 10/14] Revert version information --- README.md | 2 +- docs-md/changelog.md | 3 --- main.go | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 220d6b66..badaaebd 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

Best Practices for Kubernetes Workload Configuration

- + diff --git a/docs-md/changelog.md b/docs-md/changelog.md index 749947c9..5306918c 100644 --- a/docs-md/changelog.md +++ b/docs-md/changelog.md @@ -1,9 +1,6 @@ --- sidebarDepth: 0 --- -## 3.1.0 -* Add ability for exemptions for namespaces and containers - ## 3.0.0 * **Breaking** - fixed inconsistency in how controller-level checks are handled Custom checks with `target: Controller` should remove `Object` from the top-level of the diff --git a/main.go b/main.go index 0941c39d..67fde74e 100644 --- a/main.go +++ b/main.go @@ -20,7 +20,7 @@ import ( const ( // Version represents the current release version of Polaris - Version = "3.1.0" + Version = "3.0.0" ) func main() { From 86b3ab5186ae96b7cb7c85c4adc8655f15fb9587 Mon Sep 17 00:00:00 2001 From: skatika Date: Tue, 22 Dec 2020 14:27:53 -0500 Subject: [PATCH 11/14] Revert nil slice declarations --- pkg/validator/container.go | 2 +- pkg/validator/container_test.go | 2 +- pkg/validator/controller.go | 2 +- test/checks_test.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/validator/container.go b/pkg/validator/container.go index 1fa8b387..25c59346 100644 --- a/pkg/validator/container.go +++ b/pkg/validator/container.go @@ -38,7 +38,7 @@ func ValidateContainer(conf *config.Configuration, controller kube.GenericWorklo // ValidateAllContainers validates both init and regular containers func ValidateAllContainers(conf *config.Configuration, controller kube.GenericWorkload) ([]ContainerResult, error) { - var results []ContainerResult + results := []ContainerResult{} pod := controller.PodSpec for _, container := range pod.InitContainers { result, err := ValidateContainer(conf, controller, &container, true) diff --git a/pkg/validator/container_test.go b/pkg/validator/container_test.go index 470f157c..f455ebc7 100644 --- a/pkg/validator/container_test.go +++ b/pkg/validator/container_test.go @@ -927,7 +927,7 @@ func TestValidateSecurity(t *testing.T) { if err != nil { panic(err) } - var messages []ResultMessage + messages := []ResultMessage{} for _, msg := range results { messages = append(messages, msg) } diff --git a/pkg/validator/controller.go b/pkg/validator/controller.go index 68016c9a..d6a49729 100644 --- a/pkg/validator/controller.go +++ b/pkg/validator/controller.go @@ -54,7 +54,7 @@ func ValidateController(conf *conf.Configuration, controller kube.GenericWorkloa func ValidateControllers(config *conf.Configuration, kubeResources *kube.ResourceProvider) ([]ControllerResult, error) { controllersToAudit := kubeResources.Controllers - var results []ControllerResult + results := []ControllerResult{} for _, controller := range controllersToAudit { if !config.DisallowExemptions && hasExemptionAnnotation(controller) { continue diff --git a/test/checks_test.go b/test/checks_test.go index 3f4ce9db..7e493b42 100644 --- a/test/checks_test.go +++ b/test/checks_test.go @@ -14,7 +14,7 @@ import ( "github.com/fairwindsops/polaris/pkg/validator" ) -var testCases []testCase +var testCases = []testCase{} type testCase struct { check string From f1957631b539fa40f8c716ede989af81a9e3c898 Mon Sep 17 00:00:00 2001 From: skatika Date: Tue, 22 Dec 2020 14:30:09 -0500 Subject: [PATCH 12/14] Remove unsued import --- pkg/validator/pod_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/validator/pod_test.go b/pkg/validator/pod_test.go index 7ce62a28..4af40cb1 100644 --- a/pkg/validator/pod_test.go +++ b/pkg/validator/pod_test.go @@ -15,7 +15,6 @@ package validator import ( - "context" "testing" "github.com/stretchr/testify/assert" From 0c398d21f2ea22b440d1b16434aa8cc94a82eab6 Mon Sep 17 00:00:00 2001 From: skatika Date: Tue, 22 Dec 2020 14:58:44 -0500 Subject: [PATCH 13/14] Fix grammar for exemption doc --- docs-md/customization/exemptions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs-md/customization/exemptions.md b/docs-md/customization/exemptions.md index b6ea5117..5f22420e 100644 --- a/docs-md/customization/exemptions.md +++ b/docs-md/customization/exemptions.md @@ -24,13 +24,13 @@ kubectl annotate deployment my-deployment polaris.fairwinds.com/cpuRequestsMissi You can add exemptions by using a combination of namespace, controller names, and container names via the config. You have to specify a list of rules and at least one of the following: a namespace, a list of controller names, or a list of container names, e.g. ```yaml exemptions: - # exemption valid in kube-system namespace, dns-controller controller for all containers + # exemption valid in kube-system namespace and dns-controller controller for all containers - namespace: kube-system controllerNames: - dns-controller rules: - hostNetworkSet - # exemption valid in all namespaces for dns-controller controller for all containers + # exemption valid in all namespaces and dns-controller controller for all containers - controllerNames: - dns-controller rules: From a79260a324fd10b80216389cbab0d5cea51ec762 Mon Sep 17 00:00:00 2001 From: skatika Date: Tue, 22 Dec 2020 15:30:39 -0500 Subject: [PATCH 14/14] Update exemption documentation and unit test --- docs-md/customization/exemptions.md | 19 +++++++++++++++---- pkg/config/exemptions_test.go | 26 +++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/docs-md/customization/exemptions.md b/docs-md/customization/exemptions.md index 5f22420e..2f0095ec 100644 --- a/docs-md/customization/exemptions.md +++ b/docs-md/customization/exemptions.md @@ -21,21 +21,32 @@ kubectl annotate deployment my-deployment polaris.fairwinds.com/cpuRequestsMissi ## Config -You can add exemptions by using a combination of namespace, controller names, and container names via the config. You have to specify a list of rules and at least one of the following: a namespace, a list of controller names, or a list of container names, e.g. +To add exemptions via the config, you have to specify at least one or more of the following: +- A namespace +- A list of controller names +- A list of container names + +You can also specify a list of particular rules. If no rules are specified then every rule is exempted. + +Controller names and container names are matched as a prefix, so an empty string will match every controller or container respectively. + +For example: ```yaml exemptions: - # exemption valid in kube-system namespace and dns-controller controller for all containers + # exemption valid for all rules on all containers in all controllers in default namespace + - namespace: default + # exemption valid for hostNetworkSet rule on all containers in dns-controller controller in kube-system namespace - namespace: kube-system controllerNames: - dns-controller rules: - hostNetworkSet - # exemption valid in all namespaces and dns-controller controller for all containers + # exemption valid for hostNetworkSet rule on all containers in dns-controller controller in all namespaces - controllerNames: - dns-controller rules: - hostNetworkSet - # exemption valid in kube-system namespace and all controllers for coredns container + # exemption valid for hostNetworkSet rule on coredns container in all controllers in kube-system namespace - namespace: kube-system - containerNames: - coredns diff --git a/pkg/config/exemptions_test.go b/pkg/config/exemptions_test.go index 82a0ad36..844a754c 100644 --- a/pkg/config/exemptions_test.go +++ b/pkg/config/exemptions_test.go @@ -65,9 +65,10 @@ exemptions: rules: - multipleReplicasForDeployment - priorityClassNotSet + - namespace: polaris ` -func TestNamespaceExemption(t *testing.T) { +func TestNamespaceExemptionForSpecifiedRules(t *testing.T) { parsedConf, err := Parse([]byte(confContainerTest)) assert.NoError(t, err) @@ -83,10 +84,33 @@ func TestNamespaceExemption(t *testing.T) { actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "prometheus", "controller1", "") assert.False(t, actionable) + actionable = parsedConf.IsActionable("pullPolicyNotAlways", "prometheus", "controller1", "") + assert.True(t, actionable) + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "", "") assert.True(t, actionable) } +func TestNamespaceExemptionForAllRules(t *testing.T) { + parsedConf, err := Parse([]byte(confContainerTest)) + assert.NoError(t, err) + + actionable := parsedConf.IsActionable("multipleReplicasForDeployment", "polaris", "", "") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "polaris", "controller1", "container11") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "polaris", "", "container11") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "polaris", "controller1", "") + assert.False(t, actionable) + + actionable = parsedConf.IsActionable("pullPolicyNotAlways", "polaris", "controller1", "") + assert.False(t, actionable) +} + func TestControllerExemption(t *testing.T) { parsedConf, err := Parse([]byte(confContainerTest)) assert.NoError(t, err)