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

Commit

Permalink
Merge pull request #75 from Shopify/autofix
Browse files Browse the repository at this point in the history
Add autofix command
  • Loading branch information
klautcomputing authored Jan 10, 2018
2 parents fd1cd79 + 17626e2 commit 05f86ec
Show file tree
Hide file tree
Showing 64 changed files with 1,054 additions and 222 deletions.
8 changes: 7 additions & 1 deletion Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 2 additions & 11 deletions cmd/allowPrivilegeEscalation.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
12 changes: 12 additions & 0 deletions cmd/allowPrivilegeEscalation_fixes.go
Original file line number Diff line number Diff line change
@@ -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)
}
31 changes: 31 additions & 0 deletions cmd/allowPrivilegeEscalation_fixes_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
2 changes: 1 addition & 1 deletion cmd/allowPrivilegeEscalation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
86 changes: 86 additions & 0 deletions cmd/autofix.go
Original file line number Diff line number Diff line change
@@ -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)
}
21 changes: 21 additions & 0 deletions cmd/autofix_test.go
Original file line number Diff line number Diff line change
@@ -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))
}
11 changes: 11 additions & 0 deletions cmd/automountServiceAccountToken_fixes.go
Original file line number Diff line number Diff line change
@@ -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)
}
43 changes: 43 additions & 0 deletions cmd/automountServiceAccountToken_fixes_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
36 changes: 36 additions & 0 deletions cmd/cap_set.go
Original file line number Diff line number Diff line change
@@ -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
}
28 changes: 28 additions & 0 deletions cmd/cap_set_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
Loading

0 comments on commit 05f86ec

Please sign in to comment.