mirror of
https://github.com/FairwindsOps/polaris.git
synced 2026-05-10 03:07:12 +00:00
Merge pull request #463 from FairwindsOps/ssk/container-exemptions
Exemptions for containers and namespaces
This commit is contained in:
@@ -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(ctx, 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)
|
||||
|
||||
@@ -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
|
||||
@@ -101,4 +101,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
|
||||
|
||||
|
||||
@@ -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 editing the Polaris config.
|
||||
- Controller: By annotating a controller, or 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,36 @@ 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 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 for kube-system namespace
|
||||
# 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
|
||||
# exemption valid for hostNetworkSet rule on all containers in dns-controller controller in all namespaces
|
||||
- controllerNames:
|
||||
- dns-controller
|
||||
rules:
|
||||
- hostNetworkSet
|
||||
# exemption valid for hostNetworkSet rule on coredns container in all controllers in kube-system namespace
|
||||
- namespace: kube-system
|
||||
- containerNames:
|
||||
- coredns
|
||||
rules:
|
||||
- hostNetworkSet
|
||||
```
|
||||
|
||||
|
||||
20
go.sum
20
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=
|
||||
|
||||
@@ -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,7 +40,8 @@ type Configuration struct {
|
||||
type Exemption struct {
|
||||
Rules []string `json:"rules"`
|
||||
ControllerNames []string `json:"controllerNames"`
|
||||
Namespace string `json:"namespace"`
|
||||
ContainerNames []string `json:"containerNames"`
|
||||
Namespace string `json:"namespace"`
|
||||
}
|
||||
|
||||
var configBox = (*packr.Box)(nil)
|
||||
@@ -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 {
|
||||
@@ -102,8 +103,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
|
||||
|
||||
@@ -5,37 +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
|
||||
}
|
||||
|
||||
for _, rule := range example.Rules {
|
||||
checkIfRuleMatches := false
|
||||
for _, rule := range exemption.Rules {
|
||||
if rule != ruleID {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, controller := range example.ControllerNames {
|
||||
if strings.HasPrefix(controllerName, controller) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
checkIfRuleMatches = true
|
||||
break
|
||||
}
|
||||
if len(example.Rules) == 0 {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -20,61 +20,232 @@ 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
|
||||
- namespace: polaris
|
||||
`
|
||||
|
||||
func TestInclusiveExemption(t *testing.T) {
|
||||
parsedConf, _ := Parse([]byte(confExemptTest))
|
||||
applicable := parsedConf.IsActionable("ANY", "test", "test")
|
||||
applicableOtherController := parsedConf.IsActionable("ANY","test", "other")
|
||||
func TestNamespaceExemptionForSpecifiedRules(t *testing.T) {
|
||||
parsedConf, err := Parse([]byte(confContainerTest))
|
||||
assert.NoError(t, err)
|
||||
|
||||
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.")
|
||||
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("pullPolicyNotAlways", "prometheus", "controller1", "")
|
||||
assert.True(t, actionable)
|
||||
|
||||
actionable = parsedConf.IsActionable("multipleReplicasForDeployment", "kube-system", "", "")
|
||||
assert.True(t, actionable)
|
||||
}
|
||||
|
||||
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")
|
||||
func TestNamespaceExemptionForAllRules(t *testing.T) {
|
||||
parsedConf, err := Parse([]byte(confContainerTest))
|
||||
assert.NoError(t, err)
|
||||
|
||||
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.")
|
||||
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 TestNamespaceExemption(t *testing.T) {
|
||||
parsedConf, _ := Parse([]byte(confNamespaceTest))
|
||||
applicable := parsedConf.IsActionable("ANY", "kube-system", "test")
|
||||
applicableOtherController := parsedConf.IsActionable("ANY","default", "test")
|
||||
func TestControllerExemption(t *testing.T) {
|
||||
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.")
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -191,7 +191,8 @@ func GetRouter(c config.Configuration, auditPath string, port int, basePath stri
|
||||
return
|
||||
}
|
||||
|
||||
auditDataObj, err := validator.RunAudit(r.Context(), 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(r.Context(), 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)
|
||||
|
||||
@@ -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) {
|
||||
func ValidateAllContainers(conf *config.Configuration, controller kube.GenericWorkload) ([]ContainerResult, error) {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
@@ -69,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(context.Background(), &parsedConf, workload, container, false)
|
||||
var results ResultSet
|
||||
results, err = applyContainerSchemaChecks(&parsedConf, workload, container, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -90,7 +90,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 +187,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 +301,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 +418,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 +923,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 +1068,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)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -27,13 +26,14 @@ 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)
|
||||
var controllerResult ResultSet
|
||||
controllerResult, err = applyControllerSchemaChecks(conf, controller)
|
||||
if err != nil {
|
||||
return ControllerResult{}, err
|
||||
}
|
||||
@@ -51,7 +51,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
|
||||
|
||||
results := []ControllerResult{}
|
||||
@@ -59,9 +59,9 @@ 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 occured validating controller:", err)
|
||||
logrus.Warn("An error occurred validating controller:", err)
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, result)
|
||||
|
||||
@@ -48,7 +48,8 @@ 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)
|
||||
var actualResult ControllerResult
|
||||
actualResult, err = ValidateController(&c, deployment)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -73,7 +74,7 @@ func TestControllerLevelChecks(t *testing.T) {
|
||||
}
|
||||
for _, controller := range res.Controllers {
|
||||
if controller.Kind == "Deployment" {
|
||||
actualResult, err := ValidateController(context.Background(), &c, controller)
|
||||
actualResult, err := ValidateController(&c, controller)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -136,7 +137,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(context.Background(), &c, deployment)
|
||||
var actualResult ControllerResult
|
||||
actualResult, err = ValidateController(&c, deployment)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -155,7 +157,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)
|
||||
}
|
||||
@@ -173,7 +175,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)
|
||||
}
|
||||
@@ -203,7 +205,8 @@ func TestControllerExemptions(t *testing.T) {
|
||||
Warnings: uint(1),
|
||||
Dangers: uint(1),
|
||||
}
|
||||
actualResults, err := ValidateControllers(context.Background(), &c, resources)
|
||||
var actualResults []ControllerResult
|
||||
actualResults, err = ValidateControllers(&c, resources)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -214,7 +217,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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@ func TestGetTemplateData(t *testing.T) {
|
||||
Dangers: uint(3),
|
||||
}
|
||||
|
||||
actualAudit, err := RunAudit(context.Background(), c, resources)
|
||||
var actualAudit AuditData
|
||||
actualAudit, err = RunAudit(c, resources)
|
||||
assert.Equal(t, err, nil, "error should be nil")
|
||||
assert.EqualValues(t, sum, actualAudit.GetSummary())
|
||||
assert.Equal(t, actualAudit.SourceType, "Cluster", "should be from a cluster")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -51,7 +50,8 @@ 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)
|
||||
var actualPodResult PodResult
|
||||
actualPodResult, err = ValidatePod(&c, deployment)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -86,7 +86,8 @@ 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)
|
||||
var actualPodResult PodResult
|
||||
actualPodResult, err = ValidatePod(&c, workload)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -96,7 +97,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,
|
||||
@@ -122,7 +123,8 @@ func TestInvalidNeworkPod(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)
|
||||
var actualPodResult PodResult
|
||||
actualPodResult, err = ValidatePod(&c, workload)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -158,7 +160,8 @@ 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)
|
||||
var actualPodResult PodResult
|
||||
actualPodResult, err = ValidatePod(&c, workload)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -177,7 +180,7 @@ func TestExemption(t *testing.T) {
|
||||
"hostPortSet": conf.SeverityDanger,
|
||||
},
|
||||
Exemptions: []conf.Exemption{
|
||||
conf.Exemption{
|
||||
{
|
||||
Rules: []string{"hostIPCSet"},
|
||||
ControllerNames: []string{"foo"},
|
||||
},
|
||||
@@ -201,7 +204,8 @@ 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)
|
||||
var actualPodResult PodResult
|
||||
actualPodResult, err = ValidatePod(&c, workload)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -2,13 +2,12 @@ package validator
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"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"
|
||||
|
||||
@@ -78,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]
|
||||
@@ -86,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
|
||||
@@ -114,7 +118,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()
|
||||
@@ -123,7 +127,7 @@ func applyPodSchemaChecks(ctx context.Context, conf *config.Configuration, contr
|
||||
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
|
||||
@@ -139,7 +143,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()
|
||||
@@ -148,7 +152,7 @@ func applyControllerSchemaChecks(ctx context.Context, conf *config.Configuration
|
||||
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
|
||||
@@ -164,7 +168,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()
|
||||
@@ -173,7 +177,7 @@ func applyContainerSchemaChecks(ctx context.Context, conf *config.Configuration,
|
||||
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 {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
conf "github.com/fairwindsops/polaris/pkg/config"
|
||||
@@ -144,14 +143,15 @@ 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)
|
||||
var results ResultSet
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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(ctx, &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)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
@@ -57,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(context.Background(), &c, *workload)
|
||||
var result validator.ControllerResult
|
||||
result, err = validator.ValidateController(&c, *workload)
|
||||
assert.NoError(t, err)
|
||||
summary := result.GetSummary()
|
||||
if tc.failure {
|
||||
|
||||
Reference in New Issue
Block a user