diff --git a/.github/workflows/post-release.yaml b/.github/workflows/post-release.yaml new file mode 100644 index 00000000..3afe6539 --- /dev/null +++ b/.github/workflows/post-release.yaml @@ -0,0 +1,17 @@ +name: create release digests + +on: + release: + types: [ published] + branches: [ master ] + +jobs: + once: + name: Creating digests + runs-on: ubuntu-latest + steps: + - name: Digest + uses: MCJack123/ghaction-generate-release-hashes@v1 + with: + hash-type: sha1 + file-name: kubescape-release-digests diff --git a/build.py b/build.py index ec8a5497..9db7d938 100644 --- a/build.py +++ b/build.py @@ -37,7 +37,7 @@ def main(): print("Building Kubescape") # print environment variables - print(os.environ) + # print(os.environ) # Set some variables packageName = getPackageName() diff --git a/cautils/customerloader.go b/cautils/customerloader.go index f0bb0334..d2263a2c 100644 --- a/cautils/customerloader.go +++ b/cautils/customerloader.go @@ -23,25 +23,31 @@ func ConfigFileFullPath() string { return getter.GetDefaultPath(configFileName + // ====================================================================================== type ConfigObj struct { - CustomerGUID string `json:"customerGUID"` - Token string `json:"invitationParam"` - CustomerAdminEMail string `json:"adminMail"` - ClusterName string `json:"clusterName"` -} - -func (co *ConfigObj) Json() []byte { - if b, err := json.Marshal(co); err == nil { - return b - } - return []byte{} + AccountID string `json:"accountID,omitempty"` + ClientID string `json:"clientID,omitempty"` + AccessKey string `json:"accessKey,omitempty"` + CustomerGUID string `json:"customerGUID,omitempty"` // Deprecated + Token string `json:"invitationParam,omitempty"` + CustomerAdminEMail string `json:"adminMail,omitempty"` + ClusterName string `json:"clusterName,omitempty"` } // Config - convert ConfigObj to config file func (co *ConfigObj) Config() []byte { + + // remove cluster name before saving to file clusterName := co.ClusterName - co.ClusterName = "" // remove cluster name before saving to file - b, err := json.Marshal(co) + customerAdminEMail := co.CustomerAdminEMail + token := co.Token + co.ClusterName = "" + co.Token = "" + co.CustomerAdminEMail = "" + + b, err := json.MarshalIndent(co, "", " ") + co.ClusterName = clusterName + co.CustomerAdminEMail = customerAdminEMail + co.Token = token if err == nil { return b @@ -56,10 +62,12 @@ func (co *ConfigObj) Config() []byte { type ITenantConfig interface { // set SetTenant() error + UpdateCachedConfig() error + DeleteCachedConfig() error // getters GetClusterName() string - GetCustomerGUID() string + GetAccountID() string GetConfigObj() *ConfigObj // GetBackendAPI() getter.IBackend // GenerateURL() @@ -93,12 +101,18 @@ func NewLocalConfig(backendAPI getter.IBackend, customerGUID, clusterName string lc.configObj = configObj } if customerGUID != "" { - lc.configObj.CustomerGUID = customerGUID // override config customerGUID + lc.configObj.AccountID = customerGUID // override config customerGUID } if clusterName != "" { lc.configObj.ClusterName = AdoptClusterName(clusterName) // override config clusterName } - if lc.configObj.CustomerGUID != "" { + getAccountFromEnv(lc.configObj) + + lc.backendAPI.SetAccountID(lc.configObj.AccountID) + lc.backendAPI.SetClientID(lc.configObj.ClientID) + lc.backendAPI.SetAccessKey(lc.configObj.AccessKey) + + if lc.configObj.AccountID != "" { if err := lc.SetTenant(); err != nil { fmt.Println(err) } @@ -107,32 +121,38 @@ func NewLocalConfig(backendAPI getter.IBackend, customerGUID, clusterName string return lc } -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 lc.configObj.ClusterName } -func (lc *LocalConfig) IsConfigFound() bool { return existsConfigFile() } +func (lc *LocalConfig) GetConfigObj() *ConfigObj { return lc.configObj } +func (lc *LocalConfig) GetAccountID() string { return lc.configObj.AccountID } +func (lc *LocalConfig) GetClusterName() string { return lc.configObj.ClusterName } +func (lc *LocalConfig) IsConfigFound() bool { return existsConfigFile() } func (lc *LocalConfig) SetTenant() error { + // ARMO tenant GUID if err := getTenantConfigFromBE(lc.backendAPI, lc.configObj); err != nil { return err } - updateConfigFile(lc.configObj) + lc.UpdateCachedConfig() return nil } +func (lc *LocalConfig) UpdateCachedConfig() error { + return updateConfigFile(lc.configObj) +} + +func (lc *LocalConfig) DeleteCachedConfig() error { + return DeleteConfigFile() +} func getTenantConfigFromBE(backendAPI getter.IBackend, configObj *ConfigObj) error { // get from armoBE - backendAPI.SetCustomerGUID(configObj.CustomerGUID) - tenantResponse, err := backendAPI.GetCustomerGUID() + tenantResponse, err := backendAPI.GetTenant() if err == nil && tenantResponse != nil { if tenantResponse.AdminMail != "" { // registered tenant configObj.CustomerAdminEMail = tenantResponse.AdminMail } else { // new tenant configObj.Token = tenantResponse.Token - configObj.CustomerGUID = tenantResponse.TenantID + configObj.AccountID = tenantResponse.TenantID } } else { if err != nil && !strings.Contains(err.Error(), "already exists") { @@ -154,8 +174,11 @@ 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' +KS_ACCOUNT_ID +KS_CLIENT_ID +KS_ACCESS_KEY + TODO - supprot: -KS_ACCOUNT // Account ID KS_CACHE // path to cached files */ type ClusterConfig struct { @@ -187,32 +210,36 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken c.configObj = configObj } if customerGUID != "" { - c.configObj.CustomerGUID = customerGUID // override config customerGUID + c.configObj.AccountID = customerGUID // override config customerGUID } if clusterName != "" { c.configObj.ClusterName = AdoptClusterName(clusterName) // override config clusterName } - if c.configObj.CustomerGUID != "" { - if err := c.SetTenant(); err != nil { - fmt.Println(err) - } - } + getAccountFromEnv(c.configObj) + if c.configObj.ClusterName == "" { c.configObj.ClusterName = AdoptClusterName(k8sinterface.GetClusterName()) } else { // override the cluster name if it has unwanted characters c.configObj.ClusterName = AdoptClusterName(c.configObj.ClusterName) } + c.backendAPI.SetAccountID(c.configObj.AccountID) + c.backendAPI.SetClientID(c.configObj.ClientID) + c.backendAPI.SetAccessKey(c.configObj.AccessKey) + + if c.configObj.AccountID != "" { + if err := c.SetTenant(); err != nil { + fmt.Println(err) // TODO: print to log + } + } + 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) SetCustomerGUID(customerGUID string) { c.configObj.CustomerGUID = customerGUID } -func (c *ClusterConfig) IsConfigFound() bool { - return existsConfigFile() || c.existsConfigMap() -} +func (c *ClusterConfig) GetConfigObj() *ConfigObj { return c.configObj } +func (c *ClusterConfig) GetDefaultNS() string { return c.configMapNamespace } +func (c *ClusterConfig) GetAccountID() string { return c.configObj.AccountID } +func (c *ClusterConfig) IsConfigFound() bool { return existsConfigFile() || c.existsConfigMap() } func (c *ClusterConfig) SetTenant() error { @@ -220,17 +247,34 @@ func (c *ClusterConfig) SetTenant() error { if err := getTenantConfigFromBE(c.backendAPI, c.configObj); err != nil { return err } - // update/create config - if c.existsConfigMap() { - c.updateConfigMap() - } else { - c.createConfigMap() - } - updateConfigFile(c.configObj) + c.UpdateCachedConfig() return nil } +func (c *ClusterConfig) UpdateCachedConfig() error { + // update/create config + if c.existsConfigMap() { + if err := c.updateConfigMap(); err != nil { + return err + } + } else { + if err := c.createConfigMap(); err != nil { + return err + } + } + return updateConfigFile(c.configObj) +} + +func (c *ClusterConfig) DeleteCachedConfig() error { + if err := c.deleteConfigMap(); err != nil { + return err + } + if err := DeleteConfigFile(); err != nil { + return err + } + return nil +} func (c *ClusterConfig) GetClusterName() string { return c.configObj.ClusterName } @@ -409,6 +453,10 @@ func readConfig(dat []byte) (*ConfigObj, error) { if err := json.Unmarshal(dat, configObj); err != nil { return nil, err } + if configObj.AccountID == "" { + configObj.AccountID = configObj.CustomerGUID + } + configObj.CustomerGUID = "" return configObj, nil } @@ -421,8 +469,7 @@ func (clusterConfig *ClusterConfig) IsSubmitted() bool { func (clusterConfig *ClusterConfig) IsRegistered() bool { // get from armoBE - clusterConfig.backendAPI.SetCustomerGUID(clusterConfig.GetCustomerGUID()) - tenantResponse, err := clusterConfig.backendAPI.GetCustomerGUID() + tenantResponse, err := clusterConfig.backendAPI.GetTenant() if err == nil && tenantResponse != nil { if tenantResponse.AdminMail != "" { // this customer already belongs to some user return true @@ -431,16 +478,7 @@ func (clusterConfig *ClusterConfig) IsRegistered() bool { return false } -func (clusterConfig *ClusterConfig) DeleteConfig() error { - if err := clusterConfig.DeleteConfigMap(); err != nil { - return err - } - if err := DeleteConfigFile(); err != nil { - return err - } - return nil -} -func (clusterConfig *ClusterConfig) DeleteConfigMap() error { +func (clusterConfig *ClusterConfig) deleteConfigMap() error { return clusterConfig.k8s.KubernetesClient.CoreV1().ConfigMaps(clusterConfig.configMapNamespace).Delete(context.Background(), clusterConfig.configMapName, metav1.DeleteOptions{}) } @@ -465,3 +503,16 @@ func getConfigMapNamespace() string { } return "default" } + +func getAccountFromEnv(configObj *ConfigObj) { + // load from env + if accountID := os.Getenv("KS_ACCOUNT_ID"); accountID != "" { + configObj.AccountID = accountID + } + if clientID := os.Getenv("KS_CLIENT_ID"); clientID != "" { + configObj.ClientID = clientID + } + if accessKey := os.Getenv("KS_ACCESS_KEY"); accessKey != "" { + configObj.AccessKey = accessKey + } +} diff --git a/cautils/getter/armoapi.go b/cautils/getter/armoapi.go index f194f4f1..32225519 100644 --- a/cautils/getter/armoapi.go +++ b/cautils/getter/armoapi.go @@ -30,24 +30,26 @@ var ( // Armo API for downloading policies type ArmoAPI struct { - httpClient *http.Client - apiURL string - erURL string - feURL string - customerGUID string + httpClient *http.Client + apiURL string + erURL string + feURL string + accountID string + clientID string + accessKey string } -var globalArmoAPIConnecctor *ArmoAPI +var globalArmoAPIConnector *ArmoAPI func SetARMOAPIConnector(armoAPI *ArmoAPI) { - globalArmoAPIConnecctor = armoAPI + globalArmoAPIConnector = armoAPI } func GetArmoAPIConnector() *ArmoAPI { - if globalArmoAPIConnecctor == nil { + if globalArmoAPIConnector == nil { glog.Error("returning nil API connector") } - return globalArmoAPIConnecctor + return globalArmoAPIConnector } func NewARMOAPIDev() *ArmoAPI { @@ -85,17 +87,15 @@ func newArmoAPI() *ArmoAPI { httpClient: &http.Client{Timeout: time.Duration(61) * time.Second}, } } -func (armoAPI *ArmoAPI) SetCustomerGUID(customerGUID string) { - armoAPI.customerGUID = customerGUID -} -func (armoAPI *ArmoAPI) GetFrontendURL() string { - return armoAPI.feURL -} - -func (armoAPI *ArmoAPI) GetReportReceiverURL() string { - return armoAPI.erURL -} +func (armoAPI *ArmoAPI) GetAccountID() string { return armoAPI.accountID } +func (armoAPI *ArmoAPI) GetClientID() string { return armoAPI.clientID } +func (armoAPI *ArmoAPI) GetAccessKey() string { return armoAPI.accessKey } +func (armoAPI *ArmoAPI) GetFrontendURL() string { return armoAPI.feURL } +func (armoAPI *ArmoAPI) GetReportReceiverURL() string { return armoAPI.erURL } +func (armoAPI *ArmoAPI) SetAccountID(accountID string) { armoAPI.accountID = accountID } +func (armoAPI *ArmoAPI) SetClientID(clientID string) { armoAPI.clientID = clientID } +func (armoAPI *ArmoAPI) SetAccessKey(accessKey string) { armoAPI.accessKey = accessKey } func (armoAPI *ArmoAPI) GetFramework(name string) (*reporthandling.Framework, error) { respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getFrameworkURL(name), nil) @@ -146,10 +146,10 @@ func (armoAPI *ArmoAPI) GetExceptions(clusterName string) ([]armotypes.PostureEx return exceptions, nil } -func (armoAPI *ArmoAPI) GetCustomerGUID() (*TenantResponse, error) { - url := armoAPI.getCustomerURL() - if armoAPI.customerGUID != "" { - url = fmt.Sprintf("%s?customerGUID=%s", url, armoAPI.customerGUID) +func (armoAPI *ArmoAPI) GetTenant() (*TenantResponse, error) { + url := armoAPI.getAccountURL() + if armoAPI.accountID != "" { + url = fmt.Sprintf("%s?customerGUID=%s", url, armoAPI.accountID) } respStr, err := HttpGetter(armoAPI.httpClient, url, nil) if err != nil { @@ -159,14 +159,16 @@ func (armoAPI *ArmoAPI) GetCustomerGUID() (*TenantResponse, error) { if err = JSONDecoder(respStr).Decode(tenant); err != nil { return nil, err } - + if tenant.TenantID != "" { + armoAPI.accountID = tenant.TenantID + } return tenant, nil } // ControlsInputs // map[][] func (armoAPI *ArmoAPI) GetAccountConfig(clusterName string) (*armotypes.CustomerConfig, error) { accountConfig := &armotypes.CustomerConfig{} - if armoAPI.customerGUID == "" { + if armoAPI.accountID == "" { return accountConfig, nil } respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getAccountConfig(clusterName), nil) diff --git a/cautils/getter/armoapiutils.go b/cautils/getter/armoapiutils.go index 3c14040f..ab326010 100644 --- a/cautils/getter/armoapiutils.go +++ b/cautils/getter/armoapiutils.go @@ -13,7 +13,7 @@ func (armoAPI *ArmoAPI) getFrameworkURL(frameworkName string) string { u.Host = armoAPI.apiURL u.Path = "api/v1/armoFrameworks" q := u.Query() - q.Add("customerGUID", armoAPI.customerGUID) + q.Add("customerGUID", armoAPI.accountID) if isNativeFramework(frameworkName) { q.Add("frameworkName", strings.ToUpper(frameworkName)) } else { @@ -31,7 +31,7 @@ func (armoAPI *ArmoAPI) getListFrameworkURL() string { u.Host = armoAPI.apiURL u.Path = "api/v1/armoFrameworks" q := u.Query() - q.Add("customerGUID", armoAPI.customerGUID) + q.Add("customerGUID", armoAPI.accountID) u.RawQuery = q.Encode() return u.String() @@ -43,7 +43,7 @@ func (armoAPI *ArmoAPI) getExceptionsURL(clusterName string) string { u.Path = "api/v1/armoPostureExceptions" q := u.Query() - q.Add("customerGUID", armoAPI.customerGUID) + q.Add("customerGUID", armoAPI.accountID) // if clusterName != "" { // TODO - fix customer name support in Armo BE // q.Add("clusterName", clusterName) // } @@ -59,7 +59,7 @@ func (armoAPI *ArmoAPI) getAccountConfig(clusterName string) string { u.Path = "api/v1/armoCustomerConfiguration" q := u.Query() - q.Add("customerGUID", armoAPI.customerGUID) + q.Add("customerGUID", armoAPI.accountID) if clusterName != "" { // TODO - fix customer name support in Armo BE q.Add("clusterName", clusterName) } @@ -68,7 +68,7 @@ func (armoAPI *ArmoAPI) getAccountConfig(clusterName string) string { return u.String() } -func (armoAPI *ArmoAPI) getCustomerURL() string { +func (armoAPI *ArmoAPI) getAccountURL() string { u := url.URL{} u.Scheme = "https" u.Host = armoAPI.apiURL diff --git a/cautils/getter/getpolicies.go b/cautils/getter/getpolicies.go index cb3abdf8..572aed32 100644 --- a/cautils/getter/getpolicies.go +++ b/cautils/getter/getpolicies.go @@ -24,8 +24,15 @@ type IExceptionsGetter interface { GetExceptions(clusterName string) ([]armotypes.PostureExceptionPolicy, error) } type IBackend interface { - GetCustomerGUID() (*TenantResponse, error) - SetCustomerGUID(customerGUID string) + GetAccountID() string + GetClientID() string + GetAccessKey() string + + SetAccountID(accountID string) + SetClientID(clientID string) + SetAccessKey(accessKey string) + + GetTenant() (*TenantResponse, error) } type IControlsInputsGetter interface { diff --git a/cautils/getter/getpoliciesutils.go b/cautils/getter/getpoliciesutils.go index fa859594..a88ddcf0 100644 --- a/cautils/getter/getpoliciesutils.go +++ b/cautils/getter/getpoliciesutils.go @@ -21,7 +21,7 @@ func GetDefaultPath(name string) string { } func SaveInFile(policy interface{}, pathStr string) error { - encodedData, err := json.Marshal(policy) + encodedData, err := json.MarshalIndent(policy, "", " ") if err != nil { return err } diff --git a/clihandler/clidelete.go b/clihandler/clidelete.go new file mode 100644 index 00000000..77e9f7ba --- /dev/null +++ b/clihandler/clidelete.go @@ -0,0 +1,7 @@ +package clihandler + +func CliDelete() error { + + tenant := getTenantConfig("", "", getKubernetesApi()) // change k8sinterface + return tenant.DeleteCachedConfig() +} diff --git a/clihandler/clidownload.go b/clihandler/clidownload.go index ca07847a..74778552 100644 --- a/clihandler/clidownload.go +++ b/clihandler/clidownload.go @@ -75,7 +75,7 @@ func downloadArtifacts(downloadInfo *cautils.DownloadInfo) error { func downloadConfigInputs(downloadInfo *cautils.DownloadInfo) error { tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) - controlsInputsGetter := getConfigInputsGetter(downloadInfo.Name, tenant.GetCustomerGUID(), nil) + controlsInputsGetter := getConfigInputsGetter(downloadInfo.Name, tenant.GetAccountID(), nil) controlInputs, err := controlsInputsGetter.GetControlsInputs(tenant.GetClusterName()) if err != nil { return err @@ -97,7 +97,7 @@ func downloadExceptions(downloadInfo *cautils.DownloadInfo) error { tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) exceptionsGetter := getExceptionsGetter("") exceptions := []armotypes.PostureExceptionPolicy{} - if tenant.GetCustomerGUID() != "" { + if tenant.GetAccountID() != "" { exceptions, err = exceptionsGetter.GetExceptions(tenant.GetClusterName()) if err != nil { return err @@ -118,7 +118,7 @@ func downloadExceptions(downloadInfo *cautils.DownloadInfo) error { func downloadFramework(downloadInfo *cautils.DownloadInfo) error { tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) - g := getPolicyGetter(nil, tenant.GetCustomerGUID(), true, nil) + g := getPolicyGetter(nil, tenant.GetAccountID(), true, nil) if downloadInfo.Name == "" { // if framework name not specified - download all frameworks @@ -154,7 +154,7 @@ func downloadFramework(downloadInfo *cautils.DownloadInfo) error { func downloadControl(downloadInfo *cautils.DownloadInfo) error { tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi()) - g := getPolicyGetter(nil, tenant.GetCustomerGUID(), false, nil) + g := getPolicyGetter(nil, tenant.GetAccountID(), false, nil) if downloadInfo.Name == "" { // TODO - support diff --git a/clihandler/clilist.go b/clihandler/clilist.go index e0c4878e..dda856ee 100644 --- a/clihandler/clilist.go +++ b/clihandler/clilist.go @@ -5,11 +5,11 @@ import ( "sort" "strings" - "github.com/armosec/kubescape/cautils" "github.com/armosec/kubescape/cautils/getter" + "github.com/armosec/kubescape/clihandler/cliobjects" ) -var listFunc = map[string]func(*cautils.ListPolicies) ([]string, error){ +var listFunc = map[string]func(*cliobjects.ListPolicies) ([]string, error){ "controls": listControls, "frameworks": listFrameworks, } @@ -21,7 +21,7 @@ func ListSupportCommands() []string { } return commands } -func CliList(listPolicies *cautils.ListPolicies) error { +func CliList(listPolicies *cliobjects.ListPolicies) error { if f, ok := listFunc[listPolicies.Target]; ok { policies, err := f(listPolicies) if err != nil { @@ -40,16 +40,16 @@ func CliList(listPolicies *cautils.ListPolicies) error { return fmt.Errorf("unknown command to download") } -func listFrameworks(listPolicies *cautils.ListPolicies) ([]string, error) { +func listFrameworks(listPolicies *cliobjects.ListPolicies) ([]string, error) { tenant := getTenantConfig(listPolicies.Account, "", getKubernetesApi()) // change k8sinterface - g := getPolicyGetter(nil, tenant.GetCustomerGUID(), true, nil) + g := getPolicyGetter(nil, tenant.GetAccountID(), true, nil) return listFrameworksNames(g), nil } -func listControls(listPolicies *cautils.ListPolicies) ([]string, error) { +func listControls(listPolicies *cliobjects.ListPolicies) ([]string, error) { tenant := getTenantConfig(listPolicies.Account, "", getKubernetesApi()) // change k8sinterface - g := getPolicyGetter(nil, tenant.GetCustomerGUID(), false, nil) + g := getPolicyGetter(nil, tenant.GetAccountID(), false, nil) l := getter.ListName if listPolicies.ListIDs { l = getter.ListID diff --git a/cautils/listpolicies.go b/clihandler/cliobjects/listpolicies.go similarity index 80% rename from cautils/listpolicies.go rename to clihandler/cliobjects/listpolicies.go index 044c2c00..c8385b0c 100644 --- a/cautils/listpolicies.go +++ b/clihandler/cliobjects/listpolicies.go @@ -1,4 +1,4 @@ -package cautils +package cliobjects type ListPolicies struct { Target string diff --git a/clihandler/cliobjects/set.go b/clihandler/cliobjects/set.go new file mode 100644 index 00000000..53ab2940 --- /dev/null +++ b/clihandler/cliobjects/set.go @@ -0,0 +1,7 @@ +package cliobjects + +type SetConfig struct { + Account string + ClientID string + AccessKey string +} diff --git a/clihandler/cliobjects/submit.go b/clihandler/cliobjects/submit.go new file mode 100644 index 00000000..e250e880 --- /dev/null +++ b/clihandler/cliobjects/submit.go @@ -0,0 +1,5 @@ +package cliobjects + +type Submit struct { + Account string +} diff --git a/clihandler/cliset.go b/clihandler/cliset.go new file mode 100644 index 00000000..3ff6d3da --- /dev/null +++ b/clihandler/cliset.go @@ -0,0 +1,22 @@ +package clihandler + +import ( + "github.com/armosec/kubescape/clihandler/cliobjects" +) + +func CliSetConfig(setConfig *cliobjects.SetConfig) error { + + tenant := getTenantConfig("", "", getKubernetesApi()) + + if setConfig.Account != "" { + tenant.GetConfigObj().AccountID = setConfig.Account + } + if setConfig.AccessKey != "" { + tenant.GetConfigObj().AccessKey = setConfig.AccessKey + } + if setConfig.ClientID != "" { + tenant.GetConfigObj().ClientID = setConfig.ClientID + } + + return tenant.UpdateCachedConfig() +} diff --git a/clihandler/cliview.go b/clihandler/cliview.go new file mode 100644 index 00000000..bd76ecb7 --- /dev/null +++ b/clihandler/cliview.go @@ -0,0 +1,9 @@ +package clihandler + +import "fmt" + +func CliView() error { + tenant := getTenantConfig("", "", getKubernetesApi()) // change k8sinterface + fmt.Printf("%s\n", tenant.GetConfigObj().Config()) + return nil +} diff --git a/clihandler/cmd/cluster.go b/clihandler/cmd/cluster.go index e81ec7d0..30f75f25 100644 --- a/clihandler/cmd/cluster.go +++ b/clihandler/cmd/cluster.go @@ -6,9 +6,10 @@ import ( // clusterCmd represents the cluster command var clusterCmd = &cobra.Command{ - Use: "cluster", - Short: "Set configuration for cluster", - Long: ``, + Use: "cluster", + Short: "Set configuration for cluster", + Long: ``, + Deprecated: "use the 'set' command instead", Run: func(cmd *cobra.Command, args []string) { }, } diff --git a/clihandler/cmd/cluster_get.go b/clihandler/cmd/cluster_get.go index a56ea117..45cab878 100644 --- a/clihandler/cmd/cluster_get.go +++ b/clihandler/cmd/cluster_get.go @@ -11,9 +11,10 @@ import ( ) var getCmd = &cobra.Command{ - Use: "get ", - Short: "Get configuration in cluster", - Long: ``, + Use: "get ", + Short: "Get configuration in cluster", + Long: ``, + Deprecated: "use the 'view' command instead", Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 || len(args) > 1 { return fmt.Errorf("requires one argument") diff --git a/clihandler/cmd/cluster_set.go b/clihandler/cmd/cluster_set.go index 609d0ca5..4858e0bb 100644 --- a/clihandler/cmd/cluster_set.go +++ b/clihandler/cmd/cluster_set.go @@ -10,10 +10,11 @@ import ( "github.com/spf13/cobra" ) -var setCmd = &cobra.Command{ - Use: "set =", - Short: "Set configuration in cluster", - Long: ``, +var setClusterCmd = &cobra.Command{ + Use: "set =", + Short: "Set configuration in cluster", + Long: ``, + Deprecated: "use the 'set' command instead", Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 || len(args) > 1 { return fmt.Errorf("requires one argument: =") @@ -40,5 +41,5 @@ var setCmd = &cobra.Command{ } func init() { - clusterCmd.AddCommand(setCmd) + clusterCmd.AddCommand(setClusterCmd) } diff --git a/clihandler/cmd/config.go b/clihandler/cmd/config.go index 10b0f3ec..f11f1954 100644 --- a/clihandler/cmd/config.go +++ b/clihandler/cmd/config.go @@ -1,18 +1,127 @@ package cmd import ( + "fmt" + "os" + "strings" + + "github.com/armosec/kubescape/clihandler" + "github.com/armosec/kubescape/clihandler/cliobjects" "github.com/spf13/cobra" ) +var ( + configExample = ` + # View cached configurations + kubescape config view + + # Delete cached configurations + kubescape config delete + + # Set cached configurations + kubescape config set --help +` + setConfigExample = ` + # Set account id + kubescape config set accountID + + # Set client id + kubescape config set clientID + + # Set access key + kubescape config set accessKey +` +) + // configCmd represents the config command var configCmd = &cobra.Command{ - Use: "config", - Short: "Set configuration", + Use: "config", + Short: "handle cached configurations", + Example: configExample, +} + +var setConfig = cliobjects.SetConfig{} + +// configCmd represents the config command +var configSetCmd = &cobra.Command{ + Use: "set", + Short: fmt.Sprintf("Set configurations, supported: %s", strings.Join(stringKeysToSlice(supportConfigSet), "/")), + Example: setConfigExample, + ValidArgs: stringKeysToSlice(supportConfigSet), + RunE: func(cmd *cobra.Command, args []string) error { + if err := parseSetArgs(args); err != nil { + return err + } + if err := clihandler.CliSetConfig(&setConfig); err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + return nil + }, +} + +var supportConfigSet = map[string]func(*cliobjects.SetConfig, string){ + "accountID": func(s *cliobjects.SetConfig, account string) { s.Account = account }, + "clientID": func(s *cliobjects.SetConfig, clientID string) { s.ClientID = clientID }, + "accessKey": func(s *cliobjects.SetConfig, accessKey string) { s.AccessKey = accessKey }, +} + +func stringKeysToSlice(m map[string]func(*cliobjects.SetConfig, string)) []string { + l := []string{} + for i := range m { + l = append(l, i) + } + return l +} + +func parseSetArgs(args []string) error { + var key string + var value string + if len(args) == 1 { + if keyValue := strings.Split(args[0], "="); len(keyValue) == 2 { + key = keyValue[0] + value = keyValue[1] + } + } else if len(args) == 2 { + key = args[0] + value = args[1] + } + if setConfigFunc, ok := supportConfigSet[key]; ok { + setConfigFunc(&setConfig, value) + } else { + return fmt.Errorf("key '%s' unknown . supported: %s", key, strings.Join(stringKeysToSlice(supportConfigSet), "/")) + } + return nil +} + +var configDeleteCmd = &cobra.Command{ + Use: "delete", + Short: "Delete cached configurations", Long: ``, Run: func(cmd *cobra.Command, args []string) { + if err := clihandler.CliDelete(); err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + }, +} + +// configCmd represents the config command +var configViewCmd = &cobra.Command{ + Use: "view", + Short: "View cached configurations", + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + if err := clihandler.CliView(); err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } }, } func init() { rootCmd.AddCommand(configCmd) + configCmd.AddCommand(configSetCmd) + configCmd.AddCommand(configDeleteCmd) + configCmd.AddCommand(configViewCmd) } diff --git a/clihandler/cmd/download.go b/clihandler/cmd/download.go index 412c3733..77d9166f 100644 --- a/clihandler/cmd/download.go +++ b/clihandler/cmd/download.go @@ -69,7 +69,7 @@ func init() { // cobra.OnInitialize(initConfig) rootCmd.AddCommand(downloadCmd) - 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") + downloadCmd.Flags().StringVarP(&downloadInfo.Path, "output", "o", "", "Output file. If not specified, will save in `~/.kubescape/.json`") } diff --git a/clihandler/cmd/list.go b/clihandler/cmd/list.go index 06befd99..0c9706b8 100644 --- a/clihandler/cmd/list.go +++ b/clihandler/cmd/list.go @@ -7,6 +7,7 @@ import ( "github.com/armosec/kubescape/cautils" "github.com/armosec/kubescape/clihandler" + "github.com/armosec/kubescape/clihandler/cliobjects" "github.com/spf13/cobra" ) @@ -28,7 +29,7 @@ var ( https://hub.armo.cloud/docs/controls ` ) -var listPolicies = cautils.ListPolicies{} +var listPolicies = cliobjects.ListPolicies{} var listCmd = &cobra.Command{ Use: "list [flags]", diff --git a/clihandler/cmd/local.go b/clihandler/cmd/local.go index 75e7b680..224de282 100644 --- a/clihandler/cmd/local.go +++ b/clihandler/cmd/local.go @@ -5,9 +5,10 @@ import ( ) var localCmd = &cobra.Command{ - Use: "local", - Short: "Set configuration locally (for config.json)", - Long: ``, + Use: "local", + Short: "Set configuration locally (for config.json)", + Long: ``, + Deprecated: "use the 'set' command instead", Run: func(cmd *cobra.Command, args []string) { }, } diff --git a/clihandler/cmd/local_get.go b/clihandler/cmd/local_get.go index 9807e794..d3bde11b 100644 --- a/clihandler/cmd/local_get.go +++ b/clihandler/cmd/local_get.go @@ -9,9 +9,10 @@ import ( ) var localGetCmd = &cobra.Command{ - Use: "get ", - Short: "Get configuration locally", - Long: ``, + Use: "get ", + Short: "Get configuration locally", + Long: ``, + Deprecated: "use the 'view' command instead", Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 || len(args) > 1 { return fmt.Errorf("requires one argument") diff --git a/clihandler/cmd/local_set.go b/clihandler/cmd/local_set.go index e5e8c7c4..0b2cc89a 100644 --- a/clihandler/cmd/local_set.go +++ b/clihandler/cmd/local_set.go @@ -9,9 +9,10 @@ import ( ) var localSetCmd = &cobra.Command{ - Use: "set =", - Short: "Set configuration locally", - Long: ``, + Use: "set =", + Short: "Set configuration locally", + Long: ``, + Deprecated: "use the 'set' command instead", Args: func(cmd *cobra.Command, args []string) error { if len(args) < 1 || len(args) > 1 { return fmt.Errorf("requires one argument: =") diff --git a/clihandler/cmd/rbac.go b/clihandler/cmd/rbac.go index 9ece1ba4..2762ddf7 100644 --- a/clihandler/cmd/rbac.go +++ b/clihandler/cmd/rbac.go @@ -29,7 +29,7 @@ var rabcCmd = &cobra.Command{ } // list RBAC - rbacObjects := cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, clusterConfig.GetCustomerGUID(), clusterConfig.GetClusterName())) + rbacObjects := cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, clusterConfig.GetAccountID(), clusterConfig.GetClusterName())) // submit resources r := reporterv1.NewReportEventReceiver(clusterConfig.GetConfigObj()) diff --git a/clihandler/cmd/results.go b/clihandler/cmd/results.go index 081e9913..1932ccb9 100644 --- a/clihandler/cmd/results.go +++ b/clihandler/cmd/results.go @@ -67,7 +67,7 @@ var resultsCmd = &cobra.Command{ return err } - resultsObjects := NewResultsObject(clusterConfig.GetCustomerGUID(), clusterConfig.GetClusterName(), args[0]) + resultsObjects := NewResultsObject(clusterConfig.GetAccountID(), clusterConfig.GetClusterName(), args[0]) // submit resources r := reporterv1.NewReportEventReceiver(clusterConfig.GetConfigObj()) diff --git a/clihandler/cmd/root.go b/clihandler/cmd/root.go index 4fd5792f..c035e216 100644 --- a/clihandler/cmd/root.go +++ b/clihandler/cmd/root.go @@ -10,7 +10,6 @@ import ( "github.com/spf13/cobra" ) -var cfgFile string var armoBEURLs = "" const envFlagUsage = "Send report results to specific URL. Format:,,.\n\t\tExample:report.armo.cloud,api.armo.cloud,portal.armo.cloud" @@ -31,7 +30,7 @@ func Execute() { } func init() { - rootCmd.PersistentFlags().StringVarP(&scanInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file") + flag.CommandLine.StringVar(&armoBEURLs, "environment", "", envFlagUsage) rootCmd.PersistentFlags().StringVar(&armoBEURLs, "environment", "", envFlagUsage) rootCmd.PersistentFlags().MarkHidden("environment") diff --git a/clihandler/cmd/scan.go b/clihandler/cmd/scan.go index 76761bc7..20469c3e 100644 --- a/clihandler/cmd/scan.go +++ b/clihandler/cmd/scan.go @@ -42,7 +42,9 @@ func init() { cobra.OnInitialize(frameworkInitConfig) rootCmd.AddCommand(scanCmd) - rootCmd.PersistentFlags().StringVarP(&scanInfo.KubeContext, "kube-context", "", "", "Kube context. Default will use the current-context") + + scanCmd.PersistentFlags().StringVarP(&scanInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file") + scanCmd.PersistentFlags().StringVarP(&scanInfo.KubeContext, "kube-context", "", "", "Kube context. Default will use the current-context") scanCmd.PersistentFlags().StringVar(&scanInfo.ControlsInputs, "controls-config", "", "Path to an controls-config obj. If not set will download controls-config from ARMO management portal") scanCmd.PersistentFlags().StringVar(&scanInfo.UseExceptions, "exceptions", "", "Path to an exceptions obj. If not set will download exceptions from ARMO management portal") scanCmd.PersistentFlags().StringVar(&scanInfo.UseArtifactsFrom, "use-artifacts-from", "", "Load artifacts from local directory. If not used will download them") diff --git a/clihandler/cmd/submit.go b/clihandler/cmd/submit.go index 1f27b4eb..7677fa84 100644 --- a/clihandler/cmd/submit.go +++ b/clihandler/cmd/submit.go @@ -4,9 +4,12 @@ import ( "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/kubescape/cautils" "github.com/armosec/kubescape/cautils/getter" + "github.com/armosec/kubescape/clihandler/cliobjects" "github.com/spf13/cobra" ) +var submitInfo cliobjects.Submit + var submitCmd = &cobra.Command{ Use: "submit ", Short: "Submit an object to the Kubescape SaaS version", @@ -16,12 +19,13 @@ var submitCmd = &cobra.Command{ } func init() { + submitCmd.PersistentFlags().StringVarP(&submitInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file") rootCmd.AddCommand(submitCmd) } func getSubmittedClusterConfig(k8s *k8sinterface.KubernetesApi) (*cautils.ClusterConfig, error) { - clusterConfig := cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account, scanInfo.KubeContext) // TODO - support none cluster env submit - if clusterConfig.GetCustomerGUID() != "" { + clusterConfig := cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), submitInfo.Account, scanInfo.KubeContext) // TODO - support none cluster env submit + if clusterConfig.GetAccountID() != "" { if err := clusterConfig.SetTenant(); err != nil { return clusterConfig, err } diff --git a/clihandler/initcli.go b/clihandler/initcli.go index 429cb854..c3ce60c7 100644 --- a/clihandler/initcli.go +++ b/clihandler/initcli.go @@ -11,7 +11,6 @@ import ( // printerv2 "github.com/armosec/kubescape/resultshandling/printer/v2" - "github.com/armosec/armoapi-go/armotypes" "github.com/armosec/kubescape/cautils" "github.com/armosec/kubescape/cautils/getter" "github.com/armosec/kubescape/clihandler/cliinterfaces" @@ -63,7 +62,12 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces { scanInfo.ExcludedNamespaces = fmt.Sprintf("%s,%s", scanInfo.ExcludedNamespaces, hostSensorHandler.GetNamespace()) } - resourceHandler := getResourceHandler(scanInfo, tenantConfig, k8s, hostSensorHandler) + registryAdaptors, err := resourcehandler.NewRegistryAdaptors() + if err != nil { + // display warning + } + + resourceHandler := getResourceHandler(scanInfo, tenantConfig, k8s, hostSensorHandler, registryAdaptors) // reporting behavior - setup reporter reportHandler := getReporter(tenantConfig, scanInfo.Submit) @@ -94,16 +98,16 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error { processNotification := make(chan *cautils.OPASessionObj) reportResults := make(chan *cautils.OPASessionObj) - cautils.ClusterName = interfaces.tenantConfig.GetClusterName() // TODO - Deprecated - cautils.CustomerGUID = interfaces.tenantConfig.GetCustomerGUID() // TODO - Deprecated + cautils.ClusterName = interfaces.tenantConfig.GetClusterName() // TODO - Deprecated + cautils.CustomerGUID = interfaces.tenantConfig.GetAccountID() // TODO - Deprecated interfaces.report.SetClusterName(interfaces.tenantConfig.GetClusterName()) - interfaces.report.SetCustomerGUID(interfaces.tenantConfig.GetCustomerGUID()) + interfaces.report.SetCustomerGUID(interfaces.tenantConfig.GetAccountID()) downloadReleasedPolicy := getter.NewDownloadReleasedPolicy() // download config inputs from github release // 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.PolicyGetter = getPolicyGetter(scanInfo.UseFrom, interfaces.tenantConfig.GetAccountID(), scanInfo.FrameworkScan, downloadReleasedPolicy) + scanInfo.Getters.ControlsInputsGetter = getConfigInputsGetter(scanInfo.ControlsInputs, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy) scanInfo.Getters.ExceptionsGetter = getExceptionsGetter(scanInfo.UseExceptions) // TODO - list supported frameworks/controls @@ -153,19 +157,9 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error { } func Scan(policyHandler *policyhandler.PolicyHandler, scanInfo *cautils.ScanInfo) error { - policyNotification := &reporthandling.PolicyNotification{ - NotificationType: reporthandling.TypeExecPostureScan, - Rules: scanInfo.PolicyIdentifier, - Designators: armotypes.PortalDesignator{}, - } - switch policyNotification.NotificationType { - case reporthandling.TypeExecPostureScan: - if err := policyHandler.HandleNotificationRequest(policyNotification, scanInfo); err != nil { - return err - } - - default: - return fmt.Errorf("notification type '%s' Unknown", policyNotification.NotificationType) + policyNotification := &reporthandling.PolicyNotification{Rules: scanInfo.PolicyIdentifier} + if err := policyHandler.HandleNotificationRequest(policyNotification, scanInfo); err != nil { + return err } return nil } diff --git a/clihandler/initcliutils.go b/clihandler/initcliutils.go index fd60a02b..56c122e5 100644 --- a/clihandler/initcliutils.go +++ b/clihandler/initcliutils.go @@ -42,7 +42,7 @@ func getExceptionsGetter(useExceptions string) getter.IExceptionsGetter { func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, submit bool) *cautils.RBACObjects { if submit { - return cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, tenantConfig.GetCustomerGUID(), tenantConfig.GetClusterName())) + return cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, tenantConfig.GetAccountID(), tenantConfig.GetClusterName())) } return nil } @@ -55,12 +55,13 @@ func getReporter(tenantConfig cautils.ITenantConfig, submit bool) reporter.IRepo return reporterv1.NewReportMock() } -func getResourceHandler(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, hostSensorHandler hostsensorutils.IHostSensor) resourcehandler.IResourceHandler { +func getResourceHandler(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, hostSensorHandler hostsensorutils.IHostSensor, registryAdaptors *resourcehandler.RegistryAdaptors) resourcehandler.IResourceHandler { if len(scanInfo.InputPatterns) > 0 || k8s == nil { - return resourcehandler.NewFileResourceHandler(scanInfo.InputPatterns) + return resourcehandler.NewFileResourceHandler(scanInfo.InputPatterns, registryAdaptors) } + getter.GetArmoAPIConnector() rbacObjects := getRBACHandler(tenantConfig, k8s, scanInfo.Submit) - return resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo), hostSensorHandler, rbacObjects) + return resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo), hostSensorHandler, rbacObjects, registryAdaptors) } func getHostSensorHandler(scanInfo *cautils.ScanInfo, k8s *k8sinterface.KubernetesApi) hostsensorutils.IHostSensor { @@ -152,7 +153,6 @@ func getPolicyGetter(loadPoliciesFromFile []string, accountID string, frameworkS } if accountID != "" && frameworkScope { g := getter.GetArmoAPIConnector() // download policy from ARMO backend - g.SetCustomerGUID(accountID) return g } if downloadReleasedPolicy == nil { @@ -183,7 +183,6 @@ func getConfigInputsGetter(ControlsInputs string, accountID string, downloadRele } if accountID != "" { g := getter.GetArmoAPIConnector() // download config from ARMO backend - g.SetCustomerGUID(accountID) return g } if downloadReleasedPolicy == nil { diff --git a/containerscan/containerscan_mock.go b/containerscan/containerscan_mock.go new file mode 100644 index 00000000..cac3471e --- /dev/null +++ b/containerscan/containerscan_mock.go @@ -0,0 +1,90 @@ +package containerscan + +import ( + "bytes" + "math/rand" + "time" + + "github.com/francoispqt/gojay" +) + +// GenerateContainerScanReportMock - generate a scan result +func GenerateContainerScanReportMock() ScanResultReport { + ds := ScanResultReport{ + WLID: "wlid://cluster-k8s-geriatrix-k8s-demo3/namespace-whisky-app/deployment-whisky4all-shipping", + CustomerGUID: "1231bcb1-49ce-4a67-bdd3-5da7a393ae08", + ImgTag: "dreg.armo.cloud:443/demoservice:v16", + ImgHash: "docker-pullable://dreg.armo.cloud:443/demoservice@sha256:754f3cfca915a07ed10655a301dd7a8dc5526a06f9bd06e7c932f4d4108a8296", + Timestamp: time.Now().UnixNano(), + } + + ds.Layers = make(LayersList, 0) + layer := ScanResultLayer{} + GenerateContainerScanLayer(&layer) + ds.Layers = append(ds.Layers, layer) + return ds +} + +// GenerateContainerScanReportMock - generate a scan result +func GenerateContainerScanReportNoVulMock() ScanResultReport { + ds := ScanResultReport{ + WLID: "wlid://cluster-k8s-geriatrix-k8s-demo3/namespace-whisky-app/deployment-whisky4all-shipping", + CustomerGUID: "1231bcb1-49ce-4a67-bdd3-5da7a393ae08", + ImgTag: "dreg.armo.cloud:443/demoservice:v16", + ImgHash: "docker-pullable://dreg.armo.cloud:443/demoservice@sha256:754f3cfca915a07ed10655a301dd7a8dc5526a06f9bd06e7c932f4d4108a8296", + Timestamp: time.Now().UnixNano(), + ContainerName: "shipping", + } + + ds.Layers = make(LayersList, 0) + layer := ScanResultLayer{LayerHash: "aaa"} + ds.Layers = append(ds.Layers, layer) + return ds +} + +var hash = []rune("abcdef0123456789") +var nums = []rune("0123456789") + +func randSeq(n int, bank []rune) string { + rand.Seed(time.Now().UnixNano()) + + b := make([]rune, n) + for i := range b { + b[i] = bank[rand.Intn(len(bank))] + } + return string(b) +} + +// GenerateContainerScanLayer - generate a layer with random vuls +func GenerateContainerScanLayer(layer *ScanResultLayer) { + layer.LayerHash = randSeq(32, hash) + layer.Vulnerabilities = make(VulnerabilitiesList, 0) + layer.Packages = make(LinuxPkgs, 0) + vuls := rand.Intn(10) + 1 + + for i := 0; i < vuls; i++ { + v := Vulnerability{} + GenerateVulnerability(&v) + layer.Vulnerabilities = append(layer.Vulnerabilities, v) + } + + pkg := LinuxPackage{PackageName: "coreutils"} + pkg.Files = make(PkgFiles, 0) + pf := PackageFile{Filename: "aa"} + pkg.Files = append(pkg.Files, pf) + layer.Packages = append(layer.Packages, pkg) +} + +// GenerateVulnerability - generate a vul (just diff "cve"'s) +func GenerateVulnerability(v *Vulnerability) error { + baseVul := " { \"name\": \"CVE-2014-9471\", \"imageTag\": \"debian:8\", \"link\": \"https://security-tracker.debian.org/tracker/CVE-2014-9471\", \"description\": \"The parse_datetime function in GNU coreutils allows remote attackers to cause a denial of service (crash) or possibly execute arbitrary code via a crafted date string, as demonstrated by the sdf\", \"severity\": \"Low\", \"metadata\": { \"NVD\": { \"CVSSv2\": { \"Score\": 7.5, \"Vectors\": \"AV:N/AC:L/Au:N/C:P/I:P\" } } }, \"fixedIn\": [ { \"name\": \"coreutils\", \"imageTag\": \"debian:8\", \"version\": \"8.23-1\" } ] }" + b := []byte(baseVul) + r := bytes.NewReader(b) + er := gojay.NewDecoder(r).DecodeObject(v) + v.RelatedPackageName = "coreutils" + v.Severity = HighSeverity + v.Relevancy = Irelevant + v.Name = "CVE-" + randSeq(4, nums) + "-" + randSeq(4, nums) + return er + +} diff --git a/containerscan/containerscan_test.go b/containerscan/containerscan_test.go new file mode 100644 index 00000000..6b668bd0 --- /dev/null +++ b/containerscan/containerscan_test.go @@ -0,0 +1,90 @@ +package containerscan + +import ( + "bytes" + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/francoispqt/gojay" +) + +func TestDecodeScanWIthDangearousArtifacts(t *testing.T) { + rhs := &ScanResultReport{} + er := gojay.NewDecoder(strings.NewReader(nginxScanJSON)).DecodeObject(rhs) + if er != nil { + t.Errorf("decode failed due to: %v", er.Error()) + } + sumObj := rhs.Summarize() + if sumObj.Registry != "" { + t.Errorf("sumObj.Registry = %v", sumObj.Registry) + } + if sumObj.VersionImage != "nginx:1.18.0" { + t.Errorf("sumObj.VersionImage = %v", sumObj.Registry) + } + if sumObj.ImgTag != "nginx:1.18.0" { + t.Errorf("sumObj.ImgTag = %v", sumObj.ImgTag) + } + if sumObj.Status != "Success" { + t.Errorf("sumObj.Status = %v", sumObj.Status) + } + if len(sumObj.ListOfDangerousArtifcats) != 3 { + t.Errorf("sumObj.ListOfDangerousArtifcats = %v", sumObj.ListOfDangerousArtifcats) + } +} + +func TestUnmarshalScanReport(t *testing.T) { + ds := GenerateContainerScanReportMock() + str1 := ds.AsFNVHash() + rhs := &ScanResultReport{} + + bolB, _ := json.Marshal(ds) + r := bytes.NewReader(bolB) + + er := gojay.NewDecoder(r).DecodeObject(rhs) + if er != nil { + t.Errorf("marshalling failed due to: %v", er.Error()) + } + + if rhs.AsFNVHash() != str1 { + t.Errorf("marshalling failed different values after marshal:\nOriginal:\n%v\nParsed:\n%v\n\n===\n", string(bolB), rhs) + } +} + +func TestUnmarshalScanReport1(t *testing.T) { + ds := Vulnerability{} + if err := GenerateVulnerability(&ds); err != nil { + t.Errorf("%v\n%v\n", ds, err) + } +} + +func TestGetByPkgNameSuccess(t *testing.T) { + ds := GenerateContainerScanReportMock() + a := ds.Layers[0].GetFilesByPackage("coreutils") + if a != nil { + + fmt.Printf("%+v\n", *a) + } + +} + +func TestGetByPkgNameMissing(t *testing.T) { + ds := GenerateContainerScanReportMock() + a := ds.Layers[0].GetFilesByPackage("s") + if a != nil && len(*a) > 0 { + t.Errorf("expected - no such package should be in that layer %v\n\n; found - %v", ds, a) + } + +} + +func TestCalculateFixed(t *testing.T) { + res := CalculateFixed([]FixedIn{{ + Name: "", + ImgTag: "", + Version: "", + }}) + if 0 != res { + t.Errorf("wrong fix status: %v", res) + } +} diff --git a/containerscan/datastructures.go b/containerscan/datastructures.go new file mode 100644 index 00000000..af4e4e1c --- /dev/null +++ b/containerscan/datastructures.go @@ -0,0 +1,37 @@ +package containerscan + +const ( + //defines Relevancy as enum-like + Unknown = "Unknown" + Relevant = "Relevant" + Irelevant = "Irelevant" + NoSP = "No signature profile to compare" + + //Clair Severities + UnknownSeverity = "Unknown" + NegligibleSeverity = "Negligible" + LowSeverity = "Low" + MediumSeverity = "Medium" + HighSeverity = "High" + CriticalSeverity = "Critical" + + ContainerScanRedisPrefix = "_containerscan" +) + +var KnownSeverities = map[string]bool{ + UnknownSeverity: true, + NegligibleSeverity: true, + LowSeverity: true, + MediumSeverity: true, + HighSeverity: true, + CriticalSeverity: true, +} + +func CalculateFixed(Fixes []FixedIn) int { + for _, fix := range Fixes { + if fix.Version != "None" && fix.Version != "" { + return 1 + } + } + return 0 +} diff --git a/containerscan/datastructuresmethods.go b/containerscan/datastructuresmethods.go new file mode 100644 index 00000000..c7cc7d2a --- /dev/null +++ b/containerscan/datastructuresmethods.go @@ -0,0 +1,51 @@ +package containerscan + +import ( + "strings" + + "github.com/armosec/armoapi-go/armotypes" +) + +func (layer *ScanResultLayer) GetFilesByPackage(pkgname string) (files *PkgFiles) { + for _, pkg := range layer.Packages { + if pkg.PackageName == pkgname { + return &pkg.Files + } + } + + return &PkgFiles{} +} + +func (layer *ScanResultLayer) GetPackagesNames() []string { + pkgsNames := []string{} + for _, pkg := range layer.Packages { + pkgsNames = append(pkgsNames, pkg.PackageName) + } + return pkgsNames +} + +func (scanresult *ScanResultReport) GetDesignatorsNContext() (*armotypes.PortalDesignator, []armotypes.ArmoContext) { + designatorsObj := armotypes.AttributesDesignatorsFromWLID(scanresult.WLID) + designatorsObj.Attributes["containerName"] = scanresult.ContainerName + designatorsObj.Attributes["customerGUID"] = scanresult.CustomerGUID + contextObj := armotypes.DesignatorToArmoContext(designatorsObj, "designators") + return designatorsObj, contextObj +} + +func (scanresult *ScanResultReport) Validate() bool { + if scanresult.CustomerGUID == "" || (scanresult.ImgHash == "" && scanresult.ImgTag == "") || scanresult.Timestamp <= 0 { + return false + } + + //TODO validate layers & vuls + + return true +} + +func (v *Vulnerability) IsRCE() bool { + desc := strings.ToLower(v.Description) + + isRCE := strings.Contains(v.Description, "RCE") + + return isRCE || strings.Contains(desc, "remote code execution") || strings.Contains(desc, "remote command execution") || strings.Contains(desc, "arbitrary code") || strings.Contains(desc, "code execution") || strings.Contains(desc, "code injection") || strings.Contains(desc, "command injection") || strings.Contains(desc, "inject arbitrary commands") +} diff --git a/containerscan/elasticadapters.go b/containerscan/elasticadapters.go new file mode 100644 index 00000000..c5bb4ef8 --- /dev/null +++ b/containerscan/elasticadapters.go @@ -0,0 +1,141 @@ +package containerscan + +import ( + "github.com/armosec/armoapi-go/armotypes" + cautils "github.com/armosec/utils-k8s-go/armometadata" +) + +// ToFlatVulnerabilities - returnsgit p +func (scanresult *ScanResultReport) ToFlatVulnerabilities() []*ElasticContainerVulnerabilityResult { + vuls := make([]*ElasticContainerVulnerabilityResult, 0) + vul2indx := make(map[string]int) + scanID := scanresult.AsFNVHash() + designatorsObj, ctxList := scanresult.GetDesignatorsNContext() + for _, layer := range scanresult.Layers { + for _, vul := range layer.Vulnerabilities { + esLayer := ESLayer{LayerHash: layer.LayerHash, ParentLayerHash: layer.ParentLayerHash} + if indx, isOk := vul2indx[vul.Name]; isOk { + vuls[indx].Layers = append(vuls[indx].Layers, esLayer) + continue + } + result := &ElasticContainerVulnerabilityResult{WLID: scanresult.WLID, + Timestamp: scanresult.Timestamp, + Designators: *designatorsObj, + Context: ctxList} + + result.Vulnerability = vul + result.Layers = make([]ESLayer, 0) + result.Layers = append(result.Layers, esLayer) + result.ContainerScanID = scanID + + result.IsFixed = CalculateFixed(vul.Fixes) + result.RelevantLinks = append(result.RelevantLinks, "https://nvd.nist.gov/vuln/detail/"+vul.Name) + result.RelevantLinks = append(result.RelevantLinks, vul.Link) + result.Vulnerability.Link = "https://nvd.nist.gov/vuln/detail/" + vul.Name + + result.Categories.IsRCE = result.IsRCE() + vuls = append(vuls, result) + vul2indx[vul.Name] = len(vuls) - 1 + + } + } + // find first introduced + for i, v := range vuls { + earlyLayer := "" + for _, layer := range v.Layers { + if layer.ParentLayerHash == earlyLayer { + earlyLayer = layer.LayerHash + } + } + vuls[i].IntroducedInLayer = earlyLayer + + } + + return vuls +} + +func (scanresult *ScanResultReport) Summarize() *ElasticContainerScanSummaryResult { + designatorsObj, ctxList := scanresult.GetDesignatorsNContext() + summary := &ElasticContainerScanSummaryResult{ + Designators: *designatorsObj, + Context: ctxList, + CustomerGUID: scanresult.CustomerGUID, + ImgTag: scanresult.ImgTag, + ImgHash: scanresult.ImgHash, + WLID: scanresult.WLID, + Timestamp: scanresult.Timestamp, + ContainerName: scanresult.ContainerName, + ContainerScanID: scanresult.AsFNVHash(), + ListOfDangerousArtifcats: scanresult.ListOfDangerousArtifcats, + } + + summary.Cluster = designatorsObj.Attributes[armotypes.AttributeCluster] + summary.Namespace = designatorsObj.Attributes[armotypes.AttributeNamespace] + + imageInfo, e2 := cautils.ImageTagToImageInfo(scanresult.ImgTag) + if e2 == nil { + summary.Registry = imageInfo.Registry + summary.VersionImage = imageInfo.VersionImage + } + + summary.PackagesName = make([]string, 0) + + severitiesStats := map[string]SeverityStats{} + + uniqueVulsMap := make(map[string]bool) + for _, layer := range scanresult.Layers { + summary.PackagesName = append(summary.PackagesName, (layer.GetPackagesNames())...) + for _, vul := range layer.Vulnerabilities { + if _, isOk := uniqueVulsMap[vul.Name]; isOk { + continue + } + uniqueVulsMap[vul.Name] = true + + // TODO: maybe add all severities just to have a placeholders + if !KnownSeverities[vul.Severity] { + vul.Severity = UnknownSeverity + } + + vulnSeverityStats, ok := severitiesStats[vul.Severity] + if !ok { + vulnSeverityStats = SeverityStats{Severity: vul.Severity} + } + + vulnSeverityStats.TotalCount++ + summary.TotalCount++ + isFixed := CalculateFixed(vul.Fixes) > 0 + if isFixed { + vulnSeverityStats.FixAvailableOfTotalCount++ + summary.FixAvailableOfTotalCount++ + } + isRCE := vul.IsRCE() + if isRCE { + vulnSeverityStats.RCECount++ + summary.RCECount++ + } + if vul.Relevancy == Relevant { + vulnSeverityStats.RelevantCount++ + summary.RelevantCount++ + if isFixed { + vulnSeverityStats.FixAvailableForRelevantCount++ + summary.FixAvailableForRelevantCount++ + } + + } + severitiesStats[vul.Severity] = vulnSeverityStats + } + } + summary.Status = "Success" + + // if criticalStats, hasCritical := severitiesStats[CriticalSeverity]; hasCritical && criticalStats.TotalCount > 0 { + // summary.Status = "Fail" + // } + // if highStats, hasHigh := severitiesStats[HighSeverity]; hasHigh && highStats.RelevantCount > 0 { + // summary.Status = "Fail" + // } + + for sever := range severitiesStats { + summary.SeveritiesStats = append(summary.SeveritiesStats, severitiesStats[sever]) + } + return summary +} diff --git a/containerscan/elasticdatastructures.go b/containerscan/elasticdatastructures.go new file mode 100644 index 00000000..46948ae4 --- /dev/null +++ b/containerscan/elasticdatastructures.go @@ -0,0 +1,89 @@ +package containerscan + +import "github.com/armosec/armoapi-go/armotypes" + +type ElasticContainerVulnerabilityResult struct { + Designators armotypes.PortalDesignator `json:"designators"` + Context []armotypes.ArmoContext `json:"context"` + + WLID string `json:"wlid"` + ContainerScanID string `json:"containersScanID"` + Layers []ESLayer `json:"layers"` + Timestamp int64 `json:"timestamp"` + IsFixed int `json:"isFixed"` + IntroducedInLayer string `json:"layerHash"` + RelevantLinks []string `json:"links"` // shitty SE practice + + Vulnerability `json:",inline"` +} + +type ESLayer struct { + LayerHash string `json:"layerHash"` + ParentLayerHash string `json:"parentLayerHash"` +} + +type SeverityStats struct { + Severity string `json:"severity,omitempty"` + TotalCount int64 `json:"total"` + FixAvailableOfTotalCount int64 `json:"fixedTotal"` + RelevantCount int64 `json:"totalRelevant"` + FixAvailableForRelevantCount int64 `json:"fixedRelevant"` + RCECount int64 `json:"rceTotal"` + UrgentCount int64 `json:"urgent"` + NeglectedCount int64 `json:"neglected"` + HealthStatus string `json:"healthStatus"` +} + +type ElasticContainerScanSeveritySummary struct { + Designators armotypes.PortalDesignator `json:"designators"` + Context []armotypes.ArmoContext `json:"context"` + + SeverityStats + CustomerGUID string `json:"customerGUID"` + ContainerScanID string `json:"containersScanID"` + Timestamp int64 `json:"timestamp"` + WLID string `json:"wlid"` + ImgTag string `json:"imageTag"` + ImgHash string `json:"imageHash"` + Cluster string `json:"cluster"` + Namespace string `json:"namespace"` + ContainerName string `json:"containerName"` + Status string `json:"status"` + Registry string `json:"registry"` + VersionImage string `json:"versionImage"` + Version string `json:"version"` + DayDate string `json:"dayDate"` +} + +type ElasticContainerScanSummaryResult struct { + SeverityStats + Designators armotypes.PortalDesignator `json:"designators"` + Context []armotypes.ArmoContext `json:"context"` + + CustomerGUID string `json:"customerGUID"` + ContainerScanID string `json:"containersScanID"` + + Timestamp int64 `json:"timestamp"` + WLID string `json:"wlid"` + ImgTag string `json:"imageTag"` + ImgHash string `json:"imageHash"` + Cluster string `json:"cluster"` + Namespace string `json:"namespace"` + ContainerName string `json:"containerName"` + PackagesName []string `json:"packages"` + + ListOfDangerousArtifcats []string `json:"listOfDangerousArtifcats"` + + Status string `json:"status"` + + Registry string `json:"registry"` + VersionImage string `json:"versionImage"` + + SeveritiesStats []SeverityStats `json:"severitiesStats"` + + Version string `json:"version"` +} + +func (summary *ElasticContainerScanSummaryResult) Validate() bool { + return summary.CustomerGUID != "" && summary.ContainerScanID != "" && (summary.ImgTag != "" || summary.ImgHash != "") && summary.Timestamp > 0 +} diff --git a/containerscan/gojayunmarshaller.go b/containerscan/gojayunmarshaller.go new file mode 100644 index 00000000..a79f5c08 --- /dev/null +++ b/containerscan/gojayunmarshaller.go @@ -0,0 +1,246 @@ +package containerscan + +import ( + "github.com/francoispqt/gojay" +) + +/* + responsible on fast unmarshaling of various COMMON containerscan structures and substructures + +*/ + +// UnmarshalJSONObject - File inside a pkg +func (file *PackageFile) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) { + + switch key { + case "name": + err = dec.String(&(file.Filename)) + } + return err + +} + +func (files *PkgFiles) UnmarshalJSONArray(dec *gojay.Decoder) error { + lae := PackageFile{} + if err := dec.Object(&lae); err != nil { + return err + } + + *files = append(*files, lae) + return nil +} + +func (file *PackageFile) NKeys() int { + return 0 +} + +// UnmarshalJSONObject--- Package +func (pkgnx *LinuxPackage) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) { + + switch key { + case "packageName": + err = dec.String(&(pkgnx.PackageName)) + + case "version": + err = dec.String(&(pkgnx.PackageVersion)) + + case "files": + err = dec.Array(&(pkgnx.Files)) + } + return err +} + +func (file *LinuxPackage) NKeys() int { + return 0 +} + +func (pkgs *LinuxPkgs) UnmarshalJSONArray(dec *gojay.Decoder) error { + lae := LinuxPackage{} + if err := dec.Object(&lae); err != nil { + return err + } + + *pkgs = append(*pkgs, lae) + return nil +} + +//--------Vul fixed in---------------------------------- +func (fx *FixedIn) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) { + + switch key { + case "name": + err = dec.String(&(fx.Name)) + + case "imageTag": + err = dec.String(&(fx.ImgTag)) + case "version": + err = dec.String(&(fx.Version)) + } + return err +} + +func (t *VulFixes) UnmarshalJSONArray(dec *gojay.Decoder) error { + lae := FixedIn{} + if err := dec.Object(&lae); err != nil { + return err + } + + *t = append(*t, lae) + return nil +} + +func (file *FixedIn) NKeys() int { + return 0 +} + +//------ VULNERABIlITy --------------------- + +// Name string `json:"name"` +// ImgHash string `json:"imageHash"` +// ImgTag string `json:"imageTag",omitempty` +// RelatedPackageName string `json:"packageName"` +// PackageVersion string `json:"packageVersion"` +// Link string `json:"link"` +// Description string `json:"description"` +// Severity string `json:"severity"` +// Metadata interface{} `json:"metadata",omitempty` +// Fixes VulFixes `json:"fixedIn",omitempty` +// Relevancy string `json:"relevant"` // use the related enum + +func (v *Vulnerability) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) { + + switch key { + case "name": + err = dec.String(&(v.Name)) + + case "imageTag": + err = dec.String(&(v.ImgTag)) + case "imageHash": + err = dec.String(&(v.ImgHash)) + + case "packageName": + err = dec.String(&(v.RelatedPackageName)) + + case "packageVersion": + err = dec.String(&(v.PackageVersion)) + + case "link": + err = dec.String(&(v.Link)) + + case "description": + err = dec.String(&(v.Description)) + + case "severity": + err = dec.String(&(v.Severity)) + + case "relevant": + err = dec.String(&(v.Relevancy)) + + case "fixedIn": + err = dec.Array(&(v.Fixes)) + + case "metadata": + err = dec.Interface(&(v.Metadata)) + } + + return err +} + +func (t *VulnerabilitiesList) UnmarshalJSONArray(dec *gojay.Decoder) error { + lae := Vulnerability{} + if err := dec.Object(&lae); err != nil { + return err + } + + *t = append(*t, lae) + return nil +} + +func (v *Vulnerability) NKeys() int { + return 0 +} + +//---------Layer Object---------------------------------- +// type ScanResultLayer struct { +// LayerHash string `json:layerHash` +// Vulnerabilities []Vulnerability `json:vulnerabilities` +// Packages []LinuxPackage `json:packageToFile` +// } + +func (scan *ScanResultLayer) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) { + + switch key { + // case "timestamp": + // err = dec.Time(&(reporter.Timestamp), time.RFC3339) + // reporter.Timestamp = reporter.Timestamp.Local() + case "layerHash": + err = dec.String(&(scan.LayerHash)) + + case "parentLayerHash": + err = dec.String(&(scan.ParentLayerHash)) + + case "vulnerabilities": + err = dec.Array(&(scan.Vulnerabilities)) + case "packageToFile": + err = dec.Array(&(scan.Packages)) + } + return err +} + +func (t *LayersList) UnmarshalJSONArray(dec *gojay.Decoder) error { + lae := ScanResultLayer{} + if err := dec.Object(&lae); err != nil { + return err + } + + *t = append(*t, lae) + return nil +} + +func (scan *ScanResultLayer) NKeys() int { + return 0 +} + +//---------------------SCAN RESULT-------------------------------------------------------------------------- + +// type ScanResultReport struct { +// CustomerGUID string `json:customerGuid` +// ImgTag string `json:imageTag,omitempty` +// ImgHash string `json:imageHash` +// WLID string `json:wlid` +// Timestamp int `json:customerGuid` +// Layers []ScanResultLayer `json:layers` +// ContainerName +// } + +func (scan *ScanResultReport) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) { + + switch key { + // case "timestamp": + // err = dec.Time(&(reporter.Timestamp), time.RFC3339) + // reporter.Timestamp = reporter.Timestamp.Local() + case "customerGUID": + err = dec.String(&(scan.CustomerGUID)) + case "imageTag": + err = dec.String(&(scan.ImgTag)) + case "imageHash": + err = dec.String(&(scan.ImgHash)) + case "wlid": + err = dec.String(&(scan.WLID)) + case "containerName": + err = dec.String(&(scan.ContainerName)) + case "timestamp": + err = dec.Int64(&(scan.Timestamp)) + case "layers": + err = dec.Array(&(scan.Layers)) + + case "listOfDangerousArtifcats": + err = dec.SliceString(&(scan.ListOfDangerousArtifcats)) + + } + return err +} + +func (scan *ScanResultReport) NKeys() int { + return 0 +} diff --git a/containerscan/jsonrawscan.go b/containerscan/jsonrawscan.go new file mode 100644 index 00000000..f0c946fb --- /dev/null +++ b/containerscan/jsonrawscan.go @@ -0,0 +1,525 @@ +package containerscan + +var nginxScanJSON = ` +{ + "customerGUID": "1e3a88bf-92ce-44f8-914e-cbe71830d566", + "imageTag": "nginx:1.18.0", + "imageHash": "", + "wlid": "wlid://cluster-test/namespace-test/deployment-davidg", + "containerName": "nginx-1", + "timestamp": 1628091365, + "layers": [ + { + "layerHash": "sha256:f7ec5a41d630a33a2d1db59b95d89d93de7ae5a619a3a8571b78457e48266eba", + "parentLayerHash": "", + "vulnerabilities": [ + { + "name": "CVE-2009-0854", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "dash", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-0854", + "description": "Untrusted search path vulnerability in dash 0.5.4, when used as a login shell, allows local users to execute arbitrary code via a Trojan horse .profile file in the current working directory.", + "severity": "Medium", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2019-13627", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "libgcrypt20", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-13627", + "description": "It was discovered that there was a ECDSA timing attack in the libgcrypt20 cryptographic library. Version affected: 1.8.4-5, 1.7.6-2+deb9u3, and 1.6.3-2+deb8u4. Versions fixed: 1.8.5-2 and 1.6.3-2+deb8u7.", + "severity": "Medium", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2021-33560", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "libgcrypt20", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-33560", + "description": "Libgcrypt before 1.8.8 and 1.9.x before 1.9.3 mishandles ElGamal encryption because it lacks exponent blinding to address a side-channel attack against mpi_powm, and the window size is not chosen appropriately. (There is also an interoperability problem because the selection of the k integer value does not properly consider the differences between basic ElGamal encryption and generalized ElGamal encryption.) This, for example, affects use of ElGamal in OpenPGP.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:1.8.4-5+deb10u1" + } + ], + "relevant": "" + }, + { + "name": "CVE-2021-3345", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "libgcrypt20", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-3345", + "description": "_gcry_md_block_write in cipher/hash-common.c in Libgcrypt version 1.9.0 has a heap-based buffer overflow when the digest final function sets a large count value. It is recommended to upgrade to 1.9.1 or later.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2010-0834", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "base-files", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-0834", + "description": "The base-files package before 5.0.0ubuntu7.1 on Ubuntu 9.10 and before 5.0.0ubuntu20.10.04.2 on Ubuntu 10.04 LTS, as shipped on Dell Latitude 2110 netbooks, does not require authentication for package installation, which allows remote archive servers and man-in-the-middle attackers to execute arbitrary code via a crafted package.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2018-6557", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "base-files", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-6557", + "description": "The MOTD update script in the base-files package in Ubuntu 18.04 LTS before 10.1ubuntu2.2, and Ubuntu 18.10 before 10.1ubuntu6 incorrectly handled temporary files. A local attacker could use this issue to cause a denial of service, or possibly escalate privileges if kernel symlink restrictions were disabled.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2013-0223", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "coreutils", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-0223", + "description": "The SUSE coreutils-i18n.patch for GNU coreutils allows context-dependent attackers to cause a denial of service (segmentation fault and crash) via a long string to the join command, when using the -i switch, which triggers a stack-based buffer overflow in the alloca function.", + "severity": "Low", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2015-4041", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "coreutils", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-4041", + "description": "The keycompare_mb function in sort.c in sort in GNU Coreutils through 8.23 on 64-bit platforms performs a size calculation without considering the number of bytes occupied by multibyte characters, which allows attackers to cause a denial of service (heap-based buffer overflow and application crash) or possibly have unspecified other impact via long UTF-8 strings.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2009-4135", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "coreutils", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-4135", + "description": "The distcheck rule in dist-check.mk in GNU coreutils 5.2.1 through 8.1 allows local users to gain privileges via a symlink attack on a file in a directory tree under /tmp.", + "severity": "Medium", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2015-4042", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "coreutils", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-4042", + "description": "Integer overflow in the keycompare_mb function in sort.c in sort in GNU Coreutils through 8.23 might allow attackers to cause a denial of service (application crash) or possibly have unspecified other impact via long strings.", + "severity": "Critical", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2013-0221", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "coreutils", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-0221", + "description": "The SUSE coreutils-i18n.patch for GNU coreutils allows context-dependent attackers to cause a denial of service (segmentation fault and crash) via a long string to the sort command, when using the (1) -d or (2) -M switch, which triggers a stack-based buffer overflow in the alloca function.", + "severity": "Medium", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2013-0222", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "coreutils", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2013-0222", + "description": "The SUSE coreutils-i18n.patch for GNU coreutils allows context-dependent attackers to cause a denial of service (segmentation fault and crash) via a long string to the uniq command, which triggers a stack-based buffer overflow in the alloca function.", + "severity": "Low", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2016-2781", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "coreutils", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-2781", + "description": "chroot in GNU coreutils, when used with --userspec, allows local users to escape to the parent session via a crafted TIOCSTI ioctl call, which pushes characters to the terminal's input buffer.", + "severity": "Medium", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2017-18018", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "coreutils", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-18018", + "description": "In GNU Coreutils through 8.29, chown-core.c in chown and chgrp does not prevent replacement of a plain file with a symlink during use of the POSIX \"-R -L\" options, which allows local users to modify the ownership of arbitrary files by leveraging a race condition.", + "severity": "Medium", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2021-20193", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "tar", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-20193", + "description": "A flaw was found in the src/list.c of tar 1.33 and earlier. This flaw allows an attacker who can submit a crafted input file to tar to cause uncontrolled consumption of memory. The highest threat from this vulnerability is to system availability.", + "severity": "Medium", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2005-2541", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "tar", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2005-2541", + "description": "Tar 1.15.1 does not properly warn the user when extracting setuid or setgid files, which may allow local users or remote attackers to gain privileges.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2019-9923", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "tar", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9923", + "description": "pax_decode_header in sparse.c in GNU Tar before 1.32 had a NULL pointer dereference when parsing certain archives that have malformed extended headers.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2018-1000654", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "libtasn1-6", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-1000654", + "description": "GNU Libtasn1-4.13 libtasn1-4.13 version libtasn1-4.13, libtasn1-4.12 contains a DoS, specifically CPU usage will reach 100% when running asn1Paser against the POC due to an issue in _asn1_expand_object_id(p_tree), after a long time, the program will be killed. This attack appears to be exploitable via parsing a crafted file.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2011-3374", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "apt", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3374", + "description": "It was found that apt-key in apt, all versions, do not correctly validate gpg keys with the master keyring, leading to a potential man-in-the-middle attack.", + "severity": "Medium", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2021-37600", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "util-linux", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-37600", + "description": "An integer overflow in util-linux through 2.37.1 can potentially cause a buffer overflow if an attacker were able to use system resources in a way that leads to a large number in the /proc/sysvipc/sem file.", + "severity": "Unknown", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2007-0822", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "util-linux", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2007-0822", + "description": "umount, when running with the Linux 2.6.15 kernel on Slackware Linux 10.2, allows local users to trigger a NULL dereference and application crash by invoking the program with a pathname for a USB pen drive that was mounted and then physically removed, which might allow the users to obtain sensitive information, including core file contents.", + "severity": "Low", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2004-1349", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "gzip", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2004-1349", + "description": "gzip before 1.3 in Solaris 8, when called with the -f or -force flags, will change the permissions of files that are hard linked to the target files, which allows local users to view or modify these files.", + "severity": "Low", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2004-0603", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "gzip", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2004-0603", + "description": "gzexe in gzip 1.3.3 and earlier will execute an argument when the creation of a temp file fails instead of exiting the program, which could allow remote attackers or local users to execute arbitrary commands, a different vulnerability than CVE-1999-1332.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2010-0002", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "bash", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-0002", + "description": "The /etc/profile.d/60alias.sh script in the Mandriva bash package for Bash 2.05b, 3.0, 3.2, 3.2.48, and 4.0 enables the --show-control-chars option in LS_OPTIONS, which allows local users to send escape sequences to terminal emulators, or hide the existence of a file, via a crafted filename.", + "severity": "Low", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + }, + { + "name": "CVE-2019-18276", + "imageHash": "sha256:c2c45d506085d300b72a6d4b10e3dce104228080a2cf095fc38333afe237e2be", + "imageTag": "", + "packageName": "bash", + "packageVersion": "", + "link": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-18276", + "description": "An issue was discovered in disable_priv_mode in shell.c in GNU Bash through 5.0 patch 11. By default, if Bash is run with its effective UID not equal to its real UID, it will drop privileges by setting its effective UID to its real UID. However, it does so incorrectly. On Linux and other systems that support \"saved UID\" functionality, the saved UID is not dropped. An attacker with command execution in the shell can use \"enable -f\" for runtime loading of a new builtin, which can be a shared object that calls setuid() and therefore regains privileges. However, binaries running with an effective UID of 0 are unaffected.", + "severity": "High", + "metadata": null, + "fixedIn": [ + { + "name": "", + "imageTag": "", + "version": "0:0" + } + ], + "relevant": "" + } + ], + "packageToFile": null + }, + { + "layerHash": "sha256:0b20d28b5eb3007f70c43cdd8efcdb04016aa193192e5911cda5b7590ffaa635", + "parentLayerHash": "sha256:f7ec5a41d630a33a2d1db59b95d89d93de7ae5a619a3a8571b78457e48266eba", + "vulnerabilities": [], + "packageToFile": null + }, + { + "layerHash": "sha256:1576642c97761adf346890bf67c43473217160a9a203ef47d0bc6020af652798", + "parentLayerHash": "sha256:0b20d28b5eb3007f70c43cdd8efcdb04016aa193192e5911cda5b7590ffaa635", + "vulnerabilities": [], + "packageToFile": null + }, + { + "layerHash": "sha256:c12a848bad84d57e3f5faafab5880484434aee3bf8bdde4d519753b7c81254fd", + "parentLayerHash": "sha256:1576642c97761adf346890bf67c43473217160a9a203ef47d0bc6020af652798", + "vulnerabilities": [], + "packageToFile": null + }, + { + "layerHash": "sha256:03f221d9cf00a7077231c6dcac3c95182727c7e7fd44fd2b2e882a01dcda2d70", + "parentLayerHash": "sha256:c12a848bad84d57e3f5faafab5880484434aee3bf8bdde4d519753b7c81254fd", + "vulnerabilities": [], + "packageToFile": null + } + ], + "listOfDangerousArtifcats": [ + "bin/dash", + "bin/bash", + "usr/bin/curl" + ] +} +` diff --git a/containerscan/rawdatastrucutres.go b/containerscan/rawdatastrucutres.go new file mode 100644 index 00000000..fd72ed71 --- /dev/null +++ b/containerscan/rawdatastrucutres.go @@ -0,0 +1,93 @@ +package containerscan + +import ( + "fmt" + "hash/fnv" +) + +//!!!!!!!!!!!!EVERY CHANGE IN THESE STRUCTURES => CHANGE gojayunmarshaller ASWELL!!!!!!!!!!!!!!!!!!!!!!!! + +// ScanResultReport - the report given from scanner to event receiver +type ScanResultReport struct { + CustomerGUID string `json:"customerGUID"` + ImgTag string `json:"imageTag"` + ImgHash string `json:"imageHash"` + WLID string `json:"wlid"` + ContainerName string `json:"containerName"` + Timestamp int64 `json:"timestamp"` + Layers LayersList `json:"layers"` + ListOfDangerousArtifcats []string `json:"listOfDangerousArtifcats"` +} + +// ScanResultLayer - represents a single layer from container scan result +type ScanResultLayer struct { + LayerHash string `json:"layerHash"` + ParentLayerHash string `json:"parentLayerHash"` + Vulnerabilities VulnerabilitiesList `json:"vulnerabilities"` + Packages LinuxPkgs `json:"packageToFile"` +} + +type VulnerabilityCategory struct { + IsRCE bool `json:"isRce"` +} + +// Vulnerability - a vul object +type Vulnerability struct { + Name string `json:"name"` + ImgHash string `json:"imageHash"` + ImgTag string `json:"imageTag"` + RelatedPackageName string `json:"packageName"` + PackageVersion string `json:"packageVersion"` + Link string `json:"link"` + Description string `json:"description"` + Severity string `json:"severity"` + Metadata interface{} `json:"metadata"` + Fixes VulFixes `json:"fixedIn"` + Relevancy string `json:"relevant"` // use the related enum + UrgentCount int `json:"urgent"` + NeglectedCount int `json:"neglected"` + HealthStatus string `json:"healthStatus"` + Categories VulnerabilityCategory `json:"categories"` +} + +// FixedIn when and which pkg was fixed (which version as well) +type FixedIn struct { + Name string `json:"name"` + ImgTag string `json:"imageTag"` + Version string `json:"version"` +} + +// LinuxPackage- Linux package representation +type LinuxPackage struct { + PackageName string `json:"packageName"` + Files PkgFiles `json:"files"` + PackageVersion string `json:"version"` +} + +// PackageFile - s.e +type PackageFile struct { + Filename string `json:"name"` +} + +// types to provide unmarshalling: + +//VulnerabilitiesList -s.e +type LayersList []ScanResultLayer + +//VulnerabilitiesList -s.e +type VulnerabilitiesList []Vulnerability + +//LinuxPkgs - slice of linux pkgs +type LinuxPkgs []LinuxPackage + +//VulFixes - information bout when/how this vul was fixed +type VulFixes []FixedIn + +//PkgFiles - slice of files belong to specific pkg +type PkgFiles []PackageFile + +func (v *ScanResultReport) AsFNVHash() string { + hasher := fnv.New64a() + hasher.Write([]byte(fmt.Sprintf("%v", *v))) + return fmt.Sprintf("%v", hasher.Sum64()) +} diff --git a/docs/proposals/container-image-vulnerability-adaptor.md b/docs/proposals/container-image-vulnerability-adaptor.md index 3cd1141e..f9ef0741 100644 --- a/docs/proposals/container-image-vulnerability-adaptor.md +++ b/docs/proposals/container-image-vulnerability-adaptor.md @@ -89,7 +89,7 @@ type ContainerImageScanStatus struct { LastScanDate time.Time } -type ContainerImageVulnerability struct { +type ContainerImageVulnerabilityReport struct { ImageID ContainerImageIdentifier // TBD } @@ -110,7 +110,7 @@ type IContainerImageVulnerabilityAdaptor interface { GetImagesScanStatus(imageIDs []ContainerImageIdentifier) ([]ContainerImageScanStatus, error) - GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerability, error) + GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerabilityReport, error) GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error) } diff --git a/go.mod b/go.mod index 63ab33b1..d226c90b 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,14 @@ go 1.17 require ( github.com/armosec/armoapi-go v0.0.41 github.com/armosec/k8s-interface v0.0.56 - github.com/armosec/opa-utils v0.0.99 + github.com/armosec/opa-utils v0.0.105 github.com/armosec/rbac-utils v0.0.12 github.com/armosec/utils-go v0.0.3 + github.com/armosec/utils-k8s-go v0.0.1 github.com/briandowns/spinner v1.18.0 github.com/enescakir/emoji v1.0.0 github.com/fatih/color v1.13.0 + github.com/francoispqt/gojay v1.2.13 github.com/gofrs/uuid v4.1.0+incompatible github.com/golang/glog v1.0.0 github.com/mattn/go-isatty v0.0.14 @@ -19,7 +21,6 @@ require ( github.com/satori/go.uuid v1.2.0 github.com/spf13/cobra v1.2.1 github.com/stretchr/testify v1.7.0 - go.uber.org/zap v1.19.1 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.22.2 k8s.io/apimachinery v0.22.2 @@ -37,7 +38,6 @@ require ( github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/armosec/armo-interfaces v0.0.3 // indirect - github.com/armosec/utils-k8s-go v0.0.1 // indirect github.com/aws/aws-sdk-go v1.41.11 // indirect github.com/coreos/go-oidc v2.2.1+incompatible // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -45,7 +45,6 @@ require ( github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect - github.com/francoispqt/gojay v1.2.13 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-gota/gota v0.12.0 // indirect github.com/go-logr/logr v0.4.0 // indirect @@ -78,6 +77,7 @@ require ( go.opencensus.io v0.23.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.19.1 // indirect golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect golang.org/x/net v0.0.0-20210825183410-e898025ed96a // indirect golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 // indirect diff --git a/go.sum b/go.sum index c2c78393..0a6a0e4f 100644 --- a/go.sum +++ b/go.sum @@ -95,8 +95,8 @@ github.com/armosec/k8s-interface v0.0.50/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2 github.com/armosec/k8s-interface v0.0.56 h1:7dOgc3qZaI7ReLRZcJa2JZKk0rliyYi05l1vuHc6gcE= github.com/armosec/k8s-interface v0.0.56/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.99 h1:ZuoIPg6vbgO4J09xJZDO/yIRD59odwmK2Bm55uTvkU8= -github.com/armosec/opa-utils v0.0.99/go.mod h1:BNTjeianyXlflJMz3bZM0GimBWqmzirUf1whWR6Os04= +github.com/armosec/opa-utils v0.0.105 h1:HQdP2LpFJtBoJpsfygh4IDXLLukIkQEuETOUePF/Zas= +github.com/armosec/opa-utils v0.0.105/go.mod h1:BNTjeianyXlflJMz3bZM0GimBWqmzirUf1whWR6Os04= github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40= github.com/armosec/rbac-utils v0.0.12 h1:uJpMGDyLAX129PrKHp6NPNB6lVRhE0OZIwV6ywzSDrs= github.com/armosec/rbac-utils v0.0.12/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0= diff --git a/install.sh b/install.sh index ec1d55c0..452335f7 100755 --- a/install.sh +++ b/install.sh @@ -6,6 +6,7 @@ echo BASE_DIR=~/.kubescape KUBESCAPE_EXEC=kubescape +KUBESCAPE_ZIP=kubescape.zip osName=$(uname -s) if [[ $osName == *"MINGW"* ]]; then diff --git a/policyhandler/handlenotification.go b/policyhandler/handlenotification.go index be7cf0ab..3d05857a 100644 --- a/policyhandler/handlenotification.go +++ b/policyhandler/handlenotification.go @@ -50,7 +50,7 @@ func (policyHandler *PolicyHandler) HandleNotificationRequest(notification *repo func (policyHandler *PolicyHandler) getResources(notification *reporthandling.PolicyNotification, opaSessionObj *cautils.OPASessionObj, scanInfo *cautils.ScanInfo) error { - opaSessionObj.PostureReport.ClusterAPIServerInfo = policyHandler.resourceHandler.GetClusterAPIServerInfo() + opaSessionObj.Report.ClusterAPIServerInfo = policyHandler.resourceHandler.GetClusterAPIServerInfo() resourcesMap, allResources, err := policyHandler.resourceHandler.GetResources(opaSessionObj.Frameworks, ¬ification.Designators) if err != nil { return err diff --git a/policyhandler/handlepullpolicies.go b/policyhandler/handlepullpolicies.go index 51b911c8..0d9b2725 100644 --- a/policyhandler/handlepullpolicies.go +++ b/policyhandler/handlepullpolicies.go @@ -16,7 +16,7 @@ func (policyHandler *PolicyHandler) getPolicies(notification *reporthandling.Pol return err } if len(frameworks) == 0 { - return fmt.Errorf("failed to download policies: '%s'. Make sure the policy exist and you spelled it correctly. For more information, please feel free to contact ARMO team", strings.Join(policyIdentifierToSlice(notification.Rules), ",")) + return fmt.Errorf("failed to download policies: '%s'. Make sure the policy exist and you spelled it correctly. For more information, please feel free to contact ARMO team", strings.Join(policyIdentifierToSlice(notification.Rules), ", ")) } policiesAndResources.Frameworks = frameworks diff --git a/registryadaptors/README.md b/registryadaptors/README.md new file mode 100644 index 00000000..ebb90565 --- /dev/null +++ b/registryadaptors/README.md @@ -0,0 +1,38 @@ +# Integrate With Vulnerability Server + +There are some controls that check the relation between the kubernetes manifest and vulnerabilities. +For these controls to work properly, it is necasery to +## Supported Servers +* Armosec + +# Integrate With Armosec Server + +1. Navigate to the [armosec.io](https://portal.armo.cloud/) +2. Click Profile(top right icon)->"User Management"->"API Tokens" and Generate a token +3. Copy the clientID and accessKey and run: +``` +kubescape config set clientID <> +``` +``` +kubescape config set accessKey <> +``` +4. Confirm the keys are set +``` +kubescape config view +``` +Expecting: +``` +{ + "accountID": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "clientID": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "accessKey": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" +} +``` +> If you are missing the `accountID` field, set it by running `kubescape config set accountID <>` + +For CICD, set environments variables as following: +``` +KS_ACCOUNT_ID // account id +KS_CLIENT_ID // client id +KS_ACCESS_KEY // access key +``` \ No newline at end of file diff --git a/registryadaptors/armosec/v1/civarmoadaptor.go b/registryadaptors/armosec/v1/civarmoadaptor.go new file mode 100644 index 00000000..134d53f0 --- /dev/null +++ b/registryadaptors/armosec/v1/civarmoadaptor.go @@ -0,0 +1,157 @@ +package v1 + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "github.com/armosec/kubescape/containerscan" + "github.com/armosec/kubescape/registryadaptors/registryvulnerabilities" +) + +func NewArmoAdaptor(registry string, credentials map[string]string) (*ArmoCivAdaptor, error) { + var accountID string + var accessKey string + var clientID string + var ok bool + if accountID, ok = credentials["accountID"]; !ok { + return nil, fmt.Errorf("define accountID in credentials") + } + if clientID, ok = credentials["clientID"]; !ok { + return nil, fmt.Errorf("define clientID in credentials") + } + if accessKey, ok = credentials["accessKey"]; !ok { + return nil, fmt.Errorf("define accessKey in credentials") + } + armoCivAdaptor := ArmoCivAdaptor{registry: registry, clientID: clientID, accountID: accountID, accessKey: accessKey} + err := armoCivAdaptor.initializeUrls() + if err != nil { + return nil, err + } + return &armoCivAdaptor, nil +} + +func (armoCivAdaptor *ArmoCivAdaptor) Login() error { + feLoginData := FeLoginData{ClientId: armoCivAdaptor.clientID, Secret: armoCivAdaptor.accessKey} + body, _ := json.Marshal(feLoginData) + + authApiTokenEndpoint := fmt.Sprintf("%s/frontegg/identity/resources/auth/v1/api-token", armoCivAdaptor.armoUrls.AuthUrl) + resp, err := http.Post(authApiTokenEndpoint, "application/json", bytes.NewBuffer(body)) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("error authenticating: %d", resp.StatusCode) + } + + responseBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + var feLoginResponse FeLoginResponse + + if err = json.Unmarshal(responseBody, &feLoginResponse); err != nil { + return err + } + armoCivAdaptor.feToken = feLoginResponse + + /* Now we have JWT */ + + armoCivAdaptor.authCookie, err = armoCivAdaptor.getAuthCookie() + if err != nil { + return err + } + + return nil +} +func (armoCivAdaptor *ArmoCivAdaptor) GetImagesVulnerabilities(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageVulnerabilityReport, error) { + resultList := make([]registryvulnerabilities.ContainerImageVulnerabilityReport, 0) + for _, imageID := range imageIDs { + result, err := armoCivAdaptor.GetImageVulnerability(&imageID) + if err == nil { + resultList = append(resultList, *result) + } + } + return resultList, nil +} + +func (armoCivAdaptor *ArmoCivAdaptor) GetImageVulnerability(imageID *registryvulnerabilities.ContainerImageIdentifier) (*registryvulnerabilities.ContainerImageVulnerabilityReport, error) { + // First + containerScanId, err := armoCivAdaptor.getImageLastScanId(imageID) + if err != nil { + return nil, err + } + if containerScanId == "" { + return nil, fmt.Errorf("last scan ID is empty") + } + + filter := []map[string]string{{"containersScanID": containerScanId}} + pageSize := 300 + pageNumber := 1 + request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"} + requestBody, _ := json.Marshal(request) + requestUrl := fmt.Sprintf("%s/api/v1/vulnerability/scanResultsDetails?customerGUID=%s", armoCivAdaptor.armoUrls.BackendUrl, armoCivAdaptor.accountID) + client := &http.Client{} + httpRequest, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(requestBody)) + if err != nil { + return nil, err + } + httpRequest.Header.Set("Content-Type", "application/json") + httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token)) + httpRequest.Header.Set("Cookie", fmt.Sprintf("auth=%s", armoCivAdaptor.authCookie)) + resp, err := client.Do(httpRequest) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("error requests %s with %d", requestUrl, resp.StatusCode) + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + scanDetailsResult := struct { + Total struct { + Value int `json:"value"` + Relation string `json:"relation"` + } `json:"total"` + Response containerscan.VulnerabilitiesList `json:"response"` + Cursor string `json:"cursor"` + }{} + err = json.Unmarshal(body, &scanDetailsResult) + if err != nil { + return nil, err + } + + vulnerabilities := responseObjectToVulnerabilities(scanDetailsResult.Response) + + resultImageVulnerabilityReport := registryvulnerabilities.ContainerImageVulnerabilityReport{ + ImageID: *imageID, + Vulnerabilities: vulnerabilities, + } + + return &resultImageVulnerabilityReport, nil +} + +func (armoCivAdaptor *ArmoCivAdaptor) DescribeAdaptor() string { + // TODO + return "" +} + +func (armoCivAdaptor *ArmoCivAdaptor) GetImagesInformation(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageInformation, error) { + // TODO + return []registryvulnerabilities.ContainerImageInformation{}, nil +} + +func (armoCivAdaptor *ArmoCivAdaptor) GetImagesScanStatus(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageScanStatus, error) { + // TODO + return []registryvulnerabilities.ContainerImageScanStatus{}, nil +} diff --git a/registryadaptors/armosec/v1/civarmoadaptor_test.go b/registryadaptors/armosec/v1/civarmoadaptor_test.go new file mode 100644 index 00000000..fb13dd6f --- /dev/null +++ b/registryadaptors/armosec/v1/civarmoadaptor_test.go @@ -0,0 +1,23 @@ +package v1 + +import ( + "testing" + + "github.com/armosec/kubescape/registryadaptors/registryvulnerabilities" + "github.com/stretchr/testify/assert" +) + +func TestSum(t *testing.T) { + var err error + var adaptor registryvulnerabilities.IContainerImageVulnerabilityAdaptor + + adaptor, err = NewArmoAdaptorMock() + assert.NoError(t, err) + + assert.NoError(t, adaptor.Login()) + + imageVulnerabilityReport, err := adaptor.GetImageVulnerability(®istryvulnerabilities.ContainerImageIdentifier{Tag: "gke.gcr.io/gcp-compute-persistent-disk-csi-driver:v1.3.4-gke.0"}) + assert.NoError(t, err) + + assert.Equal(t, 25, len(imageVulnerabilityReport.Vulnerabilities)) +} diff --git a/registryadaptors/armosec/v1/civarmoadaptormock.go b/registryadaptors/armosec/v1/civarmoadaptormock.go new file mode 100644 index 00000000..dac0726d --- /dev/null +++ b/registryadaptors/armosec/v1/civarmoadaptormock.go @@ -0,0 +1,67 @@ +package v1 + +import ( + "encoding/json" + + "github.com/armosec/kubescape/containerscan" + "github.com/armosec/kubescape/registryadaptors/registryvulnerabilities" +) + +type ArmoCivAdaptorMock struct { + resultList *registryvulnerabilities.ContainerImageVulnerabilityReport +} + +func NewArmoAdaptorMock() (*ArmoCivAdaptorMock, error) { + scanDetailsResult := struct { + Total struct { + Value int `json:"value"` + Relation string `json:"relation"` + } `json:"total"` + Response containerscan.VulnerabilitiesList `json:"response"` + Cursor string `json:"cursor"` + }{} + + if err := json.Unmarshal([]byte(minikubeReponseMock), &scanDetailsResult); err != nil { + return nil, err + } + + vulnerabilities := responseObjectToVulnerabilities(scanDetailsResult.Response) + + resultImageVulnerabilityReport := registryvulnerabilities.ContainerImageVulnerabilityReport{ + ImageID: registryvulnerabilities.ContainerImageIdentifier{Tag: vulnerabilities[0].Name}, + Vulnerabilities: vulnerabilities, + } + return &ArmoCivAdaptorMock{resultList: &resultImageVulnerabilityReport}, nil +} + +func (armoCivAdaptorMock *ArmoCivAdaptorMock) Login() error { + return nil +} +func (armoCivAdaptorMock *ArmoCivAdaptorMock) GetImagesVulnerabilities(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageVulnerabilityReport, error) { + return []registryvulnerabilities.ContainerImageVulnerabilityReport{*armoCivAdaptorMock.resultList}, nil +} + +func (armoCivAdaptorMock *ArmoCivAdaptorMock) GetImageVulnerability(imageID *registryvulnerabilities.ContainerImageIdentifier) (*registryvulnerabilities.ContainerImageVulnerabilityReport, error) { + return armoCivAdaptorMock.resultList, nil +} + +func (armoCivAdaptorMock *ArmoCivAdaptorMock) DescribeAdaptor() string { + // TODO + return "" +} + +func (armoCivAdaptorMock *ArmoCivAdaptorMock) GetImagesInformation(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageInformation, error) { + // TODO + return []registryvulnerabilities.ContainerImageInformation{}, nil +} + +func (armoCivAdaptorMock *ArmoCivAdaptorMock) GetImagesScanStatus(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageScanStatus, error) { + // TODO + return []registryvulnerabilities.ContainerImageScanStatus{}, nil +} + +//============================================================================================================================== +//============================================================================================================================== +//============================================================================================================================== + +var minikubeReponseMock = `{"total":{"value":73,"relation":"eq"},"response":[{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2005-2541","https://security-tracker.debian.org/tracker/CVE-2005-2541"],"name":"CVE-2005-2541","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"tar","packageVersion":"1.30+dfsg-6","link":"https://nvd.nist.gov/vuln/detail/CVE-2005-2541","description":"Tar 1.15.1 does not properly warn the user when extracting setuid or setgid files, which may allow local users or remote attackers to gain privileges.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2007-5686","https://security-tracker.debian.org/tracker/CVE-2007-5686"],"name":"CVE-2007-5686","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"login","packageVersion":"1:4.5-1.1","link":"https://nvd.nist.gov/vuln/detail/CVE-2007-5686","description":"initscripts in rPath Linux 1 sets insecure permissions for the /var/log/btmp file, which allows local users to obtain sensitive information regarding authentication attempts. NOTE: because sshd detects the insecure permissions and does not log certain events, this also prevents sshd from logging failed authentication attempts by remote attackers.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2007-6755","https://security-tracker.debian.org/tracker/CVE-2007-6755"],"name":"CVE-2007-6755","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libssl1.1","packageVersion":"1.1.1d-0+deb10u6","link":"https://nvd.nist.gov/vuln/detail/CVE-2007-6755","description":"The NIST SP 800-90A default statement of the Dual Elliptic Curve Deterministic Random Bit Generation (Dual_EC_DRBG) algorithm contains point Q constants with a possible relationship to certain \"skeleton key\" values, which might allow context-dependent attackers to defeat cryptographic protection mechanisms by leveraging knowledge of those values. NOTE: this is a preliminary CVE for Dual_EC_DRBG; future research may provide additional details about point Q and associated attacks, and could potentially lead to a RECAST or REJECT of this CVE.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2010-0928","https://security-tracker.debian.org/tracker/CVE-2010-0928"],"name":"CVE-2010-0928","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libssl1.1","packageVersion":"1.1.1d-0+deb10u6","link":"https://nvd.nist.gov/vuln/detail/CVE-2010-0928","description":"OpenSSL 0.9.8i on the Gaisler Research LEON3 SoC on the Xilinx Virtex-II Pro FPGA uses a Fixed Width Exponentiation (FWE) algorithm for certain signature calculations, and does not verify the signature before providing it to a caller, which makes it easier for physically proximate attackers to determine the private key via a modified supply voltage for the microprocessor, related to a \"fault-based attack.\"","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2010-4756","https://security-tracker.debian.org/tracker/CVE-2010-4756"],"name":"CVE-2010-4756","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libc-bin","packageVersion":"2.28-10","link":"https://nvd.nist.gov/vuln/detail/CVE-2010-4756","description":"The glob implementation in the GNU C Library (aka glibc or libc6) allows remote authenticated users to cause a denial of service (CPU and memory consumption) via crafted glob expressions that do not match any pathnames, as demonstrated by glob expressions in STAT commands to an FTP daemon, a different vulnerability than CVE-2010-2632.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2011-3374","https://security-tracker.debian.org/tracker/CVE-2011-3374"],"name":"CVE-2011-3374","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"apt","packageVersion":"1.8.2.3","link":"https://nvd.nist.gov/vuln/detail/CVE-2011-3374","description":"It was found that apt-key in apt, all versions, do not correctly validate gpg keys with the master keyring, leading to a potential man-in-the-middle attack.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2011-3389","https://security-tracker.debian.org/tracker/CVE-2011-3389"],"name":"CVE-2011-3389","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libgnutls30","packageVersion":"3.6.7-4+deb10u7","link":"https://nvd.nist.gov/vuln/detail/CVE-2011-3389","description":"The SSL protocol, as used in certain configurations in Microsoft Windows and Microsoft Internet Explorer, Mozilla Firefox, Google Chrome, Opera, and other products, encrypts data by using CBC mode with chained initialization vectors, which allows man-in-the-middle attackers to obtain plaintext HTTP headers via a blockwise chosen-boundary attack (BCBA) on an HTTPS session, in conjunction with JavaScript code that uses (1) the HTML5 WebSocket API, (2) the Java URLConnection API, or (3) the Silverlight WebClient API, aka a \"BEAST\" attack.","severity":"Medium","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2011-4116","https://security-tracker.debian.org/tracker/CVE-2011-4116"],"name":"CVE-2011-4116","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"perl-base","packageVersion":"5.28.1-6+deb10u1","link":"https://nvd.nist.gov/vuln/detail/CVE-2011-4116","description":"_is_safe in the File::Temp module for Perl does not properly handle symlinks.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2012-2663","https://security-tracker.debian.org/tracker/CVE-2012-2663"],"name":"CVE-2012-2663","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"iptables","packageVersion":"1.8.5-3~bpo10+1","link":"https://nvd.nist.gov/vuln/detail/CVE-2012-2663","description":"extensions/libxt_tcp.c in iptables through 1.4.21 does not match TCP SYN+FIN packets in --syn rules, which might allow remote attackers to bypass intended firewall restrictions via crafted packets. NOTE: the CVE-2012-6638 fix makes this issue less relevant.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2013-4235","https://security-tracker.debian.org/tracker/CVE-2013-4235"],"name":"CVE-2013-4235","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"login","packageVersion":"1:4.5-1.1","link":"https://nvd.nist.gov/vuln/detail/CVE-2013-4235","description":"shadow: TOCTOU (time-of-check time-of-use) race condition when copying and removing directory trees","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2013-4392","https://security-tracker.debian.org/tracker/CVE-2013-4392"],"name":"CVE-2013-4392","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libsystemd0","packageVersion":"241-7~deb10u7","link":"https://nvd.nist.gov/vuln/detail/CVE-2013-4392","description":"systemd, when updating file permissions, allows local users to change the permissions and SELinux security contexts for arbitrary files via a symlink attack on unspecified files.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2016-10228","https://security-tracker.debian.org/tracker/CVE-2016-10228"],"name":"CVE-2016-10228","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libc-bin","packageVersion":"2.28-10","link":"https://nvd.nist.gov/vuln/detail/CVE-2016-10228","description":"The iconv program in the GNU C Library (aka glibc or libc6) 2.31 and earlier, when invoked with multiple suffixes in the destination encoding (TRANSLATE or IGNORE) along with the -c option, enters an infinite loop when processing invalid multi-byte input sequences, leading to a denial of service.","severity":"Low","metadata":null,"fixedIn":[{"name":"wont-fix","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2016-2781","https://security-tracker.debian.org/tracker/CVE-2016-2781"],"name":"CVE-2016-2781","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"coreutils","packageVersion":"8.30-3","link":"https://nvd.nist.gov/vuln/detail/CVE-2016-2781","description":"chroot in GNU coreutils, when used with --userspec, allows local users to escape to the parent session via a crafted TIOCSTI ioctl call, which pushes characters to the terminal's input buffer.","severity":"Low","metadata":null,"fixedIn":[{"name":"wont-fix","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2017-11164","https://security-tracker.debian.org/tracker/CVE-2017-11164"],"name":"CVE-2017-11164","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libpcre3","packageVersion":"2:8.39-12","link":"https://nvd.nist.gov/vuln/detail/CVE-2017-11164","description":"In PCRE 8.41, the OP_KETRMAX feature in the match function in pcre_exec.c allows stack exhaustion (uncontrolled recursion) when processing a crafted regular expression.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2017-16231","https://security-tracker.debian.org/tracker/CVE-2017-16231"],"name":"CVE-2017-16231","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libpcre3","packageVersion":"2:8.39-12","link":"https://nvd.nist.gov/vuln/detail/CVE-2017-16231","description":"** DISPUTED ** In PCRE 8.41, after compiling, a pcretest load test PoC produces a crash overflow in the function match() in pcre_exec.c because of a self-recursive call. NOTE: third parties dispute the relevance of this report, noting that there are options that can be used to limit the amount of stack that is used.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2017-18018","https://security-tracker.debian.org/tracker/CVE-2017-18018"],"name":"CVE-2017-18018","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"coreutils","packageVersion":"8.30-3","link":"https://nvd.nist.gov/vuln/detail/CVE-2017-18018","description":"In GNU Coreutils through 8.29, chown-core.c in chown and chgrp does not prevent replacement of a plain file with a symlink during use of the POSIX \"-R -L\" options, which allows local users to modify the ownership of arbitrary files by leveraging a race condition.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2017-7245","https://security-tracker.debian.org/tracker/CVE-2017-7245"],"name":"CVE-2017-7245","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libpcre3","packageVersion":"2:8.39-12","link":"https://nvd.nist.gov/vuln/detail/CVE-2017-7245","description":"Stack-based buffer overflow in the pcre32_copy_substring function in pcre_get.c in libpcre1 in PCRE 8.40 allows remote attackers to cause a denial of service (WRITE of size 4) or possibly have unspecified other impact via a crafted file.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2017-7246","https://security-tracker.debian.org/tracker/CVE-2017-7246"],"name":"CVE-2017-7246","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libpcre3","packageVersion":"2:8.39-12","link":"https://nvd.nist.gov/vuln/detail/CVE-2017-7246","description":"Stack-based buffer overflow in the pcre32_copy_substring function in pcre_get.c in libpcre1 in PCRE 8.40 allows remote attackers to cause a denial of service (WRITE of size 268) or possibly have unspecified other impact via a crafted file.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2018-1000654","https://security-tracker.debian.org/tracker/CVE-2018-1000654"],"name":"CVE-2018-1000654","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libtasn1-6","packageVersion":"4.13-3","link":"https://nvd.nist.gov/vuln/detail/CVE-2018-1000654","description":"GNU Libtasn1-4.13 libtasn1-4.13 version libtasn1-4.13, libtasn1-4.12 contains a DoS, specifically CPU usage will reach 100% when running asn1Paser against the POC due to an issue in _asn1_expand_object_id(p_tree), after a long time, the program will be killed. This attack appears to be exploitable via parsing a crafted file.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2018-12886","https://security-tracker.debian.org/tracker/CVE-2018-12886"],"name":"CVE-2018-12886","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"gcc-8-base","packageVersion":"8.3.0-6","link":"https://nvd.nist.gov/vuln/detail/CVE-2018-12886","description":"stack_protect_prologue in cfgexpand.c and stack_protect_epilogue in function.c in GNU Compiler Collection (GCC) 4.1 through 8 (under certain circumstances) generate instruction sequences when targeting ARM targets that spill the address of the stack protector guard, which allows an attacker to bypass the protection of -fstack-protector, -fstack-protector-all, -fstack-protector-strong, and -fstack-protector-explicit against stack overflow by controlling what the stack canary is compared against.","severity":"High","metadata":null,"fixedIn":[{"name":"wont-fix","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2018-20796","https://security-tracker.debian.org/tracker/CVE-2018-20796"],"name":"CVE-2018-20796","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libc-bin","packageVersion":"2.28-10","link":"https://nvd.nist.gov/vuln/detail/CVE-2018-20796","description":"In the GNU C Library (aka glibc or libc6) through 2.29, check_dst_limits_calc_pos_1 in posix/regexec.c has Uncontrolled Recursion, as demonstrated by '(\\227|)(\\\\1\\\\1|t1|\\\\\\2537)+' in grep.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2018-6829","https://security-tracker.debian.org/tracker/CVE-2018-6829"],"name":"CVE-2018-6829","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libgcrypt20","packageVersion":"1.8.4-5+deb10u1","link":"https://nvd.nist.gov/vuln/detail/CVE-2018-6829","description":"cipher/elgamal.c in Libgcrypt through 1.8.2, when used to encrypt messages directly, improperly encodes plaintexts, which allows attackers to obtain sensitive information by reading ciphertext data (i.e., it does not have semantic security in face of a ciphertext-only attack). The Decisional Diffie-Hellman (DDH) assumption does not hold for Libgcrypt's ElGamal implementation.","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2018-7169","https://security-tracker.debian.org/tracker/CVE-2018-7169"],"name":"CVE-2018-7169","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"login","packageVersion":"1:4.5-1.1","link":"https://nvd.nist.gov/vuln/detail/CVE-2018-7169","description":"An issue was discovered in shadow 4.5. newgidmap (in shadow-utils) is setuid and allows an unprivileged user to be placed in a user namespace where setgroups(2) is permitted. This allows an attacker to remove themselves from a supplementary group, which may allow access to certain filesystem paths if the administrator has used \"group blacklisting\" (e.g., chmod g-rwx) to restrict access to paths. This flaw effectively reverts a security feature in the kernel (in particular, the /proc/self/setgroups knob) to prevent this sort of privilege escalation.","severity":"Low","metadata":null,"fixedIn":[{"name":"wont-fix","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2019-1010022","https://security-tracker.debian.org/tracker/CVE-2019-1010022"],"name":"CVE-2019-1010022","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libc-bin","packageVersion":"2.28-10","link":"https://nvd.nist.gov/vuln/detail/CVE-2019-1010022","description":"** DISPUTED ** GNU Libc current is affected by: Mitigation bypass. The impact is: Attacker may bypass stack guard protection. The component is: nptl. The attack vector is: Exploit stack buffer overflow vulnerability and use this bypass vulnerability to bypass stack guard. NOTE: Upstream comments indicate \"this is being treated as a non-security bug and no real threat.\"","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}},{"designators":{"designatorType":"Attributes","attributes":{"cluster":"minikube","containerName":"kube-proxy","customerGUID":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","kind":"daemonset","name":"kube-proxy","namespace":"kube-system"}},"context":[{"attribute":"customerGUID","value":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","source":"designators.attributes"},{"attribute":"cluster","value":"minikube","source":"designators.attributes"},{"attribute":"namespace","value":"kube-system","source":"designators.attributes"},{"attribute":"kind","value":"daemonset","source":"designators.attributes"},{"attribute":"name","value":"kube-proxy","source":"designators.attributes"},{"attribute":"containerName","value":"kube-proxy","source":"designators.attributes"}],"wlid":"wlid://cluster-minikube/namespace-kube-system/daemonset-kube-proxy","containersScanID":"XXXXXXXXXXXXXXXXXX","layers":[{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""},{"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","parentLayerHash":""}],"timestamp":1643522422,"isFixed":0,"layerHash":"sha256:48b90c7688a2c85d7081a437ecba5cb706fbaa98b09def0b206dbbe39e3af558","links":["https://nvd.nist.gov/vuln/detail/CVE-2019-1010023","https://security-tracker.debian.org/tracker/CVE-2019-1010023"],"name":"CVE-2019-1010023","imageHash":"sha256:132b7fc61d18b3ec4a35348f6c915b429f4757d92196e2ea8cd937aea3db41df","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","packageName":"libc-bin","packageVersion":"2.28-10","link":"https://nvd.nist.gov/vuln/detail/CVE-2019-1010023","description":"** DISPUTED ** GNU Libc current is affected by: Re-mapping current loaded library with malicious ELF file. The impact is: In worst case attacker may evaluate privileges. The component is: libld. The attack vector is: Attacker sends 2 ELF files to victim and asks to run ldd on it. ldd execute code. NOTE: Upstream comments indicate \"this is being treated as a non-security bug and no real threat.\"","severity":"Negligible","metadata":null,"fixedIn":[{"name":"not-fixed","imageTag":"k8s.gcr.io/kube-proxy@sha256:561d6cb95c32333db13ea847396167e903d97cf6e08dd937906c3dd0108580b7","version":""}],"relevant":"No signature profile to compare","urgent":0,"neglected":0,"healthStatus":"","categories":{"isRce":false}}],"cursor":""}` diff --git a/registryadaptors/armosec/v1/civarmoadaptorutils.go b/registryadaptors/armosec/v1/civarmoadaptorutils.go new file mode 100644 index 00000000..b019ed5b --- /dev/null +++ b/registryadaptors/armosec/v1/civarmoadaptorutils.go @@ -0,0 +1,149 @@ +package v1 + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strings" + + "github.com/armosec/kubescape/containerscan" + "github.com/armosec/kubescape/registryadaptors/registryvulnerabilities" +) + +func (armoCivAdaptor *ArmoCivAdaptor) initializeUrls() error { + configUrl := fmt.Sprintf("https://%s/assets/configs/config.json", armoCivAdaptor.registry) + resp, err := http.Get(configUrl) + if err != nil { + return err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("cannot retrieve backend config file %s: status %d", configUrl, resp.StatusCode) + } + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + err = json.Unmarshal(body, &armoCivAdaptor.armoUrls) + if err != nil { + return err + } + return nil + +} + +func (armoCivAdaptor *ArmoCivAdaptor) getAuthCookie() (string, error) { + selectCustomer := ArmoSelectCustomer{SelectedCustomerGuid: armoCivAdaptor.accountID} + requestBody, _ := json.Marshal(selectCustomer) + requestUrl := fmt.Sprintf("%s/api/v1/openid_customers", armoCivAdaptor.armoUrls.BackendUrl) + client := &http.Client{} + httpRequest, err := http.NewRequest(http.MethodPost, requestUrl, bytes.NewBuffer(requestBody)) + if err != nil { + return "", err + } + httpRequest.Header.Set("Content-Type", "application/json") + httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token)) + httpResponse, err := client.Do(httpRequest) + if err != nil { + return "", err + } + defer httpResponse.Body.Close() + if httpResponse.StatusCode != http.StatusOK { + return "", fmt.Errorf("error getting cookie at %s: status %d", requestUrl, httpResponse.StatusCode) + } + + cookies := httpResponse.Header.Get("set-cookie") + if len(cookies) == 0 { + return "", fmt.Errorf("no cookie field in response from %s", requestUrl) + } + + authCookie := "" + for _, cookie := range strings.Split(cookies, ";") { + kv := strings.Split(cookie, "=") + if kv[0] == "auth" { + authCookie = kv[1] + } + } + + if len(authCookie) == 0 { + return "", fmt.Errorf("no auth cookie field in response from %s", requestUrl) + } + + return authCookie, nil +} + +func (armoCivAdaptor *ArmoCivAdaptor) getImageLastScanId(imageID *registryvulnerabilities.ContainerImageIdentifier) (string, error) { + filter := []map[string]string{{"imageTag": imageID.Tag, "status": "Success"}} + pageSize := 1 + pageNumber := 1 + request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"} + requestBody, _ := json.Marshal(request) + requestUrl := fmt.Sprintf("%s/api/v1/vulnerability/scanResultsSumSummary?customerGUID=%s", armoCivAdaptor.armoUrls.BackendUrl, armoCivAdaptor.accountID) + client := &http.Client{} + httpRequest, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(requestBody)) + if err != nil { + return "", err + } + + httpRequest.Header.Set("Content-Type", "application/json") + httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token)) + httpRequest.Header.Set("Cookie", fmt.Sprintf("auth=%s", armoCivAdaptor.authCookie)) + resp, err := client.Do(httpRequest) + if err != nil { + return "", err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("error requests %s with %d", requestUrl, resp.StatusCode) + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + + scanSummartResult := struct { + Total struct { + Value int `json:"value"` + Relation string `json:"relation"` + } `json:"total"` + Response []containerscan.ElasticContainerScanSummaryResult `json:"response"` + Cursor string `json:"cursor"` + }{} + err = json.Unmarshal(body, &scanSummartResult) + if err != nil { + return "", err + } + + if len(scanSummartResult.Response) < pageSize { + return "", fmt.Errorf("did not get response for image %s", imageID.Tag) + } + + return scanSummartResult.Response[0].ContainerScanID, nil +} + +func responseObjectToVulnerabilities(vulnerabilitiesList containerscan.VulnerabilitiesList) []registryvulnerabilities.Vulnerability { + vulnerabilities := make([]registryvulnerabilities.Vulnerability, len(vulnerabilitiesList)) + for i, vulnerabilityEntry := range vulnerabilitiesList { + vulnerabilities[i].Description = vulnerabilityEntry.Description + vulnerabilities[i].Fixes = make([]registryvulnerabilities.FixedIn, len(vulnerabilityEntry.Fixes)) + for j, fix := range vulnerabilityEntry.Fixes { + vulnerabilities[i].Fixes[j].ImgTag = fix.ImgTag + vulnerabilities[i].Fixes[j].Name = fix.Name + vulnerabilities[i].Fixes[j].Version = fix.Version + } + vulnerabilities[i].HealthStatus = vulnerabilityEntry.HealthStatus + vulnerabilities[i].Link = vulnerabilityEntry.Link + vulnerabilities[i].Metadata = vulnerabilityEntry.Metadata + vulnerabilities[i].Name = vulnerabilityEntry.Name + vulnerabilities[i].PackageVersion = vulnerabilityEntry.PackageVersion + vulnerabilities[i].RelatedPackageName = vulnerabilityEntry.RelatedPackageName + vulnerabilities[i].Relevancy = vulnerabilityEntry.Relevancy + vulnerabilities[i].Severity = vulnerabilityEntry.Severity + vulnerabilities[i].UrgentCount = vulnerabilityEntry.UrgentCount + } + return vulnerabilities +} diff --git a/registryadaptors/armosec/v1/datastructures.go b/registryadaptors/armosec/v1/datastructures.go new file mode 100644 index 00000000..79ab8f5c --- /dev/null +++ b/registryadaptors/armosec/v1/datastructures.go @@ -0,0 +1,58 @@ +package v1 + +import "time" + +type V2ListRequest struct { + // properties of the requested next page + // Use ValidatePageProperties to set PageSize field + PageSize *int `json:"pageSize,omitempty"` + // One can leave it empty for 0, then call ValidatePageProperties + PageNum *int `json:"pageNum,omitempty"` + // The time window of the list to return. Default: since - begining og the time, until - now. + Since *time.Time `json:"since,omitempty"` + Until *time.Time `json:"until,omitempty"` + // Which elements of the list to return, each field can hold multiple values separated by comma + // Example: ": {"severity": "High,Medium", "type": "61539,30303"} + // An empty map means "return the complete list" + InnerFilters []map[string]string `json:"innerFilters,omitempty"` + // How to order (sort) the list, field name + sort order (asc/desc), like https://www.w3schools.com/sql/sql_orderby.asp + // Example: "timestamp:asc,severity:desc" + OrderBy string `json:"orderBy,omitempty"` + // Cursor to the next page of former requset. Not supported yet + // Cursor cannot be used with another parameters of this struct + Cursor string `json:"cursor,omitempty"` + // FieldsList allow us to return only subset of the source document fields + // Don't expose FieldsList outside without well designed decision + FieldsList []string `json:"includeFields,omitempty"` + FieldsReverseKeywordMap map[string]string `json:"-,omitempty"` +} + +type FeLoginData struct { + Secret string `json:"secret"` + ClientId string `json:"clientId"` +} + +type FeLoginResponse struct { + Token string `json:"accessToken"` + RefreshToken string `json:"refreshToken"` + ExpiresIn int32 `json:"expiresIn"` + Expires string `json:"expires"` +} + +type ArmoBeConfiguration struct { + BackendUrl string `json:"backend"` + AuthUrl string `json:"authUrl"` +} +type ArmoSelectCustomer struct { + SelectedCustomerGuid string `json:"selectedCustomer"` +} + +type ArmoCivAdaptor struct { + registry string + accountID string + clientID string + accessKey string + feToken FeLoginResponse + armoUrls ArmoBeConfiguration + authCookie string +} diff --git a/registryadaptors/contribute.md b/registryadaptors/contribute.md new file mode 100644 index 00000000..abf0688e --- /dev/null +++ b/registryadaptors/contribute.md @@ -0,0 +1,164 @@ +# Container image vulnerability adaptor interface + +## 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 { + 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 ContainerImageVulnerabilityReport 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) ([]ContainerImageVulnerabilityReport, error) + + GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error) +} +``` + + + +# Integration + +# Input + +The objects received from the interface will be converted to an IMetadata compatible objects as following + +``` +{ + "apiVersion": "image.vulnscan.com/v1", + "kind": "ImageVulnerabilities", + "metadata": { + "name": "nginx:latest" + }, + "data": { + // list of vulnerabilities + } +} +``` + + +# Output + +The rego results will be a combination of the k8s artifact and the list of relevant CVEs for the control + +``` +{ + "apiVersion": "result.vulnscan.com/v1", + "kind": "Pod", + "metadata": { + "name": "nginx" + "namespace": "default" + + }, + "relatedObjects": [ + { + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "name": "nginx" + "namespace": "default" + }, + "spec": { + // podSpec + }, + }, + { + "apiVersion": "image.vulnscan.com/v1", + "kind": "ImageVulnerabilities", + "metadata": { + "name": "nginx:latest", + }, + "data": { + // list of vulnerabilities + } + } + ] +} +``` \ No newline at end of file diff --git a/registryadaptors/registryvulnerabilities/datastructures.go b/registryadaptors/registryvulnerabilities/datastructures.go new file mode 100644 index 00000000..429786b6 --- /dev/null +++ b/registryadaptors/registryvulnerabilities/datastructures.go @@ -0,0 +1,50 @@ +package registryvulnerabilities + +import ( + "time" +) + +type ContainerImageIdentifier struct { + Registry string + Repository string + Tag string + Hash string +} + +type ContainerImageScanStatus struct { + ImageID ContainerImageIdentifier + IsScanAvailable bool + IsBomAvailable bool + LastScanDate time.Time +} + +type FixedIn struct { + Name string `json:"name"` + ImgTag string `json:"imageTag"` + Version string `json:"version"` +} +type Vulnerability struct { + Name string `json:"name"` + RelatedPackageName string `json:"packageName"` + PackageVersion string `json:"packageVersion"` + Link string `json:"link"` + Description string `json:"description"` + Severity string `json:"severity"` + Metadata interface{} `json:"metadata"` + Fixes []FixedIn `json:"fixedIn"` + Relevancy string `json:"relevant"` // use the related enum + UrgentCount int `json:"urgent"` + NeglectedCount int `json:"neglected"` + HealthStatus string `json:"healthStatus"` +} + +type ContainerImageVulnerabilityReport struct { + ImageID ContainerImageIdentifier + Vulnerabilities []Vulnerability +} + +type ContainerImageInformation struct { + ImageID ContainerImageIdentifier + Bom []string + //ImageManifest Manifest // will use here Docker package definition +} diff --git a/registryadaptors/registryvulnerabilities/interfaces.go b/registryadaptors/registryvulnerabilities/interfaces.go new file mode 100644 index 00000000..cbef91e5 --- /dev/null +++ b/registryadaptors/registryvulnerabilities/interfaces.go @@ -0,0 +1,17 @@ +package registryvulnerabilities + +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() error + + // For "help" purposes + DescribeAdaptor() string + + GetImagesScanStatus(imageIDs []ContainerImageIdentifier) ([]ContainerImageScanStatus, error) + + GetImagesVulnerabilities(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerabilityReport, error) + GetImageVulnerability(imageID *ContainerImageIdentifier) (*ContainerImageVulnerabilityReport, error) + + GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error) +} diff --git a/resourcehandler/filesloader.go b/resourcehandler/filesloader.go index c4052fe2..86fe4434 100644 --- a/resourcehandler/filesloader.go +++ b/resourcehandler/filesloader.go @@ -34,13 +34,15 @@ const ( // FileResourceHandler handle resources from files and URLs type FileResourceHandler struct { - inputPatterns []string + inputPatterns []string + registryAdaptors *RegistryAdaptors } -func NewFileResourceHandler(inputPatterns []string) *FileResourceHandler { +func NewFileResourceHandler(inputPatterns []string, registryAdaptors *RegistryAdaptors) *FileResourceHandler { k8sinterface.InitializeMapResourcesMock() // initialize the resource map return &FileResourceHandler{ - inputPatterns: inputPatterns, + inputPatterns: inputPatterns, + registryAdaptors: registryAdaptors, } } @@ -90,6 +92,10 @@ func (fileHandler *FileResourceHandler) GetResources(frameworks []reporthandling } } + if err := fileHandler.registryAdaptors.collectImagesVulnerabilities(k8sResources, allResources); err != nil { + cautils.WarningDisplay(os.Stderr, "Warning: failed to collect images vulnerabilities: %s\n", err.Error()) + } + return k8sResources, allResources, nil } diff --git a/resourcehandler/k8sresources.go b/resourcehandler/k8sresources.go index 6a21f1fd..7198613c 100644 --- a/resourcehandler/k8sresources.go +++ b/resourcehandler/k8sresources.go @@ -30,14 +30,16 @@ type K8sResourceHandler struct { hostSensorHandler hostsensorutils.IHostSensor fieldSelector IFieldSelector rbacObjectsAPI *cautils.RBACObjects + registryAdaptors *RegistryAdaptors } -func NewK8sResourceHandler(k8s *k8sinterface.KubernetesApi, fieldSelector IFieldSelector, hostSensorHandler hostsensorutils.IHostSensor, rbacObjects *cautils.RBACObjects) *K8sResourceHandler { +func NewK8sResourceHandler(k8s *k8sinterface.KubernetesApi, fieldSelector IFieldSelector, hostSensorHandler hostsensorutils.IHostSensor, rbacObjects *cautils.RBACObjects, registryAdaptors *RegistryAdaptors) *K8sResourceHandler { return &K8sResourceHandler{ k8s: k8s, fieldSelector: fieldSelector, hostSensorHandler: hostSensorHandler, rbacObjectsAPI: rbacObjects, + registryAdaptors: registryAdaptors, } } @@ -60,6 +62,11 @@ func (k8sHandler *K8sResourceHandler) GetResources(frameworks []reporthandling.F if err := k8sHandler.pullResources(k8sResourcesMap, allResources, namespace, labels); err != nil { return k8sResourcesMap, allResources, err } + + if err := k8sHandler.registryAdaptors.collectImagesVulnerabilities(k8sResourcesMap, allResources); err != nil { + cautils.WarningDisplay(os.Stderr, "Warning: failed to collect image vulnerabilities: %s\n", err.Error()) + } + if err := k8sHandler.collectHostResources(allResources, k8sResourcesMap); err != nil { cautils.WarningDisplay(os.Stderr, "Warning: failed to collect host sensor resources\n") } diff --git a/resourcehandler/registrydata.go b/resourcehandler/registrydata.go new file mode 100644 index 00000000..7805f1a9 --- /dev/null +++ b/resourcehandler/registrydata.go @@ -0,0 +1,155 @@ +package resourcehandler + +import ( + "github.com/armosec/k8s-interface/k8sinterface" + "github.com/armosec/k8s-interface/workloadinterface" + "github.com/armosec/kubescape/cautils" + "github.com/armosec/kubescape/cautils/getter" + armosecadaptorv1 "github.com/armosec/kubescape/registryadaptors/armosec/v1" + "github.com/armosec/kubescape/registryadaptors/registryvulnerabilities" + "github.com/armosec/opa-utils/shared" +) + +const ( + ImagevulnerabilitiesObjectGroup = "image.vulnscan.com" + ImagevulnerabilitiesObjectVersion = "v1" + ImagevulnerabilitiesObjectKind = "ImageVulnerabilities" +) + +type RegistryAdaptors struct { + adaptors []registryvulnerabilities.IContainerImageVulnerabilityAdaptor +} + +func NewRegistryAdaptors() (*RegistryAdaptors, error) { + // list supported adaptors + registryAdaptors := &RegistryAdaptors{} + adaptors, err := listAdaptores() + if err != nil { + return registryAdaptors, err + } + registryAdaptors.adaptors = adaptors + return registryAdaptors, nil +} + +func (registryAdaptors *RegistryAdaptors) collectImagesVulnerabilities(k8sResourcesMap *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata) error { + + // list cluster images + images := listImagesTags(k8sResourcesMap, allResources) + imagesIdentifiers := imageTagsToContainerImageIdentifier(images) + + imagesVulnerability := map[string][]registryvulnerabilities.Vulnerability{} + for i := range registryAdaptors.adaptors { // login and and get vulnerabilities + + if err := registryAdaptors.adaptors[i].Login(); err != nil { + return err + } + vulnerabilities, err := registryAdaptors.adaptors[i].GetImagesVulnerabilities(imagesIdentifiers) + if err != nil { + return err + } + for j := range vulnerabilities { + imagesVulnerability[vulnerabilities[j].ImageID.Tag] = vulnerabilities[j].Vulnerabilities + } + } + + // convert result to IMetadata object + metaObjs := vulnerabilitiesToIMetadata(imagesVulnerability) + + // save in resources map + for i := range metaObjs { + allResources[metaObjs[i].GetID()] = metaObjs[i] + } + (*k8sResourcesMap)[k8sinterface.JoinResourceTriplets(ImagevulnerabilitiesObjectGroup, ImagevulnerabilitiesObjectVersion, ImagevulnerabilitiesObjectKind)] = workloadinterface.ListMetaIDs(metaObjs) + + return nil +} + +func vulnerabilitiesToIMetadata(vulnerabilities map[string][]registryvulnerabilities.Vulnerability) []workloadinterface.IMetadata { + objs := []workloadinterface.IMetadata{} + for i := range vulnerabilities { + objs = append(objs, vulnerabilityToIMetadata(i, vulnerabilities[i])) + } + return objs +} + +func vulnerabilityToIMetadata(imageTag string, vulnerabilities []registryvulnerabilities.Vulnerability) workloadinterface.IMetadata { + obj := map[string]interface{}{} + metadata := map[string]interface{}{} + metadata["name"] = imageTag // store image tag as object name + obj["kind"] = ImagevulnerabilitiesObjectKind + obj["apiVersion"] = k8sinterface.JoinGroupVersion(ImagevulnerabilitiesObjectGroup, ImagevulnerabilitiesObjectVersion) + obj["data"] = vulnerabilities + obj["metadata"] = metadata + + return workloadinterface.NewWorkloadObj(obj) +} + +// list all images tags +func listImagesTags(k8sResourcesMap *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata) []string { + images := []string{} + for _, resources := range *k8sResourcesMap { + for j := range resources { + if resource, ok := allResources[resources[j]]; ok { + if resource.GetObjectType() == workloadinterface.TypeWorkloadObject { + workload := workloadinterface.NewWorkloadObj(resource.GetObject()) + if contianers, err := workload.GetContainers(); err == nil { + for i := range contianers { + images = append(images, contianers[i].Image) + } + } + if contianers, err := workload.GetInitContainers(); err == nil { + for i := range contianers { + images = append(images, contianers[i].Image) + } + } + } + } + } + } + + return shared.SliceStringToUnique(images) +} + +func imageTagsToContainerImageIdentifier(images []string) []registryvulnerabilities.ContainerImageIdentifier { + imagesIdentifiers := make([]registryvulnerabilities.ContainerImageIdentifier, len(images)) + for i := range images { + imageIdentifier := registryvulnerabilities.ContainerImageIdentifier{ + Tag: images[i], + } + // splitted := strings.Split(images[i], "/") + // if len(splitted) == 1 { + // imageIdentifier.Tag = splitted[0] + // } else if len(splitted) == 2 { + // imageIdentifier.Registry = splitted[0] + // imageIdentifier.Tag = splitted[1] + // } else if len(splitted) >= 3 { + // imageIdentifier.Registry = splitted[0] + // imageIdentifier.Repository = strings.Join(splitted[1:len(splitted)-1], "/") + // imageIdentifier.Tag = splitted[len(splitted)-1] + // } + imagesIdentifiers[i] = imageIdentifier + } + return imagesIdentifiers +} +func listAdaptores() ([]registryvulnerabilities.IContainerImageVulnerabilityAdaptor, error) { + + adaptors := []registryvulnerabilities.IContainerImageVulnerabilityAdaptor{} + + armoAPI := getter.GetArmoAPIConnector() + if armoAPI == nil { + accountID := armoAPI.GetAccountID() + clientID := armoAPI.GetClientID() + accessKey := armoAPI.GetAccessKey() + if accountID != "" && clientID != "" && accessKey != "" { + armosecAdaptor, err := armosecadaptorv1.NewArmoAdaptor(armoAPI.GetFrontendURL(), map[string]string{"accountID": accountID, "clientID": clientID, "accessKey": accessKey}) + if err != nil { + return nil, err + } + adaptors = append(adaptors, armosecAdaptor) + } else { + // TODO - print warning + } + } + + return adaptors, nil +} diff --git a/resultshandling/reporter/v1/reporteventreceiver.go b/resultshandling/reporter/v1/reporteventreceiver.go index 853700ef..88d1087b 100644 --- a/resultshandling/reporter/v1/reporteventreceiver.go +++ b/resultshandling/reporter/v1/reporteventreceiver.go @@ -31,7 +31,7 @@ func NewReportEventReceiver(tenantConfig *cautils.ConfigObj) *ReportEventReceive return &ReportEventReceiver{ httpClient: &http.Client{}, clusterName: tenantConfig.ClusterName, - customerGUID: tenantConfig.CustomerGUID, + customerGUID: tenantConfig.AccountID, token: tenantConfig.Token, customerAdminEMail: tenantConfig.CustomerAdminEMail, } diff --git a/resultshandling/reporter/v2/reporteventreceiver.go b/resultshandling/reporter/v2/reporteventreceiver.go index 03664b97..3784eab4 100644 --- a/resultshandling/reporter/v2/reporteventreceiver.go +++ b/resultshandling/reporter/v2/reporteventreceiver.go @@ -32,7 +32,7 @@ func NewReportEventReceiver(tenantConfig *cautils.ConfigObj) *ReportEventReceive return &ReportEventReceiver{ httpClient: &http.Client{}, clusterName: tenantConfig.ClusterName, - customerGUID: tenantConfig.CustomerGUID, + customerGUID: tenantConfig.AccountID, token: tenantConfig.Token, customerAdminEMail: tenantConfig.CustomerAdminEMail, }