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

Commit

Permalink
Pretty Print (#287)
Browse files Browse the repository at this point in the history
  • Loading branch information
genevieveluyt authored Aug 27, 2020
1 parent d51341c commit 8b87bc8
Show file tree
Hide file tree
Showing 33 changed files with 943 additions and 234 deletions.
78 changes: 59 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ The rest of this README will focus on how to use kubeaudit as a command line too

* [Installation](#installation)
* [Quick Start](#quick-start)
* [Audit Results](#audit-results)
* [Commands](#commands)
* [Configuration File](#configuration-file)
* [Override Errors](#override-errors)
Expand All @@ -41,7 +42,7 @@ Kubeaudit has official releases that are blessed and stable:

### DIY build

Master will have newer features than the stable releases. If you need a newer
Master may have newer features than the stable releases. If you need a newer
feature not yet included in a release you can do the following to get
kubeaudit:

Expand Down Expand Up @@ -91,18 +92,32 @@ kubeaudit all -f "/path/to/manifest.yml"

Example output:
```
$ kubeaudit all -f auditors/all/fixtures/audit_all_v1.yml
ERRO[0000] AppArmor annotation missing. The annotation 'container.apparmor.security.beta.kubernetes.io/fakeContainerSC' should be added. AuditResultName=AppArmorAnnotationMissing Container=fakeContainerSC MissingAnnotation=container.apparmor.security.beta.kubernetes.io/fakeContainerSC
ERRO[0000] Default serviceAccount with token mounted. automountServiceAccountToken should be set to 'false' or a non-default service account should be used. AuditResultName=AutomountServiceAccountTokenTrueAndDefaultSA
WARN[0000] Image tag is missing. AuditResultName=ImageTagMissing Container=fakeContainerSC
WARN[0000] Resource limits not set. AuditResultName=LimitsNotSet Container=fakeContainerSC
ERRO[0000] runAsNonRoot is not set in container SecurityContext nor the PodSecurityContext. It should be set to 'true' in at least one of the two. AuditResultName=RunAsNonRootPSCNilCSCNil Container=fakeContainerSC
ERRO[0000] allowPrivilegeEscalation not set which allows privilege escalation. It should be set to 'false'. AuditResultName=AllowPrivilegeEscalationNil Container=fakeContainerSC
WARN[0000] privileged is not set in container SecurityContext. Privileged defaults to 'false' but it should be explicitly set to 'false'. AuditResultName=PrivilegedNil Container=fakeContainerSC
ERRO[0000] readOnlyRootFilesystem is not set in container SecurityContext. It should be set to 'true'. AuditResultName=ReadOnlyRootFilesystemNil Container=fakeContainerSC
ERRO[0000] Seccomp annotation is missing. The annotation seccomp.security.alpha.kubernetes.io/pod: runtime/default should be added. AuditResultName=SeccompAnnotationMissing MissingAnnotation=seccomp.security.alpha.kubernetes.io/pod
ERRO[0000] Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities. AuditResultName=CapabilityNotDropped Capability=AUDIT_WRITE Container=fakeContainerSC
ERRO[0000] Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities. AuditResultName=CapabilityNotDropped Capability=CHOWN Container=fakeContainerSC
$ kubeaudit all -f "internal/test/fixtures/all_resources/deployment-apps-v1.yml"
---------------- Results for ---------------
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment
namespace: deployment-apps-v1
--------------------------------------------
-- [error] AppArmorAnnotationMissing
Message: AppArmor annotation missing. The annotation 'container.apparmor.security.beta.kubernetes.io/container' should be added.
Metadata:
Container: container
MissingAnnotation: container.apparmor.security.beta.kubernetes.io/container
-- [error] AutomountServiceAccountTokenTrueAndDefaultSA
Message: Default service account with token mounted. automountServiceAccountToken should be set to 'false' or a non-default service account should be used.
-- [error] CapabilityNotDropped
Message: Capability not dropped. Ideally, the capability drop list should include the single capability 'ALL' which drops all capabilities.
Metadata:
Container: container
Capability: AUDIT_WRITE
...
```

Expand Down Expand Up @@ -137,6 +152,16 @@ Kubeaudit can detect if it is running within a container in a cluster. If so, it
kubeaudit all
```

## Audit Results

Kubeaudit produces results with three levels of severity:

`Error`: A security issue or invalid kubernetes configuration
`Warning`: A best practice recommendation
`Info`: Informational, no action required. This includes results that are [overridden](#override-errors)

The minimum severity level can be set using the `--minSeverity/-m` flag. See [Global Flags](#global-flags) for a more detailed description.

## Commands

| Command | Description | Documentation |
Expand Down Expand Up @@ -168,19 +193,19 @@ Auditors can also be run individually.

| Short | Long | Description |
| :------ | :------------- | :-------------------------------------------------------------------------------------------------- |
| -j | --json | Output audit results in JSON |
| | --format | The output format to use (one of "pretty", "logrus", "json") (default is "pretty") |
| -c | --kubeconfig | Path to local Kubernetes config file. Only used in local mode (default is `$HOME/.kube/config`) |
| -f | --manifest | Path to the yaml configuration to audit. Only used in manifest mode. |
| -n | --namespace | Only audit resources in the specified namespace. Only used in cluster mode. |
| -m | --minseverity | Set the lowest severity level to report (one of "ERROR", "WARN", "INFO") (default "INFO") |
| -n | --namespace | Only audit resources in the specified namespace. Not currently supported in manifest mode. |
| -m | --minseverity | Set the lowest severity level to report (one of "error", "warning", "info") (default "info") |

## Configuration File

Kubeaudit can be used with a configuration file instead of flags. See the [all command](docs/all.md).

## Override Errors

Security issues can be ignored for specific containers or pods by adding override labels. This means the auditor will produce `WARN` results instead of `ERROR` results. The labels are documented in each auditor's documentation, but the general format for auditors that support overrides is as follows:
Security issues can be ignored for specific containers or pods by adding override labels. This means the auditor will produce `info` results instead of `error` results and the audit result name will have `Allowed` appended to it. The labels are documented in each auditor's documentation, but the general format for auditors that support overrides is as follows:

An override label consists of a `key` and a `value`.

Expand All @@ -195,9 +220,24 @@ container.audit.kubernetes.io/[container name].[override identifier]
audit.kubernetes.io/pod.[override identifier]
```

If the `value` is set to a non-empty string, it will be displayed in the `WARN` result as the `OverrideReason`:
If the `value` is set to a non-empty string, it will be displayed in the `info` result as the `OverrideReason`:
```
WARN[0000] ... AuditResultName=DockerSocketMounted OverrideReason=AppNeedsAccessToDocker
$ kubeaudit asat -f "auditors/asat/fixtures/service-account-token-true-allowed.yml"
---------------- Results for ---------------
apiVersion: v1
kind: ReplicationController
metadata:
name: replicationcontroller
namespace: service-account-token-true-allowed
--------------------------------------------
-- [info] AutomountServiceAccountTokenTrueAndDefaultSAAllowed
Message: Audit result overridden: Default service account with token mounted. automountServiceAccountToken should be set to 'false' or a non-default service account should be used.
Metadata:
OverrideReason: SomeReason
```

As per Kubernetes spec, `value` must be 63 characters or less and must be empty or begin and end with an alphanumeric character (`[a-z0-9A-Z]`) with dashes (`-`), underscores (`_`), dots (`.`), and alphanumerics between.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ spec:
metadata:
labels:
name: replicationcontroller
audit.kubernetes.io/pod.allow-automount-service-account-token: "True"
audit.kubernetes.io/pod.allow-automount-service-account-token: "SomeReason"
spec:
automountServiceAccountToken: false
containers:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ spec:
metadata:
labels:
name: replicationcontroller
audit.kubernetes.io/pod.allow-automount-service-account-token: "True"
audit.kubernetes.io/pod.allow-automount-service-account-token: "SomeReason"
spec:
automountServiceAccountToken: true
containers:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ spec:
metadata:
labels:
name: deployment
audit.kubernetes.io/pod.allow-capability-chown: "True"
container.audit.kubernetes.io/container1.allow-capability-chown: "True"
audit.kubernetes.io/pod.allow-capability-chown: "SomeReason"
container.audit.kubernetes.io/container1.allow-capability-chown: "SomeReason"
container.audit.kubernetes.io/container1.allow-capability-sys-time: "SomeReason"
container.audit.kubernetes.io/container2.allow-capability-sys-time: "SomeReason"
spec:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ spec:
metadata:
labels:
name: deployment
container.audit.kubernetes.io/container1.allow-capability-chown: "True"
container.audit.kubernetes.io/container1.allow-capability-chown: "SomeReason"
container.audit.kubernetes.io/container1.allow-capability-sys-time: "SomeReason"
spec:
containers:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ spec:
metadata:
labels:
name: deployment
audit.kubernetes.io/pod.allow-capability-chown: "True"
audit.kubernetes.io/pod.allow-capability-chown: "SomeReason"
audit.kubernetes.io/pod.allow-capability-sys-time: "SomeReason"
spec:
containers:
Expand Down
24 changes: 15 additions & 9 deletions auditors/hostns/hostns.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,18 @@ func (a *HostNamespaces) Audit(resource k8stypes.Resource, _ []k8stypes.Resource

func auditHostNetwork(podSpec *k8stypes.PodSpecV1) *kubeaudit.AuditResult {
if podSpec.HostNetwork {
metadata := kubeaudit.Metadata{}
if podSpec.Hostname != "" {
metadata["PodHost"] = podSpec.Hostname
}
return &kubeaudit.AuditResult{
Name: NamespaceHostNetworkTrue,
Severity: kubeaudit.Error,
Message: "hostNetwork is set to 'true' in PodSpec. It should be set to 'false'.",
PendingFix: &fixHostNetworkTrue{
podSpec: podSpec,
},
Metadata: kubeaudit.Metadata{
"PodHost": podSpec.Hostname,
},
Metadata: metadata,
}
}

Expand All @@ -76,16 +78,18 @@ func auditHostNetwork(podSpec *k8stypes.PodSpecV1) *kubeaudit.AuditResult {

func auditHostIPC(podSpec *k8stypes.PodSpecV1) *kubeaudit.AuditResult {
if podSpec.HostIPC {
metadata := kubeaudit.Metadata{}
if podSpec.Hostname != "" {
metadata["PodHost"] = podSpec.Hostname
}
return &kubeaudit.AuditResult{
Name: NamespaceHostIPCTrue,
Severity: kubeaudit.Error,
Message: "hostIPC is set to 'true' in PodSpec. It should be set to 'false'.",
PendingFix: &fixHostIPCTrue{
podSpec: podSpec,
},
Metadata: kubeaudit.Metadata{
"PodHost": podSpec.Hostname,
},
Metadata: metadata,
}
}

Expand All @@ -94,16 +98,18 @@ func auditHostIPC(podSpec *k8stypes.PodSpecV1) *kubeaudit.AuditResult {

func auditHostPID(podSpec *k8stypes.PodSpecV1) *kubeaudit.AuditResult {
if podSpec.HostPID {
metadata := kubeaudit.Metadata{}
if podSpec.Hostname != "" {
metadata["PodHost"] = podSpec.Hostname
}
return &kubeaudit.AuditResult{
Name: NamespaceHostPIDTrue,
Severity: kubeaudit.Error,
Message: "hostPID is set to 'true' in PodSpec. It should be set to 'false'.",
PendingFix: &fixHostPIDTrue{
podSpec: podSpec,
},
Metadata: kubeaudit.Metadata{
"PodHost": podSpec.Hostname,
},
Metadata: metadata,
}
}

Expand Down
2 changes: 1 addition & 1 deletion auditors/limits/limits.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func (limits *Limits) auditContainer(container *k8stypes.ContainerV1) (auditResu
Metadata: kubeaudit.Metadata{
"Container": container.Name,
"ContainerCpuLimit": cpu,
"maxCPU": maxCPU,
"MaxCPU": maxCPU,
},
}
auditResults = append(auditResults, auditResult)
Expand Down
30 changes: 18 additions & 12 deletions cmd/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package commands

import (
"os"
"strings"

log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
Expand All @@ -16,7 +17,7 @@ import (
var rootConfig rootFlags

type rootFlags struct {
json bool
format string
kubeConfig string
manifest string
namespace string
Expand Down Expand Up @@ -48,31 +49,36 @@ func Execute() {

func init() {
RootCmd.PersistentFlags().StringVarP(&rootConfig.kubeConfig, "kubeconfig", "c", "", "Path to local Kubernetes config file. Only used in local mode (default is $HOME/.kube/config)")
RootCmd.PersistentFlags().StringVarP(&rootConfig.minSeverity, "minseverity", "m", "INFO", "Set the lowest severity level to report (one of \"ERROR\", \"WARN\", \"INFO\")")
RootCmd.PersistentFlags().BoolVarP(&rootConfig.json, "json", "j", false, "Output audit results in JSON")
RootCmd.PersistentFlags().StringVarP(&rootConfig.minSeverity, "minseverity", "m", "info", "Set the lowest severity level to report (one of \"error\", \"warning\", \"info\")")
RootCmd.PersistentFlags().StringVarP(&rootConfig.format, "format", "p", "pretty", "The output format to use (one of \"pretty\", \"logrus\", \"json\")")
RootCmd.PersistentFlags().StringVarP(&rootConfig.namespace, "namespace", "n", apiv1.NamespaceAll, "Only audit resources in the specified namespace. Not currently supported in manifest mode.")
RootCmd.PersistentFlags().StringVarP(&rootConfig.manifest, "manifest", "f", "", "Path to the yaml configuration to audit. Only used in manifest mode.")
}

// KubeauditLogLevels represents an enum for the supported log levels.
var KubeauditLogLevels = map[string]int{
"ERROR": kubeaudit.Error,
"WARN": kubeaudit.Warn,
"INFO": kubeaudit.Info,
var KubeauditLogLevels = map[string]kubeaudit.SeverityLevel{
"error": kubeaudit.Error,
"warn": kubeaudit.Warn,
"warning": kubeaudit.Warn,
"info": kubeaudit.Info,
}

func runAudit(auditable ...kubeaudit.Auditable) func(cmd *cobra.Command, args []string) {
return func(cmd *cobra.Command, args []string) {
report := getReport(auditable...)

minSeverity := KubeauditLogLevels[rootConfig.minSeverity]
printOptions := []kubeaudit.PrintOption{
kubeaudit.WithMinSeverity(KubeauditLogLevels[strings.ToLower(rootConfig.minSeverity)]),
}

var formatter log.Formatter
if rootConfig.json {
formatter = &log.JSONFormatter{}
switch rootConfig.format {
case "json":
printOptions = append(printOptions, kubeaudit.WithFormatter(&log.JSONFormatter{}))
case "logrus":
printOptions = append(printOptions, kubeaudit.WithFormatter(&log.TextFormatter{}))
}

report.PrintResults(os.Stdout, minSeverity, formatter)
report.PrintResults(printOptions...)

if report.HasErrors() {
os.Exit(2)
Expand Down
Loading

0 comments on commit 8b87bc8

Please sign in to comment.