1
0
mirror of https://github.com/aquasecurity/kube-bench.git synced 2025-05-30 04:38:48 +00:00

Merge branch 'master' into roadmap

This commit is contained in:
Liz Rice 2017-08-15 20:12:12 +01:00 committed by GitHub
commit f48ad5eb54
9 changed files with 428 additions and 203 deletions

View File

@ -596,10 +596,25 @@ groups:
checks: checks:
- id: 1.4.1 - id: 1.4.1
text: "Ensure that the apiserver file permissions are set to 644 or more restrictive (Scored)" text: "Ensure that the apiserver file permissions are set to 644 or more restrictive (Scored)"
audit: "if test -e $apiserverconf; then stat -c %a $apiserverconf; fi" # audit: "/bin/bash -c 'if test -e $apiserverconf; then stat -c %a $apiserverconf; fi'"
audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %a $apiserverconf; fi'"
tests: tests:
bin_op: or
test_items: test_items:
- flag: "644" - flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true set: true
remediation: "Run the below command (based on the file location on your system) on the master node. remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chmod 644 $apiserverconf" \nFor example, chmod 644 $apiserverconf"
@ -607,10 +622,13 @@ groups:
- id: 1.4.2 - id: 1.4.2
text: "Ensure that the apiserver file ownership is set to root:root (Scored)" text: "Ensure that the apiserver file ownership is set to root:root (Scored)"
audit: "if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi" audit: "/bin/sh -c 'if test -e $apiserverconf; then stat -c %U:%G $apiserverconf; fi'"
tests: tests:
test_items: test_items:
- flag: "root:root" - flag: "root:root"
compare:
op: eq
value: "root:root"
set: true set: true
remediation: "Run the below command (based on the file location on your system) on the master node. remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chown root:root $apiserverconf" \nFor example, chown root:root $apiserverconf"
@ -618,10 +636,24 @@ groups:
- id: 1.4.3 - id: 1.4.3
text: "Ensure that the config file permissions are set to 644 or more restrictive (Scored)" text: "Ensure that the config file permissions are set to 644 or more restrictive (Scored)"
audit: "if test -e $config; then stat -c %a $config; fi" audit: "/bin/sh -c 'if test -e $config; then stat -c %a $config; fi'"
tests: tests:
bin_op: or
test_items: test_items:
- flag: "644" - flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true set: true
remediation: "Run the below command (based on the file location on your system) on the master node. remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chmod 644 $config" \nFor example, chmod 644 $config"
@ -629,10 +661,13 @@ groups:
- id: 1.4.4 - id: 1.4.4
text: "Ensure that the config file ownership is set to root:root (Scored)" text: "Ensure that the config file ownership is set to root:root (Scored)"
audit: "if test -e $config; then stat -c %U:%G $config; fi" audit: "/bin/sh -c 'if test -e $config; then stat -c %U:%G $config; fi'"
tests: tests:
test_items: test_items:
- flag: "root:root" - flag: "root:root"
compare:
op: eq
value: "root:root"
set: true set: true
remediation: "Run the below command (based on the file location on your system) on the master node. remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chown root:root $config" \nFor example, chown root:root $config"
@ -640,10 +675,24 @@ groups:
- id: 1.4.5 - id: 1.4.5
text: "Ensure that the scheduler file permissions are set to 644 or more restrictive (Scored)" text: "Ensure that the scheduler file permissions are set to 644 or more restrictive (Scored)"
audit: "if test -e $schedulerconf; then stat -c %a $schedulerconf; fi" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %a $schedulerconf; fi'"
tests: tests:
bin_op: or
test_items: test_items:
- flag: "644" - flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true set: true
remediation: "Run the below command (based on the file location on your system) on the master node. remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chmod 644 $schedulerconf" \nFor example, chmod 644 $schedulerconf"
@ -651,10 +700,13 @@ groups:
- id: 1.4.6 - id: 1.4.6
text: "Ensure that the scheduler file ownership is set to root:root (Scored)" text: "Ensure that the scheduler file ownership is set to root:root (Scored)"
audit: "if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi" audit: "/bin/sh -c 'if test -e $schedulerconf; then stat -c %U:%G $schedulerconf; fi'"
tests: tests:
test_items: test_items:
- flag: "root:root" - flag: "root:root"
compare:
op: eq
value: "root:root"
set: true set: true
remediation: "Run the below command (based on the file location on your system) on the master node. remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chown root:root $schedulerconf" \nFor example, chown root:root $schedulerconf"
@ -662,10 +714,24 @@ groups:
- id: 1.4.7 - id: 1.4.7
text: "Ensure that the etcd.conf file permissions are set to 644 or more restrictive (Scored)" text: "Ensure that the etcd.conf file permissions are set to 644 or more restrictive (Scored)"
audit: "if test -e $etcdconf; then stat -c %a $etcdconf; fi" audit: "/bin/sh -c 'if test -e $etcdconf; then stat -c %a $etcdconf; fi'"
tests: tests:
bin_op: or
test_items: test_items:
- flag: "644" - flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true set: true
remediation: "Run the below command (based on the file location on your system) on the master node. remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chmod 644 $etcdconf" \nFor example, chmod 644 $etcdconf"
@ -673,10 +739,13 @@ groups:
- id: 1.4.8 - id: 1.4.8
text: "Ensure that the etcd.conf file ownership is set to root:root (Scored)" text: "Ensure that the etcd.conf file ownership is set to root:root (Scored)"
audit: "if test -e $etcdconf; then stat -c %U:%G $etcdconf; fi" audit: "/bin/sh -c 'if test -e $etcdconf; then stat -c %U:%G $etcdconf; fi'"
tests: tests:
test_items: test_items:
- flag: "root:root" - flag: "root:root"
compare:
op: eq
value: "root:root"
set: true set: true
remediation: "Run the below command (based on the file location on your system) on the master node. remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chown root:root $etcdconf" \nFor example, chown root:root $etcdconf"
@ -684,10 +753,24 @@ groups:
- id: 1.4.9 - id: 1.4.9
text: "Ensure that the flanneld file permissions are set to 644 or more restrictive (Scored)" text: "Ensure that the flanneld file permissions are set to 644 or more restrictive (Scored)"
audit: "if test -e $flanneldconf; then stat -c %a $flanneldconf; fi" audit: "/bin/sh -c 'if test -e $flanneldconf; then stat -c %a $flanneldconf; fi'"
tests: tests:
bin_op: or
test_items: test_items:
- flag: "644" - flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true set: true
remediation: "Run the below command (based on the file location on your system) on the master node. remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chmod 644 $flanneldconf" \nFor example, chmod 644 $flanneldconf"
@ -695,10 +778,13 @@ groups:
- id: 1.4.10 - id: 1.4.10
text: "Ensure that the flanneld file ownership is set to root:root (Scored)" text: "Ensure that the flanneld file ownership is set to root:root (Scored)"
audit: "if test -e $flanneldconf; then stat -c %U:%G $flanneldconf; fi" audit: "/bin/sh -c 'if test -e $flanneldconf; then stat -c %U:%G $flanneldconf; fi'"
tests: tests:
test_items: test_items:
- flag: "root:root" - flag: "root:root"
compare:
op: eq
value: "root:root"
set: true set: true
remediation: "Run the below command (based on the file location on your system) on the master node. remediation: "Run the below command (based on the file location on your system) on the master node.
\nFor example, chown root:root $flanneldconf" \nFor example, chown root:root $flanneldconf"
@ -710,6 +796,9 @@ groups:
tests: tests:
test_items: test_items:
- flag: "700" - flag: "700"
compare:
op: eq
value: "700"
set: true set: true
remediation: "On the etcd server node, get the etcd data directory, passed as an argument --data-dir , remediation: "On the etcd server node, get the etcd data directory, passed as an argument --data-dir ,
from the below command:\n from the below command:\n

View File

@ -221,10 +221,24 @@ groups:
checks: checks:
- id: 2.2.1 - id: 2.2.1
text: "Ensure that the config file permissions are set to 644 or more restrictive (Scored)" text: "Ensure that the config file permissions are set to 644 or more restrictive (Scored)"
audit: "if test -e $config; then stat -c %a $config; fi" audit: "/bin/sh -c 'if test -e $config; then stat -c %a $config; fi'"
tests: tests:
bin_op: or
test_items: test_items:
- flag: "644" - flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true set: true
remediation: "Run the below command (based on the file location on your system) on the each worker node. remediation: "Run the below command (based on the file location on your system) on the each worker node.
\nFor example, chmod 644 $config" \nFor example, chmod 644 $config"
@ -232,10 +246,13 @@ groups:
- id: 2.2.2 - id: 2.2.2
text: "Ensure that the config file ownership is set to root:root (Scored)" text: "Ensure that the config file ownership is set to root:root (Scored)"
audit: "if test -e $config; then stat -c %U:%G $config; fi" audit: "/bin/sh -c 'if test -e $config; then stat -c %U:%G $config; fi'"
tests: tests:
test_items: test_items:
- flag: "root:root" - flag: "root:root"
compare:
op: eq
value: root:root
set: true set: true
remediation: "Run the below command (based on the file location on your system) on the each worker node. remediation: "Run the below command (based on the file location on your system) on the each worker node.
\nFor example, chown root:root $config" \nFor example, chown root:root $config"
@ -243,10 +260,24 @@ groups:
- id: 2.2.3 - id: 2.2.3
text: "Ensure that the kubelet file permissions are set to 644 or more restrictive (Scored)" text: "Ensure that the kubelet file permissions are set to 644 or more restrictive (Scored)"
audit: "if test -e $kubeletconf; then stat -c %a $kubeletconf; fi" audit: "/bin/sh -c 'if test -e $kubeletconf; then stat -c %a $kubeletconf; fi'"
tests: tests:
bin_op: or
test_items: test_items:
- flag: "644" - flag: "644"
compare:
op: eq
value: 644
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true set: true
remediation: "Run the below command (based on the file location on your system) on the each worker node. remediation: "Run the below command (based on the file location on your system) on the each worker node.
\nFor example, chmod 644 $kubeletconf" \nFor example, chmod 644 $kubeletconf"
@ -254,7 +285,7 @@ groups:
- id: 2.2.4 - id: 2.2.4
text: "Ensure that the kubelet file ownership is set to root:root (Scored)" text: "Ensure that the kubelet file ownership is set to root:root (Scored)"
audit: "if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi" audit: "/bin/sh -c 'if test -e $kubeletconf; then stat -c %U:%G $kubeletconf; fi'"
tests: tests:
test_items: test_items:
- flag: "root:root" - flag: "root:root"
@ -265,10 +296,24 @@ groups:
- id: 2.2.5 - id: 2.2.5
text: "Ensure that the proxy file permissions are set to 644 or more restrictive (Scored)" text: "Ensure that the proxy file permissions are set to 644 or more restrictive (Scored)"
audit: "if test -e $proxyconf; then stat -c %a $proxyconf; fi" audit: "/bin/sh -c 'if test -e $proxyconf; then stat -c %a $proxyconf; fi'"
tests: tests:
bin_op: or
test_items: test_items:
- flag: "644" - flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true set: true
remediation: "Run the below command (based on the file location on your system) on the each worker node. remediation: "Run the below command (based on the file location on your system) on the each worker node.
\nFor example, chmod 644 $proxyconf" \nFor example, chmod 644 $proxyconf"
@ -276,7 +321,7 @@ groups:
- id: 2.2.6 - id: 2.2.6
text: "Ensure that the proxy file ownership is set to root:root (Scored)" text: "Ensure that the proxy file ownership is set to root:root (Scored)"
audit: "if test -e $proxyconf; then stat -c %U:%G $proxyconf; fi" audit: "/bin/sh -c 'if test -e $proxyconf; then stat -c %U:%G $proxyconf; fi'"
tests: tests:
test_items: test_items:
- flag: "root:root" - flag: "root:root"
@ -288,10 +333,24 @@ groups:
- id: 2.2.7 - id: 2.2.7
text: "Ensure that the certificate authorities file permissions are set to text: "Ensure that the certificate authorities file permissions are set to
644 or more restrictive (Scored)" 644 or more restrictive (Scored)"
audit: "if test -e $ca-file; then stat -c %a $ca-file; fi" audit: "/bin/sh -c 'if test -e $ca-file; then stat -c %a $ca-file; fi'"
tests: tests:
bin_op: or
test_items: test_items:
- flag: "644" - flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true set: true
remediation: "Run the following command to modify the file permissions of the --client-ca-file remediation: "Run the following command to modify the file permissions of the --client-ca-file
\nchmod 644 <filename>" \nchmod 644 <filename>"
@ -299,7 +358,7 @@ groups:
- id: 2.2.8 - id: 2.2.8
text: "Ensure that the client certificate authorities file ownership is set to root:root" text: "Ensure that the client certificate authorities file ownership is set to root:root"
audit: "if test -e $ca-file; then stat -c %U:%G $ca-file; fi" audit: "/bin/sh -c 'if test -e $ca-file; then stat -c %U:%G $ca-file; fi'"
tests: tests:
test_items: test_items:
- flag: "notexist:notexist" - flag: "notexist:notexist"

View File

@ -18,7 +18,9 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"os"
"os/exec" "os/exec"
"regexp"
"strings" "strings"
"github.com/golang/glog" "github.com/golang/glog"
@ -83,8 +85,7 @@ func (c *Check) Run() {
// Check if command exists or exit with WARN. // Check if command exists or exit with WARN.
for _, cmd := range c.Commands { for _, cmd := range c.Commands {
_, err := exec.LookPath(cmd.Path) if !isShellCommand(cmd.Path) {
if err != nil {
c.State = WARN c.State = WARN
return return
} }
@ -119,7 +120,6 @@ func (c *Check) Run() {
cs[i].Args, cs[i].Args,
), ),
) )
i++ i++
} }
@ -166,18 +166,44 @@ func (c *Check) Run() {
} }
} }
// textToCommand transforms a text representation of commands to be // textToCommand transforms an input text representation of commands to be
// run into a slice of commands. // run into a slice of commands.
// TODO: Make this more robust. // TODO: Make this more robust.
func textToCommand(s string) []*exec.Cmd { func textToCommand(s string) []*exec.Cmd {
cmds := []*exec.Cmd{} cmds := []*exec.Cmd{}
cp := strings.Split(s, "|") cp := strings.Split(s, "|")
// fmt.Println("check.toCommand:", cp)
for _, v := range cp { for _, v := range cp {
v = strings.Trim(v, " ") v = strings.Trim(v, " ")
cs := strings.Split(v, " ")
// TODO:
// GOAL: To split input text into arguments for exec.Cmd.
//
// CHALLENGE: The input text may contain quoted strings that
// must be passed as a unit to exec.Cmd.
// eg. bash -c 'foo bar'
// 'foo bar' must be passed as unit to exec.Cmd if not the command
// will fail when it is executed.
// eg. exec.Cmd("bash", "-c", "foo bar")
//
// PROBLEM: Current solution assumes the grouped string will always
// be at the end of the input text.
re := regexp.MustCompile(`^(.*)(['"].*['"])$`)
grps := re.FindStringSubmatch(v)
var cs []string
if len(grps) > 0 {
s := strings.Trim(grps[1], " ")
cs = strings.Split(s, " ")
s1 := grps[len(grps)-1]
s1 = strings.Trim(s1, "'\"")
cs = append(cs, s1)
} else {
cs = strings.Split(v, " ")
}
cmd := exec.Command(cs[0], cs[1:]...) cmd := exec.Command(cs[0], cs[1:]...)
cmds = append(cmds, cmd) cmds = append(cmds, cmd)
@ -185,3 +211,18 @@ func textToCommand(s string) []*exec.Cmd {
return cmds return cmds
} }
func isShellCommand(s string) bool {
cmd := exec.Command("/bin/sh", "-c", "command -v "+s)
out, err := cmd.Output()
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
if strings.Contains(string(out), s) {
return true
}
return false
}

View File

@ -7,59 +7,42 @@ groups:
- id: 1.1 - id: 1.1
text: "Kube-apiserver" text: "Kube-apiserver"
checks: checks:
- id: 1.1.1 - id: 0
text: "Ensure that the --allow-privileged argument is set (Scored)" text: "flag is set"
audit: "ps -ef | grep kube-apiserver | grep -v grep"
tests: tests:
test_items: test_items:
- - flag: "--allow-privileged"
flag: "--allow-privileged"
set: true set: true
remediation: "Edit the /etc/kubernetes/config file on the master node and set the KUBE_ALLOW_PRIV parameter to '--allow-privileged=false'"
scored: true
- id: 1.1.2 - id: 1
text: "Ensure that the --basic-auth argument is not set (Scored)" text: "flag is not set"
audit: "ps -ef | grep kube-apiserver | grep -v grep"
tests: tests:
test_item: test_item:
- - flag: "--basic-auth"
flag: "--basic-auth"
set: false set: false
remediation: "Edit the /etc/kubernetes/config file on the master node and set the KUBE_ALLOW_PRIV parameter to '--allow-privileged=false'"
scored: true
- id: 1.1.3 - id: 2
text: "Ensure that the --insecure-port argument is set to 0 (Scored)" text: "flag value is set to some value"
audit: "ps -ef | grep kube-apiserver | grep -v grep"
tests: tests:
test_items: test_items:
- - flag: "--insecure-port"
flag: "--insecure-port"
compare: compare:
op: eq op: eq
value: 0 value: 0
set: true set: true
remediation: "Edit the /etc/kubernetes/config file on the master node and set the KUBE_ALLOW_PRIV parameter to '--allow-privileged=false'"
scored: true
- id: 1.1.4 - id: 3
text: "Ensure that the --audit-log-maxage argument is set to 30 or appropriate (Scored)" text: "flag value is greater than or equal some number"
audit: "ps -ef | grep kube-apiserver | grep -v grep"
tests: tests:
test_items: test_items:
- - flag: "--audit-log-maxage"
flag: "--audit-log-maxage"
compare: compare:
op: gte op: gte
value: 30 value: 30
set: true set: true
remediation: "Edit the /etc/kubernetes/config file on the master node and set the KUBE_ALLOW_PRIV parameter to '--allow-privileged=false'"
scored: true
- id: 1.1.5 - id: 4
text: "Ensure that the --max-backlog argument is set to 30 or less (Scored)" text: "flag value is less than some number"
audit: "ps -ef | grep kube-apiserver | grep -v grep"
tests: tests:
test_items: test_items:
- flag: "--max-backlog" - flag: "--max-backlog"
@ -67,26 +50,19 @@ groups:
op: lt op: lt
value: 30 value: 30
set: true set: true
remediation: "Edit the /etc/kubernetes/config file on the master node and set the KUBE_ALLOW_PRIV parameter to '--allow-privileged=false'"
scored: true
- id: 1.1.6 - id: 5
text: "Ensure admission control does not include AlwaysAdmit (Scored)" text: "flag value does not have some value"
audit: "ps -ef | grep kube-apiserver | grep -v grep"
tests: tests:
test_items: test_items:
- - flag: "--admission-control"
flag: "--admission-control"
compare: compare:
op: nothave op: nothave
value: AlwaysAdmit value: AlwaysAdmit
set: true set: true
remediation: "Edit the /etc/kubernetes/config file on the master node and set the KUBE_ALLOW_PRIV parameter to '--allow-privileged=false'"
scored: true
- id: 1.1.7 - id: 6
text: "Ensure that the --kubelet-client-certificate and --kubelet-clientkey arguments are set as appropriate (Scored)" text: "test AND binary operation"
audit: "ps -ef | grep kube-apiserver | grep -v grep"
tests: tests:
bin_op: and bin_op: and
test_items: test_items:
@ -94,17 +70,13 @@ groups:
set: true set: true
- flag: "--kubelet-clientkey" - flag: "--kubelet-clientkey"
set: true set: true
remediation: "Edit the /etc/kubernetes/config file on the master node and set the KUBE_ALLOW_PRIV parameter to '--allow-privileged=false'"
scored: true
- id: 1.1.8 - id: 7
text: "Ensure that the --secure-port argument is not set to 0 (Scored)" text: "test OR binary operation"
audit: "ps -ef | grep kube-apiserver | grep -v grep"
tests: tests:
bin_op: or bin_op: or
test_items: test_items:
- - flag: "--secure-port"
flag: "--secure-port"
compare: compare:
op: eq op: eq
value: 0 value: 0
@ -112,28 +84,35 @@ groups:
- -
flag: "--secure-port" flag: "--secure-port"
set: false set: false
remediation: "Edit the /etc/kubernetes/apiserver file on the master node and either remove the -secure-port argument from the KUBE_API_ARGS parameter or set it to a different desired port."
scored: true
- id: 1.4.1 - id: 8
text: "Ensure that the apiserver file permissions are set to 644 or more restrictive (Scored)" text: "test flag with arbitrary text"
audit: "stat -c %a /etc/kubernetes/apiserver"
tests: tests:
test_items: test_items:
- flag: "644" - flag: "644"
set: true
remediation: "Run the below command (based on the file location on your system) on the master node. For example, chmod 644 /etc/kubernetes/apiserver"
scored: true
- id: 2.1.14
text: "Ensure that the apiserver file permissions are set to 644 or more restrictive (Scored)"
audit: "ps -ef | grep kubelet | grep -v grep"
tests:
test_items:
- flag: "KubeletClient"
compare: compare:
op: eq op: eq
value: true value: "644"
set: true
- id: 9
text: "test permissions"
audit: "/bin/sh -c 'if test -e $config; then stat -c %a $config; fi'"
tests:
bin_op: or
test_items:
- flag: "644"
compare:
op: eq
value: "644"
set: true
- flag: "640"
compare:
op: eq
value: "640"
set: true
- flag: "600"
compare:
op: eq
value: "600"
set: true set: true
remediation: "Run the below command (based on the file location on your system) on the master node. For example, chmod 644 /etc/kubernetes/apiserver"
scored: true

View File

@ -38,6 +38,7 @@ const (
type testItem struct { type testItem struct {
Flag string Flag string
Output string
Value string Value string
Set bool Set bool
Compare compare Compare compare
@ -57,14 +58,22 @@ func (t *testItem) execute(s string) (result bool) {
isset := match isset := match
if isset && t.Compare.Op != "" { if isset && t.Compare.Op != "" {
pttn := t.Flag + `=([^\s,]*) *` // Expects flags in the form;
// --flag=somevalue
// --flag
// somevalue
pttn := `(` + t.Flag + `)(=)*([^\s,]*) *`
flagRe := regexp.MustCompile(pttn) flagRe := regexp.MustCompile(pttn)
vals := flagRe.FindStringSubmatch(s) vals := flagRe.FindStringSubmatch(s)
if len(vals) > 0 { if len(vals) > 0 {
flagVal = vals[1] if vals[3] != "" {
flagVal = vals[3]
} else {
flagVal = vals[1]
}
} else { } else {
fmt.Fprintf(os.Stderr, "expected value for %s but none found\n", t.Flag) fmt.Fprintf(os.Stderr, "invalid flag in testitem definition")
os.Exit(1) os.Exit(1)
} }

View File

@ -16,6 +16,8 @@ package check
import ( import (
"io/ioutil" "io/ioutil"
"os"
"strings"
"testing" "testing"
) )
@ -30,79 +32,74 @@ func init() {
if err != nil { if err != nil {
panic("Failed reading test data: " + err.Error()) panic("Failed reading test data: " + err.Error())
} }
controls, err = NewControls(MASTER, in)
// substitute variables in data file
user := os.Getenv("USER")
s := strings.Replace(string(in), "$user", user, -1)
controls, err = NewControls(MASTER, []byte(s))
// controls, err = NewControls(MASTER, in)
if err != nil { if err != nil {
panic("Failed creating test controls: " + err.Error()) panic("Failed creating test controls: " + err.Error())
} }
} }
func TestTestExecute(t *testing.T) { func TestTestExecute(t *testing.T) {
cases := []struct { cases := []struct {
*tests *Check
testfor string str string
str string
}{ }{
{ {
controls.Groups[0].Checks[0].Tests, controls.Groups[0].Checks[0],
"flag set",
"2:45 ../kubernetes/kube-apiserver --allow-privileged=false --option1=20,30,40", "2:45 ../kubernetes/kube-apiserver --allow-privileged=false --option1=20,30,40",
}, },
{ {
controls.Groups[0].Checks[1].Tests, controls.Groups[0].Checks[1],
"flag not set",
"2:45 ../kubernetes/kube-apiserver --allow-privileged=false", "2:45 ../kubernetes/kube-apiserver --allow-privileged=false",
}, },
{ {
controls.Groups[0].Checks[2].Tests, controls.Groups[0].Checks[2],
"flag and value set",
"niinai 13617 2635 99 19:26 pts/20 00:03:08 ./kube-apiserver --insecure-port=0 --anonymous-auth", "niinai 13617 2635 99 19:26 pts/20 00:03:08 ./kube-apiserver --insecure-port=0 --anonymous-auth",
}, },
{ {
controls.Groups[0].Checks[3].Tests, controls.Groups[0].Checks[3],
"flag value greater than value",
"2:45 ../kubernetes/kube-apiserver --secure-port=0 --audit-log-maxage=40 --option", "2:45 ../kubernetes/kube-apiserver --secure-port=0 --audit-log-maxage=40 --option",
}, },
{ {
controls.Groups[0].Checks[4].Tests, controls.Groups[0].Checks[4],
"flag value less than value",
"2:45 ../kubernetes/kube-apiserver --max-backlog=20 --secure-port=0 --audit-log-maxage=40 --option", "2:45 ../kubernetes/kube-apiserver --max-backlog=20 --secure-port=0 --audit-log-maxage=40 --option",
}, },
{ {
controls.Groups[0].Checks[5].Tests, controls.Groups[0].Checks[5],
"flag value does not have",
"2:45 ../kubernetes/kube-apiserver --option --admission-control=WebHook,RBAC ---audit-log-maxage=40", "2:45 ../kubernetes/kube-apiserver --option --admission-control=WebHook,RBAC ---audit-log-maxage=40",
}, },
{ {
controls.Groups[0].Checks[6].Tests, controls.Groups[0].Checks[6],
"AND multiple tests, all testitems pass",
"2:45 .. --kubelet-clientkey=foo --kubelet-client-certificate=bar --admission-control=Webhook,RBAC", "2:45 .. --kubelet-clientkey=foo --kubelet-client-certificate=bar --admission-control=Webhook,RBAC",
}, },
{ {
controls.Groups[0].Checks[7].Tests, controls.Groups[0].Checks[7],
"OR multiple tests",
"2:45 .. --secure-port=0 --kubelet-client-certificate=bar --admission-control=Webhook,RBAC", "2:45 .. --secure-port=0 --kubelet-client-certificate=bar --admission-control=Webhook,RBAC",
}, },
{ {
controls.Groups[0].Checks[8].Tests, controls.Groups[0].Checks[8],
"text",
"644", "644",
}, },
{ {
controls.Groups[0].Checks[9].Tests, controls.Groups[0].Checks[9],
"flag value is comma-separated", "640",
"2:35 ../kubelet --features-gates=KubeletClient=true,KubeletServer=true",
}, },
{ {
controls.Groups[0].Checks[9].Tests, controls.Groups[0].Checks[9],
"flag value is comma-separated", "600",
"2:35 ../kubelet --features-gates=KubeletServer=true,KubeletClient=true",
}, },
} }
for _, c := range cases { for _, c := range cases {
res := c.tests.execute(c.str) res := c.Tests.execute(c.str)
if !res { if !res {
t.Errorf("%s, expected:%v, got:%v\n", c.testfor, true, res) t.Errorf("%s, expected:%v, got:%v\n", c.Text, true, res)
} }
} }
} }

View File

@ -17,7 +17,7 @@ package cmd
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"strings" "os"
"github.com/aquasecurity/kube-bench/check" "github.com/aquasecurity/kube-bench/check"
"github.com/spf13/viper" "github.com/spf13/viper"
@ -96,26 +96,26 @@ func runChecks(t check.NodeType) {
} }
// Variable substitutions. Replace all occurrences of variables in controls files. // Variable substitutions. Replace all occurrences of variables in controls files.
s := strings.Replace(string(in), "$apiserverbin", apiserverBin, -1) s := multiWordReplace(string(in), "$apiserverbin", apiserverBin)
s = strings.Replace(s, "$apiserverconf", apiserverConf, -1) s = multiWordReplace(s, "$apiserverconf", apiserverConf)
s = strings.Replace(s, "$schedulerbin", schedulerBin, -1) s = multiWordReplace(s, "$schedulerbin", schedulerBin)
s = strings.Replace(s, "$schedulerconf", schedulerConf, -1) s = multiWordReplace(s, "$schedulerconf", schedulerConf)
s = strings.Replace(s, "$controllermanagerbin", controllerManagerBin, -1) s = multiWordReplace(s, "$controllermanagerbin", controllerManagerBin)
s = strings.Replace(s, "$controllermanagerconf", controllerManagerConf, -1) s = multiWordReplace(s, "$controllermanagerconf", controllerManagerConf)
s = strings.Replace(s, "$config", config, -1) s = multiWordReplace(s, "$config", config)
s = strings.Replace(s, "$etcdbin", etcdBin, -1) s = multiWordReplace(s, "$etcdbin", etcdBin)
s = strings.Replace(s, "$etcdconf", etcdConf, -1) s = multiWordReplace(s, "$etcdconf", etcdConf)
s = strings.Replace(s, "$flanneldbin", flanneldBin, -1) s = multiWordReplace(s, "$flanneldbin", flanneldBin)
s = strings.Replace(s, "$flanneldconf", flanneldConf, -1) s = multiWordReplace(s, "$flanneldconf", flanneldConf)
s = strings.Replace(s, "$kubeletbin", kubeletBin, -1) s = multiWordReplace(s, "$kubeletbin", kubeletBin)
s = strings.Replace(s, "$kubeletconf", kubeletConf, -1) s = multiWordReplace(s, "$kubeletconf", kubeletConf)
s = strings.Replace(s, "$proxybin", proxyBin, -1) s = multiWordReplace(s, "$proxybin", proxyBin)
s = strings.Replace(s, "$proxyconf", proxyConf, -1) s = multiWordReplace(s, "$proxyconf", proxyConf)
s = strings.Replace(s, "$fedapiserverbin", fedApiserverBin, -1) s = multiWordReplace(s, "$fedapiserverbin", fedApiserverBin)
s = strings.Replace(s, "$fedcontrollermanagerbin", fedControllerManagerBin, -1) s = multiWordReplace(s, "$fedcontrollermanagerbin", fedControllerManagerBin)
controls, err := check.NewControls(t, []byte(s)) controls, err := check.NewControls(t, []byte(s))
if err != nil { if err != nil {
@ -150,15 +150,35 @@ func runChecks(t check.NodeType) {
// verifyNodeType checks the executables and config files are as expected // verifyNodeType checks the executables and config files are as expected
// for the specified tests (master, node or federated). // for the specified tests (master, node or federated).
func verifyNodeType(t check.NodeType) { func verifyNodeType(t check.NodeType) {
var bins []string
var confs []string
switch t { switch t {
case check.MASTER: case check.MASTER:
verifyBin(apiserverBin, schedulerBin, controllerManagerBin) bins = []string{apiserverBin, schedulerBin, controllerManagerBin}
verifyConf(apiserverConf, schedulerConf, controllerManagerConf) confs = []string{apiserverConf, schedulerConf, controllerManagerConf}
case check.NODE: case check.NODE:
verifyBin(kubeletBin, proxyBin) bins = []string{kubeletBin, proxyBin}
verifyConf(kubeletConf, proxyConf) confs = []string{kubeletConf, proxyConf}
case check.FEDERATED: case check.FEDERATED:
verifyBin(fedApiserverBin, fedControllerManagerBin) bins = []string{fedApiserverBin, fedControllerManagerBin}
}
for _, bin := range bins {
if !verifyBin(bin, ps) {
printlnWarn(fmt.Sprintf("%s is not running", bin))
}
}
for _, conf := range confs {
_, err := os.Stat(conf)
if err != nil {
if os.IsNotExist(err) {
printlnWarn(fmt.Sprintf("Missing kubernetes config file: %s", conf))
} else {
exitWithError(fmt.Errorf("error looking for file %s: %v", conf, err))
}
}
} }
} }

View File

@ -64,64 +64,30 @@ func cleanIDs(list string) []string {
return ids return ids
} }
func verifyConf(confPath ...string) { // ps execs out to the ps command; it's separated into a function so we can write tests
var missing string func ps(proc string) string {
cmd := exec.Command("ps", "-C", proc, "-o", "cmd", "--no-headers")
for _, c := range confPath {
if _, err := os.Stat(c); err != nil && os.IsNotExist(err) {
continueWithError(err, "")
missing += c + ", "
}
}
if len(missing) > 0 {
missing = strings.Trim(missing, ", ")
printlnWarn(fmt.Sprintf("Missing kubernetes config files: %s", missing))
}
}
func verifyBin(binPath ...string) {
var binSlice []string
var bin string
var missing string
var notRunning string
// Construct proc name for ps(1)
for _, b := range binPath {
_, err := exec.LookPath(b)
bin = bin + "," + b
binSlice = append(binSlice, b)
if err != nil {
missing += b + ", "
continueWithError(err, "")
}
}
bin = strings.Trim(bin, ",")
cmd := exec.Command("ps", "-C", bin, "-o", "cmd", "--no-headers")
out, err := cmd.Output() out, err := cmd.Output()
if err != nil { if err != nil {
continueWithError(fmt.Errorf("%s: %s", cmd.Args, err), "") continueWithError(fmt.Errorf("%s: %s", cmd.Args, err), "")
} }
for _, b := range binSlice { return string(out)
matched := strings.Contains(string(out), b) }
if !matched { // verifyBin checks that the binary specified is running
notRunning += b + ", " func verifyBin(bin string, psFunc func(string) string) bool {
}
}
if len(missing) > 0 { // Strip any quotes
missing = strings.Trim(missing, ", ") bin = strings.Trim(bin, "'\"")
printlnWarn(fmt.Sprintf("Missing kubernetes binaries: %s", missing))
}
if len(notRunning) > 0 { // bin could consist of more than one word
notRunning = strings.Trim(notRunning, ", ") // We'll search for running processes with the first word, and then check the whole
printlnWarn(fmt.Sprintf("Kubernetes binaries not running: %s", notRunning)) // proc as supplied is included in the results
} proc := strings.Fields(bin)[0]
out := psFunc(proc)
return strings.Contains(out, bin)
} }
func verifyKubeVersion(major string, minor string) { func verifyKubeVersion(major string, minor string) {
@ -138,7 +104,9 @@ func verifyKubeVersion(major string, minor string) {
if err != nil { if err != nil {
s := fmt.Sprintf("Kubernetes version check skipped with error %v", err) s := fmt.Sprintf("Kubernetes version check skipped with error %v", err)
continueWithError(err, sprintlnWarn(s)) continueWithError(err, sprintlnWarn(s))
return if len(out) == 0 {
return
}
} }
msg := checkVersion("Client", string(out), major, minor) msg := checkVersion("Client", string(out), major, minor)
@ -182,3 +150,12 @@ func versionMatch(r *regexp.Regexp, s string) string {
} }
return match[1] return match[1]
} }
func multiWordReplace(s string, subname string, sub string) string {
f := strings.Fields(sub)
if len(f) > 1 {
sub = "'" + sub + "'"
}
return strings.Replace(s, subname, sub, -1)
}

View File

@ -16,6 +16,7 @@ package cmd
import ( import (
"regexp" "regexp"
"strconv"
"testing" "testing"
) )
@ -36,8 +37,8 @@ func TestCheckVersion(t *testing.T) {
{t: "Server", s: "something unexpected", major: "2", minor: "0", exp: "Couldn't find Server version from kubectl output 'something unexpected'"}, {t: "Server", s: "something unexpected", major: "2", minor: "0", exp: "Couldn't find Server version from kubectl output 'something unexpected'"},
} }
for _, c := range cases { for id, c := range cases {
t.Run("", func(t *testing.T) { t.Run(strconv.Itoa(id), func(t *testing.T) {
m := checkVersion(c.t, c.s, c.major, c.minor) m := checkVersion(c.t, c.s, c.major, c.minor)
if m != c.exp { if m != c.exp {
t.Fatalf("Got: %s, expected: %s", m, c.exp) t.Fatalf("Got: %s, expected: %s", m, c.exp)
@ -66,8 +67,8 @@ func TestVersionMatch(t *testing.T) {
{r: minor}, // Checking that we don't fall over if the string is empty {r: minor}, // Checking that we don't fall over if the string is empty
} }
for _, c := range cases { for id, c := range cases {
t.Run("", func(t *testing.T) { t.Run(strconv.Itoa(id), func(t *testing.T) {
m := versionMatch(c.r, c.s) m := versionMatch(c.r, c.s)
if m != c.exp { if m != c.exp {
t.Fatalf("Got %s expected %s", m, c.exp) t.Fatalf("Got %s expected %s", m, c.exp)
@ -75,3 +76,56 @@ func TestVersionMatch(t *testing.T) {
}) })
} }
} }
var g string
func fakeps(proc string) string {
return g
}
func TestVerifyBin(t *testing.T) {
cases := []struct {
proc string
psOut string
exp bool
}{
{proc: "single", psOut: "single", exp: true},
{proc: "single", psOut: "", exp: false},
{proc: "two words", psOut: "two words", exp: true},
{proc: "two words", psOut: "", exp: false},
{proc: "cmd", psOut: "cmd param1 param2", exp: true},
{proc: "cmd param", psOut: "cmd param1 param2", exp: true},
{proc: "cmd param", psOut: "cmd", exp: false},
}
for id, c := range cases {
t.Run(strconv.Itoa(id), func(t *testing.T) {
g = c.psOut
v := verifyBin(c.proc, fakeps)
if v != c.exp {
t.Fatalf("Expected %v got %v", c.exp, v)
}
})
}
}
func TestMultiWordReplace(t *testing.T) {
cases := []struct {
input string
sub string
subname string
output string
}{
{input: "Here's a file with no substitutions", sub: "blah", subname: "blah", output: "Here's a file with no substitutions"},
{input: "Here's a file with a substitution", sub: "blah", subname: "substitution", output: "Here's a file with a blah"},
{input: "Here's a file with multi-word substitutions", sub: "multi word", subname: "multi-word", output: "Here's a file with 'multi word' substitutions"},
{input: "Here's a file with several several substitutions several", sub: "blah", subname: "several", output: "Here's a file with blah blah substitutions blah"},
}
for id, c := range cases {
t.Run(strconv.Itoa(id), func(t *testing.T) {
s := multiWordReplace(c.input, c.subname, c.sub)
if s != c.output {
t.Fatalf("Expected %s got %s", c.output, s)
}
})
}
}