package hostsensorutils import ( "encoding/json" "fmt" "sync" "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/kubescape/v2/core/cautils/logger" "github.com/armosec/kubescape/v2/core/cautils/logger/helpers" "github.com/armosec/opa-utils/objectsenvelopes/hostsensor" "github.com/armosec/opa-utils/reporthandling/apis" "sigs.k8s.io/yaml" ) func (hsh *HostSensorHandler) getPodList() (res map[string]string, err error) { hsh.podListLock.RLock() jsonBytes, err := json.Marshal(hsh.HostSensorPodNames) hsh.podListLock.RUnlock() if err != nil { return res, fmt.Errorf("failed to marshal pod list: %v", err) } err = json.Unmarshal(jsonBytes, &res) if err != nil { return res, fmt.Errorf("failed to unmarshal pod list: %v", err) } return res, nil } func (hsh *HostSensorHandler) HTTPGetToPod(podName, path string) ([]byte, error) { // send the request to the port restProxy := hsh.k8sObj.KubernetesClient.CoreV1().Pods(hsh.DaemonSet.Namespace).ProxyGet("http", podName, fmt.Sprintf("%d", hsh.HostSensorPort), path, map[string]string{}) return restProxy.DoRaw(hsh.k8sObj.Context) } func (hsh *HostSensorHandler) ForwardToPod(podName, path string) ([]byte, error) { // NOT IN USE: // --- // spawn port forwarding // req := hsh.k8sObj.KubernetesClient.CoreV1().RESTClient().Post() // req = req.Name(podName) // req = req.Namespace(hsh.DaemonSet.Namespace) // req = req.Resource("pods") // req = req.SubResource("portforward") // ---- // https://github.com/gianarb/kube-port-forward // fullPath := fmt.Sprintf("/api/v1/namespaces/%s/pods/%s/portforward", // hsh.DaemonSet.Namespace, podName) // transport, upgrader, err := spdy.RoundTripperFor(hsh.k8sObj.KubernetesClient.config) // if err != nil { // return nil, err // } // hostIP := strings.TrimLeft(req.RestConfig.Host, "htps:/") // dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, http.MethodPost, &url.URL{Scheme: "http", Path: path, Host: hostIP}) return nil, nil } // sendAllPodsHTTPGETRequest fills the raw byte response in the envelope and the node name, but not the GroupVersionKind // so the caller is responsible to convert the raw data to some structured data and add the GroupVersionKind details func (hsh *HostSensorHandler) sendAllPodsHTTPGETRequest(path, requestKind string) ([]hostsensor.HostSensorDataEnvelope, error) { podList, err := hsh.getPodList() if err != nil { return nil, fmt.Errorf("failed to sendAllPodsHTTPGETRequest: %v", err) } res := make([]hostsensor.HostSensorDataEnvelope, 0, len(podList)) resLock := sync.Mutex{} wg := sync.WaitGroup{} wg.Add(len(podList)) for podName := range podList { go func(podName, path string) { defer wg.Done() resBytes, err := hsh.HTTPGetToPod(podName, path) if err != nil { logger.L().Error("failed to get data", helpers.String("path", path), helpers.String("podName", podName), helpers.Error(err)) } else { resLock.Lock() defer resLock.Unlock() hostSensorDataEnvelope := hostsensor.HostSensorDataEnvelope{} hostSensorDataEnvelope.SetApiVersion(k8sinterface.JoinGroupVersion(hostsensor.GroupHostSensor, hostsensor.Version)) hostSensorDataEnvelope.SetKind(requestKind) hostSensorDataEnvelope.SetName(podList[podName]) hostSensorDataEnvelope.SetData(resBytes) res = append(res, hostSensorDataEnvelope) } }(podName, path) } wg.Wait() return res, nil } // return list of LinuxKernelVariables func (hsh *HostSensorHandler) GetKernelVariables() ([]hostsensor.HostSensorDataEnvelope, error) { // loop over pods and port-forward it to each of them return hsh.sendAllPodsHTTPGETRequest("/LinuxKernelVariables", "LinuxKernelVariables") } // return list of OpenPortsList func (hsh *HostSensorHandler) GetOpenPortsList() ([]hostsensor.HostSensorDataEnvelope, error) { // loop over pods and port-forward it to each of them return hsh.sendAllPodsHTTPGETRequest("/openedPorts", "OpenPortsList") } // return list of LinuxSecurityHardeningStatus func (hsh *HostSensorHandler) GetLinuxSecurityHardeningStatus() ([]hostsensor.HostSensorDataEnvelope, error) { // loop over pods and port-forward it to each of them return hsh.sendAllPodsHTTPGETRequest("/linuxSecurityHardening", "LinuxSecurityHardeningStatus") } // return list of KubeletCommandLine func (hsh *HostSensorHandler) GetKubeletCommandLine() ([]hostsensor.HostSensorDataEnvelope, error) { // loop over pods and port-forward it to each of them resps, err := hsh.sendAllPodsHTTPGETRequest("/kubeletCommandLine", "KubeletCommandLine") if err != nil { return resps, err } for resp := range resps { var data = make(map[string]interface{}) data["fullCommand"] = string(resps[resp].Data) resBytesMarshal, err := json.Marshal(data) // TODO catch error if err == nil { resps[resp].Data = json.RawMessage(resBytesMarshal) } } return resps, nil } // return list of func (hsh *HostSensorHandler) GetKernelVersion() ([]hostsensor.HostSensorDataEnvelope, error) { // loop over pods and port-forward it to each of them return hsh.sendAllPodsHTTPGETRequest("/kernelVersion", "KernelVersion") } // return list of func (hsh *HostSensorHandler) GetOsReleaseFile() ([]hostsensor.HostSensorDataEnvelope, error) { // loop over pods and port-forward it to each of them return hsh.sendAllPodsHTTPGETRequest("/osRelease", "OsReleaseFile") } // return list of func (hsh *HostSensorHandler) GetKubeletConfigurations() ([]hostsensor.HostSensorDataEnvelope, error) { // loop over pods and port-forward it to each of them res, err := hsh.sendAllPodsHTTPGETRequest("/kubeletConfigurations", "KubeletConfiguration") // empty kind, will be overridden for resIdx := range res { jsonBytes, err := yaml.YAMLToJSON(res[resIdx].Data) if err != nil { logger.L().Error("failed to convert kubelet configurations from yaml to json", helpers.Error(err)) continue } res[resIdx].SetData(jsonBytes) } return res, err } func (hsh *HostSensorHandler) CollectResources() ([]hostsensor.HostSensorDataEnvelope, map[string]apis.StatusInfo, error) { res := make([]hostsensor.HostSensorDataEnvelope, 0) infoMap := make(map[string]apis.StatusInfo) if hsh.DaemonSet == nil { return res, nil, nil } var kcData []hostsensor.HostSensorDataEnvelope var err error logger.L().Debug("Accessing host scanner") kcData, err = hsh.GetKubeletConfigurations() if err != nil { addInfoToMap(KubeletConfiguration, infoMap, err) logger.L().Warning(err.Error()) } if len(kcData) > 0 { res = append(res, kcData...) } // kcData, err = hsh.GetKubeletCommandLine() if err != nil { addInfoToMap(KubeletCommandLine, infoMap, err) logger.L().Warning(err.Error()) } if len(kcData) > 0 { res = append(res, kcData...) } // kcData, err = hsh.GetOsReleaseFile() if err != nil { addInfoToMap(OsReleaseFile, infoMap, err) logger.L().Warning(err.Error()) } if len(kcData) > 0 { res = append(res, kcData...) } // kcData, err = hsh.GetKernelVersion() if err != nil { addInfoToMap(KernelVersion, infoMap, err) logger.L().Warning(err.Error()) } if len(kcData) > 0 { res = append(res, kcData...) } // kcData, err = hsh.GetLinuxSecurityHardeningStatus() if err != nil { addInfoToMap(LinuxSecurityHardeningStatus, infoMap, err) logger.L().Warning(err.Error()) } if len(kcData) > 0 { res = append(res, kcData...) } // kcData, err = hsh.GetOpenPortsList() if err != nil { addInfoToMap(OpenPortsList, infoMap, err) logger.L().Warning(err.Error()) } if len(kcData) > 0 { res = append(res, kcData...) } // GetKernelVariables kcData, err = hsh.GetKernelVariables() if err != nil { addInfoToMap(LinuxKernelVariables, infoMap, err) logger.L().Warning(err.Error()) } if len(kcData) > 0 { res = append(res, kcData...) } logger.L().Debug("Done reading information from host scanner") return res, infoMap, nil }