Files
kubescape/core/pkg/opaprocessor/processorhandlerutils.go
Matthias Bertschy 70a9380966 fix go imports
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2025-01-09 12:14:56 +01:00

288 lines
9.3 KiB
Go

package opaprocessor
import (
"context"
"github.com/kubescape/go-logger"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/kubescape/v3/core/cautils"
"github.com/kubescape/opa-utils/exceptions"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
resources "github.com/kubescape/opa-utils/resources"
"go.opentelemetry.io/otel"
corev1 "k8s.io/api/core/v1"
)
const clusterScope = "clusterScope"
var largeClusterSize int = -1
// updateResults updates the results objects and report objects. This is a critical function - DO NOT CHANGE
//
// The function:
// - removes sensible data
// - adds exceptions (and updates controls status)
// - summarizes results
func (opap *OPAProcessor) updateResults(ctx context.Context) {
_, span := otel.Tracer("").Start(ctx, "OPAProcessor.updateResults")
defer span.End()
defer logger.L().Ctx(ctx).Success("Done aggregating results")
cautils.StartSpinner()
defer cautils.StopSpinner()
// remove data from all objects
for i := range opap.AllResources {
removeData(opap.AllResources[i])
}
processor := exceptions.NewProcessor()
// set exceptions
for i := range opap.ResourcesResult {
t := opap.ResourcesResult[i]
// first set exceptions (reuse the same exceptions processor)
if resource, ok := opap.AllResources[i]; ok {
t.SetExceptions(
resource,
opap.Exceptions,
opap.clusterName,
opap.AllPolicies.Controls, // update status depending on action required
resourcesresults.WithExceptionsProcessor(processor),
)
}
// summarize the resources
opap.Report.AppendResourceResultToSummary(&t)
// Add score
// TODO
// save changes
opap.ResourcesResult[i] = t
}
// set result summary
// map control to error
controlToInfoMap := mapControlToInfo(opap.ResourceToControlsMap, opap.InfoMap, opap.Report.SummaryDetails.Controls)
opap.Report.SummaryDetails.InitResourcesSummary(controlToInfoMap)
}
func mapControlToInfo(mapResourceToControls map[string][]string, infoMap map[string]apis.StatusInfo, controlSummary reportsummary.ControlSummaries) map[string]apis.StatusInfo {
controlToInfoMap := make(map[string]apis.StatusInfo)
for resource, statusInfo := range infoMap {
controlIDs := mapResourceToControls[resource]
for _, controlID := range controlIDs {
ctrl := controlSummary.GetControl(reportsummary.EControlCriteriaID, controlID)
if ctrl != nil {
resources := ctrl.NumberOfResources()
// Check that there are no K8s resources too
if isEmptyResources(resources) {
controlToInfoMap[controlID] = statusInfo
}
}
}
}
return controlToInfoMap
}
func isEmptyResources(counters reportsummary.ICounters) bool {
return counters.Failed() == 0 && counters.Skipped() == 0 && counters.Passed() == 0
}
func getAllSupportedObjects(k8sResources cautils.K8SResources, externalResources cautils.ExternalResources, allResources map[string]workloadinterface.IMetadata, rule *reporthandling.PolicyRule) map[string][]workloadinterface.IMetadata {
k8sObjects := getKubernetesObjects(k8sResources, allResources, rule.Match)
externalObjs := getKubernetesObjectsFromExternalResources(externalResources, allResources, rule.DynamicMatch)
if len(externalObjs) > 0 {
l, ok := k8sObjects[clusterScope]
if !ok {
l = []workloadinterface.IMetadata{}
}
l = append(l, externalObjs...)
k8sObjects[clusterScope] = l
}
return k8sObjects
}
func getKubernetesObjectsFromExternalResources(externalResources cautils.ExternalResources, allResources map[string]workloadinterface.IMetadata, match []reporthandling.RuleMatchObjects) []workloadinterface.IMetadata {
k8sObjects := []workloadinterface.IMetadata{}
for m := range match {
for _, groups := range match[m].APIGroups {
for _, version := range match[m].APIVersions {
for _, resource := range match[m].Resources {
groupResources := k8sinterface.ResourceGroupToString(groups, version, resource)
for _, groupResource := range groupResources {
if k8sObj, ok := externalResources[groupResource]; ok {
for i := range k8sObj {
k8sObjects = append(k8sObjects, allResources[k8sObj[i]])
}
}
}
}
}
}
}
return k8sObjects
}
func getKubernetesObjects(k8sResources cautils.K8SResources, allResources map[string]workloadinterface.IMetadata, match []reporthandling.RuleMatchObjects) map[string][]workloadinterface.IMetadata {
k8sObjects := map[string][]workloadinterface.IMetadata{}
for m := range match {
for _, groups := range match[m].APIGroups {
for _, version := range match[m].APIVersions {
for _, resource := range match[m].Resources {
groupResources := k8sinterface.ResourceGroupToString(groups, version, resource)
for _, groupResource := range groupResources {
if k8sObj, ok := k8sResources[groupResource]; ok {
for i := range k8sObj {
obj := allResources[k8sObj[i]]
ns := getNamespaceName(obj, len(allResources))
l, ok := k8sObjects[ns]
if !ok {
l = []workloadinterface.IMetadata{}
}
l = append(l, obj)
k8sObjects[ns] = l
}
}
}
}
}
}
}
return k8sObjects
// return filterOutChildResources(k8sObjects, match)
}
func getRuleDependencies(ctx context.Context) (map[string]string, error) {
modules := resources.LoadRegoModules()
if len(modules) == 0 {
logger.L().Ctx(ctx).Warning("failed to load rule dependencies")
}
return modules, nil
}
func removeData(obj workloadinterface.IMetadata) {
if !k8sinterface.IsTypeWorkload(obj.GetObject()) {
return // remove data only from kubernetes objects
}
workload := workloadinterface.NewWorkloadObj(obj.GetObject())
switch workload.GetKind() {
case "Secret":
removeSecretData(workload)
case "ConfigMap":
removeConfigMapData(workload)
default:
removePodData(workload)
}
}
func removeConfigMapData(workload workloadinterface.IWorkload) {
workload.RemoveAnnotation("kubectl.kubernetes.io/last-applied-configuration")
workloadinterface.RemoveFromMap(workload.GetObject(), "metadata", "managedFields")
overrideSensitiveData(workload)
}
func overrideSensitiveData(workload workloadinterface.IWorkload) {
dataInterface, ok := workloadinterface.InspectMap(workload.GetObject(), "data")
if ok {
data, ok := dataInterface.(map[string]interface{})
if ok {
for key := range data {
workloadinterface.SetInMap(workload.GetObject(), []string{"data"}, key, "XXXXXX")
}
}
}
}
func removeSecretData(workload workloadinterface.IWorkload) {
workload.RemoveAnnotation("kubectl.kubernetes.io/last-applied-configuration")
workloadinterface.RemoveFromMap(workload.GetObject(), "metadata", "managedFields")
overrideSensitiveData(workload)
}
func removePodData(workload workloadinterface.IWorkload) {
workload.RemoveAnnotation("kubectl.kubernetes.io/last-applied-configuration")
workloadinterface.RemoveFromMap(workload.GetObject(), "metadata", "managedFields")
workloadinterface.RemoveFromMap(workload.GetObject(), "status")
// containers
if containers, err := workload.GetContainers(); err == nil && len(containers) > 0 {
removeContainersData(containers)
workloadinterface.SetInMap(workload.GetObject(), workloadinterface.PodSpec(workload.GetKind()), "containers", containers)
}
// init containers
if initContainers, err := workload.GetInitContainers(); err == nil && len(initContainers) > 0 {
removeContainersData(initContainers)
workloadinterface.SetInMap(workload.GetObject(), workloadinterface.PodSpec(workload.GetKind()), "initContainers", initContainers)
}
// ephemeral containers
if ephemeralContainers, err := workload.GetEphemeralContainers(); err == nil && len(ephemeralContainers) > 0 {
removeEphemeralContainersData(ephemeralContainers)
workloadinterface.SetInMap(workload.GetObject(), workloadinterface.PodSpec(workload.GetKind()), "ephemeralContainers", ephemeralContainers)
}
}
func removeContainersData(containers []corev1.Container) {
for i := range containers {
for j := range containers[i].Env {
containers[i].Env[j].Value = "XXXXXX"
}
}
}
func removeEphemeralContainersData(containers []corev1.EphemeralContainer) {
for i := range containers {
for j := range containers[i].Env {
containers[i].Env[j].Value = "XXXXXX"
}
}
}
func ruleData(rule *reporthandling.PolicyRule) string {
return rule.Rule
}
func ruleEnumeratorData(rule *reporthandling.PolicyRule) string {
return rule.ResourceEnumerator
}
func getNamespaceName(obj workloadinterface.IMetadata, clusterSize int) string {
if !isLargeCluster(clusterSize) {
return clusterScope
}
// if the resource is in namespace scope, get the namespace
if k8sinterface.IsResourceInNamespaceScope(obj.GetKind()) {
return obj.GetNamespace()
}
if obj.GetKind() == "Namespace" {
return obj.GetName()
}
return clusterScope
}
// isLargeCluster returns true if the cluster size is larger than the largeClusterSize
// This code is a workaround for large clusters. The final solution will be to scan resources individually
func isLargeCluster(clusterSize int) bool {
if largeClusterSize < 0 {
// initialize large cluster size
largeClusterSize, _ = cautils.ParseIntEnvVar("LARGE_CLUSTER_SIZE", 2500)
}
return clusterSize > largeClusterSize
}