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 b618cff..28fe8ba 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -18,8 +18,13 @@ import ( "fmt" "io/ioutil" + "os" + "time" + "github.com/aquasecurity/kube-bench/check" "github.com/golang/glog" + "github.com/jinzhu/gorm" + _ "github.com/jinzhu/gorm/dialects/postgres" "github.com/spf13/viper" ) @@ -118,7 +123,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) + } } } @@ -168,3 +183,51 @@ func prettyPrint(r *check.Controls, summary check.Summary) { summary.Pass, summary.Fail, summary.Warn, ) } + +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 == "" { + err := fmt.Errorf("Environment variable %s missing", envVarsPrefix+"_"+k) + panic(err) + } + } + + 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 { + panic(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 { + panic(err) + } + + db.Debug().AutoMigrate(&ScanResult{}) + db.Save(&ScanResult{ScanHost: hostname, ScanTime: timestamp, ScanInfo: jsonInfo}) +} 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. diff --git a/makefile b/makefile index c0a68dd..53cdb79 100644 --- a/makefile +++ b/makefile @@ -1,4 +1,4 @@ -SOURCES := $(shell find . -name '*.go') +OURCES := $(shell find . -name '*.go') TARGET_OS := linux BINARY := kube-bench