Skip to content
This repository has been archived by the owner on Jan 12, 2023. It is now read-only.

Commit

Permalink
Added policy to block the root user
Browse files Browse the repository at this point in the history
  • Loading branch information
djcass44 committed Jul 3, 2021
1 parent a84bce8 commit 2172411
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 1 deletion.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,13 @@ The Docker socket bind mount provides API access to the host Docker daemon, whic

**Note:** It is recommended to use the `No Bind Mounts` policy to disable all `hostPath` mounts rather than only this policy, because it is easily bypassed. This policy does not provide meaningful protection and is here for informative purposes.

## No Root User

Running as the root user is extremely dangerous and should be forbidden for all possible workloads.
This policy blocks pods when the security context doesn't explicitly set `runAsUser: [some uid > 0]` or `runAsNonRoot: true`

The securityContext can be set at the pod level or on each individual container.

## EmptyDir size limit

By [default](https://kubernetes.io/docs/concepts/storage/volumes/#example-pod), an `emptyDir` lacks a `sizeLimit` parameter, and is disk-based;
Expand Down
2 changes: 1 addition & 1 deletion charts/k-rail/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ apiVersion: v1
name: k-rail
description: Kubernetes security tool for policy enforcement
home: https://github.com/cruise-automation/k-rail
version: v3.4.1
version: v3.4.2
maintainers:
- name: cruise-automation
url: https://cruise-automation.github.io/k-rail/
3 changes: 3 additions & 0 deletions charts/k-rail/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ config:
- name: "pod_no_docker_sock"
enabled: True
report_only: False
- name: "pod_no_root_user"
enabled: True
report_only: False
- name: "pod_immutable_reference"
enabled: True
report_only: False
Expand Down
5 changes: 5 additions & 0 deletions examples/non-compliant-daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ spec:
spec:
hostNetwork: true
hostPID: true
securityContext:
runAsNonRoot: false
runAsUser: 0
volumes:
- name: dockersock
hostPath:
Expand All @@ -32,6 +35,8 @@ spec:
- name: hostroot
mountPath: "/host"
securityContext:
runAsUser: 0
runAsNonRoot: false
privileged: true
capabilities:
add: ["NET_ADMIN", "SYS_ADMIN"]
5 changes: 5 additions & 0 deletions examples/non-compliant-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ spec:
spec:
hostNetwork: true
hostPID: true
securityContext:
runAsNonRoot: false
runAsUser: 0
volumes:
- name: dockersock
hostPath:
Expand All @@ -33,6 +36,8 @@ spec:
- name: hostroot
mountPath: "/host"
securityContext:
runAsUser: 0
runAsNonRoot: false
privileged: true
capabilities:
add: ["NET_ADMIN", "SYS_ADMIN"]
Expand Down
68 changes: 68 additions & 0 deletions policies/pod/no_root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package pod

import (
"context"
"fmt"
"github.com/cruise-automation/k-rail/v3/policies"
"github.com/cruise-automation/k-rail/v3/resource"
admissionv1 "k8s.io/api/admission/v1"
corev1 "k8s.io/api/core/v1"
)

type PolicyNoRootUser struct{}

func (p PolicyNoRootUser) Name() string {
return "pod_no_root_user"
}

func (p PolicyNoRootUser) Validate(ctx context.Context, _ policies.Config, ar *admissionv1.AdmissionRequest) ([]policies.ResourceViolation, []policies.PatchOperation) {
resourceViolations := []policies.ResourceViolation{}
podResource := resource.GetPodResource(ctx, ar)
if podResource == nil {
return resourceViolations, nil
}
violationText := "No Root user: Running as the root user is forbidden"

// check the containers first
validateSecurityContext := func(container corev1.Container) {
if container.SecurityContext != nil && container.SecurityContext.RunAsNonRoot != nil && *container.SecurityContext.RunAsNonRoot {
return
} else if container.SecurityContext != nil && container.SecurityContext.RunAsUser != nil && *container.SecurityContext.RunAsUser > 0 {
return
}
resourceViolations = append(resourceViolations, policies.ResourceViolation{
Namespace: ar.Namespace,
ResourceName: podResource.ResourceName,
ResourceKind: podResource.ResourceKind,
Violation: fmt.Sprintf("No Root user: Container %s can as the root user which is forbidden", container.Name),
Policy: p.Name(),
})
}
for _, container := range podResource.PodSpec.Containers {
validateSecurityContext(container)
}
for _, container := range podResource.PodSpec.InitContainers {
validateSecurityContext(container)
}

// if all the containers have the appropriate securityContext
// and the podSecurityContext is unset, we can skip checking it
if len(resourceViolations) == 0 && podResource.PodSpec.SecurityContext == nil {
return resourceViolations, nil
}

if podResource.PodSpec.SecurityContext != nil && podResource.PodSpec.SecurityContext.RunAsNonRoot != nil && *podResource.PodSpec.SecurityContext.RunAsNonRoot {
return resourceViolations, nil
} else if podResource.PodSpec.SecurityContext != nil && podResource.PodSpec.SecurityContext.RunAsUser != nil && *podResource.PodSpec.SecurityContext.RunAsUser > 0 {
return resourceViolations, nil
}

resourceViolations = append(resourceViolations, policies.ResourceViolation{
Namespace: ar.Namespace,
ResourceName: podResource.ResourceName,
ResourceKind: podResource.ResourceKind,
Violation: violationText,
Policy: p.Name(),
})
return resourceViolations, nil
}
115 changes: 115 additions & 0 deletions policies/pod/no_root_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package pod

import (
"context"
"encoding/json"
"github.com/cruise-automation/k-rail/v3/policies"
admissionv1 "k8s.io/api/admission/v1"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/utils/pointer"
"reflect"
"testing"
)

func TestPolicyNoRoot(t *testing.T) {
ctx := context.Background()

tests := []struct {
name string
podSpec v1.PodSpec
violations int
}{
{
name: "empty securityContext",
violations: 3,
podSpec: v1.PodSpec{
SecurityContext: &v1.PodSecurityContext{},
Containers: []v1.Container{
{
SecurityContext: &v1.SecurityContext{},
},
},
InitContainers: []v1.Container{
{
SecurityContext: &v1.SecurityContext{},
},
},
},
},
{
name: "runAsRoot",
violations: 1,
podSpec: v1.PodSpec{
SecurityContext: &v1.PodSecurityContext{
RunAsUser: pointer.Int64Ptr(0),
},
},
},
{
name: "runAsUser",
violations: 0,
podSpec: v1.PodSpec{
SecurityContext: &v1.PodSecurityContext{
RunAsUser: pointer.Int64Ptr(1001),
},
},
},
{
name: "runAsNonRoot",
violations: 0,
podSpec: v1.PodSpec{
SecurityContext: &v1.PodSecurityContext{
RunAsNonRoot: pointer.BoolPtr(true),
},
},
},
{
name: "runAsUser container",
violations: 0,
podSpec: v1.PodSpec{
SecurityContext: &v1.PodSecurityContext{
RunAsNonRoot: pointer.BoolPtr(true),
},
Containers: []v1.Container{
{
SecurityContext: &v1.SecurityContext{
RunAsUser: pointer.Int64Ptr(1001),
},
},
},
},
},
{
name: "no pod context, but containers are set",
violations: 0,
podSpec: v1.PodSpec{
SecurityContext: nil,
Containers: []v1.Container{
{
SecurityContext: &v1.SecurityContext{
RunAsNonRoot: pointer.BoolPtr(true),
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
raw, _ := json.Marshal(corev1.Pod{Spec: tt.podSpec})
ar := &admissionv1.AdmissionRequest{
Namespace: "namespace",
Name: "name",
Object: runtime.RawExtension{Raw: raw},
Resource: metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"},
}
v := PolicyNoRootUser{}
if got, _ := v.Validate(ctx, policies.Config{}, ar); !reflect.DeepEqual(len(got), tt.violations) {
t.Errorf("PolicyNoRootUser() %s got %v want %v violations", tt.name, len(got), tt.violations)
}
})
}
}
1 change: 1 addition & 0 deletions server/policies.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ func (s *Server) registerPolicies() {
s.registerPolicy(pod.PolicyNoExec{})
s.registerPolicy(pod.PolicyBindMounts{})
s.registerPolicy(pod.PolicyDockerSock{})
s.registerPolicy(pod.PolicyNoRootUser{})
s.registerPolicy(pod.PolicyEmptyDirSizeLimit{})
s.registerPolicy(pod.PolicyImageImmutableReference{})
s.registerPolicy(pod.PolicyNoTiller{})
Expand Down

0 comments on commit 2172411

Please sign in to comment.