diff --git a/clihandler/initcli.go b/clihandler/initcli.go index c8c9744c..515cf525 100644 --- a/clihandler/initcli.go +++ b/clihandler/initcli.go @@ -42,7 +42,6 @@ func initHostSensor(scanInfo *cautils.ScanInfo, k8s *k8sinterface.KubernetesApi) glog.Errorf("failed to create host sensor: %v", err) return &hostsensorutils.HostSensorHandlerMock{} } - scanInfo.ExcludedNamespaces = fmt.Sprintf("%s,%s", scanInfo.ExcludedNamespaces, hostSensorHandler.DaemonSet.Namespace) return hostSensorHandler } else { fmt.Printf("Skipping nodes scanning\n") @@ -55,6 +54,7 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces { var hostSensorHandler hostsensorutils.IHostSensor var tenantConfig cautils.ITenantConfig + hostSensorHandler = &hostsensorutils.HostSensorHandlerMock{} // scanning environment scanningTarget := scanInfo.GetScanningEnvironment() switch scanningTarget { @@ -108,13 +108,33 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error { interfaces.report.SetClusterName(interfaces.tenantConfig.GetClusterName()) interfaces.report.SetCustomerGUID(interfaces.tenantConfig.GetCustomerGUID()) + if err := interfaces.hostSensorHandler.Init(); err != nil { + errMsg := "failed to init host sensor" + if scanInfo.VerboseMode { + errMsg = fmt.Sprintf("%s: %v", errMsg, err) + } + cautils.ErrorDisplay(errMsg) + } else if len(scanInfo.IncludeNamespaces) == 0 && interfaces.hostSensorHandler.GetNamespace() != "" { + scanInfo.ExcludedNamespaces = fmt.Sprintf("%s,%s", scanInfo.ExcludedNamespaces, interfaces.hostSensorHandler) + } + + defer func() { + if err := interfaces.hostSensorHandler.TearDown(); err != nil { + errMsg := "failed to tear down host sensor" + if scanInfo.VerboseMode { + errMsg = fmt.Sprintf("%s: %v", errMsg, err) + } + cautils.ErrorDisplay(errMsg) + } + }() + // set policy getter only after setting the customerGUID setPolicyGetter(scanInfo, interfaces.tenantConfig.GetCustomerGUID()) // cli handler setup go func() { // policy handler setup - policyHandler := policyhandler.NewPolicyHandler(&processNotification, interfaces.resourceHandler) + policyHandler := policyhandler.NewPolicyHandler(&processNotification, interfaces.resourceHandler, interfaces.hostSensorHandler) if err := Scan(policyHandler, scanInfo); err != nil { fmt.Println(err) diff --git a/hostsensorutils/hostsensordataenvelope.go b/hostsensorutils/hostsensordataenvelope.go index 7c68f96b..de394157 100644 --- a/hostsensorutils/hostsensordataenvelope.go +++ b/hostsensorutils/hostsensordataenvelope.go @@ -8,7 +8,7 @@ import ( ) type HostSensorDataEnvelope struct { - schema.GroupVersionKind + schema.GroupVersionResource NodeName string `json:"nodeName"` Data json.RawMessage `json:"data"` } @@ -22,7 +22,7 @@ func (hsde *HostSensorDataEnvelope) SetName(val string) { } func (hsde *HostSensorDataEnvelope) SetKind(val string) { - hsde.Kind = val + hsde.Resource = val } @@ -43,7 +43,7 @@ func (hsde *HostSensorDataEnvelope) GetName() string { } func (hsde *HostSensorDataEnvelope) GetKind() string { - return hsde.Kind + return hsde.Resource } func (hsde *HostSensorDataEnvelope) GetApiVersion() string { diff --git a/hostsensorutils/hostsensordeploy.go b/hostsensorutils/hostsensordeploy.go index 74fbc678..1913b254 100644 --- a/hostsensorutils/hostsensordeploy.go +++ b/hostsensorutils/hostsensordeploy.go @@ -8,6 +8,7 @@ import ( "time" "github.com/armosec/k8s-interface/k8sinterface" + "github.com/armosec/kubescape/cautils" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -18,22 +19,15 @@ import ( ) type HostSensorHandler struct { - HostSensorNamespace string - HostSensorPort int32 - HostSensorDaemonSetName string - HostSensorPodNames map[string]string //map from pod names to node names - IsReady <-chan bool //readonly chan - k8sObj *k8sinterface.KubernetesApi - DaemonSet *appsv1.DaemonSet - podListLock sync.RWMutex + HostSensorPort int32 + HostSensorPodNames map[string]string //map from pod names to node names + IsReady <-chan bool //readonly chan + k8sObj *k8sinterface.KubernetesApi + DaemonSet *appsv1.DaemonSet + podListLock sync.RWMutex } func NewHostSensorHandler(k8sObj *k8sinterface.KubernetesApi) (*HostSensorHandler, error) { - // deploy the YAML - // store namespace + port - // store pod names - // make sure all pods are running, after X seconds treat has running anyway, and log an error on the pods not running yet - // return the object if k8sObj == nil { return nil, fmt.Errorf("nil k8s interface received") @@ -54,6 +48,13 @@ func NewHostSensorHandler(k8sObj *k8sinterface.KubernetesApi) (*HostSensorHandle } func (hsh *HostSensorHandler) Init() error { + // deploy the YAML + // store namespace + port + // store pod names + // make sure all pods are running, after X seconds treat has running anyway, and log an error on the pods not running yet + cautils.ProgressTextDisplay("Installing host sensor") + cautils.StartSpinner() + defer cautils.StopSpinner() if err := hsh.applyYAML(); err != nil { return fmt.Errorf("in HostSensorHandler init failed to apply YAML: %v", err) } @@ -77,13 +78,14 @@ func (hsh *HostSensorHandler) applyYAML() error { if err := yaml.Unmarshal(singleYAMLBytes, namespaceAC); err != nil { return fmt.Errorf("failed to Unmarshal YAML of namespace: %v", err) } + namespaceName := "" if ns, err := hsh.k8sObj.KubernetesClient.CoreV1().Namespaces().Apply(hsh.k8sObj.Context, namespaceAC, metav1.ApplyOptions{ FieldManager: "kubescape", }); err != nil { return fmt.Errorf("failed to apply YAML of namespace: %v", err) } else { - hsh.HostSensorNamespace = ns.Name + namespaceName = ns.Name } // apply deamonset deamonAC := &appsapplyv1.DaemonSetApplyConfiguration{} @@ -96,13 +98,12 @@ func (hsh *HostSensorHandler) applyYAML() error { if err := yaml.Unmarshal(singleYAMLBytes, deamonAC); err != nil { return fmt.Errorf("failed to Unmarshal YAML of deamonset: %v", err) } - deamonAC.Namespace = &hsh.HostSensorNamespace - if ds, err := hsh.k8sObj.KubernetesClient.AppsV1().DaemonSets(hsh.HostSensorNamespace).Apply(hsh.k8sObj.Context, deamonAC, metav1.ApplyOptions{ + deamonAC.Namespace = &namespaceName + if ds, err := hsh.k8sObj.KubernetesClient.AppsV1().DaemonSets(namespaceName).Apply(hsh.k8sObj.Context, deamonAC, metav1.ApplyOptions{ FieldManager: "kubescape", }); err != nil { return fmt.Errorf("failed to apply YAML of deamonset: %v", err) } else { - hsh.HostSensorDaemonSetName = ds.Name hsh.HostSensorPort = ds.Spec.Template.Spec.Containers[0].Ports[0].ContainerPort hsh.DaemonSet = ds } @@ -171,10 +172,10 @@ func (hsh *HostSensorHandler) updatePodInListAtomic(eventType watch.EventType, p func (hsh *HostSensorHandler) TearDown() error { // remove the namespace gracePeriod := int64(15) - if err := hsh.k8sObj.KubernetesClient.AppsV1().DaemonSets(hsh.HostSensorNamespace).Delete(hsh.k8sObj.Context, hsh.HostSensorDaemonSetName, metav1.DeleteOptions{GracePeriodSeconds: &gracePeriod}); err != nil { + if err := hsh.k8sObj.KubernetesClient.AppsV1().DaemonSets(hsh.GetNamespace()).Delete(hsh.k8sObj.Context, hsh.DaemonSet.Name, metav1.DeleteOptions{GracePeriodSeconds: &gracePeriod}); err != nil { return fmt.Errorf("failed to delete host-sensor daemonset: %v", err) } - if err := hsh.k8sObj.KubernetesClient.CoreV1().Namespaces().Delete(hsh.k8sObj.Context, hsh.HostSensorNamespace, + if err := hsh.k8sObj.KubernetesClient.CoreV1().Namespaces().Delete(hsh.k8sObj.Context, hsh.GetNamespace(), metav1.DeleteOptions{GracePeriodSeconds: &gracePeriod}); err != nil { return fmt.Errorf("failed to delete host-sensor namespace: %v", err) } @@ -182,3 +183,7 @@ func (hsh *HostSensorHandler) TearDown() error { return nil } + +func (hsh *HostSensorHandler) GetNamespace() string { + return hsh.DaemonSet.Namespace +} diff --git a/hostsensorutils/hostsensorgetfrompod.go b/hostsensorutils/hostsensorgetfrompod.go index 11a36374..3fed6adc 100644 --- a/hostsensorutils/hostsensorgetfrompod.go +++ b/hostsensorutils/hostsensorgetfrompod.go @@ -5,7 +5,8 @@ import ( "fmt" "sync" - "k8s.io/apimachinery/pkg/runtime/schema" + "github.com/armosec/kubescape/cautils" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/yaml" ) @@ -93,20 +94,26 @@ func (hsh *HostSensorHandler) GetKubeletConfigurations() ([]HostSensorDataEnvelo continue } res[resIdx].Data = jsonBytes - kindDet := schema.GroupVersionKind{} + kindDet := metav1.TypeMeta{} if err = json.Unmarshal(jsonBytes, &kindDet); err != nil { fmt.Printf("In GetKubeletConfigurations failed to Unmarshal GroupVersionKind: %v;\n%v", err, jsonBytes) continue } - res[resIdx].GroupVersionKind = kindDet + res[resIdx].GroupVersionResource.Resource = kindDet.Kind + res[resIdx].GroupVersionResource.Group = kindDet.GroupVersionKind().Group + res[resIdx].GroupVersionResource.Version = kindDet.GroupVersionKind().Version } return res, err } func (hsh *HostSensorHandler) CollectResources() ([]HostSensorDataEnvelope, error) { + cautils.ProgressTextDisplay("Accessing host sensor") + cautils.StartSpinner() + defer cautils.StopSpinner() kcData, err := hsh.GetKubeletConfigurations() if err != nil { return kcData, err } + cautils.SuccessTextDisplay("Read host information from host sensor") return kcData, nil } diff --git a/hostsensorutils/hostsensorinterface.go b/hostsensorutils/hostsensorinterface.go index 33f578d8..6b9e4170 100644 --- a/hostsensorutils/hostsensorinterface.go +++ b/hostsensorutils/hostsensorinterface.go @@ -4,4 +4,5 @@ type IHostSensor interface { Init() error TearDown() error CollectResources() ([]HostSensorDataEnvelope, error) + GetNamespace() string } diff --git a/hostsensorutils/hostsensormock.go b/hostsensorutils/hostsensormock.go index 6c790410..a8610a33 100644 --- a/hostsensorutils/hostsensormock.go +++ b/hostsensorutils/hostsensormock.go @@ -14,3 +14,7 @@ func (hshm *HostSensorHandlerMock) TearDown() error { func (hshm *HostSensorHandlerMock) CollectResources() ([]HostSensorDataEnvelope, error) { return []HostSensorDataEnvelope{}, nil } + +func (hshm *HostSensorHandlerMock) GetNamespace() string { + return "" +} diff --git a/policyhandler/handlenotification.go b/policyhandler/handlenotification.go index 8db8963e..bbce422c 100644 --- a/policyhandler/handlenotification.go +++ b/policyhandler/handlenotification.go @@ -3,24 +3,29 @@ package policyhandler import ( "fmt" + "github.com/armosec/k8s-interface/k8sinterface" + "github.com/armosec/k8s-interface/workloadinterface" "github.com/armosec/kubescape/cautils" + "github.com/armosec/kubescape/hostsensorutils" "github.com/armosec/kubescape/resourcehandler" "github.com/armosec/opa-utils/reporthandling" ) // PolicyHandler - type PolicyHandler struct { - resourceHandler resourcehandler.IResourceHandler + resourceHandler resourcehandler.IResourceHandler + hostSensorHandler hostsensorutils.IHostSensor // we are listening on this chan in opaprocessor/processorhandler.go/ProcessRulesListenner func processPolicy *chan *cautils.OPASessionObj getters *cautils.Getters } // CreatePolicyHandler Create ws-handler obj -func NewPolicyHandler(processPolicy *chan *cautils.OPASessionObj, resourceHandler resourcehandler.IResourceHandler) *PolicyHandler { +func NewPolicyHandler(processPolicy *chan *cautils.OPASessionObj, resourceHandler resourcehandler.IResourceHandler, hostSensorHandler hostsensorutils.IHostSensor) *PolicyHandler { return &PolicyHandler{ - resourceHandler: resourceHandler, - processPolicy: processPolicy, + resourceHandler: resourceHandler, + processPolicy: processPolicy, + hostSensorHandler: hostSensorHandler, } } @@ -52,5 +57,25 @@ func (policyHandler *PolicyHandler) HandleNotificationRequest(notification *repo func (policyHandler *PolicyHandler) getResources(notification *reporthandling.PolicyNotification, opaSessionObj *cautils.OPASessionObj, scanInfo *cautils.ScanInfo) (*cautils.K8SResources, error) { opaSessionObj.PostureReport.ClusterAPIServerInfo = policyHandler.resourceHandler.GetClusterAPIServerInfo() - return policyHandler.resourceHandler.GetResources(opaSessionObj.Frameworks, ¬ification.Designators) + resourcesMap, err := policyHandler.resourceHandler.GetResources(opaSessionObj.Frameworks, ¬ification.Designators) + if err != nil { + return resourcesMap, err + } + hostResources, err := policyHandler.hostSensorHandler.CollectResources() + if err != nil { + return resourcesMap, err + } + for rscIdx := range hostResources { + groupResources := k8sinterface.ResourceGroupToString(hostResources[rscIdx].Group, hostResources[rscIdx].GetApiVersion(), hostResources[rscIdx].GetKind()) + for _, groupResource := range groupResources { + grpReasorceList, ok := (*resourcesMap)[groupResource] + if !ok { + grpReasorceList = make([]workloadinterface.IMetadata, 0) + } + grpReasorceList = append(grpReasorceList, &hostResources[rscIdx]) + (*resourcesMap)[groupResource] = grpReasorceList + } + } + cautils.SuccessTextDisplay("Let’s start!!!") + return resourcesMap, nil } diff --git a/resourcehandler/k8sresources.go b/resourcehandler/k8sresources.go index 7f948ab2..cc682f9b 100644 --- a/resourcehandler/k8sresources.go +++ b/resourcehandler/k8sresources.go @@ -47,7 +47,7 @@ func (k8sHandler *K8sResourceHandler) GetResources(frameworks []reporthandling.F return k8sResourcesMap, err } - cautils.SuccessTextDisplay("Accessed successfully to Kubernetes objects, let’s start!!!") + cautils.SuccessTextDisplay("Accessed successfully to Kubernetes objects") return k8sResourcesMap, nil }