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

Commit 2061bc7

Browse files
Migrate to Seccomp profile in security Context ⚠️ (#475)
* Initial commit * Fix tests * update docs * cover localhost * Update usages of seccomp * remove test * Update docs * Add warning when annotations are present * upd * Add warning for manifest mode when annotations are present and no seccomp profile * Update auditors/seccomp/seccomp.go Co-authored-by: Genevieve Luyt <11131143+genevieveluyt@users.noreply.github.com> * yml formatting * fix name Co-authored-by: Genevieve Luyt <11131143+genevieveluyt@users.noreply.github.com>
1 parent c875a37 commit 2061bc7

23 files changed

+438
-429
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
[![Go Report Card](https://goreportcard.com/badge/github.com/Shopify/kubeaudit)](https://goreportcard.com/report/github.com/Shopify/kubeaudit)
33
[![GoDoc](https://godoc.org/github.com/Shopify/kubeaudit?status.png)](https://godoc.org/github.com/Shopify/kubeaudit)
44

5-
> 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
5+
> It is now a requirement for clusters to run Kubernetes >=1.19.
66
77

88
# kubeaudit :cloud: :lock: :muscle:

auditors/all/all_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func TestAuditAll(t *testing.T) {
4343
privesc.AllowPrivilegeEscalationNil,
4444
privileged.PrivilegedNil,
4545
rootfs.ReadOnlyRootFilesystemNil,
46-
seccomp.SeccompAnnotationMissing,
46+
seccomp.SeccompProfileMissing,
4747
}
4848

4949
allAuditors, err := Auditors(
@@ -86,7 +86,7 @@ func TestAllWithConfig(t *testing.T) {
8686
}
8787
expectedErrors := []string{
8888
apparmor.AppArmorAnnotationMissing,
89-
seccomp.SeccompAnnotationMissing,
89+
seccomp.SeccompProfileMissing,
9090
}
9191

9292
conf := config.KubeauditConfig{

auditors/apparmor/apparmor.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ func auditPodAnnotations(resource k8s.Resource, containerNames []string) []*kube
117117
"Container": containerName,
118118
"Annotation": fmt.Sprintf("%s: %s", annotationKey, annotationValue),
119119
},
120-
PendingFix: &fix.ByRemovingPodAnnotation{
121-
Key: annotationKey,
120+
PendingFix: &fix.ByRemovingPodAnnotations{
121+
Keys: []string{annotationKey},
122122
},
123123
})
124124
}

auditors/image/fixtures/image-tag-missing.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ spec:
1313
name: deployment
1414
annotations:
1515
container.apparmor.security.beta.kubernetes.io/container: runtime/default
16-
seccomp.security.alpha.kubernetes.io/pod: runtime/default
1716
spec:
17+
securityContext:
18+
seccompProfile:
19+
type: RuntimeDefault
1820
containers:
1921
- name: container
2022
image: scratch

auditors/seccomp/fix.go

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package seccomp
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/Shopify/kubeaudit/pkg/k8s"
7+
apiv1 "k8s.io/api/core/v1"
8+
)
9+
10+
type BySettingSeccompProfile struct {
11+
seccompProfileType apiv1.SeccompProfileType
12+
}
13+
14+
func (pending *BySettingSeccompProfile) Plan() string {
15+
return fmt.Sprintf("Set SeccompProfile type to '%s' in pod SecurityContext", pending.seccompProfileType)
16+
}
17+
18+
func (pending *BySettingSeccompProfile) Apply(resource k8s.Resource) []k8s.Resource {
19+
podSpec := k8s.GetPodSpec(resource)
20+
if podSpec.SecurityContext == nil {
21+
podSpec.SecurityContext = &apiv1.PodSecurityContext{}
22+
}
23+
podSpec.SecurityContext.SeccompProfile = &apiv1.SeccompProfile{Type: pending.seccompProfileType}
24+
25+
return nil
26+
}
27+
28+
type BySettingSeccompProfileInContainer struct {
29+
container *k8s.ContainerV1
30+
seccompProfileType apiv1.SeccompProfileType
31+
}
32+
33+
func (pending *BySettingSeccompProfileInContainer) Plan() string {
34+
return fmt.Sprintf("Set SeccompProfile type to '%s' in SecurityContext for container `%s`", pending.seccompProfileType, pending.container.Name)
35+
}
36+
37+
func (pending *BySettingSeccompProfileInContainer) Apply(resource k8s.Resource) []k8s.Resource {
38+
if pending.container.SecurityContext == nil {
39+
pending.container.SecurityContext = &apiv1.SecurityContext{}
40+
}
41+
pending.container.SecurityContext.SeccompProfile = &apiv1.SeccompProfile{Type: pending.seccompProfileType}
42+
return nil
43+
}
44+
45+
type ByRemovingSeccompProfileInContainer struct {
46+
container *k8s.ContainerV1
47+
}
48+
49+
func (pending *ByRemovingSeccompProfileInContainer) Plan() string {
50+
return fmt.Sprintf("Remove SeccompProfile in SecurityContext for container `%s`", pending.container.Name)
51+
}
52+
53+
func (pending *ByRemovingSeccompProfileInContainer) Apply(resource k8s.Resource) []k8s.Resource {
54+
if pending.container.SecurityContext == nil {
55+
return nil
56+
}
57+
pending.container.SecurityContext.SeccompProfile = nil
58+
return nil
59+
}

auditors/seccomp/fix_test.go

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package seccomp
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/Shopify/kubeaudit/internal/test"
8+
"github.com/Shopify/kubeaudit/pkg/k8s"
9+
"github.com/stretchr/testify/assert"
10+
"github.com/stretchr/testify/require"
11+
apiv1 "k8s.io/api/core/v1"
12+
)
13+
14+
const fixtureDir = "fixtures"
15+
const emptyProfile = apiv1.SeccompProfileType("EMPTY")
16+
const defaultProfile = apiv1.SeccompProfileTypeRuntimeDefault
17+
const localhostProfile = apiv1.SeccompProfileTypeLocalhost
18+
19+
func TestFixSeccomp(t *testing.T) {
20+
cases := []struct {
21+
file string
22+
expectedPodSeccompProfile apiv1.SeccompProfileType
23+
expectedContainerSeccompProfiles []apiv1.SeccompProfileType
24+
}{
25+
{"seccomp-profile-missing.yml", defaultProfile, []apiv1.SeccompProfileType{emptyProfile}},
26+
{"seccomp-profile-missing-disabled-container.yml", defaultProfile, []apiv1.SeccompProfileType{emptyProfile}},
27+
{"seccomp-profile-missing-annotations.yml", defaultProfile, []apiv1.SeccompProfileType{emptyProfile}},
28+
{"seccomp-disabled-pod.yml", defaultProfile, []apiv1.SeccompProfileType{defaultProfile}},
29+
{"seccomp-disabled.yml", defaultProfile, []apiv1.SeccompProfileType{emptyProfile, emptyProfile}},
30+
{"seccomp-disabled-localhost.yml", localhostProfile, []apiv1.SeccompProfileType{defaultProfile, emptyProfile}},
31+
}
32+
33+
for _, tc := range cases {
34+
// This line is needed because of how scopes work with parallel tests (see https://gist.github.com/posener/92a55c4cd441fc5e5e85f27bca008721)
35+
tc := tc
36+
t.Run(tc.file, func(t *testing.T) {
37+
resources, _ := test.FixSetup(t, fixtureDir, tc.file, New())
38+
require.Len(t, resources, 1)
39+
resource := resources[0]
40+
41+
updatedPodSpec := k8s.GetPodSpec(resource)
42+
checkPodSeccompProfile(t, updatedPodSpec, tc.expectedPodSeccompProfile)
43+
checkContainerSeccompProfiles(t, updatedPodSpec, tc.expectedContainerSeccompProfiles)
44+
checkNoSeccompAnnotations(t, resource)
45+
})
46+
}
47+
}
48+
49+
func checkPodSeccompProfile(t *testing.T, podSpec *apiv1.PodSpec, expectedPodSeccompProfile apiv1.SeccompProfileType) {
50+
securityContext := podSpec.SecurityContext
51+
if expectedPodSeccompProfile == emptyProfile {
52+
require.Nil(t, securityContext)
53+
} else {
54+
assert.Equal(t, expectedPodSeccompProfile, securityContext.SeccompProfile.Type)
55+
}
56+
}
57+
58+
func checkContainerSeccompProfiles(t *testing.T, podSpec *apiv1.PodSpec, expectedContainerSeccompProfiles []apiv1.SeccompProfileType) {
59+
for i, container := range podSpec.Containers {
60+
securityContext := container.SecurityContext
61+
expectedProfile := expectedContainerSeccompProfiles[i]
62+
if expectedProfile == emptyProfile {
63+
require.True(t, securityContext == nil || securityContext.SeccompProfile == nil)
64+
} else {
65+
assert.Equal(t, expectedProfile, securityContext.SeccompProfile.Type)
66+
}
67+
}
68+
}
69+
70+
func checkNoSeccompAnnotations(t *testing.T, resource k8s.Resource) {
71+
annotations := k8s.GetAnnotations(resource)
72+
if annotations == nil {
73+
return
74+
}
75+
76+
seccompAnnotations := []string{}
77+
for annotation := range annotations {
78+
if annotation == PodAnnotationKey || strings.HasPrefix(annotation, ContainerAnnotationKeyPrefix) {
79+
seccompAnnotations = append(seccompAnnotations, annotation)
80+
}
81+
}
82+
assert.Empty(t, seccompAnnotations)
83+
}

auditors/seccomp/fixtures/seccomp-deprecated.yml

-11
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: v1
2+
kind: Pod
3+
metadata:
4+
name: pod
5+
namespace: seccomp-disabled-localhost
6+
spec:
7+
securityContext:
8+
seccompProfile:
9+
type: Localhost
10+
localhostProfile: my-seccomp-profile.json
11+
containers:
12+
- name: container1
13+
image: scratch
14+
securityContext:
15+
seccompProfile:
16+
type: Unconfined
17+
- name: container2
18+
image: scratch

auditors/seccomp/fixtures/seccomp-disabled-pod.yml

+6-3
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@ kind: Pod
33
metadata:
44
name: pod
55
namespace: seccomp-disabled-pod
6-
annotations:
7-
seccomp.security.alpha.kubernetes.io/pod: unconfined
8-
container.seccomp.security.alpha.kubernetes.io/container: runtime/default
96
spec:
7+
securityContext:
8+
seccompProfile:
9+
type: Unconfined
1010
containers:
1111
- name: container
1212
image: scratch
13+
securityContext:
14+
seccompProfile:
15+
type: RuntimeDefault

auditors/seccomp/fixtures/seccomp-disabled.yml

+6-6
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ kind: Pod
33
metadata:
44
name: pod
55
namespace: seccomp-disabled
6-
annotations:
7-
seccomp.security.alpha.kubernetes.io/pod: runtime/default
8-
container.seccomp.security.alpha.kubernetes.io/container1: badval
9-
container.seccomp.security.alpha.kubernetes.io/container2: unconfined
106
spec:
7+
securityContext:
8+
seccompProfile:
9+
type: RuntimeDefault
1110
containers:
1211
- name: container1
1312
image: scratch
13+
securityContext:
14+
seccompProfile:
15+
type: Unconfined
1416
- name: container2
1517
image: scratch
16-
- name: container3
17-
image: scratch

auditors/seccomp/fixtures/seccomp-enabled-pod.yml

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ kind: Pod
33
metadata:
44
name: pod
55
namespace: seccomp-enabled-pod
6-
annotations:
7-
seccomp.security.alpha.kubernetes.io/pod: localhost/bla
86
spec:
7+
securityContext:
8+
seccompProfile:
9+
type: Localhost
10+
localhostProfile: my-seccomp-profile.json
911
containers:
1012
- name: container
1113
image: scratch

auditors/seccomp/fixtures/seccomp-enabled.yml

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ kind: Pod
33
metadata:
44
name: pod
55
namespace: seccomp-enabled
6-
annotations:
7-
container.seccomp.security.alpha.kubernetes.io/container: runtime/default
86
spec:
97
containers:
108
- name: container
119
image: scratch
10+
securityContext:
11+
seccompProfile:
12+
type: RuntimeDefault

auditors/seccomp/fixtures/seccomp-deprecated-pod.yml auditors/seccomp/fixtures/seccomp-profile-missing-annotations.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ apiVersion: v1
22
kind: Pod
33
metadata:
44
name: pod
5-
namespace: seccomp-deprecated-pod
5+
namespace: seccomp-profile-missing-annotations
66
annotations:
7-
seccomp.security.alpha.kubernetes.io/pod: docker/default
7+
seccomp.security.alpha.kubernetes.io/pod: runtime/default
88
container.seccomp.security.alpha.kubernetes.io/container: localhost/bla
99
spec:
1010
containers:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: v1
2+
kind: Pod
3+
metadata:
4+
name: pod
5+
namespace: seccomp-profile-missing-disabled-container
6+
spec:
7+
containers:
8+
- name: container
9+
image: scratch
10+
securityContext:
11+
seccompProfile:
12+
type: Unconfined

auditors/seccomp/fixtures/seccomp-annotation-missing.yml auditors/seccomp/fixtures/seccomp-profile-missing.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ apiVersion: v1
22
kind: Pod
33
metadata:
44
name: pod
5-
namespace: seccomp-annotation-missing
5+
namespace: seccomp-profile-missing
66
spec:
77
containers:
88
- name: container

0 commit comments

Comments
 (0)