This repository has been archived by the owner on Oct 30, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 186
Adds a new 'mounts' command to audit sensitive host paths mounts #322
Merged
Merged
Changes from 5 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
6a42bf1
Add a new 'mounts' command to audit sensitive host paths mounts
jcbbc 230e1a6
Remove unused code
jcbbc fa88f5e
Various improvements following feedbacks
jcbbc ebad8dd
Merge branch 'master' into mount-audit
jcbbc 26cc11a
Move mount info to metadata
jcbbc cf59458
Another batch of small improvements
jcbbc 57c7453
More improvements following feedbacks
jcbbc f2782d0
Documentation improvements
jcbbc 7eb28a2
Rename --path argument to --denyPathsList
jcbbc 0452090
Change -s shorthand to -d. Also fix docs
jcbbc d0fb510
More doc fixes
jcbbc File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package mounts | ||
|
||
type Config struct { | ||
SensitivePaths []string `yaml:"paths"` | ||
} | ||
|
||
func (config *Config) GetSensitivePaths() []string { | ||
if config == nil { | ||
return DefaultSensitivePaths | ||
} | ||
return config.SensitivePaths | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
apiVersion: v1 | ||
kind: Pod | ||
metadata: | ||
name: pod | ||
namespace: docker-sock-mounted | ||
spec: | ||
containers: | ||
- name: container | ||
image: scratch | ||
volumeMounts: | ||
- mountPath: /var/run/docker.sock | ||
name: docker-sock-volume | ||
volumes: | ||
- name: docker-sock-volume | ||
hostPath: | ||
path: /var/run/docker.sock |
25 changes: 25 additions & 0 deletions
25
auditors/mounts/fixtures/proc-mounted-allowed-multi-containers-multi-labels.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
apiVersion: v1 | ||
kind: Pod | ||
metadata: | ||
name: pod | ||
labels: | ||
name: pod | ||
container.audit.kubernetes.io/container1.allow-host-path-mount-proc-volume: "SomeReason" | ||
container.audit.kubernetes.io/container2.allow-host-path-mount-proc-volume: "SomeReason" | ||
namespace: proc-mounted-allowed-multi-containers-multi-labels | ||
spec: | ||
containers: | ||
- name: container1 | ||
image: scratch | ||
volumeMounts: | ||
- mountPath: /host/proc | ||
name: proc-volume | ||
- name: container2 | ||
image: scratch | ||
volumeMounts: | ||
- mountPath: /host/proc | ||
name: proc-volume | ||
volumes: | ||
- name: proc-volume | ||
hostPath: | ||
path: /proc |
24 changes: 24 additions & 0 deletions
24
auditors/mounts/fixtures/proc-mounted-allowed-multi-containers-single-label.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
apiVersion: v1 | ||
kind: Pod | ||
metadata: | ||
name: pod | ||
labels: | ||
name: pod | ||
container.audit.kubernetes.io/container1.allow-host-path-mount-proc-volume: "SomeReason" | ||
namespace: proc-mounted-allowed-multi-containers-single-label | ||
spec: | ||
containers: | ||
- name: container1 | ||
image: scratch | ||
volumeMounts: | ||
- mountPath: /host/proc | ||
name: proc-volume | ||
- name: container2 | ||
image: scratch | ||
volumeMounts: | ||
- mountPath: /host/proc | ||
name: proc-volume | ||
volumes: | ||
- name: proc-volume | ||
hostPath: | ||
path: /proc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
apiVersion: v1 | ||
kind: Pod | ||
metadata: | ||
name: pod | ||
labels: | ||
name: pod | ||
audit.kubernetes.io/pod.allow-host-path-mount-proc-volume: "SomeReason" | ||
namespace: proc-mounted-allowed | ||
spec: | ||
containers: | ||
- name: container | ||
image: scratch | ||
volumeMounts: | ||
- mountPath: /host/proc | ||
name: proc-volume | ||
volumes: | ||
- name: proc-volume | ||
hostPath: | ||
path: /proc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
apiVersion: v1 | ||
kind: Pod | ||
metadata: | ||
name: pod | ||
namespace: proc-mounted | ||
spec: | ||
containers: | ||
- name: container | ||
image: scratch | ||
volumeMounts: | ||
- mountPath: /host/proc | ||
name: proc-volume | ||
volumes: | ||
- name: proc-volume | ||
hostPath: | ||
path: /proc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,120 @@ | ||||||
package mounts | ||||||
|
||||||
import ( | ||||||
"fmt" | ||||||
"github.com/Shopify/kubeaudit" | ||||||
"github.com/Shopify/kubeaudit/internal/k8s" | ||||||
"github.com/Shopify/kubeaudit/internal/override" | ||||||
"github.com/Shopify/kubeaudit/k8stypes" | ||||||
v1 "k8s.io/api/core/v1" | ||||||
"strings" | ||||||
) | ||||||
|
||||||
const Name = "mounts" | ||||||
|
||||||
const ( | ||||||
// SensitivePathsMounted occurs when a container has sensitive host paths mounted | ||||||
SensitivePathsMounted = "SensitivePathsMounted" | ||||||
) | ||||||
|
||||||
// DefaultSensitivePaths is the default list of sensitive mount paths (from Falco rule: https://github.com/falcosecurity/falco/blob/master/rules/k8s_audit_rules.yaml#L155) | ||||||
var DefaultSensitivePaths = []string{"/proc", "/var/run/docker.sock", "/", "/etc", "/root", "/var/run/crio/crio.sock", "/home/admin", "/var/lib/kubelet", "/var/lib/kubelet/pki", "/etc/kubernetes", "/etc/kubernetes/manifests"} | ||||||
|
||||||
const overrideLabelPrefix = "allow-host-path-mount-" | ||||||
|
||||||
const ( | ||||||
MountNameMetadataKey = "MountName" | ||||||
MountPathMetadataKey = "MountPath" | ||||||
MountReadOnlyMetadataKey = "MountReadOnly" | ||||||
) | ||||||
|
||||||
// SensitivePathMounts implements Auditable | ||||||
type SensitivePathMounts struct { | ||||||
sensitivePaths map[string]bool | ||||||
} | ||||||
|
||||||
func New(config Config) *SensitivePathMounts { | ||||||
paths := make(map[string]bool) | ||||||
for _, path := range config.GetSensitivePaths() { | ||||||
paths[path] = true | ||||||
} | ||||||
return &SensitivePathMounts{ | ||||||
sensitivePaths: paths, | ||||||
} | ||||||
} | ||||||
|
||||||
// Audit checks that the container does not have any sensitive host path | ||||||
func (sensitive *SensitivePathMounts) Audit(resource k8stypes.Resource, _ []k8stypes.Resource) ([]*kubeaudit.AuditResult, error) { | ||||||
var auditResults []*kubeaudit.AuditResult | ||||||
|
||||||
spec := k8s.GetPodSpec(resource) | ||||||
if spec == nil { | ||||||
return auditResults, nil | ||||||
} | ||||||
|
||||||
sensitiveVolumes := auditPodVolumes(spec, sensitive.sensitivePaths) | ||||||
|
||||||
if len(sensitiveVolumes) == 0 { | ||||||
return auditResults, nil | ||||||
} | ||||||
|
||||||
for _, container := range k8s.GetContainers(resource) { | ||||||
for _, auditResult := range auditContainer(container, sensitiveVolumes) { | ||||||
auditResult = override.ApplyOverride(auditResult, container.Name, resource, getOverrideLabel(auditResult.Metadata[MountNameMetadataKey])) | ||||||
if auditResult != nil { | ||||||
auditResults = append(auditResults, auditResult) | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
return auditResults, nil | ||||||
} | ||||||
|
||||||
func auditPodVolumes(podSpec *k8stypes.PodSpecV1, sensitivePaths map[string]bool) map[string]v1.Volume { | ||||||
if podSpec.Volumes == nil { | ||||||
return nil | ||||||
} | ||||||
|
||||||
found := make(map[string]v1.Volume) | ||||||
for _, volume := range podSpec.Volumes { | ||||||
if volume.HostPath == nil { | ||||||
continue | ||||||
} | ||||||
|
||||||
if _, ok := sensitivePaths[volume.HostPath.Path]; ok { | ||||||
found[volume.Name] = volume | ||||||
} | ||||||
} | ||||||
|
||||||
return found | ||||||
} | ||||||
|
||||||
func auditContainer(container *k8stypes.ContainerV1, sensitiveVolumes map[string]v1.Volume) []*kubeaudit.AuditResult { | ||||||
if container.VolumeMounts == nil { | ||||||
return nil | ||||||
} | ||||||
|
||||||
var auditResults []*kubeaudit.AuditResult | ||||||
|
||||||
for _, mount := range container.VolumeMounts { | ||||||
if volume, ok := sensitiveVolumes[mount.Name]; ok { | ||||||
auditResults = append(auditResults, &kubeaudit.AuditResult{ | ||||||
Name: SensitivePathsMounted, | ||||||
Severity: kubeaudit.Error, | ||||||
Message: fmt.Sprintf("Sensitive path mounted as volume: %s (hostPath: %s). It should be removed from the container's mounts list.", mount.Name, volume.HostPath.Path), | ||||||
Metadata: kubeaudit.Metadata{ | ||||||
"Container": container.Name, | ||||||
MountNameMetadataKey: mount.Name, | ||||||
MountPathMetadataKey: mount.MountPath, | ||||||
MountReadOnlyMetadataKey: fmt.Sprintf("%t", mount.ReadOnly), | ||||||
}, | ||||||
}) | ||||||
} | ||||||
} | ||||||
|
||||||
return auditResults | ||||||
} | ||||||
|
||||||
func getOverrideLabel(mountName string) string { | ||||||
return overrideLabelPrefix + strings.Replace(strings.ToLower(mountName), "_", "-", -1) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package mounts | ||
|
||
import ( | ||
"github.com/Shopify/kubeaudit/internal/override" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/Shopify/kubeaudit/internal/test" | ||
) | ||
|
||
const fixtureDir = "fixtures" | ||
|
||
func TestSensitivePathsMounted(t *testing.T) { | ||
cases := []struct { | ||
file string | ||
fixtureDir string | ||
expectedErrors []string | ||
}{ | ||
{"docker-sock-mounted.yml", fixtureDir, []string{SensitivePathsMounted}}, | ||
{"proc-mounted.yml", fixtureDir, []string{SensitivePathsMounted}}, | ||
{"proc-mounted-allowed.yml", fixtureDir, []string{override.GetOverriddenResultName(SensitivePathsMounted)}}, | ||
{"proc-mounted-allowed-multi-containers-multi-labels.yml", fixtureDir, []string{override.GetOverriddenResultName(SensitivePathsMounted)}}, | ||
{"proc-mounted-allowed-multi-containers-single-label.yml", fixtureDir, []string{SensitivePathsMounted, override.GetOverriddenResultName(SensitivePathsMounted)}}, | ||
} | ||
|
||
config := Config{SensitivePaths: DefaultSensitivePaths} | ||
jcbbc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
for _, tc := range cases { | ||
t.Run(tc.file, func(t *testing.T) { | ||
test.AuditManifest(t, tc.fixtureDir, tc.file, New(config), tc.expectedErrors) | ||
test.AuditLocal(t, tc.fixtureDir, tc.file, New(config), strings.Split(tc.file, ".")[0], tc.expectedErrors) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,46 @@ | ||||||
package commands | ||||||
|
||||||
import ( | ||||||
"bytes" | ||||||
"fmt" | ||||||
"github.com/Shopify/kubeaudit/auditors/mounts" | ||||||
"github.com/spf13/cobra" | ||||||
"strings" | ||||||
) | ||||||
|
||||||
var mountsConfig mounts.Config | ||||||
|
||||||
var mountsCmd = &cobra.Command{ | ||||||
Use: "mounts", | ||||||
Short: "Audit containers that mount sensitive paths", | ||||||
Long: fmt.Sprintf(`This command determines which containers mount sensitive host paths. If no paths list is provided, the following | ||||||
paths are used: | ||||||
%s | ||||||
|
||||||
A WARN result is generated when a container mounts one or more paths specified with the '--paths' argument. | ||||||
|
||||||
Example usage: | ||||||
kubeaudit mount --paths "%s"`, formatPathsList(), strings.Join(mounts.DefaultSensitivePaths[:3], ",")), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
Run: func(cmd *cobra.Command, args []string) { | ||||||
runAudit(mounts.New(mountsConfig))(cmd, args) | ||||||
}, | ||||||
} | ||||||
|
||||||
func init() { | ||||||
RootCmd.AddCommand(mountsCmd) | ||||||
setPathsFlags(mountsCmd) | ||||||
} | ||||||
|
||||||
func setPathsFlags(cmd *cobra.Command) { | ||||||
cmd.Flags().StringSliceVarP(&mountsConfig.SensitivePaths, "paths", "s", mounts.DefaultSensitivePaths, | ||||||
"List of sensitive paths that shouldn't be mounted") | ||||||
} | ||||||
|
||||||
func formatPathsList() string { | ||||||
var buffer bytes.Buffer | ||||||
for _, path := range mounts.DefaultSensitivePaths { | ||||||
buffer.WriteString("\n- ") | ||||||
buffer.WriteString(path) | ||||||
} | ||||||
return buffer.String() | ||||||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually can we add the volume name and host path as metadata as well? It might be useful for JSON output