diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d5bba495..7523cbb4 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -53,7 +53,7 @@ jobs: KUBESCAPE_SKIP_UPDATE_CHECK: "true" run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape - - name: Upload Release binaries + - name: Upload release binaries id: upload-release-asset uses: actions/upload-release-asset@v1 env: @@ -64,6 +64,18 @@ jobs: asset_name: kubescape-${{ matrix.os }} asset_content_type: application/octet-stream + - name: Upload release hash + id: upload-release-hash + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.once.outputs.upload_url }} + asset_path: build/${{ matrix.os }}/kubescape.sha256 + asset_name: kubescape-${{ matrix.os }}-sha256 + asset_content_type: application/octet-stream + + build-docker: name: Build docker container, tag and upload to registry diff --git a/README.md b/README.md index ebaa5320..91011846 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ [![build](https://github.com/armosec/kubescape/actions/workflows/build.yaml/badge.svg)](https://github.com/armosec/kubescape/actions/workflows/build.yaml) [![Go Report Card](https://goreportcard.com/badge/github.com/armosec/kubescape)](https://goreportcard.com/report/github.com/armosec/kubescape) + + Kubescape is a K8s open-source tool providing a multi-cloud K8s single pane of glass, including risk analysis, security compliance, RBAC visualizer and image vulnerabilities scanning. Kubescape scans K8s clusters, YAML files, and HELM charts, detecting misconfigurations according to multiple frameworks (such as the [NSA-CISA](https://www.armosec.io/blog/kubernetes-hardening-guidance-summary-by-armo) , [MITRE ATT&CK®](https://www.microsoft.com/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/)), software vulnerabilities, and RBAC (role-based-access-control) violations at early stages of the CI/CD pipeline, calculates risk score instantly and shows risk trends over time. It became one of the fastest-growing Kubernetes tools among developers due to its easy-to-use CLI interface, flexible output formats, and automated scanning capabilities, saving Kubernetes users and admins’ precious time, effort, and resources. @@ -48,6 +50,7 @@ Want to contribute? Want to discuss something? Have an issue? * Open a issue, we are trying to respond within 48 hours * [Join us](https://armosec.github.io/kubescape/) in a discussion on our discord server! +[![discord](https://goreportcard.com/badge/github.com/armosec/kubescape)](https://img.shields.io/discord/893048809884643379) [logo](https://armosec.github.io/kubescape/) diff --git a/build.py b/build.py index fc8a19d3..eeeee049 100644 --- a/build.py +++ b/build.py @@ -52,22 +52,40 @@ def main(): # Create build directory buildDir = getBuildDir() + ks_file = os.path.join(buildDir, packageName) + hash_file = ks_file + ".sha256" + if not os.path.isdir(buildDir): os.makedirs(buildDir) # Build kubescape - ldflags = "-w -s -X %s=%s -X %s=%s -X %s=%s -X %s=%s -X %s=%s" \ - % (buildUrl, releaseVersion, BE_SERVER_CONST, ArmoBEServer, - ER_SERVER_CONST, ArmoERServer, WEBSITE_CONST, ArmoWebsite, - AUTH_SERVER_CONST, ArmoAuthServer) - status = subprocess.call(["go", "build", "-o", "%s/%s" % (buildDir, packageName), "-ldflags" ,ldflags]) + ldflags = "-w -s" + if releaseVersion: + ldflags += " -X {}={}".format(buildUrl, releaseVersion) + if ArmoBEServer: + ldflags += " -X {}={}".format(BE_SERVER_CONST, ArmoBEServer) + if ArmoERServer: + ldflags += " -X {}={}".format(ER_SERVER_CONST, ArmoERServer) + if ArmoWebsite: + ldflags += " -X {}={}".format(WEBSITE_CONST, ArmoWebsite) + if ArmoAuthServer: + ldflags += " -X {}={}".format(AUTH_SERVER_CONST, ArmoAuthServer) + + build_command = ["go", "build", "-o", ks_file, "-ldflags" ,ldflags] + + print("Building kubescape and saving here: {}".format(ks_file)) + print("Build command: {}".format(" ".join(build_command))) + + status = subprocess.call(build_command) checkStatus(status, "Failed to build kubescape") - sha1 = hashlib.sha1() - with open(buildDir + "/" + packageName, "rb") as kube: - sha1.update(kube.read()) - with open(buildDir + "/" + packageName + ".sha1", "w") as kube_sha: - kube_sha.write(sha1.hexdigest()) + sha256 = hashlib.sha256() + with open(ks_file, "rb") as kube: + sha256.update(kube.read()) + with open(hash_file, "w") as kube_sha: + hash = sha256.hexdigest() + print("kubescape hash: {}, file: {}".format(hash, hash_file)) + kube_sha.write(sha256.hexdigest()) print("Build Done") diff --git a/build/Dockerfile b/build/Dockerfile index 20474310..600bcb5e 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -3,7 +3,7 @@ FROM golang:1.17-alpine as builder ARG image_version -ENV RELEASE=image_version +ENV RELEASE=$image_version ENV GO111MODULE= @@ -21,7 +21,6 @@ ADD . . RUN python build.py RUN ls -ltr build/ubuntu-latest -RUN cat /work/build/ubuntu-latest/kubescape.sha1 FROM alpine COPY --from=builder /work/build/ubuntu-latest/kubescape /usr/bin/kubescape diff --git a/cautils/customerloader.go b/cautils/customerloader.go index adec4e65..b156e2b2 100644 --- a/cautils/customerloader.go +++ b/cautils/customerloader.go @@ -11,7 +11,6 @@ import ( "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/kubescape/cautils/getter" - "github.com/armosec/kubescape/cautils/logger" corev1 "k8s.io/api/core/v1" ) @@ -113,12 +112,6 @@ func NewLocalConfig(backendAPI getter.IBackend, customerGUID, clusterName string lc.backendAPI.SetClientID(lc.configObj.ClientID) lc.backendAPI.SetSecretKey(lc.configObj.SecretKey) - if lc.configObj.AccountID != "" { - if err := lc.SetTenant(); err != nil { - logger.L().Error(err.Error()) - } - } - return lc } @@ -228,12 +221,6 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken c.backendAPI.SetClientID(c.configObj.ClientID) c.backendAPI.SetSecretKey(c.configObj.SecretKey) - if c.configObj.AccountID != "" { - if err := c.SetTenant(); err != nil { - logger.L().Error(err.Error()) - } - } - return c } diff --git a/cautils/getter/armoapi.go b/cautils/getter/armoapi.go index cc6a1fd8..dc482553 100644 --- a/cautils/getter/armoapi.go +++ b/cautils/getter/armoapi.go @@ -11,6 +11,7 @@ import ( "github.com/armosec/armoapi-go/armotypes" "github.com/armosec/kubescape/cautils/logger" + "github.com/armosec/kubescape/cautils/logger/helpers" "github.com/armosec/opa-utils/reporthandling" ) @@ -26,6 +27,11 @@ var ( armoFEURL = "portal.armo.cloud" armoAUTHURL = "auth.armo.cloud" + armoStageERURL = "report-ks.eustage2.cyberarmorsoft" + armoStageBEURL = "api-stage.armo.cloud" + armoStageFEURL = "armoui.eustage2.cyberarmorsoft.com" + armoStageAUTHURL = "eggauth.eustage2.cyberarmorsoft.com" + armoDevERURL = "report.eudev3.cyberarmorsoft.com" armoDevBEURL = "api-dev.armo.cloud" armoDevFEURL = "armoui-dev.eudev3.cyberarmorsoft.com" @@ -50,6 +56,7 @@ type ArmoAPI struct { var globalArmoAPIConnector *ArmoAPI func SetARMOAPIConnector(armoAPI *ArmoAPI) { + logger.L().Debug("Armo URLs", helpers.String("api", armoAPI.apiURL), helpers.String("auth", armoAPI.authURL), helpers.String("report", armoAPI.erURL), helpers.String("UI", armoAPI.feURL)) globalArmoAPIConnector = armoAPI } @@ -82,6 +89,17 @@ func NewARMOAPIProd() *ArmoAPI { return apiObj } +func NewARMOAPIStaging() *ArmoAPI { + apiObj := newArmoAPI() + + apiObj.apiURL = armoStageBEURL + apiObj.erURL = armoStageERURL + apiObj.feURL = armoStageFEURL + apiObj.authURL = armoStageAUTHURL + + return apiObj +} + func NewARMOAPICustomized(armoERURL, armoBEURL, armoFEURL, armoAUTHURL string) *ArmoAPI { apiObj := newArmoAPI() diff --git a/cautils/getter/armoapiutils.go b/cautils/getter/armoapiutils.go index 39674036..0b213b48 100644 --- a/cautils/getter/armoapiutils.go +++ b/cautils/getter/armoapiutils.go @@ -17,7 +17,7 @@ func (armoAPI *ArmoAPI) getFrameworkURL(frameworkName string) string { u.Host = armoAPI.apiURL u.Path = "api/v1/armoFrameworks" q := u.Query() - q.Add("customerGUID", armoAPI.accountID) + q.Add("customerGUID", armoAPI.getCustomerGUIDFallBack()) if isNativeFramework(frameworkName) { q.Add("frameworkName", strings.ToUpper(frameworkName)) } else { @@ -35,7 +35,7 @@ func (armoAPI *ArmoAPI) getListFrameworkURL() string { u.Host = armoAPI.apiURL u.Path = "api/v1/armoFrameworks" q := u.Query() - q.Add("customerGUID", armoAPI.accountID) + q.Add("customerGUID", armoAPI.getCustomerGUIDFallBack()) u.RawQuery = q.Encode() return u.String() @@ -47,7 +47,7 @@ func (armoAPI *ArmoAPI) getExceptionsURL(clusterName string) string { u.Path = "api/v1/armoPostureExceptions" q := u.Query() - q.Add("customerGUID", armoAPI.accountID) + q.Add("customerGUID", armoAPI.getCustomerGUIDFallBack()) // if clusterName != "" { // TODO - fix customer name support in Armo BE // q.Add("clusterName", clusterName) // } @@ -63,7 +63,7 @@ func (armoAPI *ArmoAPI) postExceptionsURL() string { u.Path = "api/v1/postureExceptionPolicy" q := u.Query() - q.Add("customerGUID", armoAPI.accountID) + q.Add("customerGUID", armoAPI.getCustomerGUIDFallBack()) u.RawQuery = q.Encode() return u.String() @@ -76,7 +76,7 @@ func (armoAPI *ArmoAPI) getAccountConfig(clusterName string) string { u.Path = "api/v1/armoCustomerConfiguration" q := u.Query() - q.Add("customerGUID", armoAPI.accountID) + q.Add("customerGUID", armoAPI.getCustomerGUIDFallBack()) if clusterName != "" { // TODO - fix customer name support in Armo BE q.Add("clusterName", clusterName) } @@ -156,3 +156,10 @@ func (armoAPI *ArmoAPI) appendAuthHeaders(headers map[string]string) { headers["Cookie"] = fmt.Sprintf("auth=%s", armoAPI.authCookie) } } + +func (armoAPI *ArmoAPI) getCustomerGUIDFallBack() string { + if armoAPI.accountID != "" { + return armoAPI.accountID + } + return "11111111-1111-1111-1111-111111111111" +} diff --git a/cautils/getter/getpoliciesutils.go b/cautils/getter/getpoliciesutils.go index 30b82e80..d577234a 100644 --- a/cautils/getter/getpoliciesutils.go +++ b/cautils/getter/getpoliciesutils.go @@ -13,11 +13,7 @@ import ( ) func GetDefaultPath(name string) string { - defaultfilePath := filepath.Join(DefaultLocalStore, name) - if homeDir, err := os.UserHomeDir(); err == nil { - defaultfilePath = filepath.Join(homeDir, defaultfilePath) - } - return defaultfilePath + return filepath.Join(DefaultLocalStore, name) } func SaveInFile(policy interface{}, pathStr string) error { diff --git a/cautils/getter/loadpolicy.go b/cautils/getter/loadpolicy.go index 7ae8c8f5..12d56555 100644 --- a/cautils/getter/loadpolicy.go +++ b/cautils/getter/loadpolicy.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "os" + "path/filepath" "strings" "github.com/armosec/armoapi-go/armotypes" @@ -13,7 +14,15 @@ import ( // ======================================================================================================================= // ============================================== LoadPolicy ============================================================= // ======================================================================================================================= -const DefaultLocalStore = ".kubescape" +var DefaultLocalStore = getCacheDir() + +func getCacheDir() string { + defaultDirPath := ".kubescape" + if homeDir, err := os.UserHomeDir(); err == nil { + defaultDirPath = filepath.Join(homeDir, defaultDirPath) + } + return defaultDirPath +} // Load policies from a local repository type LoadPolicy struct { diff --git a/cautils/logger/methods.go b/cautils/logger/methods.go index 959abe42..be208fd2 100644 --- a/cautils/logger/methods.go +++ b/cautils/logger/methods.go @@ -2,9 +2,12 @@ package logger import ( "os" + "strings" "github.com/armosec/kubescape/cautils/logger/helpers" "github.com/armosec/kubescape/cautils/logger/prettylogger" + "github.com/armosec/kubescape/cautils/logger/zaplogger" + "github.com/mattn/go-isatty" ) type ILogger interface { @@ -26,16 +29,23 @@ var l ILogger func L() ILogger { if l == nil { - InitializeLogger() + InitializeLogger("") } return l } -func InitializeLogger() { - initializeLogger() -} +func InitializeLogger(loggerName string) { -func initializeLogger() { - // TODO - support zap logger - l = prettylogger.NewPrettyLogger() + switch strings.ToLower(loggerName) { + case "zap": + l = zaplogger.NewZapLogger() + case "pretty": + l = prettylogger.NewPrettyLogger() + default: + if isatty.IsTerminal(os.Stdout.Fd()) { + l = prettylogger.NewPrettyLogger() + } else { + l = zaplogger.NewZapLogger() + } + } } diff --git a/cautils/logger/zaplogger/logger.go b/cautils/logger/zaplogger/logger.go index 5300ccfb..bd2c33c5 100644 --- a/cautils/logger/zaplogger/logger.go +++ b/cautils/logger/zaplogger/logger.go @@ -10,20 +10,40 @@ import ( type ZapLogger struct { zapL *zap.Logger + cfg zap.Config } func NewZapLogger() *ZapLogger { + ec := zap.NewProductionEncoderConfig() + ec.EncodeTime = zapcore.RFC3339TimeEncoder + cfg := zap.NewProductionConfig() + cfg.DisableCaller = true + cfg.DisableStacktrace = true + cfg.Encoding = "json" + cfg.EncoderConfig = ec + + zapLogger, err := cfg.Build() + if err != nil { + panic(err) + } return &ZapLogger{ - zapL: zap.L(), + zapL: zapLogger, + cfg: cfg, } } -func (zl *ZapLogger) GetLevel() string { return "" } +func (zl *ZapLogger) GetLevel() string { return zl.cfg.Level.Level().String() } func (zl *ZapLogger) SetWriter(w *os.File) {} +func (zl *ZapLogger) GetWriter() *os.File { return nil } func GetWriter() *os.File { return nil } func (zl *ZapLogger) SetLevel(level string) error { - return nil + l := zapcore.Level(1) + err := l.Set(level) + if err == nil { + zl.cfg.Level.SetLevel(l) + } + return err } func (zl *ZapLogger) Fatal(msg string, details ...helpers.IDetails) { zl.zapL.Fatal(msg, detailsToZapFields(details)...) diff --git a/cautils/scaninfo.go b/cautils/scaninfo.go index eb0c4a4c..128d4ce1 100644 --- a/cautils/scaninfo.go +++ b/cautils/scaninfo.go @@ -73,6 +73,7 @@ type ScanInfo struct { Local bool // Do not submit results Account string // account ID Logger string // logger level + CacheDir string // cached dir KubeContext string // context name FrameworkScan bool // false if scanning control ScanAll bool // true if scan all frameworks diff --git a/clihandler/clidownload.go b/clihandler/clidownload.go index 35c1fdf9..ec9b02d3 100644 --- a/clihandler/clidownload.go +++ b/clihandler/clidownload.go @@ -77,6 +77,7 @@ func downloadArtifacts(downloadInfo *cautils.DownloadInfo) error { func downloadConfigInputs(downloadInfo *cautils.DownloadInfo) error { tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) + controlsInputsGetter := getConfigInputsGetter(downloadInfo.Name, tenant.GetAccountID(), nil) controlInputs, err := controlsInputsGetter.GetControlsInputs(tenant.GetClusterName()) if err != nil { @@ -97,6 +98,7 @@ func downloadConfigInputs(downloadInfo *cautils.DownloadInfo) error { func downloadExceptions(downloadInfo *cautils.DownloadInfo) error { var err error tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) + exceptionsGetter := getExceptionsGetter("") exceptions := []armotypes.PostureExceptionPolicy{} if tenant.GetAccountID() != "" { @@ -120,6 +122,7 @@ func downloadExceptions(downloadInfo *cautils.DownloadInfo) error { func downloadFramework(downloadInfo *cautils.DownloadInfo) error { tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) + g := getPolicyGetter(nil, tenant.GetAccountID(), true, nil) if downloadInfo.Name == "" { @@ -156,6 +159,7 @@ func downloadFramework(downloadInfo *cautils.DownloadInfo) error { func downloadControl(downloadInfo *cautils.DownloadInfo) error { tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) + g := getPolicyGetter(nil, tenant.GetAccountID(), false, nil) if downloadInfo.Name == "" { diff --git a/clihandler/clilist.go b/clihandler/clilist.go index dda856ee..852c0000 100644 --- a/clihandler/clilist.go +++ b/clihandler/clilist.go @@ -32,7 +32,7 @@ func CliList(listPolicies *cliobjects.ListPolicies) error { sep := "\n * " usageCmd := strings.TrimSuffix(listPolicies.Target, "s") fmt.Printf("Supported %s:%s%s\n", listPolicies.Target, sep, strings.Join(policies, sep)) - fmt.Printf("\nUseage:\n") + fmt.Printf("\nUsage:\n") fmt.Printf("$ kubescape scan %s \"name\"\n", usageCmd) fmt.Printf("$ kubescape scan %s \"name-0\",\"name-1\"\n\n", usageCmd) return nil @@ -49,6 +49,7 @@ func listFrameworks(listPolicies *cliobjects.ListPolicies) ([]string, error) { func listControls(listPolicies *cliobjects.ListPolicies) ([]string, error) { tenant := getTenantConfig(listPolicies.Account, "", getKubernetesApi()) // change k8sinterface + g := getPolicyGetter(nil, tenant.GetAccountID(), false, nil) l := getter.ListName if listPolicies.ListIDs { diff --git a/clihandler/clisubmit.go b/clihandler/clisubmit.go index 2f19aff0..878fc5f7 100644 --- a/clihandler/clisubmit.go +++ b/clihandler/clisubmit.go @@ -33,7 +33,10 @@ func SubmitExceptions(accountID, excPath string) error { logger.L().Info("submitting exceptions", helpers.String("path", excPath)) // load cached config - getTenantConfig(accountID, "", getKubernetesApi()) + tenantConfig := getTenantConfig(accountID, "", getKubernetesApi()) + if err := tenantConfig.SetTenant(); err != nil { + logger.L().Error("failed setting account ID", helpers.Error(err)) + } // load exceptions from file loader := getter.NewLoadPolicy([]string{excPath}) diff --git a/clihandler/cmd/rbac.go b/clihandler/cmd/rbac.go index 938b73b9..a666a58e 100644 --- a/clihandler/cmd/rbac.go +++ b/clihandler/cmd/rbac.go @@ -5,6 +5,7 @@ import ( "github.com/armosec/kubescape/cautils" "github.com/armosec/kubescape/cautils/getter" "github.com/armosec/kubescape/cautils/logger" + "github.com/armosec/kubescape/cautils/logger/helpers" "github.com/armosec/kubescape/clihandler" "github.com/armosec/kubescape/clihandler/cliinterfaces" reporterv1 "github.com/armosec/kubescape/resultshandling/reporter/v1" @@ -23,6 +24,9 @@ var rabcCmd = &cobra.Command{ // get config clusterConfig := getTenantConfig(submitInfo.Account, "", k8s) + if err := clusterConfig.SetTenant(); err != nil { + logger.L().Error("failed setting account ID", helpers.Error(err)) + } // list RBAC rbacObjects := cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, clusterConfig.GetAccountID(), clusterConfig.GetClusterName())) diff --git a/clihandler/cmd/results.go b/clihandler/cmd/results.go index ccf2d536..f7c09896 100644 --- a/clihandler/cmd/results.go +++ b/clihandler/cmd/results.go @@ -8,6 +8,7 @@ import ( "github.com/armosec/k8s-interface/workloadinterface" "github.com/armosec/kubescape/cautils/logger" + "github.com/armosec/kubescape/cautils/logger/helpers" "github.com/armosec/kubescape/clihandler" "github.com/armosec/kubescape/clihandler/cliinterfaces" reporterv1 "github.com/armosec/kubescape/resultshandling/reporter/v1" @@ -63,6 +64,9 @@ var resultsCmd = &cobra.Command{ // get config clusterConfig := getTenantConfig(submitInfo.Account, "", k8s) + if err := clusterConfig.SetTenant(); err != nil { + logger.L().Error("failed setting account ID", helpers.Error(err)) + } resultsObjects := NewResultsObject(clusterConfig.GetAccountID(), clusterConfig.GetClusterName(), args[0]) diff --git a/clihandler/cmd/root.go b/clihandler/cmd/root.go index 8c20df28..96245611 100644 --- a/clihandler/cmd/root.go +++ b/clihandler/cmd/root.go @@ -3,8 +3,10 @@ package cmd import ( "flag" "fmt" + "os" "strings" + "github.com/armosec/kubescape/cautils" "github.com/armosec/kubescape/cautils/getter" "github.com/armosec/kubescape/cautils/logger" "github.com/armosec/kubescape/cautils/logger/helpers" @@ -31,6 +33,7 @@ var ksExamples = ` var rootCmd = &cobra.Command{ Use: "kubescape", + Version: cautils.BuildNumber, Short: "Kubescape is a tool for testing Kubernetes security posture", Long: `Kubescape is a tool for testing Kubernetes security posture based on NSA \ MITRE ATT&CK® and other frameworks specifications`, Example: ksExamples, @@ -40,20 +43,42 @@ func Execute() { rootCmd.Execute() } func init() { - cobra.OnInitialize(initLogger, initEnvironment) + cobra.OnInitialize(initLogger, initLoggerLevel, initEnvironment, initCacheDir) flag.CommandLine.StringVar(&armoBEURLs, "environment", "", envFlagUsage) rootCmd.PersistentFlags().StringVar(&armoBEURLs, "environment", "", envFlagUsage) rootCmd.PersistentFlags().MarkHidden("environment") - rootCmd.PersistentFlags().StringVar(&scanInfo.Logger, "logger", "info", fmt.Sprintf("Logger level. Supported: %s", strings.Join(helpers.SupportedLevels(), "/"))) + rootCmd.PersistentFlags().StringVarP(&scanInfo.Logger, "logger", "l", helpers.InfoLevel.String(), fmt.Sprintf("Logger level. Supported: %s [$KS_LOGGER]", strings.Join(helpers.SupportedLevels(), "/"))) + rootCmd.PersistentFlags().StringVar(&scanInfo.CacheDir, "cache-dir", getter.DefaultLocalStore, "Cache directory [$KS_CACHE_DIR]") flag.Parse() } func initLogger() { + if l := os.Getenv("KS_LOGGER_NAME"); l != "" { + logger.InitializeLogger(l) + } +} +func initLoggerLevel() { + if scanInfo.Logger != helpers.InfoLevel.String() { + } else if l := os.Getenv("KS_LOGGER"); l != "" { + scanInfo.Logger = l + } if err := logger.L().SetLevel(scanInfo.Logger); err != nil { logger.L().Fatal(fmt.Sprintf("supported levels: %s", strings.Join(helpers.SupportedLevels(), "/")), helpers.Error(err)) } } + +func initCacheDir() { + if scanInfo.CacheDir != getter.DefaultLocalStore { + getter.DefaultLocalStore = scanInfo.CacheDir + } else if cacheDir := os.Getenv("KS_CACHE_DIR"); cacheDir != "" { + getter.DefaultLocalStore = cacheDir + } else { + return // using default cache di location + } + + logger.L().Debug("cache dir updated", helpers.String("path", getter.DefaultLocalStore)) +} func initEnvironment() { urlSlices := strings.Split(armoBEURLs, ",") if len(urlSlices) != 1 && len(urlSlices) < 3 { @@ -62,8 +87,10 @@ func initEnvironment() { switch len(urlSlices) { case 1: switch urlSlices[0] { - case "dev": + case "dev", "development": getter.SetARMOAPIConnector(getter.NewARMOAPIDev()) + case "stage", "staging": + getter.SetARMOAPIConnector(getter.NewARMOAPIStaging()) case "": getter.SetARMOAPIConnector(getter.NewARMOAPIProd()) default: diff --git a/clihandler/initcliutils.go b/clihandler/initcliutils.go index a03a5d62..1a364ea1 100644 --- a/clihandler/initcliutils.go +++ b/clihandler/initcliutils.go @@ -216,8 +216,8 @@ func getDefaultFrameworksPaths() []string { func listFrameworksNames(policyGetter getter.IPolicyGetter) []string { fw, err := policyGetter.ListFrameworks() - if err != nil { - fw = getDefaultFrameworksPaths() + if err == nil { + return fw } - return fw + return getter.NativeFrameworks } diff --git a/hostsensorutils/hostsensor.yaml b/hostsensorutils/hostsensor.yaml index bc03b94c..5647a162 100644 --- a/hostsensorutils/hostsensor.yaml +++ b/hostsensorutils/hostsensor.yaml @@ -40,6 +40,7 @@ spec: - name: http hostPort: 7888 containerPort: 7888 + protocol: TCP resources: limits: cpu: 0.1m diff --git a/resourcehandler/k8sresources.go b/resourcehandler/k8sresources.go index 2e41daea..0a1fbba2 100644 --- a/resourcehandler/k8sresources.go +++ b/resourcehandler/k8sresources.go @@ -168,6 +168,18 @@ func ConvertMapListToMeta(resourceMap []map[string]interface{}) []workloadinterf return workloads } +// func (k8sHandler *K8sResourceHandler) collectHostResourcesAPI(allResources map[string]workloadinterface.IMetadata, resourcesMap *cautils.K8SResources) error { + +// HostSensorAPI := map[string]string{ +// "bla/v1": "", +// } +// for apiVersion := range allResources { +// if HostSensorAPI == apiVersion { +// k8sHandler.collectHostResources() +// } +// } +// return nil +// } func (k8sHandler *K8sResourceHandler) collectHostResources(allResources map[string]workloadinterface.IMetadata, resourcesMap *cautils.K8SResources) error { logger.L().Debug("Collecting host sensor resources") @@ -175,6 +187,7 @@ func (k8sHandler *K8sResourceHandler) collectHostResources(allResources map[stri if err != nil { return err } + for rscIdx := range hostResources { group, version := getGroupNVersion(hostResources[rscIdx].GetApiVersion()) groupResource := k8sinterface.JoinResourceTriplets(group, version, hostResources[rscIdx].GetKind()) @@ -220,9 +233,10 @@ func getCloudProviderDescription(allResources map[string]workloadinterface.IMeta if err != nil { return err } - logger.L().Debug("cloud", helpers.String("cluster", cluster), helpers.String("clusterName", clusterName), helpers.String("provider", provider), helpers.String("region", region), helpers.String("project", project)) if provider != "" { + logger.L().Debug("cloud", helpers.String("cluster", cluster), helpers.String("clusterName", clusterName), helpers.String("provider", provider), helpers.String("region", region), helpers.String("project", project)) + wl, err := cloudsupport.GetDescriptiveInfoFromCloudProvider(clusterName, provider, region, project) if err != nil { // Return error with useful info on how to configure credentials for getting cloud provider info diff --git a/resultshandling/reporter/v1/mockreporter.go b/resultshandling/reporter/v1/mockreporter.go index 451a59f4..8ffc01fe 100644 --- a/resultshandling/reporter/v1/mockreporter.go +++ b/resultshandling/reporter/v1/mockreporter.go @@ -25,6 +25,6 @@ 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()) + message := fmt.Sprintf("\nScan results have not been submitted.\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.Stderr, fmt.Sprintf("\n%s\n", message)) } diff --git a/resultshandling/reporter/v1/reporteventreceiver.go b/resultshandling/reporter/v1/reporteventreceiver.go index 4b815b65..abdbdcc9 100644 --- a/resultshandling/reporter/v1/reporteventreceiver.go +++ b/resultshandling/reporter/v1/reporteventreceiver.go @@ -44,11 +44,11 @@ func (report *ReportEventReceiver) ActionSendReport(opaSessionObj *cautils.OPASe } if report.customerGUID == "" { - report.message = "WARNING: Failed to publish results. Reason: Unknown accout ID. Run kubescape with the '--account ' flag. Contact ARMO team for more details" + report.message = "WARNING: Failed to publish results. Reason: Unknown accout ID. Run kubescape with the '--account ' flag. Please feel free to contact ARMO team for more details" return nil } if report.clusterName == "" { - report.message = "WARNING: Failed to publish results because the cluster name is Unknown. If you are scanning YAML files the results are not submitted to the Kubescape SaaS" + report.message = "WARNING: Failed to publish results because the cluster name is Unknown. If you are scanning YAML files the results are not submitted to the Kubescape SaaS.Please feel free to contact ARMO team for more details" return nil } @@ -144,7 +144,7 @@ func (report *ReportEventReceiver) generateMessage() { if report.customerAdminEMail != "" { logger.L().Debug("", helpers.String("account ID", report.customerGUID)) - report.message = fmt.Sprintf("%s %s/risk/%s", message, u.String(), report.clusterName) + report.message = fmt.Sprintf("%s %s/configuration-scanning/%s", message, u.String(), report.clusterName) return } u.Path = "account/sign-up" diff --git a/resultshandling/reporter/v2/reporteventreceiver.go b/resultshandling/reporter/v2/reporteventreceiver.go index db29b77f..77ba546e 100644 --- a/resultshandling/reporter/v2/reporteventreceiver.go +++ b/resultshandling/reporter/v2/reporteventreceiver.go @@ -60,6 +60,8 @@ func (report *ReportEventReceiver) ActionSendReport(opaSessionObj *cautils.OPASe } else { report.generateMessage() } + logger.L().Debug("", helpers.String("account ID", report.customerGUID)) + return nil } @@ -202,24 +204,28 @@ func (report *ReportEventReceiver) sendReport(host string, postureReport *report } func (report *ReportEventReceiver) generateMessage() { - message := "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 registering here:" u := url.URL{} u.Scheme = "https" u.Host = getter.GetArmoAPIConnector().GetFrontendURL() - if report.customerAdminEMail != "" { - logger.L().Debug("", helpers.String("account ID", report.customerGUID)) - report.message = fmt.Sprintf("%s %s/risk/%s", message, u.String(), report.clusterName) - return + if report.customerAdminEMail != "" { // data has been submitted + u.Path = fmt.Sprintf("configuration-scanning/%s", report.clusterName) + } else { + u.Path = "account/sign-up" + q := u.Query() + q.Add("invitationToken", report.token) + q.Add("customerGUID", report.customerGUID) + + u.RawQuery = q.Encode() } - u.Path = "account/sign-up" - q := u.Query() - q.Add("invitationToken", report.token) - q.Add("customerGUID", report.customerGUID) + + sep := "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" + report.message = sep + report.message += " << WOW! Now you can see the scan results on the web >>\n\n" + report.message += fmt.Sprintf(" %s\n", u.String()) + report.message += sep - u.RawQuery = q.Encode() - report.message = fmt.Sprintf("%s %s", message, u.String()) } func (report *ReportEventReceiver) DisplayReportURL() {