From 0cac7cb1a5768f6dc811fdf900cc6b518a883b80 Mon Sep 17 00:00:00 2001 From: Daniel-GrunbergerCA Date: Tue, 21 Dec 2021 09:23:38 +0200 Subject: [PATCH 01/19] fix kubeletcmd for marshalling --- go.mod | 2 +- go.sum | 4 ++-- hostsensorutils/hostsensorgetfrompod.go | 7 +++++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4f903f82..ac4d14f5 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.17 require ( github.com/armosec/armoapi-go v0.0.23 - github.com/armosec/k8s-interface v0.0.44 + github.com/armosec/k8s-interface v0.0.45 github.com/armosec/opa-utils v0.0.68 github.com/armosec/rbac-utils v0.0.9 github.com/armosec/utils-go v0.0.3 diff --git a/go.sum b/go.sum index b12f132f..21f29534 100644 --- a/go.sum +++ b/go.sum @@ -89,8 +89,8 @@ github.com/armosec/armoapi-go v0.0.23/go.mod h1:iaVVGyc23QGGzAdv4n+szGQg3Rbpixn9 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.43/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU= -github.com/armosec/k8s-interface v0.0.44 h1:UsmVPo0GyyBuTVIP+F8rgHpEaZqQE7sUYjj1DT3kHmc= -github.com/armosec/k8s-interface v0.0.44/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU= +github.com/armosec/k8s-interface v0.0.45 h1:LnncyBfarM29vXB0JwteSiLphVsiu7ONrNmmWSi377M= +github.com/armosec/k8s-interface v0.0.45/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.68 h1:qsmDjEVPdfJ6+hlSeoBZF5sgeR63HdtPfpVQIg0r1G0= github.com/armosec/opa-utils v0.0.68/go.mod h1:NVJEQ7695VyylqC7dHw2nD7MHIGJrRIXLgyej1onF64= diff --git a/hostsensorutils/hostsensorgetfrompod.go b/hostsensorutils/hostsensorgetfrompod.go index e6e9f480..2f77ba70 100644 --- a/hostsensorutils/hostsensorgetfrompod.go +++ b/hostsensorutils/hostsensorgetfrompod.go @@ -80,6 +80,13 @@ func (hsh *HostSensorHandler) sendAllPodsHTTPGETRequest(path, requestKind string hostSensorDataEnvelope.SetApiVersion(k8sinterface.JoinGroupVersion(hostsensor.GroupHostSensor, hostsensor.Version)) hostSensorDataEnvelope.SetKind(requestKind) hostSensorDataEnvelope.SetName(podList[podName]) + if requestKind == "KubeletCommandLine" { + data := map[string]interface{}{"fullCommand": string(resBytes)} + resBytesMarshal, err := json.Marshal(data) + if err == nil { + resBytes = resBytesMarshal + } + } hostSensorDataEnvelope.SetData(resBytes) res = append(res, hostSensorDataEnvelope) } From a1c34646f1ca3f30b0300f84661b79d37c20c95c Mon Sep 17 00:00:00 2001 From: Daniel-GrunbergerCA Date: Tue, 21 Dec 2021 13:34:31 +0200 Subject: [PATCH 02/19] waning for host sensor --- clihandler/initcliutils.go | 3 +- hostsensorutils/hostsensorgetfrompod.go | 44 ++++++++++++++++++++----- resourcehandler/k8sresources.go | 2 +- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/clihandler/initcliutils.go b/clihandler/initcliutils.go index 55a35b4e..9521d937 100644 --- a/clihandler/initcliutils.go +++ b/clihandler/initcliutils.go @@ -58,11 +58,12 @@ func getHostSensorHandler(scanInfo *cautils.ScanInfo, k8s *k8sinterface.Kubernet // we need to determined which controls needs host sensor if scanInfo.HostSensor.Get() == nil && hasHostSensorControls { scanInfo.HostSensor.SetBool(askUserForHostSensor()) + cautils.WarningDisplay(os.Stdout, "Warning: K8S nodes scanning is disabled. This is required to collect valuable data for certain controls. You can enable it using the --enable-host-scan flag\n") } if hostSensorVal := scanInfo.HostSensor.Get(); hostSensorVal != nil && *hostSensorVal { hostSensorHandler, err := hostsensorutils.NewHostSensorHandler(k8s) if err != nil { - glog.Errorf("failed to create host sensor: %v", err) + cautils.WarningDisplay(os.Stdout, fmt.Sprintf("Warning: failed to create host sensor: %v\n", err.Error())) return &hostsensorutils.HostSensorHandlerMock{} } return hostSensorHandler diff --git a/hostsensorutils/hostsensorgetfrompod.go b/hostsensorutils/hostsensorgetfrompod.go index 2f77ba70..c82f3d33 100644 --- a/hostsensorutils/hostsensorgetfrompod.go +++ b/hostsensorutils/hostsensorgetfrompod.go @@ -3,6 +3,7 @@ package hostsensorutils import ( "encoding/json" "fmt" + "strings" "sync" "github.com/armosec/k8s-interface/k8sinterface" @@ -80,13 +81,6 @@ func (hsh *HostSensorHandler) sendAllPodsHTTPGETRequest(path, requestKind string hostSensorDataEnvelope.SetApiVersion(k8sinterface.JoinGroupVersion(hostsensor.GroupHostSensor, hostsensor.Version)) hostSensorDataEnvelope.SetKind(requestKind) hostSensorDataEnvelope.SetName(podList[podName]) - if requestKind == "KubeletCommandLine" { - data := map[string]interface{}{"fullCommand": string(resBytes)} - resBytesMarshal, err := json.Marshal(data) - if err == nil { - resBytes = resBytesMarshal - } - } hostSensorDataEnvelope.SetData(resBytes) res = append(res, hostSensorDataEnvelope) } @@ -112,7 +106,41 @@ func (hsh *HostSensorHandler) GetLinuxSecurityHardeningStatus() ([]hostsensor.Ho // return list of KubeletCommandLine func (hsh *HostSensorHandler) GetKubeletCommandLine() ([]hostsensor.HostSensorDataEnvelope, error) { // loop over pods and port-forward it to each of them - return hsh.sendAllPodsHTTPGETRequest("/kubeletCommandLine", "KubeletCommandLine") + resps, err := hsh.sendAllPodsHTTPGETRequest("/kubeletCommandLine", "KubeletCommandLine") + if err != nil { + return resps, err + } + for resp := range resps { + data := makeCmdLineMap(string(resps[resp].Data)) + resBytesMarshal, err := json.Marshal(data) + // TODO catch error + if err == nil { + resps[resp].Data = json.RawMessage(resBytesMarshal) + } + } + + return resps, nil + +} + +func makeCmdLineMap(fullCommand string) map[string]interface{} { + + commands := strings.Split(fullCommand, " ") + if len(commands) < 1 { + return nil + } + command := commands[0] + var cmdMap = make(map[string]interface{}) + cmdMap["command"] = command + for _, cmd := range commands { + splitted := strings.Split(cmd, "=") + if len(splitted) == 2 { + if strings.HasPrefix(splitted[0], "--") { + cmdMap[splitted[0][2:]] = splitted[1] + } + } + } + return cmdMap } // return list of diff --git a/resourcehandler/k8sresources.go b/resourcehandler/k8sresources.go index 4bcc499f..5e56a696 100644 --- a/resourcehandler/k8sresources.go +++ b/resourcehandler/k8sresources.go @@ -59,7 +59,7 @@ func (k8sHandler *K8sResourceHandler) GetResources(frameworks []reporthandling.F return k8sResourcesMap, allResources, err } if err := k8sHandler.collectHostResources(allResources, k8sResourcesMap); err != nil { - return k8sResourcesMap, allResources, err + cautils.WarningDisplay(os.Stdout, "Warning: failed to collect host sensor resources\n") } if err := k8sHandler.collectRbacResources(allResources); err != nil { From 35682bf5b858d41c9265817b59c25259b1db43b9 Mon Sep 17 00:00:00 2001 From: Daniel-GrunbergerCA Date: Tue, 21 Dec 2021 19:02:16 +0200 Subject: [PATCH 03/19] pull regoes from dev --- cautils/getter/downloadreleasedpolicy.go | 2 +- hostsensorutils/hostsensorgetfrompod.go | 2 +- opaprocessor/processorhandler.go | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cautils/getter/downloadreleasedpolicy.go b/cautils/getter/downloadreleasedpolicy.go index 9b6f9f86..75ca0529 100644 --- a/cautils/getter/downloadreleasedpolicy.go +++ b/cautils/getter/downloadreleasedpolicy.go @@ -18,7 +18,7 @@ type DownloadReleasedPolicy struct { func NewDownloadReleasedPolicy() *DownloadReleasedPolicy { return &DownloadReleasedPolicy{ - gs: gitregostore.NewDefaultGitRegoStore(-1), + gs: gitregostore.InitGitRegoStore("https://api.github.com/repos", "armosec", "regolibrary", "git/trees", "", "dev", -1), } } diff --git a/hostsensorutils/hostsensorgetfrompod.go b/hostsensorutils/hostsensorgetfrompod.go index 69d81301..30519bd5 100644 --- a/hostsensorutils/hostsensorgetfrompod.go +++ b/hostsensorutils/hostsensorgetfrompod.go @@ -157,7 +157,7 @@ func (hsh *HostSensorHandler) GetOsReleaseFile() ([]hostsensor.HostSensorDataEnv // return list of func (hsh *HostSensorHandler) GetKubeletConfigurations() ([]hostsensor.HostSensorDataEnvelope, error) { // loop over pods and port-forward it to each of them - res, err := hsh.sendAllPodsHTTPGETRequest("/kubeletConfigurations", "KubeletConfigurations") // empty kind, will be overridden + res, err := hsh.sendAllPodsHTTPGETRequest("/kubeletConfigurations", "KubeletConfiguration") // empty kind, will be overridden for resIdx := range res { jsonBytes, err := yaml.YAMLToJSON(res[resIdx].Data) if err != nil { diff --git a/opaprocessor/processorhandler.go b/opaprocessor/processorhandler.go index 7a320b92..6015be54 100644 --- a/opaprocessor/processorhandler.go +++ b/opaprocessor/processorhandler.go @@ -171,7 +171,6 @@ func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule) (*reporth } inputRawResources := workloadinterface.ListMetaToMap(inputResources) - ruleReport, err := opap.runOPAOnSingleRule(rule, inputRawResources, ruleData) if err != nil { // ruleReport.RuleStatus.Status = reporthandling.StatusFailed From 972793b98a7b0faa5915e7af5a83380716876354 Mon Sep 17 00:00:00 2001 From: Daniel-GrunbergerCA Date: Tue, 21 Dec 2021 20:27:23 +0200 Subject: [PATCH 04/19] print json --- opaprocessor/processorhandler.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/opaprocessor/processorhandler.go b/opaprocessor/processorhandler.go index 6015be54..6a03d967 100644 --- a/opaprocessor/processorhandler.go +++ b/opaprocessor/processorhandler.go @@ -2,6 +2,7 @@ package opaprocessor import ( "context" + "encoding/json" "fmt" "time" @@ -171,6 +172,9 @@ func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule) (*reporth } inputRawResources := workloadinterface.ListMetaToMap(inputResources) + jsonStr, _ := json.Marshal(inputRawResources) + fmt.Println(jsonStr) + ruleReport, err := opap.runOPAOnSingleRule(rule, inputRawResources, ruleData) if err != nil { // ruleReport.RuleStatus.Status = reporthandling.StatusFailed From cfa3993b79dfd8fdf0dbf0631af304f928d56f31 Mon Sep 17 00:00:00 2001 From: Daniel-GrunbergerCA Date: Tue, 21 Dec 2021 20:31:12 +0200 Subject: [PATCH 05/19] print json --- opaprocessor/processorhandler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opaprocessor/processorhandler.go b/opaprocessor/processorhandler.go index 6a03d967..374f1923 100644 --- a/opaprocessor/processorhandler.go +++ b/opaprocessor/processorhandler.go @@ -173,7 +173,7 @@ func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule) (*reporth inputRawResources := workloadinterface.ListMetaToMap(inputResources) jsonStr, _ := json.Marshal(inputRawResources) - fmt.Println(jsonStr) + fmt.Println(string(jsonStr)) ruleReport, err := opap.runOPAOnSingleRule(rule, inputRawResources, ruleData) if err != nil { From ad94ac7595e3ddbb8881f958ea07af70e6c71359 Mon Sep 17 00:00:00 2001 From: Daniel-GrunbergerCA Date: Wed, 22 Dec 2021 08:29:35 +0200 Subject: [PATCH 06/19] rm json print --- opaprocessor/processorhandler.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/opaprocessor/processorhandler.go b/opaprocessor/processorhandler.go index 374f1923..7a320b92 100644 --- a/opaprocessor/processorhandler.go +++ b/opaprocessor/processorhandler.go @@ -2,7 +2,6 @@ package opaprocessor import ( "context" - "encoding/json" "fmt" "time" @@ -172,8 +171,6 @@ func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule) (*reporth } inputRawResources := workloadinterface.ListMetaToMap(inputResources) - jsonStr, _ := json.Marshal(inputRawResources) - fmt.Println(string(jsonStr)) ruleReport, err := opap.runOPAOnSingleRule(rule, inputRawResources, ruleData) if err != nil { From c35d1e879195db2a781d2072d3c6cdeacfeb07fa Mon Sep 17 00:00:00 2001 From: David Delarosa Date: Wed, 22 Dec 2021 20:27:38 +0200 Subject: [PATCH 07/19] Use stderr By using stderr fd we can separate the information logs from the application output --- cautils/display.go | 14 +++++++------- cautils/versioncheck.go | 2 +- resultshandling/printer/jsonprinter.go | 2 +- resultshandling/printer/junit.go | 2 +- resultshandling/reporter/mockreporter.go | 2 +- resultshandling/reporter/reporteventreceiver.go | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cautils/display.go b/cautils/display.go index d82e9114..4efd4a2c 100644 --- a/cautils/display.go +++ b/cautils/display.go @@ -35,15 +35,15 @@ func ScanStartDisplay() { if IsSilent() { return } - InfoDisplay(os.Stdout, "ARMO security scanner starting\n") + InfoDisplay(os.Stderr, "ARMO security scanner starting\n") } func SuccessTextDisplay(str string) { if IsSilent() { return } - SuccessDisplay(os.Stdout, "[success] ") - SimpleDisplay(os.Stdout, fmt.Sprintf("%s\n", str)) + SuccessDisplay(os.Stderr, "[success] ") + SimpleDisplay(os.Stderr, fmt.Sprintf("%s\n", str)) } @@ -51,8 +51,8 @@ func ErrorDisplay(str string) { if IsSilent() { return } - SuccessDisplay(os.Stdout, "[Error] ") - SimpleDisplay(os.Stdout, fmt.Sprintf("%s\n", str)) + SuccessDisplay(os.Stderr, "[Error] ") + SimpleDisplay(os.Stderr, fmt.Sprintf("%s\n", str)) } @@ -60,8 +60,8 @@ func ProgressTextDisplay(str string) { if IsSilent() { return } - InfoDisplay(os.Stdout, "[progress] ") - SimpleDisplay(os.Stdout, fmt.Sprintf("%s\n", str)) + InfoDisplay(os.Stderr, "[progress] ") + SimpleDisplay(os.Stderr, fmt.Sprintf("%s\n", str)) } func StartSpinner() { diff --git a/cautils/versioncheck.go b/cautils/versioncheck.go index ab5d4217..2eba7e4b 100644 --- a/cautils/versioncheck.go +++ b/cautils/versioncheck.go @@ -22,7 +22,7 @@ type IVersionCheckHandler interface { func NewIVersionCheckHandler() IVersionCheckHandler { if BuildNumber == "" { - WarningDisplay(os.Stdout, "Warning: unknown build number, this might affect your scan results. Please make sure you are updated to latest version.\n") + WarningDisplay(os.Stderr, "Warning: unknown build number, this might affect your scan results. Please make sure you are updated to latest version.\n") } if v, ok := os.LookupEnv(SKIP_VERSION_CHECK); ok && pkgutils.StringToBool(v) { return NewVersionCheckHandlerMock() diff --git a/resultshandling/printer/jsonprinter.go b/resultshandling/printer/jsonprinter.go index 01acad7e..ab2de4d9 100644 --- a/resultshandling/printer/jsonprinter.go +++ b/resultshandling/printer/jsonprinter.go @@ -21,7 +21,7 @@ func (jsonPrinter *JsonPrinter) SetWriter(outputFile string) { } func (jsonPrinter *JsonPrinter) Score(score float32) { - fmt.Printf("\nFinal score: %d", int(score*100)) + fmt.Fprintf(os.Stderr, "\nFinal score: %d", int(score*100)) } func (jsonPrinter *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) { diff --git a/resultshandling/printer/junit.go b/resultshandling/printer/junit.go index 9c480b50..1c8ea48d 100644 --- a/resultshandling/printer/junit.go +++ b/resultshandling/printer/junit.go @@ -22,7 +22,7 @@ func (junitPrinter *JunitPrinter) SetWriter(outputFile string) { } func (junitPrinter *JunitPrinter) Score(score float32) { - fmt.Printf("\nFinal score: %d", int(score*100)) + fmt.Fprintf(os.Stderr, "\nFinal score: %d", int(score*100)) } func (junitPrinter *JunitPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) { diff --git a/resultshandling/reporter/mockreporter.go b/resultshandling/reporter/mockreporter.go index 22983fef..803066a6 100644 --- a/resultshandling/reporter/mockreporter.go +++ b/resultshandling/reporter/mockreporter.go @@ -26,5 +26,5 @@ func (reportMock *ReportMock) SetClusterName(clusterName string) { func (reportMock *ReportMock) DisplayReportURL() { message := fmt.Sprintf("\nYou can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more by registering here: https://%s/cli-signup \n", getter.GetArmoAPIConnector().GetFrontendURL()) - cautils.InfoTextDisplay(os.Stdout, fmt.Sprintf("\n%s\n", message)) + cautils.InfoTextDisplay(os.Stderr, fmt.Sprintf("\n%s\n", message)) } diff --git a/resultshandling/reporter/reporteventreceiver.go b/resultshandling/reporter/reporteventreceiver.go index fc01232d..07fee8cf 100644 --- a/resultshandling/reporter/reporteventreceiver.go +++ b/resultshandling/reporter/reporteventreceiver.go @@ -126,7 +126,7 @@ func (report *ReportEventReceiver) DisplayReportURL() { u.Host = getter.GetArmoAPIConnector().GetFrontendURL() if report.customerAdminEMail != "" { - cautils.InfoTextDisplay(os.Stdout, fmt.Sprintf("\n\n%s %s/risk/%s\n(Account: %s)\n\n", message, u.String(), report.clusterName, report.customerGUID)) + cautils.InfoTextDisplay(os.Stderr, fmt.Sprintf("\n\n%s %s/risk/%s\n(Account: %s)\n\n", message, u.String(), report.clusterName, report.customerGUID)) return } u.Path = "account/sign-up" @@ -135,5 +135,5 @@ func (report *ReportEventReceiver) DisplayReportURL() { q.Add("customerGUID", report.customerGUID) u.RawQuery = q.Encode() - cautils.InfoTextDisplay(os.Stdout, fmt.Sprintf("\n\n%s %s\n\n", message, u.String())) + cautils.InfoTextDisplay(os.Stderr, fmt.Sprintf("\n\n%s %s\n\n", message, u.String())) } From 14a74e7312f027296abda281b0b8f7632cfa21f3 Mon Sep 17 00:00:00 2001 From: yiscah Date: Thu, 23 Dec 2021 10:33:23 +0200 Subject: [PATCH 08/19] support pulling config inputs from git --- cautils/getter/downloadreleasedpolicy.go | 12 ++++++++++ cautils/scaninfo.go | 9 -------- clihandler/initcli.go | 5 +++- clihandler/initcliutils.go | 29 ++++++++++++++++++------ go.mod | 2 +- go.sum | 4 ++-- 6 files changed, 41 insertions(+), 20 deletions(-) diff --git a/cautils/getter/downloadreleasedpolicy.go b/cautils/getter/downloadreleasedpolicy.go index 9b6f9f86..6450449e 100644 --- a/cautils/getter/downloadreleasedpolicy.go +++ b/cautils/getter/downloadreleasedpolicy.go @@ -41,7 +41,19 @@ func (drp *DownloadReleasedPolicy) GetFramework(name string) (*reporthandling.Fr return framework, err } +func (drp *DownloadReleasedPolicy) GetControlsInputs(customerGUID, clusterName string) (map[string][]string, error) { + defaultConfigInputs, err := drp.gs.GetDefaultConfigInputs() + if err != nil { + return nil, err + } + return defaultConfigInputs.Settings.PostureControlInputs, err +} + func (drp *DownloadReleasedPolicy) SetRegoObjects() error { + fwNames, err := drp.gs.GetOPAFrameworksNamesList() + if len(fwNames) != 0 && err == nil { + return nil + } return drp.gs.SetRegoObjects() } diff --git a/cautils/scaninfo.go b/cautils/scaninfo.go index 989c4115..e2aa1f7f 100644 --- a/cautils/scaninfo.go +++ b/cautils/scaninfo.go @@ -77,7 +77,6 @@ type Getters struct { func (scanInfo *ScanInfo) Init() { scanInfo.setUseFrom() scanInfo.setUseExceptions() - scanInfo.setAccountConfig() scanInfo.setOutputFile() } @@ -91,14 +90,6 @@ func (scanInfo *ScanInfo) setUseExceptions() { } } -func (scanInfo *ScanInfo) setAccountConfig() { - if scanInfo.ControlsInputs != "" { - // load account config from file - scanInfo.ControlsInputsGetter = getter.NewLoadPolicy([]string{scanInfo.ControlsInputs}) - } else { - scanInfo.ControlsInputsGetter = getter.GetArmoAPIConnector() - } -} func (scanInfo *ScanInfo) setUseFrom() { if scanInfo.UseDefault { for _, policy := range scanInfo.PolicyIdentifier { diff --git a/clihandler/initcli.go b/clihandler/initcli.go index eebd806f..0d3b1735 100644 --- a/clihandler/initcli.go +++ b/clihandler/initcli.go @@ -7,6 +7,7 @@ import ( "github.com/armosec/armoapi-go/armotypes" "github.com/armosec/kubescape/cautils" + "github.com/armosec/kubescape/cautils/getter" "github.com/armosec/kubescape/clihandler/cliinterfaces" "github.com/armosec/kubescape/hostsensorutils" "github.com/armosec/kubescape/opaprocessor" @@ -85,8 +86,10 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error { interfaces.report.SetClusterName(interfaces.tenantConfig.GetClusterName()) interfaces.report.SetCustomerGUID(interfaces.tenantConfig.GetCustomerGUID()) + downloadReleasedPolicy := getter.NewDownloadReleasedPolicy() // download config inputs from github release // set policy getter only after setting the customerGUID - setPolicyGetter(scanInfo, interfaces.tenantConfig.GetCustomerGUID()) + setPolicyGetter(scanInfo, interfaces.tenantConfig.GetCustomerGUID(), downloadReleasedPolicy) + setConfigInputsGetter(scanInfo, interfaces.tenantConfig.GetCustomerGUID(), downloadReleasedPolicy) defer func() { if err := interfaces.hostSensorHandler.TearDown(); err != nil { diff --git a/clihandler/initcliutils.go b/clihandler/initcliutils.go index d455a54b..f34a1aa6 100644 --- a/clihandler/initcliutils.go +++ b/clihandler/initcliutils.go @@ -136,25 +136,40 @@ func setSubmitBehavior(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantC } // setPolicyGetter set the policy getter - local file/github release/ArmoAPI -func setPolicyGetter(scanInfo *cautils.ScanInfo, customerGUID string) { +func setPolicyGetter(scanInfo *cautils.ScanInfo, customerGUID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) { if len(scanInfo.UseFrom) > 0 { scanInfo.PolicyGetter = getter.NewLoadPolicy(scanInfo.UseFrom) } else { if customerGUID == "" || !scanInfo.FrameworkScan { - setDownloadReleasedPolicy(scanInfo) + setDownloadReleasedPolicy(scanInfo, downloadReleasedPolicy) } else { setGetArmoAPIConnector(scanInfo, customerGUID) } } } -func setDownloadReleasedPolicy(scanInfo *cautils.ScanInfo) { - g := getter.NewDownloadReleasedPolicy() // download policy from github release - if err := g.SetRegoObjects(); err != nil { // if failed to pull policy, fallback to cache - cautils.WarningDisplay(os.Stdout, "Warning: failed to get policies from github release, loading policies from cache\n") +// setConfigInputsGetter sets the config input getter - local file/github release/ArmoAPI +func setConfigInputsGetter(scanInfo *cautils.ScanInfo, customerGUID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) { + if len(scanInfo.ControlsInputs) > 0 { + scanInfo.PolicyGetter = getter.NewLoadPolicy([]string{scanInfo.ControlsInputs}) + } else { + if customerGUID != "" { + scanInfo.Getters.ControlsInputsGetter = getter.GetArmoAPIConnector() + } else { + if err := downloadReleasedPolicy.SetRegoObjects(); err != nil { // if failed to pull config inputs, fallback to BE + cautils.WarningDisplay(os.Stderr, "Warning: failed to get config inputs from github release, this may affect the scanning results\n") + } + scanInfo.Getters.ControlsInputsGetter = downloadReleasedPolicy + } + } +} + +func setDownloadReleasedPolicy(scanInfo *cautils.ScanInfo, downloadReleasedPolicy *getter.DownloadReleasedPolicy) { + if err := downloadReleasedPolicy.SetRegoObjects(); err != nil { // if failed to pull policy, fallback to cache + cautils.WarningDisplay(os.Stderr, "Warning: failed to get policies from github release, loading policies from cache\n") scanInfo.PolicyGetter = getter.NewLoadPolicy(getDefaultFrameworksPaths()) } else { - scanInfo.PolicyGetter = g + scanInfo.PolicyGetter = downloadReleasedPolicy } } func setGetArmoAPIConnector(scanInfo *cautils.ScanInfo, customerGUID string) { diff --git a/go.mod b/go.mod index 72545313..28d91b4d 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/armosec/armoapi-go v0.0.23 github.com/armosec/k8s-interface v0.0.50 - github.com/armosec/opa-utils v0.0.75 + github.com/armosec/opa-utils v0.0.76 github.com/armosec/rbac-utils v0.0.9 github.com/armosec/utils-go v0.0.3 github.com/briandowns/spinner v1.18.0 diff --git a/go.sum b/go.sum index eea11167..4192e388 100644 --- a/go.sum +++ b/go.sum @@ -91,8 +91,8 @@ github.com/armosec/k8s-interface v0.0.37/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2 github.com/armosec/k8s-interface v0.0.50 h1:iLPGI0j85vwKANr9QDAnba4Efjg3DyIJg15jRJdvOnc= github.com/armosec/k8s-interface v0.0.50/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.75 h1:GBI3K18xc3WXJHIorIu4bGNAsfMYHUc1x7zueDz2ZbY= -github.com/armosec/opa-utils v0.0.75/go.mod h1:L7d+uiIIXAZ3LEyKtmEIbMcI1hWgWaXGpn5zVCqzwSU= +github.com/armosec/opa-utils v0.0.76 h1:MMh40/QUIzAd/2GGNOq6i6xzQdKvpCmhnKjA5KPZLmQ= +github.com/armosec/opa-utils v0.0.76/go.mod h1:L7d+uiIIXAZ3LEyKtmEIbMcI1hWgWaXGpn5zVCqzwSU= github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40= github.com/armosec/rbac-utils v0.0.9 h1:rIOWp4K7BELUNX32ktSjVbb8d/0SpH7W76W6Tf+8rzw= github.com/armosec/rbac-utils v0.0.9/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0= From 0d4350ae242fba5429cd6e5e32d9d1d506bb58ca Mon Sep 17 00:00:00 2001 From: yiscah Date: Thu, 23 Dec 2021 17:31:23 +0200 Subject: [PATCH 09/19] fix ControlsInputsGetter init --- clihandler/initcliutils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clihandler/initcliutils.go b/clihandler/initcliutils.go index f34a1aa6..0ed0dd58 100644 --- a/clihandler/initcliutils.go +++ b/clihandler/initcliutils.go @@ -151,7 +151,7 @@ func setPolicyGetter(scanInfo *cautils.ScanInfo, customerGUID string, downloadRe // setConfigInputsGetter sets the config input getter - local file/github release/ArmoAPI func setConfigInputsGetter(scanInfo *cautils.ScanInfo, customerGUID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) { if len(scanInfo.ControlsInputs) > 0 { - scanInfo.PolicyGetter = getter.NewLoadPolicy([]string{scanInfo.ControlsInputs}) + scanInfo.Getters.ControlsInputsGetter = getter.NewLoadPolicy([]string{scanInfo.ControlsInputs}) } else { if customerGUID != "" { scanInfo.Getters.ControlsInputsGetter = getter.GetArmoAPIConnector() From e49499f08574cbfd21182b83530224d389b2c5bd Mon Sep 17 00:00:00 2001 From: Daniel-GrunbergerCA Date: Mon, 27 Dec 2021 08:45:50 +0200 Subject: [PATCH 10/19] use regoes from master --- cautils/getter/downloadreleasedpolicy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cautils/getter/downloadreleasedpolicy.go b/cautils/getter/downloadreleasedpolicy.go index 75ca0529..9b6f9f86 100644 --- a/cautils/getter/downloadreleasedpolicy.go +++ b/cautils/getter/downloadreleasedpolicy.go @@ -18,7 +18,7 @@ type DownloadReleasedPolicy struct { func NewDownloadReleasedPolicy() *DownloadReleasedPolicy { return &DownloadReleasedPolicy{ - gs: gitregostore.InitGitRegoStore("https://api.github.com/repos", "armosec", "regolibrary", "git/trees", "", "dev", -1), + gs: gitregostore.NewDefaultGitRegoStore(-1), } } From accd80eda8c8da6d7aefa1ec5fd936085b307850 Mon Sep 17 00:00:00 2001 From: Daniel-GrunbergerCA Date: Tue, 28 Dec 2021 09:07:50 +0200 Subject: [PATCH 11/19] rm cmdline map --- go.mod | 4 ++-- go.sum | 7 ++++--- hostsensorutils/hostsensorgetfrompod.go | 24 ++---------------------- 3 files changed, 8 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index 72545313..ce063198 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.23 + github.com/armosec/armoapi-go v0.0.40 github.com/armosec/k8s-interface v0.0.50 - github.com/armosec/opa-utils v0.0.75 + github.com/armosec/opa-utils v0.0.78 github.com/armosec/rbac-utils v0.0.9 github.com/armosec/utils-go v0.0.3 github.com/briandowns/spinner v1.18.0 diff --git a/go.sum b/go.sum index eea11167..525a97a2 100644 --- a/go.sum +++ b/go.sum @@ -84,15 +84,16 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5 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/armoapi-go v0.0.2/go.mod h1:vIK17yoKbJRQyZXWWLe3AqfqCRITxW8qmSkApyq5xFs= -github.com/armosec/armoapi-go v0.0.23 h1:jqoLIWM5CR7DCD9fpFgN0ePqtHvOCoZv/XzCwsUluJU= 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/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 h1:iLPGI0j85vwKANr9QDAnba4Efjg3DyIJg15jRJdvOnc= github.com/armosec/k8s-interface v0.0.50/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.75 h1:GBI3K18xc3WXJHIorIu4bGNAsfMYHUc1x7zueDz2ZbY= -github.com/armosec/opa-utils v0.0.75/go.mod h1:L7d+uiIIXAZ3LEyKtmEIbMcI1hWgWaXGpn5zVCqzwSU= +github.com/armosec/opa-utils v0.0.78 h1:wFkVqJ1vEftn3E1hHdUfKlp5xHgof616ljyKKebuNkI= +github.com/armosec/opa-utils v0.0.78/go.mod h1:ZOXYVTtuyrV4TldcfbzgRqP6F9Drlf4hB0zr210OXgM= github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40= github.com/armosec/rbac-utils v0.0.9 h1:rIOWp4K7BELUNX32ktSjVbb8d/0SpH7W76W6Tf+8rzw= github.com/armosec/rbac-utils v0.0.9/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0= diff --git a/hostsensorutils/hostsensorgetfrompod.go b/hostsensorutils/hostsensorgetfrompod.go index 30519bd5..906e7ca9 100644 --- a/hostsensorutils/hostsensorgetfrompod.go +++ b/hostsensorutils/hostsensorgetfrompod.go @@ -3,7 +3,6 @@ package hostsensorutils import ( "encoding/json" "fmt" - "strings" "sync" "github.com/armosec/k8s-interface/k8sinterface" @@ -110,7 +109,8 @@ func (hsh *HostSensorHandler) GetKubeletCommandLine() ([]hostsensor.HostSensorDa return resps, err } for resp := range resps { - data := makeCmdLineMap(string(resps[resp].Data)) + var data = make(map[string]interface{}) + data["fullCommand"] = string(resps[resp].Data) resBytesMarshal, err := json.Marshal(data) // TODO catch error if err == nil { @@ -122,26 +122,6 @@ func (hsh *HostSensorHandler) GetKubeletCommandLine() ([]hostsensor.HostSensorDa } -func makeCmdLineMap(fullCommand string) map[string]interface{} { - - commands := strings.Split(fullCommand, " ") - if len(commands) < 1 { - return nil - } - command := commands[0] - var cmdMap = make(map[string]interface{}) - cmdMap["command"] = command - for _, cmd := range commands { - splitted := strings.Split(cmd, "=") - if len(splitted) == 2 { - if strings.HasPrefix(splitted[0], "--") { - cmdMap[splitted[0][2:]] = splitted[1] - } - } - } - return cmdMap -} - // return list of func (hsh *HostSensorHandler) GetKernelVersion() ([]hostsensor.HostSensorDataEnvelope, error) { // loop over pods and port-forward it to each of them From 5f7391a76b228328376e3378ce20d6cb4be656b6 Mon Sep 17 00:00:00 2001 From: Daniel-GrunbergerCA Date: Tue, 28 Dec 2021 09:20:05 +0200 Subject: [PATCH 12/19] stdout to stderror --- clihandler/initcliutils.go | 6 +++--- resourcehandler/k8sresources.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clihandler/initcliutils.go b/clihandler/initcliutils.go index cc7ebb35..47c9bdfe 100644 --- a/clihandler/initcliutils.go +++ b/clihandler/initcliutils.go @@ -58,12 +58,12 @@ func getHostSensorHandler(scanInfo *cautils.ScanInfo, k8s *k8sinterface.Kubernet // we need to determined which controls needs host sensor if scanInfo.HostSensor.Get() == nil && hasHostSensorControls { scanInfo.HostSensor.SetBool(askUserForHostSensor()) - cautils.WarningDisplay(os.Stdout, "Warning: K8S nodes scanning is disabled. This is required to collect valuable data for certain controls. You can enable it using the --enable-host-scan flag\n") + cautils.WarningDisplay(os.Stderr, "Warning: Kubernetes cluster nodes scanning is disabled. This is required to collect valuable data for certain controls. You can enable it using the --enable-host-scan flag\n") } if hostSensorVal := scanInfo.HostSensor.Get(); hostSensorVal != nil && *hostSensorVal { hostSensorHandler, err := hostsensorutils.NewHostSensorHandler(k8s) if err != nil { - cautils.WarningDisplay(os.Stdout, fmt.Sprintf("Warning: failed to create host sensor: %v\n", err.Error())) + cautils.WarningDisplay(os.Stderr, fmt.Sprintf("Warning: failed to create host sensor: %v\n", err.Error())) return &hostsensorutils.HostSensorHandlerMock{} } return hostSensorHandler @@ -153,7 +153,7 @@ func setPolicyGetter(scanInfo *cautils.ScanInfo, customerGUID string) { func setDownloadReleasedPolicy(scanInfo *cautils.ScanInfo) { g := getter.NewDownloadReleasedPolicy() // download policy from github release if err := g.SetRegoObjects(); err != nil { // if failed to pull policy, fallback to cache - cautils.WarningDisplay(os.Stdout, "Warning: failed to get policies from github release, loading policies from cache\n") + cautils.WarningDisplay(os.Stderr, "Warning: failed to get policies from github release, loading policies from cache\n") scanInfo.PolicyGetter = getter.NewLoadPolicy(getDefaultFrameworksPaths()) } else { scanInfo.PolicyGetter = g diff --git a/resourcehandler/k8sresources.go b/resourcehandler/k8sresources.go index 3aa926ad..75528038 100644 --- a/resourcehandler/k8sresources.go +++ b/resourcehandler/k8sresources.go @@ -61,14 +61,14 @@ func (k8sHandler *K8sResourceHandler) GetResources(frameworks []reporthandling.F return k8sResourcesMap, allResources, err } if err := k8sHandler.collectHostResources(allResources, k8sResourcesMap); err != nil { - cautils.WarningDisplay(os.Stdout, "Warning: failed to collect host sensor resources\n") + cautils.WarningDisplay(os.Stderr, "Warning: failed to collect host sensor resources\n") } if err := k8sHandler.collectRbacResources(allResources); err != nil { - cautils.WarningDisplay(os.Stdout, "Warning: failed to collect rbac resources\n") + cautils.WarningDisplay(os.Stderr, "Warning: failed to collect rbac resources\n") } if err := getCloudProviderDescription(allResources, k8sResourcesMap); err != nil { - cautils.WarningDisplay(os.Stdout, fmt.Sprintf("Warning: %v\n", err.Error())) + cautils.WarningDisplay(os.Stderr, fmt.Sprintf("Warning: %v\n", err.Error())) } cautils.StopSpinner() From 1b0e2b87de8d701d037857da6e9c6e432be8316d Mon Sep 17 00:00:00 2001 From: dwertent Date: Tue, 28 Dec 2021 10:47:12 +0200 Subject: [PATCH 13/19] Handle all resources failure --- clihandler/initcli.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clihandler/initcli.go b/clihandler/initcli.go index 0d3b1735..c8faee0f 100644 --- a/clihandler/initcli.go +++ b/clihandler/initcli.go @@ -124,7 +124,7 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error { // print report url interfaces.report.DisplayReportURL() - if score >= float32(scanInfo.FailThreshold) { + if score > float32(scanInfo.FailThreshold) { return fmt.Errorf("scan risk-score %.2f is above permitted threshold %d", score, scanInfo.FailThreshold) } From 716bdaaf38393e52666257ecaa5c3e9ecc1c06e0 Mon Sep 17 00:00:00 2001 From: dwertent Date: Wed, 29 Dec 2021 12:06:48 +0200 Subject: [PATCH 14/19] support kind List --- resourcehandler/filesloader.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/resourcehandler/filesloader.go b/resourcehandler/filesloader.go index 25cc7e74..c4052fe2 100644 --- a/resourcehandler/filesloader.go +++ b/resourcehandler/filesloader.go @@ -213,7 +213,11 @@ func readYamlFile(yamlFile []byte) ([]workloadinterface.IMetadata, []error) { } if obj, ok := j.(map[string]interface{}); ok { if o := objectsenvelopes.NewObject(obj); o != nil { - yamlObjs = append(yamlObjs, o) + if o.GetKind() == "List" { + yamlObjs = append(yamlObjs, handleListObject(o)...) + } else { + yamlObjs = append(yamlObjs, o) + } } } else { errs = append(errs, fmt.Errorf("failed to convert yaml file to map[string]interface, file content: %v", j)) @@ -303,3 +307,20 @@ func getFileFormat(filePath string) FileFormat { return FileFormat(filePath) } } + +// handleListObject handle a List manifest +func handleListObject(obj workloadinterface.IMetadata) []workloadinterface.IMetadata { + yamlObjs := []workloadinterface.IMetadata{} + if i, ok := workloadinterface.InspectMap(obj.GetObject(), "items"); ok && i != nil { + if items, ok := i.([]interface{}); ok && items != nil { + for item := range items { + if m, ok := items[item].(map[string]interface{}); ok && m != nil { + if o := objectsenvelopes.NewObject(m); o != nil { + yamlObjs = append(yamlObjs, o) + } + } + } + } + } + return yamlObjs +} From f22f60508fff276eca726ce33b050470a7762fc4 Mon Sep 17 00:00:00 2001 From: yiscah Date: Wed, 29 Dec 2021 16:23:14 +0200 Subject: [PATCH 15/19] rbacTable and rbac struct deprecated --- cautils/rbac.go | 10 ---------- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/cautils/rbac.go b/cautils/rbac.go index 47667cab..61eac56a 100644 --- a/cautils/rbac.go +++ b/cautils/rbac.go @@ -43,16 +43,6 @@ 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 - rbacIMeta, err := rbacutils.RbacObjectIMetadataWrapper(resources.Rbac) - if err != nil { - return nil, err - } - allresources[rbacIMeta.GetID()] = rbacIMeta - rbacTableIMeta, err := rbacutils.RbacTableObjectIMetadataWrapper(resources.RbacT) - if err != nil { - return nil, err - } - allresources[rbacTableIMeta.GetID()] = rbacTableIMeta SA2WLIDmapIMeta, err := rbacutils.SA2WLIDmapIMetadataWrapper(resources.SA2WLIDmap) if err != nil { return nil, err diff --git a/go.mod b/go.mod index ce063198..1be3fc6a 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/armosec/armoapi-go v0.0.40 github.com/armosec/k8s-interface v0.0.50 github.com/armosec/opa-utils v0.0.78 - github.com/armosec/rbac-utils v0.0.9 + github.com/armosec/rbac-utils v0.0.10 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 525a97a2..e5c512ca 100644 --- a/go.sum +++ b/go.sum @@ -95,8 +95,8 @@ github.com/armosec/opa-utils v0.0.64/go.mod h1:6tQP8UDq2EvEfSqh8vrUdr/9QVSCG4sJf github.com/armosec/opa-utils v0.0.78 h1:wFkVqJ1vEftn3E1hHdUfKlp5xHgof616ljyKKebuNkI= github.com/armosec/opa-utils v0.0.78/go.mod h1:ZOXYVTtuyrV4TldcfbzgRqP6F9Drlf4hB0zr210OXgM= github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40= -github.com/armosec/rbac-utils v0.0.9 h1:rIOWp4K7BELUNX32ktSjVbb8d/0SpH7W76W6Tf+8rzw= -github.com/armosec/rbac-utils v0.0.9/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0= +github.com/armosec/rbac-utils v0.0.10 h1:bFjesO8+xJS1ryR9vqj4xFEo1cQ0HvClzR+LWHzozW4= +github.com/armosec/rbac-utils v0.0.10/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= From 0de6892ddddc7cad13af00485116778858321743 Mon Sep 17 00:00:00 2001 From: Benyamin Hirschberg <59160382+BenHirschbergCa@users.noreply.github.com> Date: Thu, 30 Dec 2021 10:44:08 +0200 Subject: [PATCH 16/19] adding container image vunerability adaptor proposal --- .../container-image-vulnerability-adaptor.md | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 docs/proposals/container-image-vulnerability-adaptor.md diff --git a/docs/proposals/container-image-vulnerability-adaptor.md b/docs/proposals/container-image-vulnerability-adaptor.md new file mode 100644 index 00000000..f3dc4b28 --- /dev/null +++ b/docs/proposals/container-image-vulnerability-adaptor.md @@ -0,0 +1,117 @@ +# Container image vulnerabilty adaptor interface proposal + +## Rationale + +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. +* 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 + +There are multiple changes and design decisions needs to be made before Kubescape will support the before outlined controls. However, a focal point the whole picutre is the ability to access vulnerabilty databases of container images. We anticiapte that most container image repositories will support image vulnerabilty scanning, some major players are already do. Since there is no a single API available which all of these data sources support it is important to create an adaption layer within Kubescape so different datasources can serve Kubescape's goals. + +## High level design of Kubescape + +### Layers + +* Controls and Rules: that actual control logic implementation, the "tests" themselves. Implemented in rego +* OPA engine: the [OPA](https://github.com/open-policy-agent/opa) rego interpreter +* Rules processor: Kubescape component, it enumerates and runs the controls while also preparing the all the input data that the controls need for running +* Data sources: set of different modules providing data to the Rules processor so it can run the controls with them. Examples: Kubernetes objects, cloud vendor API objects and adding in this proposal the vulnerability infomration +* Cloud Image Vulnerability adaption interface: the subject of this proposal, it gives a common interface for different registry/vulnerabilty vendors to adapt to. +* CIV adaptors: specific implementation of the CIV interface, example Harbor adaption +``` + ----------------------- +| Controls/Rules (rego) | + ----------------------- + | + ----------------------- +| OPA engine | + ----------------------- + | + ----------------------- +| Rules processor | + ----------------------- + | + ----------------------- +| Data sources | + ----------------------- + | + ======================= +| CIV adaption interface| <- Adding this layer in this proposal + ======================= + | + ----------------------- +| Specific CIV adaptors | <- will be implemented based on this proposal + ----------------------- + + + +``` + +## Functionalities to cover + +The interface needs to cover the following functionalities: + +* Authentication against the information source (abstracted login) +* Triggering image scan (if applicable, the source might store vulnerabilities for images but cannot scan alone) +* Reading image scan status (with last scan date and etc.) +* Getting vulnerability information for a given image +* Getting image information + * Image manifests + * Image BOMs (bill of material) + +## Go API proposal + +``` + +/*type ContainerImageRegistryCredentials struct { + map[string]string + Password string + Tag string + Hash string +}*/ + +type ContainerImageIdentifier struct { + Registry string + Repository string + Tag string + Hash string +} + +type ContainerImageScanStatus struct { + ImageID ContainerImageIdentifier + IsScanAvailable bool + IsBomAvailable bool + LastScanDate time.Time +} + +type ContainerImageVulnerability struct { + ImageID ContainerImageIdentifier + // TBD +} + +type ContainerImageInformation struct { + ImageID ContainerImageIdentifier + Bom []string + ImageManifest Manifest // will use here Docker package definition +} + +type IContainerImageVulnerabilityAdaptor interface { + // Credentials are coming from user input (CLI or configuration file) and they are abstracted at string to string map level + // so and example use would be like registry: "simpledockerregistry:80" and credentials like {"username":"joedoe","password":"abcd1234"} + Login(registry string, credentials map[string]string) error + + // For "help" purposes + DescribeAdaptor() string + + GetImagesScanStatus(imageIDs []ContainerImageIdentifier) ([]ContainerImageScanStatus, error) + + GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerability, error) + + GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error) +} +``` \ No newline at end of file From 803e62020ecef5ba30b27ae223c90e22f36477c6 Mon Sep 17 00:00:00 2001 From: Rotem Refael Date: Thu, 30 Dec 2021 16:40:07 +0200 Subject: [PATCH 17/19] add devopsbest framework --- cautils/getter/armoapiutils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cautils/getter/armoapiutils.go b/cautils/getter/armoapiutils.go index 4991fcea..8cb157ef 100644 --- a/cautils/getter/armoapiutils.go +++ b/cautils/getter/armoapiutils.go @@ -5,7 +5,7 @@ import ( "strings" ) -var NativeFrameworks = []string{"nsa", "mitre", "armobest"} +var NativeFrameworks = []string{"nsa", "mitre", "armobest", "devopsbest"} func (armoAPI *ArmoAPI) getFrameworkURL(frameworkName string) string { u := url.URL{} From 7bc2c2be13ffbfd7986dbf23d79caadbafff7306 Mon Sep 17 00:00:00 2001 From: dwertent Date: Mon, 3 Jan 2022 13:36:29 +0200 Subject: [PATCH 18/19] fliter ot reources based on owners --- cautils/getter/armoapi.go | 2 +- opaprocessor/processorhandlerutils.go | 27 ++++++++++++++++++++++++++- resourcehandler/k8sresources.go | 2 +- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/cautils/getter/armoapi.go b/cautils/getter/armoapi.go index 93ebbf9c..ed920136 100644 --- a/cautils/getter/armoapi.go +++ b/cautils/getter/armoapi.go @@ -100,7 +100,7 @@ func (armoAPI *ArmoAPI) GetReportReceiverURL() string { func (armoAPI *ArmoAPI) GetFramework(name string) (*reporthandling.Framework, error) { respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getFrameworkURL(name), nil) if err != nil { - return nil, err + return nil, nil } framework := &reporthandling.Framework{} diff --git a/opaprocessor/processorhandlerutils.go b/opaprocessor/processorhandlerutils.go index beb2c208..8c727465 100644 --- a/opaprocessor/processorhandlerutils.go +++ b/opaprocessor/processorhandlerutils.go @@ -41,6 +41,7 @@ func getAllSupportedObjects(k8sResources *cautils.K8SResources, allResources map func getKubernetesObjects(k8sResources *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata, match []reporthandling.RuleMatchObjects) []workloadinterface.IMetadata { k8sObjects := []workloadinterface.IMetadata{} + for m := range match { for _, groups := range match[m].APIGroups { for _, version := range match[m].APIVersions { @@ -62,9 +63,33 @@ func getKubernetesObjects(k8sResources *cautils.K8SResources, allResources map[s } } - return k8sObjects + return filterOutChildResources(k8sObjects, match) } +// filterOutChildResources filter out child resources if the parent resource is in the list +func filterOutChildResources(objects []workloadinterface.IMetadata, match []reporthandling.RuleMatchObjects) []workloadinterface.IMetadata { + response := []workloadinterface.IMetadata{} + owners := []string{} + for m := range match { + for i := range match[m].Resources { + owners = append(owners, match[m].Resources[i]) + } + } + for i := range objects { + if !k8sinterface.IsTypeWorkload(objects[i].GetObject()) { + response = append(response, objects[i]) + continue + } + w := workloadinterface.NewWorkloadObj(objects[i].GetObject()) + ownerReferences, err := w.GetOwnerReferences() + if err != nil || len(ownerReferences) == 0 { + response = append(response, w) + } else if !k8sinterface.IsStringInSlice(owners, ownerReferences[0].Kind) { + response = append(response, w) + } + } + return response +} func getRuleDependencies() (map[string]string, error) { modules := resources.LoadRegoModules() if len(modules) == 0 { diff --git a/resourcehandler/k8sresources.go b/resourcehandler/k8sresources.go index 75528038..aa308f76 100644 --- a/resourcehandler/k8sresources.go +++ b/resourcehandler/k8sresources.go @@ -105,7 +105,7 @@ func (k8sHandler *K8sResourceHandler) pullResources(k8sResources *cautils.K8SRes continue } // store result as []map[string]interface{} - metaObjs := ConvertMapListToMeta(k8sinterface.ConvertUnstructuredSliceToMap(k8sinterface.FilterOutOwneredResources(result))) + metaObjs := ConvertMapListToMeta(k8sinterface.ConvertUnstructuredSliceToMap(result)) for i := range metaObjs { allResources[metaObjs[i].GetID()] = metaObjs[i] } From 5bb64b634a005a8dc4036470c37735e801fd48e1 Mon Sep 17 00:00:00 2001 From: dwertent Date: Tue, 4 Jan 2022 14:42:25 +0200 Subject: [PATCH 19/19] support loading ks config in env --- cautils/customerloader.go | 93 ++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 35 deletions(-) diff --git a/cautils/customerloader.go b/cautils/customerloader.go index fab50611..b6fc0de1 100644 --- a/cautils/customerloader.go +++ b/cautils/customerloader.go @@ -14,10 +14,7 @@ import ( corev1 "k8s.io/api/core/v1" ) -const ( - configMapName = "kubescape" - configFileName = "config" -) +const configFileName = "config" func ConfigFileFullPath() string { return getter.GetDefaultPath(configFileName + ".json") } @@ -145,26 +142,38 @@ func getTenantConfigFromBE(backendAPI getter.IBackend, configObj *ConfigObj) err // ========================== Cluster Config ============================================ // ====================================================================================== +// ClusterConfig configuration of specific cluster +/* + +Supported environments variables: +KS_DEFAULT_CONFIGMAP_NAME // name of configmap, if not set default is 'kubescape' +KS_DEFAULT_CONFIGMAP_NAMESPACE // configmap namespace, if not set default is 'default' + +TODO - supprot: +KS_ACCOUNT // Account ID +KS_CACHE // path to cached files +*/ type ClusterConfig struct { - k8s *k8sinterface.KubernetesApi - defaultNS string - backendAPI getter.IBackend - configObj *ConfigObj + k8s *k8sinterface.KubernetesApi + configMapName string + configMapNamespace string + backendAPI getter.IBackend + configObj *ConfigObj } func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBackend, customerGUID string) *ClusterConfig { - defaultNS := k8sinterface.GetDefaultNamespace() var configObj *ConfigObj c := &ClusterConfig{ - k8s: k8s, - backendAPI: backendAPI, - configObj: &ConfigObj{}, - defaultNS: defaultNS, + k8s: k8s, + backendAPI: backendAPI, + configObj: &ConfigObj{}, + configMapName: getConfigMapName(), + configMapNamespace: getConfigMapNamespace(), } // get from configMap - if existsConfigMap(k8s, defaultNS) { - configObj, _ = loadConfigFromConfigMap(k8s, defaultNS) + if c.existsConfigMap() { + configObj, _ = c.loadConfigFromConfigMap() } else if existsConfigFile() { // get from file configObj, _ = loadConfigFromFile() } @@ -189,10 +198,10 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken } func (c *ClusterConfig) GetConfigObj() *ConfigObj { return c.configObj } -func (c *ClusterConfig) GetDefaultNS() string { return c.defaultNS } +func (c *ClusterConfig) GetDefaultNS() string { return c.configMapNamespace } func (c *ClusterConfig) GetCustomerGUID() string { return c.configObj.CustomerGUID } func (c *ClusterConfig) IsConfigFound() bool { - return existsConfigFile() || existsConfigMap(c.k8s, c.defaultNS) + return existsConfigFile() || c.existsConfigMap() } func (c *ClusterConfig) SetTenant() error { @@ -202,7 +211,7 @@ func (c *ClusterConfig) SetTenant() error { return err } // update/create config - if existsConfigMap(c.k8s, c.defaultNS) { + if c.existsConfigMap() { c.updateConfigMap() } else { c.createConfigMap() @@ -223,8 +232,8 @@ func (c *ClusterConfig) ToMapString() map[string]interface{} { } return m } -func loadConfigFromConfigMap(k8s *k8sinterface.KubernetesApi, ns string) (*ConfigObj, error) { - configMap, err := k8s.KubernetesClient.CoreV1().ConfigMaps(ns).Get(context.Background(), configMapName, metav1.GetOptions{}) +func (c *ClusterConfig) loadConfigFromConfigMap() (*ConfigObj, error) { + configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Get(context.Background(), c.configMapName, metav1.GetOptions{}) if err != nil { return nil, err } @@ -235,15 +244,15 @@ func loadConfigFromConfigMap(k8s *k8sinterface.KubernetesApi, ns string) (*Confi return nil, nil } -func existsConfigMap(k8s *k8sinterface.KubernetesApi, ns string) bool { - _, err := k8s.KubernetesClient.CoreV1().ConfigMaps(ns).Get(context.Background(), configMapName, metav1.GetOptions{}) +func (c *ClusterConfig) existsConfigMap() bool { + _, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Get(context.Background(), c.configMapName, metav1.GetOptions{}) // TODO - check if has customerGUID return err == nil } func (c *ClusterConfig) GetValueByKeyFromConfigMap(key string) (string, error) { - configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Get(context.Background(), configMapName, metav1.GetOptions{}) + configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Get(context.Background(), c.configMapName, metav1.GetOptions{}) if err != nil { return "", err @@ -295,11 +304,11 @@ func SetKeyValueInConfigJson(key string, value string) error { func (c *ClusterConfig) SetKeyValueInConfigmap(key string, value string) error { - configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Get(context.Background(), configMapName, metav1.GetOptions{}) + configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Get(context.Background(), c.configMapName, metav1.GetOptions{}) if err != nil { configMap = &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: configMapName, + Name: c.configMapName, }, } } @@ -311,9 +320,9 @@ func (c *ClusterConfig) SetKeyValueInConfigmap(key string, value string) error { configMap.Data[key] = value if err != nil { - _, err = c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Create(context.Background(), configMap, metav1.CreateOptions{}) + _, err = c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Create(context.Background(), configMap, metav1.CreateOptions{}) } else { - _, err = c.k8s.KubernetesClient.CoreV1().ConfigMaps(configMap.Namespace).Update(context.Background(), configMap, metav1.UpdateOptions{}) + _, err = c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Update(context.Background(), configMap, metav1.UpdateOptions{}) } return err @@ -330,12 +339,12 @@ func (c *ClusterConfig) createConfigMap() error { } configMap := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{ - Name: configMapName, + Name: c.configMapName, }, } c.updateConfigData(configMap) - _, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Create(context.Background(), configMap, metav1.CreateOptions{}) + _, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Create(context.Background(), configMap, metav1.CreateOptions{}) return err } @@ -343,7 +352,7 @@ func (c *ClusterConfig) updateConfigMap() error { if c.k8s == nil { return nil } - configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Get(context.Background(), configMapName, metav1.GetOptions{}) + configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Get(context.Background(), c.configMapName, metav1.GetOptions{}) if err != nil { return err @@ -351,7 +360,7 @@ func (c *ClusterConfig) updateConfigMap() error { c.updateConfigData(configMap) - _, err = c.k8s.KubernetesClient.CoreV1().ConfigMaps(configMap.Namespace).Update(context.Background(), configMap, metav1.UpdateOptions{}) + _, err = c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Update(context.Background(), configMap, metav1.UpdateOptions{}) return err } @@ -394,7 +403,7 @@ func readConfig(dat []byte) (*ConfigObj, error) { // Check if the customer is submitted func (clusterConfig *ClusterConfig) IsSubmitted() bool { - return existsConfigMap(clusterConfig.k8s, clusterConfig.defaultNS) || existsConfigFile() + return clusterConfig.existsConfigMap() || existsConfigFile() } // Check if the customer is registered @@ -411,7 +420,7 @@ func (clusterConfig *ClusterConfig) IsRegistered() bool { } func (clusterConfig *ClusterConfig) DeleteConfig() error { - if err := DeleteConfigMap(clusterConfig.k8s); err != nil { + if err := clusterConfig.DeleteConfigMap(); err != nil { return err } if err := DeleteConfigFile(); err != nil { @@ -419,8 +428,8 @@ func (clusterConfig *ClusterConfig) DeleteConfig() error { } return nil } -func DeleteConfigMap(k8s *k8sinterface.KubernetesApi) error { - return k8s.KubernetesClient.CoreV1().ConfigMaps(k8sinterface.GetDefaultNamespace()).Delete(context.Background(), configMapName, metav1.DeleteOptions{}) +func (clusterConfig *ClusterConfig) DeleteConfigMap() error { + return clusterConfig.k8s.KubernetesClient.CoreV1().ConfigMaps(clusterConfig.configMapNamespace).Delete(context.Background(), clusterConfig.configMapName, metav1.DeleteOptions{}) } func DeleteConfigFile() error { @@ -430,3 +439,17 @@ func DeleteConfigFile() error { func AdoptClusterName(clusterName string) string { return strings.ReplaceAll(clusterName, "/", "-") } + +func getConfigMapName() string { + if n := os.Getenv("KS_DEFAULT_CONFIGMAP_NAME"); n != "" { + return n + } + return "kubescape" +} + +func getConfigMapNamespace() string { + if n := os.Getenv("KS_DEFAULT_CONFIGMAP_NAMESPACE"); n != "" { + return n + } + return "default" +}