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

Migrate to Seccomp profile in security Context ⚠️ #475

Merged
merged 13 commits into from
Oct 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
[![Go Report Card](https://goreportcard.com/badge/github.com/Shopify/kubeaudit)](https://goreportcard.com/report/github.com/Shopify/kubeaudit)
[![GoDoc](https://godoc.org/github.com/Shopify/kubeaudit?status.png)](https://godoc.org/github.com/Shopify/kubeaudit)

> Kubeaudit no longer supports APIs deprecated as of [Kubernetes v.1.16 release](https://kubernetes.io/blog/2019/07/18/api-deprecations-in-1-16/). So, it is now a requirement for clusters to run Kubernetes >=1.16
> It is now a requirement for clusters to run Kubernetes >=1.19.


# kubeaudit :cloud: :lock: :muscle:
Expand Down
4 changes: 2 additions & 2 deletions auditors/all/all_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func TestAuditAll(t *testing.T) {
privesc.AllowPrivilegeEscalationNil,
privileged.PrivilegedNil,
rootfs.ReadOnlyRootFilesystemNil,
seccomp.SeccompAnnotationMissing,
seccomp.SeccompProfileMissing,
}

allAuditors, err := Auditors(
Expand Down Expand Up @@ -86,7 +86,7 @@ func TestAllWithConfig(t *testing.T) {
}
expectedErrors := []string{
apparmor.AppArmorAnnotationMissing,
seccomp.SeccompAnnotationMissing,
seccomp.SeccompProfileMissing,
}

conf := config.KubeauditConfig{
Expand Down
4 changes: 2 additions & 2 deletions auditors/apparmor/apparmor.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ func auditPodAnnotations(resource k8s.Resource, containerNames []string) []*kube
"Container": containerName,
"Annotation": fmt.Sprintf("%s: %s", annotationKey, annotationValue),
},
PendingFix: &fix.ByRemovingPodAnnotation{
Key: annotationKey,
PendingFix: &fix.ByRemovingPodAnnotations{
Keys: []string{annotationKey},
},
})
}
Expand Down
4 changes: 3 additions & 1 deletion auditors/image/fixtures/image-tag-missing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ spec:
name: deployment
annotations:
container.apparmor.security.beta.kubernetes.io/container: runtime/default
seccomp.security.alpha.kubernetes.io/pod: runtime/default
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: container
image: scratch
59 changes: 59 additions & 0 deletions auditors/seccomp/fix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package seccomp

import (
"fmt"

"github.com/Shopify/kubeaudit/pkg/k8s"
apiv1 "k8s.io/api/core/v1"
)

type BySettingSeccompProfile struct {
seccompProfileType apiv1.SeccompProfileType
}

func (pending *BySettingSeccompProfile) Plan() string {
return fmt.Sprintf("Set SeccompProfile type to '%s' in pod SecurityContext", pending.seccompProfileType)
}

func (pending *BySettingSeccompProfile) Apply(resource k8s.Resource) []k8s.Resource {
podSpec := k8s.GetPodSpec(resource)
if podSpec.SecurityContext == nil {
podSpec.SecurityContext = &apiv1.PodSecurityContext{}
}
podSpec.SecurityContext.SeccompProfile = &apiv1.SeccompProfile{Type: pending.seccompProfileType}

return nil
}

type BySettingSeccompProfileInContainer struct {
container *k8s.ContainerV1
seccompProfileType apiv1.SeccompProfileType
}

func (pending *BySettingSeccompProfileInContainer) Plan() string {
return fmt.Sprintf("Set SeccompProfile type to '%s' in SecurityContext for container `%s`", pending.seccompProfileType, pending.container.Name)
}

func (pending *BySettingSeccompProfileInContainer) Apply(resource k8s.Resource) []k8s.Resource {
if pending.container.SecurityContext == nil {
pending.container.SecurityContext = &apiv1.SecurityContext{}
}
pending.container.SecurityContext.SeccompProfile = &apiv1.SeccompProfile{Type: pending.seccompProfileType}
return nil
}

type ByRemovingSeccompProfileInContainer struct {
container *k8s.ContainerV1
}

func (pending *ByRemovingSeccompProfileInContainer) Plan() string {
return fmt.Sprintf("Remove SeccompProfile in SecurityContext for container `%s`", pending.container.Name)
}

func (pending *ByRemovingSeccompProfileInContainer) Apply(resource k8s.Resource) []k8s.Resource {
if pending.container.SecurityContext == nil {
return nil
}
pending.container.SecurityContext.SeccompProfile = nil
return nil
}
83 changes: 83 additions & 0 deletions auditors/seccomp/fix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package seccomp

import (
"strings"
"testing"

"github.com/Shopify/kubeaudit/internal/test"
"github.com/Shopify/kubeaudit/pkg/k8s"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
apiv1 "k8s.io/api/core/v1"
)

const fixtureDir = "fixtures"
const emptyProfile = apiv1.SeccompProfileType("EMPTY")
const defaultProfile = apiv1.SeccompProfileTypeRuntimeDefault
const localhostProfile = apiv1.SeccompProfileTypeLocalhost

func TestFixSeccomp(t *testing.T) {
cases := []struct {
file string
expectedPodSeccompProfile apiv1.SeccompProfileType
expectedContainerSeccompProfiles []apiv1.SeccompProfileType
}{
{"seccomp-profile-missing.yml", defaultProfile, []apiv1.SeccompProfileType{emptyProfile}},
{"seccomp-profile-missing-disabled-container.yml", defaultProfile, []apiv1.SeccompProfileType{emptyProfile}},
{"seccomp-profile-missing-annotations.yml", defaultProfile, []apiv1.SeccompProfileType{emptyProfile}},
{"seccomp-disabled-pod.yml", defaultProfile, []apiv1.SeccompProfileType{defaultProfile}},
{"seccomp-disabled.yml", defaultProfile, []apiv1.SeccompProfileType{emptyProfile, emptyProfile}},
{"seccomp-disabled-localhost.yml", localhostProfile, []apiv1.SeccompProfileType{defaultProfile, emptyProfile}},
}

for _, tc := range cases {
// This line is needed because of how scopes work with parallel tests (see https://gist.github.com/posener/92a55c4cd441fc5e5e85f27bca008721)
tc := tc
t.Run(tc.file, func(t *testing.T) {
resources, _ := test.FixSetup(t, fixtureDir, tc.file, New())
require.Len(t, resources, 1)
resource := resources[0]

updatedPodSpec := k8s.GetPodSpec(resource)
checkPodSeccompProfile(t, updatedPodSpec, tc.expectedPodSeccompProfile)
checkContainerSeccompProfiles(t, updatedPodSpec, tc.expectedContainerSeccompProfiles)
checkNoSeccompAnnotations(t, resource)
})
}
}

func checkPodSeccompProfile(t *testing.T, podSpec *apiv1.PodSpec, expectedPodSeccompProfile apiv1.SeccompProfileType) {
securityContext := podSpec.SecurityContext
if expectedPodSeccompProfile == emptyProfile {
require.Nil(t, securityContext)
} else {
assert.Equal(t, expectedPodSeccompProfile, securityContext.SeccompProfile.Type)
}
}

func checkContainerSeccompProfiles(t *testing.T, podSpec *apiv1.PodSpec, expectedContainerSeccompProfiles []apiv1.SeccompProfileType) {
for i, container := range podSpec.Containers {
securityContext := container.SecurityContext
expectedProfile := expectedContainerSeccompProfiles[i]
if expectedProfile == emptyProfile {
require.True(t, securityContext == nil || securityContext.SeccompProfile == nil)
} else {
assert.Equal(t, expectedProfile, securityContext.SeccompProfile.Type)
}
}
}

func checkNoSeccompAnnotations(t *testing.T, resource k8s.Resource) {
annotations := k8s.GetAnnotations(resource)
if annotations == nil {
return
}

seccompAnnotations := []string{}
for annotation := range annotations {
if annotation == PodAnnotationKey || strings.HasPrefix(annotation, ContainerAnnotationKeyPrefix) {
seccompAnnotations = append(seccompAnnotations, annotation)
}
}
assert.Empty(t, seccompAnnotations)
}
11 changes: 0 additions & 11 deletions auditors/seccomp/fixtures/seccomp-deprecated.yml

This file was deleted.

18 changes: 18 additions & 0 deletions auditors/seccomp/fixtures/seccomp-disabled-localhost.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: v1
kind: Pod
metadata:
name: pod
namespace: seccomp-disabled-localhost
spec:
securityContext:
seccompProfile:
type: Localhost
localhostProfile: my-seccomp-profile.json
containers:
- name: container1
image: scratch
securityContext:
seccompProfile:
type: Unconfined
- name: container2
image: scratch
9 changes: 6 additions & 3 deletions auditors/seccomp/fixtures/seccomp-disabled-pod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ kind: Pod
metadata:
name: pod
namespace: seccomp-disabled-pod
annotations:
seccomp.security.alpha.kubernetes.io/pod: unconfined
container.seccomp.security.alpha.kubernetes.io/container: runtime/default
spec:
securityContext:
seccompProfile:
type: Unconfined
containers:
- name: container
image: scratch
securityContext:
seccompProfile:
type: RuntimeDefault
12 changes: 6 additions & 6 deletions auditors/seccomp/fixtures/seccomp-disabled.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ kind: Pod
metadata:
name: pod
namespace: seccomp-disabled
annotations:
seccomp.security.alpha.kubernetes.io/pod: runtime/default
container.seccomp.security.alpha.kubernetes.io/container1: badval
container.seccomp.security.alpha.kubernetes.io/container2: unconfined
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: container1
image: scratch
securityContext:
seccompProfile:
type: Unconfined
- name: container2
image: scratch
- name: container3
image: scratch
6 changes: 4 additions & 2 deletions auditors/seccomp/fixtures/seccomp-enabled-pod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ kind: Pod
metadata:
name: pod
namespace: seccomp-enabled-pod
annotations:
seccomp.security.alpha.kubernetes.io/pod: localhost/bla
spec:
securityContext:
seccompProfile:
type: Localhost
localhostProfile: my-seccomp-profile.json
containers:
- name: container
image: scratch
5 changes: 3 additions & 2 deletions auditors/seccomp/fixtures/seccomp-enabled.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ kind: Pod
metadata:
name: pod
namespace: seccomp-enabled
annotations:
container.seccomp.security.alpha.kubernetes.io/container: runtime/default
spec:
containers:
- name: container
image: scratch
securityContext:
seccompProfile:
type: RuntimeDefault
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ apiVersion: v1
kind: Pod
metadata:
name: pod
namespace: seccomp-deprecated-pod
namespace: seccomp-profile-missing-annotations
annotations:
seccomp.security.alpha.kubernetes.io/pod: docker/default
seccomp.security.alpha.kubernetes.io/pod: runtime/default
container.seccomp.security.alpha.kubernetes.io/container: localhost/bla
spec:
containers:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v1
kind: Pod
metadata:
name: pod
namespace: seccomp-profile-missing-disabled-container
spec:
containers:
- name: container
image: scratch
securityContext:
seccompProfile:
type: Unconfined
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ apiVersion: v1
kind: Pod
metadata:
name: pod
namespace: seccomp-annotation-missing
namespace: seccomp-profile-missing
spec:
containers:
- name: container
Expand Down
Loading