diff --git a/Gopkg.lock b/Gopkg.lock index 927cb750..df90a8f2 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -61,6 +61,12 @@ packages = ["."] revision = "f3f9494671f93fcff853e3c6e9e948b3eb71e590" +[[projects]] + name = "github.com/go-test/deep" + packages = ["."] + revision = "9898238679c264cfb10411539f14a0553dc8b295" + version = "v1.0.0" + [[projects]] name = "github.com/gogo/protobuf" packages = ["proto","sortkeys"] @@ -262,6 +268,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "373e69515448e7aff2725117b3ca38bf005c4d156b5bc5de82368c7d8e00ced7" + inputs-digest = "5f16b7e1739c7ca9eb4037e66b484b3e70df0e04e7b27b0277365e7b6cb28476" solver-name = "gps-cdcl" solver-version = 1 diff --git a/cmd/allowPrivilegeEscalation.go b/cmd/allowPrivilegeEscalation.go index 36c9d9dd..cb583073 100644 --- a/cmd/allowPrivilegeEscalation.go +++ b/cmd/allowPrivilegeEscalation.go @@ -6,17 +6,8 @@ import ( ) func checkAllowPrivilegeEscalation(container Container, result *Result) { - if container.SecurityContext == nil { - occ := Occurrence{ - id: ErrorSecurityContextNIL, - kind: Error, - message: "SecurityContext not set, please set it!", - } - result.Occurrences = append(result.Occurrences, occ) - return - } if reason := result.Labels["kubeaudit.allow.privilegeEscalation"]; reason == "" { - if container.SecurityContext.AllowPrivilegeEscalation == nil { + if container.SecurityContext == nil || container.SecurityContext.AllowPrivilegeEscalation == nil { occ := Occurrence{ id: ErrorAllowPrivilegeEscalationNIL, kind: Error, @@ -31,7 +22,7 @@ func checkAllowPrivilegeEscalation(container Container, result *Result) { } result.Occurrences = append(result.Occurrences, occ) } - } else if container.SecurityContext.AllowPrivilegeEscalation == nil || *container.SecurityContext.AllowPrivilegeEscalation == true { + } else if container.SecurityContext == nil || container.SecurityContext.AllowPrivilegeEscalation == nil || *container.SecurityContext.AllowPrivilegeEscalation == true { occ := Occurrence{ id: ErrorAllowPrivilegeEscalationTrueAllowed, kind: Warn, diff --git a/cmd/allowPrivilegeEscalation_fixes.go b/cmd/allowPrivilegeEscalation_fixes.go new file mode 100644 index 00000000..43ec6926 --- /dev/null +++ b/cmd/allowPrivilegeEscalation_fixes.go @@ -0,0 +1,12 @@ +package cmd + +import k8sRuntime "k8s.io/apimachinery/pkg/runtime" + +func fixAllowPrivilegeEscalation(resource k8sRuntime.Object) k8sRuntime.Object { + var containers []Container + for _, container := range getContainers(resource) { + container.SecurityContext.AllowPrivilegeEscalation = newFalse() + containers = append(containers, container) + } + return setContainers(resource, containers) +} diff --git a/cmd/allowPrivilegeEscalation_fixes_test.go b/cmd/allowPrivilegeEscalation_fixes_test.go new file mode 100644 index 00000000..05a24d19 --- /dev/null +++ b/cmd/allowPrivilegeEscalation_fixes_test.go @@ -0,0 +1,31 @@ +package cmd + +import "testing" + +func TestFixAllowPrivilegeEscalation(t *testing.T) { + assert, resource := FixTestSetup(t, "allow_privilege_escalation_nil.yml", auditAllowPrivilegeEscalation) + for _, container := range getContainers(resource) { + assert.False(*container.SecurityContext.AllowPrivilegeEscalation) + } +} + +func TestFixAllowPrivilegeEscalationTrue(t *testing.T) { + assert, resource := FixTestSetup(t, "allow_privilege_escalation_true.yml", auditAllowPrivilegeEscalation) + for _, container := range getContainers(resource) { + assert.False(*container.SecurityContext.AllowPrivilegeEscalation) + } +} + +func TestFixAllowPrivilegeEscalationTrueAllowed(t *testing.T) { + assert, resource := FixTestSetup(t, "allow_privilege_escalation_true_allowed.yml", auditAllowPrivilegeEscalation) + for _, container := range getContainers(resource) { + assert.True(*container.SecurityContext.AllowPrivilegeEscalation) + } +} + +func TestFixAllowPrivilegeEscalationMisconfiguredAllow(t *testing.T) { + assert, resource := FixTestSetup(t, "allow_privilege_escalation_misconfigured_allow.yml", auditAllowPrivilegeEscalation) + for _, container := range getContainers(resource) { + assert.False(*container.SecurityContext.AllowPrivilegeEscalation) + } +} diff --git a/cmd/allowPrivilegeEscalation_test.go b/cmd/allowPrivilegeEscalation_test.go index 6bd55713..d010679b 100644 --- a/cmd/allowPrivilegeEscalation_test.go +++ b/cmd/allowPrivilegeEscalation_test.go @@ -5,7 +5,7 @@ import ( ) func TestSecurityContextNIL_APE(t *testing.T) { - runAuditTest(t, "security_context_nil.yml", auditAllowPrivilegeEscalation, []int{ErrorSecurityContextNIL}) + runAuditTest(t, "security_context_nil.yml", auditAllowPrivilegeEscalation, []int{ErrorAllowPrivilegeEscalationNIL}) } func TestAllowPrivilegeEscalationNil(t *testing.T) { diff --git a/cmd/autofix.go b/cmd/autofix.go new file mode 100644 index 00000000..2fc8f0c4 --- /dev/null +++ b/cmd/autofix.go @@ -0,0 +1,86 @@ +package cmd + +import ( + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + k8sRuntime "k8s.io/apimachinery/pkg/runtime" +) + +func getAuditFunctions() []interface{} { + return []interface{}{ + auditAllowPrivilegeEscalation, auditReadOnlyRootFS, auditRunAsNonRoot, + auditAutomountServiceAccountToken, auditPrivileged, auditCapabilities, + } +} + +func runAllAudits(resource k8sRuntime.Object) (results []Result) { + for _, function := range getAuditFunctions() { + for _, result := range getResults([]k8sRuntime.Object{resource}, function) { + results = append(results, result) + } + } + return results +} + +func fixPotentialSecurityIssue(resource k8sRuntime.Object, result Result) k8sRuntime.Object { + resource = fixSecurityContextNIL(resource) + resource = fixCapabilitiesNIL(resource) + for _, occurrence := range result.Occurrences { + switch occurrence.id { + case ErrorAllowPrivilegeEscalationNIL, ErrorAllowPrivilegeEscalationTrue: + resource = fixAllowPrivilegeEscalation(resource) + case ErrorCapabilityNotDropped: + resource = fixCapabilityNotDropped(resource, occurrence) + case ErrorCapabilityAdded: + resource = fixCapabilityAdded(resource, occurrence) + case ErrorPrivilegedNIL, ErrorPrivilegedTrue: + resource = fixPrivileged(resource) + case ErrorReadOnlyRootFilesystemFalse, ErrorReadOnlyRootFilesystemNIL: + resource = fixReadOnlyRootFilesystem(resource) + case ErrorRunAsNonRootFalse, ErrorRunAsNonRootNIL: + resource = fixRunAsNonRoot(resource) + case ErrorServiceAccountTokenDeprecated: + resource = fixDeprecatedServiceAccount(resource) + case ErrorAutomountServiceAccountTokenTrueAndNoName, ErrorAutomountServiceAccountTokenNILAndNoName: + resource = fixServiceAccountToken(resource) + } + } + return resource +} + +func fix(resources []k8sRuntime.Object) (fixedResources []k8sRuntime.Object) { + for _, resource := range resources { + results := runAllAudits(resource) + for _, result := range results { + resource = fixPotentialSecurityIssue(resource, result) + } + fixedResources = append(fixedResources, resource) + } + return +} + +func autofix(*cobra.Command, []string) { + resources, err := getKubeResourcesManifest(rootConfig.manifest) + if err != nil { + log.Error(err) + } + fixedResources := fix(resources) + err = writeManifestFile(fixedResources, rootConfig.manifest) + if err != nil { + return + } +} + +var autofixCmd = &cobra.Command{ + Use: "autofix", + Short: "Automagically fixes a manifest to be secure", + Long: `"autofix" will examine a manifest file and automagically fill in the blanks to leave your yaml file more secure than it found it + +Example usage: +kubeaudit autofix -f /path/to/yaml`, + Run: autofix, +} + +func init() { + RootCmd.AddCommand(autofixCmd) +} diff --git a/cmd/autofix_test.go b/cmd/autofix_test.go new file mode 100644 index 00000000..fa59400e --- /dev/null +++ b/cmd/autofix_test.go @@ -0,0 +1,21 @@ +package cmd + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/go-test/deep" +) + +func TestFix(t *testing.T) { + file := "../fixtures/autofix.yml" + fileFixed := "../fixtures/autofix-fixed.yml" + assert := assert.New(t) + resources, err := getKubeResourcesManifest(file) + assert.Nil(err) + fixedResources := fix(resources) + correctlyFixedResources, err := getKubeResourcesManifest(fileFixed) + assert.Nil(err) + assert.Nil(deep.Equal(correctlyFixedResources, fixedResources)) +} diff --git a/cmd/automountServiceAccountToken_fixes.go b/cmd/automountServiceAccountToken_fixes.go new file mode 100644 index 00000000..ad8a7db4 --- /dev/null +++ b/cmd/automountServiceAccountToken_fixes.go @@ -0,0 +1,11 @@ +package cmd + +import k8sRuntime "k8s.io/apimachinery/pkg/runtime" + +func fixServiceAccountToken(resource k8sRuntime.Object) k8sRuntime.Object { + return setASAT(resource, false) +} + +func fixDeprecatedServiceAccount(resource k8sRuntime.Object) k8sRuntime.Object { + return disableDSA(resource) +} diff --git a/cmd/automountServiceAccountToken_fixes_test.go b/cmd/automountServiceAccountToken_fixes_test.go new file mode 100644 index 00000000..fbc8456e --- /dev/null +++ b/cmd/automountServiceAccountToken_fixes_test.go @@ -0,0 +1,43 @@ +package cmd + +import "testing" + +func TestFixServiceAccountTokenDeprecated(t *testing.T) { + assert, resource := FixTestSetup(t, "service_account_token_deprecated.yml", auditAutomountServiceAccountToken) + switch typ := resource.(type) { + case *ReplicationController: + assert.Equal("", typ.Spec.Template.Spec.DeprecatedServiceAccount) + } +} + +func TestFixServiceAccountTokenTrueAndNoName(t *testing.T) { + assert, resource := FixTestSetup(t, "service_account_token_true_and_no_name.yml", auditAutomountServiceAccountToken) + switch typ := resource.(type) { + case *ReplicationController: + assert.False(*typ.Spec.Template.Spec.AutomountServiceAccountToken) + } +} + +func TestFixServiceAccountTokenNILAndNoName(t *testing.T) { + assert, resource := FixTestSetup(t, "service_account_token_nil_and_no_name.yml", auditAutomountServiceAccountToken) + switch typ := resource.(type) { + case *ReplicationController: + assert.False(*typ.Spec.Template.Spec.AutomountServiceAccountToken) + } +} + +func TestFixServiceAccountTokenTrueAllowed(t *testing.T) { + assert, resource := FixTestSetup(t, "service_account_token_true_allowed.yml", auditAutomountServiceAccountToken) + switch typ := resource.(type) { + case *ReplicationController: + assert.True(*typ.Spec.Template.Spec.AutomountServiceAccountToken) + } +} + +func TestFixServiceAccountTokenMisconfiguredAllow(t *testing.T) { + assert, resource := FixTestSetup(t, "service_account_token_misconfigured_allow.yml", auditAutomountServiceAccountToken) + switch typ := resource.(type) { + case *ReplicationController: + assert.False(*typ.Spec.Template.Spec.AutomountServiceAccountToken) + } +} diff --git a/cmd/cap_set.go b/cmd/cap_set.go new file mode 100644 index 00000000..d1fe7ae3 --- /dev/null +++ b/cmd/cap_set.go @@ -0,0 +1,36 @@ +package cmd + +import "sort" + +type CapSet map[Capability]bool + +func NewCapSetFromArray(array []Capability) (set CapSet) { + set = make(CapSet) + for _, cap := range array { + set[cap] = true + } + return +} + +func mergeCapSets(sets ...CapSet) (merged CapSet) { + merged = make(CapSet) + for _, set := range sets { + for k, v := range set { + merged[k] = v + } + } + return +} + +func sortCapSet(capSet CapSet) (sorted []Capability) { + keys := []string{} + for key := range capSet { + keys = append(keys, string(key)) + } + sort.Strings(keys) + + for _, key := range keys { + sorted = append(sorted, Capability(key)) + } + return +} diff --git a/cmd/cap_set_test.go b/cmd/cap_set_test.go new file mode 100644 index 00000000..6ef0e570 --- /dev/null +++ b/cmd/cap_set_test.go @@ -0,0 +1,28 @@ +package cmd + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewCapSetFromArray(t *testing.T) { + assert := assert.New(t) + capArray := []Capability{"AUDIT_WRITE", "CHOWN"} + capSet := CapSet{"AUDIT_WRITE": true, "CHOWN": true} + assert.Equal(NewCapSetFromArray(capArray), capSet) +} + +func TestMergeCapSets(t *testing.T) { + assert := assert.New(t) + set1 := CapSet{"AUDIT_WRITE": true, "CHOWN": true} + set2 := CapSet{"CHOWN": true, "DAC_OVERRIDE": true} + set3 := CapSet{"AUDIT_WRITE": true, "CHOWN": true, "DAC_OVERRIDE": true} + assert.Equal(mergeCapSets(set1, set2), set3) +} + +func TestSortCapSet(t *testing.T) { + assert := assert.New(t) + sorted := sortCapSet(CapSet{"DAC_OVVERRIDE": true, "AUDIT_WRITE": true, "CHOWN": true}) + assert.Equal([]Capability{"AUDIT_WRITE", "CHOWN", "DAC_OVVERRIDE"}, sorted) +} diff --git a/cmd/capabilities.go b/cmd/capabilities.go index c78a5b9d..d2d4bd35 100644 --- a/cmd/capabilities.go +++ b/cmd/capabilities.go @@ -2,7 +2,6 @@ package cmd import ( "io/ioutil" - "strings" "github.com/spf13/cobra" "gopkg.in/yaml.v2" @@ -13,8 +12,6 @@ type capsDropList struct { Drop []string `yaml:"capabilitiesToBeDropped"` } -type CapSet map[Capability]bool - func recommendedCapabilitiesToBeDropped() (dropCapSet CapSet, err error) { yamlFile, err := ioutil.ReadFile("config/capabilities-drop-list.yml") if err != nil { @@ -32,62 +29,20 @@ func recommendedCapabilitiesToBeDropped() (dropCapSet CapSet, err error) { return } -func allowedCaps(result *Result) (allowed map[Capability]string) { - allowed = make(map[Capability]string) - for k, v := range result.Labels { - if strings.Contains(k, "kubeaudit.allow.capability.") { - allowed[Capability(strings.ToUpper(strings.TrimPrefix(k, "kubeaudit.allow.capability.")))] = v - } - } - return -} - -func arrayToCapSet(array []Capability) (set CapSet) { - set = make(CapSet) - for _, cap := range array { - set[cap] = true - } - return -} - -func mergeCapSets(sets ...CapSet) (merged CapSet) { - merged = make(CapSet) - for _, set := range sets { - for k, v := range set { - merged[k] = v - } - } - return -} - func checkCapabilities(container Container, result *Result) { - if container.SecurityContext == nil { - occ := Occurrence{ - id: ErrorSecurityContextNIL, - kind: Error, - message: "SecurityContext not set, please set it!", - } - result.Occurrences = append(result.Occurrences, occ) - return - } - - if container.SecurityContext.Capabilities == nil { - occ := Occurrence{ - id: ErrorCapabilitiesNIL, - kind: Error, - message: "Capabilities field not defined!", - } - result.Occurrences = append(result.Occurrences, occ) - return + added := CapSet{} + dropped := CapSet{} + if container.SecurityContext != nil && container.SecurityContext.Capabilities != nil { + added = NewCapSetFromArray(container.SecurityContext.Capabilities.Add) + dropped = NewCapSetFromArray(container.SecurityContext.Capabilities.Drop) } - added := arrayToCapSet(container.SecurityContext.Capabilities.Add) - dropped := arrayToCapSet(container.SecurityContext.Capabilities.Drop) - allowedMap := allowedCaps(result) + allowedMap := result.allowedCaps() allowed := make(CapSet) for k := range allowedMap { allowed[k] = true } + toBeDropped, err := recommendedCapabilitiesToBeDropped() if err != nil { occ := Occurrence{ @@ -99,7 +54,7 @@ func checkCapabilities(container Container, result *Result) { return } - for cap := range mergeCapSets(toBeDropped, dropped, allowed, added) { + for _, cap := range sortCapSet(mergeCapSets(toBeDropped, dropped, allowed, added)) { if !allowed[cap] && !dropped[cap] && toBeDropped[cap] { occ := Occurrence{ id: ErrorCapabilityNotDropped, diff --git a/cmd/capabilities_fixes.go b/cmd/capabilities_fixes.go new file mode 100644 index 00000000..5b4b9c2a --- /dev/null +++ b/cmd/capabilities_fixes.go @@ -0,0 +1,44 @@ +package cmd + +import k8sRuntime "k8s.io/apimachinery/pkg/runtime" + +func fixCapabilitiesNIL(resource k8sRuntime.Object) k8sRuntime.Object { + var containers []Container + for _, container := range getContainers(resource) { + if container.SecurityContext.Capabilities == nil { + container.SecurityContext.Capabilities = &Capabilities{} + } + if container.SecurityContext.Capabilities.Drop == nil { + container.SecurityContext.Capabilities.Drop = []Capability{} + } + if container.SecurityContext.Capabilities.Add == nil { + container.SecurityContext.Capabilities.Add = []Capability{} + } + containers = append(containers, container) + } + return setContainers(resource, containers) +} + +func fixCapabilityNotDropped(resource k8sRuntime.Object, occurrence Occurrence) k8sRuntime.Object { + var containers []Container + for _, container := range getContainers(resource) { + container.SecurityContext.Capabilities.Drop = append(container.SecurityContext.Capabilities.Drop, Capability(occurrence.metadata["CapName"])) + containers = append(containers, container) + } + return setContainers(resource, containers) +} + +func fixCapabilityAdded(resource k8sRuntime.Object, occurrence Occurrence) k8sRuntime.Object { + var containers []Container + for _, container := range getContainers(resource) { + add := []Capability{} + for _, cap := range container.SecurityContext.Capabilities.Add { + if string(cap) != occurrence.metadata["CapName"] { + add = append(add, cap) + } + } + container.SecurityContext.Capabilities.Add = add + containers = append(containers, container) + } + return setContainers(resource, containers) +} diff --git a/cmd/capabilities_fixes_test.go b/cmd/capabilities_fixes_test.go new file mode 100644 index 00000000..ab8e2f21 --- /dev/null +++ b/cmd/capabilities_fixes_test.go @@ -0,0 +1,84 @@ +package cmd + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" +) + +func assertAllDropped(assert *assert.Assertions, dropped []Capability, allowed ...[]Capability) { + to_be_dropped := []Capability{"AUDIT_WRITE", "CHOWN", "DAC_OVERRIDE", "FOWNER", "FSETID", "KILL", "MKNOD", "NET_BIND_SERVICE", "NET_RAW", "SETFCAP", "SETGID", "SETUID", "SETPCAP", "SYS_CHROOT"} + for _, cap := range to_be_dropped { + skip := false + if allowed != nil { + assert.Equal(1, len(allowed)) + for _, allowed_cap := range allowed[0] { + if allowed_cap == cap { + skip = true + } + } + } + if skip { + continue + } + assert.Contains(dropped, cap) + } +} + +func TestFixCapabilitiesNotDropped(t *testing.T) { + assert, resource := FixTestSetup(t, "capabilities_nil.yml", auditCapabilities) + add := []Capability{} + for _, container := range getContainers(resource) { + assert.Equal(add, container.SecurityContext.Capabilities.Add) + assertAllDropped(assert, container.SecurityContext.Capabilities.Drop) + } +} + +func TestFixCapabilitySomeAllowed(t *testing.T) { + assert, resource := FixTestSetup(t, "capabilities_some_allowed.yml", auditCapabilities) + add := []Capability{"SYS_TIME"} + for _, container := range getContainers(resource) { + assert.Equal(add, container.SecurityContext.Capabilities.Add) + assertAllDropped(assert, container.SecurityContext.Capabilities.Drop, []Capability{"CHOWN"}) + } +} + +func TestFixCapabilitiesNIL(t *testing.T) { + assert, resource := FixTestSetup(t, "capabilities_nil.yml", auditCapabilities) + add := []Capability{} + for _, container := range getContainers(resource) { + assert.Equal(add, container.SecurityContext.Capabilities.Add) + assertAllDropped(assert, container.SecurityContext.Capabilities.Drop) + } +} + +func TestFixCapabilitiesAdded(t *testing.T) { + assert, resource := FixTestSetup(t, "capabilities_added.yml", auditCapabilities) + add := []Capability{} + for _, container := range getContainers(resource) { + assert.Equal(add, container.SecurityContext.Capabilities.Add) + assertAllDropped(assert, container.SecurityContext.Capabilities.Drop) + } +} + +func TestFixCapabilitiesSomeDropped(t *testing.T) { + assert, resource := FixTestSetup(t, "capabilities_some_dropped.yml", auditCapabilities) + add := []Capability{} + for _, container := range getContainers(resource) { + assert.Equal(add, container.SecurityContext.Capabilities.Add) + assertAllDropped(assert, container.SecurityContext.Capabilities.Drop) + } +} + +func TestFixCapabilitiesMisconfiguredAllow(t *testing.T) { + assert, resource := FixTestSetup(t, "capabilities_misconfigured_allow.yml", auditCapabilities) + add := []Capability{} + for _, container := range getContainers(resource) { + if container.SecurityContext.Capabilities.Add == nil { + fmt.Println("it is nil!") + } + assert.Equal(add, container.SecurityContext.Capabilities.Add) + assertAllDropped(assert, container.SecurityContext.Capabilities.Drop) + } +} diff --git a/cmd/capabilities_test.go b/cmd/capabilities_test.go index 75020fa6..7c3a584c 100644 --- a/cmd/capabilities_test.go +++ b/cmd/capabilities_test.go @@ -10,15 +10,15 @@ func TestRecommendedCapabilitiesToBeDropped(t *testing.T) { assert := assert.New(t) capabilities, err := recommendedCapabilitiesToBeDropped() assert.Nil(err) - assert.Equal(arrayToCapSet([]Capability{"AUDIT_WRITE", "CHOWN", "DAC_OVERRIDE", "FOWNER", "FSETID", "KILL", "MKNOD", "NET_BIND_SERVICE", "NET_RAW", "SETFCAP", "SETGID", "SETUID", "SETPCAP", "SYS_CHROOT"}), capabilities, "") + assert.Equal(NewCapSetFromArray([]Capability{"AUDIT_WRITE", "CHOWN", "DAC_OVERRIDE", "FOWNER", "FSETID", "KILL", "MKNOD", "NET_BIND_SERVICE", "NET_RAW", "SETFCAP", "SETGID", "SETUID", "SETPCAP", "SYS_CHROOT"}), capabilities, "") } func TestSecurityContextNIL_SC(t *testing.T) { - runAuditTest(t, "security_context_nil.yml", auditCapabilities, []int{ErrorSecurityContextNIL}) + runAuditTest(t, "security_context_nil.yml", auditCapabilities, []int{ErrorCapabilityNotDropped}) } func TestCapabilitiesNIL(t *testing.T) { - runAuditTest(t, "capabilities_nil.yml", auditCapabilities, []int{ErrorCapabilitiesNIL}) + runAuditTest(t, "capabilities_nil.yml", auditCapabilities, []int{ErrorCapabilityNotDropped}) } func TestCapabilitiesAdded(t *testing.T) { @@ -26,7 +26,7 @@ func TestCapabilitiesAdded(t *testing.T) { } func TestCapabilitiesSomeAllowed(t *testing.T) { - runAuditTest(t, "capabilities_some_allowed.yml", auditCapabilities, []int{ErrorCapabilityAllowed}) + runAuditTest(t, "capabilities_some_allowed.yml", auditCapabilities, []int{ErrorCapabilityAllowed, ErrorCapabilityAllowed}) } func TestCapabilitiesSomeDropped(t *testing.T) { diff --git a/cmd/errors.go b/cmd/errors.go index 0fcdd50b..b03913eb 100644 --- a/cmd/errors.go +++ b/cmd/errors.go @@ -4,35 +4,33 @@ const ( _ = iota KubeauditInternalError ErrorAllowPrivilegeEscalationNIL - ErrorAllowPrivilegeEscalationTrueAllowed ErrorAllowPrivilegeEscalationTrue + ErrorAllowPrivilegeEscalationTrueAllowed + ErrorAutomountServiceAccountTokenNILAndNoName + ErrorAutomountServiceAccountTokenTrueAllowed + ErrorAutomountServiceAccountTokenTrueAndNoName ErrorCapabilityAdded ErrorCapabilityAllowed - ErrorCapabilitiesNIL - ErrorCapabilitiesNoneDropped ErrorCapabilityNotDropped ErrorImageTagIncorrect ErrorImageTagMissing + ErrorMisconfiguredKubeauditAllow ErrorPrivilegedNIL ErrorPrivilegedTrue ErrorPrivilegedTrueAllowed ErrorReadOnlyRootFilesystemFalse ErrorReadOnlyRootFilesystemFalseAllowed ErrorReadOnlyRootFilesystemNIL - ErrorResourcesLimitsNIL - ErrorResourcesLimitsCpuNIL ErrorResourcesLimitsCpuExceeded - ErrorResourcesLimitsMemoryNIL + ErrorResourcesLimitsCpuNIL ErrorResourcesLimitsMemoryExceeded + ErrorResourcesLimitsMemoryNIL + ErrorResourcesLimitsNIL ErrorRunAsNonRootFalse - ErrorRunAsNonRootNIL ErrorRunAsNonRootFalseAllowed - ErrorSecurityContextNIL + ErrorRunAsNonRootNIL ErrorServiceAccountTokenDeprecated - ErrorAutomountServiceAccountTokenNILAndNoName ErrorServiceAccountTokenNoName - ErrorAutomountServiceAccountTokenTrueAndNoName - ErrorAutomountServiceAccountTokenTrueAllowed - ErrorMisconfiguredKubeauditAllow InfoImageCorrect + PlaceHolder ) diff --git a/cmd/k8sruntime_util.go b/cmd/k8sruntime_util.go new file mode 100644 index 00000000..d4b4648a --- /dev/null +++ b/cmd/k8sruntime_util.go @@ -0,0 +1,109 @@ +package cmd + +import ( + "io/ioutil" + + k8sRuntime "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes/scheme" +) + +func setContainers(resource k8sRuntime.Object, containers []Container) k8sRuntime.Object { + switch t := resource.(type) { + case *DaemonSet: + t.Spec.Template.Spec.Containers = containers + return t.DeepCopyObject() + case *Deployment: + t.Spec.Template.Spec.Containers = containers + return t.DeepCopyObject() + case *Pod: + t.Spec.Containers = containers + return t.DeepCopyObject() + case *ReplicationController: + t.Spec.Template.Spec.Containers = containers + return t.DeepCopyObject() + case *StatefulSet: + t.Spec.Template.Spec.Containers = containers + return t.DeepCopyObject() + } + return resource +} + +func disableDSA(resource k8sRuntime.Object) k8sRuntime.Object { + switch t := resource.(type) { + case *DaemonSet: + t.Spec.Template.Spec.DeprecatedServiceAccount = "" + return t.DeepCopyObject() + case *Deployment: + t.Spec.Template.Spec.DeprecatedServiceAccount = "" + return t.DeepCopyObject() + case *Pod: + t.Spec.DeprecatedServiceAccount = "" + return t.DeepCopyObject() + case *ReplicationController: + t.Spec.Template.Spec.DeprecatedServiceAccount = "" + return t.DeepCopyObject() + case *StatefulSet: + t.Spec.Template.Spec.DeprecatedServiceAccount = "" + return t.DeepCopyObject() + } + return resource +} + +func setASAT(resource k8sRuntime.Object, b bool) k8sRuntime.Object { + var boolean *bool + if b { + boolean = newTrue() + } else { + boolean = newFalse() + } + switch t := resource.(type) { + case *DaemonSet: + t.Spec.Template.Spec.AutomountServiceAccountToken = boolean + return t.DeepCopyObject() + case *Deployment: + t.Spec.Template.Spec.AutomountServiceAccountToken = boolean + return t.DeepCopyObject() + case *Pod: + t.Spec.AutomountServiceAccountToken = boolean + return t.DeepCopyObject() + case *ReplicationController: + t.Spec.Template.Spec.AutomountServiceAccountToken = boolean + return t.DeepCopyObject() + case *StatefulSet: + t.Spec.Template.Spec.AutomountServiceAccountToken = boolean + return t.DeepCopyObject() + } + return resource +} + +func getContainers(resource k8sRuntime.Object) (container []Container) { + switch kubeType := resource.(type) { + case *DaemonSet: + container = kubeType.Spec.Template.Spec.Containers + case *Deployment: + container = kubeType.Spec.Template.Spec.Containers + case *Pod: + container = kubeType.Spec.Containers + case *ReplicationController: + container = kubeType.Spec.Template.Spec.Containers + case *StatefulSet: + container = kubeType.Spec.Template.Spec.Containers + } + return container +} + +func WriteToFile(decode k8sRuntime.Object, filename string) error { + info, _ := k8sRuntime.SerializerInfoForMediaType(scheme.Codecs.SupportedMediaTypes(), "application/yaml") + groupVersion := schema.GroupVersion{Group: decode.GetObjectKind().GroupVersionKind().Group, Version: decode.GetObjectKind().GroupVersionKind().Version} + encoder := scheme.Codecs.EncoderForVersion(info.Serializer, groupVersion) + yaml, err := k8sRuntime.Encode(encoder, decode) + if err != nil { + return err + } + err = ioutil.WriteFile(filename, yaml, 0644) + if err != nil { + return err + } + return nil +} diff --git a/cmd/k8sruntime_util_test.go b/cmd/k8sruntime_util_test.go new file mode 100644 index 00000000..73742a56 --- /dev/null +++ b/cmd/k8sruntime_util_test.go @@ -0,0 +1,44 @@ +package cmd + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSetContainers(t *testing.T) { + assert := assert.New(t) + obj := NewPod().DeepCopyObject() + containers := getContainers(obj) + containers[0].Name = "modified" + setContainers(obj, containers) + for _, container := range getContainers(obj) { + assert.Equal(container.Name, "modified") + } +} + +func TestGetContainers(t *testing.T) { + assert := assert.New(t) + obj := NewPod().DeepCopyObject() + for _, container := range getContainers(obj) { + assert.Equal(container.Name, "container") + } +} + +func TestWriteToFile(t *testing.T) { + file := "../fixtures/read_only_root_filesystem_false.yml" + fileout := "out.yml" + assert := assert.New(t) + resource, err := getKubeResourcesManifest(file) + assert.Equal(1, len(resource)) + assert.Nil(err) + err = WriteToFile(resource[0], fileout) + assert.Nil(err) + resource2, err := getKubeResourcesManifest(file) + assert.Nil(err) + assert.Equal(1, len(resource2)) + assert.Equal(resource, resource2) + err = os.Remove(fileout) + assert.Nil(err) +} diff --git a/cmd/occurrence.go b/cmd/occurrence.go index 2cd23e63..c2f11898 100644 --- a/cmd/occurrence.go +++ b/cmd/occurrence.go @@ -1,8 +1,8 @@ package cmd type Occurrence struct { - kind int // or int? just needs to represent {debug, log, warn, error} + kind int // represent {debug, log, warn, error} id int // KubeAuditInfo, ErrorImageTagMissing ... - message string // the message that currently is in the printResultX function, which would go away with the introduction of this + message string // just the message metadata Metadata } diff --git a/cmd/privileged.go b/cmd/privileged.go index f4e05ca7..8e560b5b 100644 --- a/cmd/privileged.go +++ b/cmd/privileged.go @@ -7,25 +7,14 @@ import ( ) func checkPrivileged(container Container, result *Result) { - if container.SecurityContext == nil { - occ := Occurrence{ - id: ErrorSecurityContextNIL, - kind: Error, - message: "SecurityContext not set, please set it!", - } - result.Occurrences = append(result.Occurrences, occ) - return - } - if container.SecurityContext.Privileged == nil { + if container.SecurityContext == nil || container.SecurityContext.Privileged == nil { occ := Occurrence{ id: ErrorPrivilegedNIL, kind: Warn, message: "Privileged defaults to false, which results in non privileged, which is okay.", } result.Occurrences = append(result.Occurrences, occ) - return - } - if reason := result.Labels["kubeaudit.allow.privileged"]; reason != "" { + } else if reason := result.Labels["kubeaudit.allow.privileged"]; reason != "" { if *container.SecurityContext.Privileged == true { occ := Occurrence{ id: ErrorPrivilegedTrueAllowed, @@ -50,6 +39,7 @@ func checkPrivileged(container Container, result *Result) { } result.Occurrences = append(result.Occurrences, occ) } + return } func auditPrivileged(resource k8sRuntime.Object) (results []Result) { diff --git a/cmd/privileged_fixes.go b/cmd/privileged_fixes.go new file mode 100644 index 00000000..75c45a15 --- /dev/null +++ b/cmd/privileged_fixes.go @@ -0,0 +1,12 @@ +package cmd + +import k8sRuntime "k8s.io/apimachinery/pkg/runtime" + +func fixPrivileged(resource k8sRuntime.Object) k8sRuntime.Object { + var containers []Container + for _, container := range getContainers(resource) { + container.SecurityContext.Privileged = newFalse() + containers = append(containers, container) + } + return setContainers(resource, containers) +} diff --git a/cmd/privileged_fixes_test.go b/cmd/privileged_fixes_test.go new file mode 100644 index 00000000..4af73f64 --- /dev/null +++ b/cmd/privileged_fixes_test.go @@ -0,0 +1,38 @@ +package cmd + +import "testing" + +func TestFixPrivilegeEscalation(t *testing.T) { + assert, resource := FixTestSetup(t, "privileged_nil.yml", auditPrivileged) + for _, container := range getContainers(resource) { + assert.False(*container.SecurityContext.Privileged) + } +} + +func TestFixPrivilegedNIL(t *testing.T) { + assert, resource := FixTestSetup(t, "privileged_nil.yml", auditPrivileged) + for _, container := range getContainers(resource) { + assert.False(*container.SecurityContext.Privileged) + } +} + +func TestFixPrivilegedTrue(t *testing.T) { + assert, resource := FixTestSetup(t, "privileged_true.yml", auditPrivileged) + for _, container := range getContainers(resource) { + assert.False(*container.SecurityContext.Privileged) + } +} + +func TestFixPrivilegedTrueAllowed(t *testing.T) { + assert, resource := FixTestSetup(t, "privileged_true_allowed.yml", auditPrivileged) + for _, container := range getContainers(resource) { + assert.True(*container.SecurityContext.Privileged) + } +} + +func TestFixPrivilegedMisconfiguredAllow(t *testing.T) { + assert, resource := FixTestSetup(t, "privileged_misconfigured_allow.yml", auditPrivileged) + for _, container := range getContainers(resource) { + assert.False(*container.SecurityContext.Privileged) + } +} diff --git a/cmd/privileged_test.go b/cmd/privileged_test.go index 8b869bfd..37e04479 100644 --- a/cmd/privileged_test.go +++ b/cmd/privileged_test.go @@ -3,7 +3,7 @@ package cmd import "testing" func TestSecurityContextNIL_Privileged(t *testing.T) { - runAuditTest(t, "security_context_nil.yml", auditPrivileged, []int{ErrorSecurityContextNIL}) + runAuditTest(t, "security_context_nil.yml", auditPrivileged, []int{ErrorPrivilegedNIL}) } func TestPrivilegedNIL(t *testing.T) { diff --git a/cmd/readOnlyRootFilesystem.go b/cmd/readOnlyRootFilesystem.go index fd94e974..b684ab06 100644 --- a/cmd/readOnlyRootFilesystem.go +++ b/cmd/readOnlyRootFilesystem.go @@ -7,26 +7,8 @@ import ( ) func checkReadOnlyRootFS(container Container, result *Result) { - if container.SecurityContext == nil { - occ := Occurrence{ - id: ErrorSecurityContextNIL, - kind: Error, - message: "SecurityContext not set, please set it!", - } - result.Occurrences = append(result.Occurrences, occ) - return - } - if container.SecurityContext.ReadOnlyRootFilesystem == nil { - occ := Occurrence{ - id: ErrorReadOnlyRootFilesystemNIL, - kind: Error, - message: "ReadOnlyRootFilesystem not set which results in a writable rootFS, please set to true", - } - result.Occurrences = append(result.Occurrences, occ) - return - } if reason := result.Labels["kubeaudit.allow.readOnlyRootFilesystemFalse"]; reason != "" { - if container.SecurityContext.ReadOnlyRootFilesystem == nil || *container.SecurityContext.ReadOnlyRootFilesystem == false { + if container.SecurityContext == nil || container.SecurityContext.ReadOnlyRootFilesystem == nil || *container.SecurityContext.ReadOnlyRootFilesystem == false { occ := Occurrence{ id: ErrorReadOnlyRootFilesystemFalseAllowed, kind: Warn, @@ -43,9 +25,14 @@ func checkReadOnlyRootFS(container Container, result *Result) { } result.Occurrences = append(result.Occurrences, occ) } - return - } - if !*container.SecurityContext.ReadOnlyRootFilesystem { + } else if container.SecurityContext == nil || container.SecurityContext.ReadOnlyRootFilesystem == nil { + occ := Occurrence{ + id: ErrorReadOnlyRootFilesystemNIL, + kind: Error, + message: "ReadOnlyRootFilesystem not set which results in a writable rootFS, please set to true", + } + result.Occurrences = append(result.Occurrences, occ) + } else if !*container.SecurityContext.ReadOnlyRootFilesystem { occ := Occurrence{ id: ErrorReadOnlyRootFilesystemFalse, kind: Error, @@ -53,6 +40,7 @@ func checkReadOnlyRootFS(container Container, result *Result) { } result.Occurrences = append(result.Occurrences, occ) } + return } func auditReadOnlyRootFS(resource k8sRuntime.Object) (results []Result) { diff --git a/cmd/readOnlyRootFilesystem_fixes.go b/cmd/readOnlyRootFilesystem_fixes.go new file mode 100644 index 00000000..29e26619 --- /dev/null +++ b/cmd/readOnlyRootFilesystem_fixes.go @@ -0,0 +1,12 @@ +package cmd + +import k8sRuntime "k8s.io/apimachinery/pkg/runtime" + +func fixReadOnlyRootFilesystem(resource k8sRuntime.Object) k8sRuntime.Object { + var containers []Container + for _, container := range getContainers(resource) { + container.SecurityContext.ReadOnlyRootFilesystem = newTrue() + containers = append(containers, container) + } + return setContainers(resource, containers) +} diff --git a/cmd/readOnlyRootFilesystem_fixes_test.go b/cmd/readOnlyRootFilesystem_fixes_test.go new file mode 100644 index 00000000..7f3882c7 --- /dev/null +++ b/cmd/readOnlyRootFilesystem_fixes_test.go @@ -0,0 +1,31 @@ +package cmd + +import "testing" + +func TestFixReadOnlyRootFilesystemNIL(t *testing.T) { + assert, resource := FixTestSetup(t, "read_only_root_filesystem_nil.yml", auditReadOnlyRootFS) + for _, container := range getContainers(resource) { + assert.True(*container.SecurityContext.ReadOnlyRootFilesystem) + } +} + +func TestFixReadOnlyRootFilesystemFalse(t *testing.T) { + assert, resource := FixTestSetup(t, "read_only_root_filesystem_false.yml", auditReadOnlyRootFS) + for _, container := range getContainers(resource) { + assert.True(*container.SecurityContext.ReadOnlyRootFilesystem) + } +} + +func TestFixReadOnlyRootFilesystemFalseAllowed(t *testing.T) { + assert, resource := FixTestSetup(t, "read_only_root_filesystem_false_allowed.yml", auditReadOnlyRootFS) + for _, container := range getContainers(resource) { + assert.False(*container.SecurityContext.ReadOnlyRootFilesystem) + } +} + +func TestFixReadOnlyRootFilesystemMisconfiguredAllow(t *testing.T) { + assert, resource := FixTestSetup(t, "read_only_root_filesystem_misconfigured_allow.yml", auditReadOnlyRootFS) + for _, container := range getContainers(resource) { + assert.True(*container.SecurityContext.ReadOnlyRootFilesystem) + } +} diff --git a/cmd/readOnlyRootFilesystem_test.go b/cmd/readOnlyRootFilesystem_test.go index b27b3c60..fb8802d7 100644 --- a/cmd/readOnlyRootFilesystem_test.go +++ b/cmd/readOnlyRootFilesystem_test.go @@ -3,7 +3,7 @@ package cmd import "testing" func TestSecurityContextNIL_RORF(t *testing.T) { - runAuditTest(t, "security_context_nil.yml", auditReadOnlyRootFS, []int{ErrorSecurityContextNIL}) + runAuditTest(t, "security_context_nil.yml", auditReadOnlyRootFS, []int{ErrorReadOnlyRootFilesystemNIL}) } func TestReadOnlyRootFilesystemNIL(t *testing.T) { diff --git a/cmd/result.go b/cmd/result.go index f265896b..0fe0c807 100644 --- a/cmd/result.go +++ b/cmd/result.go @@ -2,6 +2,7 @@ package cmd import ( "reflect" + "strings" log "github.com/sirupsen/logrus" ) @@ -77,3 +78,13 @@ func shouldLog(err int) (members []string) { } return } + +func (r *Result) allowedCaps() (allowed map[Capability]string) { + allowed = make(map[Capability]string) + for k, v := range r.Labels { + if strings.Contains(k, "kubeaudit.allow.capability.") { + allowed[Capability(strings.ToUpper(strings.TrimPrefix(k, "kubeaudit.allow.capability.")))] = v + } + } + return +} diff --git a/cmd/runAsNonRoot.go b/cmd/runAsNonRoot.go index cbe994ed..c8a36759 100644 --- a/cmd/runAsNonRoot.go +++ b/cmd/runAsNonRoot.go @@ -7,17 +7,8 @@ import ( ) func checkRunAsNonRoot(container Container, result *Result) { - if container.SecurityContext == nil { - occ := Occurrence{ - id: ErrorSecurityContextNIL, - kind: Error, - message: "SecurityContext not set, please set it!", - } - result.Occurrences = append(result.Occurrences, occ) - return - } if reason := result.Labels["kubeaudit.allow.runAsRoot"]; reason != "" { - if container.SecurityContext.RunAsNonRoot == nil || *container.SecurityContext.RunAsNonRoot == false { + if container.SecurityContext == nil || container.SecurityContext.RunAsNonRoot == nil || *container.SecurityContext.RunAsNonRoot == false { occ := Occurrence{ id: ErrorRunAsNonRootFalseAllowed, kind: Warn, @@ -34,18 +25,14 @@ func checkRunAsNonRoot(container Container, result *Result) { } result.Occurrences = append(result.Occurrences, occ) } - return - } - if container.SecurityContext.RunAsNonRoot == nil { + } else if container.SecurityContext == nil || container.SecurityContext.RunAsNonRoot == nil { occ := Occurrence{ id: ErrorRunAsNonRootNIL, kind: Error, message: "RunAsNonRoot is not set, which results in root user being allowed!", } result.Occurrences = append(result.Occurrences, occ) - return - } - if *container.SecurityContext.RunAsNonRoot == false { + } else if *container.SecurityContext.RunAsNonRoot == false { occ := Occurrence{ id: ErrorRunAsNonRootFalse, kind: Error, @@ -53,6 +40,7 @@ func checkRunAsNonRoot(container Container, result *Result) { } result.Occurrences = append(result.Occurrences, occ) } + return } func auditRunAsNonRoot(resource k8sRuntime.Object) (results []Result) { diff --git a/cmd/runAsNonRoot_fixes.go b/cmd/runAsNonRoot_fixes.go new file mode 100644 index 00000000..8e45ff19 --- /dev/null +++ b/cmd/runAsNonRoot_fixes.go @@ -0,0 +1,12 @@ +package cmd + +import k8sRuntime "k8s.io/apimachinery/pkg/runtime" + +func fixRunAsNonRoot(resource k8sRuntime.Object) k8sRuntime.Object { + var containers []Container + for _, container := range getContainers(resource) { + container.SecurityContext.RunAsNonRoot = newTrue() + containers = append(containers, container) + } + return setContainers(resource, containers) +} diff --git a/cmd/runAsNonRoot_fixes_test.go b/cmd/runAsNonRoot_fixes_test.go new file mode 100644 index 00000000..fedb8881 --- /dev/null +++ b/cmd/runAsNonRoot_fixes_test.go @@ -0,0 +1,38 @@ +package cmd + +import "testing" + +func TestFixRunAsNonRoot(t *testing.T) { + assert, resource := FixTestSetup(t, "run_as_non_root_false.yml", auditRunAsNonRoot) + for _, container := range getContainers(resource) { + assert.True(*container.SecurityContext.RunAsNonRoot) + } +} + +func TestFixRunAsNonRootNil(t *testing.T) { + assert, resource := FixTestSetup(t, "run_as_non_root_nil.yml", auditRunAsNonRoot) + for _, container := range getContainers(resource) { + assert.True(*container.SecurityContext.RunAsNonRoot) + } +} + +func TestFixRunAsNonRootFalse(t *testing.T) { + assert, resource := FixTestSetup(t, "run_as_non_root_false.yml", auditRunAsNonRoot) + for _, container := range getContainers(resource) { + assert.True(*container.SecurityContext.RunAsNonRoot) + } +} + +func TestFixRunAsRootFalseAllowed(t *testing.T) { + assert, resource := FixTestSetup(t, "run_as_non_root_false_allowed.yml", auditRunAsNonRoot) + for _, container := range getContainers(resource) { + assert.False(*container.SecurityContext.RunAsNonRoot) + } +} + +func TestFixRunAsNonRootMisconfiguredAllow(t *testing.T) { + assert, resource := FixTestSetup(t, "run_as_non_root_misconfigured_allow.yml", auditRunAsNonRoot) + for _, container := range getContainers(resource) { + assert.True(*container.SecurityContext.RunAsNonRoot) + } +} diff --git a/cmd/runAsNonRoot_test.go b/cmd/runAsNonRoot_test.go index 1a3654b8..615477ea 100644 --- a/cmd/runAsNonRoot_test.go +++ b/cmd/runAsNonRoot_test.go @@ -5,7 +5,7 @@ import ( ) func TestSecurityContextNIL(t *testing.T) { - runAuditTest(t, "security_context_nil.yml", auditRunAsNonRoot, []int{ErrorSecurityContextNIL}) + runAuditTest(t, "security_context_nil.yml", auditRunAsNonRoot, []int{ErrorRunAsNonRootNIL}) } func TestRunAsNonRootNil(t *testing.T) { @@ -16,10 +16,10 @@ func TestRunAsNonRootFalse(t *testing.T) { runAuditTest(t, "run_as_non_root_false.yml", auditRunAsNonRoot, []int{ErrorRunAsNonRootFalse}) } -func TestAllowRunAsRootFalseAllowed(t *testing.T) { +func TestRunAsRootFalseAllowed(t *testing.T) { runAuditTest(t, "run_as_non_root_false_allowed.yml", auditRunAsNonRoot, []int{ErrorRunAsNonRootFalseAllowed}) } -func TestAllowRunAsNonRootMisconfiguredAllow(t *testing.T) { +func TestRunAsNonRootMisconfiguredAllow(t *testing.T) { runAuditTest(t, "run_as_non_root_misconfigured_allow.yml", auditRunAsNonRoot, []int{ErrorMisconfiguredKubeauditAllow}) } diff --git a/cmd/securitycontext_fixes.go b/cmd/securitycontext_fixes.go new file mode 100644 index 00000000..4f2cc541 --- /dev/null +++ b/cmd/securitycontext_fixes.go @@ -0,0 +1,14 @@ +package cmd + +import k8sRuntime "k8s.io/apimachinery/pkg/runtime" + +func fixSecurityContextNIL(resource k8sRuntime.Object) k8sRuntime.Object { + var containers []Container + for _, container := range getContainers(resource) { + if container.SecurityContext == nil { + container.SecurityContext = &SecurityContext{Capabilities: &Capabilities{Drop: []Capability{}, Add: []Capability{}}} + } + containers = append(containers, container) + } + return setContainers(resource, containers) +} diff --git a/cmd/test_util.go b/cmd/test_util.go index e93c80d9..3575cab0 100644 --- a/cmd/test_util.go +++ b/cmd/test_util.go @@ -2,6 +2,8 @@ package cmd import ( "path/filepath" + "reflect" + "runtime" "testing" log "github.com/sirupsen/logrus" @@ -12,6 +14,19 @@ import ( var path = "../fixtures/" +func FixTestSetup(t *testing.T, file string, auditFunction func(k8sRuntime.Object) []Result) (*assert.Assertions, k8sRuntime.Object) { + assert := assert.New(t) + file = filepath.Join(path, file) + resources, err := getKubeResourcesManifest(file) + assert.Nil(err) + assert.Equal(1, len(resources)) + resource := resources[0] + results := getResults(resources, auditFunction) + assert.Equal(1, len(results)) + result := results[0] + return assert, fixPotentialSecurityIssue(resource, result) +} + func runAuditTest(t *testing.T, file string, function interface{}, errCodes []int, argStr ...string) (results []Result) { assert := assert.New(t) file = filepath.Join(path, file) @@ -48,7 +63,8 @@ func runAuditTest(t *testing.T, file string, function interface{}, errCodes []in case (func(limitFlags, k8sRuntime.Object) []Result): currentResults = f(limits, resource) default: - log.Fatal("Invalid function provided") + name := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() + log.Fatal("Invalid audit function provided: ", name) } for _, currentResult := range currentResults { results = append(results, currentResult) @@ -61,10 +77,10 @@ func runAuditTest(t *testing.T, file string, function interface{}, errCodes []in } } + assert.Equal(len(errCodes), len(errors)) for _, errCode := range errCodes { assert.True(errors[errCode]) } - assert.Equal(len(errors), len(errCodes)) return } @@ -73,3 +89,17 @@ func runAuditTestInNamespace(t *testing.T, namespace string, file string, functi runAuditTest(t, file, function, errCodes) rootConfig.namespace = apiv1.NamespaceAll } + +func NewPod() *Pod { + resources, err := getKubeResourcesManifest("../fixtures/pod.yml") + if err != nil { + return nil + } + for _, resource := range resources { + switch t := resource.(type) { + case *Pod: + return t + } + } + return nil +} diff --git a/cmd/types.go b/cmd/types.go index 7ad3cfb9..5d9a910c 100644 --- a/cmd/types.go +++ b/cmd/types.go @@ -14,6 +14,10 @@ type DaemonSet = extensionsv1beta1.DaemonSet type Deployment = v1beta1.Deployment type StatefulSet = v1beta1.StatefulSet type NetworkPolicy = networking.NetworkPolicy +type SecurityContext = apiv1.SecurityContext + +type ObjectMeta = metav1.ObjectMeta +type PodSpec = apiv1.PodSpec type PodList = apiv1.PodList type ReplicationControllerList = apiv1.ReplicationControllerList @@ -24,6 +28,7 @@ type NamespaceList = apiv1.NamespaceList type NetworkPolicyList = networking.NetworkPolicyList type Capability = apiv1.Capability +type Capabilities = apiv1.Capabilities type Container = apiv1.Container type ListOptions = metav1.ListOptions diff --git a/cmd/util.go b/cmd/util.go index 5fc23a12..46127307 100644 --- a/cmd/util.go +++ b/cmd/util.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "io/ioutil" + "reflect" "runtime" "strings" "sync" @@ -17,6 +18,15 @@ import ( "k8s.io/client-go/kubernetes/scheme" ) +func newTrue() *bool { + b := true + return &b +} + +func newFalse() *bool { + return new(bool) +} + func debugPrint() { if rootConfig.verbose == "DEBUG" { buf := make([]byte, 1<<16) @@ -33,22 +43,6 @@ func isInNamespace(meta metav1.ObjectMeta, namespace string) (valid bool) { return namespace == apiv1.NamespaceAll || namespace == meta.Namespace } -func getContainers(resource k8sRuntime.Object) (container []Container) { - switch kubeType := resource.(type) { - case *DaemonSet: - container = kubeType.Spec.Template.Spec.Containers - case *Deployment: - container = kubeType.Spec.Template.Spec.Containers - case *Pod: - container = kubeType.Spec.Containers - case *ReplicationController: - container = kubeType.Spec.Template.Spec.Containers - case *StatefulSet: - container = kubeType.Spec.Template.Spec.Containers - } - return container -} - func newResultFromResource(resource k8sRuntime.Object) (result Result) { switch kubeType := resource.(type) { case *DaemonSet: @@ -136,6 +130,15 @@ func getKubeResources(clientset *kubernetes.Clientset) (resources []k8sRuntime.O return } +func writeManifestFile(decoded []k8sRuntime.Object, filename string) error { + for _, decode := range decoded { + if err := WriteToFile(decode, filename); err != nil { + log.Error(err) + } + } + return nil +} + func getKubeResourcesManifest(filename string) (decoded []k8sRuntime.Object, err error) { buf, err := ioutil.ReadFile(filename) @@ -204,7 +207,8 @@ func getResults(resources []k8sRuntime.Object, auditFunc interface{}) []Result { case func(limits limitFlags, resource k8sRuntime.Object) (results []Result): resultsChannel <- append(results, f(limitConfig, resource)...) default: - log.Fatal("Invalid audit function provided") + name := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() + log.Fatal("Invalid audit function provided: ", name) } wg.Done() }(resource) @@ -223,11 +227,13 @@ func getResults(resources []k8sRuntime.Object, auditFunc interface{}) []Result { func runAudit(auditFunc interface{}) func(cmd *cobra.Command, args []string) { return func(cmd *cobra.Command, args []string) { if err := checkParams(auditFunc); err != nil { + log.Error("Parameter check failed") log.Error(err) } setFormatter() resources, err := getResources() if err != nil { + log.Error("getResources failed") log.Error(err) return } diff --git a/config/capabilities-drop-list.yml b/config/capabilities-drop-list.yml index 36029fdb..46732556 100644 --- a/config/capabilities-drop-list.yml +++ b/config/capabilities-drop-list.yml @@ -1,17 +1,17 @@ # SANE DEFAULTS: capabilitiesToBeDropped: # https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities - - AUDIT_WRITE # Write records to kernel auditing log. - - CHOWN # Make arbitrary changes to file UIDs and GIDs (see chown(2)). - - DAC_OVERRIDE # Bypass file read, write, and execute permission checks. - - FOWNER # Bypass permission checks on operations that normally require the file system UID of the process to match the UID of the file. - - FSETID # Don’t clear set-user-ID and set-group-ID permission bits when a file is modified. - - KILL # Bypass permission checks for sending signals. - - MKNOD # Create special files using mknod(2). - - NET_BIND_SERVICE # Bind a socket to internet domain privileged ports (port numbers less than 1024). - - NET_RAW # Use RAW and PACKET sockets. - - SETFCAP # Set file capabilities. - - SETGID # Make arbitrary manipulations of process GIDs and supplementary GID list. - - SETUID # Make arbitrary manipulations of process UIDs. - - SETPCAP # Modify process capabilities. - - SYS_CHROOT # Use chroot(2), change root directory. + - SETPCAP #Modify process capabilities. + - MKNOD #Create special files using mknod(2). + - AUDIT_WRITE #Write records to kernel auditing log. + - CHOWN #Make arbitrary changes to file UIDs and GIDs (see chown(2)). + - NET_RAW #Use RAW and PACKET sockets. + - DAC_OVERRIDE #Bypass file read, write, and execute permission checks. + - FOWNER #Bypass permission checks on operations that normally require the file system UID of the process to match the UID of the file. + - FSETID #Don’t clear set-user-ID and set-group-ID permission bits when a file is modified. + - KILL #Bypass permission checks for sending signals. + - SETGID #Make arbitrary manipulations of process GIDs and supplementary GID list. + - SETUID #Make arbitrary manipulations of process UIDs. + - NET_BIND_SERVICE #Bind a socket to internet domain privileged ports (port numbers less than 1024). + - SYS_CHROOT #Use chroot(2), change root directory. + - SETFCAP #Set file capabilities. diff --git a/fixtures/allow_privilege_escalation_nil.yml b/fixtures/allow_privilege_escalation_nil.yml index 1352b4a8..cdfcd786 100644 --- a/fixtures/allow_privilege_escalation_nil.yml +++ b/fixtures/allow_privilege_escalation_nil.yml @@ -1,17 +1,21 @@ ---- apiVersion: apps/v1beta1 kind: StatefulSet metadata: + creationTimestamp: null name: fakeStatefulSetAPE namespace: fakeStatefulSetAPE spec: + serviceName: "" template: metadata: + creationTimestamp: null labels: apps: fakeAllowPrivilegeEscalation spec: containers: - name: fakeContainerAPE - securityContext: - drop: - - KILL + resources: {} + securityContext: {} + updateStrategy: {} +status: + replicas: 0 diff --git a/fixtures/allow_privilege_escalation_true.yml b/fixtures/allow_privilege_escalation_true.yml index e90ebf50..61e4001f 100644 --- a/fixtures/allow_privilege_escalation_true.yml +++ b/fixtures/allow_privilege_escalation_true.yml @@ -1,16 +1,22 @@ ---- apiVersion: apps/v1beta1 kind: StatefulSet metadata: + creationTimestamp: null name: fakeStatefulSetAPE namespace: fakeStatefulSetAPE spec: + serviceName: "" template: metadata: + creationTimestamp: null labels: apps: fakeAllowPrivilegeEscalation spec: containers: - name: fakeContainerAPE + resources: {} securityContext: allowPrivilegeEscalation: true + updateStrategy: {} +status: + replicas: 0 diff --git a/fixtures/allow_privilege_escalation_true_allowed.yml b/fixtures/allow_privilege_escalation_true_allowed.yml index 8bb14633..3ad6724f 100644 --- a/fixtures/allow_privilege_escalation_true_allowed.yml +++ b/fixtures/allow_privilege_escalation_true_allowed.yml @@ -14,4 +14,4 @@ spec: containers: - name: fakeContainerAPE securityContext: - privileged: true + allowPrivilegeEscalation: true diff --git a/fixtures/autofix-fixed.yml b/fixtures/autofix-fixed.yml new file mode 100644 index 00000000..f2533fad --- /dev/null +++ b/fixtures/autofix-fixed.yml @@ -0,0 +1,40 @@ +apiVersion: apps/v1beta1 +kind: Deployment +metadata: + creationTimestamp: null + name: cababilitiesAdded + namespace: fakeDeploymentSC +spec: + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + apps: fakeSecurityContext + spec: + automountServiceAccountToken: false + containers: + - name: fakeContainerSC + resources: {} + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - AUDIT_WRITE + - CHOWN + - DAC_OVERRIDE + - FOWNER + - FSETID + - KILL + - MKNOD + - NET_BIND_SERVICE + - NET_RAW + - SETFCAP + - SETGID + - SETPCAP + - SETUID + - SYS_CHROOT + privileged: false + readOnlyRootFilesystem: true + runAsNonRoot: true +status: {} diff --git a/fixtures/autofix.yml b/fixtures/autofix.yml new file mode 100644 index 00000000..5da15774 --- /dev/null +++ b/fixtures/autofix.yml @@ -0,0 +1,18 @@ +apiVersion: apps/v1beta1 +kind: Deployment +metadata: + creationTimestamp: null + name: cababilitiesAdded + namespace: fakeDeploymentSC +spec: + strategy: {} + template: + metadata: + creationTimestamp: null + labels: + apps: fakeSecurityContext + spec: + containers: + - name: fakeContainerSC + resources: {} +status: {} diff --git a/fixtures/capabilities_added.yml b/fixtures/capabilities_added.yml index 9d4abb21..133a365e 100644 --- a/fixtures/capabilities_added.yml +++ b/fixtures/capabilities_added.yml @@ -1,17 +1,20 @@ ---- apiVersion: apps/v1beta1 kind: Deployment metadata: + creationTimestamp: null name: cababilitiesAdded namespace: fakeDeploymentSC spec: + strategy: {} template: metadata: + creationTimestamp: null labels: apps: fakeSecurityContext spec: containers: - name: fakeContainerSC + resources: {} securityContext: capabilities: add: @@ -32,3 +35,4 @@ spec: - SETUID - SETPCAP - SYS_CHROOT +status: {} diff --git a/fixtures/capabilities_nil.yml b/fixtures/capabilities_nil.yml index 574ccc7e..a05c7979 100644 --- a/fixtures/capabilities_nil.yml +++ b/fixtures/capabilities_nil.yml @@ -1,16 +1,19 @@ ---- apiVersion: apps/v1beta1 kind: Deployment metadata: + creationTimestamp: null name: fakeDeploymentSC2 namespace: fakeDeploymentSC spec: + strategy: {} template: metadata: + creationTimestamp: null labels: apps: fakeSecurityContext spec: containers: - name: fakeContainerSC - securityContext: - capabilities: + resources: {} + securityContext: {} +status: {} diff --git a/fixtures/capabilities_some_allowed.yml b/fixtures/capabilities_some_allowed.yml index 1ce6114e..8ed1dcc3 100644 --- a/fixtures/capabilities_some_allowed.yml +++ b/fixtures/capabilities_some_allowed.yml @@ -18,6 +18,7 @@ spec: capabilities: add: - SYS_TIME + - SYS_MODULE drop: - AUDIT_WRITE - DAC_OVERRIDE diff --git a/fixtures/capabilities_some_dropped.yml b/fixtures/capabilities_some_dropped.yml index d0637cbc..91627bf7 100644 --- a/fixtures/capabilities_some_dropped.yml +++ b/fixtures/capabilities_some_dropped.yml @@ -1,17 +1,20 @@ ---- apiVersion: apps/v1beta1 kind: Deployment metadata: + creationTimestamp: null name: fakeDeploymentSC5 namespace: fakeDeploymentSC spec: + strategy: {} template: metadata: + creationTimestamp: null labels: apps: fakeSecurityContext spec: containers: - name: fakeContainerSC + resources: {} securityContext: capabilities: drop: @@ -28,3 +31,4 @@ spec: - SETUID - SETPCAP - SYS_CHROOT +status: {} diff --git a/fixtures/image_tag_missing.yml b/fixtures/image_tag_missing.yml index 20328a2e..8b9a3b6d 100644 --- a/fixtures/image_tag_missing.yml +++ b/fixtures/image_tag_missing.yml @@ -1,15 +1,19 @@ ---- apiVersion: apps/v1beta1 kind: Deployment metadata: + creationTimestamp: null name: fakeDeploymentImg1 namespace: fakeDeploymentImg spec: + strategy: {} template: metadata: + creationTimestamp: null labels: apps: fakeImage spec: containers: - - name: fakeContainerImg - image: fakeContainerImg + - image: fakeContainerImg + name: fakeContainerImg + resources: {} +status: {} diff --git a/fixtures/image_tag_present.yml b/fixtures/image_tag_present.yml index 55ed7292..531803ec 100644 --- a/fixtures/image_tag_present.yml +++ b/fixtures/image_tag_present.yml @@ -1,15 +1,19 @@ ---- apiVersion: apps/v1beta1 kind: Deployment metadata: + creationTimestamp: null name: fakeDeploymentImg2 namespace: fakeDeploymentImg spec: + strategy: {} template: metadata: + creationTimestamp: null labels: apps: fakeImage spec: containers: - - name: fakeContainerImg - image: fakeContainerImg:1.5 + - image: fakeContainerImg:1.5 + name: fakeContainerImg + resources: {} +status: {} diff --git a/fixtures/pod.yml b/fixtures/pod.yml new file mode 100644 index 00000000..5e62ece1 --- /dev/null +++ b/fixtures/pod.yml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Pod +metadata: + name: Pod + namespace: PodNamespace +spec: + containers: + - name: container diff --git a/fixtures/privileged_nil.yml b/fixtures/privileged_nil.yml index 7d0c56f8..2a1f05e3 100644 --- a/fixtures/privileged_nil.yml +++ b/fixtures/privileged_nil.yml @@ -1,19 +1,27 @@ ---- apiVersion: extensions/v1beta1 kind: DaemonSet metadata: + creationTimestamp: null name: fakeDaemonSetPrivileged2 namespace: fakeDaemonSetPrivileged spec: template: metadata: + creationTimestamp: null labels: apps: fakePrivileged spec: containers: - name: fakeContainerPrivileged + resources: {} securityContext: capabilities: add: - NET_ADMIN - SYS_TIME + updateStrategy: {} +status: + currentNumberScheduled: 0 + desiredNumberScheduled: 0 + numberMisscheduled: 0 + numberReady: 0 diff --git a/fixtures/privileged_true.yml b/fixtures/privileged_true.yml index 555cdc67..de19b4f8 100644 --- a/fixtures/privileged_true.yml +++ b/fixtures/privileged_true.yml @@ -1,16 +1,24 @@ ---- apiVersion: extensions/v1beta1 kind: DaemonSet metadata: + creationTimestamp: null name: fakeDaemonSetPrivileged2 namespace: fakeDaemonSetPrivileged spec: template: metadata: + creationTimestamp: null labels: apps: fakePrivileged spec: containers: - name: fakeContainerPrivileged + resources: {} securityContext: privileged: true + updateStrategy: {} +status: + currentNumberScheduled: 0 + desiredNumberScheduled: 0 + numberMisscheduled: 0 + numberReady: 0 diff --git a/fixtures/read_only_root_filesystem_false.yml b/fixtures/read_only_root_filesystem_false.yml index f776fa4f..e66bc424 100644 --- a/fixtures/read_only_root_filesystem_false.yml +++ b/fixtures/read_only_root_filesystem_false.yml @@ -1,16 +1,22 @@ ---- apiVersion: apps/v1beta1 kind: StatefulSet metadata: + creationTimestamp: null name: fakeStatefulSetRORF3 namespace: fakeStatefulSetRORF spec: + serviceName: "" template: metadata: + creationTimestamp: null labels: apps: fakeReadOnlyRootFilesystem spec: containers: - name: fakeContainerRORF + resources: {} securityContext: readOnlyRootFilesystem: false + updateStrategy: {} +status: + replicas: 0 diff --git a/fixtures/read_only_root_filesystem_nil.yml b/fixtures/read_only_root_filesystem_nil.yml index 7588a079..41e57364 100644 --- a/fixtures/read_only_root_filesystem_nil.yml +++ b/fixtures/read_only_root_filesystem_nil.yml @@ -1,17 +1,20 @@ ---- apiVersion: apps/v1beta1 kind: StatefulSet metadata: + creationTimestamp: null name: fakeStatefulSetRORF2 namespace: fakeStatefulSetRORF spec: + serviceName: "" template: metadata: + creationTimestamp: null labels: apps: fakeReadOnlyRootFilesystem spec: containers: - name: fakeContainerRORF + resources: {} securityContext: capabilities: drop: @@ -29,3 +32,6 @@ spec: - SETUID - SETPCAP - SYS_CHROOT + updateStrategy: {} +status: + replicas: 0 diff --git a/fixtures/resources_limit.yml b/fixtures/resources_limit.yml index 977a3e4c..9b2b256b 100644 --- a/fixtures/resources_limit.yml +++ b/fixtures/resources_limit.yml @@ -1,22 +1,24 @@ ---- apiVersion: apps/v1beta1 kind: Deployment metadata: + creationTimestamp: null name: resources_limit_no_memory namespace: fakeDeploymentQuota spec: + strategy: {} template: metadata: + creationTimestamp: null labels: apps: fakeNoLimitQuota spec: containers: - name: fakeContainerLimitOk - resources: - requests: - memory: 256Mi - cpu: 500m + resources: limits: - memory: 512Mi cpu: 750m - + memory: 512Mi + requests: + cpu: 500m + memory: 256Mi +status: {} diff --git a/fixtures/resources_limit_nil.yml b/fixtures/resources_limit_nil.yml index 23ef5ed9..e7efeadf 100644 --- a/fixtures/resources_limit_nil.yml +++ b/fixtures/resources_limit_nil.yml @@ -1,15 +1,18 @@ ---- apiVersion: apps/v1beta1 kind: Deployment metadata: + creationTimestamp: null name: resources_limit_nil namespace: fakeDeploymentQuota spec: + strategy: {} template: metadata: + creationTimestamp: null labels: apps: fakeNoLimitQuota spec: containers: - name: fakeContainerNoLimit - resources: + resources: {} +status: {} diff --git a/fixtures/resources_limit_no_cpu.yml b/fixtures/resources_limit_no_cpu.yml index a18ea705..9e6a1641 100644 --- a/fixtures/resources_limit_no_cpu.yml +++ b/fixtures/resources_limit_no_cpu.yml @@ -1,18 +1,20 @@ ---- apiVersion: apps/v1beta1 kind: Deployment metadata: + creationTimestamp: null name: resources_limit_no_cpu namespace: fakeDeploymentQuota spec: + strategy: {} template: metadata: + creationTimestamp: null labels: apps: fakeNoLimitQuota spec: containers: - name: fakeContainerNoCPULimit - resources: + resources: limits: memory: 512Mi - +status: {} diff --git a/fixtures/resources_limit_no_memory.yml b/fixtures/resources_limit_no_memory.yml index e0b496db..efd83daf 100644 --- a/fixtures/resources_limit_no_memory.yml +++ b/fixtures/resources_limit_no_memory.yml @@ -1,18 +1,20 @@ ---- apiVersion: apps/v1beta1 kind: Deployment metadata: + creationTimestamp: null name: resources_limit_no_memory namespace: fakeDeploymentQuota spec: + strategy: {} template: metadata: + creationTimestamp: null labels: apps: fakeNoLimitQuota spec: containers: - name: fakeContainerNoMemoryLimit - resources: + resources: limits: - cpu: 1000m - + cpu: "1" +status: {} diff --git a/fixtures/run_as_non_root_false.yml b/fixtures/run_as_non_root_false.yml index fc266534..040afd69 100644 --- a/fixtures/run_as_non_root_false.yml +++ b/fixtures/run_as_non_root_false.yml @@ -1,16 +1,20 @@ ---- apiVersion: apps/v1beta1 kind: Deployment metadata: + creationTimestamp: null name: run_as_non_root_false namespace: fakeDeploymentRANR spec: + strategy: {} template: metadata: + creationTimestamp: null labels: apps: fakeRunAsNonRoot spec: containers: - name: fakeContainerRANR + resources: {} securityContext: runAsNonRoot: false +status: {} diff --git a/fixtures/run_as_non_root_misconfigured_allow.yml b/fixtures/run_as_non_root_misconfigured_allow.yml index 5eda8765..3e705dcc 100644 --- a/fixtures/run_as_non_root_misconfigured_allow.yml +++ b/fixtures/run_as_non_root_misconfigured_allow.yml @@ -1,17 +1,20 @@ ---- apiVersion: apps/v1beta1 kind: Deployment metadata: name: allow_run_as_root namespace: fakeDeploymentRANR spec: + strategy: {} template: metadata: + creationTimestamp: null labels: apps: fakeSecurityContext kubeaudit.allow.runAsRoot: "Superuser privileges needed" spec: containers: - name: fakeContainerRANR + resources: {} securityContext: - runAsNonRoot: true + runAsNonRoot: true +status: {} diff --git a/fixtures/run_as_non_root_nil.yml b/fixtures/run_as_non_root_nil.yml index 02bdc38a..089a06a1 100644 --- a/fixtures/run_as_non_root_nil.yml +++ b/fixtures/run_as_non_root_nil.yml @@ -1,17 +1,19 @@ ---- apiVersion: apps/v1beta1 kind: Deployment metadata: + creationTimestamp: null name: run_as_non_root_nil namespace: fakeDeploymentRANR spec: + strategy: {} template: metadata: + creationTimestamp: null labels: apps: fakeRunAsNonRoot spec: containers: - name: fakeContainerRANR - securityContext: - drop: - - AUDIT_WRITE + resources: {} + securityContext: {} +status: {} diff --git a/fixtures/security_context_nil.yml b/fixtures/security_context_nil.yml index d495eed6..b039b99f 100644 --- a/fixtures/security_context_nil.yml +++ b/fixtures/security_context_nil.yml @@ -1,14 +1,18 @@ ---- apiVersion: apps/v1beta1 kind: Deployment metadata: + creationTimestamp: null name: security_context_nil namespace: fakeDeploymentRANR spec: + strategy: {} template: metadata: + creationTimestamp: null labels: apps: fakeRunAsNonRoot spec: containers: - name: fakeContainerRANR + resources: {} +status: {} diff --git a/fixtures/service_account_token_deprecated.yml b/fixtures/service_account_token_deprecated.yml index c007679e..3a96e2b2 100644 --- a/fixtures/service_account_token_deprecated.yml +++ b/fixtures/service_account_token_deprecated.yml @@ -1,15 +1,19 @@ ---- apiVersion: v1 kind: ReplicationController metadata: + creationTimestamp: null name: fakeReplicationControllerASAT1 namespace: fakeReplicationControllerASAT spec: template: metadata: + creationTimestamp: null labels: apps: fakeAutomountServiceAccountToken spec: - serviceAccount: fakeDeprecatedServiceAccount containers: - name: fakeContainerASAT + resources: {} + serviceAccount: fakeDeprecatedServiceAccount +status: + replicas: 0 diff --git a/fixtures/service_account_token_nil_and_no_name.yml b/fixtures/service_account_token_nil_and_no_name.yml index 74ba86a3..85e24d86 100644 --- a/fixtures/service_account_token_nil_and_no_name.yml +++ b/fixtures/service_account_token_nil_and_no_name.yml @@ -1,14 +1,18 @@ ---- apiVersion: v1 kind: ReplicationController metadata: + creationTimestamp: null name: fakeReplicationControllerASAT2 namespace: fakeReplicationControllerASAT spec: template: metadata: + creationTimestamp: null labels: apps: fakeAutomountServiceAccountToken spec: containers: - name: fakeContainerASAT + resources: {} +status: + replicas: 0 diff --git a/fixtures/service_account_token_true_and_no_name.yml b/fixtures/service_account_token_true_and_no_name.yml index b98f93c4..6a4e8d75 100644 --- a/fixtures/service_account_token_true_and_no_name.yml +++ b/fixtures/service_account_token_true_and_no_name.yml @@ -1,15 +1,19 @@ ---- apiVersion: v1 kind: ReplicationController metadata: + creationTimestamp: null name: fakeReplicationControllerASAT2 namespace: fakeReplicationControllerASAT spec: template: metadata: + creationTimestamp: null labels: apps: fakeAutomountServiceAccountToken spec: automountServiceAccountToken: true containers: - name: fakeContainerASAT + resources: {} +status: + replicas: 0