From 26cc77ec1df6b4d6009702e339a17109e16c8a9e Mon Sep 17 00:00:00 2001 From: Liz Rice Date: Mon, 19 Jun 2017 21:17:19 +0100 Subject: [PATCH] Get the tests working on deployments where file names may be different or not in path (#1) * Replace the default help text * Readme file, including the test config format documentation * Typo * Warn if config files / executables aren't found * Ignore original name of executable (as per current README) * Update tests to avoid failing on stat of a non-existant file * Add a makefile for ease of build --- .gitignore | 1 + README.md | 66 ++++++++++++++++++++++++++++++++++++++++++++++--- cfg/master.yaml | 20 +++++++-------- cfg/node.yaml | 12 ++++----- check/README.md | 55 ----------------------------------------- cmd/common.go | 18 ++++++++------ cmd/root.go | 9 ++----- makefile | 8 ++++++ 8 files changed, 101 insertions(+), 88 deletions(-) delete mode 100644 check/README.md create mode 100644 makefile diff --git a/.gitignore b/.gitignore index 0ebc744..caacc97 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ kubernetes-bench-security +cis_kubernetes *.swp diff --git a/README.md b/README.md index 2aff0ce..f342905 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,68 @@ Tests are configured with YAML files, making this tool easy to update as test sp ## Installation -Install by cloning this repository and running +You will need to run this application on the target machines that you want to test. + +If Go is installed on the target machines, you can simply clone this repository, build and install as follows: + +```go build -o cis_kubernetes .``` +```./install.sh``` + +The installation script creates a directory ~/.cis_kubernetes and copies the test config files there. + +## Test config YAML representation +The tests are represented as YAML documents (installed by default into ~/.cis_kubernetes). + +An example is as listed below: +``` +--- +controls: +id: 1 +text: "Master Checks" +type: "master" +groups: +- id: 1.1 + text: "Kube-apiserver" + checks: + - id: 1.1.1 + text: "Ensure that the --allow-privileged argument is set (Scored)" + audit: "ps -ef | grep kube-apiserver | grep -v grep" + tests: + - flag: "--allow-privileged" + 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 +``` + +Recommendations (called `checks` in this document) can run on Kubernetes Master, Node or Federated API Servers. +Checks are organized into `groups` which share similar controls (things to check for) and are grouped together in the section of the CIS Kubernetes document. +These groups are further organized under `controls` which can be of the type `master`, `node` or `federated apiserver` to reflect the various Kubernetes node types. + +## Tests +Tests are the items we actually look for to determine if a check is successful or not. Checks can have multiple tests, which must all be successful for the check to pass. + +The syntax for tests: +``` +tests: +- flag: + set: + compare: + op: + value: +... +``` +Tests have various `operations` which are used to compare the output of audit commands for success. +These operations are: + +- `eq`: tests if the flag value is equal to the compared value. +- `noteq`: tests if the flag value is unequal to the compared value. +- `gt`: tests if the flag value is greater than the compared value. +- `gte`: tests if the flag value is greater than or equal to the compared value. +- `lt`: tests if the flag value is less than the compared value. +- `lte`: tests if the flag value is less than or equal to the compared value. +- `has`: tests if the flag value contains the compared value. +- `nothave`: tests if the flag value does not contain the compared value. + + -```make install``` -This builds the application and also copies the test configuration files into a .cis_kubernetes directory in your home directory. diff --git a/cfg/master.yaml b/cfg/master.yaml index f334e65..ddd3693 100644 --- a/cfg/master.yaml +++ b/cfg/master.yaml @@ -531,7 +531,7 @@ groups: checks: - id: 1.4.1 text: "Ensure that the apiserver file permissions are set to 644 or more restrictive (Scored)" - audit: "stat -c %a $kubeConfDir/apiserver" + audit: "if test -e $kubeConfDir/apiserver; then stat -c %a $kubeConfDir/apiserver; fi" tests: test_items: - flag: "644" @@ -542,7 +542,7 @@ groups: - id: 1.4.2 text: "Ensure that the apiserver file ownership is set to root:root (Scored)" - audit: "stat -c %U:%G $kubeConfDir/apiserver" + audit: "if test -e $kubeConfDir/apiserver; then stat -c %U:%G $kubeConfDir/apiserver; fi" tests: test_items: - flag: "root:root" @@ -553,7 +553,7 @@ groups: - id: 1.4.3 text: "Ensure that the config file permissions are set to 644 or more restrictive (Scored)" - audit: "stat -c %a $kubeConfDir/config" + audit: "if test -e $kubeConfDir/config; then stat -c %a $kubeConfDir/config; fi" tests: test_items: - flag: "644" @@ -564,7 +564,7 @@ groups: - id: 1.4.4 text: "Ensure that the config file ownership is set to root:root (Scored)" - audit: "stat -c %U:%G $kubeConfDir/config" + audit: "if test -e $kubeConfDir/config; then stat -c %U:%G $kubeConfDir/config; fi" tests: test_items: - flag: "root:root" @@ -575,7 +575,7 @@ groups: - id: 1.4.5 text: "Ensure that the scheduler file permissions are set to 644 or more restrictive (Scored)" - audit: "stat -c %a $kubeConfDir/scheduler" + audit: "if test -e $kubeConfDir/scheduler; then stat -c %a $kubeConfDir/scheduler; fi" tests: test_items: - flag: "644" @@ -586,7 +586,7 @@ groups: - id: 1.4.6 text: "Ensure that the scheduler file ownership is set to root:root (Scored)" - audit: "stat -c %U:%G $kubeConfDir/scheduler" + audit: "if test -e $kubeConfDir/scheduler; then stat -c %U:%G $kubeConfDir/scheduler; fi" tests: test_items: - flag: "root:root" @@ -597,7 +597,7 @@ groups: - id: 1.4.7 text: "Ensure that the etcd.conf file permissions are set to 644 or more restrictive (Scored)" - audit: "stat -c %a $etcdConfDir/etcd.conf" + audit: "if test -e $etcdConfDir/etcd.conf; then stat -c %a $etcdConfDir/etcd.conf; fi" tests: test_items: - flag: "644" @@ -608,7 +608,7 @@ groups: - id: 1.4.8 text: "Ensure that the etcd.conf file ownership is set to root:root (Scored)" - audit: "stat -c %U:%G $etcdConfDir/etcd.conf" + audit: "if test -e $etcdConfDir/kubelet; then stat -c %U:%G $etcdConfDir/etcd.conf; fi" tests: test_items: - flag: "root:root" @@ -619,7 +619,7 @@ groups: - id: 1.4.9 text: "Ensure that the flanneld file permissions are set to 644 or more restrictive (Scored)" - audit: "stat -c %a /etc/sysconfig/flanneld" + audit: "if test -e /etc/sysconfig/flanneld; then stat -c %a /etc/sysconfig/flanneld; fi" tests: test_items: - flag: "644" @@ -630,7 +630,7 @@ groups: - id: 1.4.10 text: "Ensure that the flanneld file ownership is set to root:root (Scored)" - audit: "stat -c %U:%G /etc/sysconfig/flanneld" + audit: "if test -e /etc/sysconfig/flanneld; then stat -c %U:%G /etc/sysconfig/flanneld; fi" tests: test_items: - flag: "root:root" diff --git a/cfg/node.yaml b/cfg/node.yaml index 31465af..4304264 100644 --- a/cfg/node.yaml +++ b/cfg/node.yaml @@ -193,7 +193,7 @@ groups: checks: - id: 2.2.1 text: "Ensure that the config file permissions are set to 644 or more restrictive (Scored)" - audit: "stat -c %a $kubeConfDir/config" + audit: "if test -e $kubeConfDir/config; then stat -c %a $kubeConfDir/config; fi" tests: test_items: - flag: "644" @@ -204,7 +204,7 @@ groups: - id: 2.2.2 text: "Ensure that the config file ownership is set to root:root (Scored)" - audit: "stat -c %U:%G $kubeConfDir/config" + audit: "if test -e $kubeConfDir/config; then stat -c %U:%G $kubeConfDir/config; fi" tests: test_items: - flag: "root:root" @@ -215,7 +215,7 @@ groups: - id: 2.2.3 text: "Ensure that the kubelet file permissions are set to 644 or more restrictive (Scored)" - audit: "stat -c %a $kubeConfDir/kubelet" + audit: "if test -e $kubeConfDir/kubelet; then stat -c %a $kubeConfDir/kubelet; fi" tests: test_items: - flag: "644" @@ -226,7 +226,7 @@ groups: - id: 2.2.4 text: "Ensure that the kubelet file ownership is set to root:root (Scored)" - audit: "stat -c %U:%G $kubeConfDir/kubelet" + audit: "if test -e $kubeConfDir/kubelet; then stat -c %U:%G $kubeConfDir/kubelet; fi" tests: test_items: - flag: "root:root" @@ -237,7 +237,7 @@ groups: - id: 2.2.5 text: "Ensure that the proxy file permissions are set to 644 or more restrictive (Scored)" - audit: "stat -c %a $kubeConfDir/proxy" + audit: "if test -e $kubeConfDir/proxy; then stat -c %a $kubeConfDir/proxy; fi" tests: test_items: - flag: "644" @@ -248,7 +248,7 @@ groups: - id: 2.2.6 text: "Ensure that the proxy file ownership is set to root:root (Scored)" - audit: "stat -c %U:%G $kubeConfDir/proxy" + audit: "if test -e $kubeConfDir/proxy; then stat -c %U:%G $kubeConfDir/proxy; fi" tests: test_items: - flag: "root:root" diff --git a/check/README.md b/check/README.md deleted file mode 100644 index cdfe4b3..0000000 --- a/check/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# Checks -Checks are recommendations from the Center for Internet Security for Kubernetes 1.6+ installations. - -## YAML Representation -In this application these recommendations are represented as YAML documents. -An example is as listed below: -``` ---- -controls: -id: 1 -text: "Master Checks" -type: "master" -groups: -- id: 1.1 - text: "Kube-apiserver" - checks: - - id: 1.1.1 - text: "Ensure that the --allow-privileged argument is set (Scored)" - audit: "ps -ef | grep kube-apiserver | grep -v grep" - tests: - - flag: "--allow-privileged" - 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 -``` - -Recommendations (called `checks` in this document) can run on Kubernetes Master, Node or Federated API Servers. -Checks are organized into `groups` which share similar controls (things to check for) and are grouped together in the section of the CIS Kubernetes document. -These groups are further organized under `controls` which can be of the type `master`, `node` or `federated apiserver` to reflect the various Kubernetes node types. - -## Tests -Tests are the items we actually look for to determine if a check is successful or not. Checks can have multiple tests, which must all be successful for the check to pass. - -The syntax for tests: -``` -tests: -- flag: - set: - compare: - op: - value: -... -``` -Tests have various `operations` which are used to compare the output of audit commands for success. -These operations are: - -- `eq`: tests if the flag value is equal to the compared value. -- `noteq`: tests if the flag value is unequal to the compared value. -- `gt`: tests if the flag value is greater than the compared value. -- `gte`: tests if the flag value is greater than or equal to the compared value. -- `lt`: tests if the flag value is less than the compared value. -- `lte`: tests if the flag value is less than or equal to the compared value. -- `has`: tests if the flag value contains the compared value. -- `nothave`: tests if the flag value does not contain the compared value. - diff --git a/cmd/common.go b/cmd/common.go index 8124225..14fffe8 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -140,11 +140,13 @@ func verifyNodeType(t check.NodeType) { confPath = kubeFederatedConf } + // These executables might not be on the user's path. + // TODO! Check the version number using kubectl, which is more likely to be on the path. for _, b := range binPath { _, err := exec.LookPath(b) if err != nil { - fmt.Fprintf(os.Stderr, "%s: command not found\n", b) - os.Exit(1) + fmt.Fprintf(os.Stderr, "WARNING: %s: command not found on path - version check skipped\n", b) + continue } // Check version @@ -152,16 +154,18 @@ func verifyNodeType(t check.NodeType) { out, _ = cmd.Output() if matched, _ := regexp.MatchString(kubeVersion, string(out)); !matched { fmt.Fprintf(os.Stderr, - "%s unsupported version, expected v%s, got %s\n", + "%s unsupported version, expected %s, got %s\n", b, kubeVersion, string(out), ) os.Exit(1) } + } - // Check if running - cmd = exec.Command("ps", "-ef") + for _, b := range binPath { + // Check if running. + cmd := exec.Command("ps", "-ef") out, _ = cmd.Output() if matched, _ := regexp.MatchString(".*"+b, string(out)); !matched { fmt.Fprintf(os.Stderr, "%s is not running\n", b) @@ -171,8 +175,8 @@ func verifyNodeType(t check.NodeType) { for _, c := range confPath { if _, err := os.Stat(c); os.IsNotExist(err) { - fmt.Fprintf(os.Stderr, "config file %s does not exist\n", c) - os.Exit(1) + fmt.Fprintf(os.Stderr, "WARNING: config file %s does not exist\n", c) + // os.Exit(1) } } } diff --git a/cmd/root.go b/cmd/root.go index bae9dba..9eaba54 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -41,13 +41,8 @@ var ( // RootCmd represents the base command when called without any subcommands var RootCmd = &cobra.Command{ Use: "cis_kubernetes", - Short: "A brief description of your application", - Long: `A longer description that spans multiple lines and likely contains -examples and usage of using your application. For example: - -Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.`, + Short: "Run CIS Benchmarks checks against a Kubernetes deployment", + Long: `This tool runs the CIS Kubernetes 1.6 Benchmark v1.0.0 checks.`, } // Execute adds all child commands to the root command sets flags appropriately. diff --git a/makefile b/makefile new file mode 100644 index 0000000..86581e4 --- /dev/null +++ b/makefile @@ -0,0 +1,8 @@ +SOURCES := $(shell find . -name '*.go') +TARGET_OS := linux + +cis_kubernetes: $(SOURCES) + GOOS=$(TARGET_OS) go build -o cis_kubernetes . + +install: cis_kubernetes + ./install.sh \ No newline at end of file