diff --git a/cautils/downloadinfo.go b/cautils/downloadinfo.go index 548bdc4f..b02e7f86 100644 --- a/cautils/downloadinfo.go +++ b/cautils/downloadinfo.go @@ -1,8 +1,9 @@ package cautils type DownloadInfo struct { - Path string - Target string - Name string - Account string + Path string // directory to save artifact. Default is "~/.kubescape/" + FileName string // can be empty + Target string // type of artifact to download + Name string // name of artifact to download + Account string // customerGUID } diff --git a/cautils/getter/armoapi.go b/cautils/getter/armoapi.go index cf8d1fd3..f194f4f1 100644 --- a/cautils/getter/armoapi.go +++ b/cautils/getter/armoapi.go @@ -112,6 +112,21 @@ func (armoAPI *ArmoAPI) GetFramework(name string) (*reporthandling.Framework, er return framework, err } +func (armoAPI *ArmoAPI) GetFrameworks() ([]reporthandling.Framework, error) { + respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getListFrameworkURL(), nil) + if err != nil { + return nil, nil + } + + frameworks := []reporthandling.Framework{} + if err = JSONDecoder(respStr).Decode(&frameworks); err != nil { + return nil, err + } + // SaveInFile(framework, GetDefaultPath(name+".json")) + + return frameworks, err +} + func (armoAPI *ArmoAPI) GetControl(policyName string) (*reporthandling.Control, error) { return nil, fmt.Errorf("control api is not public") } diff --git a/cautils/getter/downloadreleasedpolicy.go b/cautils/getter/downloadreleasedpolicy.go index 0ae9e034..e9745ef5 100644 --- a/cautils/getter/downloadreleasedpolicy.go +++ b/cautils/getter/downloadreleasedpolicy.go @@ -41,6 +41,14 @@ func (drp *DownloadReleasedPolicy) GetFramework(name string) (*reporthandling.Fr return framework, err } +func (drp *DownloadReleasedPolicy) GetFrameworks() ([]reporthandling.Framework, error) { + frameworks, err := drp.gs.GetOPAFrameworks() + if err != nil { + return nil, err + } + return frameworks, err +} + func (drp *DownloadReleasedPolicy) ListFrameworks() ([]string, error) { return drp.gs.GetOPAFrameworksNamesList() } diff --git a/cautils/getter/getpolicies.go b/cautils/getter/getpolicies.go index 1a30c6f3..cb3abdf8 100644 --- a/cautils/getter/getpolicies.go +++ b/cautils/getter/getpolicies.go @@ -13,6 +13,7 @@ const ListName ListType = "name" type IPolicyGetter interface { GetFramework(name string) (*reporthandling.Framework, error) + GetFrameworks() ([]reporthandling.Framework, error) GetControl(name string) (*reporthandling.Control, error) ListFrameworks() ([]string, error) diff --git a/cautils/getter/loadpolicy.go b/cautils/getter/loadpolicy.go index 50307868..9cde9a0f 100644 --- a/cautils/getter/loadpolicy.go +++ b/cautils/getter/loadpolicy.go @@ -78,6 +78,12 @@ func (lp *LoadPolicy) GetFramework(frameworkName string) (*reporthandling.Framew return framework, err } +func (lp *LoadPolicy) GetFrameworks() ([]reporthandling.Framework, error) { + frameworks := []reporthandling.Framework{} + var err error + return frameworks, err +} + func (lp *LoadPolicy) ListFrameworks() ([]string, error) { // TODO - Support return []string{}, fmt.Errorf("loading frameworks list from file is not supported") diff --git a/clihandler/clidownload.go b/clihandler/clidownload.go index 038bc082..2f96cf81 100644 --- a/clihandler/clidownload.go +++ b/clihandler/clidownload.go @@ -3,6 +3,11 @@ package clihandler import ( "fmt" + "path/filepath" + "strings" + + + "github.com/armosec/armoapi-go/armotypes" "github.com/armosec/kubescape/cautils" "github.com/armosec/kubescape/cautils/getter" ) @@ -12,6 +17,7 @@ var downloadFunc = map[string]func(*cautils.DownloadInfo) error{ "exceptions": downloadExceptions, "control": downloadControl, "framework": downloadFramework, + "artifacts": downloadArtifacts, } func DownloadSupportCommands() []string { @@ -23,28 +29,65 @@ func DownloadSupportCommands() []string { } func CliDownload(downloadInfo *cautils.DownloadInfo) error { - if f, ok := downloadFunc[downloadInfo.Target]; ok { + setPathandFilename(downloadInfo) + if err := downloadArtifact(downloadInfo, downloadFunc); err != nil { + return err + } + return nil +} + +func downloadArtifact(downloadInfo *cautils.DownloadInfo, downloadArtifactFunc map[string]func(*cautils.DownloadInfo) error) error { + if f, ok := downloadArtifactFunc[downloadInfo.Target]; ok { if err := f(downloadInfo); err != nil { return err } - fmt.Printf("'%s' downloaded successfully and saved at: '%s'\n", downloadInfo.Target, downloadInfo.Path) + fmt.Printf("'%s' downloaded successfully and saved at: '%s'\n", downloadInfo.Target, filepath.Join(downloadInfo.Path, downloadInfo.FileName)) return nil } return fmt.Errorf("unknown command to download") } +func setPathandFilename(downloadInfo *cautils.DownloadInfo) { + if downloadInfo.Path == "" { + downloadInfo.Path = getter.GetDefaultPath("") + } else { + dir, file := filepath.Split(downloadInfo.Path) + if dir == "" { + downloadInfo.Path = file + } else { + downloadInfo.Path = dir + downloadInfo.FileName = file + } + } +} + +func downloadArtifacts(downloadInfo *cautils.DownloadInfo) error { + downloadInfo.FileName = "" + var artifacts = map[string]func(*cautils.DownloadInfo) error{ + "controls-inputs": downloadConfigInputs, + "exceptions": downloadExceptions, + "framework": downloadFramework, + } + for artifact := range artifacts { + if err := downloadArtifact(&cautils.DownloadInfo{Target: artifact, Path: downloadInfo.Path, FileName: fmt.Sprintf("%s.json", artifact)}, artifacts); err != nil { + fmt.Printf("error downloading %s, error: %s", artifact, err) + } + } + return nil +} + func downloadConfigInputs(downloadInfo *cautils.DownloadInfo) error { - tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) // change k8sinterface + tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) 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)) + if downloadInfo.FileName == "" { + downloadInfo.FileName = fmt.Sprintf("%s.json", downloadInfo.Target) } // save in file - err = getter.SaveInFile(controlInputs, downloadInfo.Path) + err = getter.SaveInFile(controlInputs, filepath.Join(downloadInfo.Path, downloadInfo.FileName)) if err != nil { return err } @@ -52,17 +95,21 @@ func downloadConfigInputs(downloadInfo *cautils.DownloadInfo) error { } func downloadExceptions(downloadInfo *cautils.DownloadInfo) error { - tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) // change k8sinterface + var err error + tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) exceptionsGetter := getExceptionsGetter("") - exceptions, err := exceptionsGetter.GetExceptions(tenant.GetClusterName()) - if err != nil { - return err + exceptions := []armotypes.PostureExceptionPolicy{} + if tenant.GetCustomerGUID() != "" { + exceptions, err = exceptionsGetter.GetExceptions(tenant.GetClusterName()) + if err != nil { + return err + } } - if downloadInfo.Path == "" { - downloadInfo.Path = getter.GetDefaultPath(fmt.Sprintf("%s.json", downloadInfo.Target)) + if downloadInfo.FileName == "" { + downloadInfo.FileName = fmt.Sprintf("%s.json", downloadInfo.Target) } // save in file - err = getter.SaveInFile(exceptions, downloadInfo.Path) + err = getter.SaveInFile(exceptions, filepath.Join(downloadInfo.Path, downloadInfo.FileName)) if err != nil { return err } @@ -70,43 +117,59 @@ func downloadExceptions(downloadInfo *cautils.DownloadInfo) error { } func downloadFramework(downloadInfo *cautils.DownloadInfo) error { - tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) // change k8sinterface + + tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) 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 + // if framework name not specified - download all frameworks + downloadInfo.Target = "frameworks" + downloadInfo.FileName = fmt.Sprintf("%s.json", "") + frameworks, err := g.GetFrameworks() + if err != nil { + return err + } + for _, fw := range frameworks { + err = getter.SaveInFile(fw, filepath.Join(downloadInfo.Path, (strings.ToLower(fw.Name)+".json"))) + if err != nil { + return err + } + } + // return fmt.Errorf("missing framework name") + } else { + if downloadInfo.FileName == "" { + downloadInfo.FileName = fmt.Sprintf("%s.json", downloadInfo.Name) + } + framework, err := g.GetFramework(downloadInfo.Name) + if err != nil { + return err + } + err = getter.SaveInFile(framework, filepath.Join(downloadInfo.Path, downloadInfo.FileName)) + if err != nil { + return err + } } return nil } func downloadControl(downloadInfo *cautils.DownloadInfo) error { - tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) // change k8sinterface + + tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) g := getPolicyGetter(nil, tenant.GetCustomerGUID(), false, nil) if downloadInfo.Name == "" { // TODO - support return fmt.Errorf("missing control name") } - if downloadInfo.Path == "" { - downloadInfo.Path = getter.GetDefaultPath(downloadInfo.Name + ".json") + filename := downloadInfo.FileName + if filename == "" { + filename = fmt.Sprintf("%s.json", "controls-inputs") } controls, err := g.GetControl(downloadInfo.Name) if err != nil { return err } - err = getter.SaveInFile(controls, downloadInfo.Path) + err = getter.SaveInFile(controls, filepath.Join(downloadInfo.Path, filename)) if err != nil { return err } diff --git a/clihandler/cmd/download.go b/clihandler/cmd/download.go index f0bbcf9a..d0cc342d 100644 --- a/clihandler/cmd/download.go +++ b/clihandler/cmd/download.go @@ -19,7 +19,7 @@ var downloadCmd = &cobra.Command{ Args: func(cmd *cobra.Command, args []string) error { supported := strings.Join(clihandler.DownloadSupportCommands(), ",") if len(args) < 1 { - return fmt.Errorf("policy type requeued, supported: %v", supported) + return fmt.Errorf("policy type required, supported: %v", supported) } if cautils.StringInSlice(clihandler.DownloadSupportCommands(), args[0]) == cautils.ValueNotFound { return fmt.Errorf("invalid parameter '%s'. Supported parameters: %s", args[0], supported) @@ -43,7 +43,7 @@ func init() { // cobra.OnInitialize(initConfig) rootCmd.AddCommand(downloadCmd) - 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 not specified, will save in `~/.kubescape/.json`") downloadCmd.PersistentFlags().StringVarP(&downloadInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file") } diff --git a/docs/kubescape.png b/docs/kubescape.png index 75b7d4c6..cfa8ffc1 100755 Binary files a/docs/kubescape.png and b/docs/kubescape.png differ