mirror of
https://github.com/kubescape/kubescape.git
synced 2026-04-15 06:58:11 +00:00
475 lines
13 KiB
Go
475 lines
13 KiB
Go
package cautils
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/armosec/kubescape/cautils/getter"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"github.com/armosec/k8s-interface/k8sinterface"
|
|
corev1 "k8s.io/api/core/v1"
|
|
)
|
|
|
|
const (
|
|
configMapName = "kubescape"
|
|
configFileName = "config"
|
|
)
|
|
|
|
func ConfigFileFullPath() string { return getter.GetDefaultPath(configFileName + ".json") }
|
|
|
|
// ======================================================================================
|
|
// =============================== Config structure =====================================
|
|
// ======================================================================================
|
|
|
|
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{}
|
|
}
|
|
|
|
// Config - convert ConfigObj to config file
|
|
func (co *ConfigObj) Config() []byte {
|
|
clusterName := co.ClusterName
|
|
co.ClusterName = "" // remove cluster name before saving to file
|
|
b, err := json.Marshal(co)
|
|
co.ClusterName = clusterName
|
|
|
|
if err == nil {
|
|
return b
|
|
}
|
|
|
|
return []byte{}
|
|
}
|
|
|
|
// ======================================================================================
|
|
// =============================== interface ============================================
|
|
// ======================================================================================
|
|
type IClusterConfig interface {
|
|
|
|
// set
|
|
SetConfig(customerGUID string) error
|
|
|
|
// getters
|
|
GetClusterName() string
|
|
GetCustomerGUID() string
|
|
GetConfigObj() *ConfigObj
|
|
GetK8sAPI() *k8sinterface.KubernetesApi
|
|
GetBackendAPI() getter.IBackend
|
|
GetDefaultNS() string
|
|
GenerateURL()
|
|
}
|
|
|
|
// ClusterConfigSetup - Setup the desired cluster behavior regarding submittion to the Armo BE
|
|
func ClusterConfigSetup(scanInfo *ScanInfo, k8s *k8sinterface.KubernetesApi, beAPI getter.IBackend) IClusterConfig {
|
|
/*
|
|
|
|
If "First run (local config not found)" -
|
|
Default - Do not send report (local)
|
|
Local - Do not send report
|
|
Submit - Create tenant & Submit report
|
|
|
|
If "Submitted but not signed up" -
|
|
Default - Delete local config & Do not send report (local)
|
|
Local - Delete local config & Do not send report
|
|
Submit - Submit report
|
|
|
|
If "Signed up user" -
|
|
Default - Submit report (submit)
|
|
Local - Do not send report
|
|
Submit - Submit report
|
|
|
|
*/
|
|
clusterConfig := NewClusterConfig(k8s, beAPI)
|
|
clusterConfig.LoadConfig()
|
|
|
|
if !IsSubmitted(clusterConfig) {
|
|
if scanInfo.Submit {
|
|
return clusterConfig // submit - Create tenant & Submit report
|
|
}
|
|
return NewEmptyConfig() // local/default - Do not send report
|
|
}
|
|
if !IsRegistered(clusterConfig) {
|
|
if scanInfo.Submit {
|
|
return clusterConfig // submit/default - Submit report
|
|
}
|
|
DeleteConfig(k8s)
|
|
return NewEmptyConfig() // local - Delete local config & Do not send report
|
|
}
|
|
if scanInfo.Local {
|
|
scanInfo.Submit = false
|
|
return NewEmptyConfig() // local - Do not send report
|
|
}
|
|
scanInfo.Submit = true
|
|
return clusterConfig // submit/default - Submit report
|
|
}
|
|
|
|
// ======================================================================================
|
|
// ============================= Mock Config ============================================
|
|
// ======================================================================================
|
|
type EmptyConfig struct {
|
|
}
|
|
|
|
func NewEmptyConfig() *EmptyConfig { return &EmptyConfig{} }
|
|
func (c *EmptyConfig) SetConfig(customerGUID string) error { return nil }
|
|
func (c *EmptyConfig) GetConfigObj() *ConfigObj { return &ConfigObj{} }
|
|
func (c *EmptyConfig) GetCustomerGUID() string { return "" }
|
|
func (c *EmptyConfig) GetK8sAPI() *k8sinterface.KubernetesApi { return nil } // TODO: return mock obj
|
|
func (c *EmptyConfig) GetDefaultNS() string { return k8sinterface.GetDefaultNamespace() }
|
|
func (c *EmptyConfig) GetBackendAPI() getter.IBackend { return nil } // TODO: return mock obj
|
|
func (c *EmptyConfig) GetClusterName() string { return k8sinterface.GetClusterName() }
|
|
func (c *EmptyConfig) GenerateURL() {
|
|
message := fmt.Sprintf("\nCheckout for more cool features: https://%s\n", getter.GetArmoAPIConnector().GetFrontendURL())
|
|
InfoTextDisplay(os.Stdout, fmt.Sprintf("\n%s\n", message))
|
|
}
|
|
|
|
// ======================================================================================
|
|
// ========================== Cluster Config ============================================
|
|
// ======================================================================================
|
|
|
|
type ClusterConfig struct {
|
|
k8s *k8sinterface.KubernetesApi
|
|
defaultNS string
|
|
backendAPI getter.IBackend
|
|
configObj *ConfigObj
|
|
}
|
|
|
|
func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBackend) *ClusterConfig {
|
|
return &ClusterConfig{
|
|
k8s: k8s,
|
|
backendAPI: backendAPI,
|
|
configObj: &ConfigObj{},
|
|
defaultNS: k8sinterface.GetDefaultNamespace(),
|
|
}
|
|
}
|
|
func (c *ClusterConfig) GetConfigObj() *ConfigObj { return c.configObj }
|
|
func (c *ClusterConfig) GetK8sAPI() *k8sinterface.KubernetesApi { return c.k8s }
|
|
func (c *ClusterConfig) GetDefaultNS() string { return c.defaultNS }
|
|
func (c *ClusterConfig) GetBackendAPI() getter.IBackend { return c.backendAPI }
|
|
|
|
func (c *ClusterConfig) GenerateURL() {
|
|
message := "Checkout for more cool features: "
|
|
|
|
u := url.URL{}
|
|
u.Scheme = "https"
|
|
u.Host = getter.GetArmoAPIConnector().GetFrontendURL()
|
|
if c.configObj == nil {
|
|
return
|
|
}
|
|
if c.configObj.CustomerAdminEMail != "" {
|
|
InfoTextDisplay(os.Stdout, "\n\n"+message+u.String()+"\n\n")
|
|
return
|
|
}
|
|
u.Path = "account/sign-up"
|
|
q := u.Query()
|
|
q.Add("invitationToken", c.configObj.Token)
|
|
q.Add("customerGUID", c.configObj.CustomerGUID)
|
|
|
|
u.RawQuery = q.Encode()
|
|
InfoTextDisplay(os.Stdout, "\n\n"+message+u.String()+"\n\n")
|
|
}
|
|
|
|
func (c *ClusterConfig) GetCustomerGUID() string {
|
|
if c.configObj != nil {
|
|
return c.configObj.CustomerGUID
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (c *ClusterConfig) SetConfig(customerGUID string) error {
|
|
if c.configObj == nil {
|
|
c.configObj = &ConfigObj{}
|
|
}
|
|
|
|
// cluster name
|
|
if c.GetClusterName() == "" {
|
|
c.setClusterName(k8sinterface.GetClusterName())
|
|
}
|
|
|
|
// ARMO customer GUID
|
|
if customerGUID != "" && c.GetCustomerGUID() != customerGUID {
|
|
c.setCustomerGUID(customerGUID) // override config customerGUID
|
|
}
|
|
|
|
customerGUID = c.GetCustomerGUID()
|
|
|
|
// get from armoBE
|
|
tenantResponse, err := c.backendAPI.GetCustomerGUID(customerGUID)
|
|
if err == nil && tenantResponse != nil {
|
|
if tenantResponse.AdminMail != "" { // this customer already belongs to some user
|
|
c.setCustomerAdminEMail(tenantResponse.AdminMail)
|
|
} else {
|
|
c.setToken(tenantResponse.Token)
|
|
c.setCustomerGUID(tenantResponse.TenantID)
|
|
}
|
|
} else {
|
|
if err != nil && !strings.Contains(err.Error(), "already exists") {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// update/create config
|
|
if c.existsConfigMap() {
|
|
c.updateConfigMap()
|
|
} else {
|
|
c.createConfigMap()
|
|
}
|
|
c.updateConfigFile()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *ClusterConfig) setToken(token string) {
|
|
c.configObj.Token = token
|
|
}
|
|
|
|
func (c *ClusterConfig) setCustomerAdminEMail(customerAdminEMail string) {
|
|
c.configObj.CustomerAdminEMail = customerAdminEMail
|
|
}
|
|
func (c *ClusterConfig) setCustomerGUID(customerGUID string) {
|
|
c.configObj.CustomerGUID = customerGUID
|
|
}
|
|
|
|
func (c *ClusterConfig) setClusterName(clusterName string) {
|
|
c.configObj.ClusterName = clusterName
|
|
}
|
|
func (c *ClusterConfig) GetClusterName() string {
|
|
return c.configObj.ClusterName
|
|
}
|
|
func (c *ClusterConfig) LoadConfig() {
|
|
// get from configMap
|
|
if c.existsConfigMap() {
|
|
c.configObj, _ = c.loadConfigFromConfigMap()
|
|
} else if existsConfigFile() { // get from file
|
|
c.configObj, _ = loadConfigFromFile()
|
|
} else {
|
|
c.configObj = &ConfigObj{}
|
|
}
|
|
}
|
|
|
|
func (c *ClusterConfig) ToMapString() map[string]interface{} {
|
|
m := map[string]interface{}{}
|
|
if bc, err := json.Marshal(c.configObj); err == nil {
|
|
json.Unmarshal(bc, &m)
|
|
}
|
|
return m
|
|
}
|
|
func (c *ClusterConfig) loadConfigFromConfigMap() (*ConfigObj, error) {
|
|
if c.k8s == nil {
|
|
return nil, nil
|
|
}
|
|
configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Get(context.Background(), configMapName, metav1.GetOptions{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if bData, err := json.Marshal(configMap.Data); err == nil {
|
|
return readConfig(bData)
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (c *ClusterConfig) existsConfigMap() bool {
|
|
_, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Get(context.Background(), configMapName, metav1.GetOptions{})
|
|
// TODO - check if has customerGUID
|
|
return err == nil
|
|
}
|
|
|
|
func (c *ClusterConfig) GetValueByKeyFromConfigMap(key string) (string, error) {
|
|
|
|
configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Get(context.Background(), configMapName, metav1.GetOptions{})
|
|
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if val, ok := configMap.Data[key]; ok {
|
|
return val, nil
|
|
} else {
|
|
return "", fmt.Errorf("value does not exist")
|
|
}
|
|
}
|
|
|
|
func GetValueFromConfigJson(key string) (string, error) {
|
|
data, err := os.ReadFile(ConfigFileFullPath())
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
var obj map[string]interface{}
|
|
if err := json.Unmarshal(data, &obj); err != nil {
|
|
return "", err
|
|
}
|
|
if val, ok := obj[key]; ok {
|
|
return fmt.Sprint(val), nil
|
|
} else {
|
|
return "", fmt.Errorf("value does not exist")
|
|
}
|
|
|
|
}
|
|
|
|
func SetKeyValueInConfigJson(key string, value string) error {
|
|
data, err := os.ReadFile(ConfigFileFullPath())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var obj map[string]interface{}
|
|
err = json.Unmarshal(data, &obj)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
obj[key] = value
|
|
newData, err := json.Marshal(obj)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return os.WriteFile(ConfigFileFullPath(), newData, 0664)
|
|
|
|
}
|
|
|
|
func (c *ClusterConfig) SetKeyValueInConfigmap(key string, value string) error {
|
|
|
|
configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Get(context.Background(), configMapName, metav1.GetOptions{})
|
|
if err != nil {
|
|
configMap = &corev1.ConfigMap{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: configMapName,
|
|
},
|
|
}
|
|
}
|
|
|
|
if len(configMap.Data) == 0 {
|
|
configMap.Data = make(map[string]string)
|
|
}
|
|
|
|
configMap.Data[key] = value
|
|
|
|
if err != nil {
|
|
_, err = c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Create(context.Background(), configMap, metav1.CreateOptions{})
|
|
} else {
|
|
_, err = c.k8s.KubernetesClient.CoreV1().ConfigMaps(configMap.Namespace).Update(context.Background(), configMap, metav1.UpdateOptions{})
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
func existsConfigFile() bool {
|
|
_, err := os.ReadFile(ConfigFileFullPath())
|
|
return err == nil
|
|
}
|
|
|
|
func (c *ClusterConfig) createConfigMap() error {
|
|
if c.k8s == nil {
|
|
return nil
|
|
}
|
|
configMap := &corev1.ConfigMap{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: configMapName,
|
|
},
|
|
}
|
|
c.updateConfigData(configMap)
|
|
|
|
_, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Create(context.Background(), configMap, metav1.CreateOptions{})
|
|
return err
|
|
}
|
|
|
|
func (c *ClusterConfig) updateConfigMap() error {
|
|
if c.k8s == nil {
|
|
return nil
|
|
}
|
|
configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Get(context.Background(), configMapName, metav1.GetOptions{})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
c.updateConfigData(configMap)
|
|
|
|
_, err = c.k8s.KubernetesClient.CoreV1().ConfigMaps(configMap.Namespace).Update(context.Background(), configMap, metav1.UpdateOptions{})
|
|
return err
|
|
}
|
|
|
|
func (c *ClusterConfig) updateConfigFile() error {
|
|
if err := os.WriteFile(ConfigFileFullPath(), c.configObj.Config(), 0664); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *ClusterConfig) updateConfigData(configMap *corev1.ConfigMap) {
|
|
if len(configMap.Data) == 0 {
|
|
configMap.Data = make(map[string]string)
|
|
}
|
|
m := c.ToMapString()
|
|
for k, v := range m {
|
|
if s, ok := v.(string); ok {
|
|
configMap.Data[k] = s
|
|
}
|
|
}
|
|
}
|
|
func loadConfigFromFile() (*ConfigObj, error) {
|
|
dat, err := os.ReadFile(ConfigFileFullPath())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return readConfig(dat)
|
|
}
|
|
func readConfig(dat []byte) (*ConfigObj, error) {
|
|
|
|
if len(dat) == 0 {
|
|
return nil, nil
|
|
}
|
|
configObj := &ConfigObj{}
|
|
err := json.Unmarshal(dat, configObj)
|
|
|
|
return configObj, err
|
|
}
|
|
|
|
// Check if the customer is submitted
|
|
func IsSubmitted(clusterConfig *ClusterConfig) bool {
|
|
return clusterConfig.existsConfigMap() || existsConfigFile()
|
|
}
|
|
|
|
// Check if the customer is registered
|
|
func IsRegistered(clusterConfig *ClusterConfig) bool {
|
|
|
|
// get from armoBE
|
|
tenantResponse, err := clusterConfig.backendAPI.GetCustomerGUID(clusterConfig.GetCustomerGUID())
|
|
if err == nil && tenantResponse != nil {
|
|
if tenantResponse.AdminMail != "" { // this customer already belongs to some user
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func DeleteConfig(k8s *k8sinterface.KubernetesApi) error {
|
|
if err := DeleteConfigMap(k8s); err != nil {
|
|
return err
|
|
}
|
|
if err := DeleteConfigFile(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
func DeleteConfigMap(k8s *k8sinterface.KubernetesApi) error {
|
|
return k8s.KubernetesClient.CoreV1().ConfigMaps(k8sinterface.GetDefaultNamespace()).Delete(context.Background(), configMapName, metav1.DeleteOptions{})
|
|
}
|
|
|
|
func DeleteConfigFile() error {
|
|
return os.Remove(ConfigFileFullPath())
|
|
}
|