From 8ce64d2a7f587809db3547d64c27f77b651591bf Mon Sep 17 00:00:00 2001 From: Clint Modien Date: Thu, 6 Jan 2022 09:45:11 -0800 Subject: [PATCH 01/34] Fixes #296 Spelling error Namescape should be Namespace --- resultshandling/printer/summeryhelpers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resultshandling/printer/summeryhelpers.go b/resultshandling/printer/summeryhelpers.go index dee39235..a428b39d 100644 --- a/resultshandling/printer/summeryhelpers.go +++ b/resultshandling/printer/summeryhelpers.go @@ -22,7 +22,7 @@ func groupByNamespaceOrKind(resources []WorkloadSummary, status func(workloadSum case workloadinterface.TypeWorkloadObject: ns := "" if resources[i].resource.GetNamespace() != "" { - ns = "Namescape " + resources[i].resource.GetNamespace() + ns = "Namespace " + resources[i].resource.GetNamespace() } if r, ok := mapResources[ns]; ok { r = append(r, resources[i]) From b4f10f854ee1752d65203f1825ef382d07c32dfc Mon Sep 17 00:00:00 2001 From: Saiyam Pathak Date: Fri, 7 Jan 2022 11:27:32 +0530 Subject: [PATCH 02/34] Adding contributors --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index ddee4832..7b320e8e 100644 --- a/README.md +++ b/README.md @@ -319,3 +319,9 @@ The tools retrieves Kubernetes objects from the API server and runs a set of [re The results by default printed in a pretty "console friendly" manner, but they can be retrieved in JSON format for further processing. Kubescape is an open source project, we welcome your feedback and ideas for improvement. We’re also aiming to collaborate with the Kubernetes community to help make the tests themselves more robust and complete as Kubernetes develops. + +## Thanks to all the contributors ❤️ + + + + From 2c9524ed45fc18ae1f582d73807c1a6c03090da7 Mon Sep 17 00:00:00 2001 From: Saiyam Pathak Date: Sat, 8 Jan 2022 19:21:33 +0530 Subject: [PATCH 03/34] Adding Kubescape katacoda playground --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 7b320e8e..0df259f3 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,9 @@ Want to contribute? Want to discuss something? Have an issue? # Options and examples +## Playground +* [Kubescape playground](https://www.katacoda.com/pathaksaiyam/scenarios/kubescape) + ## Tutorials * [Overview](https://youtu.be/wdBkt_0Qhbg) From 392625b774ae019b493988c33f424a292e147804 Mon Sep 17 00:00:00 2001 From: alegrey91 Date: Mon, 10 Jan 2022 18:39:06 +0100 Subject: [PATCH 04/34] style(host-sensor): move kube-host-sensor manifest to indipendent yaml file --- hostsensorutils/{hostsensoryamls.go => hostsensor.yaml} | 6 ++---- hostsensorutils/hostsensordeploy.go | 6 ++++++ 2 files changed, 8 insertions(+), 4 deletions(-) rename hostsensorutils/{hostsensoryamls.go => hostsensor.yaml} (94%) diff --git a/hostsensorutils/hostsensoryamls.go b/hostsensorutils/hostsensor.yaml similarity index 94% rename from hostsensorutils/hostsensoryamls.go rename to hostsensorutils/hostsensor.yaml index a29bcce8..305bd09b 100644 --- a/hostsensorutils/hostsensoryamls.go +++ b/hostsensorutils/hostsensor.yaml @@ -1,6 +1,4 @@ -package hostsensorutils - -const hostSensorYAML = `apiVersion: v1 +apiVersion: v1 kind: Namespace metadata: labels: @@ -62,4 +60,4 @@ spec: name: host-filesystem hostNetwork: true hostPID: true - hostIPC: true` + hostIPC: true \ No newline at end of file diff --git a/hostsensorutils/hostsensordeploy.go b/hostsensorutils/hostsensordeploy.go index 06e1c008..3d7042e8 100644 --- a/hostsensorutils/hostsensordeploy.go +++ b/hostsensorutils/hostsensordeploy.go @@ -1,6 +1,7 @@ package hostsensorutils import ( + _ "embed" "fmt" "io" "strings" @@ -18,6 +19,11 @@ import ( coreapplyv1 "k8s.io/client-go/applyconfigurations/core/v1" ) +var ( + //go:embed hostsensor.yaml + hostSensorYAML string +) + type HostSensorHandler struct { HostSensorPort int32 HostSensorPodNames map[string]string //map from pod names to node names From 995f615b10e7e17ab41f7ce1646730682bb4990d Mon Sep 17 00:00:00 2001 From: yiscah Date: Mon, 10 Jan 2022 20:00:42 +0200 Subject: [PATCH 05/34] support load artifacts from local path --- cautils/getter/loadpolicy.go | 17 ++++++++++++--- cautils/scaninfo.go | 40 ++++++++++++++++++++++++++++++++++-- clihandler/cmd/scan.go | 1 + 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/cautils/getter/loadpolicy.go b/cautils/getter/loadpolicy.go index 9cde9a0f..7ae8c8f5 100644 --- a/cautils/getter/loadpolicy.go +++ b/cautils/getter/loadpolicy.go @@ -85,8 +85,19 @@ func (lp *LoadPolicy) GetFrameworks() ([]reporthandling.Framework, error) { } func (lp *LoadPolicy) ListFrameworks() ([]string, error) { - // TODO - Support - return []string{}, fmt.Errorf("loading frameworks list from file is not supported") + fwNames := []string{} + framework := &reporthandling.Framework{} + for _, f := range lp.filePaths { + file, err := os.ReadFile(f) + if err == nil { + if err := json.Unmarshal(file, framework); err == nil { + if !contains(fwNames, framework.Name) { + fwNames = append(fwNames, framework.Name) + } + } + } + } + return fwNames, nil } func (lp *LoadPolicy) ListControls(listType ListType) ([]string, error) { @@ -114,7 +125,7 @@ func (lp *LoadPolicy) GetControlsInputs(clusterName string) (map[string][]string return nil, err } - if err = json.Unmarshal(f, &accountConfig); err == nil { + if err = json.Unmarshal(f, &accountConfig.Settings.PostureControlInputs); err == nil { return accountConfig.Settings.PostureControlInputs, nil } return nil, err diff --git a/cautils/scaninfo.go b/cautils/scaninfo.go index 3175f1d9..a2c83ca9 100644 --- a/cautils/scaninfo.go +++ b/cautils/scaninfo.go @@ -1,7 +1,11 @@ package cautils import ( + "encoding/json" "fmt" + "io/ioutil" + "log" + "os" "path/filepath" "github.com/armosec/kubescape/cautils/getter" @@ -9,8 +13,10 @@ import ( ) const ( - ScanCluster string = "cluster" - ScanLocalFiles string = "yaml" + ScanCluster string = "cluster" + ScanLocalFiles string = "yaml" + localControlInputsFilename string = "controls-inputs.json" + localExceptionsFilename string = "exceptions.json" ) type BoolPtrFlag struct { @@ -52,6 +58,7 @@ type ScanInfo struct { ControlsInputs string // Load file with inputs for controls UseFrom []string // Load framework from local file (instead of download). Use when running offline UseDefault bool // Load framework from cached file (instead of download). Use when running offline + UseArtifactsFrom string // Load artifacts from local path. Use when running offline VerboseMode bool // Display all of the input resources and not only failed resources Format string // Format results (table, json, junit ...) Output string // Store results in an output file, Output file name @@ -78,7 +85,36 @@ type Getters struct { func (scanInfo *ScanInfo) Init() { scanInfo.setUseFrom() scanInfo.setOutputFile() + scanInfo.setUseArtifactsFrom() +} +func (scanInfo *ScanInfo) setUseArtifactsFrom() { + // UseArtifactsFrom must be a path without a filename + dir, file := filepath.Split(scanInfo.UseArtifactsFrom) + if dir == "" { + scanInfo.UseArtifactsFrom = file + } else { + scanInfo.UseArtifactsFrom = dir + } + // set frameworks files + files, err := ioutil.ReadDir(scanInfo.UseArtifactsFrom) + if err != nil { + log.Fatal(err) + } + framework := &reporthandling.Framework{} + for _, f := range files { + filePath := filepath.Join(scanInfo.UseArtifactsFrom, f.Name()) + file, err := os.ReadFile(filePath) + if err == nil { + if err := json.Unmarshal(file, framework); err == nil { + scanInfo.UseFrom = append(scanInfo.UseFrom, filepath.Join(scanInfo.UseArtifactsFrom, f.Name())) + } + } + } + // set config-inputs file + scanInfo.ControlsInputs = filepath.Join(scanInfo.UseArtifactsFrom, localControlInputsFilename) + // set exceptions + scanInfo.UseExceptions = filepath.Join(scanInfo.UseArtifactsFrom, localExceptionsFilename) } func (scanInfo *ScanInfo) setUseFrom() { diff --git a/clihandler/cmd/scan.go b/clihandler/cmd/scan.go index 00440d62..80f0e538 100644 --- a/clihandler/cmd/scan.go +++ b/clihandler/cmd/scan.go @@ -45,6 +45,7 @@ func init() { rootCmd.PersistentFlags().StringVarP(&scanInfo.ClusterName, "cluster", "", "", "Cluster name. Default will use the current-context") 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().StringVar(&scanInfo.UseArtifactsFrom, "use-artifacts-from", "", "Load artifacts from local directory. If not used will download them") 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", 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"`) From 05b6394c5c66550b45ec8c8ee822c30e1915861c Mon Sep 17 00:00:00 2001 From: dwertent Date: Tue, 11 Jan 2022 14:21:16 +0200 Subject: [PATCH 06/34] send report to v2 --- clihandler/initcliutils.go | 5 +++-- go.mod | 5 +++-- go.sum | 10 ++++++---- resultshandling/printer/v2/utils.go | 8 ++++---- .../reporter/v2/reporteventreceiver.go | 19 ++++++++++++------- .../reporter/v2/reporteventreceiverutils.go | 10 ++++++++-- resultshandling/reporter/v2/utils.go | 8 ++++---- resultshandling/results.go | 10 ++++++---- 8 files changed, 46 insertions(+), 29 deletions(-) diff --git a/clihandler/initcliutils.go b/clihandler/initcliutils.go index 6ac729a2..0315ef25 100644 --- a/clihandler/initcliutils.go +++ b/clihandler/initcliutils.go @@ -11,9 +11,9 @@ import ( "github.com/armosec/kubescape/resourcehandler" "github.com/armosec/kubescape/resultshandling/reporter" reporterv1 "github.com/armosec/kubescape/resultshandling/reporter/v1" + reporterv2 "github.com/armosec/kubescape/resultshandling/reporter/v2" "github.com/armosec/opa-utils/reporthandling" "github.com/armosec/rbac-utils/rbacscanner" - // reporterv2 "github.com/armosec/kubescape/resultshandling/reporter/v2" ) // getKubernetesApi @@ -48,7 +48,8 @@ func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.Kubern func getReporter(tenantConfig cautils.ITenantConfig, submit bool) reporter.IReport { if submit { - return reporterv1.NewReportEventReceiver(tenantConfig.GetConfigObj()) + // return reporterv1.NewReportEventReceiver(tenantConfig.GetConfigObj()) + return reporterv2.NewReportEventReceiver(tenantConfig.GetConfigObj()) } return reporterv1.NewReportMock() } diff --git a/go.mod b/go.mod index c22c9285..f508bc22 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/armosec/kubescape go 1.17 require ( - github.com/armosec/armoapi-go v0.0.40 + github.com/armosec/armoapi-go v0.0.41 github.com/armosec/k8s-interface v0.0.54 - github.com/armosec/opa-utils v0.0.92 + github.com/armosec/opa-utils v0.0.95 github.com/armosec/rbac-utils v0.0.11 github.com/armosec/utils-go v0.0.3 github.com/briandowns/spinner v1.18.0 @@ -35,6 +35,7 @@ require ( github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect + github.com/armosec/armo-interfaces v0.0.3 // 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 diff --git a/go.sum b/go.sum index 77624138..4b9b0bd4 100644 --- a/go.sum +++ b/go.sum @@ -83,18 +83,20 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armosec/armo-interfaces v0.0.3 h1:kG4mJIPgWBJvQFDDy8JzdqX3ASbyl8t32IuJYqB31Pk= +github.com/armosec/armo-interfaces v0.0.3/go.mod h1:7XYefhcBCFYoF5LflCZHWuUHu+JrSJbmzk0zoNv2WlU= github.com/armosec/armoapi-go v0.0.2/go.mod h1:vIK17yoKbJRQyZXWWLe3AqfqCRITxW8qmSkApyq5xFs= github.com/armosec/armoapi-go v0.0.23/go.mod h1:iaVVGyc23QGGzAdv4n+szGQg3Rbpixn9yQTU3qWRpaw= -github.com/armosec/armoapi-go v0.0.40 h1:KQRJXFqw95s6cV7HoGgw1x8qrRZ9eNVze//yQbo24Lk= -github.com/armosec/armoapi-go v0.0.40/go.mod h1:iaVVGyc23QGGzAdv4n+szGQg3Rbpixn9yQTU3qWRpaw= +github.com/armosec/armoapi-go v0.0.41 h1:iMkaCsME+zhE6vnCOMaqfqc0cp7pste8QFHojeGKfGg= +github.com/armosec/armoapi-go v0.0.41/go.mod h1:exk1O3rK6V+X8SSyxc06lwb0j9ILQuKAoIdz9hs6Ndw= github.com/armosec/k8s-interface v0.0.8/go.mod h1:xxS+V5QT3gVQTwZyAMMDrYLWGrfKOpiJ7Jfhfa0w9sM= github.com/armosec/k8s-interface v0.0.37/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU= github.com/armosec/k8s-interface v0.0.50/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU= github.com/armosec/k8s-interface v0.0.54 h1:1sQeoEZA5bgpXVibXhEiTSeLd3GKY5NkTOeewdgR0Bs= github.com/armosec/k8s-interface v0.0.54/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.92 h1:RzzORhfLx9Evc2ceFtNRoehxUFzwlvK5iMtR6fLWzZc= -github.com/armosec/opa-utils v0.0.92/go.mod h1:ZOXYVTtuyrV4TldcfbzgRqP6F9Drlf4hB0zr210OXgM= +github.com/armosec/opa-utils v0.0.95 h1:tGFJQChy8Yn+8HhLbQHUfa6HmbrtBFBOlIgkO/9IFY8= +github.com/armosec/opa-utils v0.0.95/go.mod h1:BNTjeianyXlflJMz3bZM0GimBWqmzirUf1whWR6Os04= github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40= github.com/armosec/rbac-utils v0.0.11 h1:SCiVLqUeV+WGpUsWbOBt6jKkFAd62jztuzB6PIgHz7w= github.com/armosec/rbac-utils v0.0.11/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0= diff --git a/resultshandling/printer/v2/utils.go b/resultshandling/printer/v2/utils.go index 304301da..86f91e89 100644 --- a/resultshandling/printer/v2/utils.go +++ b/resultshandling/printer/v2/utils.go @@ -3,8 +3,8 @@ package v2 import ( "github.com/armosec/k8s-interface/workloadinterface" "github.com/armosec/kubescape/cautils" + "github.com/armosec/opa-utils/reporthandling" "github.com/armosec/opa-utils/reporthandling/results/v1/resourcesresults" - reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2" ) // finalizeV2Report finalize the results objects by copying data from map to lists @@ -16,7 +16,7 @@ func finalizeReport(opaSessionObj *cautils.OPASessionObj) { } if len(opaSessionObj.Report.Resources) == 0 { - opaSessionObj.Report.Resources = make([]reporthandlingv2.Resource, len(opaSessionObj.AllResources)) + opaSessionObj.Report.Resources = make([]reporthandling.Resource, len(opaSessionObj.AllResources)) finalizeResources(opaSessionObj.Report.Resources, opaSessionObj.AllResources) opaSessionObj.AllResources = nil } @@ -30,10 +30,10 @@ func finalizeResults(results []resourcesresults.Result, resourcesResult map[stri } } -func finalizeResources(resources []reporthandlingv2.Resource, allResources map[string]workloadinterface.IMetadata) { +func finalizeResources(resources []reporthandling.Resource, allResources map[string]workloadinterface.IMetadata) { index := 0 for resourceID := range allResources { - resources[index] = reporthandlingv2.Resource{ + resources[index] = reporthandling.Resource{ ResourceID: resourceID, Object: allResources[resourceID], } diff --git a/resultshandling/reporter/v2/reporteventreceiver.go b/resultshandling/reporter/v2/reporteventreceiver.go index 71f681df..98966255 100644 --- a/resultshandling/reporter/v2/reporteventreceiver.go +++ b/resultshandling/reporter/v2/reporteventreceiver.go @@ -11,6 +11,7 @@ import ( "github.com/armosec/kubescape/cautils/getter" uuid "github.com/satori/go.uuid" + "github.com/armosec/opa-utils/reporthandling" "github.com/armosec/opa-utils/reporthandling/results/v1/resourcesresults" reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2" ) @@ -87,12 +88,12 @@ func (report *ReportEventReceiver) prepareReport(postureReport *reporthandlingv2 if err := report.sendResources(host, postureReport, &reportCounter); err != nil { return err } - reportCounter++ + // reportCounter++ - // send framework results - if err := report.sendSummary(host, postureReport, &reportCounter); err != nil { - return err - } + // // send framework results + // if err := report.sendSummary(host, postureReport, &reportCounter); err != nil { + // return err + // } return nil } @@ -116,7 +117,7 @@ func (report *ReportEventReceiver) sendResources(host string, postureReport *rep *reportCounter++ // delete resources - splittedPostureReport.Resources = []reporthandlingv2.Resource{} + splittedPostureReport.Resources = []reporthandling.Resource{} // restart counter counter = 0 @@ -126,7 +127,7 @@ func (report *ReportEventReceiver) sendResources(host string, postureReport *rep splittedPostureReport.Resources = append(splittedPostureReport.Resources, v) } - return report.sendReport(host, splittedPostureReport, *reportCounter, false) + return report.sendReport(host, splittedPostureReport, *reportCounter, true) } func (report *ReportEventReceiver) sendResults(host string, postureReport *reporthandlingv2.PostureReport, reportCounter *int) error { @@ -168,6 +169,10 @@ func (report *ReportEventReceiver) sendSummary(host string, postureReport *repor return report.sendReport(host, splittedPostureReport, *reportCounter, true) } func (report *ReportEventReceiver) sendReport(host string, postureReport *reporthandlingv2.PostureReport, counter int, isLastReport bool) error { + postureReport.PaginationInfo = reporthandlingv2.PaginationMarks{ + ReportNumber: counter, + IsLastReport: isLastReport, + } reqBody, err := json.Marshal(postureReport) if err != nil { return fmt.Errorf("in 'sendReport' failed to json.Marshal, reason: %v", err) diff --git a/resultshandling/reporter/v2/reporteventreceiverutils.go b/resultshandling/reporter/v2/reporteventreceiverutils.go index 4740d2c9..66c823f3 100644 --- a/resultshandling/reporter/v2/reporteventreceiverutils.go +++ b/resultshandling/reporter/v2/reporteventreceiverutils.go @@ -15,7 +15,8 @@ func (report *ReportEventReceiver) initEventReceiverURL() { urlObj.Scheme = "https" urlObj.Host = getter.GetArmoAPIConnector().GetReportReceiverURL() - urlObj.Path = "/k8s/postureReport" + urlObj.Path = "/k8s/v2/postureReport" + q := urlObj.Query() q.Add("customerGUID", uuid.FromStringOrNil(report.customerGUID).String()) q.Add("clusterName", report.clusterName) @@ -27,7 +28,7 @@ func (report *ReportEventReceiver) initEventReceiverURL() { func hostToString(host *url.URL, reportID string) string { q := host.Query() - q.Add("reportID", reportID) // TODO - do we add the reportID? + q.Add("reportGUID", reportID) // TODO - do we add the reportID? host.RawQuery = q.Encode() return host.String() } @@ -38,6 +39,11 @@ func setSubReport(postureReport *reporthandlingv2.PostureReport) *reporthandling ClusterName: postureReport.ClusterName, ReportID: postureReport.ReportID, ReportGenerationTime: postureReport.ReportGenerationTime, + SummaryDetails: postureReport.SummaryDetails, + Attributes: postureReport.Attributes, + ClusterCloudProvider: postureReport.ClusterCloudProvider, + JobID: postureReport.JobID, + ClusterAPIServerInfo: postureReport.ClusterAPIServerInfo, } } func iMetaToResource(obj workloadinterface.IMetadata) *reporthandling.Resource { diff --git a/resultshandling/reporter/v2/utils.go b/resultshandling/reporter/v2/utils.go index 0f3ebc03..e4e32217 100644 --- a/resultshandling/reporter/v2/utils.go +++ b/resultshandling/reporter/v2/utils.go @@ -5,8 +5,8 @@ import ( "github.com/armosec/k8s-interface/workloadinterface" "github.com/armosec/kubescape/cautils" + "github.com/armosec/opa-utils/reporthandling" "github.com/armosec/opa-utils/reporthandling/results/v1/resourcesresults" - reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2" ) // finalizeV2Report finalize the results objects by copying data from map to lists @@ -19,7 +19,7 @@ func finalizeReport(opaSessionObj *cautils.OPASessionObj) { } if len(opaSessionObj.Report.Resources) == 0 { - opaSessionObj.Report.Resources = make([]reporthandlingv2.Resource, len(opaSessionObj.AllResources)) + opaSessionObj.Report.Resources = make([]reporthandling.Resource, len(opaSessionObj.AllResources)) finalizeResources(opaSessionObj.Report.Resources, opaSessionObj.AllResources) opaSessionObj.AllResources = nil } @@ -33,10 +33,10 @@ func finalizeResults(results []resourcesresults.Result, resourcesResult map[stri } } -func finalizeResources(resources []reporthandlingv2.Resource, allResources map[string]workloadinterface.IMetadata) { +func finalizeResources(resources []reporthandling.Resource, allResources map[string]workloadinterface.IMetadata) { index := 0 for resourceID := range allResources { - resources[index] = reporthandlingv2.Resource{ + resources[index] = reporthandling.Resource{ ResourceID: resourceID, Object: allResources[resourceID], } diff --git a/resultshandling/results.go b/resultshandling/results.go index 5dea6e4f..82628862 100644 --- a/resultshandling/results.go +++ b/resultshandling/results.go @@ -35,11 +35,13 @@ func (resultsHandler *ResultsHandler) HandleResults(scanInfo *cautils.ScanInfo) // TODO - get score from table var score float32 = 0 - for i := range opaSessionObj.PostureReport.FrameworkReports { - score += opaSessionObj.PostureReport.FrameworkReports[i].Score + if opaSessionObj.PostureReport != nil { + for i := range opaSessionObj.PostureReport.FrameworkReports { + score += opaSessionObj.PostureReport.FrameworkReports[i].Score + } + score /= float32(len(opaSessionObj.PostureReport.FrameworkReports)) + resultsHandler.printerObj.Score(score) } - score /= float32(len(opaSessionObj.PostureReport.FrameworkReports)) - resultsHandler.printerObj.Score(score) return score } From 95b579d1913c62bd615a03e4c15a6a5fa1028769 Mon Sep 17 00:00:00 2001 From: dwertent Date: Wed, 12 Jan 2022 14:03:12 +0200 Subject: [PATCH 07/34] fixed resource list --- README.md | 2 +- cautils/rbac.go | 44 ++++++++++++------- cautils/scaninfo.go | 7 +-- clihandler/cmd/scan.go | 4 +- clihandler/cmd/submit.go | 2 +- clihandler/initcli.go | 2 +- clihandler/initcliutils.go | 1 + go.mod | 4 +- go.sum | 8 ++-- resultshandling/printer/v2/utils.go | 8 ++-- .../reporter/v1/reporteventreceiver.go | 2 +- .../reporter/v2/reporteventreceiver.go | 2 +- resultshandling/reporter/v2/utils.go | 8 ++-- 13 files changed, 56 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index a9d30e55..14c1914e 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ Set-ExecutionPolicy RemoteSigned -scope CurrentUser | `--submit` | `false` | If set, Kubescape will 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 sent | `true`/`false` | | `--keep-local` | `false` | Kubescape will not send scan results to Armo management portal. Use this flag if you ran with the `--submit` flag in the past and you do not want to submit your current scan results | `true`/`false` | | `--account` | | Armo portal account ID. Default will load account ID from configMap or config file | | -| `--cluster` | current-context | Cluster context to scan | | +| `--kube-context` | current-context | Cluster context to scan | | | `--verbose` | `false` | Display all of the input resources and not only failed resources | `true`/`false` | diff --git a/cautils/rbac.go b/cautils/rbac.go index 64854016..9b804702 100644 --- a/cautils/rbac.go +++ b/cautils/rbac.go @@ -42,19 +42,31 @@ func (rbacObjects *RBACObjects) ListAllResources() (map[string]workloadinterface func (rbacObjects *RBACObjects) rbacObjectsToResources(resources *rbacutils.RbacObjects) (map[string]workloadinterface.IMetadata, error) { allresources := map[string]workloadinterface.IMetadata{} - // wrap rbac aggregated objects in IMetadata and add to allresources - // TODO - DEPRECATE SA2WLIDmap - SA2WLIDmapIMeta, err := rbacutils.SA2WLIDmapIMetadataWrapper(resources.SA2WLIDmap) - if err != nil { - return nil, err - } - allresources[SA2WLIDmapIMeta.GetID()] = SA2WLIDmapIMeta - SAID2WLIDmapIMeta, err := rbacutils.SAID2WLIDmapIMetadataWrapper(resources.SAID2WLIDmap) - if err != nil { - return nil, err - } - allresources[SAID2WLIDmapIMeta.GetID()] = SAID2WLIDmapIMeta + /* + ************************************************************************************************************************ + This code is adding a non valid ID -> + (github.com/armosec/rbac-utils v0.0.11): "//SA2WLIDmap/SA2WLIDmap" + (github.com/armosec/rbac-utils v0.0.12): "armo.rbac.com/v0beta1//SAID2WLIDmap/SAID2WLIDmap" + + Should be investigated + ************************************************************************************************************************ + + // wrap rbac aggregated objects in IMetadata and add to allresources + // TODO - DEPRECATE SA2WLIDmap + SA2WLIDmapIMeta, err := rbacutils.SA2WLIDmapIMetadataWrapper(resources.SA2WLIDmap) + if err != nil { + return nil, err + } + allresources[SA2WLIDmapIMeta.GetID()] = SA2WLIDmapIMeta + + SAID2WLIDmapIMeta, err := rbacutils.SAID2WLIDmapIMetadataWrapper(resources.SAID2WLIDmap) + if err != nil { + return nil, err + } + allresources[SAID2WLIDmapIMeta.GetID()] = SAID2WLIDmapIMeta + + */ // convert rbac k8s resources to IMetadata and add to allresources for _, cr := range resources.ClusterRoles.Items { @@ -62,7 +74,7 @@ func (rbacObjects *RBACObjects) rbacObjectsToResources(resources *rbacutils.Rbac if err != nil { return nil, err } - crmap["apiVersion"] = "rbac.authorization.k8s.io/v1" + crmap["apiVersion"] = "rbac.authorization.k8s.io/v1" // TODO - is the the correct apiVersion? crIMeta := workloadinterface.NewWorkloadObj(crmap) crIMeta.SetKind("ClusterRole") allresources[crIMeta.GetID()] = crIMeta @@ -72,7 +84,7 @@ func (rbacObjects *RBACObjects) rbacObjectsToResources(resources *rbacutils.Rbac if err != nil { return nil, err } - crmap["apiVersion"] = "rbac.authorization.k8s.io/v1" + crmap["apiVersion"] = "rbac.authorization.k8s.io/v1" // TODO - is the the correct apiVersion? crIMeta := workloadinterface.NewWorkloadObj(crmap) crIMeta.SetKind("Role") allresources[crIMeta.GetID()] = crIMeta @@ -82,7 +94,7 @@ func (rbacObjects *RBACObjects) rbacObjectsToResources(resources *rbacutils.Rbac if err != nil { return nil, err } - crmap["apiVersion"] = "rbac.authorization.k8s.io/v1" + crmap["apiVersion"] = "rbac.authorization.k8s.io/v1" // TODO - is the the correct apiVersion? crIMeta := workloadinterface.NewWorkloadObj(crmap) crIMeta.SetKind("ClusterRoleBinding") allresources[crIMeta.GetID()] = crIMeta @@ -92,7 +104,7 @@ func (rbacObjects *RBACObjects) rbacObjectsToResources(resources *rbacutils.Rbac if err != nil { return nil, err } - crmap["apiVersion"] = "rbac.authorization.k8s.io/v1" + crmap["apiVersion"] = "rbac.authorization.k8s.io/v1" // TODO - is the the correct apiVersion? crIMeta := workloadinterface.NewWorkloadObj(crmap) crIMeta.SetKind("RoleBinding") allresources[crIMeta.GetID()] = crIMeta diff --git a/cautils/scaninfo.go b/cautils/scaninfo.go index 3175f1d9..a9893cb5 100644 --- a/cautils/scaninfo.go +++ b/cautils/scaninfo.go @@ -64,9 +64,10 @@ type ScanInfo struct { HostSensor BoolPtrFlag // Deploy ARMO K8s host sensor to collect data from certain controls Local bool // Do not submit results Account string // account ID - ClusterName string // cluster name - FrameworkScan bool // false if scanning control - ScanAll bool // true if scan all frameworks + // ClusterName string // cluster name + KubeContext string // context name + FrameworkScan bool // false if scanning control + ScanAll bool // true if scan all frameworks } type Getters struct { diff --git a/clihandler/cmd/scan.go b/clihandler/cmd/scan.go index 00440d62..3674f216 100644 --- a/clihandler/cmd/scan.go +++ b/clihandler/cmd/scan.go @@ -35,14 +35,14 @@ var scanCmd = &cobra.Command{ } func frameworkInitConfig() { - k8sinterface.SetClusterContextName(scanInfo.ClusterName) + k8sinterface.SetClusterContextName(scanInfo.KubeContext) } func init() { cobra.OnInitialize(frameworkInitConfig) rootCmd.AddCommand(scanCmd) - rootCmd.PersistentFlags().StringVarP(&scanInfo.ClusterName, "cluster", "", "", "Cluster name. Default will use the current-context") + rootCmd.PersistentFlags().StringVarP(&scanInfo.KubeContext, "--kube-context", "", "", "Kube context. Default will use the current-context") 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") diff --git a/clihandler/cmd/submit.go b/clihandler/cmd/submit.go index 8e412064..1f27b4eb 100644 --- a/clihandler/cmd/submit.go +++ b/clihandler/cmd/submit.go @@ -20,7 +20,7 @@ func init() { } func getSubmittedClusterConfig(k8s *k8sinterface.KubernetesApi) (*cautils.ClusterConfig, error) { - clusterConfig := cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account, scanInfo.ClusterName) // TODO - support none cluster env submit + clusterConfig := cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account, scanInfo.KubeContext) // TODO - support none cluster env submit if clusterConfig.GetCustomerGUID() != "" { if err := clusterConfig.SetTenant(); err != nil { return clusterConfig, err diff --git a/clihandler/initcli.go b/clihandler/initcli.go index 58fcc1b9..429cb854 100644 --- a/clihandler/initcli.go +++ b/clihandler/initcli.go @@ -44,7 +44,7 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces { } } - tenantConfig := getTenantConfig(scanInfo.Account, scanInfo.ClusterName, k8s) + tenantConfig := getTenantConfig(scanInfo.Account, scanInfo.KubeContext, k8s) // Set submit behavior AFTER loading tenant config setSubmitBehavior(scanInfo, tenantConfig) diff --git a/clihandler/initcliutils.go b/clihandler/initcliutils.go index 0315ef25..071e6612 100644 --- a/clihandler/initcliutils.go +++ b/clihandler/initcliutils.go @@ -11,6 +11,7 @@ import ( "github.com/armosec/kubescape/resourcehandler" "github.com/armosec/kubescape/resultshandling/reporter" reporterv1 "github.com/armosec/kubescape/resultshandling/reporter/v1" + reporterv2 "github.com/armosec/kubescape/resultshandling/reporter/v2" "github.com/armosec/opa-utils/reporthandling" "github.com/armosec/rbac-utils/rbacscanner" diff --git a/go.mod b/go.mod index f508bc22..8ec424ee 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,8 @@ go 1.17 require ( github.com/armosec/armoapi-go v0.0.41 github.com/armosec/k8s-interface v0.0.54 - github.com/armosec/opa-utils v0.0.95 - github.com/armosec/rbac-utils v0.0.11 + github.com/armosec/opa-utils v0.0.97 + github.com/armosec/rbac-utils v0.0.12 github.com/armosec/utils-go v0.0.3 github.com/briandowns/spinner v1.18.0 github.com/enescakir/emoji v1.0.0 diff --git a/go.sum b/go.sum index 4b9b0bd4..619faa85 100644 --- a/go.sum +++ b/go.sum @@ -95,11 +95,11 @@ github.com/armosec/k8s-interface v0.0.50/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2 github.com/armosec/k8s-interface v0.0.54 h1:1sQeoEZA5bgpXVibXhEiTSeLd3GKY5NkTOeewdgR0Bs= github.com/armosec/k8s-interface v0.0.54/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.95 h1:tGFJQChy8Yn+8HhLbQHUfa6HmbrtBFBOlIgkO/9IFY8= -github.com/armosec/opa-utils v0.0.95/go.mod h1:BNTjeianyXlflJMz3bZM0GimBWqmzirUf1whWR6Os04= +github.com/armosec/opa-utils v0.0.97 h1:KPjRZdsAC9EObo17QxiW+s5KWmF6vNFu+VQSOgFv5uk= +github.com/armosec/opa-utils v0.0.97/go.mod h1:BNTjeianyXlflJMz3bZM0GimBWqmzirUf1whWR6Os04= github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40= -github.com/armosec/rbac-utils v0.0.11 h1:SCiVLqUeV+WGpUsWbOBt6jKkFAd62jztuzB6PIgHz7w= -github.com/armosec/rbac-utils v0.0.11/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0= +github.com/armosec/rbac-utils v0.0.12 h1:uJpMGDyLAX129PrKHp6NPNB6lVRhE0OZIwV6ywzSDrs= +github.com/armosec/rbac-utils v0.0.12/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= diff --git a/resultshandling/printer/v2/utils.go b/resultshandling/printer/v2/utils.go index 86f91e89..c432bcac 100644 --- a/resultshandling/printer/v2/utils.go +++ b/resultshandling/printer/v2/utils.go @@ -33,10 +33,12 @@ func finalizeResults(results []resourcesresults.Result, resourcesResult map[stri func finalizeResources(resources []reporthandling.Resource, allResources map[string]workloadinterface.IMetadata) { index := 0 for resourceID := range allResources { - resources[index] = reporthandling.Resource{ - ResourceID: resourceID, - Object: allResources[resourceID], + if obj, ok := allResources[resourceID]; ok { + r := *reporthandling.NewResource(obj.GetObject()) + r.ResourceID = resourceID + resources[index] = r } + index++ } } diff --git a/resultshandling/reporter/v1/reporteventreceiver.go b/resultshandling/reporter/v1/reporteventreceiver.go index 3d7bf16b..ce197254 100644 --- a/resultshandling/reporter/v1/reporteventreceiver.go +++ b/resultshandling/reporter/v1/reporteventreceiver.go @@ -45,7 +45,7 @@ func (report *ReportEventReceiver) ActionSendReport(opaSessionObj *cautils.OPASe return nil } if report.clusterName == "" { - report.message = "WARNING: Failed to publish results. Reason: Unknown cluster name. Run kubescape with the '--cluster ' flag" + report.message = "WARNING: Failed to publish results. Reason: Unknown cluster name. Run kubescape with the '--kube-context ' flag" return nil } diff --git a/resultshandling/reporter/v2/reporteventreceiver.go b/resultshandling/reporter/v2/reporteventreceiver.go index 98966255..6b1c57ee 100644 --- a/resultshandling/reporter/v2/reporteventreceiver.go +++ b/resultshandling/reporter/v2/reporteventreceiver.go @@ -46,7 +46,7 @@ func (report *ReportEventReceiver) ActionSendReport(opaSessionObj *cautils.OPASe return nil } if report.clusterName == "" { - report.message = "WARNING: Failed to publish results. Reason: Unknown cluster name. Run kubescape with the '--cluster ' flag" + report.message = "WARNING: Failed to publish results. Reason: Unknown cluster name. Run kubescape with the '--kube-context ' flag" return nil } opaSessionObj.Report.ReportID = uuid.NewV4().String() diff --git a/resultshandling/reporter/v2/utils.go b/resultshandling/reporter/v2/utils.go index e4e32217..e50713d9 100644 --- a/resultshandling/reporter/v2/utils.go +++ b/resultshandling/reporter/v2/utils.go @@ -36,10 +36,12 @@ func finalizeResults(results []resourcesresults.Result, resourcesResult map[stri func finalizeResources(resources []reporthandling.Resource, allResources map[string]workloadinterface.IMetadata) { index := 0 for resourceID := range allResources { - resources[index] = reporthandling.Resource{ - ResourceID: resourceID, - Object: allResources[resourceID], + if obj, ok := allResources[resourceID]; ok { + r := *reporthandling.NewResource(obj.GetObject()) + r.ResourceID = resourceID + resources[index] = r } + index++ } } From 839c3e261fae761d153934b47e9b738bcb2ee00b Mon Sep 17 00:00:00 2001 From: dwertent Date: Thu, 13 Jan 2022 11:04:09 +0200 Subject: [PATCH 08/34] swap result and resource --- clihandler/initcliutils.go | 2 +- .../container-image-vulnerability-adaptor.md | 63 ++++++++++++++++++- .../reporter/v2/reporteventreceiver.go | 28 +++------ 3 files changed, 70 insertions(+), 23 deletions(-) diff --git a/clihandler/initcliutils.go b/clihandler/initcliutils.go index 071e6612..fd60a02b 100644 --- a/clihandler/initcliutils.go +++ b/clihandler/initcliutils.go @@ -11,8 +11,8 @@ import ( "github.com/armosec/kubescape/resourcehandler" "github.com/armosec/kubescape/resultshandling/reporter" reporterv1 "github.com/armosec/kubescape/resultshandling/reporter/v1" - reporterv2 "github.com/armosec/kubescape/resultshandling/reporter/v2" + "github.com/armosec/opa-utils/reporthandling" "github.com/armosec/rbac-utils/rbacscanner" ) diff --git a/docs/proposals/container-image-vulnerability-adaptor.md b/docs/proposals/container-image-vulnerability-adaptor.md index f3dc4b28..3cd1141e 100644 --- a/docs/proposals/container-image-vulnerability-adaptor.md +++ b/docs/proposals/container-image-vulnerability-adaptor.md @@ -1,4 +1,4 @@ -# Container image vulnerabilty adaptor interface proposal +# Container image vulnerability adaptor interface proposal ## Rationale @@ -6,7 +6,7 @@ source #287 ### Big picture -* Kubescape team planning to create controls which take into account image vulnerabilities, example: looking for public internet facing workloads with critical vulnerabilities. These are seriously effecting the security health of a cluster and therefore we think it is important to cover it. We think that most container registries are/will support image scanning like Harbor and therefore the ability to get information from them is important. +* Kubescape team is planning to create controls which take into account image vulnerabilities, example: looking for public internet facing workloads with critical vulnerabilities. These are seriously effecting the security health of a cluster and therefore we think it is important to cover it. We think that most container registries are/will support image scanning like Harbor and therefore the ability to get information from them is important. * There are information in the image repository which is important for existing controls as well. They are incomplete without it, example see this issue: Non-root containers check is broken #19 . These are not necessarily image vulnerability related. Can be information in the image manifest (like the issue before), but it can be the image BOM related. ### Relation to this proposal @@ -114,4 +114,63 @@ type IContainerImageVulnerabilityAdaptor interface { GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error) } +``` + + + +# Integration + +# Input + +The objects received from the interface will be converted to an Imetadata compatible objects as following + +``` +{ + "apiVersion": "image.vulnscan.com/v1", + "kind": "VulnScan", + "metadata": { + "name": "nginx:latest" + }, + "data": { + // returned by the adaptor API (structure like our backend gives for an image + } +} +``` + + +# Output + +The rego results will be a combination of the k8s artifact and the list of relevant CVEs for the control + +``` +{ + "apiVersion": "result.vulnscan.com/v1", + "kind": "Pod", + "metadata": { + "name": "nginx" + }, + "relatedObjects": [ + { + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "name": "nginx" + }, + "spec": { + // podSpec + }, + }, + { + "apiVersion": "container.vulnscan.com/v1", + "kind": "VulnScan", + "metadata": { + "name": "nginx:latest", + }, + "data": { + + // returned by the adaptor API (structure like our backend gives for an image + } + } + ] +} ``` \ No newline at end of file diff --git a/resultshandling/reporter/v2/reporteventreceiver.go b/resultshandling/reporter/v2/reporteventreceiver.go index 6b1c57ee..01927537 100644 --- a/resultshandling/reporter/v2/reporteventreceiver.go +++ b/resultshandling/reporter/v2/reporteventreceiver.go @@ -78,27 +78,21 @@ func (report *ReportEventReceiver) prepareReport(postureReport *reporthandlingv2 reportCounter := 0 - // send results - if err := report.sendResults(host, postureReport, &reportCounter); err != nil { + // send resources + if err := report.sendResources(host, postureReport, &reportCounter, false); err != nil { return err } reportCounter++ - // send resources - if err := report.sendResources(host, postureReport, &reportCounter); err != nil { + // send results + if err := report.sendResults(host, postureReport, &reportCounter, true); err != nil { return err } - // reportCounter++ - - // // send framework results - // if err := report.sendSummary(host, postureReport, &reportCounter); err != nil { - // return err - // } return nil } -func (report *ReportEventReceiver) sendResources(host string, postureReport *reporthandlingv2.PostureReport, reportCounter *int) error { +func (report *ReportEventReceiver) sendResources(host string, postureReport *reporthandlingv2.PostureReport, reportCounter *int, isLastReport bool) error { splittedPostureReport := setSubReport(postureReport) counter := 0 @@ -127,10 +121,10 @@ func (report *ReportEventReceiver) sendResources(host string, postureReport *rep splittedPostureReport.Resources = append(splittedPostureReport.Resources, v) } - return report.sendReport(host, splittedPostureReport, *reportCounter, true) + return report.sendReport(host, splittedPostureReport, *reportCounter, isLastReport) } -func (report *ReportEventReceiver) sendResults(host string, postureReport *reporthandlingv2.PostureReport, reportCounter *int) error { +func (report *ReportEventReceiver) sendResults(host string, postureReport *reporthandlingv2.PostureReport, reportCounter *int, isLastReport bool) error { splittedPostureReport := setSubReport(postureReport) counter := 0 @@ -159,15 +153,9 @@ func (report *ReportEventReceiver) sendResults(host string, postureReport *repor splittedPostureReport.Results = append(splittedPostureReport.Results, v) } - return report.sendReport(host, splittedPostureReport, *reportCounter, false) + return report.sendReport(host, splittedPostureReport, *reportCounter, isLastReport) } -func (report *ReportEventReceiver) sendSummary(host string, postureReport *reporthandlingv2.PostureReport, reportCounter *int) error { - splittedPostureReport := setSubReport(postureReport) - splittedPostureReport.SummaryDetails = postureReport.SummaryDetails - - return report.sendReport(host, splittedPostureReport, *reportCounter, true) -} func (report *ReportEventReceiver) sendReport(host string, postureReport *reporthandlingv2.PostureReport, counter int, isLastReport bool) error { postureReport.PaginationInfo = reporthandlingv2.PaginationMarks{ ReportNumber: counter, From e100f18bb018ba9fdee5f430a01cbeb1bbc51248 Mon Sep 17 00:00:00 2001 From: dwertent Date: Thu, 13 Jan 2022 11:44:46 +0200 Subject: [PATCH 09/34] fixed tests --- resultshandling/reporter/v2/reporteventreceiverutils_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resultshandling/reporter/v2/reporteventreceiverutils_test.go b/resultshandling/reporter/v2/reporteventreceiverutils_test.go index f788a0f1..efd1f512 100644 --- a/resultshandling/reporter/v2/reporteventreceiverutils_test.go +++ b/resultshandling/reporter/v2/reporteventreceiverutils_test.go @@ -9,10 +9,10 @@ func TestHostToString(t *testing.T) { host := url.URL{ Scheme: "https", Host: "report.eudev3.cyberarmorsoft.com", - Path: "k8srestapi/v1/postureReport", + Path: "k8srestapi/v2/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" + expectedHost := "https://report.eudev3.cyberarmorsoft.com/k8srestapi/v2/postureReport?cluster=openrasty_seal-7fvz&customerGUID=5d817063-096f-4d91-b39b-8665240080af&reportGUID=ffdd2a00-4dc8-4bf3-b97a-a6d4fd198a41" receivedHost := hostToString(&host, "ffdd2a00-4dc8-4bf3-b97a-a6d4fd198a41") if receivedHost != expectedHost { t.Errorf("%s != %s", receivedHost, expectedHost) From 473746eab0542c562339ea927a743561dd16f62c Mon Sep 17 00:00:00 2001 From: dwertent Date: Thu, 13 Jan 2022 12:50:50 +0200 Subject: [PATCH 10/34] merged with Daniels branch --- cautils/scaninfo.go | 15 ++- go.mod | 2 +- go.sum | 4 +- resourcehandler/cloudproviderhandler.go | 107 ++++++++++++++++++++ resourcehandler/ekssupport.go | 95 ++++++++++++++++++ resourcehandler/gkesupport.go | 125 ++++++++++++++++++++++++ resourcehandler/k8sresources.go | 18 +++- 7 files changed, 355 insertions(+), 11 deletions(-) create mode 100644 resourcehandler/cloudproviderhandler.go create mode 100644 resourcehandler/ekssupport.go create mode 100644 resourcehandler/gkesupport.go diff --git a/cautils/scaninfo.go b/cautils/scaninfo.go index a9893cb5..7e5f4bbe 100644 --- a/cautils/scaninfo.go +++ b/cautils/scaninfo.go @@ -64,10 +64,9 @@ type ScanInfo struct { HostSensor BoolPtrFlag // Deploy ARMO K8s host sensor to collect data from certain controls Local bool // Do not submit results Account string // account ID - // ClusterName string // cluster name - KubeContext string // context name - FrameworkScan bool // false if scanning control - ScanAll bool // true if scan all frameworks + KubeContext string // context name + FrameworkScan bool // false if scanning control + ScanAll bool // true if scan all frameworks } type Getters struct { @@ -79,7 +78,15 @@ type Getters struct { func (scanInfo *ScanInfo) Init() { scanInfo.setUseFrom() scanInfo.setOutputFile() +} +func (scanInfo *ScanInfo) setUseExceptions() { + if scanInfo.UseExceptions != "" { + // load exceptions from file + scanInfo.ExceptionsGetter = getter.NewLoadPolicy([]string{scanInfo.UseExceptions}) + } else { + scanInfo.ExceptionsGetter = getter.GetArmoAPIConnector() + } } func (scanInfo *ScanInfo) setUseFrom() { diff --git a/go.mod b/go.mod index 8ec424ee..21260544 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.17 require ( github.com/armosec/armoapi-go v0.0.41 - github.com/armosec/k8s-interface v0.0.54 + github.com/armosec/k8s-interface v0.0.56 github.com/armosec/opa-utils v0.0.97 github.com/armosec/rbac-utils v0.0.12 github.com/armosec/utils-go v0.0.3 diff --git a/go.sum b/go.sum index 619faa85..05b941f4 100644 --- a/go.sum +++ b/go.sum @@ -92,8 +92,8 @@ github.com/armosec/armoapi-go v0.0.41/go.mod h1:exk1O3rK6V+X8SSyxc06lwb0j9ILQuKA github.com/armosec/k8s-interface v0.0.8/go.mod h1:xxS+V5QT3gVQTwZyAMMDrYLWGrfKOpiJ7Jfhfa0w9sM= github.com/armosec/k8s-interface v0.0.37/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU= github.com/armosec/k8s-interface v0.0.50/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU= -github.com/armosec/k8s-interface v0.0.54 h1:1sQeoEZA5bgpXVibXhEiTSeLd3GKY5NkTOeewdgR0Bs= -github.com/armosec/k8s-interface v0.0.54/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU= +github.com/armosec/k8s-interface v0.0.56 h1:7dOgc3qZaI7ReLRZcJa2JZKk0rliyYi05l1vuHc6gcE= +github.com/armosec/k8s-interface v0.0.56/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.97 h1:KPjRZdsAC9EObo17QxiW+s5KWmF6vNFu+VQSOgFv5uk= github.com/armosec/opa-utils v0.0.97/go.mod h1:BNTjeianyXlflJMz3bZM0GimBWqmzirUf1whWR6Os04= diff --git a/resourcehandler/cloudproviderhandler.go b/resourcehandler/cloudproviderhandler.go new file mode 100644 index 00000000..9fe545bc --- /dev/null +++ b/resourcehandler/cloudproviderhandler.go @@ -0,0 +1,107 @@ +package resourcehandler + +import ( + "os" + + "github.com/armosec/k8s-interface/cloudsupport" + "github.com/armosec/k8s-interface/k8sinterface" +) + +var ( + KS_KUBE_CLUSTER_ENV_VAR = "KS_KUBE_CLUSTER" + KS_CLOUD_PROVIDER_ENV_VAR = "KS_CLOUD_PROVIDER" + KS_CLOUD_REGION_ENV_VAR = "KS_CLOUD_REGION" + KS_GKE_PROJECT_ENV_VAR = "KS_GKE_PROJECT" +) + +type ICloudProvider interface { + getKubeCluster() string + getRegion(cluster string, provider string) (string, error) + getProject(cluster string, provider string) (string, error) + getKubeClusterName() string +} + +func initCloudProvider() ICloudProvider { + var provider string + if isEnvVars() { + provider = getCloudProviderFromEnvVar() + switch provider { + case "gke": + return NewGKEProviderEnvVar() + case "eks": + return NewEKSProviderEnvVar() + } + } else { + provider = getCloudProviderFromContext() + switch provider { + case "gke": + return NewGKEProviderContext() + case "eks": + return NewEKSProviderContext() + } + } + return NewEmptyCloudProvider() +} + +func getCloudProvider() string { + if isEnvVars() { + return getCloudProviderFromEnvVar() + } + return getCloudProviderFromContext() +} + +func getCloudProviderFromContext() string { + return cloudsupport.GetCloudProvider(getClusterFromContext()) +} + +func getClusterFromContext() string { + cluster := k8sinterface.GetCurrentContext().Cluster + if cluster != "" { + return cluster + } + return k8sinterface.GetClusterName() +} + +func getCloudProviderFromEnvVar() string { + val, present := os.LookupEnv(KS_CLOUD_PROVIDER_ENV_VAR) + if present { + return val + } + return "" +} + +func isEnvVars() bool { + _, present := os.LookupEnv(KS_KUBE_CLUSTER_ENV_VAR) + if !present { + return false + } + _, present = os.LookupEnv(KS_CLOUD_PROVIDER_ENV_VAR) + if !present { + return false + } + _, present = os.LookupEnv(KS_CLOUD_REGION_ENV_VAR) + return present +} + +type EmptyCloudProvider struct { +} + +func NewEmptyCloudProvider() *EmptyCloudProvider { + return &EmptyCloudProvider{} +} + +func (emptyCloudProvider *EmptyCloudProvider) getKubeCluster() string { + return getClusterFromContext() +} + +func (emptyCloudProvider *EmptyCloudProvider) getKubeClusterName() string { + return emptyCloudProvider.getKubeCluster() +} + +func (emptyCloudProvider *EmptyCloudProvider) getRegion(cluster string, provider string) (string, error) { + return "", nil +} + +func (emptyCloudProvider *EmptyCloudProvider) getProject(cluster string, provider string) (string, error) { + return "", nil +} diff --git a/resourcehandler/ekssupport.go b/resourcehandler/ekssupport.go new file mode 100644 index 00000000..91f4b1a8 --- /dev/null +++ b/resourcehandler/ekssupport.go @@ -0,0 +1,95 @@ +package resourcehandler + +import ( + "fmt" + "os" + "strings" + + "github.com/armosec/k8s-interface/k8sinterface" +) + +type EKSProviderEnvVar struct { +} + +func NewEKSProviderEnvVar() *EKSProviderEnvVar { + return &EKSProviderEnvVar{} +} + +func (eksProviderEnvVar *EKSProviderEnvVar) getKubeClusterName() string { + return eksProviderEnvVar.getKubeCluster() +} + +func (eksProviderEnvVar *EKSProviderEnvVar) getKubeCluster() string { + val, present := os.LookupEnv(KS_KUBE_CLUSTER_ENV_VAR) + if present { + return val + } + return "" +} + +func (eksProviderEnvVar *EKSProviderEnvVar) getRegion(cluster string, provider string) (string, error) { + return eksProviderEnvVar.getRegionForEKS(cluster) +} + +func (eksProviderEnvVar *EKSProviderEnvVar) getProject(cluster string, provider string) (string, error) { + return "", nil +} + +func (eksProviderEnvVar *EKSProviderEnvVar) getRegionForEKS(cluster string) (string, error) { + region, present := os.LookupEnv(KS_CLOUD_REGION_ENV_VAR) + if present { + return region, nil + } + splittedClusterContext := strings.Split(cluster, ".") + if len(splittedClusterContext) < 2 { + return "", fmt.Errorf("error: failed to get region") + } + region = splittedClusterContext[1] + return region, nil +} + +// ------------------------------------- EKSProviderContext ------------------------- + +type EKSProviderContext struct { +} + +func NewEKSProviderContext() *EKSProviderContext { + return &EKSProviderContext{} +} + +func (eksProviderContext *EKSProviderContext) getKubeClusterName() string { + cluster := k8sinterface.GetCurrentContext().Cluster + var splittedCluster []string + if cluster != "" { + splittedCluster = strings.Split(cluster, ".") + if len(splittedCluster) > 1 { + return splittedCluster[0] + } + } + splittedCluster = strings.Split(k8sinterface.GetClusterName(), ".") + if len(splittedCluster) > 1 { + return splittedCluster[0] + } + return "" +} + +func (eksProviderContext *EKSProviderContext) getKubeCluster() string { + cluster := k8sinterface.GetCurrentContext().Cluster + if cluster != "" { + return cluster + } + return k8sinterface.GetClusterName() +} + +func (eksProviderContext *EKSProviderContext) getRegion(cluster string, provider string) (string, error) { + splittedClusterContext := strings.Split(cluster, ".") + if len(splittedClusterContext) < 2 { + return "", fmt.Errorf("error: failed to get region") + } + region := splittedClusterContext[1] + return region, nil +} + +func (eksProviderContext *EKSProviderContext) getProject(cluster string, provider string) (string, error) { + return "", nil +} diff --git a/resourcehandler/gkesupport.go b/resourcehandler/gkesupport.go new file mode 100644 index 00000000..e6f2eeef --- /dev/null +++ b/resourcehandler/gkesupport.go @@ -0,0 +1,125 @@ +package resourcehandler + +import ( + "fmt" + "os" + "strings" + + "github.com/armosec/k8s-interface/k8sinterface" +) + +type GKEProviderEnvVar struct { +} + +func NewGKEProviderEnvVar() *GKEProviderEnvVar { + return &GKEProviderEnvVar{} +} +func (gkeProvider *GKEProviderEnvVar) getKubeClusterName() string { + return gkeProvider.getKubeCluster() +} + +func (gkeProvider *GKEProviderEnvVar) getKubeCluster() string { + val, present := os.LookupEnv(KS_KUBE_CLUSTER_ENV_VAR) + if present { + return val + } + return "" +} + +func (gkeProvider *GKEProviderEnvVar) getRegion(cluster string, provider string) (string, error) { + return gkeProvider.getRegionForGKE(cluster) +} + +func (gkeProvider *GKEProviderEnvVar) getProject(cluster string, provider string) (string, error) { + return gkeProvider.getProjectForGKE(cluster) +} + +func (gkeProvider *GKEProviderEnvVar) getProjectForGKE(cluster string) (string, error) { + project, present := os.LookupEnv(KS_GKE_PROJECT_ENV_VAR) + if present { + return project, nil + } + parsedName := strings.Split(cluster, "_") + if len(parsedName) < 3 { + return "", fmt.Errorf("error: failed to parse cluster name") + } + project = parsedName[1] + return project, nil +} + +func (gkeProvider *GKEProviderEnvVar) getRegionForGKE(cluster string) (string, error) { + region, present := os.LookupEnv(KS_CLOUD_REGION_ENV_VAR) + if present { + return region, nil + } + parsedName := strings.Split(cluster, "_") + if len(parsedName) < 3 { + return "", fmt.Errorf("error: failed to parse cluster name") + } + region = parsedName[2] + return region, nil + +} + +// ------------------------------ GKEProviderContext -------------------------------------------------------- + +type GKEProviderContext struct { +} + +func NewGKEProviderContext() *GKEProviderContext { + return &GKEProviderContext{} +} + +func (gkeProviderContext *GKEProviderContext) getKubeClusterName() string { + cluster := k8sinterface.GetCurrentContext().Cluster + parsedName := strings.Split(cluster, "_") + if len(parsedName) < 3 { + return "" + } + clusterName := parsedName[3] + if clusterName != "" { + return clusterName + } + cluster = k8sinterface.GetClusterName() + parsedName = strings.Split(cluster, "_") + if len(parsedName) < 3 { + return "" + } + clusterName = parsedName[3] + return clusterName +} + +func (gkeProviderContext *GKEProviderContext) getKubeCluster() string { + cluster := k8sinterface.GetCurrentContext().Cluster + if cluster != "" { + return cluster + } + return k8sinterface.GetClusterName() + +} + +func (gkeProviderContext *GKEProviderContext) getRegion(cluster string, provider string) (string, error) { + return gkeProviderContext.getRegionForGKE(cluster) +} + +func (gkeProviderContext *GKEProviderContext) getProject(cluster string, provider string) (string, error) { + return gkeProviderContext.getProjectForGKE(cluster) +} + +func (gkeProviderContext *GKEProviderContext) getProjectForGKE(cluster string) (string, error) { + parsedName := strings.Split(cluster, "_") + if len(parsedName) < 3 { + return "", fmt.Errorf("error: failed to parse cluster name") + } + project := parsedName[1] + return project, nil +} + +func (gkeProviderContext *GKEProviderContext) getRegionForGKE(cluster string) (string, error) { + parsedName := strings.Split(cluster, "_") + if len(parsedName) < 3 { + return "", fmt.Errorf("error: failed to parse cluster name") + } + region := parsedName[2] + return region, nil +} diff --git a/resourcehandler/k8sresources.go b/resourcehandler/k8sresources.go index aa308f76..6a21f1fd 100644 --- a/resourcehandler/k8sresources.go +++ b/resourcehandler/k8sresources.go @@ -193,11 +193,21 @@ func (k8sHandler *K8sResourceHandler) collectRbacResources(allResources map[stri } func getCloudProviderDescription(allResources map[string]workloadinterface.IMetadata, k8sResourcesMap *cautils.K8SResources) error { - if cloudsupport.IsRunningInCloudProvider() { - wl, err := cloudsupport.GetDescriptiveInfoFromCloudProvider() + cloudProvider := initCloudProvider() + cluster := cloudProvider.getKubeCluster() + clusterName := cloudProvider.getKubeClusterName() + provider := getCloudProvider() + region, err := cloudProvider.getRegion(cluster, provider) + if err != nil { + return err + } + project, err := cloudProvider.getProject(cluster, provider) + if err != nil { + return err + } + if provider != "" { + wl, err := cloudsupport.GetDescriptiveInfoFromCloudProvider(clusterName, provider, region, project) 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": From d2884b893688e1f4eaa2612e308fbee0cb53ce8a Mon Sep 17 00:00:00 2001 From: dwertent Date: Thu, 13 Jan 2022 13:07:29 +0200 Subject: [PATCH 11/34] update failure message --- resultshandling/reporter/v1/reporteventreceiver.go | 2 +- resultshandling/reporter/v2/reporteventreceiver.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/resultshandling/reporter/v1/reporteventreceiver.go b/resultshandling/reporter/v1/reporteventreceiver.go index ce197254..fa774983 100644 --- a/resultshandling/reporter/v1/reporteventreceiver.go +++ b/resultshandling/reporter/v1/reporteventreceiver.go @@ -45,7 +45,7 @@ func (report *ReportEventReceiver) ActionSendReport(opaSessionObj *cautils.OPASe return nil } if report.clusterName == "" { - report.message = "WARNING: Failed to publish results. Reason: Unknown cluster name. Run kubescape with the '--kube-context ' flag" + 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" return nil } diff --git a/resultshandling/reporter/v2/reporteventreceiver.go b/resultshandling/reporter/v2/reporteventreceiver.go index 01927537..443e60e1 100644 --- a/resultshandling/reporter/v2/reporteventreceiver.go +++ b/resultshandling/reporter/v2/reporteventreceiver.go @@ -46,7 +46,7 @@ func (report *ReportEventReceiver) ActionSendReport(opaSessionObj *cautils.OPASe return nil } if report.clusterName == "" { - report.message = "WARNING: Failed to publish results. Reason: Unknown cluster name. Run kubescape with the '--kube-context ' flag" + 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" return nil } opaSessionObj.Report.ReportID = uuid.NewV4().String() From 0bde8a65bad54651ca7b32a11a0bb8ed27f40ec7 Mon Sep 17 00:00:00 2001 From: dwertent Date: Thu, 13 Jan 2022 14:16:36 +0200 Subject: [PATCH 12/34] fixed context flag --- clihandler/cmd/scan.go | 2 +- resourcehandler/cloudproviderhandler.go | 31 ++++++++++--------------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/clihandler/cmd/scan.go b/clihandler/cmd/scan.go index 3674f216..351c6cdc 100644 --- a/clihandler/cmd/scan.go +++ b/clihandler/cmd/scan.go @@ -42,7 +42,7 @@ func init() { cobra.OnInitialize(frameworkInitConfig) rootCmd.AddCommand(scanCmd) - rootCmd.PersistentFlags().StringVarP(&scanInfo.KubeContext, "--kube-context", "", "", "Kube context. Default will use the current-context") + rootCmd.PersistentFlags().StringVarP(&scanInfo.KubeContext, "kube-context", "", "", "Kube context. Default will use the current-context") 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") diff --git a/resourcehandler/cloudproviderhandler.go b/resourcehandler/cloudproviderhandler.go index 9fe545bc..e0ebb4c0 100644 --- a/resourcehandler/cloudproviderhandler.go +++ b/resourcehandler/cloudproviderhandler.go @@ -2,6 +2,7 @@ package resourcehandler import ( "os" + "strings" "github.com/armosec/k8s-interface/cloudsupport" "github.com/armosec/k8s-interface/k8sinterface" @@ -22,32 +23,24 @@ type ICloudProvider interface { } func initCloudProvider() ICloudProvider { - var provider string - if isEnvVars() { - provider = getCloudProviderFromEnvVar() - switch provider { - case "gke": - return NewGKEProviderEnvVar() - case "eks": - return NewEKSProviderEnvVar() - } - } else { - provider = getCloudProviderFromContext() - switch provider { - case "gke": - return NewGKEProviderContext() - case "eks": - return NewEKSProviderContext() - } + + switch getCloudProvider() { + case "gke", "gcp": + return NewGKEProviderContext() + case "eks", "aws": + return NewEKSProviderContext() } return NewEmptyCloudProvider() } func getCloudProvider() string { + var provider string if isEnvVars() { - return getCloudProviderFromEnvVar() + provider = getCloudProviderFromEnvVar() + } else { + provider = getCloudProviderFromContext() } - return getCloudProviderFromContext() + return strings.ToLower(provider) } func getCloudProviderFromContext() string { From 500df8737ecdfdedbd1d6c4718da3689f4af0855 Mon Sep 17 00:00:00 2001 From: dwertent Date: Thu, 13 Jan 2022 15:03:23 +0200 Subject: [PATCH 13/34] send small clusters once --- .../reporter/v2/reporteventreceiver.go | 53 ++++++++++++++----- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/resultshandling/reporter/v2/reporteventreceiver.go b/resultshandling/reporter/v2/reporteventreceiver.go index 01927537..acafe4a2 100644 --- a/resultshandling/reporter/v2/reporteventreceiver.go +++ b/resultshandling/reporter/v2/reporteventreceiver.go @@ -82,12 +82,12 @@ func (report *ReportEventReceiver) prepareReport(postureReport *reporthandlingv2 if err := report.sendResources(host, postureReport, &reportCounter, false); err != nil { return err } - reportCounter++ + // reportCounter++ - // send results - if err := report.sendResults(host, postureReport, &reportCounter, true); err != nil { - return err - } + // // send results + // if err := report.sendResults(host, postureReport, &reportCounter, true); err != nil { + // return err + // } return nil } @@ -112,6 +112,7 @@ func (report *ReportEventReceiver) sendResources(host string, postureReport *rep // delete resources splittedPostureReport.Resources = []reporthandling.Resource{} + splittedPostureReport.Results = []resourcesresults.Result{} // restart counter counter = 0 @@ -121,13 +122,6 @@ func (report *ReportEventReceiver) sendResources(host string, postureReport *rep splittedPostureReport.Resources = append(splittedPostureReport.Resources, v) } - return report.sendReport(host, splittedPostureReport, *reportCounter, isLastReport) -} - -func (report *ReportEventReceiver) sendResults(host string, postureReport *reporthandlingv2.PostureReport, reportCounter *int, isLastReport bool) error { - splittedPostureReport := setSubReport(postureReport) - counter := 0 - for _, v := range postureReport.Results { r, err := json.Marshal(v) if err != nil { @@ -144,6 +138,7 @@ func (report *ReportEventReceiver) sendResults(host string, postureReport *repor // delete results splittedPostureReport.Results = []resourcesresults.Result{} + splittedPostureReport.Resources = []reporthandling.Resource{} // restart counter counter = 0 @@ -153,9 +148,41 @@ func (report *ReportEventReceiver) sendResults(host string, postureReport *repor splittedPostureReport.Results = append(splittedPostureReport.Results, v) } - return report.sendReport(host, splittedPostureReport, *reportCounter, isLastReport) + return report.sendReport(host, splittedPostureReport, *reportCounter, true) } +// func (report *ReportEventReceiver) sendResults(host string, postureReport *reporthandlingv2.PostureReport, reportCounter *int, isLastReport bool) error { +// splittedPostureReport := setSubReport(postureReport) +// counter := 0 + +// for _, v := range postureReport.Results { +// r, err := json.Marshal(v) +// if err != nil { +// return fmt.Errorf("failed to unmarshal resource '%s', reason: %v", v.GetResourceID(), err) +// } + +// if counter+len(r) >= MAX_REPORT_SIZE && len(splittedPostureReport.Resources) > 0 { + +// // send report +// if err := report.sendReport(host, splittedPostureReport, *reportCounter, false); err != nil { +// return err +// } +// *reportCounter++ + +// // delete results +// splittedPostureReport.Results = []resourcesresults.Result{} + +// // restart counter +// counter = 0 +// } + +// counter += len(r) +// splittedPostureReport.Results = append(splittedPostureReport.Results, v) +// } + +// return report.sendReport(host, splittedPostureReport, *reportCounter, isLastReport) +// } + func (report *ReportEventReceiver) sendReport(host string, postureReport *reporthandlingv2.PostureReport, counter int, isLastReport bool) error { postureReport.PaginationInfo = reporthandlingv2.PaginationMarks{ ReportNumber: counter, From 0be8d57eaab560e5fc6659d12fac8a295962b78c Mon Sep 17 00:00:00 2001 From: rrefael Date: Thu, 13 Jan 2022 15:44:21 +0200 Subject: [PATCH 14/34] add --enable-host-scan flag --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index df1f5f56..54e84c16 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ curl -s https://raw.githubusercontent.com/armosec/kubescape/master/install.sh | ## Run: ``` -kubescape scan --submit +kubescape scan --submit --enable-host-scan ``` @@ -267,7 +267,7 @@ go build -o kubescape . 3. Run ``` -./kubescape scan framework nsa +./kubescape scan --submit --enable-host-scan ``` 4. Enjoy :zany_face: From cb3bdb9df2e7b7b6202036132192d9fcbe0f30dd Mon Sep 17 00:00:00 2001 From: Lior Alafi Date: Fri, 14 Jan 2022 17:54:39 +0200 Subject: [PATCH 15/34] supporting both structures for score calculation --- go.mod | 4 +-- go.sum | 4 +-- opaprocessor/processorhandler.go | 4 +++ resultshandling/results.go | 12 +-------- score/score.go | 43 ++++++++++++++++++++++++++++++++ score/score_test.go | 1 + 6 files changed, 53 insertions(+), 15 deletions(-) create mode 100644 score/score.go create mode 100644 score/score_test.go diff --git a/go.mod b/go.mod index 21260544..63ab33b1 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/armosec/armoapi-go v0.0.41 github.com/armosec/k8s-interface v0.0.56 - github.com/armosec/opa-utils v0.0.97 + github.com/armosec/opa-utils v0.0.99 github.com/armosec/rbac-utils v0.0.12 github.com/armosec/utils-go v0.0.3 github.com/briandowns/spinner v1.18.0 @@ -19,6 +19,7 @@ require ( github.com/satori/go.uuid v1.2.0 github.com/spf13/cobra v1.2.1 github.com/stretchr/testify v1.7.0 + go.uber.org/zap v1.19.1 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.22.2 k8s.io/apimachinery v0.22.2 @@ -77,7 +78,6 @@ require ( 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-20210711020723-a769d52b0f97 // indirect golang.org/x/net v0.0.0-20210825183410-e898025ed96a // indirect golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 // indirect diff --git a/go.sum b/go.sum index 05b941f4..c2c78393 100644 --- a/go.sum +++ b/go.sum @@ -95,8 +95,8 @@ github.com/armosec/k8s-interface v0.0.50/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2 github.com/armosec/k8s-interface v0.0.56 h1:7dOgc3qZaI7ReLRZcJa2JZKk0rliyYi05l1vuHc6gcE= github.com/armosec/k8s-interface v0.0.56/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.97 h1:KPjRZdsAC9EObo17QxiW+s5KWmF6vNFu+VQSOgFv5uk= -github.com/armosec/opa-utils v0.0.97/go.mod h1:BNTjeianyXlflJMz3bZM0GimBWqmzirUf1whWR6Os04= +github.com/armosec/opa-utils v0.0.99 h1:ZuoIPg6vbgO4J09xJZDO/yIRD59odwmK2Bm55uTvkU8= +github.com/armosec/opa-utils v0.0.99/go.mod h1:BNTjeianyXlflJMz3bZM0GimBWqmzirUf1whWR6Os04= github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40= github.com/armosec/rbac-utils v0.0.12 h1:uJpMGDyLAX129PrKHp6NPNB6lVRhE0OZIwV6ywzSDrs= github.com/armosec/rbac-utils v0.0.12/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0= diff --git a/opaprocessor/processorhandler.go b/opaprocessor/processorhandler.go index d9c53fbc..e1a4d73c 100644 --- a/opaprocessor/processorhandler.go +++ b/opaprocessor/processorhandler.go @@ -6,6 +6,7 @@ import ( "time" "github.com/armosec/kubescape/cautils" + ksscore "github.com/armosec/kubescape/score" "github.com/armosec/opa-utils/objectsenvelopes" "github.com/armosec/opa-utils/reporthandling" "github.com/armosec/opa-utils/reporthandling/apis" @@ -71,6 +72,9 @@ func (opaHandler *OPAProcessorHandler) ProcessRulesListenner() { // edit results opap.updateResults() + //TODO: review this location + scorewrapper := ksscore.NewScoreWrapper(opaSessionObj) + scorewrapper.Calculate(ksscore.EPostureReportV2) // report *opaHandler.reportResults <- opaSessionObj } diff --git a/resultshandling/results.go b/resultshandling/results.go index 82628862..1c4ff560 100644 --- a/resultshandling/results.go +++ b/resultshandling/results.go @@ -33,17 +33,7 @@ func (resultsHandler *ResultsHandler) HandleResults(scanInfo *cautils.ScanInfo) fmt.Println(err) } - // TODO - get score from table - var score float32 = 0 - if opaSessionObj.PostureReport != nil { - for i := range opaSessionObj.PostureReport.FrameworkReports { - score += opaSessionObj.PostureReport.FrameworkReports[i].Score - } - score /= float32(len(opaSessionObj.PostureReport.FrameworkReports)) - resultsHandler.printerObj.Score(score) - } - - return score + return opaSessionObj.Report.SummaryDetails.Score } // CalculatePostureScore calculate final score diff --git a/score/score.go b/score/score.go new file mode 100644 index 00000000..6e78d858 --- /dev/null +++ b/score/score.go @@ -0,0 +1,43 @@ +package score + +import ( + "fmt" + + "github.com/armosec/opa-utils/score" + + "github.com/armosec/kubescape/cautils" +) + +/* provides a wrapper for scoreUtils, since there's no common interface between postureReportV1 and PostureReportV2 +and the need of concrete objects + I've decided to create scoreWrapper that will allow calculating score regardless (as long as opaSessionObj is there) +*/ +type ScoreWrapper struct { + scoreUtil *score.ScoreUtil + opaSessionObj *cautils.OPASessionObj +} + +type PostureReportVersion string + +const ( + EPostureReportV1 PostureReportVersion = "v1" + EPostureReportV2 PostureReportVersion = "V2" +) + +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) + } + + return fmt.Errorf("unsupported score calculator") +} + +func NewScoreWrapper(opaSessionObj *cautils.OPASessionObj) *ScoreWrapper { + return &ScoreWrapper{ + scoreUtil: score.NewScore(opaSessionObj.AllResources), + opaSessionObj: opaSessionObj, + } +} diff --git a/score/score_test.go b/score/score_test.go new file mode 100644 index 00000000..323f8eed --- /dev/null +++ b/score/score_test.go @@ -0,0 +1 @@ +package score From 9a7aeff870b7bc6e3ee679b867aeec6d9975c934 Mon Sep 17 00:00:00 2001 From: rrefael Date: Sun, 16 Jan 2022 15:40:34 +0200 Subject: [PATCH 16/34] update overview text- README --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 54e84c16..8737f078 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,10 @@ [![build](https://github.com/armosec/kubescape/actions/workflows/build.yaml/badge.svg)](https://github.com/armosec/kubescape/actions/workflows/build.yaml) [![Go Report Card](https://goreportcard.com/badge/github.com/armosec/kubescape)](https://goreportcard.com/report/github.com/armosec/kubescape) -Kubescape is the first open-source tool for testing if Kubernetes is deployed securely according to multiple frameworks: -regulatory, customized company policies and DevSecOps best practices, such as the [NSA-CISA](https://www.armosec.io/blog/kubernetes-hardening-guidance-summary-by-armo) and the [MITRE ATT&CK®](https://www.microsoft.com/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/) . -Kubescape scans K8s clusters, YAML files, and HELM charts, and detect misconfigurations and software vulnerabilities at early stages of the CI/CD pipeline and provides a risk score instantly and risk trends over time. -Kubescape integrates natively with other DevOps tools, including Jenkins, CircleCI and Github workflows. +Kubescape is a K8s open-source tool providing a multi-cloud K8s single pane of glass, including risk analysis, security compliance, RBAC visualizer and image vulnerabilities scanning. +Kubescape scans K8s clusters, YAML files, and HELM charts, detecting misconfigurations according to multiple frameworks (such as the [NSA-CISA](https://www.armosec.io/blog/kubernetes-hardening-guidance-summary-by-armo) , [MITRE ATT&CK®](https://www.microsoft.com/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/)), software vulnerabilities, and RBAC (role-based-access-control) violations at early stages of the CI/CD pipeline, calculates risk score instantly and shows risk trends over time. +It became one of the fastest-growing Kubernetes tools among developers due to its easy-to-use CLI interface, flexible output formats, and automated scanning capabilities, saving Kubernetes users and admins’ precious time, effort, and resources. +Kubescape integrates natively with other DevOps tools, including Jenkins, CircleCI, Github workflows, Prometheus, and Slack, and supports multi-cloud K8s deployments like EKS, GKE, and AKS.
From 8a166e5ba5f54038c52c72344c199c9fa7811248 Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Mon, 17 Jan 2022 11:54:02 +0200 Subject: [PATCH 17/34] typo fix --- resultshandling/reporter/v2/reporteventreceiver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resultshandling/reporter/v2/reporteventreceiver.go b/resultshandling/reporter/v2/reporteventreceiver.go index 2c4fd3ba..03664b97 100644 --- a/resultshandling/reporter/v2/reporteventreceiver.go +++ b/resultshandling/reporter/v2/reporteventreceiver.go @@ -128,7 +128,7 @@ func (report *ReportEventReceiver) sendResources(host string, postureReport *rep return fmt.Errorf("failed to unmarshal resource '%s', reason: %v", v.GetResourceID(), err) } - if counter+len(r) >= MAX_REPORT_SIZE && len(splittedPostureReport.Resources) > 0 { + if counter+len(r) >= MAX_REPORT_SIZE && len(splittedPostureReport.Results) > 0 { // send report if err := report.sendReport(host, splittedPostureReport, *reportCounter, false); err != nil { From a44823c3edccb3d1782773830e969a9c88bc31bd Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Mon, 17 Jan 2022 13:55:43 +0200 Subject: [PATCH 18/34] example cloud integration scripts --- examples/cloud-vendor-integration/aws.sh | 68 ++++++++++++++++++++++++ examples/cloud-vendor-integration/gcp.sh | 48 +++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100755 examples/cloud-vendor-integration/aws.sh create mode 100755 examples/cloud-vendor-integration/gcp.sh diff --git a/examples/cloud-vendor-integration/aws.sh b/examples/cloud-vendor-integration/aws.sh new file mode 100755 index 00000000..67f36bfe --- /dev/null +++ b/examples/cloud-vendor-integration/aws.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# AWS +# Attach the Kubescape service account to an AWS IAM role with the described cluster permission + +# Prerequisites: +# eksctl, awscli v2 + +# Set environment variables +echo 'Set environment variables' +export kubescape_namespace=armo-system +export kubescape_serviceaccount=armo-kubescape-service-account + +# Get current context +echo 'Get current context' +export context=$(kubectl config current-context) + +# Get cluster arn +echo 'Get cluster arn' +export cluster_arn=$(kubectl config view -o jsonpath="{.contexts[?(@.name == \"$context\")].context.cluster}") + +# Get cluster name +echo 'Get cluster name' +export cluster_name=$(echo "$cluster_arn" | awk -F'/' '{print $NF}') + +# Get cluster region +echo 'Get cluster region' +export cluster_region=$(echo "$cluster_arn" | awk -F':' '{print $4}') + +# First step, Create IAM OIDC provider for the cluster (Not required if the third step runs as is): +echo 'Create IAM OIDC provider for the cluster' +eksctl utils associate-iam-oidc-provider --cluster $cluster_name --approve + +# Second step, Create a policy and service account role: +# Create a kubescape policy +echo 'Create a kubescape policy' +export kubescape_policy_arn=$(aws iam create-policy \ + --output yaml \ + --query 'Policy.Arn' \ + --policy-name kubescape \ + --policy-document \ +"$(cat <&2 echo "Please set the CLUSTER_NAME environment variable" && exit 1 +[ -z $CLUSTER_REGION ] && >&2 echo "Please set the CLUSTER_REGION environment variable" && exit 1 + +# Create GCP service account +gcloud iam service-accounts create kubescape --display-name=kubescape + +# Set environment variables +echo 'Set environment variables' +export kubescape_namespace=armo-system +export kubescape_serviceaccount=armo-kubescape-service-account + +# Get current GCP project +echo 'Get current GCP project' +export gcp_project=$(gcloud config get-value project) + +sleep 5 +# Get service account email +echo 'Get service account email' +export gcp_service_account=$(gcloud iam service-accounts list --filter="email ~ kubescape@" --format="value(email)") + +# Create custome cluster.get role +echo 'Create custome cluster.get role' +export custom_role_name=$(gcloud iam roles create kubescape --project=$gcp_project --title='Armo kubernetes' --description='Allow clusters.get to Kubernetes armo service account' --permissions=container.clusters.get --stage=GA --format='value(name)') + +# Attach policies to the service account +echo 'Attach policies to the service account' +gcloud --quiet projects add-iam-policy-binding $gcp_project --member serviceAccount:$gcp_service_account --role $custom_role_name >/dev/null + +# If there are missing permissions, use this role instead +# gcloud --quiet projects add-iam-policy-binding $gcp_project --member serviceAccount:$gcp_service_account --role roles/container.clusterViewer + +# Bind the GCP kubescape service account to kubescape kubernetes service account +gcloud iam service-accounts add-iam-policy-binding $gcp_service_account --role roles/iam.workloadIdentityUser --member "serviceAccount:${gcp_project}.svc.id.goog[${kubescape_namespace}/${kubescape_serviceaccount}]" + +# Install/Upgrade Kubescape chart +echo 'Install/Upgrade Kubescape chart' +helm upgrade --install armo armo-components/ -n armo-system --create-namespace --set cloud_provider_engine=gke --set gke_service_account=$gcp_service_account --set cloudRegion=$CLUSTER_REGION --set clusterName=$CLUSTER_NAME --set gkeProject=$gcp_project From cfc69f5a0fb153d1a50e807b96653afc2eb3e33e Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Tue, 18 Jan 2022 14:41:07 +0200 Subject: [PATCH 19/34] adding access to container registry --- examples/cloud-vendor-integration/gcp.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/cloud-vendor-integration/gcp.sh b/examples/cloud-vendor-integration/gcp.sh index 2edd7414..0f33cb15 100755 --- a/examples/cloud-vendor-integration/gcp.sh +++ b/examples/cloud-vendor-integration/gcp.sh @@ -36,6 +36,7 @@ export custom_role_name=$(gcloud iam roles create kubescape --project=$gcp_proje # Attach policies to the service account echo 'Attach policies to the service account' gcloud --quiet projects add-iam-policy-binding $gcp_project --member serviceAccount:$gcp_service_account --role $custom_role_name >/dev/null +gcloud --quiet projects add-iam-policy-binding $gcp_project --member serviceAccount:$gcp_service_account --role roles/storage.objectViewer >/dev/null # If there are missing permissions, use this role instead # gcloud --quiet projects add-iam-policy-binding $gcp_project --member serviceAccount:$gcp_service_account --role roles/container.clusterViewer From 61ce00108edaf6e37c15194b8197e9847799dd28 Mon Sep 17 00:00:00 2001 From: yiscah Date: Tue, 18 Jan 2022 15:29:35 +0200 Subject: [PATCH 20/34] fixed download to local path --- clihandler/clidownload.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clihandler/clidownload.go b/clihandler/clidownload.go index 9129344d..b82dbf64 100644 --- a/clihandler/clidownload.go +++ b/clihandler/clidownload.go @@ -51,7 +51,7 @@ func setPathandFilename(downloadInfo *cautils.DownloadInfo) { dir, file := filepath.Split(downloadInfo.Path) if dir == "" { downloadInfo.Path = file - } else { + } else if strings.Contains(file, ".") { downloadInfo.Path = dir downloadInfo.FileName = file } From d91304f9ad22a6af78e148af616a63f6a8866ebf Mon Sep 17 00:00:00 2001 From: yiscah Date: Tue, 18 Jan 2022 16:32:01 +0200 Subject: [PATCH 21/34] setUseArtifactsFrom only when flag is set --- cautils/scaninfo.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cautils/scaninfo.go b/cautils/scaninfo.go index a2c83ca9..f1308951 100644 --- a/cautils/scaninfo.go +++ b/cautils/scaninfo.go @@ -89,6 +89,9 @@ func (scanInfo *ScanInfo) Init() { } func (scanInfo *ScanInfo) setUseArtifactsFrom() { + if scanInfo.UseArtifactsFrom == "" { + return + } // UseArtifactsFrom must be a path without a filename dir, file := filepath.Split(scanInfo.UseArtifactsFrom) if dir == "" { From f65e79152215bd7a4e5ba8eff3a8ff5c487a7b4c Mon Sep 17 00:00:00 2001 From: yiscah Date: Tue, 18 Jan 2022 17:01:53 +0200 Subject: [PATCH 22/34] fix parsing local path to artifacts --- cautils/scaninfo.go | 3 ++- clihandler/clidownload.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cautils/scaninfo.go b/cautils/scaninfo.go index f1308951..7ceb5c67 100644 --- a/cautils/scaninfo.go +++ b/cautils/scaninfo.go @@ -7,6 +7,7 @@ import ( "log" "os" "path/filepath" + "strings" "github.com/armosec/kubescape/cautils/getter" "github.com/armosec/opa-utils/reporthandling" @@ -96,7 +97,7 @@ func (scanInfo *ScanInfo) setUseArtifactsFrom() { dir, file := filepath.Split(scanInfo.UseArtifactsFrom) if dir == "" { scanInfo.UseArtifactsFrom = file - } else { + } else if strings.Contains(file, ".json") { scanInfo.UseArtifactsFrom = dir } // set frameworks files diff --git a/clihandler/clidownload.go b/clihandler/clidownload.go index b82dbf64..ca07847a 100644 --- a/clihandler/clidownload.go +++ b/clihandler/clidownload.go @@ -51,7 +51,7 @@ func setPathandFilename(downloadInfo *cautils.DownloadInfo) { dir, file := filepath.Split(downloadInfo.Path) if dir == "" { downloadInfo.Path = file - } else if strings.Contains(file, ".") { + } else if strings.Contains(file, ".json") { downloadInfo.Path = dir downloadInfo.FileName = file } From cc06a414fe6091e965fa030a37205c114af1e484 Mon Sep 17 00:00:00 2001 From: yiscah Date: Tue, 18 Jan 2022 17:50:28 +0200 Subject: [PATCH 23/34] added examples of download + load artifacts --- README.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8737f078..1327ae0a 100644 --- a/README.md +++ b/README.md @@ -97,9 +97,11 @@ Set-ExecutionPolicy RemoteSigned -scope CurrentUser | `-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 | | +| `--use-from` | | Load local framework object from specified path. If not used will download latest | +| `--use-artifacts-from` | | Load artifacts (frameworks, control-config, exceptions) from local directory. If not used will download them | | | `--use-default` | `false` | Load local framework object from default path. If not used will download latest | `true`/`false` | -| `--exceptions` | | Path to an [exceptions obj](examples/exceptions.json). If not set will download exceptions from Armo management portal | | +| `--exceptions` | | Path to an [exceptions obj](examples/exceptions.json). If not set will download exceptions from Armo management portal | +| `--controls-config` | | Path to a controls-config obj. If not set will download controls-config from ARMO management portal | | | `--submit` | `false` | If set, Kubescape will 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 sent | `true`/`false` | | `--keep-local` | `false` | Kubescape will not send scan results to Armo management portal. Use this flag if you ran with the `--submit` flag in the past and you do not want to submit your current scan results | `true`/`false` | | `--account` | | Armo portal account ID. Default will load account ID from configMap or config file | | @@ -193,7 +195,7 @@ It is possible to run Kubescape offline! First download the framework and then scan with `--use-from` flag -1. Download and save in file, if file name not specified, will store save to `~/.kubescape/.json` +1. Download and save in file, if file name not specified, will save in `~/.kubescape/.json` ``` kubescape download framework nsa --output nsa.json ``` @@ -204,6 +206,19 @@ kubescape scan framework nsa --use-from nsa.json ``` + +You can also download all artifacts to a local path and then load them using `--use-artifacts-from` flag + +1. Download and save in local directory, if path not specified, will save all in `~/.kubescape` +``` +kubescape download artifacts --output path/to/local/dir +``` + +2. Scan using the downloaded artifacts +``` +kubescape scan framework nsa --use-artifacts-from path/to/local/dir +``` + ## Scan Periodically using Helm - Contributed by [@yonahd](https://github.com/yonahd) You can scan your cluster periodically by adding a `CronJob` that will repeatedly trigger kubescape From 62af441a1d2f987a5d508738ca712629f8d044a4 Mon Sep 17 00:00:00 2001 From: rrefael Date: Wed, 19 Jan 2022 09:19:16 +0200 Subject: [PATCH 24/34] Increase release & tag to major --- .github/workflows/build.yaml | 10 +++++----- .github/workflows/build_dev.yaml | 6 +++--- .github/workflows/master_pr_checks.yaml | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 03c6b1ac..57845193 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -16,8 +16,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - tag_name: v1.0.${{ github.run_number }} - release_name: Release v1.0.${{ github.run_number }} + tag_name: v2.0.${{ github.run_number }} + release_name: Release v2.0.${{ github.run_number }} draft: false prerelease: false build: @@ -39,7 +39,7 @@ jobs: - name: Build env: - RELEASE: v1.0.${{ github.run_number }} + RELEASE: v2.0.${{ github.run_number }} ArmoBEServer: api.armo.cloud ArmoERServer: report.armo.cloud ArmoWebsite: portal.armo.cloud @@ -48,7 +48,7 @@ jobs: - name: Smoke Testing env: - RELEASE: v1.0.${{ github.run_number }} + RELEASE: v2.0.${{ github.run_number }} KUBESCAPE_SKIP_UPDATE_CHECK: "true" run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape @@ -74,7 +74,7 @@ jobs: - uses: actions/checkout@v2 - name: Set name - run: echo quay.io/armosec/kubescape:v1.0.${{ github.run_number }} > build_tag.txt + run: echo quay.io/armosec/kubescape:v2.0.${{ github.run_number }} > build_tag.txt - name: Build the Docker image run: docker build . --file build/Dockerfile --tag $(cat build_tag.txt) --build-arg run_number=${{ github.run_number }} diff --git a/.github/workflows/build_dev.yaml b/.github/workflows/build_dev.yaml index 2ee5e2b0..8cd5da5c 100644 --- a/.github/workflows/build_dev.yaml +++ b/.github/workflows/build_dev.yaml @@ -23,7 +23,7 @@ jobs: - name: Build env: - RELEASE: v1.0.${{ github.run_number }} + RELEASE: v2.0.${{ github.run_number }} ArmoBEServer: api.armo.cloud ArmoERServer: report.euprod1.cyberarmorsoft.com ArmoWebsite: portal.armo.cloud @@ -32,7 +32,7 @@ jobs: - name: Smoke Testing env: - RELEASE: v1.0.${{ github.run_number }} + RELEASE: v2.0.${{ github.run_number }} KUBESCAPE_SKIP_UPDATE_CHECK: "true" run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape @@ -53,7 +53,7 @@ jobs: - uses: actions/checkout@v2 - name: Set name - run: echo quay.io/armosec/kubescape:dev-v1.0.${{ github.run_number }} > build_tag.txt + run: echo quay.io/armosec/kubescape:dev-v2.0.${{ github.run_number }} > build_tag.txt - name: Build the Docker image run: docker build . --file build/Dockerfile --tag $(cat build_tag.txt) --build-arg run_number=${{ github.run_number }} diff --git a/.github/workflows/master_pr_checks.yaml b/.github/workflows/master_pr_checks.yaml index b4a3bbca..bc88bc45 100644 --- a/.github/workflows/master_pr_checks.yaml +++ b/.github/workflows/master_pr_checks.yaml @@ -24,7 +24,7 @@ jobs: - name: Build env: - RELEASE: v1.0.${{ github.run_number }} + RELEASE: v2.0.${{ github.run_number }} ArmoBEServer: api.armo.cloud ArmoERServer: report.armo.cloud ArmoWebsite: portal.armo.cloud @@ -33,7 +33,7 @@ jobs: - name: Smoke Testing env: - RELEASE: v1.0.${{ github.run_number }} + RELEASE: v2.0.${{ github.run_number }} KUBESCAPE_SKIP_UPDATE_CHECK: "true" run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape \ No newline at end of file From 1ae76b4377bce196195a732a8beaa8cb4d3e1acf Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Wed, 19 Jan 2022 16:37:32 +0200 Subject: [PATCH 25/34] checking if context exists --- resourcehandler/cloudproviderhandler.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/resourcehandler/cloudproviderhandler.go b/resourcehandler/cloudproviderhandler.go index e0ebb4c0..26e49761 100644 --- a/resourcehandler/cloudproviderhandler.go +++ b/resourcehandler/cloudproviderhandler.go @@ -48,7 +48,11 @@ func getCloudProviderFromContext() string { } func getClusterFromContext() string { - cluster := k8sinterface.GetCurrentContext().Cluster + context := k8sinterface.GetCurrentContext() + if context == nil { + return "" + } + cluster := context.Cluster if cluster != "" { return cluster } From 71404f2205acc7e6e72887c499341509198702c4 Mon Sep 17 00:00:00 2001 From: Quirino Gervacio Date: Thu, 20 Jan 2022 04:51:04 +0800 Subject: [PATCH 26/34] Prevent index out of range [0] when control report do not have a rule report --- resultshandling/printer/v1/junit.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resultshandling/printer/v1/junit.go b/resultshandling/printer/v1/junit.go index 8ec5f17c..749b5d85 100644 --- a/resultshandling/printer/v1/junit.go +++ b/resultshandling/printer/v1/junit.go @@ -107,7 +107,7 @@ func convertPostureReportToJunitResult(postureResult *reporthandling.PostureRepo testCase.Name = controlReports.Name testCase.Classname = "Kubescape" testCase.Time = postureResult.ReportGenerationTime.String() - if 0 < len(controlReports.RuleReports[0].RuleResponses) { + if len(controlReports.RuleReports) > 0 { testCase.Resources = controlReports.GetNumberOfResources() testCase.Excluded = controlReports.GetNumberOfWarningResources() From c0161c9b33400bc8d04e84dae22b611ac1639f96 Mon Sep 17 00:00:00 2001 From: Viktor Farcic Date: Thu, 20 Jan 2022 19:54:57 +0100 Subject: [PATCH 27/34] Video --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1327ae0a..5bf1a38b 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ Want to contribute? Want to discuss something? Have an issue? ## Tutorials * [Overview](https://youtu.be/wdBkt_0Qhbg) +* [How To Secure Kubernetes Clusters With Kubescape And Armo](https://youtu.be/ZATGiDIDBQk) * [Scanning Kubernetes YAML files](https://youtu.be/Ox6DaR7_4ZI) * [Scan Kubescape on an air-gapped environment (offline support)](https://youtu.be/IGXL9s37smM) * [Managing exceptions in the Kubescape SaaS version](https://youtu.be/OzpvxGmCR80) From aa0be474e20cf5b978004f035475b5d8815d0562 Mon Sep 17 00:00:00 2001 From: Geert Smelt Date: Fri, 21 Jan 2022 15:34:56 +0100 Subject: [PATCH 28/34] Fix typo in namespace --- resultshandling/printer/v2/controlmapping/summeryhelpers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resultshandling/printer/v2/controlmapping/summeryhelpers.go b/resultshandling/printer/v2/controlmapping/summeryhelpers.go index ce74551d..cc606f8a 100644 --- a/resultshandling/printer/v2/controlmapping/summeryhelpers.go +++ b/resultshandling/printer/v2/controlmapping/summeryhelpers.go @@ -40,7 +40,7 @@ func groupByNamespaceOrKind(resources []WorkloadSummary, status func(workloadSum case workloadinterface.TypeWorkloadObject: ns := "" if resources[i].resource.GetNamespace() != "" { - ns = "Namescape " + resources[i].resource.GetNamespace() + ns = "Namespace " + resources[i].resource.GetNamespace() } if r, ok := mapResources[ns]; ok { r = append(r, resources[i]) From 5f43da94badb5b35bf4dfb7ef278ba70ef38f29e Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Sun, 23 Jan 2022 20:17:03 +0200 Subject: [PATCH 29/34] fixing the crash around submit rbac --- resultshandling/reporter/v1/reporteventreceiver.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resultshandling/reporter/v1/reporteventreceiver.go b/resultshandling/reporter/v1/reporteventreceiver.go index fa774983..853700ef 100644 --- a/resultshandling/reporter/v1/reporteventreceiver.go +++ b/resultshandling/reporter/v1/reporteventreceiver.go @@ -38,7 +38,9 @@ func NewReportEventReceiver(tenantConfig *cautils.ConfigObj) *ReportEventReceive } func (report *ReportEventReceiver) ActionSendReport(opaSessionObj *cautils.OPASessionObj) error { - cautils.ReportV2ToV1(opaSessionObj) + 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. Contact ARMO team for more details" From aa2f69125f05a3b3cdf2533cdd426ebc100deaf8 Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Mon, 24 Jan 2022 09:12:13 +0200 Subject: [PATCH 30/34] returning rbac submit code --- cautils/rbac.go | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/cautils/rbac.go b/cautils/rbac.go index 9b804702..a9d8bfbc 100644 --- a/cautils/rbac.go +++ b/cautils/rbac.go @@ -51,23 +51,22 @@ func (rbacObjects *RBACObjects) rbacObjectsToResources(resources *rbacutils.Rbac Should be investigated ************************************************************************************************************************ - - // wrap rbac aggregated objects in IMetadata and add to allresources - // TODO - DEPRECATE SA2WLIDmap - SA2WLIDmapIMeta, err := rbacutils.SA2WLIDmapIMetadataWrapper(resources.SA2WLIDmap) - if err != nil { - return nil, err - } - allresources[SA2WLIDmapIMeta.GetID()] = SA2WLIDmapIMeta - - SAID2WLIDmapIMeta, err := rbacutils.SAID2WLIDmapIMetadataWrapper(resources.SAID2WLIDmap) - if err != nil { - return nil, err - } - allresources[SAID2WLIDmapIMeta.GetID()] = SAID2WLIDmapIMeta - */ + // wrap rbac aggregated objects in IMetadata and add to allresources + // TODO - DEPRECATE SA2WLIDmap + SA2WLIDmapIMeta, err := rbacutils.SA2WLIDmapIMetadataWrapper(resources.SA2WLIDmap) + if err != nil { + return nil, err + } + allresources[SA2WLIDmapIMeta.GetID()] = SA2WLIDmapIMeta + + SAID2WLIDmapIMeta, err := rbacutils.SAID2WLIDmapIMetadataWrapper(resources.SAID2WLIDmap) + if err != nil { + return nil, err + } + allresources[SAID2WLIDmapIMeta.GetID()] = SAID2WLIDmapIMeta + // convert rbac k8s resources to IMetadata and add to allresources for _, cr := range resources.ClusterRoles.Items { crmap, err := convertToMap(cr) From 09db5d94e17945579c35aad28636dc0ab6e773e6 Mon Sep 17 00:00:00 2001 From: Ben Hirschberg Date: Mon, 24 Jan 2022 16:47:12 +0200 Subject: [PATCH 31/34] Fixing null pointer exception in case of in-cluster installation --- resourcehandler/gkesupport.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/resourcehandler/gkesupport.go b/resourcehandler/gkesupport.go index e6f2eeef..9ed961e1 100644 --- a/resourcehandler/gkesupport.go +++ b/resourcehandler/gkesupport.go @@ -71,7 +71,11 @@ func NewGKEProviderContext() *GKEProviderContext { } func (gkeProviderContext *GKEProviderContext) getKubeClusterName() string { - cluster := k8sinterface.GetCurrentContext().Cluster + context := k8sinterface.GetCurrentContext() + if context == nil { + return "" + } + cluster := context.Cluster parsedName := strings.Split(cluster, "_") if len(parsedName) < 3 { return "" @@ -90,7 +94,11 @@ func (gkeProviderContext *GKEProviderContext) getKubeClusterName() string { } func (gkeProviderContext *GKEProviderContext) getKubeCluster() string { - cluster := k8sinterface.GetCurrentContext().Cluster + context := k8sinterface.GetCurrentContext() + if context == nil { + return "" + } + cluster := context.Cluster if cluster != "" { return cluster } From bbb0d2154f4ca6ef121081ff06de52fa9f006ae5 Mon Sep 17 00:00:00 2001 From: dwertent Date: Sun, 30 Jan 2022 08:06:50 +0200 Subject: [PATCH 32/34] fixed version print --- cautils/versioncheck.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cautils/versioncheck.go b/cautils/versioncheck.go index 22f88c83..55f6417e 100644 --- a/cautils/versioncheck.go +++ b/cautils/versioncheck.go @@ -96,7 +96,7 @@ func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckReques if latestVersion.ClientUpdate != "" { if BuildNumber != "" && BuildNumber < latestVersion.ClientUpdate { - WarningDisplay(os.Stderr, warningMessage(latestVersion.Client, latestVersion.ClientUpdate), "\n") + WarningDisplay(os.Stderr, warningMessage(latestVersion.Client, latestVersion.ClientUpdate)+"\n") } } @@ -106,7 +106,7 @@ func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckReques // } if latestVersion.Message != "" { - InfoDisplay(os.Stderr, latestVersion.Message, "\n") + InfoDisplay(os.Stderr, latestVersion.Message+"\n") } return nil From db84380844842491f7c01b50cb3425b9cc6c0054 Mon Sep 17 00:00:00 2001 From: dwertent Date: Sun, 30 Jan 2022 08:14:10 +0200 Subject: [PATCH 33/34] print summary and scoe --- .gitignore | 3 ++- resultshandling/printer/v2/controlmapping/prettyprinter.go | 5 +---- resultshandling/results.go | 5 ++++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 3c3ed147..a52b4539 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ *debug* *vender* *.pyc* -.idea \ No newline at end of file +.idea +ca.srl \ No newline at end of file diff --git a/resultshandling/printer/v2/controlmapping/prettyprinter.go b/resultshandling/printer/v2/controlmapping/prettyprinter.go index bbb3a7e2..dec73734 100644 --- a/resultshandling/printer/v2/controlmapping/prettyprinter.go +++ b/resultshandling/printer/v2/controlmapping/prettyprinter.go @@ -49,10 +49,7 @@ func (prettyPrinter *PrettyPrinter) printResults(controls *reportsummary.Control controlSummary := controls.GetControl(reportsummary.EControlCriteriaName, prettyPrinter.sortedControlNames[i]) // summaryDetails.Controls ListControls().All() Controls.GetControl(ca) prettyPrinter.printTitle(controlSummary) prettyPrinter.printResources(controlSummary, allResources) - - if controlSummary.GetStatus().IsSkipped() { - prettyPrinter.printSummary(prettyPrinter.sortedControlNames[i], controlSummary) - } + prettyPrinter.printSummary(prettyPrinter.sortedControlNames[i], controlSummary) } } diff --git a/resultshandling/results.go b/resultshandling/results.go index 1c4ff560..b20d9bb2 100644 --- a/resultshandling/results.go +++ b/resultshandling/results.go @@ -33,7 +33,10 @@ func (resultsHandler *ResultsHandler) HandleResults(scanInfo *cautils.ScanInfo) fmt.Println(err) } - return opaSessionObj.Report.SummaryDetails.Score + score := opaSessionObj.Report.SummaryDetails.Score + resultsHandler.printerObj.Score(score) + + return score } // CalculatePostureScore calculate final score From bb7f38ce315e62bdf76dd10cb3ed331780e8e8d1 Mon Sep 17 00:00:00 2001 From: dwertent Date: Sun, 30 Jan 2022 11:09:02 +0200 Subject: [PATCH 34/34] load cloud provider from env --- resourcehandler/cloudproviderhandler.go | 6 ++++++ resourcehandler/gkesupport.go | 8 ++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/resourcehandler/cloudproviderhandler.go b/resourcehandler/cloudproviderhandler.go index 26e49761..aa101766 100644 --- a/resourcehandler/cloudproviderhandler.go +++ b/resourcehandler/cloudproviderhandler.go @@ -26,8 +26,14 @@ func initCloudProvider() ICloudProvider { switch getCloudProvider() { case "gke", "gcp": + if isEnvVars() { + return NewGKEProviderEnvVar() + } return NewGKEProviderContext() case "eks", "aws": + if isEnvVars() { + return NewEKSProviderEnvVar() + } return NewEKSProviderContext() } return NewEmptyCloudProvider() diff --git a/resourcehandler/gkesupport.go b/resourcehandler/gkesupport.go index 9ed961e1..0dda579e 100644 --- a/resourcehandler/gkesupport.go +++ b/resourcehandler/gkesupport.go @@ -41,7 +41,7 @@ func (gkeProvider *GKEProviderEnvVar) getProjectForGKE(cluster string) (string, } parsedName := strings.Split(cluster, "_") if len(parsedName) < 3 { - return "", fmt.Errorf("error: failed to parse cluster name") + return "", fmt.Errorf("failed to parse project name from cluster name: '%s'", cluster) } project = parsedName[1] return project, nil @@ -54,7 +54,7 @@ func (gkeProvider *GKEProviderEnvVar) getRegionForGKE(cluster string) (string, e } parsedName := strings.Split(cluster, "_") if len(parsedName) < 3 { - return "", fmt.Errorf("error: failed to parse cluster name") + return "", fmt.Errorf("failed to parse region name from cluster name: '%s'", cluster) } region = parsedName[2] return region, nil @@ -117,7 +117,7 @@ func (gkeProviderContext *GKEProviderContext) getProject(cluster string, provide func (gkeProviderContext *GKEProviderContext) getProjectForGKE(cluster string) (string, error) { parsedName := strings.Split(cluster, "_") if len(parsedName) < 3 { - return "", fmt.Errorf("error: failed to parse cluster name") + return "", fmt.Errorf("failed to parse project name from cluster name: '%s'", cluster) } project := parsedName[1] return project, nil @@ -126,7 +126,7 @@ func (gkeProviderContext *GKEProviderContext) getProjectForGKE(cluster string) ( func (gkeProviderContext *GKEProviderContext) getRegionForGKE(cluster string) (string, error) { parsedName := strings.Split(cluster, "_") if len(parsedName) < 3 { - return "", fmt.Errorf("error: failed to parse cluster name") + return "", fmt.Errorf("failed to parse region name from cluster name: '%s'", cluster) } region := parsedName[2] return region, nil