Skip to content

Commit

Permalink
[FEATURE] Audit Pod Namespace Nix (#207)
Browse files Browse the repository at this point in the history
* add Nix checks

* Add autofix for namespaces

* update readme and add to auditConfig

* fix md anchors

* Add All tests

* deepCopy object only once and on return

* remove unreachable error and warn blocks

* Update cmd/namespaces.go

Co-Authored-By: nschhina <[email protected]>

* Update cmd/namespaces.go

Co-Authored-By: nschhina <[email protected]>

* Update cmd/namespaces.go

Co-Authored-By: nschhina <[email protected]>

* Armour->Armor and use switch instead of if statements
  • Loading branch information
nschhina authored Apr 16, 2019
1 parent 9f7b46e commit f3eeb33
Show file tree
Hide file tree
Showing 20 changed files with 466 additions and 18 deletions.
58 changes: 57 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ The manifest might end up a little too secure for the work it is supposed to do.
- [Audit Service Accounts](#sat)
- [Audit network policies](#netpol)
- [Audit resources](#resources)
- [Audit AppArmor](#apparmor)
- [Audit Seccomp](#seccomp)
- [Audit namespaces](#namespaces)

<a name="all" />

Expand Down Expand Up @@ -314,6 +317,8 @@ WARN[0000] CPU limit exceeded, it is set to 1 but it must not exceed 500m. Pleas
WARN[0000] Memory limit exceeded, it is set to 512Mi but it must not exceed 125Mi. Please adjust it!
```
<a name="apparmor" />
## Audit AppArmor
It checks that AppArmor is enabled for all containers by making sure the following annotation exists on the pod.
Expand All @@ -340,6 +345,8 @@ ERRO[0000] AppArmor disabled. Annotation=container.apparmor.security.beta.kubern
Container=myContainer KubeType=pod Name=myPod Namespace=myNamespace Reason=badval
```
<a name="seccomp" />
## Audit Seccomp
It checks that Seccomp is enabled for all containers by making sure one or both of the following annotations exists
Expand Down Expand Up @@ -379,6 +386,19 @@ ERRO[0000] Seccomp disabled for pod. Annotation=seccomp.security.alpha.kubernete
Name=myPod Namespace=myNamespace Reason=unconfined
```
<a name="namespaces" />
## Audit namespaces
`kubeaudit` will detect whether `hostNetwork`,`hostIPC` or `hostPID` is either set to `true` in `podSpec` for `Pod` workloads
```sh
kubeaudit namespaces
ERRO[0000] hostNetwork is set to true in podSpec, please set to false!
ERRO[0000] hostIPC is set to true in podSpec, please set to false!
ERRO[0000] hostPID is set to true in podSpec, please set to false!
```
<a name="labels" />
## Override Labels
Expand Down Expand Up @@ -444,6 +464,9 @@ metadata:
- [container.audit.kubernetes.io/\<container-name\>/allow-read-only-root-filesystem-false](#rootfs_label)
- [audit.kubernetes.io/\<namespace-name\>/allow-non-default-deny-egress-network-policy](#egress_label)
- [audit.kubernetes.io/\<namespace-name\>/allow-non-default-deny-ingress-network-policy](#ingress_label)
- [audit.kubernetes.io/pod/allow-namespace-host-network](#namespacenetwork_label)
- [audit.kubernetes.io/pod/allow-namespace-host-IPC](#namespaceipc_label)
- [audit.kubernetes.io/pod/allow-namespace-host-PID](#namespacepid_label)
<a name="allowpe_label"/>
Expand Down Expand Up @@ -555,6 +578,36 @@ audit.kubernetes.io/default/allow-non-default-deny-egress-network-policy: "Egres
WARN[0000] Allowed Namespace without a default deny egress NetworkPolicy KubeType=namespace Name=default Reason="Egress is allowed"
```
<a name="namespacenetwork_label"/>
### audit.kubernetes.io/pod/allow-namespace-host-network
```sh
audit.kubernetes.io/pod/allow-namespace-host-network: "hostNetwork is allowed"
WARN[0000] Allowed setting hostNetwork to true KubeType=pod Name=Pod Namespace=PodNamespace Reason="hostNetwork is allowed"
```
<a name="namespaceipc_label"/>
### audit.kubernetes.io/pod/allow-namespace-host-IPC
```sh
audit.kubernetes.io/pod/allow-namespace-host-IPC: "hostIPC is allowed"
WARN[0000] Allowed setting hostIPC to true KubeType=pod Name=Pod Namespace=PodNamespace Reason="hostIPC is allowed"
```
<a name="namespacepid_label"/>
### audit.kubernetes.io/pod/allow-namespace-host-PID
```sh
audit.kubernetes.io/pod/allow-namespace-host-PID: "hostPID is allowed"
WARN[0000] Allowed setting hostPID to true KubeType=pod Name=Pod Namespace=PodNamespace Reason="hostPID is allowed"
```
<a name="contribute" />
## Drop capabilities list
Expand Down Expand Up @@ -617,7 +670,10 @@ spec:
automount-service-account-token: deny # Set to `allow` to skip auditing potential vulnerability
read-only-root-filesystem-false: deny # Set to `allow` to skip auditing potential vulnerability
non-default-deny-ingress-network-policy: deny # Set to `allow` to skip auditing potential vulnerability
non-default-deny-egress-network-policy: deny # Set to `allow` to skip auditing potential vulnerability
non-default-deny-egress-network-policy: deny # Set to `allow` to skip auditing potential vulnerability
namespace-host-network: deny # Set to `allow` to skip auditing potential vulnerability
namespace-host-IPC: deny # Set to `allow` to skip auditing potential vulnerability
namespace-host-PID: deny # Set to `allow` to skip auditing potential vulnerability
```
<a name="contribute" />
Expand Down
2 changes: 1 addition & 1 deletion cmd/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
var allAuditFunctions = []interface{}{
auditAllowPrivilegeEscalation, auditReadOnlyRootFS, auditRunAsNonRoot,
auditAutomountServiceAccountToken, auditPrivileged, auditCapabilities,
auditLimits, auditImages, auditAppArmor, auditSeccomp, auditNetworkPolicies,
auditLimits, auditImages, auditAppArmor, auditSeccomp, auditNetworkPolicies, auditNamespaces,
}

var auditAllCmd = &cobra.Command{
Expand Down
4 changes: 3 additions & 1 deletion cmd/autofix_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func getAuditFunctions() []interface{} {
return []interface{}{
auditAllowPrivilegeEscalation, auditReadOnlyRootFS, auditRunAsNonRoot,
auditAutomountServiceAccountToken, auditPrivileged, auditCapabilities,
auditAppArmor, auditSeccomp, auditNetworkPolicies,
auditAppArmor, auditSeccomp, auditNetworkPolicies, auditNamespaces,
}
}

Expand Down Expand Up @@ -48,6 +48,8 @@ func fixPotentialSecurityIssue(resource Resource, result Result) Resource {
resource = fixSeccomp(resource)
case ErrorMissingDefaultDenyIngressNetworkPolicy, ErrorMissingDefaultDenyEgressNetworkPolicy, ErrorMissingDefaultDenyIngressAndEgressNetworkPolicy:
resource = fixNetworkPolicy(resource, occurrence)
case ErrorNamespaceHostIPCTrue, ErrorNamespaceHostNetworkTrue, ErrorNamespaceHostPIDTrue:
resource = fixNamespace(&result, resource)
}
}
return resource
Expand Down
30 changes: 17 additions & 13 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,29 +48,33 @@ type KubeauditConfigOverrides struct {
ReadOnlyRootFilesystemFalse string `yaml:"read-only-root-filesystem-false"`
NonDefaultDenyIngressNetworkPolicy string `yaml:"non-default-deny-ingress-network-policy"`
NonDefaultDenyEgressNetworkPolicy string `yaml:"non-default-deny-egress-network-policy"`
HostNetwork string `yaml:"namespace-host-network"`
HostPID string `yaml:"namespace-host-PID"`
HostIPC string `yaml:"namespace-host-IPC"`
}

func mapOverridesToStructFields(label string) string {
if label == "allow-privilege-escalation" {
switch label {
case "allow-privilege-escalation":
return "PrivilegeEscalation"
}
if label == "allow-privileged" {
case "allow-privileged":
return "Privileged"
}
if label == "allow-run-as-root" {
case "allow-run-as-root":
return "RunAsRoot"
}
if label == "allow-automount-service-account-token" {
case "allow-automount-service-account-token":
return "AutomountServiceAccountToken"
}
if label == "allow-read-only-root-filesystem-false" {
case "allow-read-only-root-filesystem-false":
return "ReadOnlyRootFilesystemFalse"
}
if label == "allow-non-default-deny-egress-network-policy" {
case "allow-non-default-deny-egress-network-policy":
return "NonDefaultDenyEgressNetworkPolicy"
}
if label == "allow-non-default-deny-ingress-network-policy" {
case "allow-non-default-deny-ingress-network-policy":
return "NonDefaultDenyIngressNetworkPolicy"
case "allow-namespace-host-network":
return "HostNetwork"
case "allow-namespace-host-IPC":
return "HostIPC"
case "allow-namespace-host-PID":
return "HostPID"
}
return ""
}
12 changes: 12 additions & 0 deletions cmd/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,18 @@ const (
ErrorMissingDefaultDenyIngressNetworkPolicy
// ErrorMissingDefaultDenyIngressNetworkPolicyAllowed occurs when a namespace is missing a default deny ingress NetworkPolicy but it's allowed
ErrorMissingDefaultDenyIngressNetworkPolicyAllowed
// ErrorNamespaceHostIPCTrue occurs when a hostIPC is set to true in PodSpec
ErrorNamespaceHostIPCTrue
// ErrorNamespaceHostIPCTrueAllowed occurs when a hostIPC is set to true in PodSpec but it's allowed
ErrorNamespaceHostIPCTrueAllowed
// ErrorNamespaceHostIPCTrue occurs when a hostNetwork is set to true in PodSpec
ErrorNamespaceHostNetworkTrue
// ErrorNamespaceHostNetworkTrueAllowed occurs when a hostNetwork is set to true in PodSpec but it's allowed
ErrorNamespaceHostNetworkTrueAllowed
// ErrorNamespaceHostIPCTrue occurs when a hostPID is set to true in PodSpec
ErrorNamespaceHostPIDTrue
// ErrorNamespaceHostPIDTrueAllowed occurs when a hostPID is set to true in PodSpec but it's allowed
ErrorNamespaceHostPIDTrueAllowed
// InfoDefaultDenyNetworkPolicyExists occurs when a namespace has a default deny NetworkPolicy
InfoDefaultDenyNetworkPolicyExists
// WarningAllowAllIngressNetworkPolicyExists occurs when a namespace has an allow all ingress NetworkPolicy
Expand Down
128 changes: 128 additions & 0 deletions cmd/namespaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package cmd

import (
"github.com/spf13/cobra"
)

// Checks the PodSecurityContext for NIX
func checkNamespaces(podSpec PodSpecV1, result *Result) {
if labelExists, reason := getPodOverrideLabelReason(result, "allow-namespace-host-network"); labelExists {
if podSpec.HostNetwork {
occ := Occurrence{
podHost: podSpec.Hostname,
id: ErrorNamespaceHostNetworkTrueAllowed,
kind: Warn,
message: "Allowed setting hostNetwork to true",
metadata: Metadata{"Reason": prettifyReason(reason)},
}
result.Occurrences = append(result.Occurrences, occ)
} else {
occ := Occurrence{
podHost: podSpec.Hostname,
id: ErrorMisconfiguredKubeauditAllow,
kind: Warn,
message: "Allowed setting hostNetwork to true, but it is set to false",
metadata: Metadata{"Reason": prettifyReason(reason)},
}
result.Occurrences = append(result.Occurrences, occ)
}
} else if podSpec.HostNetwork {
occ := Occurrence{
podHost: podSpec.Hostname,
id: ErrorNamespaceHostNetworkTrue,
kind: Error,
message: "hostNetwork is set to true in podSpec, please set to false!",
}
result.Occurrences = append(result.Occurrences, occ)
}
if labelExists, reason := getPodOverrideLabelReason(result, "allow-namespace-host-IPC"); labelExists {
if podSpec.HostIPC {
occ := Occurrence{
podHost: podSpec.Hostname,
id: ErrorNamespaceHostIPCTrueAllowed,
kind: Warn,
message: "Allowed setting hostIPC to true",
metadata: Metadata{"Reason": prettifyReason(reason)},
}
result.Occurrences = append(result.Occurrences, occ)
} else {
occ := Occurrence{
podHost: podSpec.Hostname,
id: ErrorMisconfiguredKubeauditAllow,
kind: Warn,
message: "Allowed setting hostIPC to true, but it is set to false",
metadata: Metadata{"Reason": prettifyReason(reason)},
}
result.Occurrences = append(result.Occurrences, occ)
}
} else if podSpec.HostIPC {
occ := Occurrence{
podHost: podSpec.Hostname,
id: ErrorNamespaceHostIPCTrue,
kind: Error,
message: "hostIPC is set to true in podSpec, please set to false!",
}
result.Occurrences = append(result.Occurrences, occ)
}
if labelExists, reason := getPodOverrideLabelReason(result, "allow-namespace-host-PID"); labelExists {
if podSpec.HostPID {
occ := Occurrence{
podHost: podSpec.Hostname,
id: ErrorNamespaceHostPIDTrueAllowed,
kind: Warn,
message: "Allowed setting hostPID to true",
metadata: Metadata{"Reason": prettifyReason(reason)},
}
result.Occurrences = append(result.Occurrences, occ)
} else {
occ := Occurrence{
podHost: podSpec.Hostname,
id: ErrorMisconfiguredKubeauditAllow,
kind: Warn,
message: "Allowed setting hostPID to true, but it is set to false",
metadata: Metadata{"Reason": prettifyReason(reason)},
}
result.Occurrences = append(result.Occurrences, occ)
}
} else if podSpec.HostPID {
occ := Occurrence{
podHost: podSpec.Hostname,
id: ErrorNamespaceHostPIDTrue,
kind: Error,
message: "hostPID is set to true in podSpec, please set to false!",
}
result.Occurrences = append(result.Occurrences, occ)
}
return
}

func auditNamespaces(resource Resource) (results []Result) {
switch kubeType := resource.(type) {
case *PodV1:
podSpec := kubeType.Spec
result, _, _ := newResultFromResource(resource)
checkNamespaces(podSpec, result)
if len(result.Occurrences) > 0 {
results = append(results, *result)
}
}
return
}

// runAsNonRootCmd represents the runAsNonRoot command
var namespacesCmd = &cobra.Command{
Use: "namespaces",
Short: "Audit Pods for hostNetwork, hostIPC and hostPID",
Long: `This command determines which pods in a kubernetes cluster
are running with hostNetwork, hostIPC or hostPID set to true.
A PASS is given when a pod has hostNetwork, hostIPC and hostPID set to false or not set
A FAIL is generated when a pod has at least one of hostNetwork, hostIPC or hostPID set to true
kubeaudit namespaces`,
Run: runAudit(auditNamespaces),
}

func init() {
RootCmd.AddCommand(namespacesCmd)
}
18 changes: 18 additions & 0 deletions cmd/namespaces_fixes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package cmd

func fixNamespace(result *Result, resource Resource) Resource {
switch kubeType := resource.(type) {
case *PodV1:
if labelExists, _ := getPodOverrideLabelReason(result, "allow-namespace-host-network"); !labelExists {
kubeType.Spec.HostNetwork = false
}
if labelExists, _ := getPodOverrideLabelReason(result, "allow-namespace-host-PID"); !labelExists {
kubeType.Spec.HostPID = false
}
if labelExists, _ := getPodOverrideLabelReason(result, "allow-namespace-host-IPC"); !labelExists {
kubeType.Spec.HostIPC = false
}
return kubeType.DeepCopyObject()
}
return resource
}
Loading

0 comments on commit f3eeb33

Please sign in to comment.