From bd5352938709a667a0bc5fefb2775e38ecab6ef2 Mon Sep 17 00:00:00 2001 From: Abubakr-Sadik Nii Nai Davis Date: Fri, 7 Jul 2017 17:01:30 +0000 Subject: [PATCH] Fix issue #16 about supporting verbosity. --- check/check.go | 52 +++++++++++++++++++++++++++++++++++------------ check/controls.go | 8 ++++---- cmd/common.go | 39 ++++++++++++++++++++++++----------- cmd/root.go | 3 +++ main.go | 4 +++- 5 files changed, 76 insertions(+), 30 deletions(-) diff --git a/check/check.go b/check/check.go index 0e16a42..53f8000 100644 --- a/check/check.go +++ b/check/check.go @@ -47,6 +47,13 @@ const ( FEDERATED NodeType = "federated" ) +func handleError(err error, context string) (errmsg string) { + if err != nil { + errmsg = fmt.Sprintf("%s, error: %s\n", context, err) + } + return +} + // Check contains information about a recommendation in the // CIS Kubernetes 1.6+ document. type Check struct { @@ -62,8 +69,9 @@ type Check struct { // Run executes the audit commands specified in a check and outputs // the results. -func (c *Check) Run() { +func (c *Check) Run(verbose bool) { var out bytes.Buffer + var errmsgs string // Check if command exists or exit with WARN. for _, cmd := range c.Commands { @@ -88,18 +96,24 @@ func (c *Check) Run() { cs := c.Commands // Initialize command pipeline - cs[0].Stderr = os.Stderr + // cs[0].Stderr = os.Stderr cs[n-1].Stdout = &out i := 1 var err error + errmsgs = "" + for i < n { cs[i-1].Stdout, err = cs[i].StdinPipe() - if err != nil { - fmt.Fprintf(os.Stderr, "%s: %s\n", cs[i].Args, err) - } - - cs[i].Stderr = os.Stderr + errmsgs += handleError( + err, + fmt.Sprintf("check.Run: Audit %s failed\nfailing command: %s", + c.Audit, + cs[i].Args, + ), + ) + + // cs[i].Stderr = os.Stderr i++ } @@ -107,9 +121,13 @@ func (c *Check) Run() { i = 0 for i < n { err := cs[i].Start() - if err != nil { - fmt.Fprintf(os.Stderr, "%s: %s\n", cs[i].Args, err) - } + errmsgs += handleError( + err, + fmt.Sprintf("check.Run: Audit %s failed\nfailing command: %s", + c.Audit, + cs[i].Args, + ), + ) i++ } @@ -117,9 +135,13 @@ func (c *Check) Run() { i = 0 for i < n { err := cs[i].Wait() - if err != nil { - fmt.Fprintf(os.Stderr, "%s: %s\n", cs[i].Args, err) - } + errmsgs += handleError( + err, + fmt.Sprintf("check.Run: Audit %s failed\nfailing command: %s", + c.Audit, + cs[i].Args, + ), + ) if i < n-1 { cs[i].Stdout.(io.Closer).Close() @@ -128,6 +150,10 @@ func (c *Check) Run() { i++ } + if verbose { + fmt.Fprintf(os.Stderr, "%s\n", errmsgs) + } + res := c.Tests.execute(out.String()) if res { c.State = PASS diff --git a/check/controls.go b/check/controls.go index dfea006..8f7530c 100644 --- a/check/controls.go +++ b/check/controls.go @@ -68,7 +68,7 @@ func NewControls(t NodeType, in []byte) (*Controls, error) { } // RunGroup runs all checks in a group. -func (controls *Controls) RunGroup(gids ...string) Summary { +func (controls *Controls) RunGroup(verbose bool, gids ...string) Summary { g := []*Group{} controls.Summary.Pass, controls.Summary.Fail, controls.Summary.Warn = 0, 0, 0 @@ -82,7 +82,7 @@ func (controls *Controls) RunGroup(gids ...string) Summary { for _, gid := range gids { if gid == group.ID { for _, check := range group.Checks { - check.Run() + check.Run(verbose) summarize(controls, check) } @@ -96,7 +96,7 @@ func (controls *Controls) RunGroup(gids ...string) Summary { } // RunChecks runs the checks with the supplied IDs. -func (controls *Controls) RunChecks(ids ...string) Summary { +func (controls *Controls) RunChecks(verbose bool, ids ...string) Summary { g := []*Group{} m := make(map[string]*Group) controls.Summary.Pass, controls.Summary.Fail, controls.Summary.Warn = 0, 0, 0 @@ -110,7 +110,7 @@ func (controls *Controls) RunChecks(ids ...string) Summary { for _, check := range group.Checks { for _, id := range ids { if id == check.ID { - check.Run() + check.Run(verbose) summarize(controls, check) // Check if we have already added this checks group. diff --git a/cmd/common.go b/cmd/common.go index 9971e3c..f58c086 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -27,6 +27,8 @@ import ( ) var ( + errmsgs string + kubeMasterBin = []string{"kube-apiserver", "kube-scheduler", "kube-controller-manager"} xMasterBin = []string{"etcd", "flanneld"} kubeMasterConf = []string{} @@ -51,6 +53,13 @@ var ( } ) +func handleError(err error, context string) (errmsg string) { + if err != nil { + errmsg = fmt.Sprintf("%s, error: %s\n", context, err) + } + return +} + func runChecks(t check.NodeType) { var summary check.Summary var file string @@ -85,18 +94,18 @@ func runChecks(t check.NodeType) { if groupList != "" && checkList == "" { ids := cleanIDs(groupList) - summary = controls.RunGroup(ids...) + summary = controls.RunGroup(verbose, ids...) } else if checkList != "" && groupList == "" { ids := cleanIDs(checkList) - summary = controls.RunChecks(ids...) + summary = controls.RunChecks(verbose, ids...) } else if checkList != "" && groupList != "" { fmt.Fprintf(os.Stderr, "group option and check option can't be used together\n") os.Exit(1) } else { - summary = controls.RunGroup() + summary = controls.RunGroup(verbose) } // if we successfully ran some tests and it's json format, ignore the warnings @@ -129,6 +138,8 @@ func cleanIDs(list string) []string { // Any check failing here is a show stopper. func verifyNodeType(t check.NodeType) []string { var w []string + // Always clear out error messages. + errmsgs = "" // Set up and check for config files. kubeConfDir = viper.Get("kubeConfDir").(string) @@ -160,6 +171,10 @@ func verifyNodeType(t check.NodeType) []string { w = append(w, verifyKubeVersion(kubeFederatedBin[0])...) } + if verbose { + fmt.Fprintf(os.Stderr, "%s\n", errmsgs) + } + return w } @@ -239,11 +254,11 @@ func verifyBin(binPath []string) []string { // Run ps command cmd := exec.Command("ps", "-C", binList, "-o", "cmd", "--no-headers") - cmd.Stderr = os.Stderr out, err := cmd.Output() - if err != nil { - fmt.Fprintf(os.Stderr, "%s: %s\n", cmd.Args, err) - } + errmsgs += handleError( + err, + fmt.Sprintf("verifyBin: %s failed", binList), + ) // Actual verification for _, b := range binPath { @@ -264,15 +279,15 @@ func verifyKubeVersion(b string) []string { // Check version cmd := exec.Command(b, "--version") - cmd.Stderr = os.Stderr out, err := cmd.Output() - if err != nil { - fmt.Fprintf(os.Stderr, "%s: %s\n", cmd.Args, err) - } + errmsgs += handleError( + err, + fmt.Sprintf("verifyKubeVersion: failed"), + ) matched := strings.Contains(string(out), kubeVersion) if !matched { - w = append(w, fmt.Sprintf("%s unsupported version.", b)) + w = append(w, fmt.Sprintf("%s unsupported version\n", b)) } return w diff --git a/cmd/root.go b/cmd/root.go index e27ede0..32bdf03 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -37,6 +37,8 @@ var ( kubeConfDir string etcdConfDir string flanneldConfDir string + + verbose bool ) // RootCmd represents the base command when called without any subcommands @@ -72,6 +74,7 @@ func init() { `Run all the checks under this comma-delimited list of groups. Example --group="1.1"`, ) RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is ./cfg/config.yaml)") + RootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output (default false)") } // initConfig reads in config file and ENV variables if set. diff --git a/main.go b/main.go index f42e122..934c962 100644 --- a/main.go +++ b/main.go @@ -14,7 +14,9 @@ package main -import "github.com/aquasecurity/kube-bench/cmd" +import ( + "github.com/aquasecurity/kube-bench/cmd" +) func main() { cmd.Execute()