diff --git a/check/controls.go b/check/controls.go index 909adb7..8660e81 100644 --- a/check/controls.go +++ b/check/controls.go @@ -23,11 +23,11 @@ import ( // Controls holds all controls to check for master nodes. type Controls struct { - ID string `yaml:"id"` + ID string `yaml:"id"` Version string - Text string - Type NodeType - Groups []*Group + Text string + Type NodeType + Groups []*Group Summary } diff --git a/cmd/common.go b/cmd/common.go index 553656d..3e01f2a 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -104,7 +104,17 @@ func runChecks(t check.NodeType) { fmt.Println(string(out)) } else { - prettyPrint(controls, summary) + // if we want to store in PostgreSQL, convert to JSON and save it + if (summary.Fail > 0 || summary.Warn > 0 || summary.Pass > 0) && pgSql { + out, err := controls.JSON() + if err != nil { + exitWithError(fmt.Errorf("failed to output in JSON format: %v", err)) + } + + savePgsql(string(out)) + } else { + prettyPrint(controls, summary) + } } } diff --git a/cmd/database.go b/cmd/database.go new file mode 100644 index 0000000..46668ae --- /dev/null +++ b/cmd/database.go @@ -0,0 +1,60 @@ +package cmd + +import ( + "fmt" + "os" + "time" + + "github.com/golang/glog" + "github.com/jinzhu/gorm" + _ "github.com/jinzhu/gorm/dialects/postgres" + "github.com/spf13/viper" +) + +func savePgsql(jsonInfo string) { + envVars := map[string]string{ + "PGSQL_HOST": viper.GetString("PGSQL_HOST"), + "PGSQL_USER": viper.GetString("PGSQL_USER"), + "PGSQL_DBNAME": viper.GetString("PGSQL_DBNAME"), + "PGSQL_SSLMODE": viper.GetString("PGSQL_SSLMODE"), + "PGSQL_PASSWORD": viper.GetString("PGSQL_PASSWORD"), + } + + for k, v := range envVars { + if v == "" { + exitWithError(fmt.Errorf("environment variable %s is missing", envVarsPrefix+"_"+k)) + } + } + + connInfo := fmt.Sprintf("host=%s user=%s dbname=%s sslmode=%s password=%s", + envVars["PGSQL_HOST"], + envVars["PGSQL_USER"], + envVars["PGSQL_DBNAME"], + envVars["PGSQL_SSLMODE"], + envVars["PGSQL_PASSWORD"], + ) + + hostname, err := os.Hostname() + if err != nil { + exitWithError(fmt.Errorf("received error looking up hostname: %s", err)) + } + + timestamp := time.Now() + + type ScanResult struct { + gorm.Model + ScanHost string `gorm:"type:varchar(63) not null"` // https://www.ietf.org/rfc/rfc1035.txt + ScanTime time.Time `gorm:"not null"` + ScanInfo string `gorm:"type:jsonb not null"` + } + + db, err := gorm.Open("postgres", connInfo) + defer db.Close() + if err != nil { + exitWithError(fmt.Errorf("received error connecting to database: %s", err)) + } + + db.Debug().AutoMigrate(&ScanResult{}) + db.Save(&ScanResult{ScanHost: hostname, ScanTime: timestamp, ScanInfo: jsonInfo}) + glog.V(2).Info(fmt.Sprintf("successfully stored result to: %s", envVars["PGSQL_HOST"])) +} diff --git a/cmd/root.go b/cmd/root.go index 601804e..08be2b3 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -25,10 +25,11 @@ import ( ) var ( - cfgDir = "./cfg" - cfgFile string - + envVarsPrefix = "KUBE_BENCH" + cfgDir = "./cfg" + cfgFile string jsonFmt bool + pgSql bool checkList string groupList string masterFile string @@ -59,6 +60,7 @@ func init() { cobra.OnInitialize(initConfig) RootCmd.PersistentFlags().BoolVar(&jsonFmt, "json", false, "Prints the results as JSON") + RootCmd.PersistentFlags().BoolVar(&pgSql, "pgsql", false, "Save the results to PostgreSQL") RootCmd.PersistentFlags().StringVarP( &checkList, "check", @@ -90,7 +92,7 @@ func initConfig() { viper.AddConfigPath(cfgDir) // adding ./cfg as first search path } - viper.SetEnvPrefix("KUBE_BENCH") + viper.SetEnvPrefix(envVarsPrefix) viper.AutomaticEnv() // read in environment variables that match // If a config file is found, read it in.