Skip to content

Commit 9aa05d4

Browse files
authored
run-as-non-root: flag explicit root group (#1178)
1 parent 2f07755 commit 9aa05d4

7 files changed

Lines changed: 185 additions & 9 deletions

File tree

docs/generated/checks.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -586,9 +586,9 @@ key: owner
586586
587587
**Enabled by default**: Yes
588588
589-
**Description**: Indicates when containers are not set to runAsNonRoot.
589+
**Description**: Indicates when containers are not set to runAsNonRoot or explicitly use the root group.
590590
591-
**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.
591+
**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.
592592
593593
**Template**: [run-as-non-root](templates.md#run-as-non-root-user)
594594
## scc-deny-privileged-container

docs/generated/templates.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -865,7 +865,7 @@ KubeLinter supports the following templates:
865865

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

868-
**Description**: Flag containers set to run as a root user
868+
**Description**: Flag containers set to run as a root user or group
869869

870870
**Supported Objects**: DeploymentLike
871871

e2etests/bats-tests.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -921,11 +921,15 @@ get_value_from() {
921921

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

926928
[[ "${message1}" == "Deployment: container \"app\" is not set to runAsNonRoot" ]]
927929
[[ "${message2}" == "DeploymentConfig: container \"app2\" is not set to runAsNonRoot" ]]
928-
[[ "${count}" == "2" ]]
930+
[[ "${message3}" == "Deployment: container \"app3\" is set to runAsGroup 0" ]]
931+
[[ "${message4}" == "DeploymentConfig: container \"app4\" is set to runAsGroup 0" ]]
932+
[[ "${count}" == "4" ]]
929933
}
930934

931935
@test "scc-deny-privileged-container" {

pkg/builtinchecks/yamls/run-as-non-root.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: "run-as-non-root"
2-
description: "Indicates when containers are not set to runAsNonRoot."
2+
description: "Indicates when containers are not set to runAsNonRoot or explicitly use the root group."
33
remediation: >-
4-
Set runAsUser to a non-zero number and runAsNonRoot to true in your pod or container securityContext.
4+
Set runAsUser and runAsGroup to non-zero numbers and runAsNonRoot to true in your pod or container securityContext.
55
Refer to https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ for details.
66
scope:
77
objectKinds:

pkg/templates/runasnonroot/template.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
v1 "k8s.io/api/core/v1"
1515
)
1616

17+
const templateKey = "run-as-non-root"
18+
1719
func effectiveRunAsNonRoot(podSC *v1.PodSecurityContext, containerSC *v1.SecurityContext) bool {
1820
if containerSC != nil && containerSC.RunAsNonRoot != nil {
1921
return *containerSC.RunAsNonRoot
@@ -34,11 +36,21 @@ func effectiveRunAsUser(podSC *v1.PodSecurityContext, containerSC *v1.SecurityCo
3436
return nil
3537
}
3638

39+
func effectiveRunAsGroup(podSC *v1.PodSecurityContext, containerSC *v1.SecurityContext) *int64 {
40+
if containerSC != nil && containerSC.RunAsGroup != nil {
41+
return containerSC.RunAsGroup
42+
}
43+
if podSC != nil {
44+
return podSC.RunAsGroup
45+
}
46+
return nil
47+
}
48+
3749
func init() {
3850
templates.Register(check.Template{
3951
HumanName: "Run as non-root user",
40-
Key: "run-as-non-root",
41-
Description: "Flag containers set to run as a root user",
52+
Key: templateKey,
53+
Description: "Flag containers set to run as a root user or group",
4254
SupportedObjectKinds: config.ObjectKindsDesc{
4355
ObjectKinds: []string{objectkinds.DeploymentLike},
4456
},
@@ -52,6 +64,13 @@ func init() {
5264
}
5365
var results []diagnostic.Diagnostic
5466
for _, container := range podSpec.AllContainers() {
67+
runAsGroup := effectiveRunAsGroup(podSpec.SecurityContext, container.SecurityContext)
68+
if runAsGroup != nil && *runAsGroup == 0 {
69+
results = append(results, diagnostic.Diagnostic{
70+
Message: fmt.Sprintf("container %q is set to runAsGroup 0", container.Name),
71+
})
72+
}
73+
5574
runAsUser := effectiveRunAsUser(podSpec.SecurityContext, container.SecurityContext)
5675
// runAsUser explicitly set to non-root. All good.
5776
if runAsUser != nil && *runAsUser > 0 {
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package runasnonroot
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/suite"
7+
"golang.stackrox.io/kube-linter/pkg/diagnostic"
8+
"golang.stackrox.io/kube-linter/pkg/lintcontext/mocks"
9+
"golang.stackrox.io/kube-linter/pkg/templates"
10+
"golang.stackrox.io/kube-linter/pkg/templates/runasnonroot/internal/params"
11+
v1 "k8s.io/api/core/v1"
12+
)
13+
14+
func TestRunAsNonRoot(t *testing.T) {
15+
suite.Run(t, new(RunAsNonRootTestSuite))
16+
}
17+
18+
type RunAsNonRootTestSuite struct {
19+
templates.TemplateTestSuite
20+
ctx *mocks.MockLintContext
21+
}
22+
23+
func (s *RunAsNonRootTestSuite) SetupTest() {
24+
s.Init(templateKey)
25+
s.ctx = mocks.NewMockContext()
26+
}
27+
28+
func (s *RunAsNonRootTestSuite) TestContainerRunAsGroupZero() {
29+
const deploymentName = "container-group-zero"
30+
31+
s.ctx.AddMockDeployment(s.T(), deploymentName)
32+
s.ctx.AddContainerToDeployment(s.T(), deploymentName, v1.Container{
33+
Name: "app",
34+
SecurityContext: &v1.SecurityContext{
35+
RunAsUser: int64Ptr(1000),
36+
RunAsGroup: int64Ptr(0),
37+
},
38+
})
39+
40+
s.Validate(s.ctx, []templates.TestCase{
41+
{
42+
Param: params.Params{},
43+
Diagnostics: map[string][]diagnostic.Diagnostic{
44+
deploymentName: {
45+
{Message: `container "app" is set to runAsGroup 0`},
46+
},
47+
},
48+
},
49+
})
50+
}
51+
52+
func (s *RunAsNonRootTestSuite) TestPodRunAsGroupZero() {
53+
const deploymentName = "pod-group-zero"
54+
55+
s.ctx.AddMockDeployment(s.T(), deploymentName)
56+
s.ctx.AddSecurityContextToDeployment(s.T(), deploymentName, &v1.PodSecurityContext{
57+
RunAsGroup: int64Ptr(0),
58+
})
59+
s.ctx.AddContainerToDeployment(s.T(), deploymentName, v1.Container{
60+
Name: "app",
61+
SecurityContext: &v1.SecurityContext{
62+
RunAsUser: int64Ptr(1000),
63+
},
64+
})
65+
66+
s.Validate(s.ctx, []templates.TestCase{
67+
{
68+
Param: params.Params{},
69+
Diagnostics: map[string][]diagnostic.Diagnostic{
70+
deploymentName: {
71+
{Message: `container "app" is set to runAsGroup 0`},
72+
},
73+
},
74+
},
75+
})
76+
}
77+
78+
func (s *RunAsNonRootTestSuite) TestContainerRunAsGroupOverridesPodRunAsGroup() {
79+
const deploymentName = "container-group-overrides-pod"
80+
81+
s.ctx.AddMockDeployment(s.T(), deploymentName)
82+
s.ctx.AddSecurityContextToDeployment(s.T(), deploymentName, &v1.PodSecurityContext{
83+
RunAsGroup: int64Ptr(0),
84+
})
85+
s.ctx.AddContainerToDeployment(s.T(), deploymentName, v1.Container{
86+
Name: "app",
87+
SecurityContext: &v1.SecurityContext{
88+
RunAsUser: int64Ptr(1000),
89+
RunAsGroup: int64Ptr(1000),
90+
},
91+
})
92+
93+
s.Validate(s.ctx, []templates.TestCase{
94+
{
95+
Param: params.Params{},
96+
Diagnostics: nil,
97+
},
98+
})
99+
}
100+
101+
func (s *RunAsNonRootTestSuite) TestMissingRunAsGroupAllowedWhenRunAsUserNonRoot() {
102+
const deploymentName = "non-root-user-no-group"
103+
104+
s.ctx.AddMockDeployment(s.T(), deploymentName)
105+
s.ctx.AddContainerToDeployment(s.T(), deploymentName, v1.Container{
106+
Name: "app",
107+
SecurityContext: &v1.SecurityContext{
108+
RunAsUser: int64Ptr(1000),
109+
},
110+
})
111+
112+
s.Validate(s.ctx, []templates.TestCase{
113+
{
114+
Param: params.Params{},
115+
Diagnostics: nil,
116+
},
117+
})
118+
}
119+
120+
func int64Ptr(v int64) *int64 {
121+
return &v
122+
}

tests/checks/run-as-non-root.yml

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,35 @@ spec:
5555
spec:
5656
containers:
5757
- name: app2
58-
runAsUser: 0
58+
runAsUser: 0
59+
---
60+
apiVersion: apps/v1
61+
kind: Deployment
62+
metadata:
63+
name: group-zero
64+
spec:
65+
selector:
66+
matchLabels:
67+
app.kubernetes.io/name: app3
68+
template:
69+
spec:
70+
containers:
71+
- name: app3
72+
securityContext:
73+
runAsUser: 1001
74+
runAsGroup: 0
75+
---
76+
apiVersion: apps.openshift.io/v1
77+
kind: DeploymentConfig
78+
metadata:
79+
name: group-zero
80+
spec:
81+
selector:
82+
app.kubernetes.io/name: app4
83+
template:
84+
spec:
85+
containers:
86+
- name: app4
87+
securityContext:
88+
runAsUser: 1001
89+
runAsGroup: 0

0 commit comments

Comments
 (0)