From 8694a929cf271cd7be52d79da952bc260011119e Mon Sep 17 00:00:00 2001 From: dwertent Date: Thu, 24 Feb 2022 13:57:33 +0200 Subject: [PATCH] support output versions --- README.md | 5 ++- cautils/scaninfo.go | 1 + clihandler/cmd/framework.go | 11 +------ clihandler/cmd/scan.go | 35 ++++++++++++++++++--- clihandler/initcli.go | 2 +- resultshandling/printer/v2/prettyprinter.go | 17 +++++++--- resultshandling/printer/v2/resourcetable.go | 4 ++- resultshandling/results.go | 4 +-- 8 files changed, 54 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 309e161e..06e4cab8 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,8 @@ Want to contribute? Want to discuss something? Have an issue? # Options and examples +[Kubescape docs](https://hub.armo.cloud/docs) + ## Playground * [Kubescape playground](https://www.katacoda.com/pathaksaiyam/scenarios/kubescape) @@ -65,9 +67,10 @@ Want to contribute? Want to discuss something? Have an issue? * [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 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) +* [Configure and run customized frameworks](https://youtu.be/12Sanq_rEhs) ## Install on Windows diff --git a/cautils/scaninfo.go b/cautils/scaninfo.go index 0030fc36..9bef29b3 100644 --- a/cautils/scaninfo.go +++ b/cautils/scaninfo.go @@ -64,6 +64,7 @@ type ScanInfo struct { 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 + OutputVersion string // Output object can be differnet between versions, this is for testing and backward compatibility ExcludedNamespaces string // used for host sensor namespace IncludeNamespaces string // DEPRECATED? InputPatterns []string // Yaml files input patterns diff --git a/clihandler/cmd/framework.go b/clihandler/cmd/framework.go index f06c9c80..ad978473 100644 --- a/clihandler/cmd/framework.go +++ b/clihandler/cmd/framework.go @@ -16,7 +16,7 @@ import ( var ( frameworkExample = ` # Scan all frameworks and submit the results - kubescape scan --submit + kubescape scan framework all --submit # Scan the NSA framework kubescape scan framework nsa @@ -30,15 +30,6 @@ var ( # Scan kubernetes YAML manifest files kubescape scan framework nsa *.yaml - # Scan and save the results in the JSON format - kubescape scan --format json --output results.json - - # Save scan results in JSON format - kubescape scan --format json --output results.json - - # Display all resources - kubescape scan --verbose - Run 'kubescape list frameworks' for the list of supported frameworks ` ) diff --git a/clihandler/cmd/scan.go b/clihandler/cmd/scan.go index ef1587d2..55e7ead1 100644 --- a/clihandler/cmd/scan.go +++ b/clihandler/cmd/scan.go @@ -8,11 +8,32 @@ import ( var scanInfo cautils.ScanInfo +var scanCmdExamples = ` + Scan command is for scanning an existing cluster or kubernetes manifest files based on pre-defind frameworks + + # Scan current cluster with all frameworks + kubescape scan --submit --enable-host-scan + + # Scan kubernetes YAML manifest files + kubescape scan *.yaml + + # Scan and save the results in the JSON format + kubescape scan --format json --output results.json + + # Display all resources + kubescape scan --verbose + + # Scan different clusters from the kubectl context + kubescape scan --kube-context + +` + // scanCmd represents the scan command var scanCmd = &cobra.Command{ - Use: "scan [command]", - Short: "Scan the current running cluster or yaml files", - Long: `The action you want to perform`, + Use: "scan", + Short: "Scan the current running cluster or yaml files", + Long: `The action you want to perform`, + Example: scanCmdExamples, Args: func(cmd *cobra.Command, args []string) error { if len(args) > 0 { if args[0] != "framework" && args[0] != "control" { @@ -57,9 +78,13 @@ func init() { scanCmd.PersistentFlags().StringSliceVar(&scanInfo.UseFrom, "use-from", nil, "Load local policy object from specified path. If not used will download latest") scanCmd.PersistentFlags().BoolVarP(&scanInfo.Silent, "silent", "s", false, "Silent progress messages") scanCmd.PersistentFlags().BoolVarP(&scanInfo.Submit, "submit", "", false, "Send the scan results to Armo management portal where you can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more. By default the results are not submitted") - scanCmd.PersistentFlags().StringVar(&scanInfo.HostSensorYamlPath, "host-scan-yaml", "", "Override default host sensor DaemonSet. Use this flag cautiously") - scanCmd.PersistentFlags().MarkHidden("host-scan-yaml") + scanCmd.PersistentFlags().StringVar(&scanInfo.OutputVersion, "output-version", "v1", "Output object can be differnet between versions, this is for testing and backward compatibility") + + // hidden flags + scanCmd.PersistentFlags().MarkHidden("host-scan-yaml") // this flag should be used very cautiously. We prefer users will not use it at all unless the DaemoSet can not run pods on the nodes + scanCmd.PersistentFlags().MarkHidden("silent") // this flag should be deprecated since we added the --logger support + scanCmd.PersistentFlags().MarkHidden("output-version") // meant for testing different output approaches and not for common use hostF := scanCmd.PersistentFlags().VarPF(&scanInfo.HostSensorEnabled, "enable-host-scan", "", "Deploy ARMO K8s host-sensor daemonset in the scanned cluster. Deleting it right after we collecting the data. Required to collect valueable data from cluster nodes for certain controls") hostF.NoOptDefVal = "true" diff --git a/clihandler/initcli.go b/clihandler/initcli.go index a0dab3c3..0a96a51f 100644 --- a/clihandler/initcli.go +++ b/clihandler/initcli.go @@ -83,7 +83,7 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces { reportHandler := getReporter(tenantConfig, scanInfo.Submit) // setup printer - printerHandler := resultshandling.NewPrinter(scanInfo.Format, scanInfo.VerboseMode) + printerHandler := resultshandling.NewPrinter(scanInfo.Format, scanInfo.OutputVersion, scanInfo.VerboseMode) printerHandler.SetWriter(scanInfo.Output) // ================== return interface ====================================== diff --git a/resultshandling/printer/v2/prettyprinter.go b/resultshandling/printer/v2/prettyprinter.go index b380e680..75e7053b 100644 --- a/resultshandling/printer/v2/prettyprinter.go +++ b/resultshandling/printer/v2/prettyprinter.go @@ -17,21 +17,23 @@ import ( ) type PrettyPrinter struct { + outputVersion string writer *os.File verboseMode bool sortedControlNames []string } -func NewPrettyPrinter(verboseMode bool) *PrettyPrinter { +func NewPrettyPrinter(verboseMode bool, outputVersion string) *PrettyPrinter { return &PrettyPrinter{ - verboseMode: verboseMode, + verboseMode: verboseMode, + outputVersion: outputVersion, } } func (prettyPrinter *PrettyPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) { prettyPrinter.sortedControlNames = getSortedControlsNames(opaSessionObj.Report.SummaryDetails.Controls) // ListControls().All()) - // prettyPrinter.resourceTable(opaSessionObj.ResourcesResult, opaSessionObj.AllResources) + prettyPrinter.resourceTable(opaSessionObj.ResourcesResult, opaSessionObj.AllResources) prettyPrinter.printResults(&opaSessionObj.Report.SummaryDetails.Controls, opaSessionObj.AllResources) prettyPrinter.printSummaryTable(&opaSessionObj.Report.SummaryDetails) @@ -45,6 +47,10 @@ func (prettyPrinter *PrettyPrinter) Score(score float32) { } func (prettyPrinter *PrettyPrinter) printResults(controls *reportsummary.ControlSummaries, allResources map[string]workloadinterface.IMetadata) { + if prettyPrinter.outputVersion != "v1" { + return + } + for i := 0; i < len(prettyPrinter.sortedControlNames); i++ { controlSummary := controls.GetControl(reportsummary.EControlCriteriaName, prettyPrinter.sortedControlNames[i]) // summaryDetails.Controls ListControls().All() Controls.GetControl(ca) @@ -165,8 +171,6 @@ func generateFooter(summaryDetails *reportsummary.SummaryDetails) []string { return row } func (prettyPrinter *PrettyPrinter) printSummaryTable(summaryDetails *reportsummary.SummaryDetails) { - // For control scan framework will be nil - cautils.InfoTextDisplay(prettyPrinter.writer, frameworksScoresToString(summaryDetails.ListFrameworks().All())) summaryTable := tablewriter.NewWriter(prettyPrinter.writer) summaryTable.SetAutoWrapText(false) @@ -183,6 +187,9 @@ func (prettyPrinter *PrettyPrinter) printSummaryTable(summaryDetails *reportsumm // summaryTable.SetFooter(generateFooter()) summaryTable.Render() + + // For control scan framework will be nil + cautils.InfoTextDisplay(prettyPrinter.writer, frameworksScoresToString(summaryDetails.ListFrameworks().All())) } func frameworksScoresToString(frameworks []reportsummary.IPolicies) string { diff --git a/resultshandling/printer/v2/resourcetable.go b/resultshandling/printer/v2/resourcetable.go index bfaaa15e..cc291e8e 100644 --- a/resultshandling/printer/v2/resourcetable.go +++ b/resultshandling/printer/v2/resourcetable.go @@ -10,7 +10,9 @@ import ( ) func (prettyPrinter *PrettyPrinter) resourceTable(results map[string]resourcesresults.Result, allResources map[string]workloadinterface.IMetadata) { - + if prettyPrinter.outputVersion != "v2" { + return + } summaryTable := tablewriter.NewWriter(prettyPrinter.writer) summaryTable.SetAutoWrapText(true) summaryTable.SetAutoMergeCells(true) diff --git a/resultshandling/results.go b/resultshandling/results.go index 1736a9f8..f6f4917d 100644 --- a/resultshandling/results.go +++ b/resultshandling/results.go @@ -53,7 +53,7 @@ func CalculatePostureScore(postureReport *reporthandling.PostureReport) float32 return (float32(len(allResources)) - float32(len(failedResources))) / float32(len(allResources)) } -func NewPrinter(printFormat string, verboseMode bool) printer.IPrinter { +func NewPrinter(printFormat, outputVersion string, verboseMode bool) printer.IPrinter { switch printFormat { case printer.JsonFormat: @@ -65,6 +65,6 @@ func NewPrinter(printFormat string, verboseMode bool) printer.IPrinter { case printer.PdfFormat: return printerv2.NewPdfPrinter() default: - return printerv2.NewPrettyPrinter(verboseMode) + return printerv2.NewPrettyPrinter(verboseMode, outputVersion) } }