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
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds a new 'mounts' command to audit sensitive host paths mounts (#322)
* Add a new 'mounts' command to audit sensitive host paths mounts
- Loading branch information
Showing
15 changed files
with
609 additions
and
1 deletion.
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:"denyPathsList"` | ||
} | ||
|
||
func (config *Config) GetSensitivePaths() []string { | ||
if config == nil || len(config.SensitivePaths) == 0 { | ||
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,123 @@ | ||
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" | ||
) | ||
|
||
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" | ||
MountVolumeNameKey = "MountVolume" | ||
MountVolumeHostPathKey = "MountVolumeHostPath" | ||
) | ||
|
||
// 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), | ||
MountVolumeNameKey: volume.Name, | ||
MountVolumeHostPathKey: volume.HostPath.Path, | ||
}, | ||
}) | ||
} | ||
} | ||
|
||
return auditResults | ||
} | ||
|
||
func getOverrideLabel(mountName string) string { | ||
return overrideLabelPrefix + mountName | ||
} |
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{} | ||
|
||
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,48 @@ | ||
package commands | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"github.com/Shopify/kubeaudit/auditors/mounts" | ||
"github.com/spf13/cobra" | ||
"strings" | ||
) | ||
|
||
const sensitivePathsFlagName = "denyPathsList" | ||
|
||
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 '--denyPathsList' argument. | ||
Example usage: | ||
kubeaudit mounts --denyPathsList "%s"`, formatPathsList(), strings.Join(mounts.DefaultSensitivePaths[:3], ",")), | ||
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, sensitivePathsFlagName, "d", 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
Oops, something went wrong.