Skip to content

Commit

Permalink
Add a couple more check templates and built-in checks (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
viswajithiii committed Sep 21, 2020
1 parent f5db165 commit 3dd7446
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 1 deletion.
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ linters-settings:
- importShadow
- emptyStringTest
- hugeParam
- rangeValCopy
nolintlint:
allow-leading-space: false # require machine-readable nolint directives (i.e. with no leading space)
allow-unused: false # report any unused nolint directives
Expand Down
4 changes: 3 additions & 1 deletion docs/generated/checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ The following table enumerates built-in checks:

| Name | Enabled by default | Description | Template | Parameters |
| ---- | ------------------ | ----------- | -------- | ---------- |
| env-var-secret | No | Alert on objects using a secret in an environment variable | env-var |- `name`: `.*secret.*` <br />|
| env-var-secret | Yes | Alert on objects using a secret in an environment variable | env-var |- `name`: `.*secret.*` <br />|
| no-read-only-root-fs | Yes | Alert on containers not running with a read-only root filesystem | read-only-root-fs | none |
| privileged-container | Yes | Alert on deployments with containers running in privileged mode | privileged | none |
| required-label-owner | No | Alert on objects without the 'owner' label | required-label |- `key`: `owner` <br />|
| run-as-non-root | Yes | Alert on containers not set to runAsNonRoot | run-as-non-root | none |
2 changes: 2 additions & 0 deletions docs/generated/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ The following table enumerates supported check templates:
| ---- | ----------- | ----------------- | ---------- |
| env-var | Flag environment variables that match the provided patterns | DeploymentLike |- `name` (required): A regex for the env var name <br />- `value`: A regex for the env var value <br />|
| privileged | Flag privileged containers | DeploymentLike | none |
| read-only-root-fs | Flag containers without read-only root file systems | DeploymentLike | none |
| required-label | Flag objects not carrying at least one label matching the provided patterns | Any |- `key` (required): A regex for the key of the required label <br />- `value`: A regex for the value of the required label <br />|
| run-as-non-root | Flag containers set to run as a root user | DeploymentLike | none |
6 changes: 6 additions & 0 deletions internal/builtinchecks/yamls/read-only-root-fs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name: "no-read-only-root-fs"
description: "Alert on containers not running with a read-only root filesystem"
scope:
objectKinds:
- DeploymentLike
template: "read-only-root-fs"
6 changes: 6 additions & 0 deletions internal/builtinchecks/yamls/run-as-non-root.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name: "run-as-non-root"
description: "Alert on containers not set to runAsNonRoot"
scope:
objectKinds:
- DeploymentLike
template: "run-as-non-root"
3 changes: 3 additions & 0 deletions internal/defaultchecks/default_checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@ var (
// List is the list of built-in checks that are enabled by default.
List = set.NewFrozenStringSet(
"privileged-container",
"env-var-secret",
"no-read-only-root-fs",
"run-as-non-root",
)
)
22 changes: 22 additions & 0 deletions internal/defaultchecks/default_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package defaultchecks

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.stackrox.io/kube-linter/internal/builtinchecks"
"golang.stackrox.io/kube-linter/internal/set"
)

func TestListReferencesOnlyValidChecks(t *testing.T) {
allChecks, err := builtinchecks.List()
require.NoError(t, err)
allCheckNames := set.NewStringSet()
for _, check := range allChecks {
allCheckNames.Add(check.Name)
}
for _, defaultCheck := range List.AsSlice() {
assert.True(t, allCheckNames.Contains(defaultCheck), "default check %s invalid", defaultCheck)
}
}
2 changes: 2 additions & 0 deletions internal/templates/all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ import (
// Import all check templates.
_ "golang.stackrox.io/kube-linter/internal/templates/envvar"
_ "golang.stackrox.io/kube-linter/internal/templates/privileged"
_ "golang.stackrox.io/kube-linter/internal/templates/readonlyrootfs"
_ "golang.stackrox.io/kube-linter/internal/templates/requiredlabel"
_ "golang.stackrox.io/kube-linter/internal/templates/runasnonroot"
)
39 changes: 39 additions & 0 deletions internal/templates/readonlyrootfs/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package readonlyrootfs

import (
"fmt"

"golang.stackrox.io/kube-linter/internal/check"
"golang.stackrox.io/kube-linter/internal/diagnostic"
"golang.stackrox.io/kube-linter/internal/extract"
"golang.stackrox.io/kube-linter/internal/lintcontext"
"golang.stackrox.io/kube-linter/internal/objectkinds"
"golang.stackrox.io/kube-linter/internal/templates"
)

func init() {
templates.Register(check.Template{
Name: "read-only-root-fs",
Description: "Flag containers without read-only root file systems",
SupportedObjectKinds: check.ObjectKindsDesc{
ObjectKinds: []string{objectkinds.DeploymentLike},
},
Parameters: nil,
Instantiate: func(_ map[string]string) (check.Func, error) {
return func(_ *lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic {
podSpec, found := extract.PodSpec(object.K8sObject)
if !found {
return nil
}
var results []diagnostic.Diagnostic
for _, container := range podSpec.Containers {
sc := container.SecurityContext
if sc == nil || sc.ReadOnlyRootFilesystem == nil || !*sc.ReadOnlyRootFilesystem {
results = append(results, diagnostic.Diagnostic{Message: fmt.Sprintf("container %q does not have a read-only root file system", container.Name)})
}
}
return results
}, nil
},
})
}
72 changes: 72 additions & 0 deletions internal/templates/runasnonroot/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package runasnonroot

import (
"fmt"

"golang.stackrox.io/kube-linter/internal/check"
"golang.stackrox.io/kube-linter/internal/diagnostic"
"golang.stackrox.io/kube-linter/internal/extract"
"golang.stackrox.io/kube-linter/internal/lintcontext"
"golang.stackrox.io/kube-linter/internal/objectkinds"
"golang.stackrox.io/kube-linter/internal/templates"
v1 "k8s.io/api/core/v1"
)

func effectiveRunAsNonRoot(podSC *v1.PodSecurityContext, containerSC *v1.SecurityContext) bool {
if containerSC != nil && containerSC.RunAsNonRoot != nil {
return *containerSC.RunAsNonRoot
}
if podSC != nil && podSC.RunAsNonRoot != nil {
return *podSC.RunAsNonRoot
}
return false
}

func effectiveRunAsUser(podSC *v1.PodSecurityContext, containerSC *v1.SecurityContext) *int64 {
if containerSC != nil && containerSC.RunAsUser != nil {
return containerSC.RunAsUser
}
if podSC != nil {
return podSC.RunAsUser
}
return nil
}

func init() {
templates.Register(check.Template{
Name: "run-as-non-root",
Description: "Flag containers set to run as a root user",
SupportedObjectKinds: check.ObjectKindsDesc{
ObjectKinds: []string{objectkinds.DeploymentLike},
},
Parameters: nil,
Instantiate: func(_ map[string]string) (check.Func, error) {
return func(_ *lintcontext.LintContext, object lintcontext.Object) []diagnostic.Diagnostic {
podSpec, found := extract.PodSpec(object.K8sObject)
if !found {
return nil
}
var results []diagnostic.Diagnostic
for _, container := range podSpec.Containers {
runAsUser := effectiveRunAsUser(podSpec.SecurityContext, container.SecurityContext)
// runAsUser explicitly set to non-root. All good.
if runAsUser != nil && *runAsUser > 0 {
continue
}
runAsNonRoot := effectiveRunAsNonRoot(podSpec.SecurityContext, container.SecurityContext)
if runAsNonRoot {
// runAsNonRoot set, but runAsUser set to 0. This will result in a runtime failure.
if runAsUser != nil && *runAsUser == 0 {
results = append(results, diagnostic.Diagnostic{
Message: fmt.Sprintf("container %q is set to runAsNonRoot, but runAsUser set to %d", container.Name, *runAsUser),
})
}
continue
}
results = append(results, diagnostic.Diagnostic{Message: fmt.Sprintf("container %q is not set to runAsNonRoot", container.Name)})
}
return results
}, nil
},
})
}

0 comments on commit 3dd7446

Please sign in to comment.