Skip to content
This repository has been archived by the owner on Oct 30, 2024. It is now read-only.

Commit

Permalink
Merge pull request #147 from Shopify/fix-138-PSC
Browse files Browse the repository at this point in the history
Inherit Pod SecurityContext incase Container SecurityContext is not defined #138
  • Loading branch information
nschhina authored Jan 28, 2019
2 parents edec4e4 + a3dd038 commit 1c6047e
Show file tree
Hide file tree
Showing 16 changed files with 240 additions and 19 deletions.
4 changes: 2 additions & 2 deletions cmd/all_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ func TestAuditAllV1(t *testing.T) {
requiredErrors := []int{
ErrorAllowPrivilegeEscalationNil, ErrorAutomountServiceAccountTokenNilAndNoName, ErrorCapabilityNotDropped,
ErrorImageTagMissing, ErrorPrivilegedNil, ErrorReadOnlyRootFilesystemNil, ErrorResourcesLimitsNil,
ErrorRunAsNonRootNil, ErrorAppArmorAnnotationMissing, ErrorSeccompAnnotationMissing,
ErrorRunAsNonRootPSCNilCSCNil, ErrorAppArmorAnnotationMissing, ErrorSeccompAnnotationMissing,
}
runAuditTest(t, "audit_all_v1.yml", mergeAuditFunctions(allAuditFunctions), requiredErrors)
}
Expand All @@ -17,7 +17,7 @@ func TestAuditAllV1beta1(t *testing.T) {
requiredErrors := []int{
ErrorAllowPrivilegeEscalationNil, ErrorAutomountServiceAccountTokenNilAndNoName, ErrorCapabilityNotDropped,
ErrorImageTagMissing, ErrorPrivilegedNil, ErrorReadOnlyRootFilesystemNil, ErrorResourcesLimitsNil,
ErrorRunAsNonRootNil, ErrorAppArmorAnnotationMissing, ErrorSeccompAnnotationMissing,
ErrorRunAsNonRootPSCNilCSCNil, ErrorAppArmorAnnotationMissing, ErrorSeccompAnnotationMissing,
}
runAuditTest(t, "audit_all_v1beta1.yml", mergeAuditFunctions(allAuditFunctions), requiredErrors)
}
4 changes: 2 additions & 2 deletions cmd/autofix.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func fixPotentialSecurityIssue(resource Resource, result Result) Resource {
resource = fixPrivileged(resource, occurrence)
case ErrorReadOnlyRootFilesystemFalse, ErrorReadOnlyRootFilesystemNil:
resource = fixReadOnlyRootFilesystem(resource, occurrence)
case ErrorRunAsNonRootFalse, ErrorRunAsNonRootNil:
case ErrorRunAsNonRootPSCTrueFalseCSCFalse, ErrorRunAsNonRootPSCNilCSCNil:
resource = fixRunAsNonRoot(resource, occurrence)
case ErrorServiceAccountTokenDeprecated:
resource = fixDeprecatedServiceAccount(resource)
Expand All @@ -46,7 +46,7 @@ func fixPotentialSecurityIssue(resource Resource, result Result) Resource {
func prepareResourceForFix(resource Resource, result Result) Resource {
needSecurityContextDefined := []int{ErrorAllowPrivilegeEscalationNil, ErrorAllowPrivilegeEscalationTrue,
ErrorPrivilegedNil, ErrorPrivilegedTrue, ErrorReadOnlyRootFilesystemFalse, ErrorReadOnlyRootFilesystemNil,
ErrorRunAsNonRootFalse, ErrorRunAsNonRootNil, ErrorServiceAccountTokenDeprecated,
ErrorRunAsNonRootPSCTrueFalseCSCFalse, ErrorRunAsNonRootPSCNilCSCNil, ErrorServiceAccountTokenDeprecated,
ErrorAutomountServiceAccountTokenTrueAndNoName, ErrorAutomountServiceAccountTokenNilAndNoName,
ErrorCapabilityNotDropped, ErrorCapabilityAdded, ErrorMisconfiguredKubeauditAllow}
needCapabilitiesDefined := []int{ErrorCapabilityNotDropped, ErrorCapabilityAdded, ErrorMisconfiguredKubeauditAllow}
Expand Down
10 changes: 6 additions & 4 deletions cmd/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,14 @@ const (
ErrorResourcesLimitsMemoryNil
// ErrorResourcesLimitsNil occurs when the resource limit is set to nil.
ErrorResourcesLimitsNil
// ErrorRunAsNonRootFalse occurs when RunAsNonRoot is set to false.
ErrorRunAsNonRootFalse
// ErrorRunAsNonRootPSCTrueCSCFalse occurs when RunAsNonRoot is set to false in the ContainerSecurityContext and to true/false in PodSecurityContext.
ErrorRunAsNonRootPSCTrueFalseCSCFalse
// ErrorRunAsNonRootPSCFalseCSCNil occurs when RunAsNonRoot is Nil in the ContainerSecurityContext and to false in Pod ecurityContext.
ErrorRunAsNonRootPSCFalseCSCNil
// ErrorRunAsNonRootFalseAllowed occurs when RunAsNonRoot is allowed to be set to false.
ErrorRunAsNonRootFalseAllowed
// ErrorRunAsNonRootNil occurs when RunAsNonRoot is not set.
ErrorRunAsNonRootNil
// ErrorRunAsNonRootNil occurs when RunAsNonRoot is not set in either PodSecurityContext or ContainerSecurityContext.
ErrorRunAsNonRootPSCNilCSCNil
// ErrorServiceAccountTokenDeprecated occurs when serviceAccount is used. ServiceAccount is a deprecated alias
// for ServiceAccountName.
ErrorServiceAccountTokenDeprecated
Expand Down
10 changes: 10 additions & 0 deletions cmd/k8sruntime_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,16 @@ func getContainers(resource Resource) (container []ContainerV1) {
return container
}

// Get PodSpec from the PodV1 resource type to check for PSC

func getPodSpecs(resource Resource) (podSpec PodSpecV1) {
switch kubeType := resource.(type) {
case *PodV1:
podSpec = kubeType.Spec
}
return podSpec
}

func getPodAnnotations(resource Resource) (annotations map[string]string) {
switch kubeType := resource.(type) {
case *CronJobV1Beta1:
Expand Down
1 change: 1 addition & 0 deletions cmd/occurrence.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ type Occurrence struct {
message string // just the message
container string // name of the container
metadata Metadata
podHost string // Hostname of the pod
}
9 changes: 8 additions & 1 deletion cmd/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,14 @@ func createFields(res Result, occ Occurrence) (fields log.Fields) {
for k, v := range occ.metadata {
fields[k] = v
}
fields["Container"] = occ.container
if len(occ.container) != 0 {
fields["Container"] = occ.container
}

if occ.id == ErrorRunAsNonRootPSCFalseCSCNil && len(occ.podHost) != 0 {
fields["Pod"] = occ.podHost
}

return
}

Expand Down
60 changes: 53 additions & 7 deletions cmd/runAsNonRoot.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import (
"github.com/spf13/cobra"
)

func checkRunAsNonRoot(container ContainerV1, result *Result) {
// Checks the CSC for RANR
func checkRunAsNonRootCSC(container ContainerV1, result *Result) {
if reason := result.Labels["audit.kubernetes.io/allow-run-as-root"]; reason != "" {
if container.SecurityContext == nil || container.SecurityContext.RunAsNonRoot == nil || *container.SecurityContext.RunAsNonRoot == false {
occ := Occurrence{
container: container.Name,
id: ErrorRunAsNonRootFalseAllowed,
kind: Warn,
message: "Allowed setting RunAsNonRoot to false",
message: "Allowed setting RunAsNonRoot to false in ContainerSecurityContext",
metadata: Metadata{"Reason": prettifyReason(reason)},
}
result.Occurrences = append(result.Occurrences, occ)
Expand All @@ -29,32 +30,77 @@ func checkRunAsNonRoot(container ContainerV1, result *Result) {
} else if container.SecurityContext == nil || container.SecurityContext.RunAsNonRoot == nil {
occ := Occurrence{
container: container.Name,
id: ErrorRunAsNonRootNil,
id: ErrorRunAsNonRootPSCNilCSCNil,
kind: Error,
message: "RunAsNonRoot is not set, which results in root user being allowed!",
message: "RunAsNonRoot is not set in ContainerSecurityContext, which results in root user being allowed!",
}
result.Occurrences = append(result.Occurrences, occ)
} else if *container.SecurityContext.RunAsNonRoot == false {
occ := Occurrence{
container: container.Name,
id: ErrorRunAsNonRootFalse,
id: ErrorRunAsNonRootPSCTrueFalseCSCFalse,
kind: Error,
message: "RunAsNonRoot is set to false (root user allowed), please set to true!",
message: "RunAsNonRoot is set to false (root user allowed) in ContainerSecurityContext, please set to true!",
}
result.Occurrences = append(result.Occurrences, occ)
}
return
}

// Checks the PodSecurityContext for RANR

func checkRunAsNonRootPSC(podSpec PodSpecV1, result *Result, containerName string) {
if reason := result.Labels["audit.kubernetes.io/allow-run-as-root"]; reason != "" {
if podSpec.SecurityContext == nil || podSpec.SecurityContext.RunAsNonRoot == nil || *podSpec.SecurityContext.RunAsNonRoot == false {
occ := Occurrence{
container: containerName,
podHost: podSpec.Hostname,
id: ErrorRunAsNonRootFalseAllowed,
kind: Warn,
message: "Allowed setting RunAsNonRoot to false",
metadata: Metadata{"Reason": prettifyReason(reason)},
}
result.Occurrences = append(result.Occurrences, occ)
} else {
occ := Occurrence{
container: containerName,
podHost: podSpec.Hostname,
id: ErrorMisconfiguredKubeauditAllow,
kind: Warn,
message: "Allowed setting RunAsNonRoot to false, but it is set to true",
metadata: Metadata{"Reason": prettifyReason(reason)},
}
result.Occurrences = append(result.Occurrences, occ)
}
} else if *podSpec.SecurityContext.RunAsNonRoot == false {
occ := Occurrence{
container: containerName,
podHost: podSpec.Hostname,
id: ErrorRunAsNonRootPSCFalseCSCNil,
kind: Error,
message: "RunAsNonRoot is set to false (root user allowed) in PodsSecurityContext and not set in ContainerSecurityContext, please set to true!",
}
result.Occurrences = append(result.Occurrences, occ)
}
return
}

func auditRunAsNonRoot(resource Resource) (results []Result) {
// get PodSpec for PodSecurityContext
podSpec := getPodSpecs(resource)
for _, container := range getContainers(resource) {
result, err := newResultFromResource(resource)
if err != nil {
log.Error(err)
return
}

checkRunAsNonRoot(container, result)
// check if ContainerSecurityContext is defined properly, else audit the PodSecurityContext
if shouldAuditCSC(podSpec, container) {
checkRunAsNonRootCSC(container, result)
} else {
checkRunAsNonRootPSC(podSpec, result, container.Name)
}
if len(result.Occurrences) > 0 {
results = append(results, *result)
}
Expand Down
33 changes: 30 additions & 3 deletions cmd/runAsNonRoot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import (
)

func TestSecurityContextNilV1(t *testing.T) {
runAuditTest(t, "security_context_nil_v1.yml", auditRunAsNonRoot, []int{ErrorRunAsNonRootNil})
runAuditTest(t, "security_context_nil_v1.yml", auditRunAsNonRoot, []int{ErrorRunAsNonRootPSCNilCSCNil})
}

func TestRunAsNonRootNilV1(t *testing.T) {
runAuditTest(t, "run_as_non_root_nil_v1.yml", auditRunAsNonRoot, []int{ErrorRunAsNonRootNil})
runAuditTest(t, "run_as_non_root_nil_v1.yml", auditRunAsNonRoot, []int{ErrorRunAsNonRootPSCNilCSCNil})
}

func TestRunAsNonRootFalseV1(t *testing.T) {
runAuditTest(t, "run_as_non_root_false_v1.yml", auditRunAsNonRoot, []int{ErrorRunAsNonRootFalse})
runAuditTest(t, "run_as_non_root_false_v1.yml", auditRunAsNonRoot, []int{ErrorRunAsNonRootPSCTrueFalseCSCFalse})
}

func TestRunAsRootFalseAllowedV1(t *testing.T) {
Expand All @@ -23,3 +23,30 @@ func TestRunAsRootFalseAllowedV1(t *testing.T) {
func TestRunAsNonRootMisconfiguredAllowV1(t *testing.T) {
runAuditTest(t, "run_as_non_root_misconfigured_allow_v1.yml", auditRunAsNonRoot, []int{ErrorMisconfiguredKubeauditAllow})
}

func TestPSCFalseCSCNilRunAsNonRootV1(t *testing.T) {
runAuditTest(t, "run_as_non_root_psc_false_v1.yml", auditRunAsNonRoot, []int{ErrorRunAsNonRootPSCFalseCSCNil})
}

func TestPSCTrueCSCFalseRunAsNonRootFalseV1(t *testing.T) {
runAuditTest(t, "run_as_non_root_psc_true_csc_false_v1.yml", auditRunAsNonRoot, []int{ErrorRunAsNonRootPSCTrueFalseCSCFalse})
}

func TestPSCFalseCSCFalseRunAsNonRootFalseV1(t *testing.T) {
runAuditTest(t, "run_as_non_root_psc_false_csc_false_v1.yml", auditRunAsNonRoot, []int{ErrorRunAsNonRootPSCTrueFalseCSCFalse})
}
func TestPSCRunAsRootFalseAllowedV1(t *testing.T) {
runAuditTest(t, "run_as_non_root_psc_false_allowed_v1.yml", auditRunAsNonRoot, []int{ErrorRunAsNonRootFalseAllowed})
}

func TestPSCFalseCSCTrueRunAsNonRootFalseV1(t *testing.T) {
runAuditTest(t, "run_as_non_root_psc_false_csc_true_v1.yml", auditRunAsNonRoot, []int{})
}

func TestPSCFalseCSCNilMultipleRunAsNonRootFalseV1(t *testing.T) {
runAuditTest(t, "run_as_non_root_psc_false_csc_nil_multiple_cont_v1.yml", auditRunAsNonRoot, []int{ErrorRunAsNonRootPSCFalseCSCNil})
}

func TestPSCFalseCSCTrueMultipleRunAsNonRootFalseV1(t *testing.T) {
runAuditTest(t, "run_as_non_root_psc_false_csc_true_multiple_cont_v1.yml", auditRunAsNonRoot, []int{})
}
10 changes: 10 additions & 0 deletions cmd/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,3 +360,13 @@ func prettifyReason(reason string) string {
}
return reason
}

func shouldAuditCSC(podSpec PodSpecV1, container ContainerV1) bool {
if container.SecurityContext != nil && container.SecurityContext.RunAsNonRoot != nil {
return true
}
if podSpec.SecurityContext == nil || podSpec.SecurityContext.RunAsNonRoot == nil {
return true
}
return false
}
17 changes: 17 additions & 0 deletions fixtures/run_as_non_root_psc_false_allowed_v1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
name: run_as_non_root_false_allowed
labels:
apps: fakeSecurityContext
audit.kubernetes.io/allow-run-as-root: "Superuser privileges needed"
namespace: fakeDeploymentRANR
spec:
securityContext:
runAsNonRoot: false
containers:
- name: fakeContainerRANR
resources: {}
securityContext: {}
status: {}
15 changes: 15 additions & 0 deletions fixtures/run_as_non_root_psc_false_csc_false_v1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
name: run_as_non_root_psc_false_csc_false
namespace: fakeDeploymentRANR
spec:
securityContext:
runAsNonRoot: false
containers:
- name: fakeContainerRANR
resources: {}
securityContext:
runAsNonRoot: false
status: {}
19 changes: 19 additions & 0 deletions fixtures/run_as_non_root_psc_false_csc_nil_multiple_cont_v1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
name: run_as_non_root_psc_false_csc_nil_multiple
namespace: fakeDeploymentRANR
spec:
hostname: multiple_containers
securityContext:
runAsNonRoot: false
containers:
- name: fakeContainerRANR1
resources: {}
securityContext:
runAsNonRoot: true
- name: fakeContainerRANR2
resources: {}
securityContext: {}
status: {}
20 changes: 20 additions & 0 deletions fixtures/run_as_non_root_psc_false_csc_true_multiple_cont_v1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
name: run_as_non_root_psc_false_csc_true_multiple
namespace: fakeDeploymentRANR
spec:
hostname: multiple_containers
securityContext:
runAsNonRoot: false
containers:
- name: fakeContainerRANR1
resources: {}
securityContext:
runAsNonRoot: true
- name: fakeContainerRANR2
resources: {}
securityContext:
runAsNonRoot: true
status: {}
15 changes: 15 additions & 0 deletions fixtures/run_as_non_root_psc_false_csc_true_v1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
name: run_as_non_root_psc_false_csc_false
namespace: fakeDeploymentRANR
spec:
securityContext:
runAsNonRoot: false
containers:
- name: fakeContainerRANR
resources: {}
securityContext:
runAsNonRoot: true
status: {}
17 changes: 17 additions & 0 deletions fixtures/run_as_non_root_psc_false_v1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
name: run_as_non_root_false
labels:
apps: fakeSecurityContext
namespace: fakeDeploymentRANR
spec:
hostname: fakeRANR
securityContext:
runAsNonRoot: false
containers:
- name: fakeContainerRANR
resources: {}
securityContext: {}
status: {}
15 changes: 15 additions & 0 deletions fixtures/run_as_non_root_psc_true_csc_false_v1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
name: run_as_non_root_psc_true_csc_false
namespace: fakeDeploymentRANR
spec:
securityContext:
runAsNonRoot: true
containers:
- name: fakeContainerRANR
resources: {}
securityContext:
runAsNonRoot: false
status: {}

0 comments on commit 1c6047e

Please sign in to comment.