From 0bc004468b2c8c4de015fba8a4e39aeb6e4e9adf Mon Sep 17 00:00:00 2001 From: Liz Rice Date: Fri, 29 Jun 2018 10:23:06 +0100 Subject: [PATCH 1/5] Include .manifest extensions as an option for config files (as used by kops and kubespreay) --- cfg/1.8/config.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cfg/1.8/config.yaml b/cfg/1.8/config.yaml index 2dcad14..1fec47e 100644 --- a/cfg/1.8/config.yaml +++ b/cfg/1.8/config.yaml @@ -9,21 +9,27 @@ master: apiserver: + confs: + - /etc/kubernetes/manifests/kube-apiserver.yaml + - /etc/kubernetes/manifests/kube-apiserver.manifest defaultconf: /etc/kubernetes/manifests/kube-apiserver.yaml scheduler: confs: - /etc/kubernetes/manifests/kube-scheduler.yaml + - /etc/kubernetes/manifests/kube-scheduler.manifest defaultconf: /etc/kubernetes/manifests/kube-scheduler.yaml controllermanager: confs: - /etc/kubernetes/manifests/kube-controller-manager.yaml + - /etc/kubernetes/manifests/kube-controller-manager.manifest defaultconf: /etc/kubernetes/manifests/kube-controller-manager.yaml etcd: confs: - /etc/kubernetes/manifests/etcd.yaml + - /etc/kubernetes/manifests/etcd.manifest defaultconf: /etc/kubernetes/manifests/etcd.yaml node: From 223ac1464215e7c5927c467c09888b194a91c5e0 Mon Sep 17 00:00:00 2001 From: Liz Rice Date: Fri, 29 Jun 2018 10:35:44 +0100 Subject: [PATCH 2/5] Don't override version specified on command line --- cmd/common.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/cmd/common.go b/cmd/common.go index 9e4cfef..1536d7c 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -52,12 +52,11 @@ func runChecks(t check.NodeType) { ver = kubeVersion } else { ver = getKubeVersion() - } - - switch ver { - case "1.9", "1.10": - continueWithError(nil, fmt.Sprintf("No CIS spec for %s - using tests from CIS 1.2.0 spec for Kubernetes 1.8\n", ver)) - ver = "1.8" + switch ver { + case "1.9", "1.10": + continueWithError(nil, fmt.Sprintf("No CIS spec for %s - using tests from CIS 1.2.0 spec for Kubernetes 1.8\n", ver)) + ver = "1.8" + } } path := filepath.Join(cfgDir, ver) From ecd14ed682e1450afc2192c399384576e926eeb1 Mon Sep 17 00:00:00 2001 From: Liz Rice Date: Fri, 29 Jun 2018 12:19:00 +0100 Subject: [PATCH 3/5] File substitutions should be a detailed log --- cmd/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/util.go b/cmd/util.go index ab78945..98a3769 100644 --- a/cmd/util.go +++ b/cmd/util.go @@ -275,7 +275,7 @@ func makeSubstitutions(s string, ext string, m map[string]string) string { glog.V(2).Info(fmt.Sprintf("No subsitution for '%s'\n", subst)) continue } - glog.V(1).Info(fmt.Sprintf("Substituting %s with '%s'\n", subst, v)) + glog.V(2).Info(fmt.Sprintf("Substituting %s with '%s'\n", subst, v)) s = multiWordReplace(s, subst, v) } From 344d2bfd24be4bfcb2d351411aa2144e47a874ce Mon Sep 17 00:00:00 2001 From: Liz Rice Date: Fri, 29 Jun 2018 12:19:34 +0100 Subject: [PATCH 4/5] Utility for getting the right config file for the Kubernetes version --- cmd/util.go | 53 ++++++++++++++++++++++++++++++++++++++++++++++++ cmd/util_test.go | 44 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/cmd/util.go b/cmd/util.go index 98a3769..5c36fa6 100644 --- a/cmd/util.go +++ b/cmd/util.go @@ -4,7 +4,9 @@ import ( "fmt" "os" "os/exec" + "path/filepath" "regexp" + "strconv" "strings" "github.com/aquasecurity/kube-bench/check" @@ -116,6 +118,57 @@ func getBinaries(v *viper.Viper) map[string]string { return binmap } +// getConfigFilePath locates the config files we should be using based on either the specified +// version, or the running version of kubernetes if not specified +func getConfigFilePath(specifiedVersion string, runningVersion string, filename string) (path string, err error) { + var fileVersion string + + if specifiedVersion != "" { + fileVersion = specifiedVersion + } else { + fileVersion = runningVersion + } + + for { + path = filepath.Join(cfgDir, fileVersion) + file := filepath.Join(path, string(filename)) + glog.V(2).Info(fmt.Sprintf("Looking for config file: %s\n", file)) + + if _, err = os.Stat(file); !os.IsNotExist(err) { + if specifiedVersion == "" && fileVersion != runningVersion { + glog.V(1).Info(fmt.Sprintf("No test file found for %s - using tests for Kubernetes %s\n", runningVersion, fileVersion)) + } + return path, nil + } + + // If we were given an explicit version to look for, don't look for any others + if specifiedVersion != "" { + return "", err + } + + fileVersion = decrementVersion(fileVersion) + if fileVersion == "" { + return "", fmt.Errorf("no test files found <= runningVersion") + } + } +} + +// decrementVersion decrements the version number +// We want to decrement individually even through versions where we don't supply test files +// just in case someone wants to specify their own test files for that version +func decrementVersion(version string) string { + split := strings.Split(version, ".") + minor, err := strconv.Atoi(split[1]) + if err != nil { + return "" + } + if minor <= 1 { + return "" + } + split[1] = strconv.Itoa(minor - 1) + return strings.Join(split, ".") +} + // getConfigFiles finds which of the set of candidate config files exist // accepts a string 't' which indicates the type of config file, conf, // podspec or untifile. diff --git a/cmd/util_test.go b/cmd/util_test.go index 3650ea0..539f400 100644 --- a/cmd/util_test.go +++ b/cmd/util_test.go @@ -15,7 +15,9 @@ package cmd import ( + "io/ioutil" "os" + "path/filepath" "reflect" "strconv" "testing" @@ -306,3 +308,45 @@ func TestMakeSubsitutions(t *testing.T) { }) } } + +func TestGetConfigFilePath(t *testing.T) { + var err error + cfgDir, err = ioutil.TempDir("", "kube-bench-test") + if err != nil { + t.Fatalf("Failed to create temp directory") + } + defer os.RemoveAll(cfgDir) + d := filepath.Join(cfgDir, "1.8") + err = os.Mkdir(d, 0666) + if err != nil { + t.Fatalf("Failed to create temp file") + } + ioutil.WriteFile(filepath.Join(d, "master.yaml"), []byte("hello world"), 0666) + + cases := []struct { + specifiedVersion string + runningVersion string + succeed bool + exp string + }{ + {runningVersion: "1.8", succeed: true, exp: d}, + {runningVersion: "1.9", succeed: true, exp: d}, + {runningVersion: "1.10", succeed: true, exp: d}, + {runningVersion: "1.1", succeed: false}, + {specifiedVersion: "1.8", succeed: true, exp: d}, + {specifiedVersion: "1.9", succeed: false}, + {specifiedVersion: "1.10", succeed: false}, + } + + for _, c := range cases { + t.Run(c.specifiedVersion+"-"+c.runningVersion, func(t *testing.T) { + path, err := getConfigFilePath(c.specifiedVersion, c.runningVersion, "/master.yaml") + if err != nil && c.succeed { + t.Fatalf("Error %v", err) + } + if path != c.exp { + t.Fatalf("Got %s expected %s", path, c.exp) + } + }) + } +} From 9d0141871ae05500a9d9b8a405541511a60341ee Mon Sep 17 00:00:00 2001 From: Liz Rice Date: Fri, 29 Jun 2018 12:20:29 +0100 Subject: [PATCH 5/5] Use new utility function for finding correct config files. Improve order of message output Remove unnecessary local variable --- cmd/common.go | 45 +++++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/cmd/common.go b/cmd/common.go index 1536d7c..69233c9 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -17,6 +17,7 @@ package cmd import ( "fmt" "io/ioutil" + "os" "path/filepath" "github.com/aquasecurity/kube-bench/check" @@ -28,55 +29,50 @@ var ( errmsgs string ) -func runChecks(t check.NodeType) { +func runChecks(nodetype check.NodeType) { var summary check.Summary - var nodetype string var file string var err error var typeConf *viper.Viper - switch t { + switch nodetype { case check.MASTER: file = masterFile - nodetype = "master" case check.NODE: file = nodeFile - nodetype = "node" case check.FEDERATED: file = federatedFile - nodetype = "federated" } - var ver string - if kubeVersion != "" { - ver = kubeVersion - } else { - ver = getKubeVersion() - switch ver { - case "1.9", "1.10": - continueWithError(nil, fmt.Sprintf("No CIS spec for %s - using tests from CIS 1.2.0 spec for Kubernetes 1.8\n", ver)) - ver = "1.8" - } + path, err := getConfigFilePath(kubeVersion, getKubeVersion(), file) + if err != nil { + exitWithError(fmt.Errorf("can't find %s controls file in %s: %v", nodetype, cfgDir, err)) } - path := filepath.Join(cfgDir, ver) def := filepath.Join(path, file) - in, err := ioutil.ReadFile(def) if err != nil { - exitWithError(fmt.Errorf("error opening %s controls file: %v", t, err)) + exitWithError(fmt.Errorf("error opening %s controls file: %v", nodetype, err)) } + glog.V(1).Info(fmt.Sprintf("Using benchmark file: %s\n", def)) + // Merge kubernetes version specific config if any. viper.SetConfigFile(path + "/config.yaml") err = viper.MergeInConfig() if err != nil { - continueWithError(err, fmt.Sprintf("Reading %s specific configuration file", ver)) + if os.IsNotExist(err) { + glog.V(2).Info(fmt.Sprintf("No version-specific config.yaml file in %s", path)) + } else { + exitWithError(fmt.Errorf("couldn't read config file %s: %v", path+"/config.yaml", err)) + } + } else { + glog.V(1).Info(fmt.Sprintf("Using config file: %s\n", viper.ConfigFileUsed())) } - typeConf = viper.Sub(nodetype) // Get the set of exectuables and config files we care about on this type of node. This also // checks that the executables we need for the node type are running. + typeConf = viper.Sub(string(nodetype)) binmap := getBinaries(typeConf) confmap := getConfigFiles(typeConf) @@ -85,12 +81,9 @@ func runChecks(t check.NodeType) { s = makeSubstitutions(s, "bin", binmap) s = makeSubstitutions(s, "conf", confmap) - glog.V(1).Info(fmt.Sprintf("Using config file: %s\n", viper.ConfigFileUsed())) - glog.V(1).Info(fmt.Sprintf("Using benchmark file: %s\n", def)) - - controls, err := check.NewControls(t, []byte(s)) + controls, err := check.NewControls(nodetype, []byte(s)) if err != nil { - exitWithError(fmt.Errorf("error setting up %s controls: %v", t, err)) + exitWithError(fmt.Errorf("error setting up %s controls: %v", nodetype, err)) } if groupList != "" && checkList == "" {