package hostsensorutils import ( "encoding/json" "fmt" "sync" "github.com/armosec/k8s-interface/k8sinterface" "github.com/armosec/opa-utils/objectsenvelopes/hostsensor" "github.com/armosec/opa-utils/reporthandling/apis" logger "github.com/dwertent/go-logger" "github.com/dwertent/go-logger/helpers" "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) getResourcesFromPod(podName, nodeName, resourceKind, path string) (hostsensor.HostSensorDataEnvelope, error) { // send the request and pack the response as an hostSensorDataEnvelope resBytes, err := hsh.HTTPGetToPod(podName, path) if err != nil { return hostsensor.HostSensorDataEnvelope{}, err } hostSensorDataEnvelope := hostsensor.HostSensorDataEnvelope{} hostSensorDataEnvelope.SetApiVersion(k8sinterface.JoinGroupVersion(hostsensor.GroupHostSensor, hostsensor.Version)) hostSensorDataEnvelope.SetKind(resourceKind) hostSensorDataEnvelope.SetName(nodeName) hostSensorDataEnvelope.SetData(resBytes) return hostSensorDataEnvelope, nil } 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 // // The function produces a worker-pool with a fixed number of workers. // For each node the request is pushed to the jobs channel, the worker sends the request and pushes the result to the result channel. // When all workers have finished, the function returns a list of results 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)) var wg sync.WaitGroup // initialization of the channels hsh.workerPool.init(len(podList)) hsh.workerPool.hostSensorApplyJobs(podList, path, requestKind) hsh.workerPool.hostSensorGetResults(&res) hsh.workerPool.createWorkerPool(hsh, &wg) hsh.workerPool.waitForDone(&wg) 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 KubeletInfo func (hsh *HostSensorHandler) GetKubeletInfo() ([]hostsensor.HostSensorDataEnvelope, error) { // loop over pods and port-forward it to each of them return hsh.sendAllPodsHTTPGETRequest("/kubeletInfo", "KubeletInfo") } // return list of KubeProxyInfo func (hsh *HostSensorHandler) GetKubeProxyInfo() ([]hostsensor.HostSensorDataEnvelope, error) { // loop over pods and port-forward it to each of them return hsh.sendAllPodsHTTPGETRequest("/kubeProxyInfo", "KubeProxyInfo") } // 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...) } // GetKubeletInfo kcData, err = hsh.GetKubeletInfo() if err != nil { addInfoToMap(KubeletInfo, infoMap, err) logger.L().Warning(err.Error()) } if len(kcData) > 0 { res = append(res, kcData...) } // GetKubeProxyInfo kcData, err = hsh.GetKubeProxyInfo() if err != nil { addInfoToMap(KubeProxyInfo, 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 }