support threshold

This commit is contained in:
dwertent
2021-12-22 16:48:18 +02:00
parent 046a22bd2b
commit f5f5552ecd
11 changed files with 91 additions and 60 deletions
+1 -1
View File
@@ -91,7 +91,7 @@ Set-ExecutionPolicy RemoteSigned -scope CurrentUser
| `-e`/`--exclude-namespaces` | Scan all namespaces | Namespaces to exclude from scanning. Recommended to exclude `kube-system` and `kube-public` namespaces | |
| `--include-namespaces` | Scan all namespaces | Scan specific namespaces | |
| `-s`/`--silent` | Display progress messages | Silent progress messages | |
| `-t`/`--fail-threshold` | `0` (do not fail) | fail command (return exit code 1) if result is below threshold | `0` -> `100` |
| `-t`/`--fail-threshold` | `100` (do not fail) | fail command (return exit code 1) if result is above threshold | `0` -> `100` |
| `-f`/`--format` | `pretty-printer` | Output format | `pretty-printer`/`json`/`junit`/`prometheus` |
| `-o`/`--output` | print to stdout | Save scan result in file | |
| `--use-from` | | Load local framework object from specified path. If not used will download latest | |
+4 -2
View File
@@ -180,7 +180,9 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken
}
}
if c.configObj.ClusterName == "" {
c.configObj.ClusterName = adoptClusterName(k8sinterface.GetClusterName())
c.configObj.ClusterName = AdoptClusterName(k8sinterface.GetClusterName())
} else { // override the cluster name if it has unwanted characters
c.configObj.ClusterName = AdoptClusterName(c.configObj.ClusterName)
}
return c
@@ -425,6 +427,6 @@ func DeleteConfigFile() error {
return os.Remove(ConfigFileFullPath())
}
func adoptClusterName(clusterName string) string {
func AdoptClusterName(clusterName string) string {
return strings.ReplaceAll(clusterName, "/", "-")
}
+6 -3
View File
@@ -65,6 +65,9 @@ func NewVersionCheckRequest(buildNumber, frameworkName, frameworkVersion, scanni
if buildNumber == "" {
buildNumber = UnknownBuildNumber
}
if scanningTarget == "" {
scanningTarget = "unknown"
}
return &VersionCheckRequest{
Client: "kubescape",
ClientVersion: buildNumber,
@@ -82,7 +85,7 @@ func (v *VersionCheckHandlerMock) CheckLatestVersion(versionData *VersionCheckRe
func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckRequest) error {
defer func() {
if err := recover(); err != nil {
fmt.Println("failed to get latest version")
WarningDisplay(os.Stderr, "failed to get latest version\n")
}
}()
@@ -93,7 +96,7 @@ func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckReques
if latestVersion.ClientUpdate != "" {
if BuildNumber != "" && BuildNumber < latestVersion.ClientUpdate {
fmt.Println(warningMessage(latestVersion.Client, latestVersion.ClientUpdate))
WarningDisplay(os.Stderr, warningMessage(latestVersion.Client, latestVersion.ClientUpdate), "\n")
}
}
@@ -103,7 +106,7 @@ func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckReques
// }
if latestVersion.Message != "" {
fmt.Println(latestVersion.Message)
InfoDisplay(os.Stderr, latestVersion.Message, "\n")
}
return nil
+58 -36
View File
@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"os"
"strings"
"github.com/armosec/kubescape/cautils"
@@ -25,43 +26,10 @@ var downloadCmd = &cobra.Command{
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if strings.EqualFold(args[0], "framework") {
downloadInfo.FrameworkName = strings.ToLower(args[1])
g := getter.NewDownloadReleasedPolicy()
if err := g.SetRegoObjects(); err != nil {
return err
}
if downloadInfo.Path == "" {
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.FrameworkName + ".json")
}
frameworks, err := g.GetFramework(downloadInfo.FrameworkName)
if err != nil {
return err
}
err = getter.SaveFrameworkInFile(frameworks, downloadInfo.Path)
if err != nil {
return err
}
} else if strings.EqualFold(args[0], "control") {
downloadInfo.ControlName = strings.ToLower(args[1])
g := getter.NewDownloadReleasedPolicy()
if err := g.SetRegoObjects(); err != nil {
return err
}
if downloadInfo.Path == "" {
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.ControlName + ".json")
}
controls, err := g.GetControl(downloadInfo.ControlName)
if err != nil {
return err
}
err = getter.SaveControlInFile(controls, downloadInfo.Path)
if err != nil {
return err
}
if err := download(args); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
return nil
},
}
@@ -71,3 +39,57 @@ func init() {
downloadInfo = cautils.DownloadInfo{}
downloadCmd.Flags().StringVarP(&downloadInfo.Path, "output", "o", "", "Output file. If specified, will store save to `~/.kubescape/<framework name>.json`")
}
func download(args []string) error {
switch strings.ToLower(args[0]) {
case "framework":
return downloadFramework(args[1])
case "control":
return downloadControl(args[1])
// case "exceptions":
// case "artifacts":
default:
return fmt.Errorf("unknown command to download")
}
}
func downloadFramework(frameworkName string) error {
downloadInfo.FrameworkName = strings.ToLower(frameworkName)
g := getter.NewDownloadReleasedPolicy()
if err := g.SetRegoObjects(); err != nil {
return err
}
if downloadInfo.Path == "" {
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.FrameworkName + ".json")
}
frameworks, err := g.GetFramework(downloadInfo.FrameworkName)
if err != nil {
return err
}
err = getter.SaveFrameworkInFile(frameworks, downloadInfo.Path)
if err != nil {
return err
}
return nil
}
func downloadControl(controlName string) error {
downloadInfo.ControlName = strings.ToLower(controlName)
g := getter.NewDownloadReleasedPolicy()
if err := g.SetRegoObjects(); err != nil {
return err
}
if downloadInfo.Path == "" {
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.ControlName + ".json")
}
controls, err := g.GetControl(downloadInfo.ControlName)
if err != nil {
return err
}
err = getter.SaveControlInFile(controls, downloadInfo.Path)
if err != nil {
return err
}
return nil
}
+1 -1
View File
@@ -90,7 +90,7 @@ var frameworkCmd = &cobra.Command{
cautils.SetSilentMode(scanInfo.Silent)
err := clihandler.ScanCliSetup(&scanInfo)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
fmt.Fprintf(os.Stderr, "error: %v\n\n", err)
os.Exit(1)
}
return nil
+2 -2
View File
@@ -39,7 +39,7 @@ func init() {
scanCmd.PersistentFlags().StringVar(&scanInfo.ControlsInputs, "controls-config", "", "Path to an controls-config obj. If not set will download controls-config from ARMO management portal")
scanCmd.PersistentFlags().StringVar(&scanInfo.UseExceptions, "exceptions", "", "Path to an exceptions obj. If not set will download exceptions from ARMO management portal")
scanCmd.PersistentFlags().StringVarP(&scanInfo.ExcludedNamespaces, "exclude-namespaces", "e", "", "Namespaces to exclude from scanning. Recommended: kube-system,kube-public")
scanCmd.PersistentFlags().Uint16VarP(&scanInfo.FailThreshold, "fail-threshold", "t", 0, "Failure threshold is the percent below which the command fails and returns exit code 1")
scanCmd.PersistentFlags().Uint16VarP(&scanInfo.FailThreshold, "fail-threshold", "t", 100, "Failure threshold is the percent above which the command fails and returns exit code 1")
scanCmd.PersistentFlags().StringVarP(&scanInfo.Format, "format", "f", "pretty-printer", `Output format. Supported formats: "pretty-printer"/"json"/"junit"/"prometheus"`)
scanCmd.PersistentFlags().StringVar(&scanInfo.IncludeNamespaces, "include-namespaces", "", "scan specific namespaces. e.g: --include-namespaces ns-a,ns-b")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Local, "keep-local", "", false, "If you do not want your Kubescape results reported to Armo backend. Use this flag if you ran with the '--submit' flag in the past and you do not want to submit your current scan results")
@@ -50,7 +50,7 @@ func init() {
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Silent, "silent", "s", false, "Silent progress messages")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Submit, "submit", "", false, "Send the scan results to Armo management portal where you can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more. By default the results are not submitted")
hostF := scanCmd.PersistentFlags().VarPF(&scanInfo.HostSensor, "enable-host-scan", "", "Deploy ARMO K8s host-sensor daemonset in the scanned cluster. Deleting it right after we collecting the data. Required to collect valueable data from cluster nodes for certain controls")
hostF := scanCmd.PersistentFlags().VarPF(&scanInfo.HostSensor, "enable-host-scan", "", "Deploy ARMO K8s host-sensor daemonset in the scanned cluster. Deleting it right after we collecting the data. Required to collect valueable data from cluster nodes for certain controls")
hostF.NoOptDefVal = "true"
hostF.DefValue = "false, for no TTY in stdin"
}
+2 -3
View File
@@ -121,9 +121,8 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error {
// print report url
interfaces.report.DisplayReportURL()
adjustedFailThreshold := float32(scanInfo.FailThreshold) / 100
if score >= adjustedFailThreshold {
return fmt.Errorf("Scan score is below threshold")
if score >= float32(scanInfo.FailThreshold) {
return fmt.Errorf("scan risk-score %.2f is above permitted threshold %d", score, scanInfo.FailThreshold)
}
return nil
-1
View File
@@ -38,7 +38,6 @@ func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.Kubern
func getReporter(tenantConfig cautils.ITenantConfig, submit bool) reporter.IReport {
if submit {
return reporter.NewReportEventReceiver(tenantConfig.GetConfigObj())
}
return reporter.NewReportMock()
}
+1 -1
View File
@@ -67,9 +67,9 @@ func (opaHandler *OPAProcessorHandler) ProcessRulesListenner() {
opap.updateResults()
// update score
// opap.updateScore()
scoreutil := score.NewScore(opaSessionObj.AllResources)
scoreutil.Calculate(opaSessionObj.PostureReport.FrameworkReports)
// report
*opaHandler.reportResults <- opaSessionObj
}
+11 -9
View File
@@ -35,10 +35,12 @@ func (printer *PrettyPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj)
warningResources := []string{}
allResources := []string{}
frameworkNames := []string{}
frameworkScores := []float32{}
var overallRiskScore float32 = 0
for _, frameworkReport := range opaSessionObj.PostureReport.FrameworkReports {
frameworkNames = append(frameworkNames, frameworkReport.Name)
frameworkScores = append(frameworkScores, frameworkReport.Score)
failedResources = reporthandling.GetUniqueResourcesIDs(append(failedResources, frameworkReport.ListResourcesIDs().GetFailedResources()...))
warningResources = reporthandling.GetUniqueResourcesIDs(append(warningResources, frameworkReport.ListResourcesIDs().GetWarningResources()...))
allResources = reporthandling.GetUniqueResourcesIDs(append(allResources, frameworkReport.ListResourcesIDs().GetAllResources()...))
@@ -56,7 +58,7 @@ func (printer *PrettyPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj)
}
printer.printResults()
printer.printSummaryTable(frameworkNames)
printer.printSummaryTable(frameworkNames, frameworkScores)
}
@@ -218,9 +220,9 @@ func generateFooter(printer *PrettyPrinter) []string {
return row
}
func (printer *PrettyPrinter) printSummaryTable(frameworksNames []string) {
func (printer *PrettyPrinter) printSummaryTable(frameworksNames []string, frameworkScores []float32) {
// For control scan framework will be nil
printer.printFramework(frameworksNames)
printer.printFramework(frameworksNames, frameworkScores)
summaryTable := tablewriter.NewWriter(printer.writer)
summaryTable.SetAutoWrapText(false)
@@ -240,16 +242,16 @@ func (printer *PrettyPrinter) printSummaryTable(frameworksNames []string) {
summaryTable.Render()
}
func (printer *PrettyPrinter) printFramework(frameworksNames []string) {
func (printer *PrettyPrinter) printFramework(frameworksNames []string, frameworkScores []float32) {
if len(frameworksNames) == 1 {
cautils.InfoTextDisplay(printer.writer, fmt.Sprintf("%s FRAMEWORK\n", frameworksNames[0]))
cautils.InfoTextDisplay(printer.writer, fmt.Sprintf("FRAMEWORK %s\n", frameworksNames[0]))
} else if len(frameworksNames) > 1 {
p := ""
p := "FRAMEWORKS: "
for i := 0; i < len(frameworksNames)-1; i++ {
p += frameworksNames[i] + ", "
p += fmt.Sprintf("%s (risk: %.2f), ", frameworksNames[i], frameworkScores[i])
}
p += frameworksNames[len(frameworksNames)-1]
cautils.InfoTextDisplay(printer.writer, fmt.Sprintf("%s FRAMEWORKS\n", p))
p += fmt.Sprintf("%s (risk: %.2f)\n", frameworksNames[len(frameworksNames)-1], frameworkScores[len(frameworkScores)-1])
cautils.InfoTextDisplay(printer.writer, p)
}
}
@@ -43,6 +43,10 @@ func NewReportEventReceiver(tenantConfig *cautils.ConfigObj) *ReportEventReceive
func (report *ReportEventReceiver) ActionSendReport(opaSessionObj *cautils.OPASessionObj) error {
if report.customerGUID == "" || report.clusterName == "" {
return fmt.Errorf("missing accout ID or cluster name. AccountID: '%s', Cluster name: '%s'", report.customerGUID, report.clusterName)
}
if err := report.prepareReport(opaSessionObj.PostureReport, opaSessionObj.AllResources); err != nil {
return err
}
@@ -54,7 +58,7 @@ func (report *ReportEventReceiver) SetCustomerGUID(customerGUID string) {
}
func (report *ReportEventReceiver) SetClusterName(clusterName string) {
report.clusterName = clusterName
report.clusterName = cautils.AdoptClusterName(clusterName) // clean cluster name
}
func (report *ReportEventReceiver) prepareReport(postureReport *reporthandling.PostureReport, allResources map[string]workloadinterface.IMetadata) error {