Support unified configuration (#1304)

* support scanning scope

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* update go mod

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* update white list

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* update go mod

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* scope empty return control should tested

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* update rego scope for system test

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* update test + mock

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* add comment

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* update rego library

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* update k8s-interface

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* update opa utils - lots of file changes in this commit since armoapi-go bump up in opa-utils

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* move to temp k8s-interface - till PR in k8s-interface repo will approved

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* update k8s-interface with released tag

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* update go mod in httphandler

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* support unified configuration

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* unitest adjustment

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* config-unified

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* CR corrections

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* remove system test till it will be merged

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* add relevant system test

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* remove delete test

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

* return config delete system test

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>

---------

Signed-off-by: rcohencyberarmor <rcohen@armosec.io>
Co-authored-by: rcohencyberarmor <rcohen@armosec.io>
This commit is contained in:
rcohencyberarmor
2023-08-15 10:34:23 +03:00
committed by GitHub
parent 3648ef286d
commit 4e48148d40
4 changed files with 190 additions and 20 deletions

View File

@@ -62,7 +62,7 @@ on:
default: 1
BINARY_TESTS:
type: string
default: '[ "scan_nsa", "scan_mitre", "scan_with_exceptions", "scan_repository", "scan_local_file", "scan_local_glob_files", "scan_local_list_of_files", "scan_nsa_and_submit_to_backend", "scan_mitre_and_submit_to_backend", "scan_local_repository_and_submit_to_backend", "scan_repository_from_url_and_submit_to_backend", "scan_with_exception_to_backend", "scan_with_custom_framework", "scan_customer_configuration", "host_scanner", "scan_compliance_score", "scan_custom_framework_scanning_file_scope_testing", "scan_custom_framework_scanning_cluster_scope_testing", "scan_custom_framework_scanning_cluster_and_file_scope_testing" ]'
default: '[ "scan_nsa", "scan_mitre", "scan_with_exceptions", "scan_repository", "scan_local_file", "scan_local_glob_files", "scan_local_list_of_files", "scan_nsa_and_submit_to_backend", "scan_mitre_and_submit_to_backend", "scan_local_repository_and_submit_to_backend", "scan_repository_from_url_and_submit_to_backend", "scan_with_exception_to_backend", "scan_with_custom_framework", "scan_customer_configuration", "host_scanner", "scan_compliance_score", "scan_custom_framework_scanning_file_scope_testing", "scan_custom_framework_scanning_cluster_scope_testing", "scan_custom_framework_scanning_cluster_and_file_scope_testing", "unified_configuration_config_view", "unified_configuration_config_set", "unified_configuration_config_delete" ]'
OS_MATRIX:
type: string
required: false

View File

@@ -17,7 +17,11 @@ import (
corev1 "k8s.io/api/core/v1"
)
const configFileName = "config"
const (
configFileName string = "config"
kubescapeNamespace string = "kubescape"
kubescapeConfigMapName string = "kubescape-config"
)
func ConfigFileFullPath() string { return getter.GetDefaultPath(configFileName + ".json") }
@@ -29,7 +33,6 @@ type ConfigObj struct {
AccountID string `json:"accountID,omitempty"`
ClientID string `json:"clientID,omitempty"`
SecretKey string `json:"secretKey,omitempty"`
CustomerGUID string `json:"customerGUID,omitempty"` // Deprecated
Token string `json:"invitationParam,omitempty"`
CustomerAdminEMail string `json:"adminMail,omitempty"`
ClusterName string `json:"clusterName,omitempty"`
@@ -63,6 +66,35 @@ func (co *ConfigObj) Config() []byte {
return []byte{}
}
func (co *ConfigObj) updateEmptyFields(inCO *ConfigObj) error {
if inCO.AccountID != "" {
co.AccountID = inCO.AccountID
}
if inCO.CloudAPIURL != "" {
co.CloudAPIURL = inCO.CloudAPIURL
}
if inCO.CloudAuthURL != "" {
co.CloudAuthURL = inCO.CloudAuthURL
}
if inCO.CloudReportURL != "" {
co.CloudReportURL = inCO.CloudReportURL
}
if inCO.CloudUIURL != "" {
co.CloudUIURL = inCO.CloudUIURL
}
if inCO.ClusterName != "" {
co.ClusterName = inCO.ClusterName
}
if inCO.CustomerAdminEMail != "" {
co.CustomerAdminEMail = inCO.CustomerAdminEMail
}
if inCO.Token != "" {
co.Token = inCO.Token
}
return nil
}
// ======================================================================================
// =============================== interface ============================================
// ======================================================================================
@@ -245,15 +277,16 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken
configMapNamespace: GetConfigMapNamespace(),
}
// first, load from configMap
if c.existsConfigMap() {
c.loadConfigFromConfigMap()
}
// second, load from file
// first, load from file
if existsConfigFile() { // get from file
loadConfigFromFile(c.configObj)
}
// second, load from configMap
if c.existsConfigMap() {
c.updateConfigEmptyFieldsFromConfigMap()
}
updateCredentials(c.configObj, credentials)
updateCloudURLs(c.configObj)
@@ -359,6 +392,22 @@ func (c *ClusterConfig) ToMapString() map[string]interface{} {
}
return m
}
func (c *ClusterConfig) updateConfigEmptyFieldsFromConfigMap() error {
configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Get(context.Background(), c.configMapName, metav1.GetOptions{})
if err != nil {
return err
}
tempCO := ConfigObj{}
if jsonConf, ok := configMap.Data["config.json"]; ok {
json.Unmarshal([]byte(jsonConf), &tempCO)
return c.configObj.updateEmptyFields(&tempCO)
}
return err
}
func (c *ClusterConfig) loadConfigFromConfigMap() error {
configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Get(context.Background(), c.configMapName, metav1.GetOptions{})
if err != nil {
@@ -509,10 +558,6 @@ func readConfig(dat []byte, configObj *ConfigObj) error {
if err := json.Unmarshal(dat, configObj); err != nil {
return err
}
if configObj.AccountID == "" {
configObj.AccountID = configObj.CustomerGUID
}
configObj.CustomerGUID = ""
return nil
}
@@ -554,7 +599,7 @@ func getConfigMapName() string {
if n := os.Getenv("KS_DEFAULT_CONFIGMAP_NAME"); n != "" {
return n
}
return "kubescape"
return kubescapeConfigMapName
}
// GetConfigMapNamespace returns the namespace of the cluster config, which is the same for all in-cluster components
@@ -562,7 +607,7 @@ func GetConfigMapNamespace() string {
if n := os.Getenv("KS_DEFAULT_CONFIGMAP_NAMESPACE"); n != "" {
return n
}
return "default"
return kubescapeNamespace
}
func getAccountFromEnv(credentials *Credentials) {

View File

@@ -308,12 +308,12 @@ func TestGetConfigMapNamespace(t *testing.T) {
}{
{
name: "no env",
want: "default",
want: kubescapeNamespace,
},
{
name: "default ns",
env: "kubescape",
want: "kubescape",
env: kubescapeNamespace,
want: kubescapeNamespace,
},
{
name: "custom ns",
@@ -330,3 +330,128 @@ func TestGetConfigMapNamespace(t *testing.T) {
})
}
}
const (
anyString string = "anyString"
shouldNotUpdate string = "shouldNotUpdate"
shouldUpdate string = "shouldUpdate"
)
func checkIsUpdateCorrectly(t *testing.T, beforeField string, afterField string) {
switch beforeField {
case anyString:
assert.Equal(t, anyString, afterField)
case "":
assert.Equal(t, shouldUpdate, afterField)
}
}
func TestUpdateEmptyFields(t *testing.T) {
tests := []struct {
inCo *ConfigObj
outCo *ConfigObj
}{
{
outCo: &ConfigObj{
AccountID: "",
Token: "",
CustomerAdminEMail: "",
ClusterName: "",
CloudReportURL: "",
CloudAPIURL: "",
CloudUIURL: "",
CloudAuthURL: "",
},
inCo: &ConfigObj{
AccountID: shouldUpdate,
Token: shouldUpdate,
CustomerAdminEMail: shouldUpdate,
ClusterName: shouldUpdate,
CloudReportURL: shouldUpdate,
CloudAPIURL: shouldUpdate,
CloudUIURL: shouldUpdate,
CloudAuthURL: shouldUpdate,
},
},
{
outCo: &ConfigObj{
AccountID: anyString,
Token: anyString,
CustomerAdminEMail: "",
ClusterName: "",
CloudReportURL: "",
CloudAPIURL: "",
CloudUIURL: "",
CloudAuthURL: "",
},
inCo: &ConfigObj{
AccountID: shouldNotUpdate,
Token: shouldNotUpdate,
CustomerAdminEMail: shouldUpdate,
ClusterName: shouldUpdate,
CloudReportURL: shouldUpdate,
CloudAPIURL: shouldUpdate,
CloudUIURL: shouldUpdate,
CloudAuthURL: shouldUpdate,
},
},
{
outCo: &ConfigObj{
AccountID: "",
Token: "",
CustomerAdminEMail: anyString,
ClusterName: anyString,
CloudReportURL: anyString,
CloudAPIURL: anyString,
CloudUIURL: anyString,
CloudAuthURL: anyString,
},
inCo: &ConfigObj{
AccountID: shouldUpdate,
Token: shouldUpdate,
CustomerAdminEMail: shouldNotUpdate,
ClusterName: shouldNotUpdate,
CloudReportURL: shouldNotUpdate,
CloudAPIURL: shouldNotUpdate,
CloudUIURL: shouldNotUpdate,
CloudAuthURL: shouldNotUpdate,
},
},
{
outCo: &ConfigObj{
AccountID: anyString,
Token: anyString,
CustomerAdminEMail: "",
ClusterName: anyString,
CloudReportURL: "",
CloudAPIURL: anyString,
CloudUIURL: "",
CloudAuthURL: anyString,
},
inCo: &ConfigObj{
AccountID: shouldNotUpdate,
Token: shouldNotUpdate,
CustomerAdminEMail: shouldUpdate,
ClusterName: shouldNotUpdate,
CloudReportURL: shouldUpdate,
CloudAPIURL: shouldNotUpdate,
CloudUIURL: shouldUpdate,
CloudAuthURL: shouldNotUpdate,
},
},
}
for i := range tests {
beforeChangesOutCO := tests[i].outCo
tests[i].outCo.updateEmptyFields(tests[i].inCo)
checkIsUpdateCorrectly(t, beforeChangesOutCO.AccountID, tests[i].outCo.AccountID)
checkIsUpdateCorrectly(t, beforeChangesOutCO.CloudAPIURL, tests[i].outCo.CloudAPIURL)
checkIsUpdateCorrectly(t, beforeChangesOutCO.CloudAuthURL, tests[i].outCo.CloudAuthURL)
checkIsUpdateCorrectly(t, beforeChangesOutCO.CloudReportURL, tests[i].outCo.CloudReportURL)
checkIsUpdateCorrectly(t, beforeChangesOutCO.CloudUIURL, tests[i].outCo.CloudUIURL)
checkIsUpdateCorrectly(t, beforeChangesOutCO.ClusterName, tests[i].outCo.ClusterName)
checkIsUpdateCorrectly(t, beforeChangesOutCO.CustomerAdminEMail, tests[i].outCo.CustomerAdminEMail)
checkIsUpdateCorrectly(t, beforeChangesOutCO.Token, tests[i].outCo.Token)
}
}

View File

@@ -9,7 +9,7 @@ import (
func (ks *Kubescape) SetCachedConfig(setConfig *metav1.SetConfig) error {
tenant := getTenantConfig(nil, "", "", getKubernetesApi())
tenant := getTenantConfig(nil, "", "", nil)
if setConfig.Account != "" {
tenant.GetConfigObj().AccountID = setConfig.Account
@@ -45,6 +45,6 @@ func (ks *Kubescape) ViewCachedConfig(viewConfig *metav1.ViewConfig) error {
func (ks *Kubescape) DeleteCachedConfig(ctx context.Context, deleteConfig *metav1.DeleteConfig) error {
tenant := getTenantConfig(nil, "", "", getKubernetesApi()) // change k8sinterface
tenant := getTenantConfig(nil, "", "", nil) // change k8sinterface
return tenant.DeleteCachedConfig(ctx)
}