From 8ec561556901e5822223f61636caaf58bcf1c321 Mon Sep 17 00:00:00 2001 From: dwertent Date: Tue, 15 Mar 2022 16:50:39 +0200 Subject: [PATCH 1/2] junit format --- build/Dockerfile | 14 +- core/cautils/scaninfo.go | 9 - core/core/download.go | 13 + core/go.mod | 2 +- core/go.sum | 4 +- .../pkg/opaprocessor/processorhandler_test.go | 8 +- core/pkg/resultshandling/printer/v2/junit.go | 6 +- core/pkg/resultshandling/printer/v2/pdf.go | 4 +- .../printer/v2/prettyprinter.go | 8 +- .../resultshandling/printer/v2/prometheus.go | 55 ++++ .../printer/v2/prometheusutils.go | 248 ++++++++++++++++++ httphandler/examples/prometheus/README.md | 3 +- .../examples/prometheus/podmonitor.yaml | 3 +- httphandler/handlerequests/v1/prometheus.go | 22 +- httphandler/listener/setup.go | 3 +- 15 files changed, 366 insertions(+), 36 deletions(-) create mode 100644 core/pkg/resultshandling/printer/v2/prometheus.go create mode 100644 core/pkg/resultshandling/printer/v2/prometheusutils.go diff --git a/build/Dockerfile b/build/Dockerfile index bb00a3f0..cf7341c5 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -17,16 +17,22 @@ RUN pip3 install --no-cache --upgrade pip setuptools WORKDIR /work ADD . . -WORKDIR /work/httphandler +# build kubescape server +WORKDIR /work/httphandler +RUN python build.py +RUN ls -ltr build/ubuntu-latest + +# build kubescape cmd +WORKDIR /work/cmd RUN python build.py -RUN ls -ltr build/ubuntu-latest +RUN /work/build/ubuntu-latest/kubescape download artifacts -o /work/artifacts FROM alpine COPY --from=builder /work/httphandler/build/ubuntu-latest/kubescape /usr/bin/kubescape -# # Download the frameworks. Use the "--use-default" flag when running kubescape -# RUN kubescape download framework nsa && kubescape download framework mitre +RUN mkdir $HOME/.kubescape && chmod 777 -R $HOME/.kubescape +COPY --from=builder /work/artifacts/ $HOME/.kubescape ENTRYPOINT ["kubescape"] diff --git a/core/cautils/scaninfo.go b/core/cautils/scaninfo.go index 988f6c14..b891d01f 100644 --- a/core/cautils/scaninfo.go +++ b/core/cautils/scaninfo.go @@ -133,15 +133,6 @@ func (scanInfo *ScanInfo) setUseArtifactsFrom() { scanInfo.UseExceptions = filepath.Join(scanInfo.UseArtifactsFrom, localExceptionsFilename) } -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() { if scanInfo.UseDefault { for _, policy := range scanInfo.PolicyIdentifier { diff --git a/core/core/download.go b/core/core/download.go index f5ce07a5..c44545e8 100644 --- a/core/core/download.go +++ b/core/core/download.go @@ -2,6 +2,7 @@ package core import ( "fmt" + "os" "path/filepath" "strings" @@ -30,6 +31,9 @@ func DownloadSupportCommands() []string { func (ks *Kubescape) Download(downloadInfo *metav1.DownloadInfo) error { setPathandFilename(downloadInfo) + if err := os.MkdirAll(downloadInfo.Path, os.ModePerm); err != nil { + return err + } if err := downloadArtifact(downloadInfo, downloadFunc); err != nil { return err } @@ -86,6 +90,9 @@ func downloadConfigInputs(downloadInfo *metav1.DownloadInfo) error { if downloadInfo.FileName == "" { downloadInfo.FileName = fmt.Sprintf("%s.json", downloadInfo.Target) } + if controlInputs == nil { + return fmt.Errorf("failed to download controlInputs - received an empty objects") + } // save in file err = getter.SaveInFile(controlInputs, filepath.Join(downloadInfo.Path, downloadInfo.FileName)) if err != nil { @@ -148,6 +155,9 @@ func downloadFramework(downloadInfo *metav1.DownloadInfo) error { if err != nil { return err } + if framework == nil { + return fmt.Errorf("failed to download framework - received an empty objects") + } downloadTo := filepath.Join(downloadInfo.Path, downloadInfo.FileName) err = getter.SaveInFile(framework, downloadTo) if err != nil { @@ -175,6 +185,9 @@ func downloadControl(downloadInfo *metav1.DownloadInfo) error { if err != nil { return err } + if controls == nil { + return fmt.Errorf("failed to download control - received an empty objects") + } downloadTo := filepath.Join(downloadInfo.Path, downloadInfo.FileName) err = getter.SaveInFile(controls, downloadTo) if err != nil { diff --git a/core/go.mod b/core/go.mod index bd7777be..941150a2 100644 --- a/core/go.mod +++ b/core/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/armosec/armoapi-go v0.0.58 github.com/armosec/k8s-interface v0.0.68 - github.com/armosec/opa-utils v0.0.116 + github.com/armosec/opa-utils v0.0.118 github.com/armosec/rbac-utils v0.0.14 github.com/armosec/utils-go v0.0.3 github.com/armosec/utils-k8s-go v0.0.3 diff --git a/core/go.sum b/core/go.sum index e75172f1..d279dfff 100644 --- a/core/go.sum +++ b/core/go.sum @@ -109,8 +109,8 @@ github.com/armosec/k8s-interface v0.0.66/go.mod h1:vwprS8qn/iowd5yf0JHpqDsLA5I8W github.com/armosec/k8s-interface v0.0.68 h1:6CtSakISiI47YHkxh+Va9FzZQIBkWa6g9sbiNxq1Zkk= github.com/armosec/k8s-interface v0.0.68/go.mod h1:PeWn41C2uenZi+xfZdyFF/zG5wXACA00htQyknDUWDE= github.com/armosec/opa-utils v0.0.64/go.mod h1:6tQP8UDq2EvEfSqh8vrUdr/9QVSCG4sJfju1SXQOn4c= -github.com/armosec/opa-utils v0.0.116 h1:3oWuhcpI+MJD/CktEStU1BA0feGNwsCbQrI3ifVfzMs= -github.com/armosec/opa-utils v0.0.116/go.mod h1:gap+EaLG5rnyqvIRGxtdNDC9y7VvoGNm90zK8Ls7avQ= +github.com/armosec/opa-utils v0.0.118 h1:ZX1crwVQmo+sDv+jmTNLbDYfApUBzlgPhD8QI2GCJX0= +github.com/armosec/opa-utils v0.0.118/go.mod h1:gap+EaLG5rnyqvIRGxtdNDC9y7VvoGNm90zK8Ls7avQ= github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40= github.com/armosec/rbac-utils v0.0.14 h1:CKYKcgqJEXWF2Hen/B1pVGtS3nDAG1wp9dDv6oNtq90= github.com/armosec/rbac-utils v0.0.14/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0= diff --git a/core/pkg/opaprocessor/processorhandler_test.go b/core/pkg/opaprocessor/processorhandler_test.go index ab947e59..34b5d97e 100644 --- a/core/pkg/opaprocessor/processorhandler_test.go +++ b/core/pkg/opaprocessor/processorhandler_test.go @@ -112,10 +112,10 @@ func TestProcessResourcesResult(t *testing.T) { assert.Equal(t, 0, len(summaryDetails.ListResourcesIDs().Passed())) // test control listing - assert.Equal(t, len(res.ListControlsIDs(nil).All()), len(summaryDetails.ListControls().All())) - assert.Equal(t, len(res.ListControlsIDs(nil).Passed()), len(summaryDetails.ListControls().Passed())) - assert.Equal(t, len(res.ListControlsIDs(nil).Failed()), len(summaryDetails.ListControls().Failed())) - assert.Equal(t, len(res.ListControlsIDs(nil).Excluded()), len(summaryDetails.ListControls().Excluded())) + assert.Equal(t, len(res.ListControlsIDs(nil).All()), summaryDetails.NumberOfControls().All()) + assert.Equal(t, len(res.ListControlsIDs(nil).Passed()), summaryDetails.NumberOfControls().Passed()) + assert.Equal(t, len(res.ListControlsIDs(nil).Failed()), summaryDetails.NumberOfControls().Failed()) + assert.Equal(t, len(res.ListControlsIDs(nil).Excluded()), summaryDetails.NumberOfControls().Excluded()) assert.True(t, summaryDetails.GetStatus().IsFailed()) opaSessionObj.Exceptions = []armotypes.PostureExceptionPolicy{*mocks.MockExceptionAllKinds(&armotypes.PosturePolicy{FrameworkName: frameworks[0].Name})} diff --git a/core/pkg/resultshandling/printer/v2/junit.go b/core/pkg/resultshandling/printer/v2/junit.go index 1a97e16c..ce13caed 100644 --- a/core/pkg/resultshandling/printer/v2/junit.go +++ b/core/pkg/resultshandling/printer/v2/junit.go @@ -128,7 +128,7 @@ func listTestsSuite(results *cautils.OPASessionObj) []JUnitTestSuite { var testSuites []JUnitTestSuite // control scan - if len(results.Report.SummaryDetails.ListFrameworks().All()) == 0 { + if len(results.Report.SummaryDetails.ListFrameworks()) == 0 { testSuite := JUnitTestSuite{} testSuite.Failures = results.Report.SummaryDetails.NumberOfControls().Failed() testSuite.Timestamp = results.Report.ReportGenerationTime.String() @@ -147,7 +147,7 @@ func listTestsSuite(results *cautils.OPASessionObj) []JUnitTestSuite { testSuite.ID = i testSuite.Name = f.Name testSuite.Properties = properties(f.Score) - testSuite.TestCases = testsCases(results, f.ListControls(), f.GetName()) + testSuite.TestCases = testsCases(results, f.GetControls(), f.GetName()) testSuites = append(testSuites, testSuite) } @@ -176,7 +176,7 @@ func testsCases(results *cautils.OPASessionObj, controls reportsummary.IControls testCaseFailure := JUnitFailure{} testCaseFailure.Type = "Control" // testCaseFailure.Contents = - testCaseFailure.Message = fmt.Sprintf("Remediation: %s\nMore details: %s\n\n%s", control.GetRemediation(), getControlURL(control.GetID()), strings.Join(resourcesStr, "\n")) + testCaseFailure.Message = fmt.Sprintf("Remediation: %s\nMore details: %s\n\n%s", control.GetRemediation(), getControlLink(control.GetID()), strings.Join(resourcesStr, "\n")) testCase.Failure = &testCaseFailure } else if control.GetStatus().IsSkipped() { diff --git a/core/pkg/resultshandling/printer/v2/pdf.go b/core/pkg/resultshandling/printer/v2/pdf.go index 26ac9cdf..9a7d8a27 100644 --- a/core/pkg/resultshandling/printer/v2/pdf.go +++ b/core/pkg/resultshandling/printer/v2/pdf.go @@ -60,7 +60,7 @@ func (pdfPrinter *PdfPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) m := pdf.NewMaroto(consts.Portrait, consts.A4) pdfPrinter.printHeader(m) - pdfPrinter.printFramework(m, opaSessionObj.Report.SummaryDetails.ListFrameworks().All()) + pdfPrinter.printFramework(m, opaSessionObj.Report.SummaryDetails.ListFrameworks()) pdfPrinter.printTable(m, &opaSessionObj.Report.SummaryDetails) pdfPrinter.printFinalResult(m, &opaSessionObj.Report.SummaryDetails) @@ -115,7 +115,7 @@ func (pdfPrinter *PdfPrinter) printHeader(m pdf.Maroto) { } // Print pdf frameworks after pdf header. -func (pdfPrinter *PdfPrinter) printFramework(m pdf.Maroto, frameworks []reportsummary.IPolicies) { +func (pdfPrinter *PdfPrinter) printFramework(m pdf.Maroto, frameworks []reportsummary.IFrameworkSummary) { m.Row(10, func() { m.Text(frameworksScoresToString(frameworks), props.Text{ Align: consts.Center, diff --git a/core/pkg/resultshandling/printer/v2/prettyprinter.go b/core/pkg/resultshandling/printer/v2/prettyprinter.go index a2fe184e..30ad8626 100644 --- a/core/pkg/resultshandling/printer/v2/prettyprinter.go +++ b/core/pkg/resultshandling/printer/v2/prettyprinter.go @@ -75,7 +75,7 @@ func (prettyPrinter *PrettyPrinter) printSummary(controlName string, controlSumm } func (prettyPrinter *PrettyPrinter) printTitle(controlSummary reportsummary.IControlSummary) { - cautils.InfoDisplay(prettyPrinter.writer, "[control: %s - %s] ", controlSummary.GetName(), getControlURL(controlSummary.GetID())) + cautils.InfoDisplay(prettyPrinter.writer, "[control: %s - %s] ", controlSummary.GetName(), getControlLink(controlSummary.GetID())) switch controlSummary.GetStatus().Status() { case apis.StatusSkipped: cautils.InfoDisplay(prettyPrinter.writer, "skipped %v\n", emoji.ConfusedFace) @@ -188,10 +188,10 @@ func (prettyPrinter *PrettyPrinter) printSummaryTable(summaryDetails *reportsumm summaryTable.Render() // For control scan framework will be nil - cautils.InfoTextDisplay(prettyPrinter.writer, frameworksScoresToString(summaryDetails.ListFrameworks().All())) + cautils.InfoTextDisplay(prettyPrinter.writer, frameworksScoresToString(summaryDetails.ListFrameworks())) } -func frameworksScoresToString(frameworks []reportsummary.IPolicies) string { +func frameworksScoresToString(frameworks []reportsummary.IFrameworkSummary) string { if len(frameworks) == 1 { if frameworks[0].GetName() != "" { return fmt.Sprintf("FRAMEWORK %s\n", frameworks[0].GetName()) @@ -217,6 +217,6 @@ func frameworksScoresToString(frameworks []reportsummary.IPolicies) string { // sort.Strings(controlNames) // return controlNames // } -func getControlURL(controlID string) string { +func getControlLink(controlID string) string { return fmt.Sprintf("https://hub.armo.cloud/docs/%s", strings.ToLower(controlID)) } diff --git a/core/pkg/resultshandling/printer/v2/prometheus.go b/core/pkg/resultshandling/printer/v2/prometheus.go new file mode 100644 index 00000000..d2efd109 --- /dev/null +++ b/core/pkg/resultshandling/printer/v2/prometheus.go @@ -0,0 +1,55 @@ +package v2 + +import ( + "fmt" + "os" + + "github.com/armosec/k8s-interface/workloadinterface" + "github.com/armosec/kubescape/core/cautils" + "github.com/armosec/kubescape/core/cautils/logger" + "github.com/armosec/kubescape/core/cautils/logger/helpers" + "github.com/armosec/kubescape/core/pkg/resultshandling/printer" + "github.com/armosec/opa-utils/reporthandling/results/v1/reportsummary" + "github.com/armosec/opa-utils/reporthandling/results/v1/resourcesresults" +) + +type PrometheusPrinter struct { + writer *os.File + verboseMode bool +} + +func NewPrometheusPrinter(verboseMode bool) *PrometheusPrinter { + return &PrometheusPrinter{ + verboseMode: verboseMode, + } +} + +func (prometheusPrinter *PrometheusPrinter) SetWriter(outputFile string) { + prometheusPrinter.writer = printer.GetWriter(outputFile) +} + +func (prometheusPrinter *PrometheusPrinter) Score(score float32) { + fmt.Printf("\n# Overall risk-score (0- Excellent, 100- All failed)\nkubescape_score %d\n", int(score)) +} + +func (printer *PrometheusPrinter) generatePrometheusFormat( + resources map[string]workloadinterface.IMetadata, + results map[string]resourcesresults.Result, + summaryDetails *reportsummary.SummaryDetails) *Metrics { + + m := &Metrics{} + m.setRiskScores(summaryDetails) + m.setResourcesCounters(resources, results) + + return m +} + +func (printer *PrometheusPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) { + + metrics := printer.generatePrometheusFormat(opaSessionObj.AllResources, opaSessionObj.ResourcesResult, &opaSessionObj.Report.SummaryDetails) + + logOUtputFile(printer.writer.Name()) + if _, err := printer.writer.Write([]byte(metrics.String())); err != nil { + logger.L().Error("failed to write results", helpers.Error(err)) + } +} diff --git a/core/pkg/resultshandling/printer/v2/prometheusutils.go b/core/pkg/resultshandling/printer/v2/prometheusutils.go new file mode 100644 index 00000000..df9e199b --- /dev/null +++ b/core/pkg/resultshandling/printer/v2/prometheusutils.go @@ -0,0 +1,248 @@ +package v2 + +import ( + "fmt" + + "github.com/armosec/k8s-interface/workloadinterface" + "github.com/armosec/opa-utils/reporthandling/apis" + "github.com/armosec/opa-utils/reporthandling/results/v1/reportsummary" + "github.com/armosec/opa-utils/reporthandling/results/v1/resourcesresults" +) + +type metricsName string + +const ( + metricsFrameworkScore metricsName = "kubescape_risk_score_framework" + metricsControlScore metricsName = "kubescape_risk_score_control" + metricsScore metricsName = "kubescape_risk_score" + metricsresourceFailed metricsName = "kubescape_resource_controls_number_of_failed" + metricsresourcePassed metricsName = "kubescape_resource_controls_number_of_passed" + metricsresourceExcluded metricsName = "kubescape_resource_controls_number_of_exclude" +) + +func (mrs *mRiskScore) string() string { + r := fmt.Sprintf("resourcesCountPassed: \"%d\"", mrs.resourcesCountPassed) + ", " + r += fmt.Sprintf("resourcesCountFailed: \"%d\"", mrs.resourcesCountFailed) + ", " + r += fmt.Sprintf("resourcesCountExcluded: \"%d\"", mrs.resourcesCountExcluded) + ", " + r += fmt.Sprintf("controlsCountPassed: \"%d\"", mrs.controlsCountPassed) + ", " + r += fmt.Sprintf("controlsCountExcluded: \"%d\"", mrs.controlsCountExcluded) + ", " + r += fmt.Sprintf("controlsCountSkipped: \"%d\"", mrs.controlsCountSkipped) + ", " + r += fmt.Sprintf("controlsCountFailed: \"%d\"", mrs.controlsCountFailed) + return r +} +func (mrs *mRiskScore) value() int { + return mrs.riskScore +} + +func (mcrs *mControlRiskScore) string() string { + r := fmt.Sprintf("controlName: \"%s\"", mcrs.controlName) + ", " + r += fmt.Sprintf("controlID: \"%s\"", mcrs.controlID) + ", " + r += fmt.Sprintf("link: \"%s\"", mcrs.link) + ", " + r += fmt.Sprintf("severity: \"%s\"", mcrs.severity) + ", " + r += fmt.Sprintf("remediation: \"%s\"", mcrs.remediation) + ", " + r += fmt.Sprintf("resourcesCountPassed: \"%d\"", mcrs.resourcesCountPassed) + ", " + r += fmt.Sprintf("resourcesCountFailed: \"%d\"", mcrs.resourcesCountFailed) + ", " + r += fmt.Sprintf("resourcesCountExcluded: \"%d\"", mcrs.resourcesCountExcluded) + return r +} +func (mcrs *mControlRiskScore) value() int { + return mcrs.riskScore +} + +func (mfrs *mFrameworkRiskScore) string() string { + r := fmt.Sprintf("frameworkName: \"%s\"", mfrs.frameworkName) + ", " + r += fmt.Sprintf("resourcesCountPassed: \"%d\"", mfrs.resourcesCountPassed) + ", " + r += fmt.Sprintf("resourcesCountFailed: \"%d\"", mfrs.resourcesCountFailed) + ", " + r += fmt.Sprintf("resourcesCountExcluded: \"%d\"", mfrs.resourcesCountExcluded) + ", " + r += fmt.Sprintf("controlsCountPassed: \"%d\"", mfrs.controlsCountPassed) + ", " + r += fmt.Sprintf("controlsCountExcluded: \"%d\"", mfrs.controlsCountExcluded) + ", " + r += fmt.Sprintf("controlsCountSkipped: \"%d\"", mfrs.controlsCountSkipped) + ", " + r += fmt.Sprintf("controlsCountFailed: \"%d\"", mfrs.controlsCountFailed) + return r +} +func (mfrs *mFrameworkRiskScore) value() int { + return mfrs.riskScore +} +func (mrc *mResourceControls) string() string { + r := fmt.Sprintf("name: \"%s\"", mrc.name) + ", " + r += fmt.Sprintf("controlID: \"%s\"", mrc.namespace) + ", " + r += fmt.Sprintf("link: \"%s\"", mrc.apiVersion) + ", " + r += fmt.Sprintf("severity: \"%s\"", mrc.kind) + return r +} +func (mrc *mResourceControls) value() int { + return mrc.controls +} +func toRowInMetrics(name metricsName, row string, value int) string { + return fmt.Sprintf("%s{%s} %d\n", name, row, value) + +} +func (m *Metrics) String() string { + + r := toRowInMetrics(metricsScore, m.rs.string(), m.rs.value()) + for i := range m.listControls { + r += toRowInMetrics(metricsScore, m.listControls[i].string(), m.listControls[i].value()) + } + for i := range m.listFrameworks { + r += toRowInMetrics(metricsScore, m.listFrameworks[i].string(), m.listFrameworks[i].value()) + } + for i := range m.listResourcesControlsExcluded { + r += toRowInMetrics(metricsScore, m.listResourcesControlsExcluded[i].string(), m.listResourcesControlsExcluded[i].value()) + } + for i := range m.listResourcesControlsFiled { + r += toRowInMetrics(metricsScore, m.listResourcesControlsFiled[i].string(), m.listResourcesControlsFiled[i].value()) + } + for i := range m.listResourcesControlsPassed { + r += toRowInMetrics(metricsScore, m.listResourcesControlsPassed[i].string(), m.listResourcesControlsPassed[i].value()) + } + return r +} + +type mRiskScore struct { + resourcesCountPassed int + resourcesCountFailed int + resourcesCountExcluded int + controlsCountPassed int + controlsCountFailed int + controlsCountExcluded int + controlsCountSkipped int + riskScore int // metric +} + +type mControlRiskScore struct { + controlName string + controlID string + link string + severity string + remediation string + resourcesCountPassed int + resourcesCountFailed int + resourcesCountExcluded int + riskScore int // metric +} + +type mFrameworkRiskScore struct { + frameworkName string + resourcesCountPassed int + resourcesCountFailed int + resourcesCountExcluded int + controlsCountPassed int + controlsCountFailed int + controlsCountExcluded int + controlsCountSkipped int + riskScore int // metric +} + +type mResourceControls struct { + name string + namespace string + apiVersion string + kind string + controls int // metric +} +type Metrics struct { + rs mRiskScore + listFrameworks []mFrameworkRiskScore + listControls []mControlRiskScore + listResourcesControlsFiled []mResourceControls + listResourcesControlsPassed []mResourceControls + listResourcesControlsExcluded []mResourceControls +} + +func (mrs *mRiskScore) set(resources reportsummary.ICounters, controls reportsummary.ICounters) { + mrs.resourcesCountExcluded = resources.Excluded() + mrs.resourcesCountFailed = resources.Failed() + mrs.resourcesCountPassed = resources.Passed() + mrs.controlsCountExcluded = controls.Excluded() + mrs.controlsCountFailed = controls.Failed() + mrs.controlsCountPassed = controls.Passed() + mrs.controlsCountSkipped = controls.Skipped() +} + +func (mfrs *mFrameworkRiskScore) set(resources reportsummary.ICounters, controls reportsummary.ICounters) { + mfrs.resourcesCountExcluded = resources.Excluded() + mfrs.resourcesCountFailed = resources.Failed() + mfrs.resourcesCountPassed = resources.Passed() + mfrs.controlsCountExcluded = controls.Excluded() + mfrs.controlsCountFailed = controls.Failed() + mfrs.controlsCountPassed = controls.Passed() + mfrs.controlsCountSkipped = controls.Skipped() +} + +func (mcrs *mControlRiskScore) set(resources reportsummary.ICounters) { + mcrs.resourcesCountExcluded = resources.Excluded() + mcrs.resourcesCountFailed = resources.Failed() + mcrs.resourcesCountPassed = resources.Passed() +} +func (m *Metrics) setRiskScores(summaryDetails *reportsummary.SummaryDetails) { + m.rs.set(summaryDetails.NumberOfResources(), summaryDetails.NumberOfControls()) + m.rs.riskScore = int(summaryDetails.GetScore()) + + for _, fw := range summaryDetails.ListFrameworks() { + mfrs := mFrameworkRiskScore{ + frameworkName: fw.GetName(), + riskScore: int(fw.GetScore()), + } + mfrs.set(fw.NumberOfResources(), fw.NumberOfControls()) + m.listFrameworks = append(m.listFrameworks, mfrs) + } + + for _, control := range summaryDetails.ListControls() { + mcrs := mControlRiskScore{ + controlName: control.GetName(), + controlID: control.GetID(), + riskScore: int(control.GetScore()), + link: getControlLink(control.GetID()), + severity: apis.ControlSeverityToString(control.GetScoreFactor()), + remediation: control.GetRemediation(), + } + mcrs.set(control.NumberOfResources()) + m.listControls = append(m.listControls, mcrs) + } +} + +// return -> (passed, exceluded, failed) +func resourceControlStatusCounters(result *resourcesresults.Result) (int, int, int) { + failed := 0 + excluded := 0 + passed := 0 + for i := range result.ListControls() { + switch result.ListControls()[i].GetStatus(nil).Status() { + case apis.StatusExcluded: + excluded++ + case apis.StatusFailed: + failed++ + case apis.StatusPassed: + passed++ + } + } + return passed, excluded, failed +} +func (m *Metrics) setResourcesCounters( + resources map[string]workloadinterface.IMetadata, + results map[string]resourcesresults.Result) { + + for resourceID, result := range results { + r, ok := resources[resourceID] + if !ok { + continue + } + passed, excluded, failed := resourceControlStatusCounters(&result) + + mrc := mResourceControls{} + mrc.apiVersion = r.GetApiVersion() + mrc.namespace = r.GetNamespace() + mrc.kind = r.GetKind() + mrc.name = r.GetName() + + // append + mrc.controls = passed + m.listResourcesControlsPassed = append(m.listResourcesControlsPassed, mrc) + + mrc.controls = failed + m.listResourcesControlsFiled = append(m.listResourcesControlsFiled, mrc) + + mrc.controls = excluded + m.listResourcesControlsExcluded = append(m.listResourcesControlsExcluded, mrc) + } +} diff --git a/httphandler/examples/prometheus/README.md b/httphandler/examples/prometheus/README.md index aa892ceb..0b44de39 100644 --- a/httphandler/examples/prometheus/README.md +++ b/httphandler/examples/prometheus/README.md @@ -10,7 +10,8 @@ ```bash helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update - helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack --set prometheus.prometheusSpec.podMonitorSelectorNilUsesHelmValues=false,prometheus.prometheusSpec.serviceMonitorSelectorNilUsesHelmValues=false + kubectl create namescape prometheus + helm install -n prometheus kube-prometheus-stack prometheus-community/kube-prometheus-stack --set prometheus.prometheusSpec.podMonitorSelectorNilUsesHelmValues=false,prometheus.prometheusSpec.serviceMonitorSelectorNilUsesHelmValues=false ``` 3. Deploy pod monitor ```bash diff --git a/httphandler/examples/prometheus/podmonitor.yaml b/httphandler/examples/prometheus/podmonitor.yaml index 1fe3f487..d036b9f4 100644 --- a/httphandler/examples/prometheus/podmonitor.yaml +++ b/httphandler/examples/prometheus/podmonitor.yaml @@ -11,5 +11,6 @@ spec: app: kubescape podMetricsEndpoints: - port: http + # path: v1 interval: 120s - scrapeTimeout: 120s + scrapeTimeout: 100s diff --git a/httphandler/handlerequests/v1/prometheus.go b/httphandler/handlerequests/v1/prometheus.go index bb89cac5..7de3edbe 100644 --- a/httphandler/handlerequests/v1/prometheus.go +++ b/httphandler/handlerequests/v1/prometheus.go @@ -6,17 +6,21 @@ import ( "os" "github.com/armosec/kubescape/core/cautils" + "github.com/armosec/kubescape/core/cautils/getter" "github.com/armosec/kubescape/core/cautils/logger" "github.com/armosec/kubescape/core/cautils/logger/helpers" "github.com/armosec/kubescape/core/core" + pkgcautils "github.com/armosec/utils-go/utils" "github.com/google/uuid" ) // Metrics http listener for prometheus support func (handler *HTTPHandler) Metrics(w http.ResponseWriter, r *http.Request) { if handler.state.isBusy() { // if already scanning the cluster - w.Write([]byte(fmt.Sprintf("scan '%s' in action", handler.state.getID()))) + message := fmt.Sprintf("scan '%s' in action", handler.state.getID()) + logger.L().Info("server is busy", helpers.String("message", message), helpers.Time()) w.WriteHeader(http.StatusServiceUnavailable) + w.Write([]byte(message)) return } @@ -29,14 +33,13 @@ func (handler *HTTPHandler) Metrics(w http.ResponseWriter, r *http.Request) { logger.L().Info(handler.state.getID(), helpers.String("action", "triggering scan"), helpers.Time()) ks := core.NewKubescape() results, err := ks.Scan(getPrometheusDefaultScanCommand(handler.state.getID(), resultsFile)) - results.HandleResults() - logger.L().Info(handler.state.getID(), helpers.String("action", "done scanning"), helpers.Time()) - if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(fmt.Sprintf("failed to complete scan. reason: %s", err.Error()))) return } + results.HandleResults() + logger.L().Info(handler.state.getID(), helpers.String("action", "done scanning"), helpers.Time()) f, err := os.ReadFile(resultsFile) // res, err := results.ToJson() @@ -45,6 +48,7 @@ func (handler *HTTPHandler) Metrics(w http.ResponseWriter, r *http.Request) { w.Write([]byte(fmt.Sprintf("failed read results from file. reason: %s", err.Error()))) return } + os.Remove(resultsFile) w.WriteHeader(http.StatusOK) w.Write(f) @@ -60,5 +64,15 @@ func getPrometheusDefaultScanCommand(scanID, resultsFile string) *cautils.ScanIn scanInfo.Format = "prometheus" // results format scanInfo.Output = resultsFile // results output scanInfo.Local = true // Do not publish results to Kubescape SaaS + if !downloadArtifactsEveryScan() { + scanInfo.UseArtifactsFrom = getter.DefaultLocalStore // Load files from cache (this will prevent kubescape fom downloading the artifacts every time) + } + scanInfo.Init() return &scanInfo } +func downloadArtifactsEveryScan() bool { + if d, ok := os.LookupEnv("KS_DOWNLOAD_ARTIFACTS"); ok { + return pkgcautils.StringToBool(d) + } + return false +} diff --git a/httphandler/listener/setup.go b/httphandler/listener/setup.go index 8a981149..1689d72f 100644 --- a/httphandler/listener/setup.go +++ b/httphandler/listener/setup.go @@ -6,6 +6,7 @@ import ( "net/http" "os" + "github.com/armosec/kubescape/core/cautils" "github.com/armosec/kubescape/core/cautils/logger" "github.com/armosec/kubescape/core/cautils/logger/helpers" handlerequestsv1 "github.com/armosec/kubescape/httphandler/handlerequests/v1" @@ -47,7 +48,7 @@ func SetupHTTPListener() error { server.Handler = rtr - logger.L().Info("Started Kubescape server", helpers.String("port", getPort())) + logger.L().Info("Started Kubescape server", helpers.String("port", getPort()), helpers.String("version", cautils.BuildNumber)) server.ListenAndServe() if keyPair != nil { return server.ListenAndServeTLS("", "") From 9d957b3c7726920c065b0a4eaae24f071a97ea43 Mon Sep 17 00:00:00 2001 From: dwertent Date: Tue, 15 Mar 2022 17:04:59 +0200 Subject: [PATCH 2/2] update output --- cmd/go.mod | 2 +- cmd/go.sum | 4 +- .../printer/v2/prometheusutils.go | 66 ++++++++++--------- core/pkg/resultshandling/results.go | 2 +- httphandler/go.mod | 4 +- httphandler/go.sum | 4 +- 6 files changed, 43 insertions(+), 39 deletions(-) diff --git a/cmd/go.mod b/cmd/go.mod index 0f1cef32..8ef4e8bd 100644 --- a/cmd/go.mod +++ b/cmd/go.mod @@ -7,7 +7,7 @@ replace github.com/armosec/kubescape/core => ../core require ( github.com/armosec/k8s-interface v0.0.68 github.com/armosec/kubescape/core v0.0.0-00010101000000-000000000000 - github.com/armosec/opa-utils v0.0.116 + github.com/armosec/opa-utils v0.0.118 github.com/armosec/rbac-utils v0.0.14 github.com/google/uuid v1.3.0 github.com/mattn/go-isatty v0.0.14 diff --git a/cmd/go.sum b/cmd/go.sum index 669e1723..ab369692 100644 --- a/cmd/go.sum +++ b/cmd/go.sum @@ -109,8 +109,8 @@ github.com/armosec/k8s-interface v0.0.66/go.mod h1:vwprS8qn/iowd5yf0JHpqDsLA5I8W github.com/armosec/k8s-interface v0.0.68 h1:6CtSakISiI47YHkxh+Va9FzZQIBkWa6g9sbiNxq1Zkk= github.com/armosec/k8s-interface v0.0.68/go.mod h1:PeWn41C2uenZi+xfZdyFF/zG5wXACA00htQyknDUWDE= github.com/armosec/opa-utils v0.0.64/go.mod h1:6tQP8UDq2EvEfSqh8vrUdr/9QVSCG4sJfju1SXQOn4c= -github.com/armosec/opa-utils v0.0.116 h1:3oWuhcpI+MJD/CktEStU1BA0feGNwsCbQrI3ifVfzMs= -github.com/armosec/opa-utils v0.0.116/go.mod h1:gap+EaLG5rnyqvIRGxtdNDC9y7VvoGNm90zK8Ls7avQ= +github.com/armosec/opa-utils v0.0.118 h1:ZX1crwVQmo+sDv+jmTNLbDYfApUBzlgPhD8QI2GCJX0= +github.com/armosec/opa-utils v0.0.118/go.mod h1:gap+EaLG5rnyqvIRGxtdNDC9y7VvoGNm90zK8Ls7avQ= github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40= github.com/armosec/rbac-utils v0.0.14 h1:CKYKcgqJEXWF2Hen/B1pVGtS3nDAG1wp9dDv6oNtq90= github.com/armosec/rbac-utils v0.0.14/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0= diff --git a/core/pkg/resultshandling/printer/v2/prometheusutils.go b/core/pkg/resultshandling/printer/v2/prometheusutils.go index df9e199b..0d1537e4 100644 --- a/core/pkg/resultshandling/printer/v2/prometheusutils.go +++ b/core/pkg/resultshandling/printer/v2/prometheusutils.go @@ -21,13 +21,13 @@ const ( ) func (mrs *mRiskScore) string() string { - r := fmt.Sprintf("resourcesCountPassed: \"%d\"", mrs.resourcesCountPassed) + ", " - r += fmt.Sprintf("resourcesCountFailed: \"%d\"", mrs.resourcesCountFailed) + ", " + r := fmt.Sprintf("resourcesCountFailed: \"%d\"", mrs.resourcesCountFailed) + ", " r += fmt.Sprintf("resourcesCountExcluded: \"%d\"", mrs.resourcesCountExcluded) + ", " - r += fmt.Sprintf("controlsCountPassed: \"%d\"", mrs.controlsCountPassed) + ", " + r += fmt.Sprintf("resourcesCountPassed: \"%d\"", mrs.resourcesCountPassed) + ", " + r += fmt.Sprintf("controlsCountFailed: \"%d\"", mrs.controlsCountFailed) + ", " r += fmt.Sprintf("controlsCountExcluded: \"%d\"", mrs.controlsCountExcluded) + ", " + r += fmt.Sprintf("controlsCountPassed: \"%d\"", mrs.controlsCountPassed) + ", " r += fmt.Sprintf("controlsCountSkipped: \"%d\"", mrs.controlsCountSkipped) + ", " - r += fmt.Sprintf("controlsCountFailed: \"%d\"", mrs.controlsCountFailed) return r } func (mrs *mRiskScore) value() int { @@ -37,12 +37,12 @@ func (mrs *mRiskScore) value() int { func (mcrs *mControlRiskScore) string() string { r := fmt.Sprintf("controlName: \"%s\"", mcrs.controlName) + ", " r += fmt.Sprintf("controlID: \"%s\"", mcrs.controlID) + ", " - r += fmt.Sprintf("link: \"%s\"", mcrs.link) + ", " r += fmt.Sprintf("severity: \"%s\"", mcrs.severity) + ", " - r += fmt.Sprintf("remediation: \"%s\"", mcrs.remediation) + ", " - r += fmt.Sprintf("resourcesCountPassed: \"%d\"", mcrs.resourcesCountPassed) + ", " r += fmt.Sprintf("resourcesCountFailed: \"%d\"", mcrs.resourcesCountFailed) + ", " - r += fmt.Sprintf("resourcesCountExcluded: \"%d\"", mcrs.resourcesCountExcluded) + r += fmt.Sprintf("resourcesCountExcluded: \"%d\"", mcrs.resourcesCountExcluded) + ", " + r += fmt.Sprintf("resourcesCountPassed: \"%d\"", mcrs.resourcesCountPassed) + ", " + r += fmt.Sprintf("link: \"%s\"", mcrs.link) + ", " + r += fmt.Sprintf("remediation: \"%s\"", mcrs.remediation) return r } func (mcrs *mControlRiskScore) value() int { @@ -51,23 +51,23 @@ func (mcrs *mControlRiskScore) value() int { func (mfrs *mFrameworkRiskScore) string() string { r := fmt.Sprintf("frameworkName: \"%s\"", mfrs.frameworkName) + ", " - r += fmt.Sprintf("resourcesCountPassed: \"%d\"", mfrs.resourcesCountPassed) + ", " r += fmt.Sprintf("resourcesCountFailed: \"%d\"", mfrs.resourcesCountFailed) + ", " r += fmt.Sprintf("resourcesCountExcluded: \"%d\"", mfrs.resourcesCountExcluded) + ", " - r += fmt.Sprintf("controlsCountPassed: \"%d\"", mfrs.controlsCountPassed) + ", " - r += fmt.Sprintf("controlsCountExcluded: \"%d\"", mfrs.controlsCountExcluded) + ", " - r += fmt.Sprintf("controlsCountSkipped: \"%d\"", mfrs.controlsCountSkipped) + ", " + r += fmt.Sprintf("resourcesCountPassed: \"%d\"", mfrs.resourcesCountPassed) + ", " r += fmt.Sprintf("controlsCountFailed: \"%d\"", mfrs.controlsCountFailed) + r += fmt.Sprintf("controlsCountExcluded: \"%d\"", mfrs.controlsCountExcluded) + ", " + r += fmt.Sprintf("controlsCountPassed: \"%d\"", mfrs.controlsCountPassed) + ", " + r += fmt.Sprintf("controlsCountSkipped: \"%d\"", mfrs.controlsCountSkipped) + ", " return r } func (mfrs *mFrameworkRiskScore) value() int { return mfrs.riskScore } func (mrc *mResourceControls) string() string { - r := fmt.Sprintf("name: \"%s\"", mrc.name) + ", " - r += fmt.Sprintf("controlID: \"%s\"", mrc.namespace) + ", " - r += fmt.Sprintf("link: \"%s\"", mrc.apiVersion) + ", " - r += fmt.Sprintf("severity: \"%s\"", mrc.kind) + r := fmt.Sprintf("apiVersion: \"%s\"", mrc.apiVersion) + ", " + r += fmt.Sprintf("kind: \"%s\"", mrc.kind) + ", " + r += fmt.Sprintf("namespace: \"%s\"", mrc.namespace) + ", " + r += fmt.Sprintf("name: \"%s\"", mrc.name) return r } func (mrc *mResourceControls) value() int { @@ -81,19 +81,19 @@ func (m *Metrics) String() string { r := toRowInMetrics(metricsScore, m.rs.string(), m.rs.value()) for i := range m.listControls { - r += toRowInMetrics(metricsScore, m.listControls[i].string(), m.listControls[i].value()) + r += toRowInMetrics(metricsControlScore, m.listControls[i].string(), m.listControls[i].value()) } for i := range m.listFrameworks { - r += toRowInMetrics(metricsScore, m.listFrameworks[i].string(), m.listFrameworks[i].value()) - } - for i := range m.listResourcesControlsExcluded { - r += toRowInMetrics(metricsScore, m.listResourcesControlsExcluded[i].string(), m.listResourcesControlsExcluded[i].value()) + r += toRowInMetrics(metricsFrameworkScore, m.listFrameworks[i].string(), m.listFrameworks[i].value()) } for i := range m.listResourcesControlsFiled { - r += toRowInMetrics(metricsScore, m.listResourcesControlsFiled[i].string(), m.listResourcesControlsFiled[i].value()) + r += toRowInMetrics(metricsresourceFailed, m.listResourcesControlsFiled[i].string(), m.listResourcesControlsFiled[i].value()) + } + for i := range m.listResourcesControlsExcluded { + r += toRowInMetrics(metricsresourceExcluded, m.listResourcesControlsExcluded[i].string(), m.listResourcesControlsExcluded[i].value()) } for i := range m.listResourcesControlsPassed { - r += toRowInMetrics(metricsScore, m.listResourcesControlsPassed[i].string(), m.listResourcesControlsPassed[i].value()) + r += toRowInMetrics(metricsresourcePassed, m.listResourcesControlsPassed[i].string(), m.listResourcesControlsPassed[i].value()) } return r } @@ -236,13 +236,17 @@ func (m *Metrics) setResourcesCounters( mrc.name = r.GetName() // append - mrc.controls = passed - m.listResourcesControlsPassed = append(m.listResourcesControlsPassed, mrc) - - mrc.controls = failed - m.listResourcesControlsFiled = append(m.listResourcesControlsFiled, mrc) - - mrc.controls = excluded - m.listResourcesControlsExcluded = append(m.listResourcesControlsExcluded, mrc) + if passed > 0 { + mrc.controls = passed + m.listResourcesControlsPassed = append(m.listResourcesControlsPassed, mrc) + } + if failed > 0 { + mrc.controls = failed + m.listResourcesControlsFiled = append(m.listResourcesControlsFiled, mrc) + } + if excluded > 0 { + mrc.controls = excluded + m.listResourcesControlsExcluded = append(m.listResourcesControlsExcluded, mrc) + } } } diff --git a/core/pkg/resultshandling/results.go b/core/pkg/resultshandling/results.go index e6a8edf0..5641a1d1 100644 --- a/core/pkg/resultshandling/results.go +++ b/core/pkg/resultshandling/results.go @@ -84,7 +84,7 @@ func NewPrinter(printFormat, formatVersion string, verboseMode bool) printer.IPr case printer.JunitResultFormat: return printerv2.NewJunitPrinter(verboseMode) case printer.PrometheusFormat: - return printerv1.NewPrometheusPrinter(verboseMode) + return printerv2.NewPrometheusPrinter(verboseMode) case printer.PdfFormat: return printerv2.NewPdfPrinter() default: diff --git a/httphandler/go.mod b/httphandler/go.mod index 10c5efed..2e036f0a 100644 --- a/httphandler/go.mod +++ b/httphandler/go.mod @@ -6,6 +6,7 @@ replace github.com/armosec/kubescape/core => ../core require ( github.com/armosec/kubescape/core v0.0.0-00010101000000-000000000000 + github.com/armosec/utils-go v0.0.3 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 ) @@ -22,9 +23,8 @@ require ( github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/armosec/armoapi-go v0.0.58 // indirect github.com/armosec/k8s-interface v0.0.68 // indirect - github.com/armosec/opa-utils v0.0.116 // indirect + github.com/armosec/opa-utils v0.0.118 // indirect github.com/armosec/rbac-utils v0.0.14 // indirect - github.com/armosec/utils-go v0.0.3 // indirect github.com/armosec/utils-k8s-go v0.0.3 // indirect github.com/aws/aws-sdk-go v1.41.11 // indirect github.com/aws/aws-sdk-go-v2 v1.12.0 // indirect diff --git a/httphandler/go.sum b/httphandler/go.sum index 0600d822..5193f2eb 100644 --- a/httphandler/go.sum +++ b/httphandler/go.sum @@ -109,8 +109,8 @@ github.com/armosec/k8s-interface v0.0.66/go.mod h1:vwprS8qn/iowd5yf0JHpqDsLA5I8W github.com/armosec/k8s-interface v0.0.68 h1:6CtSakISiI47YHkxh+Va9FzZQIBkWa6g9sbiNxq1Zkk= github.com/armosec/k8s-interface v0.0.68/go.mod h1:PeWn41C2uenZi+xfZdyFF/zG5wXACA00htQyknDUWDE= github.com/armosec/opa-utils v0.0.64/go.mod h1:6tQP8UDq2EvEfSqh8vrUdr/9QVSCG4sJfju1SXQOn4c= -github.com/armosec/opa-utils v0.0.116 h1:3oWuhcpI+MJD/CktEStU1BA0feGNwsCbQrI3ifVfzMs= -github.com/armosec/opa-utils v0.0.116/go.mod h1:gap+EaLG5rnyqvIRGxtdNDC9y7VvoGNm90zK8Ls7avQ= +github.com/armosec/opa-utils v0.0.118 h1:ZX1crwVQmo+sDv+jmTNLbDYfApUBzlgPhD8QI2GCJX0= +github.com/armosec/opa-utils v0.0.118/go.mod h1:gap+EaLG5rnyqvIRGxtdNDC9y7VvoGNm90zK8Ls7avQ= github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40= github.com/armosec/rbac-utils v0.0.14 h1:CKYKcgqJEXWF2Hen/B1pVGtS3nDAG1wp9dDv6oNtq90= github.com/armosec/rbac-utils v0.0.14/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0=