From 13b37abcb416d05705b954e2878c81ad13aa7713 Mon Sep 17 00:00:00 2001 From: David Wertenteil Date: Fri, 5 Aug 2022 00:06:33 +0300 Subject: [PATCH] fixed rbac submit --- cmd/submit/exceptions.go | 2 +- cmd/submit/rbac.go | 29 ++- cmd/submit/results.go | 44 ++--- core/cautils/datastructures.go | 13 +- core/cautils/rbac.go | 16 +- core/cautils/reportv2tov1.go | 9 +- core/core/initutils.go | 8 +- core/core/scan.go | 2 +- core/core/submit.go | 10 +- core/meta/cliinterfaces/submit.go | 4 +- .../pkg/opaprocessor/processorhandler_test.go | 39 ---- .../resultshandling/printer/v1/jsonprinter.go | 8 +- .../printer/v1/prometheusprinter.go | 4 +- .../reporter/v1/reporteventreceiver.go | 185 ------------------ .../reporter/v1/reporteventreceiverutils.go | 48 ----- .../v1/reporteventreceiverutils_test.go | 20 -- .../reporter/v2/reporteventreceive_test.go | 48 ----- .../reporter/v2/reporteventreceiver.go | 51 +++-- .../reporter/v2/reporteventreceiver_test.go | 118 +++++++++++ .../reporter/v2/reporteventreceiverutils.go | 3 +- core/pkg/resultshandling/reporter/v2/utils.go | 2 +- .../resultshandling/reporter/v2/utils_test.go | 10 +- core/pkg/score/score.go | 2 - 23 files changed, 238 insertions(+), 437 deletions(-) delete mode 100644 core/pkg/resultshandling/reporter/v1/reporteventreceiver.go delete mode 100644 core/pkg/resultshandling/reporter/v1/reporteventreceiverutils.go delete mode 100644 core/pkg/resultshandling/reporter/v1/reporteventreceiverutils_test.go delete mode 100644 core/pkg/resultshandling/reporter/v2/reporteventreceive_test.go create mode 100644 core/pkg/resultshandling/reporter/v2/reporteventreceiver_test.go diff --git a/cmd/submit/exceptions.go b/cmd/submit/exceptions.go index e441b491..266b87d1 100644 --- a/cmd/submit/exceptions.go +++ b/cmd/submit/exceptions.go @@ -12,7 +12,7 @@ import ( func getExceptionsCmd(ks meta.IKubescape, submitInfo *metav1.Submit) *cobra.Command { return &cobra.Command{ - Use: "exceptions ", + Use: "exceptions ", Short: "Submit exceptions to the Kubescape SaaS version", Args: func(cmd *cobra.Command, args []string) error { if len(args) != 1 { diff --git a/cmd/submit/rbac.go b/cmd/submit/rbac.go index 0d002f28..d585352a 100644 --- a/cmd/submit/rbac.go +++ b/cmd/submit/rbac.go @@ -1,27 +1,40 @@ package submit import ( + "fmt" + "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/kubescape/v2/core/cautils" "github.com/armosec/kubescape/v2/core/cautils/getter" "github.com/armosec/kubescape/v2/core/meta" "github.com/armosec/kubescape/v2/core/meta/cliinterfaces" v1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1" + reporterv2 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v2" logger "github.com/dwertent/go-logger" "github.com/dwertent/go-logger/helpers" - - reporterv1 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v1" + "github.com/google/uuid" "github.com/armosec/rbac-utils/rbacscanner" "github.com/spf13/cobra" ) +var ( + rbacExamples = ` + # Submit cluster's Role-Based Access Control(RBAC) + kubescape submit rbac + + # Submit cluster's Role-Based Access Control(RBAC) with account ID + kubescape submit rbac --account-id + ` +) + // getRBACCmd represents the RBAC command func getRBACCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command { return &cobra.Command{ - Use: "rbac \nExample:\n$ kubescape submit rbac", - Short: "Submit cluster's Role-Based Access Control(RBAC)", - Long: ``, + Use: "rbac", + Example: rbacExamples, + Short: "Submit cluster's Role-Based Access Control(RBAC)", + Long: ``, RunE: func(cmd *cobra.Command, args []string) error { k8s := k8sinterface.NewKubernetesApi() @@ -32,11 +45,15 @@ func getRBACCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command { logger.L().Error("failed setting account ID", helpers.Error(err)) } + if clusterConfig.GetAccountID() == "" { + return fmt.Errorf("account ID is not set, run 'kubescape submit rbac --account-id '") + } + // list RBAC rbacObjects := cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, clusterConfig.GetAccountID(), clusterConfig.GetContextName())) // submit resources - r := reporterv1.NewReportEventReceiver(clusterConfig.GetConfigObj()) + r := reporterv2.NewReportEventReceiver(clusterConfig.GetConfigObj(), uuid.NewString(), reporterv2.SubmitContextRBAC) submitInterfaces := cliinterfaces.SubmitInterfaces{ ClusterConfig: clusterConfig, diff --git a/cmd/submit/results.go b/cmd/submit/results.go index 1451f43e..a4ed0607 100644 --- a/cmd/submit/results.go +++ b/cmd/submit/results.go @@ -4,20 +4,18 @@ import ( "encoding/json" "fmt" "os" - "time" + + reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2" + "github.com/google/uuid" "github.com/armosec/k8s-interface/workloadinterface" "github.com/armosec/kubescape/v2/core/meta" "github.com/armosec/kubescape/v2/core/meta/cliinterfaces" v1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1" - "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter" - reporterv1 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v1" reporterv2 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v2" logger "github.com/dwertent/go-logger" "github.com/dwertent/go-logger/helpers" - "github.com/armosec/opa-utils/reporthandling" - "github.com/google/uuid" "github.com/spf13/cobra" ) @@ -37,19 +35,13 @@ func NewResultsObject(customerGUID, clusterName, filePath string) *ResultsObject } } -func (resultsObject *ResultsObject) SetResourcesReport() (*reporthandling.PostureReport, error) { +func (resultsObject *ResultsObject) SetResourcesReport() (*reporthandlingv2.PostureReport, error) { // load framework results from json file - frameworkReports, err := loadResultsFromFile(resultsObject.filePath) + report, err := loadResultsFromFile(resultsObject.filePath) if err != nil { return nil, err } - return &reporthandling.PostureReport{ - FrameworkReports: frameworkReports, - ReportID: uuid.NewString(), - ReportGenerationTime: time.Now().UTC(), - CustomerGUID: resultsObject.customerGUID, - ClusterName: resultsObject.clusterName, - }, nil + return report, nil } func (resultsObject *ResultsObject) ListAllResources() (map[string]workloadinterface.IMetadata, error) { @@ -76,15 +68,7 @@ func getResultsCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command { resultsObjects := NewResultsObject(clusterConfig.GetAccountID(), clusterConfig.GetContextName(), args[0]) - // submit resources - var r reporter.IReport - switch formatVersion { - case "v2": - r = reporterv2.NewReportEventReceiver(clusterConfig.GetConfigObj(), "") - default: - logger.L().Warning("Deprecated results version. run with '--format-version' flag", helpers.String("your version", formatVersion), helpers.String("latest version", "v2")) - r = reporterv1.NewReportEventReceiver(clusterConfig.GetConfigObj()) - } + r := reporterv2.NewReportEventReceiver(clusterConfig.GetConfigObj(), uuid.NewString(), reporterv2.SubmitContextScan) submitInterfaces := cliinterfaces.SubmitInterfaces{ ClusterConfig: clusterConfig, @@ -102,18 +86,14 @@ func getResultsCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command { return resultsCmd } -func loadResultsFromFile(filePath string) ([]reporthandling.FrameworkReport, error) { - frameworkReports := []reporthandling.FrameworkReport{} +func loadResultsFromFile(filePath string) (*reporthandlingv2.PostureReport, error) { + report := &reporthandlingv2.PostureReport{} f, err := os.ReadFile(filePath) if err != nil { return nil, err } - if err = json.Unmarshal(f, &frameworkReports); err != nil { - frameworkReport := reporthandling.FrameworkReport{} - if err = json.Unmarshal(f, &frameworkReport); err != nil { - return frameworkReports, err - } - frameworkReports = append(frameworkReports, frameworkReport) + if err = json.Unmarshal(f, report); err != nil { + return report, fmt.Errorf("failed to unmarshal results file: %s, make sure you run kubescape with '--format=json --format-version=v2'", err.Error()) } - return frameworkReports, nil + return report, nil } diff --git a/core/cautils/datastructures.go b/core/cautils/datastructures.go index 74920988..ed9e6d03 100644 --- a/core/cautils/datastructures.go +++ b/core/cautils/datastructures.go @@ -20,7 +20,6 @@ type OPASessionObj struct { AllResources map[string]workloadinterface.IMetadata // all scanned resources, map[] ResourcesResult map[string]resourcesresults.Result // resources scan results, map[] ResourceSource map[string]reporthandling.Source // resources sources, map[] - PostureReport *reporthandling.PostureReport // scan results v1 - Remove Report *reporthandlingv2.PostureReport // scan results v2 - Remove Exceptions []armotypes.PostureExceptionPolicy // list of exceptions to apply on scan results RegoInputData RegoInputData // input passed to rgo for scanning. map[][] @@ -41,11 +40,7 @@ func NewOPASessionObj(frameworks []reporthandling.Framework, k8sResources *K8SRe ResourceToControlsMap: make(map[string][]string), ResourceSource: make(map[string]reporthandling.Source), SessionID: scanInfo.ScanID, - PostureReport: &reporthandling.PostureReport{ - ClusterName: ClusterName, - CustomerGUID: CustomerGUID, - }, - Metadata: scanInfoToScanMetadata(scanInfo), + Metadata: scanInfoToScanMetadata(scanInfo), } } @@ -56,12 +51,6 @@ func NewOPASessionObjMock() *OPASessionObj { AllResources: make(map[string]workloadinterface.IMetadata), ResourcesResult: make(map[string]resourcesresults.Result), Report: &reporthandlingv2.PostureReport{}, - PostureReport: &reporthandling.PostureReport{ - ClusterName: "", - CustomerGUID: "", - ReportID: "", - JobID: "", - }, Metadata: &reporthandlingv2.Metadata{ ScanMetadata: reporthandlingv2.ScanMetadata{ ScanningTarget: 0, diff --git a/core/cautils/rbac.go b/core/cautils/rbac.go index 3f729f49..407aa333 100644 --- a/core/cautils/rbac.go +++ b/core/cautils/rbac.go @@ -4,8 +4,9 @@ import ( "encoding/json" "time" + reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2" + "github.com/armosec/k8s-interface/workloadinterface" - "github.com/armosec/opa-utils/reporthandling" "github.com/armosec/rbac-utils/rbacscanner" "github.com/armosec/rbac-utils/rbacutils" "github.com/google/uuid" @@ -19,12 +20,19 @@ func NewRBACObjects(scanner *rbacscanner.RbacScannerFromK8sAPI) *RBACObjects { return &RBACObjects{scanner: scanner} } -func (rbacObjects *RBACObjects) SetResourcesReport() (*reporthandling.PostureReport, error) { - return &reporthandling.PostureReport{ +func (rbacObjects *RBACObjects) SetResourcesReport() (*reporthandlingv2.PostureReport, error) { + return &reporthandlingv2.PostureReport{ ReportID: uuid.NewString(), ReportGenerationTime: time.Now().UTC(), CustomerGUID: rbacObjects.scanner.CustomerGUID, ClusterName: rbacObjects.scanner.ClusterName, + Metadata: reporthandlingv2.Metadata{ + ContextMetadata: reporthandlingv2.ContextMetadata{ + ClusterContextMetadata: &reporthandlingv2.ClusterMetadata{ + ContextName: rbacObjects.scanner.ClusterName, + }, + }, + }, }, nil } @@ -53,7 +61,7 @@ func (rbacObjects *RBACObjects) rbacObjectsToResources(resources *rbacutils.Rbac ************************************************************************************************************************ */ - // wrap rbac aggregated objects in IMetadata and add to allresources + // wrap rbac aggregated objects in IMetadata and add to AllResources // TODO - DEPRECATE SA2WLIDmap SA2WLIDmapIMeta, err := rbacutils.SA2WLIDmapIMetadataWrapper(resources.SA2WLIDmap) if err != nil { diff --git a/core/cautils/reportv2tov1.go b/core/cautils/reportv2tov1.go index ba5e6478..45ae818d 100644 --- a/core/cautils/reportv2tov1.go +++ b/core/cautils/reportv2tov1.go @@ -7,10 +7,8 @@ import ( "github.com/armosec/opa-utils/reporthandling/results/v1/reportsummary" ) -func ReportV2ToV1(opaSessionObj *OPASessionObj) { - if len(opaSessionObj.PostureReport.FrameworkReports) > 0 { - return // report already converted - } +func ReportV2ToV1(opaSessionObj *OPASessionObj) *reporthandling.PostureReport { + report := &reporthandling.PostureReport{} // opaSessionObj.PostureReport.ClusterCloudProvider = opaSessionObj.Report.ClusterCloudProvider frameworks := []reporthandling.FrameworkReport{} @@ -53,7 +51,8 @@ func ReportV2ToV1(opaSessionObj *OPASessionObj) { // scoreutil := score.NewScore(opaSessionObj.AllResources) // scoreutil.Calculate(frameworks) - opaSessionObj.PostureReport.FrameworkReports = frameworks + report.FrameworkReports = frameworks + return report } func controlReportV2ToV1(opaSessionObj *OPASessionObj, frameworkName string, controls map[string]reportsummary.ControlSummary) []reporthandling.ControlReport { diff --git a/core/core/initutils.go b/core/core/initutils.go index 51bd360f..847e841e 100644 --- a/core/core/initutils.go +++ b/core/core/initutils.go @@ -48,9 +48,13 @@ func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.Kubern return nil } -func getReporter(tenantConfig cautils.ITenantConfig, reportID string, submit, fwScan bool) reporter.IReport { +func getReporter(tenantConfig cautils.ITenantConfig, reportID string, submit, fwScan bool, scanningContext cautils.ScanningContext) reporter.IReport { if submit { - return reporterv2.NewReportEventReceiver(tenantConfig.GetConfigObj(), reportID) + submitData := reporterv2.SubmitContextScan + if scanningContext != cautils.ContextCluster { + submitData = reporterv2.SubmitContextRBAC + } + return reporterv2.NewReportEventReceiver(tenantConfig.GetConfigObj(), reportID, submitData) } if tenantConfig.GetAccountID() == "" { // Add link only when scanning a cluster using a framework diff --git a/core/core/scan.go b/core/core/scan.go index 7c0589d2..ace5d25b 100644 --- a/core/core/scan.go +++ b/core/core/scan.go @@ -86,7 +86,7 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces { // ================== setup reporter & printer objects ====================================== // reporting behavior - setup reporter - reportHandler := getReporter(tenantConfig, scanInfo.ScanID, scanInfo.Submit, scanInfo.FrameworkScan) + reportHandler := getReporter(tenantConfig, scanInfo.ScanID, scanInfo.Submit, scanInfo.FrameworkScan, scanInfo.GetScanningContext()) // setup printer printerHandler := resultshandling.NewPrinter(scanInfo.Format, scanInfo.FormatVersion, scanInfo.VerboseMode, cautils.ViewTypes(scanInfo.View)) diff --git a/core/core/submit.go b/core/core/submit.go index f0526002..9033396c 100644 --- a/core/core/submit.go +++ b/core/core/submit.go @@ -4,6 +4,7 @@ import ( "github.com/armosec/kubescape/v2/core/cautils" "github.com/armosec/kubescape/v2/core/cautils/getter" "github.com/armosec/kubescape/v2/core/meta/cliinterfaces" + logger "github.com/dwertent/go-logger" "github.com/dwertent/go-logger/helpers" ) @@ -11,7 +12,7 @@ import ( func (ks *Kubescape) Submit(submitInterfaces cliinterfaces.SubmitInterfaces) error { // list resources - postureReport, err := submitInterfaces.SubmitObjects.SetResourcesReport() + report, err := submitInterfaces.SubmitObjects.SetResourcesReport() if err != nil { return err } @@ -20,7 +21,12 @@ func (ks *Kubescape) Submit(submitInterfaces cliinterfaces.SubmitInterfaces) err return err } // report - if err := submitInterfaces.Reporter.Submit(&cautils.OPASessionObj{PostureReport: postureReport, AllResources: allresources}); err != nil { + o := &cautils.OPASessionObj{ + Report: report, + AllResources: allresources, + Metadata: &report.Metadata, + } + if err := submitInterfaces.Reporter.Submit(o); err != nil { return err } logger.L().Success("Data has been submitted successfully") diff --git a/core/meta/cliinterfaces/submit.go b/core/meta/cliinterfaces/submit.go index dcf9846a..ef81279b 100644 --- a/core/meta/cliinterfaces/submit.go +++ b/core/meta/cliinterfaces/submit.go @@ -4,11 +4,11 @@ import ( "github.com/armosec/k8s-interface/workloadinterface" "github.com/armosec/kubescape/v2/core/cautils" "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter" - "github.com/armosec/opa-utils/reporthandling" + reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2" ) type ISubmitObjects interface { - SetResourcesReport() (*reporthandling.PostureReport, error) + SetResourcesReport() (*reporthandlingv2.PostureReport, error) ListAllResources() (map[string]workloadinterface.IMetadata, error) } diff --git a/core/pkg/opaprocessor/processorhandler_test.go b/core/pkg/opaprocessor/processorhandler_test.go index 83105f1e..f1e86be8 100644 --- a/core/pkg/opaprocessor/processorhandler_test.go +++ b/core/pkg/opaprocessor/processorhandler_test.go @@ -6,12 +6,10 @@ import ( "github.com/armosec/armoapi-go/armotypes" "github.com/armosec/kubescape/v2/core/cautils" "github.com/armosec/kubescape/v2/core/mocks" - "github.com/armosec/opa-utils/objectsenvelopes" "github.com/armosec/opa-utils/reporthandling" "github.com/armosec/opa-utils/resources" "github.com/stretchr/testify/assert" - "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/k8s-interface/workloadinterface" // _ "k8s.io/client-go/plugin/pkg/client/auth" ) @@ -19,43 +17,6 @@ import ( func NewOPAProcessorMock() *OPAProcessor { return &OPAProcessor{} } -func TestProcess(t *testing.T) { - - // set k8s - k8sResources := make(cautils.K8SResources) - allResources := make(map[string]workloadinterface.IMetadata) - imetaObj := objectsenvelopes.ListMapToMeta(k8sinterface.ConvertUnstructuredSliceToMap(k8sinterface.V1KubeSystemNamespaceMock().Items)) - for i := range imetaObj { - allResources[imetaObj[i].GetID()] = imetaObj[i] - } - k8sResources["/v1/pods"] = workloadinterface.ListMetaIDs(imetaObj) - - // set opaSessionObj - opaSessionObj := cautils.NewOPASessionObjMock() - opaSessionObj.Policies = []reporthandling.Framework{*reporthandling.MockFrameworkA()} - policies := ConvertFrameworksToPolicies(opaSessionObj.Policies, "") - - opaSessionObj.K8SResources = &k8sResources - opaSessionObj.AllResources = allResources - - opap := NewOPAProcessor(opaSessionObj, resources.NewRegoDependenciesDataMock()) // , - opap.Process(policies) - opap.updateResults() - for _, f := range opap.PostureReport.FrameworkReports { - for _, c := range f.ControlReports { - for _, r := range c.RuleReports { - for _, rr := range r.RuleResponses { - // t.Errorf("AlertMessage: %v", rr.AlertMessage) - if rr.Exception != nil { - t.Errorf("Exception: %v", rr.Exception) - } - } - } - } - } - -} - func TestProcessResourcesResult(t *testing.T) { // set k8s diff --git a/core/pkg/resultshandling/printer/v1/jsonprinter.go b/core/pkg/resultshandling/printer/v1/jsonprinter.go index 1a5c6c39..d5f231bd 100644 --- a/core/pkg/resultshandling/printer/v1/jsonprinter.go +++ b/core/pkg/resultshandling/printer/v1/jsonprinter.go @@ -27,15 +27,15 @@ func (jsonPrinter *JsonPrinter) Score(score float32) { } func (jsonPrinter *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) { - cautils.ReportV2ToV1(opaSessionObj) + report := cautils.ReportV2ToV1(opaSessionObj) var postureReportStr []byte var err error - if len(opaSessionObj.PostureReport.FrameworkReports) == 1 { - postureReportStr, err = json.Marshal(opaSessionObj.PostureReport.FrameworkReports[0]) + if len(report.FrameworkReports) == 1 { + postureReportStr, err = json.Marshal(report.FrameworkReports[0]) } else { - postureReportStr, err = json.Marshal(opaSessionObj.PostureReport.FrameworkReports) + postureReportStr, err = json.Marshal(report.FrameworkReports) } if err != nil { diff --git a/core/pkg/resultshandling/printer/v1/prometheusprinter.go b/core/pkg/resultshandling/printer/v1/prometheusprinter.go index 2360b550..6947c26c 100644 --- a/core/pkg/resultshandling/printer/v1/prometheusprinter.go +++ b/core/pkg/resultshandling/printer/v1/prometheusprinter.go @@ -87,9 +87,9 @@ func (printer *PrometheusPrinter) printReports(allResources map[string]workloadi } func (printer *PrometheusPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) { - cautils.ReportV2ToV1(opaSessionObj) + report := cautils.ReportV2ToV1(opaSessionObj) - err := printer.printReports(opaSessionObj.AllResources, opaSessionObj.PostureReport.FrameworkReports) + err := printer.printReports(opaSessionObj.AllResources, report.FrameworkReports) if err != nil { logger.L().Fatal(err.Error()) } diff --git a/core/pkg/resultshandling/reporter/v1/reporteventreceiver.go b/core/pkg/resultshandling/reporter/v1/reporteventreceiver.go deleted file mode 100644 index 850e2d23..00000000 --- a/core/pkg/resultshandling/reporter/v1/reporteventreceiver.go +++ /dev/null @@ -1,185 +0,0 @@ -package v1 - -import ( - "encoding/json" - "fmt" - "net/http" - "net/url" - "os" - - "github.com/armosec/k8s-interface/workloadinterface" - "github.com/armosec/kubescape/v2/core/cautils" - "github.com/armosec/kubescape/v2/core/cautils/getter" - v2 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v2" - "github.com/armosec/opa-utils/reporthandling" - logger "github.com/dwertent/go-logger" - "github.com/dwertent/go-logger/helpers" - - "github.com/google/uuid" -) - -const MAX_REPORT_SIZE = 2097152 // 2 MB - -type ReportEventReceiver struct { - httpClient *http.Client - clusterName string - customerGUID string - eventReceiverURL *url.URL - token string - customerAdminEMail string - message string -} - -func NewReportEventReceiver(tenantConfig *cautils.ConfigObj) *ReportEventReceiver { - return &ReportEventReceiver{ - httpClient: &http.Client{}, - clusterName: tenantConfig.ClusterName, - customerGUID: tenantConfig.AccountID, - token: tenantConfig.Token, - customerAdminEMail: tenantConfig.CustomerAdminEMail, - } -} - -func (report *ReportEventReceiver) Submit(opaSessionObj *cautils.OPASessionObj) error { - if opaSessionObj.PostureReport == nil && opaSessionObj.Report != nil { - cautils.ReportV2ToV1(opaSessionObj) - } - - if report.customerGUID == "" { - report.message = "WARNING: Failed to publish results. Reason: Unknown accout ID. Run kubescape with the '--account ' flag. Please feel free to contact ARMO team for more details" - return nil - } - if report.clusterName == "" { - report.message = "WARNING: Failed to publish results because the cluster name is Unknown. If you are scanning YAML files the results are not submitted to the Kubescape SaaS.Please feel free to contact ARMO team for more details" - return nil - } - opaSessionObj.PostureReport.ReportID = uuid.NewString() - opaSessionObj.PostureReport.CustomerGUID = report.customerGUID - opaSessionObj.PostureReport.ClusterName = report.clusterName - - if err := report.prepareReport(opaSessionObj.PostureReport, opaSessionObj.AllResources); err != nil { - report.message = err.Error() - return nil - } - return nil -} - -func (report *ReportEventReceiver) SetCustomerGUID(customerGUID string) { - report.customerGUID = customerGUID -} - -func (report *ReportEventReceiver) SetClusterName(clusterName string) { - 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() - - // send framework results - if err := report.sendReport(host, postureReport); err != nil { - cautils.StopSpinner() - return err - } - - // send resources - if err := report.sendResources(host, postureReport, allResources); err != nil { - cautils.StopSpinner() - return err - } - cautils.StopSpinner() - report.generateMessage() - - return nil -} - -func (report *ReportEventReceiver) sendResources(host string, postureReport *reporthandling.PostureReport, allResources map[string]workloadinterface.IMetadata) error { - splittedPostureReport := setPaginationReport(postureReport) - counter := 0 - - for _, v := range allResources { - r, err := json.Marshal(*iMetaToResource(v)) - if err != nil { - return fmt.Errorf("failed to unmarshal resource '%s', reason: %v", v.GetID(), err) - } - - if counter+len(r) >= MAX_REPORT_SIZE && len(splittedPostureReport.Resources) > 0 { - - // send report - if err := report.sendReport(host, splittedPostureReport); err != nil { - return err - } - - // delete resources - splittedPostureReport.Resources = []reporthandling.Resource{} - - // restart counter - counter = 0 - } - - counter += len(r) - splittedPostureReport.Resources = append(splittedPostureReport.Resources, *iMetaToResource(v)) - } - - return report.sendReport(host, splittedPostureReport) -} -func (report *ReportEventReceiver) sendReport(host string, postureReport *reporthandling.PostureReport) error { - reqBody, err := json.Marshal(postureReport) - if err != nil { - return fmt.Errorf("in 'sendReport' failed to json.Marshal, reason: %v", err) - } - - msg, err := getter.HttpPost(report.httpClient, host, nil, reqBody) - if err != nil { - return fmt.Errorf("%s, %v:%s", host, err, msg) - } - return nil -} - -func (report *ReportEventReceiver) generateMessage() { - 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.Host = getter.GetArmoAPIConnector().GetFrontendURL() - v2.ParseHost(&u) - - if report.customerAdminEMail != "" { - logger.L().Debug("", helpers.String("account ID", report.customerGUID)) - report.message = fmt.Sprintf("%s %s/configuration-scanning/%s", message, u.String(), report.clusterName) - return - } - u.Path = "account/sign-up" - q := u.Query() - q.Add("invitationToken", report.token) - q.Add("customerGUID", report.customerGUID) - - u.RawQuery = q.Encode() - report.message = fmt.Sprintf("%s %s", message, u.String()) -} - -func (report *ReportEventReceiver) GetURL() string { - return getter.GetArmoAPIConnector().GetFrontendURL() -} -func (report *ReportEventReceiver) DisplayReportURL() { - cautils.InfoTextDisplay(os.Stderr, fmt.Sprintf("\n\n%s\n\n", report.message)) -} - -// func maskID(id string) string { -// sep := "-" -// splitted := strings.Split(id, sep) -// if len(splitted) != 5 { -// return "" -// } -// str := splitted[0][:4] -// splitted[0] = splitted[0][4:] -// for i := range splitted { -// for j := 0; j < len(splitted[i]); j++ { -// str += "X" -// } -// str += sep -// } - -// return strings.TrimSuffix(str, sep) -// } diff --git a/core/pkg/resultshandling/reporter/v1/reporteventreceiverutils.go b/core/pkg/resultshandling/reporter/v1/reporteventreceiverutils.go deleted file mode 100644 index 8e814c69..00000000 --- a/core/pkg/resultshandling/reporter/v1/reporteventreceiverutils.go +++ /dev/null @@ -1,48 +0,0 @@ -package v1 - -import ( - "net/url" - - "github.com/armosec/k8s-interface/workloadinterface" - "github.com/armosec/kubescape/v2/core/cautils/getter" - v2 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v2" - "github.com/armosec/opa-utils/reporthandling" - "github.com/google/uuid" -) - -func (report *ReportEventReceiver) initEventReceiverURL() { - urlObj := url.URL{} - urlObj.Host = getter.GetArmoAPIConnector().GetReportReceiverURL() - v2.ParseHost(&urlObj) - - urlObj.Path = "/k8s/postureReport" - q := urlObj.Query() - q.Add("customerGUID", uuid.MustParse(report.customerGUID).String()) - q.Add("clusterName", report.clusterName) - - urlObj.RawQuery = q.Encode() - - report.eventReceiverURL = &urlObj -} - -func hostToString(host *url.URL, reportID string) string { - q := host.Query() - q.Add("reportID", reportID) // TODO - do we add the reportID? - host.RawQuery = q.Encode() - return host.String() -} - -func setPaginationReport(postureReport *reporthandling.PostureReport) *reporthandling.PostureReport { - return &reporthandling.PostureReport{ - CustomerGUID: postureReport.CustomerGUID, - ClusterName: postureReport.ClusterName, - ReportID: postureReport.ReportID, - ReportGenerationTime: postureReport.ReportGenerationTime, - } -} -func iMetaToResource(obj workloadinterface.IMetadata) *reporthandling.Resource { - return &reporthandling.Resource{ - ResourceID: obj.GetID(), - Object: obj.GetObject(), - } -} diff --git a/core/pkg/resultshandling/reporter/v1/reporteventreceiverutils_test.go b/core/pkg/resultshandling/reporter/v1/reporteventreceiverutils_test.go deleted file mode 100644 index 70b08550..00000000 --- a/core/pkg/resultshandling/reporter/v1/reporteventreceiverutils_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package v1 - -import ( - "net/url" - "testing" -) - -func TestHostToString(t *testing.T) { - host := url.URL{ - Scheme: "https", - Host: "report.eudev3.cyberarmorsoft.com", - Path: "k8srestapi/v1/postureReport", - RawQuery: "cluster=openrasty_seal-7fvz&customerGUID=5d817063-096f-4d91-b39b-8665240080af", - } - expectedHost := "https://report.eudev3.cyberarmorsoft.com/k8srestapi/v1/postureReport?cluster=openrasty_seal-7fvz&customerGUID=5d817063-096f-4d91-b39b-8665240080af&reportID=ffdd2a00-4dc8-4bf3-b97a-a6d4fd198a41" - receivedHost := hostToString(&host, "ffdd2a00-4dc8-4bf3-b97a-a6d4fd198a41") - if receivedHost != expectedHost { - t.Errorf("%s != %s", receivedHost, expectedHost) - } -} diff --git a/core/pkg/resultshandling/reporter/v2/reporteventreceive_test.go b/core/pkg/resultshandling/reporter/v2/reporteventreceive_test.go deleted file mode 100644 index b5074c60..00000000 --- a/core/pkg/resultshandling/reporter/v2/reporteventreceive_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package v2 - -import ( - "testing" - - "github.com/armosec/kubescape/v2/core/cautils" - "github.com/stretchr/testify/assert" -) - -func TestGetURL(t *testing.T) { - // Test submit and registered url - { - reporter := NewReportEventReceiver( - &cautils.ConfigObj{ - AccountID: "1234", - Token: "token", - CustomerAdminEMail: "my@email", - ClusterName: "test", - }, - "", - ) - assert.Equal(t, "https://cloud.armosec.io/configuration-scanning/test?utm_campaign=Submit&utm_medium=CLI&utm_source=GitHub", reporter.GetURL()) - } - - // Test submit and NOT registered url - { - - reporter := NewReportEventReceiver( - &cautils.ConfigObj{ - AccountID: "1234", - Token: "token", - ClusterName: "test", - }, - "", - ) - assert.Equal(t, "https://cloud.armosec.io/account/sign-up?customerGUID=1234&invitationToken=token&utm_campaign=Submit&utm_medium=CLI&utm_source=GitHub", reporter.GetURL()) - } - // Test None submit url - { - reporter := NewReportMock(NO_SUBMIT_QUERY, "") - assert.Equal(t, "https://cloud.armosec.io/account/sign-up?utm_source=GitHub&utm_medium=CLI&utm_campaign=no_submit", reporter.GetURL()) - } - // Test None report url - { - reporter := NewReportMock("", "") - assert.Equal(t, "https://cloud.armosec.io/account/sign-up", reporter.GetURL()) - } -} diff --git a/core/pkg/resultshandling/reporter/v2/reporteventreceiver.go b/core/pkg/resultshandling/reporter/v2/reporteventreceiver.go index 51c86cd6..a0b096ce 100644 --- a/core/pkg/resultshandling/reporter/v2/reporteventreceiver.go +++ b/core/pkg/resultshandling/reporter/v2/reporteventreceiver.go @@ -20,6 +20,14 @@ import ( const MAX_REPORT_SIZE = 2097152 // 2 MB +type SubmitContext string + +const ( + SubmitContextScan SubmitContext = "scan" + SubmitContextRBAC SubmitContext = "rbac" + SubmitContextRepository SubmitContext = "repository" +) + type ReportEventReceiver struct { httpClient *http.Client clusterName string @@ -29,9 +37,10 @@ type ReportEventReceiver struct { customerAdminEMail string message string reportID string + submitContext SubmitContext } -func NewReportEventReceiver(tenantConfig *cautils.ConfigObj, reportID string) *ReportEventReceiver { +func NewReportEventReceiver(tenantConfig *cautils.ConfigObj, reportID string, submitContext SubmitContext) *ReportEventReceiver { return &ReportEventReceiver{ httpClient: &http.Client{}, clusterName: tenantConfig.ClusterName, @@ -39,6 +48,7 @@ func NewReportEventReceiver(tenantConfig *cautils.ConfigObj, reportID string) *R token: tenantConfig.Token, customerAdminEMail: tenantConfig.CustomerAdminEMail, reportID: reportID, + submitContext: submitContext, } } @@ -95,21 +105,11 @@ func (report *ReportEventReceiver) prepareReport(opaSessionObj *cautils.OPASessi func (report *ReportEventReceiver) GetURL() string { u := url.URL{} u.Host = getter.GetArmoAPIConnector().GetFrontendURL() - ParseHost(&u) + + parseHost(&u) + report.addPathURL(&u) + q := u.Query() - - if report.customerAdminEMail != "" || report.token == "" { // data has been submitted - if report.clusterName != "" { - u.Path = fmt.Sprintf("configuration-scanning/%s", report.clusterName) - } else { - u.Path = fmt.Sprintf("repositories-scan/%s", report.reportID) - } - } else { - u.Path = "account/sign-up" - q.Add("invitationToken", report.token) - q.Add("customerGUID", report.customerGUID) - } - q.Add("utm_source", "GitHub") q.Add("utm_medium", "CLI") q.Add("utm_campaign", "Submit") @@ -226,3 +226,24 @@ func (report *ReportEventReceiver) DisplayReportURL() { cautils.InfoTextDisplay(os.Stderr, fmt.Sprintf("\n\n%s\n\n", report.message)) } } + +func (report *ReportEventReceiver) addPathURL(urlObj *url.URL) { + if report.customerAdminEMail != "" || report.token == "" { // data has been submitted + switch report.submitContext { + case SubmitContextScan: + urlObj.Path = fmt.Sprintf("configuration-scanning/%s", report.clusterName) + case SubmitContextRBAC: + urlObj.Path = fmt.Sprintf("rbac-visualizer") + case SubmitContextRepository: + urlObj.Path = fmt.Sprintf("repository-scanning/%s", report.clusterName) + } + return + } + urlObj.Path = "account/sign-up" + + q := urlObj.Query() + q.Add("invitationToken", report.token) + q.Add("customerGUID", report.customerGUID) + urlObj.RawQuery = q.Encode() + +} diff --git a/core/pkg/resultshandling/reporter/v2/reporteventreceiver_test.go b/core/pkg/resultshandling/reporter/v2/reporteventreceiver_test.go new file mode 100644 index 00000000..d979d4c5 --- /dev/null +++ b/core/pkg/resultshandling/reporter/v2/reporteventreceiver_test.go @@ -0,0 +1,118 @@ +package v2 + +import ( + "net/url" + "testing" + + "github.com/armosec/kubescape/v2/core/cautils" + "github.com/stretchr/testify/assert" +) + +func TestReportEventReceiver_addPathURL(t *testing.T) { + tests := []struct { + name string + report *ReportEventReceiver + urlObj *url.URL + want *url.URL + }{ + { + name: "add scan path", + report: &ReportEventReceiver{ + clusterName: "test", + customerGUID: "FFFF", + token: "XXXX", + customerAdminEMail: "test@test", + reportID: "1234", + submitContext: SubmitContextScan, + }, + urlObj: &url.URL{ + Scheme: "https", + Host: "localhost:8080", + }, + want: &url.URL{ + Scheme: "https", + Host: "localhost:8080", + Path: "configuration-scanning/test", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.report.addPathURL(tt.urlObj) + assert.Equal(t, tt.want.String(), tt.urlObj.String()) + + }) + } +} + +func TestGetURL(t *testing.T) { + // Test submit and registered url + { + reporter := NewReportEventReceiver( + &cautils.ConfigObj{ + AccountID: "1234", + Token: "token", + CustomerAdminEMail: "my@email", + ClusterName: "test", + }, + "", + SubmitContextScan, + ) + assert.Equal(t, "https://cloud.armosec.io/configuration-scanning/test?utm_campaign=Submit&utm_medium=CLI&utm_source=GitHub", reporter.GetURL()) + } + + // Test rbac submit and registered url + { + reporter := NewReportEventReceiver( + &cautils.ConfigObj{ + AccountID: "1234", + Token: "token", + CustomerAdminEMail: "my@email", + ClusterName: "test", + }, + "", + SubmitContextRBAC, + ) + assert.Equal(t, "https://cloud.armosec.io/rbac-visualizer?utm_campaign=Submit&utm_medium=CLI&utm_source=GitHub", reporter.GetURL()) + } + + // Test repo submit and registered url + { + reporter := NewReportEventReceiver( + &cautils.ConfigObj{ + AccountID: "1234", + Token: "token", + CustomerAdminEMail: "my@email", + ClusterName: "test", + }, + "", + SubmitContextRepository, + ) + assert.Equal(t, "https://cloud.armosec.io/repository-scanning/test?utm_campaign=Submit&utm_medium=CLI&utm_source=GitHub", reporter.GetURL()) + } + + // Test submit and NOT registered url + { + + reporter := NewReportEventReceiver( + &cautils.ConfigObj{ + AccountID: "1234", + Token: "token", + ClusterName: "test", + }, + "", + SubmitContextScan, + ) + assert.Equal(t, "https://cloud.armosec.io/account/sign-up?customerGUID=1234&invitationToken=token&utm_campaign=Submit&utm_medium=CLI&utm_source=GitHub", reporter.GetURL()) + } + // Test None submit url + { + reporter := NewReportMock(NO_SUBMIT_QUERY, "") + assert.Equal(t, "https://cloud.armosec.io/account/sign-up?utm_source=GitHub&utm_medium=CLI&utm_campaign=no_submit", reporter.GetURL()) + } + // Test None report url + { + reporter := NewReportMock("", "") + assert.Equal(t, "https://cloud.armosec.io/account/sign-up", reporter.GetURL()) + } +} diff --git a/core/pkg/resultshandling/reporter/v2/reporteventreceiverutils.go b/core/pkg/resultshandling/reporter/v2/reporteventreceiverutils.go index 3d9b926b..d67b6e98 100644 --- a/core/pkg/resultshandling/reporter/v2/reporteventreceiverutils.go +++ b/core/pkg/resultshandling/reporter/v2/reporteventreceiverutils.go @@ -12,7 +12,8 @@ import ( func (report *ReportEventReceiver) initEventReceiverURL() { urlObj := url.URL{} urlObj.Host = getter.GetArmoAPIConnector().GetReportReceiverURL() - ParseHost(&urlObj) + parseHost(&urlObj) + urlObj.Path = "/k8s/v2/postureReport" q := urlObj.Query() q.Add("customerGUID", uuid.MustParse(report.customerGUID).String()) diff --git a/core/pkg/resultshandling/reporter/v2/utils.go b/core/pkg/resultshandling/reporter/v2/utils.go index d2c30cbb..8770d501 100644 --- a/core/pkg/resultshandling/reporter/v2/utils.go +++ b/core/pkg/resultshandling/reporter/v2/utils.go @@ -23,7 +23,7 @@ func maskID(id string) string { return strings.TrimSuffix(str, sep) } -func ParseHost(urlObj *url.URL) { +func parseHost(urlObj *url.URL) { if strings.Contains(urlObj.Host, "http://") { urlObj.Scheme = "http" urlObj.Host = strings.Replace(urlObj.Host, "http://", "", 1) diff --git a/core/pkg/resultshandling/reporter/v2/utils_test.go b/core/pkg/resultshandling/reporter/v2/utils_test.go index cb4a3429..4ec6a4d7 100644 --- a/core/pkg/resultshandling/reporter/v2/utils_test.go +++ b/core/pkg/resultshandling/reporter/v2/utils_test.go @@ -11,27 +11,27 @@ func TestParseHost(t *testing.T) { urlObj := url.URL{} urlObj.Host = "http://localhost:7555" - ParseHost(&urlObj) + parseHost(&urlObj) assert.Equal(t, "http", urlObj.Scheme) assert.Equal(t, "localhost:7555", urlObj.Host) urlObj.Host = "https://localhost:7555" - ParseHost(&urlObj) + parseHost(&urlObj) assert.Equal(t, "https", urlObj.Scheme) assert.Equal(t, "localhost:7555", urlObj.Host) urlObj.Host = "http://portal-dev.armo.cloud" - ParseHost(&urlObj) + parseHost(&urlObj) assert.Equal(t, "http", urlObj.Scheme) assert.Equal(t, "portal-dev.armo.cloud", urlObj.Host) urlObj.Host = "https://portal-dev.armo.cloud" - ParseHost(&urlObj) + parseHost(&urlObj) assert.Equal(t, "https", urlObj.Scheme) assert.Equal(t, "portal-dev.armo.cloud", urlObj.Host) urlObj.Host = "portal-dev.armo.cloud" - ParseHost(&urlObj) + parseHost(&urlObj) assert.Equal(t, "https", urlObj.Scheme) assert.Equal(t, "portal-dev.armo.cloud", urlObj.Host) diff --git a/core/pkg/score/score.go b/core/pkg/score/score.go index 519a7e5e..b313e965 100644 --- a/core/pkg/score/score.go +++ b/core/pkg/score/score.go @@ -26,8 +26,6 @@ const ( func (su *ScoreWrapper) Calculate(reportVersion PostureReportVersion) error { switch reportVersion { - case EPostureReportV1: - return su.scoreUtil.Calculate(su.opaSessionObj.PostureReport.FrameworkReports) case EPostureReportV2: return su.scoreUtil.CalculatePostureReportV2(su.opaSessionObj.Report) }