diff --git a/README.md b/README.md index 2860d179..ddee4832 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ Set-ExecutionPolicy RemoteSigned -scope CurrentUser | `-e`/`--exclude-namespaces` | Scan all namespaces | Namespaces to exclude from scanning. Recommended to exclude `kube-system` and `kube-public` namespaces | | | `--include-namespaces` | Scan all namespaces | Scan specific namespaces | | | `-s`/`--silent` | Display progress messages | Silent progress messages | | -| `-t`/`--fail-threshold` | `0` (do not fail) | fail command (return exit code 1) if result is below threshold | `0` -> `100` | +| `-t`/`--fail-threshold` | `100` (do not fail) | fail command (return exit code 1) if result is above threshold | `0` -> `100` | | `-f`/`--format` | `pretty-printer` | Output format | `pretty-printer`/`json`/`junit`/`prometheus` | | `-o`/`--output` | print to stdout | Save scan result in file | | | `--use-from` | | Load local framework object from specified path. If not used will download latest | | diff --git a/cautils/customerloader.go b/cautils/customerloader.go index d6000681..fab50611 100644 --- a/cautils/customerloader.go +++ b/cautils/customerloader.go @@ -180,7 +180,9 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken } } if c.configObj.ClusterName == "" { - c.configObj.ClusterName = adoptClusterName(k8sinterface.GetClusterName()) + c.configObj.ClusterName = AdoptClusterName(k8sinterface.GetClusterName()) + } else { // override the cluster name if it has unwanted characters + c.configObj.ClusterName = AdoptClusterName(c.configObj.ClusterName) } return c @@ -425,6 +427,6 @@ func DeleteConfigFile() error { return os.Remove(ConfigFileFullPath()) } -func adoptClusterName(clusterName string) string { +func AdoptClusterName(clusterName string) string { return strings.ReplaceAll(clusterName, "/", "-") } diff --git a/cautils/display.go b/cautils/display.go index d82e9114..155124d1 100644 --- a/cautils/display.go +++ b/cautils/display.go @@ -51,8 +51,8 @@ func ErrorDisplay(str string) { if IsSilent() { return } - SuccessDisplay(os.Stdout, "[Error] ") - SimpleDisplay(os.Stdout, fmt.Sprintf("%s\n", str)) + FailureDisplay(os.Stderr, "[Error] ") + SimpleDisplay(os.Stderr, fmt.Sprintf("%s\n", str)) } diff --git a/cautils/versioncheck.go b/cautils/versioncheck.go index ab5d4217..aaa72795 100644 --- a/cautils/versioncheck.go +++ b/cautils/versioncheck.go @@ -65,6 +65,9 @@ func NewVersionCheckRequest(buildNumber, frameworkName, frameworkVersion, scanni if buildNumber == "" { buildNumber = UnknownBuildNumber } + if scanningTarget == "" { + scanningTarget = "unknown" + } return &VersionCheckRequest{ Client: "kubescape", ClientVersion: buildNumber, @@ -82,7 +85,7 @@ func (v *VersionCheckHandlerMock) CheckLatestVersion(versionData *VersionCheckRe func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckRequest) error { defer func() { if err := recover(); err != nil { - fmt.Println("failed to get latest version") + WarningDisplay(os.Stderr, "failed to get latest version\n") } }() @@ -93,7 +96,7 @@ func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckReques if latestVersion.ClientUpdate != "" { if BuildNumber != "" && BuildNumber < latestVersion.ClientUpdate { - fmt.Println(warningMessage(latestVersion.Client, latestVersion.ClientUpdate)) + WarningDisplay(os.Stderr, warningMessage(latestVersion.Client, latestVersion.ClientUpdate), "\n") } } @@ -103,7 +106,7 @@ func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckReques // } if latestVersion.Message != "" { - fmt.Println(latestVersion.Message) + InfoDisplay(os.Stderr, latestVersion.Message, "\n") } return nil diff --git a/clihandler/cmd/download.go b/clihandler/cmd/download.go index 74e3cad3..bc4bd3ca 100644 --- a/clihandler/cmd/download.go +++ b/clihandler/cmd/download.go @@ -2,6 +2,7 @@ package cmd import ( "fmt" + "os" "strings" "github.com/armosec/kubescape/cautils" @@ -25,43 +26,10 @@ var downloadCmd = &cobra.Command{ return nil }, RunE: func(cmd *cobra.Command, args []string) error { - if strings.EqualFold(args[0], "framework") { - downloadInfo.FrameworkName = strings.ToLower(args[1]) - g := getter.NewDownloadReleasedPolicy() - if err := g.SetRegoObjects(); err != nil { - return err - } - - if downloadInfo.Path == "" { - downloadInfo.Path = getter.GetDefaultPath(downloadInfo.FrameworkName + ".json") - } - frameworks, err := g.GetFramework(downloadInfo.FrameworkName) - if err != nil { - return err - } - err = getter.SaveFrameworkInFile(frameworks, downloadInfo.Path) - if err != nil { - return err - } - } else if strings.EqualFold(args[0], "control") { - downloadInfo.ControlName = strings.ToLower(args[1]) - g := getter.NewDownloadReleasedPolicy() - if err := g.SetRegoObjects(); err != nil { - return err - } - if downloadInfo.Path == "" { - downloadInfo.Path = getter.GetDefaultPath(downloadInfo.ControlName + ".json") - } - controls, err := g.GetControl(downloadInfo.ControlName) - if err != nil { - return err - } - err = getter.SaveControlInFile(controls, downloadInfo.Path) - if err != nil { - return err - } + if err := download(args); err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) } - return nil }, } @@ -71,3 +39,57 @@ func init() { downloadInfo = cautils.DownloadInfo{} downloadCmd.Flags().StringVarP(&downloadInfo.Path, "output", "o", "", "Output file. If specified, will store save to `~/.kubescape/.json`") } + +func download(args []string) error { + switch strings.ToLower(args[0]) { + case "framework": + return downloadFramework(args[1]) + case "control": + return downloadControl(args[1]) + // case "exceptions": + // case "artifacts": + default: + return fmt.Errorf("unknown command to download") + } +} + +func downloadFramework(frameworkName string) error { + downloadInfo.FrameworkName = strings.ToLower(frameworkName) + g := getter.NewDownloadReleasedPolicy() + if err := g.SetRegoObjects(); err != nil { + return err + } + + if downloadInfo.Path == "" { + downloadInfo.Path = getter.GetDefaultPath(downloadInfo.FrameworkName + ".json") + } + frameworks, err := g.GetFramework(downloadInfo.FrameworkName) + if err != nil { + return err + } + err = getter.SaveFrameworkInFile(frameworks, downloadInfo.Path) + if err != nil { + return err + } + return nil +} + +func downloadControl(controlName string) error { + downloadInfo.ControlName = strings.ToLower(controlName) + g := getter.NewDownloadReleasedPolicy() + if err := g.SetRegoObjects(); err != nil { + return err + } + if downloadInfo.Path == "" { + downloadInfo.Path = getter.GetDefaultPath(downloadInfo.ControlName + ".json") + } + controls, err := g.GetControl(downloadInfo.ControlName) + if err != nil { + return err + } + err = getter.SaveControlInFile(controls, downloadInfo.Path) + if err != nil { + return err + } + return nil +} diff --git a/clihandler/cmd/framework.go b/clihandler/cmd/framework.go index 03d2e1d2..ca2aaf04 100644 --- a/clihandler/cmd/framework.go +++ b/clihandler/cmd/framework.go @@ -90,7 +90,8 @@ var frameworkCmd = &cobra.Command{ cautils.SetSilentMode(scanInfo.Silent) err := clihandler.ScanCliSetup(&scanInfo) if err != nil { - return err + fmt.Fprintf(os.Stderr, "error: %v\n\n", err) + os.Exit(1) } return nil }, diff --git a/clihandler/cmd/scan.go b/clihandler/cmd/scan.go index e45abd3e..9370f4ab 100644 --- a/clihandler/cmd/scan.go +++ b/clihandler/cmd/scan.go @@ -39,7 +39,7 @@ func init() { scanCmd.PersistentFlags().StringVar(&scanInfo.ControlsInputs, "controls-config", "", "Path to an controls-config obj. If not set will download controls-config from ARMO management portal") scanCmd.PersistentFlags().StringVar(&scanInfo.UseExceptions, "exceptions", "", "Path to an exceptions obj. If not set will download exceptions from ARMO management portal") scanCmd.PersistentFlags().StringVarP(&scanInfo.ExcludedNamespaces, "exclude-namespaces", "e", "", "Namespaces to exclude from scanning. Recommended: kube-system,kube-public") - scanCmd.PersistentFlags().Uint16VarP(&scanInfo.FailThreshold, "fail-threshold", "t", 0, "Failure threshold is the percent below which the command fails and returns exit code 1") + scanCmd.PersistentFlags().Uint16VarP(&scanInfo.FailThreshold, "fail-threshold", "t", 100, "Failure threshold is the percent above which the command fails and returns exit code 1") scanCmd.PersistentFlags().StringVarP(&scanInfo.Format, "format", "f", "pretty-printer", `Output format. Supported formats: "pretty-printer"/"json"/"junit"/"prometheus"`) scanCmd.PersistentFlags().StringVar(&scanInfo.IncludeNamespaces, "include-namespaces", "", "scan specific namespaces. e.g: --include-namespaces ns-a,ns-b") scanCmd.PersistentFlags().BoolVarP(&scanInfo.Local, "keep-local", "", false, "If you do not want your Kubescape results reported to Armo backend. Use this flag if you ran with the '--submit' flag in the past and you do not want to submit your current scan results") @@ -50,7 +50,7 @@ func init() { scanCmd.PersistentFlags().BoolVarP(&scanInfo.Silent, "silent", "s", false, "Silent progress messages") scanCmd.PersistentFlags().BoolVarP(&scanInfo.Submit, "submit", "", false, "Send the scan results to Armo management portal where you can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more. By default the results are not submitted") - hostF := scanCmd.PersistentFlags().VarPF(&scanInfo.HostSensor, "enable-host-scan", "", "Deploy ARMO K8s host-sensor daemonset in the scanned cluster. Deleting it right after we collecting the data. Required to collect valueable data from cluster nodes for certain controls") + hostF := scanCmd.PersistentFlags().VarPF(&scanInfo.HostSensor, "enable-host-scan", "", "Deploy ARMO K8s host-sensor daemonset in the scanned cluster. Deleting it right after we collecting the data. Required to collect valueable data from cluster nodes for certain controls") hostF.NoOptDefVal = "true" hostF.DefValue = "false, for no TTY in stdin" } diff --git a/clihandler/initcli.go b/clihandler/initcli.go index f574286f..eebd806f 100644 --- a/clihandler/initcli.go +++ b/clihandler/initcli.go @@ -6,9 +6,7 @@ import ( "os" "github.com/armosec/armoapi-go/armotypes" - "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/kubescape/cautils" - "github.com/armosec/kubescape/cautils/getter" "github.com/armosec/kubescape/clihandler/cliinterfaces" "github.com/armosec/kubescape/hostsensorutils" "github.com/armosec/kubescape/opaprocessor" @@ -18,8 +16,6 @@ import ( "github.com/armosec/kubescape/resultshandling/printer" "github.com/armosec/kubescape/resultshandling/reporter" "github.com/armosec/opa-utils/reporthandling" - "github.com/armosec/rbac-utils/rbacscanner" - "github.com/golang/glog" "github.com/mattn/go-isatty" ) @@ -31,60 +27,36 @@ type componentInterfaces struct { hostSensorHandler hostsensorutils.IHostSensor } -func initHostSensor(scanInfo *cautils.ScanInfo, k8s *k8sinterface.KubernetesApi) hostsensorutils.IHostSensor { - - hasHostSensorControls := true - // we need to determined which controls needs host sensor - if scanInfo.HostSensor.Get() == nil && hasHostSensorControls { - scanInfo.HostSensor.SetBool(askUserForHostSensor()) - } - if hostSensorVal := scanInfo.HostSensor.Get(); hostSensorVal != nil && *hostSensorVal { - hostSensorHandler, err := hostsensorutils.NewHostSensorHandler(k8s) - if err != nil { - glog.Errorf("failed to create host sensor: %v", err) - return &hostsensorutils.HostSensorHandlerMock{} - } - return hostSensorHandler - } - return &hostsensorutils.HostSensorHandlerMock{} -} - func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces { - var resourceHandler resourcehandler.IResourceHandler - var hostSensorHandler hostsensorutils.IHostSensor - var tenantConfig cautils.ITenantConfig - hostSensorHandler = &hostsensorutils.HostSensorHandlerMock{} + k8s := getKubernetesApi(scanInfo) - // scanning environment - scanningTarget := scanInfo.GetScanningEnvironment() - switch scanningTarget { - case cautils.ScanLocalFiles: - k8sinterface.ConnectedToCluster = false // DEPRECATED ? - scanInfo.Local = true // do not submit results when scanning YAML files + tenantConfig := getTenantConfig(scanInfo, k8s) - // not scanning a cluster - use localConfig struct - tenantConfig = cautils.NewLocalConfig(getter.GetArmoAPIConnector(), scanInfo.Account) - - // load resources from file - resourceHandler = resourcehandler.NewFileResourceHandler(scanInfo.InputPatterns) - case cautils.ScanCluster: - k8s := k8sinterface.NewKubernetesApi() // initialize kubernetes api object - - // use clusterConfig struct - tenantConfig = cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account) - - // pull k8s resources - hostSensorHandler = initHostSensor(scanInfo, k8s) - rbacObjects := cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, tenantConfig.GetCustomerGUID(), tenantConfig.GetClusterName())) - resourceHandler = resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo), hostSensorHandler, rbacObjects) + // Set submit behavior AFTER loading tenant config + setSubmitBehavior(scanInfo, tenantConfig) + hostSensorHandler := getHostSensorHandler(scanInfo, k8s) + if err := hostSensorHandler.Init(); err != nil { + errMsg := "failed to init host sensor" + if scanInfo.VerboseMode { + errMsg = fmt.Sprintf("%s: %v", errMsg, err) + } + cautils.ErrorDisplay(errMsg) + hostSensorHandler = &hostsensorutils.HostSensorHandlerMock{} } + // excluding hostsensor namespace + if len(scanInfo.IncludeNamespaces) == 0 && hostSensorHandler.GetNamespace() != "" { + scanInfo.ExcludedNamespaces = fmt.Sprintf("%s,%s", scanInfo.ExcludedNamespaces, hostSensorHandler.GetNamespace()) + } + + resourceHandler := getResourceHandler(scanInfo, tenantConfig, k8s, hostSensorHandler) + // reporting behavior - setup reporter - reportHandler := getReporter(scanInfo, tenantConfig) + reportHandler := getReporter(tenantConfig, scanInfo.Submit) v := cautils.NewIVersionCheckHandler() - v.CheckLatestVersion(cautils.NewVersionCheckRequest(cautils.BuildNumber, policyIdentifierNames(scanInfo.PolicyIdentifier), "", scanningTarget)) + v.CheckLatestVersion(cautils.NewVersionCheckRequest(cautils.BuildNumber, policyIdentifierNames(scanInfo.PolicyIdentifier), "", scanInfo.GetScanningEnvironment())) // setup printer printerHandler := printer.GetPrinter(scanInfo.Format, scanInfo.VerboseMode) @@ -113,28 +85,19 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error { interfaces.report.SetClusterName(interfaces.tenantConfig.GetClusterName()) interfaces.report.SetCustomerGUID(interfaces.tenantConfig.GetCustomerGUID()) - if err := interfaces.hostSensorHandler.Init(); err != nil { - errMsg := "failed to init host sensor" - if scanInfo.VerboseMode { - errMsg = fmt.Sprintf("%s: %v", errMsg, err) - } - cautils.ErrorDisplay(errMsg) - } else if len(scanInfo.IncludeNamespaces) == 0 && interfaces.hostSensorHandler.GetNamespace() != "" { - scanInfo.ExcludedNamespaces = fmt.Sprintf("%s,%s", scanInfo.ExcludedNamespaces, interfaces.hostSensorHandler) - defer func() { - if err := interfaces.hostSensorHandler.TearDown(); err != nil { - errMsg := "failed to tear down host sensor" - if scanInfo.VerboseMode { - errMsg = fmt.Sprintf("%s: %v", errMsg, err) - } - cautils.ErrorDisplay(errMsg) - } - }() - } - // set policy getter only after setting the customerGUID setPolicyGetter(scanInfo, interfaces.tenantConfig.GetCustomerGUID()) + defer func() { + if err := interfaces.hostSensorHandler.TearDown(); err != nil { + errMsg := "failed to tear down host sensor" + if scanInfo.VerboseMode { + errMsg = fmt.Sprintf("%s: %v", errMsg, err) + } + cautils.ErrorDisplay(errMsg) + } + }() + // cli handler setup go func() { // policy handler setup @@ -158,9 +121,8 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error { // print report url interfaces.report.DisplayReportURL() - adjustedFailThreshold := float32(scanInfo.FailThreshold) / 100 - if score < adjustedFailThreshold { - return fmt.Errorf("Scan score is below threshold") + if score >= float32(scanInfo.FailThreshold) { + return fmt.Errorf("scan risk-score %.2f is above permitted threshold %d", score, scanInfo.FailThreshold) } return nil diff --git a/clihandler/initcliutils.go b/clihandler/initcliutils.go index c62bee2c..d455a54b 100644 --- a/clihandler/initcliutils.go +++ b/clihandler/initcliutils.go @@ -4,27 +4,70 @@ import ( "fmt" "os" + "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/kubescape/cautils" "github.com/armosec/kubescape/cautils/getter" + "github.com/armosec/kubescape/hostsensorutils" "github.com/armosec/kubescape/resourcehandler" "github.com/armosec/kubescape/resultshandling/reporter" "github.com/armosec/opa-utils/reporthandling" + "github.com/armosec/rbac-utils/rbacscanner" "github.com/golang/glog" ) -func getReporter(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig) reporter.IReport { - setSubmitBehavior(scanInfo, tenantConfig) - - if !scanInfo.Submit { - return reporter.NewReportMock() +func getKubernetesApi(scanInfo *cautils.ScanInfo) *k8sinterface.KubernetesApi { + if scanInfo.GetScanningEnvironment() == cautils.ScanLocalFiles { + return nil } - if !scanInfo.FrameworkScan { - return reporter.NewReportMock() + return k8sinterface.NewKubernetesApi() +} +func getTenantConfig(scanInfo *cautils.ScanInfo, k8s *k8sinterface.KubernetesApi) cautils.ITenantConfig { + if scanInfo.GetScanningEnvironment() == cautils.ScanLocalFiles { + return cautils.NewLocalConfig(getter.GetArmoAPIConnector(), scanInfo.Account) } - - return reporter.NewReportEventReceiver(tenantConfig.GetConfigObj()) + return cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account) } +func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, submit bool) *cautils.RBACObjects { + if submit { + return cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, tenantConfig.GetCustomerGUID(), tenantConfig.GetClusterName())) + } + return nil +} + +func getReporter(tenantConfig cautils.ITenantConfig, submit bool) reporter.IReport { + if submit { + return reporter.NewReportEventReceiver(tenantConfig.GetConfigObj()) + } + return reporter.NewReportMock() +} +func getResourceHandler(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, hostSensorHandler hostsensorutils.IHostSensor) resourcehandler.IResourceHandler { + if scanInfo.GetScanningEnvironment() == cautils.ScanLocalFiles { + return resourcehandler.NewFileResourceHandler(scanInfo.InputPatterns) + } + rbacObjects := getRBACHandler(tenantConfig, k8s, scanInfo.Submit) + return resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo), hostSensorHandler, rbacObjects) +} + +func getHostSensorHandler(scanInfo *cautils.ScanInfo, k8s *k8sinterface.KubernetesApi) hostsensorutils.IHostSensor { + if scanInfo.GetScanningEnvironment() == cautils.ScanLocalFiles { + return &hostsensorutils.HostSensorHandlerMock{} + } + hasHostSensorControls := true + // we need to determined which controls needs host sensor + if scanInfo.HostSensor.Get() == nil && hasHostSensorControls { + scanInfo.HostSensor.SetBool(askUserForHostSensor()) + } + if hostSensorVal := scanInfo.HostSensor.Get(); hostSensorVal != nil && *hostSensorVal { + hostSensorHandler, err := hostsensorutils.NewHostSensorHandler(k8s) + if err != nil || hostSensorHandler == nil { + glog.Errorf("failed to create host sensor: %v", err) + return &hostsensorutils.HostSensorHandlerMock{} + } + return hostSensorHandler + } + return &hostsensorutils.HostSensorHandlerMock{} +} func getFieldSelector(scanInfo *cautils.ScanInfo) resourcehandler.IFieldSelector { if scanInfo.IncludeNamespaces != "" { return resourcehandler.NewIncludeSelector(scanInfo.IncludeNamespaces) @@ -65,6 +108,18 @@ func setSubmitBehavior(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantC */ + // do not submit control scanning + if !scanInfo.FrameworkScan { + scanInfo.Submit = false + return + } + + // do not submit yaml/url scanning + if scanInfo.GetScanningEnvironment() == cautils.ScanLocalFiles { + scanInfo.Submit = false + return + } + if tenantConfig.IsConfigFound() { // config found in cache (submitted) if !scanInfo.Local { // Submit report diff --git a/go.mod b/go.mod index b218c096..72545313 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,11 @@ go 1.17 require ( github.com/armosec/armoapi-go v0.0.23 - github.com/armosec/k8s-interface v0.0.35 - github.com/armosec/opa-utils v0.0.62 - github.com/armosec/rbac-utils v0.0.8 + github.com/armosec/k8s-interface v0.0.50 + github.com/armosec/opa-utils v0.0.75 + github.com/armosec/rbac-utils v0.0.9 github.com/armosec/utils-go v0.0.3 - github.com/briandowns/spinner v1.16.0 + github.com/briandowns/spinner v1.18.0 github.com/enescakir/emoji v1.0.0 github.com/fatih/color v1.13.0 github.com/gofrs/uuid v4.1.0+incompatible @@ -36,6 +36,7 @@ require ( github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/armosec/utils-k8s-go v0.0.1 // indirect + github.com/aws/aws-sdk-go v1.41.11 // indirect github.com/coreos/go-oidc v2.2.1+incompatible // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/docker v20.10.9+incompatible // indirect @@ -48,12 +49,15 @@ require ( github.com/go-logr/logr v0.4.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/go-cmp v0.5.5 // indirect github.com/google/gofuzz v1.1.0 // indirect + github.com/googleapis/gax-go/v2 v2.0.5 // indirect github.com/googleapis/gnostic v0.5.5 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.11 // indirect github.com/mattn/go-colorable v0.1.9 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect @@ -69,10 +73,11 @@ require ( github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b // indirect + go.opencensus.io v0.23.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.19.1 // indirect - golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 // indirect + golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect golang.org/x/net v0.0.0-20210825183410-e898025ed96a // indirect golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 // indirect golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect @@ -80,7 +85,10 @@ require ( golang.org/x/text v0.3.6 // indirect golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect gonum.org/v1/gonum v0.9.1 // indirect + google.golang.org/api v0.44.0 // indirect google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect + google.golang.org/grpc v1.38.0 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect diff --git a/go.sum b/go.sum index 6123fc1c..eea11167 100644 --- a/go.sum +++ b/go.sum @@ -87,15 +87,15 @@ github.com/armosec/armoapi-go v0.0.2/go.mod h1:vIK17yoKbJRQyZXWWLe3AqfqCRITxW8qm github.com/armosec/armoapi-go v0.0.23 h1:jqoLIWM5CR7DCD9fpFgN0ePqtHvOCoZv/XzCwsUluJU= github.com/armosec/armoapi-go v0.0.23/go.mod h1:iaVVGyc23QGGzAdv4n+szGQg3Rbpixn9yQTU3qWRpaw= github.com/armosec/k8s-interface v0.0.8/go.mod h1:xxS+V5QT3gVQTwZyAMMDrYLWGrfKOpiJ7Jfhfa0w9sM= -github.com/armosec/k8s-interface v0.0.19/go.mod h1:0ztwEWLez2lpMMKHbs0DoZV64S0YIA4s72vCs9iAAiE= -github.com/armosec/k8s-interface v0.0.26/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU= -github.com/armosec/k8s-interface v0.0.35 h1:8kfQT7MxNMPb5HL3Sv3PriI4eVQzogECbBzckso/HkA= -github.com/armosec/k8s-interface v0.0.35/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU= -github.com/armosec/opa-utils v0.0.62 h1:C/VN1HUDoa0BX2CTSqgjuKEIlb/rkIwGY2/UlvfS154= -github.com/armosec/opa-utils v0.0.62/go.mod h1:P63qZDPG3vjPtSQqwx2N4Nfm8VaboDul7R7kmnUvvWc= +github.com/armosec/k8s-interface v0.0.37/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU= +github.com/armosec/k8s-interface v0.0.50 h1:iLPGI0j85vwKANr9QDAnba4Efjg3DyIJg15jRJdvOnc= +github.com/armosec/k8s-interface v0.0.50/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU= +github.com/armosec/opa-utils v0.0.64/go.mod h1:6tQP8UDq2EvEfSqh8vrUdr/9QVSCG4sJfju1SXQOn4c= +github.com/armosec/opa-utils v0.0.75 h1:GBI3K18xc3WXJHIorIu4bGNAsfMYHUc1x7zueDz2ZbY= +github.com/armosec/opa-utils v0.0.75/go.mod h1:L7d+uiIIXAZ3LEyKtmEIbMcI1hWgWaXGpn5zVCqzwSU= github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40= -github.com/armosec/rbac-utils v0.0.8 h1:Hpr2p9F3Elp9NxbDAZiMlNUcQOdfyu9rWFE2fM2nNGw= -github.com/armosec/rbac-utils v0.0.8/go.mod h1:fmLZDgT5ILNo5gzR19boIIn7TOpfWh2pIgprxLrYN/g= +github.com/armosec/rbac-utils v0.0.9 h1:rIOWp4K7BELUNX32ktSjVbb8d/0SpH7W76W6Tf+8rzw= +github.com/armosec/rbac-utils v0.0.9/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0= github.com/armosec/utils-go v0.0.2/go.mod h1:itWmRLzRdsnwjpEOomL0mBWGnVNNIxSjDAdyc+b0iUo= github.com/armosec/utils-go v0.0.3 h1:uyQI676yRciQM0sSN9uPoqHkbspTxHO0kmzXhBeE/xU= github.com/armosec/utils-go v0.0.3/go.mod h1:itWmRLzRdsnwjpEOomL0mBWGnVNNIxSjDAdyc+b0iUo= @@ -103,6 +103,7 @@ github.com/armosec/utils-k8s-go v0.0.1 h1:Ay3y7fW+4+FjVc0+obOWm8YsnEvM31vPAVoKTy github.com/armosec/utils-k8s-go v0.0.1/go.mod h1:qrU4pmY2iZsOb39Eltpm0sTTNM3E4pmeyWx4dgDUC2U= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.41.1/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= +github.com/aws/aws-sdk-go v1.41.11 h1:QLouWsiYQ8i22kD8k58Dpdhio1A0MpT7bg9ZNXqEjuI= github.com/aws/aws-sdk-go v1.41.11/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= @@ -116,8 +117,8 @@ github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqO github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/briandowns/spinner v1.16.0 h1:DFmp6hEaIx2QXXuqSJmtfSBSAjRmpGiKG6ip2Wm/yOs= -github.com/briandowns/spinner v1.16.0/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ= +github.com/briandowns/spinner v1.18.0 h1:SJs0maNOs4FqhBwiJ3Gr7Z1D39/rukIVGQvpNZVHVcM= +github.com/briandowns/spinner v1.18.0/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/bytecodealliance/wasmtime-go v0.30.0 h1:WfYpr4WdqInt8m5/HvYinf+HrSEAIhItKIcth+qb1h4= github.com/bytecodealliance/wasmtime-go v0.30.0/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI= @@ -335,9 +336,11 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= @@ -382,7 +385,9 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= @@ -690,8 +695,9 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -887,6 +893,7 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1013,6 +1020,7 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0 h1:URs6qR1lAxDsqWITsQXI4ZkGiYJ5dHtRNiCpfs2OeKA= google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1070,6 +1078,7 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= @@ -1094,6 +1103,7 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= diff --git a/hostsensorutils/hostsensordataenvelope.go b/hostsensorutils/hostsensordataenvelope.go deleted file mode 100644 index de394157..00000000 --- a/hostsensorutils/hostsensordataenvelope.go +++ /dev/null @@ -1,67 +0,0 @@ -package hostsensorutils - -import ( - "encoding/json" - "fmt" - - "k8s.io/apimachinery/pkg/runtime/schema" -) - -type HostSensorDataEnvelope struct { - schema.GroupVersionResource - NodeName string `json:"nodeName"` - Data json.RawMessage `json:"data"` -} - -func (hsde *HostSensorDataEnvelope) SetNamespace(string) { - -} - -func (hsde *HostSensorDataEnvelope) SetName(val string) { - hsde.NodeName = val -} - -func (hsde *HostSensorDataEnvelope) SetKind(val string) { - hsde.Resource = val - -} - -func (hsde *HostSensorDataEnvelope) SetWorkload(val map[string]interface{}) { //deprecated - hsde.Data, _ = json.Marshal(val) -} - -func (hsde *HostSensorDataEnvelope) SetObject(val map[string]interface{}) { - hsde.Data, _ = json.Marshal(val) -} - -func (hsde *HostSensorDataEnvelope) GetNamespace() string { - return "" -} - -func (hsde *HostSensorDataEnvelope) GetName() string { - return hsde.NodeName -} - -func (hsde *HostSensorDataEnvelope) GetKind() string { - return hsde.Resource -} - -func (hsde *HostSensorDataEnvelope) GetApiVersion() string { - return hsde.Version -} - -func (hsde *HostSensorDataEnvelope) GetWorkload() map[string]interface{} { // DEPRECATED - res := map[string]interface{}{} - json.Unmarshal(hsde.Data, &res) - return res -} - -func (hsde *HostSensorDataEnvelope) GetObject() map[string]interface{} { - res := map[string]interface{}{} - json.Unmarshal(hsde.Data, &res) - return res -} - -func (hsde *HostSensorDataEnvelope) GetID() string { // -> /// - return fmt.Sprintf("%s/%s/%s/%s", hsde.Group, hsde.GetApiVersion(), hsde.GetKind(), hsde.GetName()) -} diff --git a/hostsensorutils/hostsensordeploy.go b/hostsensorutils/hostsensordeploy.go index 83a6d020..06e1c008 100644 --- a/hostsensorutils/hostsensordeploy.go +++ b/hostsensorutils/hostsensordeploy.go @@ -201,5 +201,8 @@ func (hsh *HostSensorHandler) TearDown() error { } func (hsh *HostSensorHandler) GetNamespace() string { + if hsh.DaemonSet == nil { + return "" + } return hsh.DaemonSet.Namespace } diff --git a/hostsensorutils/hostsensorgetfrompod.go b/hostsensorutils/hostsensorgetfrompod.go index 865bf276..aa58aae5 100644 --- a/hostsensorutils/hostsensorgetfrompod.go +++ b/hostsensorutils/hostsensorgetfrompod.go @@ -5,8 +5,9 @@ import ( "fmt" "sync" + "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/kubescape/cautils" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/armosec/opa-utils/objectsenvelopes/hostsensor" "sigs.k8s.io/yaml" ) @@ -56,12 +57,12 @@ func (hsh *HostSensorHandler) ForwardToPod(podName, path string) ([]byte, error) // sendAllPodsHTTPGETRequest fills the raw byte response in the envelope and the node name, but not the GroupVersionKind // so the caller is responsible to convert the raw data to some structured data and add the GroupVersionKind details -func (hsh *HostSensorHandler) sendAllPodsHTTPGETRequest(path string) ([]HostSensorDataEnvelope, error) { +func (hsh *HostSensorHandler) sendAllPodsHTTPGETRequest(path, requestKind string) ([]hostsensor.HostSensorDataEnvelope, error) { podList, err := hsh.getPodList() if err != nil { return nil, fmt.Errorf("failed to sendAllPodsHTTPGETRequest: %v", err) } - res := make([]HostSensorDataEnvelope, 0, len(podList)) + res := make([]hostsensor.HostSensorDataEnvelope, 0, len(podList)) resLock := sync.Mutex{} wg := sync.WaitGroup{} wg.Add(len(podList)) @@ -74,7 +75,12 @@ func (hsh *HostSensorHandler) sendAllPodsHTTPGETRequest(path string) ([]HostSens } else { resLock.Lock() defer resLock.Unlock() - res = append(res, HostSensorDataEnvelope{NodeName: podList[podName], Data: resBytes}) + hostSensorDataEnvelope := hostsensor.HostSensorDataEnvelope{} + hostSensorDataEnvelope.SetApiVersion(k8sinterface.JoinGroupVersion(hostsensor.GroupHostSensor, hostsensor.Version)) + hostSensorDataEnvelope.SetKind(requestKind) + hostSensorDataEnvelope.SetName(podList[podName]) + hostSensorDataEnvelope.SetData(resBytes) + res = append(res, hostSensorDataEnvelope) } }(podName, path) @@ -83,91 +89,53 @@ func (hsh *HostSensorHandler) sendAllPodsHTTPGETRequest(path string) ([]HostSens return res, nil } -// return list of -func (hsh *HostSensorHandler) GetOpenPortsList() ([]HostSensorDataEnvelope, error) { +// return list of OpenPortsList +func (hsh *HostSensorHandler) GetOpenPortsList() ([]hostsensor.HostSensorDataEnvelope, error) { // loop over pods and port-forward it to each of them - res, err := hsh.sendAllPodsHTTPGETRequest("/openedPorts") - for resIdx := range res { - res[resIdx].GroupVersionResource.Resource = "OpenPortsList" - res[resIdx].GroupVersionResource.Group = "hostdata.armo.cloud" - res[resIdx].GroupVersionResource.Version = "v1beta0" - } - return res, err + return hsh.sendAllPodsHTTPGETRequest("/openedPorts", "OpenPortsList") +} + +// return list of LinuxSecurityHardeningStatus +func (hsh *HostSensorHandler) GetLinuxSecurityHardeningStatus() ([]hostsensor.HostSensorDataEnvelope, error) { + // loop over pods and port-forward it to each of them + return hsh.sendAllPodsHTTPGETRequest("/linuxSecurityHardening", "LinuxSecurityHardeningStatus") +} + +// return list of KubeletCommandLine +func (hsh *HostSensorHandler) GetKubeletCommandLine() ([]hostsensor.HostSensorDataEnvelope, error) { + // loop over pods and port-forward it to each of them + return hsh.sendAllPodsHTTPGETRequest("/kubeletCommandLine", "KubeletCommandLine") } // return list of -func (hsh *HostSensorHandler) GetLinuxSecurityHardeningStatus() ([]HostSensorDataEnvelope, error) { +func (hsh *HostSensorHandler) GetKernelVersion() ([]hostsensor.HostSensorDataEnvelope, error) { // loop over pods and port-forward it to each of them - res, err := hsh.sendAllPodsHTTPGETRequest("/linuxSecurityHardening") - for resIdx := range res { - res[resIdx].GroupVersionResource.Resource = "LinuxSecurityHardeningStatus" - res[resIdx].GroupVersionResource.Group = "hostdata.armo.cloud" - res[resIdx].GroupVersionResource.Version = "v1beta0" - } - return res, err + return hsh.sendAllPodsHTTPGETRequest("/kernelVersion", "KernelVersion") } // return list of -func (hsh *HostSensorHandler) GetKubeletCommandLine() ([]HostSensorDataEnvelope, error) { +func (hsh *HostSensorHandler) GetOsReleaseFile() ([]hostsensor.HostSensorDataEnvelope, error) { // loop over pods and port-forward it to each of them - res, err := hsh.sendAllPodsHTTPGETRequest("/kubeletCommandLine") - for resIdx := range res { - res[resIdx].GroupVersionResource.Resource = "KubeletCommandLine" - res[resIdx].GroupVersionResource.Group = "hostdata.armo.cloud" - res[resIdx].GroupVersionResource.Version = "v1beta0" - } - return res, err + return hsh.sendAllPodsHTTPGETRequest("/osRelease", "OsReleaseFile") } // return list of -func (hsh *HostSensorHandler) GetKernelVersion() ([]HostSensorDataEnvelope, error) { +func (hsh *HostSensorHandler) GetKubeletConfigurations() ([]hostsensor.HostSensorDataEnvelope, error) { // loop over pods and port-forward it to each of them - res, err := hsh.sendAllPodsHTTPGETRequest("/kernelVersion") - for resIdx := range res { - res[resIdx].GroupVersionResource.Resource = "KernelVersion" - res[resIdx].GroupVersionResource.Group = "hostdata.armo.cloud" - res[resIdx].GroupVersionResource.Version = "v1beta0" - } - return res, err -} - -// return list of -func (hsh *HostSensorHandler) GetOsReleaseFile() ([]HostSensorDataEnvelope, error) { - // loop over pods and port-forward it to each of them - res, err := hsh.sendAllPodsHTTPGETRequest("/osRelease") - for resIdx := range res { - res[resIdx].GroupVersionResource.Resource = "OsReleaseFile" - res[resIdx].GroupVersionResource.Group = "hostdata.armo.cloud" - res[resIdx].GroupVersionResource.Version = "v1beta0" - } - return res, err -} - -// return list of -func (hsh *HostSensorHandler) GetKubeletConfigurations() ([]HostSensorDataEnvelope, error) { - // loop over pods and port-forward it to each of them - res, err := hsh.sendAllPodsHTTPGETRequest("/kubeletConfigurations") + res, err := hsh.sendAllPodsHTTPGETRequest("/kubeletConfigurations", "KubeletConfigurations") // empty kind, will be overridden for resIdx := range res { jsonBytes, err := yaml.YAMLToJSON(res[resIdx].Data) if err != nil { fmt.Printf("In GetKubeletConfigurations failed to YAMLToJSON: %v;\n%v", err, res[resIdx]) continue } - res[resIdx].Data = jsonBytes - kindDet := metav1.TypeMeta{} - if err = json.Unmarshal(jsonBytes, &kindDet); err != nil { - fmt.Printf("In GetKubeletConfigurations failed to Unmarshal GroupVersionKind: %v;\n%v", err, jsonBytes) - continue - } - res[resIdx].GroupVersionResource.Resource = kindDet.Kind - res[resIdx].GroupVersionResource.Group = kindDet.GroupVersionKind().Group - res[resIdx].GroupVersionResource.Version = kindDet.GroupVersionKind().Version + res[resIdx].SetData(jsonBytes) } return res, err } -func (hsh *HostSensorHandler) CollectResources() ([]HostSensorDataEnvelope, error) { - res := make([]HostSensorDataEnvelope, 0) +func (hsh *HostSensorHandler) CollectResources() ([]hostsensor.HostSensorDataEnvelope, error) { + res := make([]hostsensor.HostSensorDataEnvelope, 0) if hsh.DaemonSet == nil { return res, nil } diff --git a/hostsensorutils/hostsensorinterface.go b/hostsensorutils/hostsensorinterface.go index 6b9e4170..9b9d9d9b 100644 --- a/hostsensorutils/hostsensorinterface.go +++ b/hostsensorutils/hostsensorinterface.go @@ -1,8 +1,10 @@ package hostsensorutils +import "github.com/armosec/opa-utils/objectsenvelopes/hostsensor" + type IHostSensor interface { Init() error TearDown() error - CollectResources() ([]HostSensorDataEnvelope, error) + CollectResources() ([]hostsensor.HostSensorDataEnvelope, error) GetNamespace() string } diff --git a/hostsensorutils/hostsensormock.go b/hostsensorutils/hostsensormock.go index a8610a33..b4518003 100644 --- a/hostsensorutils/hostsensormock.go +++ b/hostsensorutils/hostsensormock.go @@ -1,5 +1,9 @@ package hostsensorutils +import ( + "github.com/armosec/opa-utils/objectsenvelopes/hostsensor" +) + type HostSensorHandlerMock struct { } @@ -11,8 +15,8 @@ func (hshm *HostSensorHandlerMock) TearDown() error { return nil } -func (hshm *HostSensorHandlerMock) CollectResources() ([]HostSensorDataEnvelope, error) { - return []HostSensorDataEnvelope{}, nil +func (hshm *HostSensorHandlerMock) CollectResources() ([]hostsensor.HostSensorDataEnvelope, error) { + return []hostsensor.HostSensorDataEnvelope{}, nil } func (hshm *HostSensorHandlerMock) GetNamespace() string { diff --git a/opaprocessor/processorhandler.go b/opaprocessor/processorhandler.go index bca27171..330d1aa6 100644 --- a/opaprocessor/processorhandler.go +++ b/opaprocessor/processorhandler.go @@ -6,7 +6,10 @@ import ( "time" "github.com/armosec/kubescape/cautils" + "github.com/armosec/opa-utils/objectsenvelopes" "github.com/armosec/opa-utils/reporthandling" + "github.com/armosec/opa-utils/score" + "github.com/golang/glog" "github.com/armosec/k8s-interface/k8sinterface" @@ -64,7 +67,8 @@ func (opaHandler *OPAProcessorHandler) ProcessRulesListenner() { opap.updateResults() // update score - // opap.updateScore() + scoreutil := score.NewScore(opaSessionObj.AllResources) + scoreutil.Calculate(opaSessionObj.PostureReport.FrameworkReports) // report *opaHandler.reportResults <- opaSessionObj @@ -161,7 +165,7 @@ func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule) (*reporth return nil, nil } - inputResources, err := reporthandling.RegoResourcesAggregator(rule, getKubernetesObjects(opap.K8SResources, opap.AllResources, rule.Match)) + inputResources, err := reporthandling.RegoResourcesAggregator(rule, getAllSupportedObjects(opap.K8SResources, opap.AllResources, rule)) if err != nil { return nil, fmt.Errorf("error getting aggregated k8sObjects: %s", err.Error()) } @@ -183,20 +187,20 @@ func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule) (*reporth if err != nil { return nil, err } - inputResources = workloadinterface.ListMapToMeta(enumeratedData) + inputResources = objectsenvelopes.ListMapToMeta(enumeratedData) ruleReport.ListInputKinds = workloadinterface.ListMetaIDs(inputResources) for i := range inputResources { opap.AllResources[inputResources[i].GetID()] = inputResources[i] } - failedResources := workloadinterface.ListMapToMeta(ruleReport.GetFailedResources()) + failedResources := objectsenvelopes.ListMapToMeta(ruleReport.GetFailedResources()) for i := range failedResources { if r, ok := opap.AllResources[failedResources[i].GetID()]; !ok { opap.AllResources[failedResources[i].GetID()] = r } } - warningResources := workloadinterface.ListMapToMeta(ruleReport.GetWarnignResources()) + warningResources := objectsenvelopes.ListMapToMeta(ruleReport.GetWarnignResources()) for i := range warningResources { if r, ok := opap.AllResources[warningResources[i].GetID()]; !ok { opap.AllResources[warningResources[i].GetID()] = r diff --git a/opaprocessor/processorhandler_test.go b/opaprocessor/processorhandler_test.go index 320f2baf..d3ec4204 100644 --- a/opaprocessor/processorhandler_test.go +++ b/opaprocessor/processorhandler_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/armosec/kubescape/cautils" + "github.com/armosec/opa-utils/objectsenvelopes" "github.com/armosec/opa-utils/reporthandling" "github.com/armosec/opa-utils/resources" @@ -20,7 +21,7 @@ func TestProcess(t *testing.T) { // set k8s k8sResources := make(cautils.K8SResources) allResources := make(map[string]workloadinterface.IMetadata) - imetaObj := workloadinterface.ListMapToMeta(k8sinterface.ConvertUnstructuredSliceToMap(k8sinterface.V1KubeSystemNamespaceMock().Items)) + imetaObj := objectsenvelopes.ListMapToMeta(k8sinterface.ConvertUnstructuredSliceToMap(k8sinterface.V1KubeSystemNamespaceMock().Items)) for i := range imetaObj { allResources[imetaObj[i].GetID()] = imetaObj[i] } diff --git a/opaprocessor/processorhandlerutils.go b/opaprocessor/processorhandlerutils.go index 0ea14921..beb2c208 100644 --- a/opaprocessor/processorhandlerutils.go +++ b/opaprocessor/processorhandlerutils.go @@ -28,10 +28,17 @@ func (opap *OPAProcessor) updateResults() { reporthandling.SetUniqueResourcesCounter(&opap.PostureReport.FrameworkReports[f]) // set default score - reporthandling.SetDefaultScore(&opap.PostureReport.FrameworkReports[f]) + // reporthandling.SetDefaultScore(&opap.PostureReport.FrameworkReports[f]) } } +func getAllSupportedObjects(k8sResources *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata, rule *reporthandling.PolicyRule) []workloadinterface.IMetadata { + k8sObjects := []workloadinterface.IMetadata{} + k8sObjects = append(k8sObjects, getKubernetesObjects(k8sResources, allResources, rule.Match)...) + k8sObjects = append(k8sObjects, getKubernetesObjects(k8sResources, allResources, rule.DynamicMatch)...) + return k8sObjects +} + func getKubernetesObjects(k8sResources *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata, match []reporthandling.RuleMatchObjects) []workloadinterface.IMetadata { k8sObjects := []workloadinterface.IMetadata{} for m := range match { @@ -100,7 +107,7 @@ func isRuleKubescapeVersionCompatible(rule *reporthandling.PolicyRule) bool { } func removeData(obj workloadinterface.IMetadata) { - if !workloadinterface.IsTypeWorkload(obj.GetObject()) { + if !k8sinterface.IsTypeWorkload(obj.GetObject()) { return // remove data only from kubernetes objects } workload := workloadinterface.NewWorkloadObj(obj.GetObject()) @@ -116,13 +123,26 @@ func removeData(obj workloadinterface.IMetadata) { func removeConfigMapData(workload workloadinterface.IWorkload) { workload.RemoveAnnotation("kubectl.kubernetes.io/last-applied-configuration") - workloadinterface.RemoveFromMap(workload.GetObject(), "data") workloadinterface.RemoveFromMap(workload.GetObject(), "metadata", "managedFields") - + overrideSensitiveData(workload) } + +func overrideSensitiveData(workload workloadinterface.IWorkload) { + dataInterface, ok := workloadinterface.InspectMap(workload.GetObject(), "data") + if ok { + data, ok := dataInterface.(map[string]interface{}) + if ok { + for key := range data { + workloadinterface.SetInMap(workload.GetObject(), []string{"data"}, key, "XXXXXX") + } + } + } +} + func removeSecretData(workload workloadinterface.IWorkload) { - workloadinterface.NewWorkloadObj(workload.GetObject()).RemoveSecretData() + workload.RemoveAnnotation("kubectl.kubernetes.io/last-applied-configuration") workloadinterface.RemoveFromMap(workload.GetObject(), "metadata", "managedFields") + overrideSensitiveData(workload) } func removePodData(workload workloadinterface.IWorkload) { workload.RemoveAnnotation("kubectl.kubernetes.io/last-applied-configuration") @@ -134,7 +154,7 @@ func removePodData(workload workloadinterface.IWorkload) { } for i := range containers { for j := range containers[i].Env { - containers[i].Env[j].Value = "" + containers[i].Env[j].Value = "XXXXXX" } } workloadinterface.SetInMap(workload.GetObject(), workloadinterface.PodSpec(workload.GetKind()), "containers", containers) diff --git a/opaprocessor/processorhandlerutils_test.go b/opaprocessor/processorhandlerutils_test.go index 7ade114b..3dc82ee2 100644 --- a/opaprocessor/processorhandlerutils_test.go +++ b/opaprocessor/processorhandlerutils_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/armosec/armoapi-go/armotypes" + "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/k8s-interface/workloadinterface" "github.com/armosec/kubescape/cautils" "github.com/armosec/opa-utils/reporthandling" @@ -57,6 +58,8 @@ func TestIsRuleKubescapeVersionCompatible(t *testing.T) { } func TestRemoveData(t *testing.T) { + k8sinterface.InitializeMapResourcesMock() + w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"demoservice-server"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"demoservice-server"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}}}` obj, _ := workloadinterface.NewWorkload([]byte(w)) removeData(obj) @@ -65,7 +68,7 @@ func TestRemoveData(t *testing.T) { c, _ := workload.GetContainers() for i := range c { for _, e := range c[i].Env { - assert.Equal(t, "", e.Value) + assert.Equal(t, "XXXXXX", e.Value) } } } diff --git a/policyhandler/handlenotification.go b/policyhandler/handlenotification.go index 2c27bdda..be7cf0ab 100644 --- a/policyhandler/handlenotification.go +++ b/policyhandler/handlenotification.go @@ -59,6 +59,5 @@ func (policyHandler *PolicyHandler) getResources(notification *reporthandling.Po opaSessionObj.K8SResources = resourcesMap opaSessionObj.AllResources = allResources - cautils.SuccessTextDisplay("Let’s start!!!") return nil } diff --git a/resourcehandler/fieldselector.go b/resourcehandler/fieldselector.go index e43edbe5..937d13f7 100644 --- a/resourcehandler/fieldselector.go +++ b/resourcehandler/fieldselector.go @@ -59,7 +59,7 @@ func getNamespacesSelector(resource *schema.GroupVersionResource, ns, operator s fieldSelector := "metadata." if resource.Resource == "namespaces" { fieldSelector += "name" - } else if k8sinterface.IsNamespaceScope(resource) { + } else if k8sinterface.IsResourceInNamespaceScope(resource.Resource) { fieldSelector += "namespace" } else { return "" diff --git a/resourcehandler/fieldselector_test.go b/resourcehandler/fieldselector_test.go index 5d3058af..15e3e1ac 100644 --- a/resourcehandler/fieldselector_test.go +++ b/resourcehandler/fieldselector_test.go @@ -15,6 +15,8 @@ func TestGetNamespacesSelector(t *testing.T) { } func TestExcludedNamespacesSelectors(t *testing.T) { + k8sinterface.InitializeMapResourcesMock() + es := NewExcludeSelector("default,ingress") selectors := es.GetNamespacesSelectors(&schema.GroupVersionResource{Resource: "pods"}) assert.Equal(t, 1, len(selectors)) @@ -26,6 +28,8 @@ func TestExcludedNamespacesSelectors(t *testing.T) { } func TestIncludeNamespacesSelectors(t *testing.T) { + k8sinterface.InitializeMapResourcesMock() + is := NewIncludeSelector("default,ingress") selectors := is.GetNamespacesSelectors(&schema.GroupVersionResource{Resource: "pods"}) assert.Equal(t, 2, len(selectors)) diff --git a/resourcehandler/filesloader.go b/resourcehandler/filesloader.go index 2b9ebf3a..25cc7e74 100644 --- a/resourcehandler/filesloader.go +++ b/resourcehandler/filesloader.go @@ -14,6 +14,7 @@ import ( "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/kubescape/cautils" + "github.com/armosec/opa-utils/objectsenvelopes" "github.com/armosec/opa-utils/reporthandling" "gopkg.in/yaml.v2" @@ -124,7 +125,7 @@ func mapResources(workloads []workloadinterface.IMetadata) map[string][]workload continue } - if workloadinterface.IsTypeWorkload(workloads[i].GetObject()) { + if k8sinterface.IsTypeWorkload(workloads[i].GetObject()) { w := workloadinterface.NewWorkloadObj(workloads[i].GetObject()) if groupVersionResource.Group != w.GetGroup() || groupVersionResource.Version != w.GetVersion() { // TODO - print warning @@ -211,7 +212,7 @@ func readYamlFile(yamlFile []byte) ([]workloadinterface.IMetadata, []error) { continue } if obj, ok := j.(map[string]interface{}); ok { - if o := workloadinterface.NewObject(obj); o != nil { + if o := objectsenvelopes.NewObject(obj); o != nil { yamlObjs = append(yamlObjs, o) } } else { @@ -237,7 +238,7 @@ func convertJsonToWorkload(jsonObj interface{}, workloads *[]workloadinterface.I switch x := jsonObj.(type) { case map[string]interface{}: - if o := workloadinterface.NewObject(x); o != nil { + if o := objectsenvelopes.NewObject(x); o != nil { (*workloads) = append(*workloads, o) } case []interface{}: diff --git a/resourcehandler/k8sresources.go b/resourcehandler/k8sresources.go index 98359f26..888e1a60 100644 --- a/resourcehandler/k8sresources.go +++ b/resourcehandler/k8sresources.go @@ -3,11 +3,15 @@ package resourcehandler import ( "context" "fmt" + "os" + "strings" "github.com/armosec/kubescape/cautils" "github.com/armosec/kubescape/hostsensorutils" + "github.com/armosec/opa-utils/objectsenvelopes" "github.com/armosec/opa-utils/reporthandling" + "github.com/armosec/k8s-interface/cloudsupport" "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/k8s-interface/workloadinterface" @@ -43,6 +47,8 @@ func (k8sHandler *K8sResourceHandler) GetResources(frameworks []reporthandling.F // get k8s resources cautils.ProgressTextDisplay("Accessing Kubernetes objects") + cautils.StartSpinner() + // build resources map // map resources based on framework required resources: map["/group/version/kind"][] k8sResourcesMap := setResourceMap(frameworks) @@ -59,8 +65,13 @@ func (k8sHandler *K8sResourceHandler) GetResources(frameworks []reporthandling.F } if err := k8sHandler.collectRbacResources(allResources); err != nil { - fmt.Println("failed to collect rbac resources") + cautils.WarningDisplay(os.Stdout, "Warning: failed to collect rbac resources\n") } + if err := getCloudProviderDescription(allResources, k8sResourcesMap); err != nil { + cautils.WarningDisplay(os.Stdout, fmt.Sprintf("Warning: %v\n", err.Error())) + } + + cautils.StopSpinner() cautils.SuccessTextDisplay("Accessed successfully to Kubernetes objects") return k8sResourcesMap, allResources, nil @@ -74,6 +85,7 @@ func (k8sHandler *K8sResourceHandler) GetClusterAPIServerInfo() *version.Info { } return clusterAPIServerInfo } + func (k8sHandler *K8sResourceHandler) pullResources(k8sResources *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata, namespace string, labels map[string]string) error { var errs error @@ -82,11 +94,13 @@ func (k8sHandler *K8sResourceHandler) pullResources(k8sResources *cautils.K8SRes gvr := schema.GroupVersionResource{Group: apiGroup, Version: apiVersion, Resource: resource} result, err := k8sHandler.pullSingleResource(&gvr, namespace, labels) if err != nil { - // handle error - if errs == nil { - errs = err - } else { - errs = fmt.Errorf("%s\n%s", errs, err.Error()) + if !strings.Contains(err.Error(), "the server could not find the requested resource") { + // handle error + if errs == nil { + errs = err + } else { + errs = fmt.Errorf("%s\n%s", errs, err.Error()) + } } continue } @@ -138,7 +152,7 @@ func (k8sHandler *K8sResourceHandler) pullSingleResource(resource *schema.GroupV func ConvertMapListToMeta(resourceMap []map[string]interface{}) []workloadinterface.IMetadata { workloads := []workloadinterface.IMetadata{} for i := range resourceMap { - if w := workloadinterface.NewObject(resourceMap[i]); w != nil { + if w := objectsenvelopes.NewObject(resourceMap[i]); w != nil { workloads = append(workloads, w) } } @@ -151,16 +165,15 @@ func (k8sHandler *K8sResourceHandler) collectHostResources(allResources map[stri return err } for rscIdx := range hostResources { - groupResources := k8sinterface.ResourceGroupToString(hostResources[rscIdx].Group, hostResources[rscIdx].GetApiVersion(), hostResources[rscIdx].GetKind()) - for _, groupResource := range groupResources { - allResources[hostResources[rscIdx].GetID()] = &hostResources[rscIdx] + group, version := getGroupNVersion(hostResources[rscIdx].GetApiVersion()) + groupResource := k8sinterface.JoinResourceTriplets(group, version, hostResources[rscIdx].GetKind()) + allResources[hostResources[rscIdx].GetID()] = &hostResources[rscIdx] - grpResourceList, ok := (*resourcesMap)[groupResource] - if !ok { - grpResourceList = make([]string, 0) - } - (*resourcesMap)[groupResource] = append(grpResourceList, hostResources[rscIdx].GetID()) + grpResourceList, ok := (*resourcesMap)[groupResource] + if !ok { + grpResourceList = make([]string, 0) } + (*resourcesMap)[groupResource] = append(grpResourceList, hostResources[rscIdx].GetID()) } return nil } @@ -178,3 +191,27 @@ func (k8sHandler *K8sResourceHandler) collectRbacResources(allResources map[stri } return nil } + +func getCloudProviderDescription(allResources map[string]workloadinterface.IMetadata, k8sResourcesMap *cautils.K8SResources) error { + if cloudsupport.IsRunningInCloudProvider() { + wl, err := cloudsupport.GetDescriptiveInfoFromCloudProvider() + if err != nil { + cluster := k8sinterface.GetCurrentContext().Cluster + provider := cloudsupport.GetCloudProvider(cluster) + // Return error with useful info on how to configure credentials for getting cloud provider info + switch provider { + case "gke": + return fmt.Errorf("could not get descriptive information about gke cluster: %s using sdk client. See https://developers.google.com/accounts/docs/application-default-credentials for more information", cluster) + case "eks": + return fmt.Errorf("could not get descriptive information about eks cluster: %s using sdk client. Check out how to configure credentials in https://docs.aws.amazon.com/sdk-for-go/api/", cluster) + case "aks": + return fmt.Errorf("could not get descriptive information about aks cluster: %s. %v", cluster, err.Error()) + } + return err + } + allResources[wl.GetID()] = wl + (*k8sResourcesMap)[fmt.Sprintf("%s/%s", wl.GetApiVersion(), wl.GetKind())] = []string{wl.GetID()} + } + return nil + +} diff --git a/resourcehandler/k8sresourcesutils.go b/resourcehandler/k8sresourcesutils.go index 2c14b05b..a969354c 100644 --- a/resourcehandler/k8sresourcesutils.go +++ b/resourcehandler/k8sresourcesutils.go @@ -1,6 +1,8 @@ package resourcehandler import ( + "strings" + "github.com/armosec/kubescape/cautils" "github.com/armosec/opa-utils/reporthandling" @@ -66,3 +68,15 @@ func insertK8sResources(k8sResources map[string]map[string]map[string]interface{ } } } + +func getGroupNVersion(apiVersion string) (string, string) { + gv := strings.Split(apiVersion, "/") + group, version := "", "" + if len(gv) >= 1 { + group = gv[0] + } + if len(gv) >= 2 { + version = gv[1] + } + return group, version +} diff --git a/resultshandling/printer/jsonprinter.go b/resultshandling/printer/jsonprinter.go index 01acad7e..7f2cc8a5 100644 --- a/resultshandling/printer/jsonprinter.go +++ b/resultshandling/printer/jsonprinter.go @@ -21,7 +21,7 @@ func (jsonPrinter *JsonPrinter) SetWriter(outputFile string) { } func (jsonPrinter *JsonPrinter) Score(score float32) { - fmt.Printf("\nFinal score: %d", int(score*100)) + fmt.Printf("\nOverall risk-score (0- Excellent, 100- All failed): %d\n", int(score)) } func (jsonPrinter *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) { diff --git a/resultshandling/printer/junit.go b/resultshandling/printer/junit.go index 9c480b50..d7648a2d 100644 --- a/resultshandling/printer/junit.go +++ b/resultshandling/printer/junit.go @@ -22,7 +22,7 @@ func (junitPrinter *JunitPrinter) SetWriter(outputFile string) { } func (junitPrinter *JunitPrinter) Score(score float32) { - fmt.Printf("\nFinal score: %d", int(score*100)) + fmt.Printf("\nOverall risk-score (0- Excellent, 100- All failed): %d\n", int(score)) } func (junitPrinter *JunitPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) { diff --git a/resultshandling/printer/prettyprinter.go b/resultshandling/printer/prettyprinter.go index 7fe156b4..3a8b416a 100644 --- a/resultshandling/printer/prettyprinter.go +++ b/resultshandling/printer/prettyprinter.go @@ -4,9 +4,11 @@ import ( "fmt" "os" "sort" + "strings" "github.com/armosec/k8s-interface/workloadinterface" "github.com/armosec/kubescape/cautils" + "github.com/armosec/opa-utils/objectsenvelopes" "github.com/armosec/opa-utils/reporthandling" "github.com/enescakir/emoji" "github.com/olekukonko/tablewriter" @@ -17,7 +19,7 @@ type PrettyPrinter struct { summary Summary verboseMode bool sortedControlNames []string - frameworkSummary ControlSummary + frameworkSummary ResultSummary } func NewPrettyPrinter(verboseMode bool) *PrettyPrinter { @@ -33,23 +35,30 @@ func (printer *PrettyPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) warningResources := []string{} allResources := []string{} frameworkNames := []string{} + frameworkScores := []float32{} + var overallRiskScore float32 = 0 for _, frameworkReport := range opaSessionObj.PostureReport.FrameworkReports { frameworkNames = append(frameworkNames, frameworkReport.Name) + frameworkScores = append(frameworkScores, frameworkReport.Score) failedResources = reporthandling.GetUniqueResourcesIDs(append(failedResources, frameworkReport.ListResourcesIDs().GetFailedResources()...)) warningResources = reporthandling.GetUniqueResourcesIDs(append(warningResources, frameworkReport.ListResourcesIDs().GetWarningResources()...)) allResources = reporthandling.GetUniqueResourcesIDs(append(allResources, frameworkReport.ListResourcesIDs().GetAllResources()...)) printer.summarySetup(frameworkReport, opaSessionObj.AllResources) + overallRiskScore += frameworkReport.Score } - printer.frameworkSummary = ControlSummary{ + overallRiskScore /= float32(len(opaSessionObj.PostureReport.FrameworkReports)) + + printer.frameworkSummary = ResultSummary{ + RiskScore: overallRiskScore, TotalResources: len(allResources), TotalFailed: len(failedResources), TotalWarning: len(warningResources), } printer.printResults() - printer.printSummaryTable(frameworkNames) + printer.printSummaryTable(frameworkNames, frameworkScores) } @@ -72,7 +81,11 @@ func (printer *PrettyPrinter) summarySetup(fr reporthandling.FrameworkReport, al if printer.verboseMode { passedWorkloads = groupByNamespaceOrKind(workloadsSummary, workloadSummaryPassed) } - printer.summary[cr.Name] = ControlSummary{ + + //controlSummary + printer.summary[cr.Name] = ResultSummary{ + ID: cr.ControlID, + RiskScore: cr.Score, TotalResources: cr.GetNumberOfResources(), TotalFailed: cr.GetNumberOfFailedResources(), TotalWarning: cr.GetNumberOfWarningResources(), @@ -99,7 +112,7 @@ func (printer *PrettyPrinter) printResults() { } } -func (printer *PrettyPrinter) printSummary(controlName string, controlSummary *ControlSummary) { +func (printer *PrettyPrinter) printSummary(controlName string, controlSummary *ResultSummary) { cautils.SimpleDisplay(printer.writer, "Summary - ") cautils.SuccessDisplay(printer.writer, "Passed:%v ", controlSummary.TotalResources-controlSummary.TotalFailed-controlSummary.TotalWarning) cautils.WarningDisplay(printer.writer, "Excluded:%v ", controlSummary.TotalWarning) @@ -111,11 +124,10 @@ func (printer *PrettyPrinter) printSummary(controlName string, controlSummary *C cautils.DescriptionDisplay(printer.writer, "\n") } - -func (printer *PrettyPrinter) printTitle(controlName string, controlSummary *ControlSummary) { - cautils.InfoDisplay(printer.writer, "[control: %s] ", controlName) +func (printer *PrettyPrinter) printTitle(controlName string, controlSummary *ResultSummary) { + cautils.InfoDisplay(printer.writer, "[control: %s - %s] ", controlName, getControlURL(controlSummary.ID)) if controlSummary.TotalResources == 0 { - cautils.InfoDisplay(printer.writer, "resources not found %v\n", emoji.ConfusedFace) + cautils.InfoDisplay(printer.writer, "skipped %v\n", emoji.ConfusedFace) } else if controlSummary.TotalFailed != 0 { cautils.FailureDisplay(printer.writer, "failed %v\n", emoji.SadButRelievedFace) } else if controlSummary.TotalWarning != 0 { @@ -127,7 +139,7 @@ func (printer *PrettyPrinter) printTitle(controlName string, controlSummary *Con cautils.DescriptionDisplay(printer.writer, "Description: %s\n", controlSummary.Description) } -func (printer *PrettyPrinter) printResources(controlSummary *ControlSummary) { +func (printer *PrettyPrinter) printResources(controlSummary *ResultSummary) { if len(controlSummary.FailedWorkloads) > 0 { cautils.FailureDisplay(printer.writer, "Failed:\n") @@ -146,41 +158,29 @@ func (printer *PrettyPrinter) printResources(controlSummary *ControlSummary) { func (printer *PrettyPrinter) printGroupedResources(workloads map[string][]WorkloadSummary) { indent := INDENT - for ns, rsc := range workloads { - if !isKindToBeGrouped(ns) { - printer.printGroupedResource(indent, ns, rsc) - } - } - if rsc, ok := workloads["User"]; ok { - printer.printGroupedResource(indent, "User", rsc) - } - if rsc, ok := workloads["Group"]; ok { - printer.printGroupedResource(indent, "Group", rsc) + for title, rsc := range workloads { + printer.printGroupedResource(indent, title, rsc) } } -func (printer *PrettyPrinter) printGroupedResource(indent string, ns string, rsc []WorkloadSummary) { +func (printer *PrettyPrinter) printGroupedResource(indent string, title string, rsc []WorkloadSummary) { preIndent := indent - if isKindToBeGrouped(ns) { - cautils.SimpleDisplay(printer.writer, "%s%ss\n", indent, ns) - } else if ns != "" { - cautils.SimpleDisplay(printer.writer, "%sNamespace %s\n", indent, ns) - } - preIndent2 := indent - for r := range rsc { + if title != "" { + cautils.SimpleDisplay(printer.writer, "%s%s\n", indent, title) indent += indent + } + + for r := range rsc { relatedObjectsStr := generateRelatedObjectsStr(rsc[r]) cautils.SimpleDisplay(printer.writer, fmt.Sprintf("%s%s - %s %s\n", indent, rsc[r].resource.GetKind(), rsc[r].resource.GetName(), relatedObjectsStr)) - indent = preIndent2 } indent = preIndent } func generateRelatedObjectsStr(workload WorkloadSummary) string { relatedStr := "" - w := workload.resource.GetObject() - if workloadinterface.IsTypeRegoResponseVector(w) { - relatedObjects := workloadinterface.NewRegoResponseVectorObject(w).GetRelatedObjects() + if workload.resource.GetObjectType() == workloadinterface.TypeWorkloadObject { + relatedObjects := objectsenvelopes.NewRegoResponseVectorObject(workload.resource.GetObject()).GetRelatedObjects() for i, related := range relatedObjects { if ns := related.GetNamespace(); i == 0 && ns != "" { relatedStr += fmt.Sprintf("Namespace - %s, ", ns) @@ -194,47 +194,35 @@ func generateRelatedObjectsStr(workload WorkloadSummary) string { return relatedStr } -func generateRow(control string, cs ControlSummary) []string { +func generateRow(control string, cs ResultSummary) []string { row := []string{control} row = append(row, cs.ToSlice()...) if cs.TotalResources != 0 { - row = append(row, fmt.Sprintf("%d%s", percentage(cs.TotalResources, cs.TotalFailed), "%")) + row = append(row, fmt.Sprintf("%d", int(cs.RiskScore))+"%") } else { - row = append(row, EmptyPercentage) + row = append(row, "skipped") } return row } func generateHeader() []string { - return []string{"Control Name", "Failed Resources", "Excluded Resources", "All Resources", "% success"} + return []string{"Control Name", "Failed Resources", "Excluded Resources", "All Resources", "% risk-score"} } -func percentage(big, small int) int { - if big == 0 { - if small == 0 { - return 100 - } - return 0 - } - return int(float64(float64(big-small)/float64(big)) * 100) -} -func generateFooter(numControlers, sumFailed, sumWarning, sumTotal int) []string { +func generateFooter(printer *PrettyPrinter) []string { // Control name | # failed resources | all resources | % success row := []string{} row = append(row, "Resource Summary") //fmt.Sprintf(""%d", numControlers")) - row = append(row, fmt.Sprintf("%d", sumFailed)) - row = append(row, fmt.Sprintf("%d", sumWarning)) - row = append(row, fmt.Sprintf("%d", sumTotal)) - if sumTotal != 0 { - row = append(row, fmt.Sprintf("%d%s", percentage(sumTotal, sumFailed), "%")) - } else { - row = append(row, EmptyPercentage) - } + row = append(row, fmt.Sprintf("%d", printer.frameworkSummary.TotalFailed)) + row = append(row, fmt.Sprintf("%d", printer.frameworkSummary.TotalWarning)) + row = append(row, fmt.Sprintf("%d", printer.frameworkSummary.TotalResources)) + row = append(row, fmt.Sprintf("%.2f%s", printer.frameworkSummary.RiskScore, "%")) + return row } -func (printer *PrettyPrinter) printSummaryTable(frameworksNames []string) { +func (printer *PrettyPrinter) printSummaryTable(frameworksNames []string, frameworkScores []float32) { // For control scan framework will be nil - printer.printFramework(frameworksNames) + printer.printFramework(frameworksNames, frameworkScores) summaryTable := tablewriter.NewWriter(printer.writer) summaryTable.SetAutoWrapText(false) @@ -247,20 +235,23 @@ func (printer *PrettyPrinter) printSummaryTable(frameworksNames []string) { controlSummary := printer.summary[printer.sortedControlNames[i]] summaryTable.Append(generateRow(printer.sortedControlNames[i], controlSummary)) } - summaryTable.SetFooter(generateFooter(len(printer.summary), printer.frameworkSummary.TotalFailed, printer.frameworkSummary.TotalWarning, printer.frameworkSummary.TotalResources)) + + summaryTable.SetFooter(generateFooter(printer)) + + // summaryTable.SetFooter(generateFooter()) summaryTable.Render() } -func (printer *PrettyPrinter) printFramework(frameworksNames []string) { +func (printer *PrettyPrinter) printFramework(frameworksNames []string, frameworkScores []float32) { if len(frameworksNames) == 1 { - cautils.InfoTextDisplay(printer.writer, fmt.Sprintf("%s FRAMEWORK\n", frameworksNames[0])) + cautils.InfoTextDisplay(printer.writer, fmt.Sprintf("FRAMEWORK %s\n", frameworksNames[0])) } else if len(frameworksNames) > 1 { - p := "" + p := "FRAMEWORKS: " for i := 0; i < len(frameworksNames)-1; i++ { - p += frameworksNames[i] + ", " + p += fmt.Sprintf("%s (risk: %.2f), ", frameworksNames[i], frameworkScores[i]) } - p += frameworksNames[len(frameworksNames)-1] - cautils.InfoTextDisplay(printer.writer, fmt.Sprintf("%s FRAMEWORKS\n", p)) + p += fmt.Sprintf("%s (risk: %.2f)\n", frameworksNames[len(frameworksNames)-1], frameworkScores[len(frameworkScores)-1]) + cautils.InfoTextDisplay(printer.writer, p) } } @@ -286,3 +277,7 @@ func getWriter(outputFile string) *os.File { return os.Stdout } + +func getControlURL(controlID string) string { + return fmt.Sprintf("https://hub.armo.cloud/docs/%s", strings.ToLower(controlID)) +} diff --git a/resultshandling/printer/printresults.go b/resultshandling/printer/printresults.go index bd82b4ed..5c4a2746 100644 --- a/resultshandling/printer/printresults.go +++ b/resultshandling/printer/printresults.go @@ -6,8 +6,6 @@ import ( var INDENT = " " -const EmptyPercentage = "NaN" - const ( PrettyFormat string = "pretty-printer" JsonFormat string = "json" diff --git a/resultshandling/printer/prometheusprinter.go b/resultshandling/printer/prometheusprinter.go index a211f55e..97ba7290 100644 --- a/resultshandling/printer/prometheusprinter.go +++ b/resultshandling/printer/prometheusprinter.go @@ -25,7 +25,7 @@ func (prometheusPrinter *PrometheusPrinter) SetWriter(outputFile string) { } func (prometheusPrinter *PrometheusPrinter) Score(score float32) { - fmt.Printf("\n# Overall score out of 100\nkubescape_score %f\n", score*100) + fmt.Printf("\n# Overall risk-score (0- Excellent, 100- All failed)\nkubescape_score %d\n", int(score)) } func (printer *PrometheusPrinter) printResources(allResources map[string]workloadinterface.IMetadata, resourcesIDs *reporthandling.ResourcesIDs, frameworkName, controlName string) { diff --git a/resultshandling/printer/summary.go b/resultshandling/printer/summary.go index 9b13b0dd..d91b2a11 100644 --- a/resultshandling/printer/summary.go +++ b/resultshandling/printer/summary.go @@ -7,13 +7,15 @@ import ( "github.com/armosec/opa-utils/reporthandling" ) -type Summary map[string]ControlSummary +type Summary map[string]ResultSummary func NewSummary() Summary { - return make(map[string]ControlSummary) + return make(map[string]ResultSummary) } -type ControlSummary struct { +type ResultSummary struct { + ID string + RiskScore float32 TotalResources int TotalFailed int TotalWarning int @@ -31,7 +33,7 @@ type WorkloadSummary struct { status string } -func (controlSummary *ControlSummary) ToSlice() []string { +func (controlSummary *ResultSummary) ToSlice() []string { s := []string{} s = append(s, fmt.Sprintf("%d", controlSummary.TotalFailed)) s = append(s, fmt.Sprintf("%d", controlSummary.TotalWarning)) diff --git a/resultshandling/printer/summeryhelpers.go b/resultshandling/printer/summeryhelpers.go index a2053278..dee39235 100644 --- a/resultshandling/printer/summeryhelpers.go +++ b/resultshandling/printer/summeryhelpers.go @@ -1,7 +1,9 @@ package printer import ( + "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/k8s-interface/workloadinterface" + "github.com/armosec/opa-utils/objectsenvelopes" "github.com/armosec/opa-utils/reporthandling" ) @@ -9,19 +11,40 @@ import ( func groupByNamespaceOrKind(resources []WorkloadSummary, status func(workloadSummary *WorkloadSummary) bool) map[string][]WorkloadSummary { mapResources := make(map[string][]WorkloadSummary) for i := range resources { - if status(&resources[i]) { - if isKindToBeGrouped(resources[i].resource.GetKind()) { - if r, ok := mapResources[resources[i].resource.GetKind()]; ok { - r = append(r, resources[i]) - mapResources[resources[i].resource.GetKind()] = r - } else { - mapResources[resources[i].resource.GetKind()] = []WorkloadSummary{resources[i]} - } - } else if r, ok := mapResources[resources[i].resource.GetNamespace()]; ok { + if !status(&resources[i]) { + continue + } + t := resources[i].resource.GetObjectType() + if t == objectsenvelopes.TypeRegoResponseVectorObject && !isKindToBeGrouped(resources[i].resource.GetKind()) { + t = workloadinterface.TypeWorkloadObject + } + switch t { // TODO - find a better way to defind the groups + case workloadinterface.TypeWorkloadObject: + ns := "" + if resources[i].resource.GetNamespace() != "" { + ns = "Namescape " + resources[i].resource.GetNamespace() + } + if r, ok := mapResources[ns]; ok { r = append(r, resources[i]) - mapResources[resources[i].resource.GetNamespace()] = r + mapResources[ns] = r } else { - mapResources[resources[i].resource.GetNamespace()] = []WorkloadSummary{resources[i]} + mapResources[ns] = []WorkloadSummary{resources[i]} + } + case objectsenvelopes.TypeRegoResponseVectorObject: + group := resources[i].resource.GetKind() + "s" + if r, ok := mapResources[group]; ok { + r = append(r, resources[i]) + mapResources[group] = r + } else { + mapResources[group] = []WorkloadSummary{resources[i]} + } + default: + group, _ := k8sinterface.SplitApiVersion(resources[i].resource.GetApiVersion()) + if r, ok := mapResources[group]; ok { + r = append(r, resources[i]) + mapResources[group] = r + } else { + mapResources[group] = []WorkloadSummary{resources[i]} } } } diff --git a/resultshandling/reporter/mockreporter.go b/resultshandling/reporter/mockreporter.go index 6c4dc6a1..22983fef 100644 --- a/resultshandling/reporter/mockreporter.go +++ b/resultshandling/reporter/mockreporter.go @@ -25,6 +25,6 @@ func (reportMock *ReportMock) SetClusterName(clusterName string) { } func (reportMock *ReportMock) DisplayReportURL() { - message := fmt.Sprintf("\nYou can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more by registering here: https://%s\n", getter.GetArmoAPIConnector().GetFrontendURL()) + message := fmt.Sprintf("\nYou can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more by registering here: https://%s/cli-signup \n", getter.GetArmoAPIConnector().GetFrontendURL()) cautils.InfoTextDisplay(os.Stdout, fmt.Sprintf("\n%s\n", message)) } diff --git a/resultshandling/reporter/reporteventreceiver.go b/resultshandling/reporter/reporteventreceiver.go index 05ef1fe9..95b70f9c 100644 --- a/resultshandling/reporter/reporteventreceiver.go +++ b/resultshandling/reporter/reporteventreceiver.go @@ -43,6 +43,10 @@ func NewReportEventReceiver(tenantConfig *cautils.ConfigObj) *ReportEventReceive func (report *ReportEventReceiver) ActionSendReport(opaSessionObj *cautils.OPASessionObj) error { + if report.customerGUID == "" || report.clusterName == "" { + return fmt.Errorf("missing accout ID or cluster name. AccountID: '%s', Cluster name: '%s'", report.customerGUID, report.clusterName) + } + if err := report.prepareReport(opaSessionObj.PostureReport, opaSessionObj.AllResources); err != nil { return err } @@ -54,13 +58,16 @@ func (report *ReportEventReceiver) SetCustomerGUID(customerGUID string) { } func (report *ReportEventReceiver) SetClusterName(clusterName string) { - report.clusterName = clusterName + report.clusterName = cautils.AdoptClusterName(clusterName) // clean cluster name } func (report *ReportEventReceiver) prepareReport(postureReport *reporthandling.PostureReport, allResources map[string]workloadinterface.IMetadata) error { report.initEventReceiverURL() host := hostToString(report.eventReceiverURL, postureReport.ReportID) + cautils.StartSpinner() + defer cautils.StopSpinner() + // send framework results if err := report.sendReport(host, postureReport); err != nil { return err @@ -116,14 +123,14 @@ func (report *ReportEventReceiver) sendReport(host string, postureReport *report } func (report *ReportEventReceiver) DisplayReportURL() { - message := "You can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more by registering here: " + message := "You can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more by registering here:" u := url.URL{} u.Scheme = "https" u.Host = getter.GetArmoAPIConnector().GetFrontendURL() if report.customerAdminEMail != "" { - cautils.InfoTextDisplay(os.Stdout, "\n\n"+message+u.String()+"\n\n") + cautils.InfoTextDisplay(os.Stdout, fmt.Sprintf("\n\n%s %s/risk/%s\n(Account: %s)\n\n", message, u.String(), report.clusterName, report.customerGUID)) return } u.Path = "account/sign-up" @@ -132,5 +139,5 @@ func (report *ReportEventReceiver) DisplayReportURL() { q.Add("customerGUID", report.customerGUID) u.RawQuery = q.Encode() - cautils.InfoTextDisplay(os.Stdout, "\n\n"+message+u.String()+"\n\n") + cautils.InfoTextDisplay(os.Stdout, fmt.Sprintf("\n\n%s %s\n\n", message, u.String())) } diff --git a/resultshandling/results.go b/resultshandling/results.go index 72d281b2..5dea6e4f 100644 --- a/resultshandling/results.go +++ b/resultshandling/results.go @@ -34,7 +34,11 @@ func (resultsHandler *ResultsHandler) HandleResults(scanInfo *cautils.ScanInfo) } // TODO - get score from table - score := CalculatePostureScore(opaSessionObj.PostureReport) + var score float32 = 0 + for i := range opaSessionObj.PostureReport.FrameworkReports { + score += opaSessionObj.PostureReport.FrameworkReports[i].Score + } + score /= float32(len(opaSessionObj.PostureReport.FrameworkReports)) resultsHandler.printerObj.Score(score) return score