Merge pull request #14 from digitalocean/varsha/privileged-containers
Privileged container check: Add warning if a privileged container container is found.varsha/versions
commit
049292bd67
|
@ -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"
|
||||
)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue