Merge pull request #14 from digitalocean/varsha/privileged-containers

Privileged container check: Add warning if a privileged container container is found.
varsha/versions
Varsha Varadarajan 2019-06-19 19:14:53 -04:00 committed by GitHub
commit 049292bd67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 258 additions and 13 deletions

View File

@ -6,4 +6,5 @@ import (
_ "github.com/digitalocean/clusterlint/checks/basic"
_ "github.com/digitalocean/clusterlint/checks/doks"
_ "github.com/digitalocean/clusterlint/checks/noop"
_ "github.com/digitalocean/clusterlint/checks/security"
)

View File

@ -153,33 +153,37 @@ func TestLatestTagWarning(t *testing.T) {
func initPod() *kube.Objects {
objs := &kube.Objects{
Pods: &corev1.PodList{},
Pods: &corev1.PodList{
Items: []corev1.Pod{
{
ObjectMeta: metav1.ObjectMeta{Name: "pod_foo", Namespace: "k8s"},
},
},
},
}
return objs
}
func container(image string) *kube.Objects {
objs := initPod()
objs.Pods = &corev1.PodList{
Items: []corev1.Pod{
objs.Pods.Items[0].Spec = corev1.PodSpec{
Containers: []corev1.Container{
{
ObjectMeta: metav1.ObjectMeta{Name: "pod_foo", Namespace: "k8s"},
Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "bar", Image: image}}},
},
},
Name: "bar",
Image: image,
}},
}
return objs
}
func initContainer(image string) *kube.Objects {
objs := initPod()
objs.Pods = &corev1.PodList{
Items: []corev1.Pod{
objs.Pods.Items[0].Spec = corev1.PodSpec{
InitContainers: []corev1.Container{
{
ObjectMeta: metav1.ObjectMeta{Name: "pod_foo", Namespace: "k8s"},
Spec: corev1.PodSpec{InitContainers: []corev1.Container{{Name: "bar", Image: image}}},
},
},
Name: "bar",
Image: image,
}},
}
return objs
}

View File

@ -0,0 +1,59 @@
package security
import (
"fmt"
"github.com/digitalocean/clusterlint/checks"
"github.com/digitalocean/clusterlint/kube"
corev1 "k8s.io/api/core/v1"
)
func init() {
checks.Register(&privilegedContainerCheck{})
}
type privilegedContainerCheck struct{}
// Name returns a unique name for this check.
func (pc *privilegedContainerCheck) Name() string {
return "privileged-containers"
}
// Groups returns a list of group names this check should be part of.
func (pc *privilegedContainerCheck) Groups() []string {
return []string{"security"}
}
// Description returns a detailed human-readable description of what this check
// does.
func (pc *privilegedContainerCheck) Description() string {
return "Checks if there are pods with containers in privileged mode"
}
// Run runs this check on a set of Kubernetes objects. It can return warnings
// (low-priority problems) and errors (high-priority problems) as well as an
// error value indicating that the check failed to run.
func (pc *privilegedContainerCheck) Run(objects *kube.Objects) (warnings []error, errors []error, err error) {
var w []error
for _, pod := range objects.Pods.Items {
podName := pod.GetName()
namespace := pod.GetNamespace()
w = append(w, checkPrivileged(pod.Spec.Containers, podName, namespace)...)
w = append(w, checkPrivileged(pod.Spec.InitContainers, podName, namespace)...)
}
return w, nil, nil
}
// checkPrivileged checks if the container is running in privileged mode
// Adds a warning if it finds any privileged container
func checkPrivileged(containers []corev1.Container, podName string, namespace string) []error {
var w []error
for _, container := range containers {
if container.SecurityContext != nil && container.SecurityContext.Privileged != nil && *container.SecurityContext.Privileged {
w = append(w, fmt.Errorf("[Best Practice] Privileged container '%s' found in pod '%s', namespace '%s'.", container.Name, podName, namespace))
}
}
return w
}

View File

@ -0,0 +1,181 @@
package security
import (
"fmt"
"testing"
"github.com/digitalocean/clusterlint/checks"
"github.com/digitalocean/clusterlint/kube"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func TestPrivilegedContainersCheckMeta(t *testing.T) {
privilegedContainerCheck := privilegedContainerCheck{}
assert.Equal(t, "privileged-containers", privilegedContainerCheck.Name())
assert.Equal(t, "Checks if there are pods with containers in privileged mode", privilegedContainerCheck.Description())
assert.Equal(t, []string{"security"}, privilegedContainerCheck.Groups())
}
func TestPrivilegedContainersCheckRegistration(t *testing.T) {
privilegedContainerCheck := &privilegedContainerCheck{}
check, err := checks.Get("privileged-containers")
assert.Equal(t, check, privilegedContainerCheck)
assert.Nil(t, err)
}
func TestPrivilegedContainerWarning(t *testing.T) {
scenarios := []struct {
name string
arg *kube.Objects
expected []error
}{
{
name: "no pods",
arg: initPod(),
expected: nil,
},
{
name: "pod with container in privileged mode",
arg: container(true),
expected: warnings(),
},
{
name: "pod with container.SecurityContext = nil",
arg: containerSecurityContextNil(),
expected: nil,
},
{
name: "pod with container.SecurityContext.Privileged = nil",
arg: containerPrivilegedNil(),
expected: nil,
},
{
name: "pod with container in regular mode",
arg: container(false),
expected: nil,
},
{
name: "pod with init container in privileged mode",
arg: initContainer(true),
expected: warnings(),
},
{
name: "pod with initContainer.SecurityContext = nil",
arg: initContainerSecurityContextNil(),
expected: nil,
},
{
name: "pod with initContainer.SecurityContext.Privileged = nil",
arg: initContainerPrivilegedNil(),
expected: nil,
},
{
name: "pod with init container in regular mode",
arg: initContainer(false),
expected: nil,
},
}
privilegedContainerCheck := privilegedContainerCheck{}
for _, scenario := range scenarios {
t.Run(scenario.name, func(t *testing.T) {
w, e, err := privilegedContainerCheck.Run(scenario.arg)
assert.ElementsMatch(t, scenario.expected, w)
assert.Empty(t, e)
assert.Nil(t, err)
})
}
}
func initPod() *kube.Objects {
objs := &kube.Objects{
Pods: &corev1.PodList{
Items: []corev1.Pod{
{
ObjectMeta: metav1.ObjectMeta{Name: "pod_foo", Namespace: "k8s"},
},
},
},
}
return objs
}
func container(privileged bool) *kube.Objects {
objs := initPod()
objs.Pods.Items[0].Spec = corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "bar",
SecurityContext: &corev1.SecurityContext{Privileged: &privileged},
}},
}
return objs
}
func containerSecurityContextNil() *kube.Objects {
objs := initPod()
objs.Pods.Items[0].Spec = corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "bar",
}},
}
return objs
}
func containerPrivilegedNil() *kube.Objects {
objs := initPod()
objs.Pods.Items[0].Spec = corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "bar",
SecurityContext: &corev1.SecurityContext{},
}},
}
return objs
}
func initContainer(privileged bool) *kube.Objects {
objs := initPod()
objs.Pods.Items[0].Spec = corev1.PodSpec{
InitContainers: []corev1.Container{
{
Name: "bar",
SecurityContext: &corev1.SecurityContext{Privileged: &privileged},
}},
}
return objs
}
func initContainerSecurityContextNil() *kube.Objects {
objs := initPod()
objs.Pods.Items[0].Spec = corev1.PodSpec{
InitContainers: []corev1.Container{
{
Name: "bar",
}},
}
return objs
}
func initContainerPrivilegedNil() *kube.Objects {
objs := initPod()
objs.Pods.Items[0].Spec = corev1.PodSpec{
InitContainers: []corev1.Container{
{
Name: "bar",
SecurityContext: &corev1.SecurityContext{},
}},
}
return objs
}
func warnings() []error {
w := []error{
fmt.Errorf("[Best Practice] Privileged container 'bar' found in pod 'pod_foo', namespace 'k8s'."),
}
return w
}