diff --git a/cautils/customerloader.go b/cautils/customerloader.go index b6fc0de1..f872a0eb 100644 --- a/cautils/customerloader.go +++ b/cautils/customerloader.go @@ -104,10 +104,11 @@ func NewLocalConfig(backendAPI getter.IBackend, customerGUID string) *LocalConfi return lc } -func (lc *LocalConfig) GetConfigObj() *ConfigObj { return lc.configObj } -func (lc *LocalConfig) GetCustomerGUID() string { return lc.configObj.CustomerGUID } -func (lc *LocalConfig) GetClusterName() string { return "" } -func (lc *LocalConfig) IsConfigFound() bool { return existsConfigFile() } +func (lc *LocalConfig) GetConfigObj() *ConfigObj { return lc.configObj } +func (lc *LocalConfig) GetCustomerGUID() string { return lc.configObj.CustomerGUID } +func (lc *LocalConfig) SetCustomerGUID(customerGUID string) { lc.configObj.CustomerGUID = customerGUID } +func (lc *LocalConfig) GetClusterName() string { return "" } +func (lc *LocalConfig) IsConfigFound() bool { return existsConfigFile() } func (lc *LocalConfig) SetTenant() error { // ARMO tenant GUID if err := getTenantConfigFromBE(lc.backendAPI, lc.configObj); err != nil { @@ -121,7 +122,8 @@ func (lc *LocalConfig) SetTenant() error { func getTenantConfigFromBE(backendAPI getter.IBackend, configObj *ConfigObj) error { // get from armoBE - tenantResponse, err := backendAPI.GetCustomerGUID(configObj.CustomerGUID) + backendAPI.SetCustomerGUID(configObj.CustomerGUID) + tenantResponse, err := backendAPI.GetCustomerGUID() if err == nil && tenantResponse != nil { if tenantResponse.AdminMail != "" { // registered tenant configObj.CustomerAdminEMail = tenantResponse.AdminMail @@ -197,9 +199,10 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken return c } -func (c *ClusterConfig) GetConfigObj() *ConfigObj { return c.configObj } -func (c *ClusterConfig) GetDefaultNS() string { return c.configMapNamespace } -func (c *ClusterConfig) GetCustomerGUID() string { return c.configObj.CustomerGUID } +func (c *ClusterConfig) GetConfigObj() *ConfigObj { return c.configObj } +func (c *ClusterConfig) GetDefaultNS() string { return c.configMapNamespace } +func (c *ClusterConfig) GetCustomerGUID() string { return c.configObj.CustomerGUID } +func (c *ClusterConfig) SetCustomerGUID(customerGUID string) { c.configObj.CustomerGUID = customerGUID } func (c *ClusterConfig) IsConfigFound() bool { return existsConfigFile() || c.existsConfigMap() } @@ -410,7 +413,8 @@ func (clusterConfig *ClusterConfig) IsSubmitted() bool { func (clusterConfig *ClusterConfig) IsRegistered() bool { // get from armoBE - tenantResponse, err := clusterConfig.backendAPI.GetCustomerGUID(clusterConfig.GetCustomerGUID()) + clusterConfig.backendAPI.SetCustomerGUID(clusterConfig.GetCustomerGUID()) + tenantResponse, err := clusterConfig.backendAPI.GetCustomerGUID() if err == nil && tenantResponse != nil { if tenantResponse.AdminMail != "" { // this customer already belongs to some user return true diff --git a/cautils/downloadinfo.go b/cautils/downloadinfo.go index c54601eb..548bdc4f 100644 --- a/cautils/downloadinfo.go +++ b/cautils/downloadinfo.go @@ -1,7 +1,8 @@ package cautils type DownloadInfo struct { - Path string - FrameworkName string - ControlName string + Path string + Target string + Name string + Account string } diff --git a/cautils/getter/armoapi.go b/cautils/getter/armoapi.go index ed920136..cf8d1fd3 100644 --- a/cautils/getter/armoapi.go +++ b/cautils/getter/armoapi.go @@ -107,7 +107,7 @@ func (armoAPI *ArmoAPI) GetFramework(name string) (*reporthandling.Framework, er if err = JSONDecoder(respStr).Decode(framework); err != nil { return nil, err } - SaveFrameworkInFile(framework, GetDefaultPath(name+".json")) + SaveInFile(framework, GetDefaultPath(name+".json")) return framework, err } @@ -116,12 +116,10 @@ func (armoAPI *ArmoAPI) GetControl(policyName string) (*reporthandling.Control, return nil, fmt.Errorf("control api is not public") } -func (armoAPI *ArmoAPI) GetExceptions(customerGUID, clusterName string) ([]armotypes.PostureExceptionPolicy, error) { +func (armoAPI *ArmoAPI) GetExceptions(clusterName string) ([]armotypes.PostureExceptionPolicy, error) { exceptions := []armotypes.PostureExceptionPolicy{} - if customerGUID == "" { - return exceptions, nil - } - respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getExceptionsURL(customerGUID, clusterName), nil) + + respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getExceptionsURL(clusterName), nil) if err != nil { return nil, err } @@ -133,10 +131,10 @@ func (armoAPI *ArmoAPI) GetExceptions(customerGUID, clusterName string) ([]armot return exceptions, nil } -func (armoAPI *ArmoAPI) GetCustomerGUID(customerGUID string) (*TenantResponse, error) { +func (armoAPI *ArmoAPI) GetCustomerGUID() (*TenantResponse, error) { url := armoAPI.getCustomerURL() - if customerGUID != "" { - url = fmt.Sprintf("%s?customerGUID=%s", url, customerGUID) + if armoAPI.customerGUID != "" { + url = fmt.Sprintf("%s?customerGUID=%s", url, armoAPI.customerGUID) } respStr, err := HttpGetter(armoAPI.httpClient, url, nil) if err != nil { @@ -151,12 +149,12 @@ func (armoAPI *ArmoAPI) GetCustomerGUID(customerGUID string) (*TenantResponse, e } // ControlsInputs // map[][] -func (armoAPI *ArmoAPI) GetAccountConfig(customerGUID, clusterName string) (*armotypes.CustomerConfig, error) { +func (armoAPI *ArmoAPI) GetAccountConfig(clusterName string) (*armotypes.CustomerConfig, error) { accountConfig := &armotypes.CustomerConfig{} - if customerGUID == "" { + if armoAPI.customerGUID == "" { return accountConfig, nil } - respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getAccountConfig(customerGUID, clusterName), nil) + respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getAccountConfig(clusterName), nil) if err != nil { return nil, err } @@ -169,15 +167,15 @@ func (armoAPI *ArmoAPI) GetAccountConfig(customerGUID, clusterName string) (*arm } // ControlsInputs // map[][] -func (armoAPI *ArmoAPI) GetControlsInputs(customerGUID, clusterName string) (map[string][]string, error) { - accountConfig, err := armoAPI.GetAccountConfig(customerGUID, clusterName) +func (armoAPI *ArmoAPI) GetControlsInputs(clusterName string) (map[string][]string, error) { + accountConfig, err := armoAPI.GetAccountConfig(clusterName) if err == nil { return accountConfig.Settings.PostureControlInputs, nil } return nil, err } -func (armoAPI *ArmoAPI) ListCustomFrameworks(customerGUID string) ([]string, error) { +func (armoAPI *ArmoAPI) ListCustomFrameworks() ([]string, error) { respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getListFrameworkURL(), nil) if err != nil { return nil, err @@ -197,7 +195,7 @@ func (armoAPI *ArmoAPI) ListCustomFrameworks(customerGUID string) ([]string, err return frameworkList, nil } -func (armoAPI *ArmoAPI) ListFrameworks(customerGUID string) ([]string, error) { +func (armoAPI *ArmoAPI) ListFrameworks() ([]string, error) { respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getListFrameworkURL(), nil) if err != nil { return nil, err @@ -219,6 +217,10 @@ func (armoAPI *ArmoAPI) ListFrameworks(customerGUID string) ([]string, error) { return frameworkList, nil } +func (armoAPI *ArmoAPI) ListControls(l ListType) ([]string, error) { + return nil, fmt.Errorf("control api is not public") +} + type TenantResponse struct { TenantID string `json:"tenantId"` Token string `json:"token"` diff --git a/cautils/getter/armoapiutils.go b/cautils/getter/armoapiutils.go index 8cb157ef..3c14040f 100644 --- a/cautils/getter/armoapiutils.go +++ b/cautils/getter/armoapiutils.go @@ -36,14 +36,14 @@ func (armoAPI *ArmoAPI) getListFrameworkURL() string { return u.String() } -func (armoAPI *ArmoAPI) getExceptionsURL(customerGUID, clusterName string) string { +func (armoAPI *ArmoAPI) getExceptionsURL(clusterName string) string { u := url.URL{} u.Scheme = "https" u.Host = armoAPI.apiURL u.Path = "api/v1/armoPostureExceptions" q := u.Query() - q.Add("customerGUID", customerGUID) + q.Add("customerGUID", armoAPI.customerGUID) // if clusterName != "" { // TODO - fix customer name support in Armo BE // q.Add("clusterName", clusterName) // } @@ -52,14 +52,14 @@ func (armoAPI *ArmoAPI) getExceptionsURL(customerGUID, clusterName string) strin return u.String() } -func (armoAPI *ArmoAPI) getAccountConfig(customerGUID, clusterName string) string { +func (armoAPI *ArmoAPI) getAccountConfig(clusterName string) string { u := url.URL{} u.Scheme = "https" u.Host = armoAPI.apiURL u.Path = "api/v1/armoCustomerConfiguration" q := u.Query() - q.Add("customerGUID", customerGUID) + q.Add("customerGUID", armoAPI.customerGUID) if clusterName != "" { // TODO - fix customer name support in Armo BE q.Add("clusterName", clusterName) } diff --git a/cautils/getter/downloadreleasedpolicy.go b/cautils/getter/downloadreleasedpolicy.go index 6450449e..0ae9e034 100644 --- a/cautils/getter/downloadreleasedpolicy.go +++ b/cautils/getter/downloadreleasedpolicy.go @@ -41,7 +41,20 @@ func (drp *DownloadReleasedPolicy) GetFramework(name string) (*reporthandling.Fr return framework, err } -func (drp *DownloadReleasedPolicy) GetControlsInputs(customerGUID, clusterName string) (map[string][]string, error) { +func (drp *DownloadReleasedPolicy) ListFrameworks() ([]string, error) { + return drp.gs.GetOPAFrameworksNamesList() +} + +func (drp *DownloadReleasedPolicy) ListControls(listType ListType) ([]string, error) { + switch listType { + case ListID: + return drp.gs.GetOPAControlsIDsList() + default: + return drp.gs.GetOPAControlsNamesList() + } +} + +func (drp *DownloadReleasedPolicy) GetControlsInputs(clusterName string) (map[string][]string, error) { defaultConfigInputs, err := drp.gs.GetDefaultConfigInputs() if err != nil { return nil, err diff --git a/cautils/getter/getpolicies.go b/cautils/getter/getpolicies.go index 819efe02..1a30c6f3 100644 --- a/cautils/getter/getpolicies.go +++ b/cautils/getter/getpolicies.go @@ -5,18 +5,28 @@ import ( "github.com/armosec/opa-utils/reporthandling" ) +// supported listing +type ListType string + +const ListID ListType = "id" +const ListName ListType = "name" + type IPolicyGetter interface { GetFramework(name string) (*reporthandling.Framework, error) GetControl(name string) (*reporthandling.Control, error) + + ListFrameworks() ([]string, error) + ListControls(ListType) ([]string, error) } type IExceptionsGetter interface { - GetExceptions(customerGUID, clusterName string) ([]armotypes.PostureExceptionPolicy, error) + GetExceptions(clusterName string) ([]armotypes.PostureExceptionPolicy, error) } type IBackend interface { - GetCustomerGUID(customerGUID string) (*TenantResponse, error) + GetCustomerGUID() (*TenantResponse, error) + SetCustomerGUID(customerGUID string) } type IControlsInputsGetter interface { - GetControlsInputs(customerGUID, clusterName string) (map[string][]string, error) + GetControlsInputs(clusterName string) (map[string][]string, error) } diff --git a/cautils/getter/getpoliciesutils.go b/cautils/getter/getpoliciesutils.go index a20f2b1d..fa859594 100644 --- a/cautils/getter/getpoliciesutils.go +++ b/cautils/getter/getpoliciesutils.go @@ -10,8 +10,6 @@ import ( "path" "path/filepath" "strings" - - "github.com/armosec/opa-utils/reporthandling" ) func GetDefaultPath(name string) string { @@ -22,33 +20,8 @@ func GetDefaultPath(name string) string { return defaultfilePath } -// Save control as json in file -func SaveControlInFile(control *reporthandling.Control, pathStr string) error { - encodedData, err := json.Marshal(control) - if err != nil { - return err - } - err = os.WriteFile(pathStr, []byte(fmt.Sprintf("%v", string(encodedData))), 0644) - if err != nil { - if os.IsNotExist(err) { - pathDir := path.Dir(pathStr) - if err := os.Mkdir(pathDir, 0744); err != nil { - return err - } - } else { - return err - - } - err = os.WriteFile(pathStr, []byte(fmt.Sprintf("%v", string(encodedData))), 0644) - if err != nil { - return err - } - } - return nil -} - -func SaveFrameworkInFile(framework *reporthandling.Framework, pathStr string) error { - encodedData, err := json.Marshal(framework) +func SaveInFile(policy interface{}, pathStr string) error { + encodedData, err := json.Marshal(policy) if err != nil { return err } diff --git a/cautils/getter/loadpolicy.go b/cautils/getter/loadpolicy.go index a3c1fb9e..50307868 100644 --- a/cautils/getter/loadpolicy.go +++ b/cautils/getter/loadpolicy.go @@ -78,7 +78,17 @@ func (lp *LoadPolicy) GetFramework(frameworkName string) (*reporthandling.Framew return framework, err } -func (lp *LoadPolicy) GetExceptions(customerGUID, clusterName string) ([]armotypes.PostureExceptionPolicy, error) { +func (lp *LoadPolicy) ListFrameworks() ([]string, error) { + // TODO - Support + return []string{}, fmt.Errorf("loading frameworks list from file is not supported") +} + +func (lp *LoadPolicy) ListControls(listType ListType) ([]string, error) { + // TODO - Support + return []string{}, fmt.Errorf("loading controls list from file is not supported") +} + +func (lp *LoadPolicy) GetExceptions(clusterName string) ([]armotypes.PostureExceptionPolicy, error) { filePath := lp.filePath() exception := []armotypes.PostureExceptionPolicy{} f, err := os.ReadFile(filePath) @@ -90,7 +100,7 @@ func (lp *LoadPolicy) GetExceptions(customerGUID, clusterName string) ([]armotyp return exception, err } -func (lp *LoadPolicy) GetControlsInputs(customerGUID, clusterName string) (map[string][]string, error) { +func (lp *LoadPolicy) GetControlsInputs(clusterName string) (map[string][]string, error) { filePath := lp.filePath() accountConfig := &armotypes.CustomerConfig{} f, err := os.ReadFile(filePath) diff --git a/cautils/rbac.go b/cautils/rbac.go index 61eac56a..64854016 100644 --- a/cautils/rbac.go +++ b/cautils/rbac.go @@ -43,12 +43,19 @@ 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 + // TODO - DEPRECATE SA2WLIDmap SA2WLIDmapIMeta, err := rbacutils.SA2WLIDmapIMetadataWrapper(resources.SA2WLIDmap) if err != nil { return nil, err } allresources[SA2WLIDmapIMeta.GetID()] = SA2WLIDmapIMeta + SAID2WLIDmapIMeta, err := rbacutils.SAID2WLIDmapIMetadataWrapper(resources.SAID2WLIDmap) + if err != nil { + return nil, err + } + allresources[SAID2WLIDmapIMeta.GetID()] = SAID2WLIDmapIMeta + // convert rbac k8s resources to IMetadata and add to allresources for _, cr := range resources.ClusterRoles.Items { crmap, err := convertToMap(cr) diff --git a/cautils/scaninfo.go b/cautils/scaninfo.go index e2aa1f7f..ca1e1961 100644 --- a/cautils/scaninfo.go +++ b/cautils/scaninfo.go @@ -76,20 +76,10 @@ type Getters struct { func (scanInfo *ScanInfo) Init() { scanInfo.setUseFrom() - scanInfo.setUseExceptions() scanInfo.setOutputFile() } -func (scanInfo *ScanInfo) setUseExceptions() { - if scanInfo.UseExceptions != "" { - // load exceptions from file - scanInfo.ExceptionsGetter = getter.NewLoadPolicy([]string{scanInfo.UseExceptions}) - } else { - scanInfo.ExceptionsGetter = getter.GetArmoAPIConnector() - } -} - func (scanInfo *ScanInfo) setUseFrom() { if scanInfo.UseDefault { for _, policy := range scanInfo.PolicyIdentifier { diff --git a/clihandler/clidownload.go b/clihandler/clidownload.go new file mode 100644 index 00000000..c12453ec --- /dev/null +++ b/clihandler/clidownload.go @@ -0,0 +1,116 @@ +package clihandler + +import ( + "fmt" + "os" + + "github.com/armosec/kubescape/cautils" + "github.com/armosec/kubescape/cautils/getter" +) + +var downloadFunc = map[string]func(*cautils.DownloadInfo) error{ + "controls-inputs": downloadConfigInputs, + "exceptions": downloadExceptions, + "control": downloadControl, + "framework": downloadFramework, +} + +func DownloadSupportCommands() []string { + commands := []string{} + for k := range downloadFunc { + commands = append(commands, k) + } + return commands +} + +func CliDownload(downloadInfo *cautils.DownloadInfo) error { + if f, ok := downloadFunc[downloadInfo.Target]; ok { + if err := f(downloadInfo); err != nil { + fmt.Println(err) + os.Exit(1) + } + fmt.Println(fmt.Sprintf("'%s' downloaded successfully and saved at: '%s'", downloadInfo.Target, downloadInfo.Path)) + return nil + } + return fmt.Errorf("unknown command to download") +} + +func downloadConfigInputs(downloadInfo *cautils.DownloadInfo) error { + tenant := getTenantConfig(downloadInfo.Account, getKubernetesApi()) // change k8sinterface + controlsInputsGetter := getConfigInputsGetter(downloadInfo.Name, tenant.GetCustomerGUID(), nil) + controlInputs, err := controlsInputsGetter.GetControlsInputs(tenant.GetClusterName()) + if err != nil { + return err + } + if downloadInfo.Path == "" { + downloadInfo.Path = getter.GetDefaultPath(fmt.Sprintf("%s.json", downloadInfo.Target)) + } + // save in file + err = getter.SaveInFile(controlInputs, downloadInfo.Path) + if err != nil { + return err + } + return nil +} + +func downloadExceptions(downloadInfo *cautils.DownloadInfo) error { + tenant := getTenantConfig(downloadInfo.Account, getKubernetesApi()) // change k8sinterface + exceptionsGetter := getExceptionsGetter("") + exceptions, err := exceptionsGetter.GetExceptions(tenant.GetClusterName()) + if err != nil { + return err + } + if downloadInfo.Path == "" { + downloadInfo.Path = getter.GetDefaultPath(fmt.Sprintf("%s.json", downloadInfo.Target)) + } + // save in file + err = getter.SaveInFile(exceptions, downloadInfo.Path) + if err != nil { + return err + } + return nil +} + +func downloadFramework(downloadInfo *cautils.DownloadInfo) error { + tenant := getTenantConfig(downloadInfo.Account, getKubernetesApi()) // change k8sinterface + g := getPolicyGetter(nil, tenant.GetCustomerGUID(), true, nil) + + if downloadInfo.Name == "" { + // TODO - support + return fmt.Errorf("missing framework name") + } + if downloadInfo.Path == "" { + downloadInfo.Path = getter.GetDefaultPath(downloadInfo.Name + ".json") + } + frameworks, err := g.GetFramework(downloadInfo.Name) + if err != nil { + return err + } + err = getter.SaveInFile(frameworks, downloadInfo.Path) + if err != nil { + return err + } + return nil +} + +func downloadControl(downloadInfo *cautils.DownloadInfo) error { + tenant := getTenantConfig(downloadInfo.Account, getKubernetesApi()) // change k8sinterface + g := getPolicyGetter(nil, tenant.GetCustomerGUID(), true, nil) + + if downloadInfo.Name == "" { + // TODO - support + return fmt.Errorf("missing control name") + } + if downloadInfo.Path == "" { + downloadInfo.Path = getter.GetDefaultPath(downloadInfo.Name + ".json") + } + controls, err := g.GetControl(downloadInfo.Name) + if err != nil { + return err + } + err = getter.SaveInFile(controls, downloadInfo.Path) + if err != nil { + return err + } + return nil +} diff --git a/clihandler/cmd/control.go b/clihandler/cmd/control.go index bc0cb876..cb406af6 100644 --- a/clihandler/cmd/control.go +++ b/clihandler/cmd/control.go @@ -13,10 +13,27 @@ import ( "github.com/spf13/cobra" ) +var ( + controlExample = ` + # Scan the 'privileged container' control + kubescape scan control 'privileged container' + + # Scan list of controls separated with a comma + kubescape scan control 'privileged container,allowed hostpath' + + # Scan list of controls using the control ID separated with a comma + kubescape scan control C-0058,C-0057 + + Control documentation: + https://hub.armo.cloud/docs/controls +` +) + // controlCmd represents the control command var controlCmd = &cobra.Command{ - Use: "control /.\nExamples:\n$ kubescape scan control C-0058,C-0057 [flags]\n$ kubescape scan contol C-0058 [flags]\n$ kubescape scan control 'privileged container,allowed hostpath' [flags]", - Short: fmt.Sprintf("The control you wish to use for scan. It must be present in at least one of the following frameworks: %s", getter.NativeFrameworks), + Use: "control /", + Short: "The controls you wish to use. Run 'kubescape list controls' for the list of supported controls", + Example: controlExample, Args: func(cmd *cobra.Command, args []string) error { if len(args) > 0 { controls := strings.Split(args[0], ",") diff --git a/clihandler/cmd/download.go b/clihandler/cmd/download.go index bc4bd3ca..f0bbcf9a 100644 --- a/clihandler/cmd/download.go +++ b/clihandler/cmd/download.go @@ -6,27 +6,32 @@ import ( "strings" "github.com/armosec/kubescape/cautils" - "github.com/armosec/kubescape/cautils/getter" + "github.com/armosec/kubescape/clihandler" "github.com/spf13/cobra" ) -var downloadInfo cautils.DownloadInfo +var downloadInfo = cautils.DownloadInfo{} var downloadCmd = &cobra.Command{ - Use: fmt.Sprintf("download framework/control / [flags]\nSupported frameworks: %s", getter.NativeFrameworks), - Short: "Download framework/control", + Use: "download ", + Short: fmt.Sprintf("Download %s", strings.Join(clihandler.DownloadSupportCommands(), "/")), Long: ``, Args: func(cmd *cobra.Command, args []string) error { - if len(args) != 2 { - return fmt.Errorf("requires two arguments : framework/control /") + supported := strings.Join(clihandler.DownloadSupportCommands(), ",") + if len(args) < 1 { + return fmt.Errorf("policy type requeued, supported: %v", supported) } - if !strings.EqualFold(args[0], "framework") && !strings.EqualFold(args[0], "control") { - return fmt.Errorf("invalid parameter '%s'. Supported parameters: framework, control", args[0]) + if cautils.StringInSlice(clihandler.DownloadSupportCommands(), args[0]) == cautils.ValueNotFound { + return fmt.Errorf("invalid parameter '%s'. Supported parameters: %s", args[0], supported) } return nil }, RunE: func(cmd *cobra.Command, args []string) error { - if err := download(args); err != nil { + downloadInfo.Target = args[0] + if len(args) >= 2 { + downloadInfo.Name = args[1] + } + if err := clihandler.CliDownload(&downloadInfo); err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) } @@ -35,61 +40,10 @@ var downloadCmd = &cobra.Command{ } func init() { + // cobra.OnInitialize(initConfig) + rootCmd.AddCommand(downloadCmd) - downloadInfo = cautils.DownloadInfo{} - downloadCmd.Flags().StringVarP(&downloadInfo.Path, "output", "o", "", "Output file. If specified, will store save to `~/.kubescape/.json`") -} + downloadCmd.Flags().StringVarP(&downloadInfo.Path, "output", "o", "", "Output file. If specified, will store save to `~/.kubescape/.json`") + downloadCmd.PersistentFlags().StringVarP(&downloadInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file") -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 } diff --git a/clihandler/cmd/framework.go b/clihandler/cmd/framework.go index ca2aaf04..eb39815f 100644 --- a/clihandler/cmd/framework.go +++ b/clihandler/cmd/framework.go @@ -39,7 +39,7 @@ var ( ) var frameworkCmd = &cobra.Command{ Use: "framework [``/`-`] [flags]", - Short: fmt.Sprintf("The framework you wish to use. Supported frameworks: %s", strings.Join(getter.NativeFrameworks, ", ")), + Short: "The framework you wish to use. Run 'kubescape list frameworks' for the list of supported frameworks", Example: frameworkExample, Long: "Execute a scan on a running Kubernetes cluster or `yaml`/`json` files (use glob) or `-` for stdin", ValidArgs: getter.NativeFrameworks, diff --git a/clihandler/cmd/root.go b/clihandler/cmd/root.go index dafd262f..4fd5792f 100644 --- a/clihandler/cmd/root.go +++ b/clihandler/cmd/root.go @@ -35,14 +35,9 @@ func init() { flag.CommandLine.StringVar(&armoBEURLs, "environment", "", envFlagUsage) rootCmd.PersistentFlags().StringVar(&armoBEURLs, "environment", "", envFlagUsage) rootCmd.PersistentFlags().MarkHidden("environment") - cobra.OnInitialize(initConfig) } -// initConfig reads in config file and ENV variables if set. -func initConfig() { -} - func InitArmoBEConnector() { urlSlices := strings.Split(armoBEURLs, ",") if len(urlSlices) > 3 { diff --git a/clihandler/initcli.go b/clihandler/initcli.go index 26bfe22c..add726a0 100644 --- a/clihandler/initcli.go +++ b/clihandler/initcli.go @@ -34,9 +34,9 @@ type componentInterfaces struct { func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces { - k8s := getKubernetesApi(scanInfo) + k8s := getKubernetesApi() - tenantConfig := getTenantConfig(scanInfo, k8s) + tenantConfig := getTenantConfig(scanInfo.Account, k8s) // Set submit behavior AFTER loading tenant config setSubmitBehavior(scanInfo, tenantConfig) @@ -92,10 +92,15 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error { 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(), downloadReleasedPolicy) - setConfigInputsGetter(scanInfo, interfaces.tenantConfig.GetCustomerGUID(), downloadReleasedPolicy) + // set policy getter only after setting the customerGUID + scanInfo.Getters.PolicyGetter = getPolicyGetter(scanInfo.UseFrom, interfaces.tenantConfig.GetCustomerGUID(), scanInfo.FrameworkScan, downloadReleasedPolicy) + scanInfo.Getters.ControlsInputsGetter = getConfigInputsGetter(scanInfo.ControlsInputs, interfaces.tenantConfig.GetCustomerGUID(), downloadReleasedPolicy) + scanInfo.Getters.ExceptionsGetter = getExceptionsGetter(scanInfo.UseExceptions) + + // TODO - list supported frameworks/controls + + // defer func() { if err := interfaces.hostSensorHandler.TearDown(); err != nil { errMsg := "failed to tear down host sensor" diff --git a/clihandler/initcliutils.go b/clihandler/initcliutils.go index 536d32d2..ecbbf52b 100644 --- a/clihandler/initcliutils.go +++ b/clihandler/initcliutils.go @@ -13,21 +13,29 @@ import ( reporterv1 "github.com/armosec/kubescape/resultshandling/reporter/v1" "github.com/armosec/opa-utils/reporthandling" "github.com/armosec/rbac-utils/rbacscanner" - "github.com/golang/glog" // reporterv2 "github.com/armosec/kubescape/resultshandling/reporter/v2" ) -func getKubernetesApi(scanInfo *cautils.ScanInfo) *k8sinterface.KubernetesApi { - if scanInfo.GetScanningEnvironment() == cautils.ScanLocalFiles { +func getKubernetesApi() *k8sinterface.KubernetesApi { + if !k8sinterface.IsConnectedToCluster() { return nil } return k8sinterface.NewKubernetesApi() } -func getTenantConfig(scanInfo *cautils.ScanInfo, k8s *k8sinterface.KubernetesApi) cautils.ITenantConfig { - if scanInfo.GetScanningEnvironment() == cautils.ScanLocalFiles { - return cautils.NewLocalConfig(getter.GetArmoAPIConnector(), scanInfo.Account) +func getTenantConfig(Account string, k8s *k8sinterface.KubernetesApi) cautils.ITenantConfig { + if !k8sinterface.IsConnectedToCluster() { + return cautils.NewLocalConfig(getter.GetArmoAPIConnector(), Account) + } + return cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), Account) +} + +func getExceptionsGetter(useExceptions string) getter.IExceptionsGetter { + if useExceptions != "" { + // load exceptions from file + return getter.NewLoadPolicy([]string{useExceptions}) + } else { + return getter.GetArmoAPIConnector() } - return cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account) } func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, submit bool) *cautils.RBACObjects { @@ -139,55 +147,64 @@ 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, downloadReleasedPolicy *getter.DownloadReleasedPolicy) { - if len(scanInfo.UseFrom) > 0 { - scanInfo.PolicyGetter = getter.NewLoadPolicy(scanInfo.UseFrom) - } else { - if customerGUID == "" || !scanInfo.FrameworkScan { - setDownloadReleasedPolicy(scanInfo, downloadReleasedPolicy) - } else { - setGetArmoAPIConnector(scanInfo, customerGUID) - } +func getPolicyGetter(loadPoliciesFromFile []string, accountID string, frameworkScope bool, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IPolicyGetter { + if len(loadPoliciesFromFile) > 0 { + return getter.NewLoadPolicy(loadPoliciesFromFile) } + if accountID != "" && frameworkScope { + g := getter.GetArmoAPIConnector() // download policy from ARMO backend + g.SetCustomerGUID(accountID) + return g + } + if downloadReleasedPolicy == nil { + downloadReleasedPolicy = getter.NewDownloadReleasedPolicy() + } + return getDownloadReleasedPolicy(downloadReleasedPolicy) + } +// func setGetArmoAPIConnector(scanInfo *cautils.ScanInfo, customerGUID string) { +// g := getter.GetArmoAPIConnector() // download policy from ARMO backend +// g.SetCustomerGUID(customerGUID) +// scanInfo.PolicyGetter = g +// if scanInfo.ScanAll { +// frameworks, err := g.ListCustomFrameworks(customerGUID) +// if err != nil { +// glog.Error("failed to get custom frameworks") // handle error +// return +// } +// scanInfo.SetPolicyIdentifiers(frameworks, reporthandling.KindFramework) +// } +// } + // 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.Getters.ControlsInputsGetter = getter.NewLoadPolicy([]string{scanInfo.ControlsInputs}) +func getConfigInputsGetter(ControlsInputs string, accountID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IControlsInputsGetter { + if len(ControlsInputs) > 0 { + return getter.NewLoadPolicy([]string{ControlsInputs}) + } + if accountID != "" { + g := getter.GetArmoAPIConnector() // download config from ARMO backend + g.SetCustomerGUID(accountID) + return g + } + if downloadReleasedPolicy == nil { + downloadReleasedPolicy = getter.NewDownloadReleasedPolicy() + } + 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") + } + return downloadReleasedPolicy +} + +func getDownloadReleasedPolicy(downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IPolicyGetter { + 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") + return getter.NewLoadPolicy(getDefaultFrameworksPaths()) } 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 - } + return 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 = downloadReleasedPolicy - } -} -func setGetArmoAPIConnector(scanInfo *cautils.ScanInfo, customerGUID string) { - g := getter.GetArmoAPIConnector() // download policy from ARMO backend - g.SetCustomerGUID(customerGUID) - scanInfo.PolicyGetter = g - if scanInfo.ScanAll { - frameworks, err := g.ListCustomFrameworks(customerGUID) - if err != nil { - glog.Error("failed to get custom frameworks") // handle error - return - } - scanInfo.SetPolicyIdentifiers(frameworks, reporthandling.KindFramework) - } -} func getDefaultFrameworksPaths() []string { fwPaths := []string{} for i := range getter.NativeFrameworks { diff --git a/go.mod b/go.mod index 5199f06b..35cc5279 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.88 - github.com/armosec/rbac-utils v0.0.10 + github.com/armosec/rbac-utils v0.0.11 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 c13950d7..bcd88136 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.88 h1:IxIml3w7l0HFqbb+XzKuXf+Pw78DHIxPwRIkgudKQRw= github.com/armosec/opa-utils v0.0.88/go.mod h1:ZOXYVTtuyrV4TldcfbzgRqP6F9Drlf4hB0zr210OXgM= github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40= -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/rbac-utils v0.0.11 h1:SCiVLqUeV+WGpUsWbOBt6jKkFAd62jztuzB6PIgHz7w= +github.com/armosec/rbac-utils v0.0.11/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= diff --git a/policyhandler/handlepullpolicies.go b/policyhandler/handlepullpolicies.go index 005fe228..2b5a31ea 100644 --- a/policyhandler/handlepullpolicies.go +++ b/policyhandler/handlepullpolicies.go @@ -21,13 +21,13 @@ func (policyHandler *PolicyHandler) getPolicies(notification *reporthandling.Pol policiesAndResources.Frameworks = frameworks // get exceptions - exceptionPolicies, err := policyHandler.getters.ExceptionsGetter.GetExceptions(cautils.CustomerGUID, cautils.ClusterName) + exceptionPolicies, err := policyHandler.getters.ExceptionsGetter.GetExceptions(cautils.ClusterName) if err == nil { policiesAndResources.Exceptions = exceptionPolicies } // get account configuration - controlsInputs, err := policyHandler.getters.ControlsInputsGetter.GetControlsInputs(cautils.CustomerGUID, cautils.ClusterName) + controlsInputs, err := policyHandler.getters.ControlsInputsGetter.GetControlsInputs(cautils.ClusterName) if err == nil { policiesAndResources.RegoInputData.PostureControlInputs = controlsInputs }