Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/generated/checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -586,9 +586,9 @@ key: owner

**Enabled by default**: Yes

**Description**: Indicates when containers are not set to runAsNonRoot.
**Description**: Indicates when containers are not set to runAsNonRoot or explicitly use the root group.

**Remediation**: Set runAsUser to a non-zero number and runAsNonRoot to true in your pod or container securityContext. Refer to https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ for details.
**Remediation**: Set runAsUser and runAsGroup to non-zero numbers and runAsNonRoot to true in your pod or container securityContext. Refer to https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ for details.

**Template**: [run-as-non-root](templates.md#run-as-non-root-user)
## scc-deny-privileged-container
Expand Down
2 changes: 1 addition & 1 deletion docs/generated/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -865,7 +865,7 @@ KubeLinter supports the following templates:

**Key**: `run-as-non-root`

**Description**: Flag containers set to run as a root user
**Description**: Flag containers set to run as a root user or group

**Supported Objects**: DeploymentLike

Expand Down
6 changes: 5 additions & 1 deletion e2etests/bats-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -921,11 +921,15 @@ get_value_from() {

message1=$(get_value_from "${lines[0]}" '.Reports[0].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[0].Diagnostic.Message')
message2=$(get_value_from "${lines[0]}" '.Reports[1].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[1].Diagnostic.Message')
message3=$(get_value_from "${lines[0]}" '.Reports[2].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[2].Diagnostic.Message')
message4=$(get_value_from "${lines[0]}" '.Reports[3].Object.K8sObject.GroupVersionKind.Kind + ": " + .Reports[3].Diagnostic.Message')
count=$(get_value_from "${lines[0]}" '.Reports | length')

[[ "${message1}" == "Deployment: container \"app\" is not set to runAsNonRoot" ]]
[[ "${message2}" == "DeploymentConfig: container \"app2\" is not set to runAsNonRoot" ]]
[[ "${count}" == "2" ]]
[[ "${message3}" == "Deployment: container \"app3\" is set to runAsGroup 0" ]]
[[ "${message4}" == "DeploymentConfig: container \"app4\" is set to runAsGroup 0" ]]
[[ "${count}" == "4" ]]
}

@test "scc-deny-privileged-container" {
Expand Down
4 changes: 2 additions & 2 deletions pkg/builtinchecks/yamls/run-as-non-root.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: "run-as-non-root"
description: "Indicates when containers are not set to runAsNonRoot."
description: "Indicates when containers are not set to runAsNonRoot or explicitly use the root group."
remediation: >-
Set runAsUser to a non-zero number and runAsNonRoot to true in your pod or container securityContext.
Set runAsUser and runAsGroup to non-zero numbers and runAsNonRoot to true in your pod or container securityContext.
Refer to https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ for details.
scope:
objectKinds:
Expand Down
23 changes: 21 additions & 2 deletions pkg/templates/runasnonroot/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
v1 "k8s.io/api/core/v1"
)

const templateKey = "run-as-non-root"

func effectiveRunAsNonRoot(podSC *v1.PodSecurityContext, containerSC *v1.SecurityContext) bool {
if containerSC != nil && containerSC.RunAsNonRoot != nil {
return *containerSC.RunAsNonRoot
Expand All @@ -34,11 +36,21 @@ func effectiveRunAsUser(podSC *v1.PodSecurityContext, containerSC *v1.SecurityCo
return nil
}

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

func init() {
templates.Register(check.Template{
HumanName: "Run as non-root user",
Key: "run-as-non-root",
Description: "Flag containers set to run as a root user",
Key: templateKey,
Description: "Flag containers set to run as a root user or group",
SupportedObjectKinds: config.ObjectKindsDesc{
ObjectKinds: []string{objectkinds.DeploymentLike},
},
Expand All @@ -52,6 +64,13 @@ func init() {
}
var results []diagnostic.Diagnostic
for _, container := range podSpec.AllContainers() {
runAsGroup := effectiveRunAsGroup(podSpec.SecurityContext, container.SecurityContext)
if runAsGroup != nil && *runAsGroup == 0 {
results = append(results, diagnostic.Diagnostic{
Message: fmt.Sprintf("container %q is set to runAsGroup 0", container.Name),
})
}

runAsUser := effectiveRunAsUser(podSpec.SecurityContext, container.SecurityContext)
// runAsUser explicitly set to non-root. All good.
if runAsUser != nil && *runAsUser > 0 {
Expand Down
122 changes: 122 additions & 0 deletions pkg/templates/runasnonroot/template_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package runasnonroot

import (
"testing"

"github.com/stretchr/testify/suite"
"golang.stackrox.io/kube-linter/pkg/diagnostic"
"golang.stackrox.io/kube-linter/pkg/lintcontext/mocks"
"golang.stackrox.io/kube-linter/pkg/templates"
"golang.stackrox.io/kube-linter/pkg/templates/runasnonroot/internal/params"
v1 "k8s.io/api/core/v1"
)

func TestRunAsNonRoot(t *testing.T) {
suite.Run(t, new(RunAsNonRootTestSuite))
}

type RunAsNonRootTestSuite struct {
templates.TemplateTestSuite
ctx *mocks.MockLintContext
}

func (s *RunAsNonRootTestSuite) SetupTest() {
s.Init(templateKey)
s.ctx = mocks.NewMockContext()
}

func (s *RunAsNonRootTestSuite) TestContainerRunAsGroupZero() {
const deploymentName = "container-group-zero"

s.ctx.AddMockDeployment(s.T(), deploymentName)
s.ctx.AddContainerToDeployment(s.T(), deploymentName, v1.Container{
Name: "app",
SecurityContext: &v1.SecurityContext{
RunAsUser: int64Ptr(1000),
RunAsGroup: int64Ptr(0),
},
})

s.Validate(s.ctx, []templates.TestCase{
{
Param: params.Params{},
Diagnostics: map[string][]diagnostic.Diagnostic{
deploymentName: {
{Message: `container "app" is set to runAsGroup 0`},
},
},
},
})
}

func (s *RunAsNonRootTestSuite) TestPodRunAsGroupZero() {
const deploymentName = "pod-group-zero"

s.ctx.AddMockDeployment(s.T(), deploymentName)
s.ctx.AddSecurityContextToDeployment(s.T(), deploymentName, &v1.PodSecurityContext{
RunAsGroup: int64Ptr(0),
})
s.ctx.AddContainerToDeployment(s.T(), deploymentName, v1.Container{
Name: "app",
SecurityContext: &v1.SecurityContext{
RunAsUser: int64Ptr(1000),
},
})

s.Validate(s.ctx, []templates.TestCase{
{
Param: params.Params{},
Diagnostics: map[string][]diagnostic.Diagnostic{
deploymentName: {
{Message: `container "app" is set to runAsGroup 0`},
},
},
},
})
}

func (s *RunAsNonRootTestSuite) TestContainerRunAsGroupOverridesPodRunAsGroup() {
const deploymentName = "container-group-overrides-pod"

s.ctx.AddMockDeployment(s.T(), deploymentName)
s.ctx.AddSecurityContextToDeployment(s.T(), deploymentName, &v1.PodSecurityContext{
RunAsGroup: int64Ptr(0),
})
s.ctx.AddContainerToDeployment(s.T(), deploymentName, v1.Container{
Name: "app",
SecurityContext: &v1.SecurityContext{
RunAsUser: int64Ptr(1000),
RunAsGroup: int64Ptr(1000),
},
})

s.Validate(s.ctx, []templates.TestCase{
{
Param: params.Params{},
Diagnostics: nil,
},
})
}

func (s *RunAsNonRootTestSuite) TestMissingRunAsGroupAllowedWhenRunAsUserNonRoot() {
const deploymentName = "non-root-user-no-group"

s.ctx.AddMockDeployment(s.T(), deploymentName)
s.ctx.AddContainerToDeployment(s.T(), deploymentName, v1.Container{
Name: "app",
SecurityContext: &v1.SecurityContext{
RunAsUser: int64Ptr(1000),
},
})

s.Validate(s.ctx, []templates.TestCase{
{
Param: params.Params{},
Diagnostics: nil,
},
})
}

func int64Ptr(v int64) *int64 {
return &v
}
33 changes: 32 additions & 1 deletion tests/checks/run-as-non-root.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,35 @@ spec:
spec:
containers:
- name: app2
runAsUser: 0
runAsUser: 0
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: group-zero
spec:
selector:
matchLabels:
app.kubernetes.io/name: app3
template:
spec:
containers:
- name: app3
securityContext:
runAsUser: 1001
runAsGroup: 0
---
apiVersion: apps.openshift.io/v1
kind: DeploymentConfig
metadata:
name: group-zero
spec:
selector:
app.kubernetes.io/name: app4
template:
spec:
containers:
- name: app4
securityContext:
runAsUser: 1001
runAsGroup: 0
Loading