Code refactor (follow up to PR #1300) (#1323)

* code refactor

Signed-off-by: Amir Malka <amirm@armosec.io>

* use scaninfo object in resource handler

Signed-off-by: Amir Malka <amirm@armosec.io>

---------

Signed-off-by: Amir Malka <amirm@armosec.io>
This commit is contained in:
Amir Malka
2023-08-03 17:50:33 +03:00
committed by GitHub
parent f799b63684
commit e2f96200e0
24 changed files with 131 additions and 140 deletions

View File

@@ -12,6 +12,7 @@ import (
"github.com/kubescape/kubescape/v2/core/meta"
v1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
"golang.org/x/exp/slices"
)
var (
@@ -55,7 +56,7 @@ func GetDownloadCmd(ks meta.IKubescape) *cobra.Command {
if len(args) < 1 {
return fmt.Errorf("policy type required, supported: %v", supported)
}
if cautils.StringInSlice(core.DownloadSupportCommands(), args[0]) == cautils.ValueNotFound {
if !slices.Contains(core.DownloadSupportCommands(), args[0]) {
return fmt.Errorf("invalid parameter '%s'. Supported parameters: %s", args[0], supported)
}
return nil

View File

@@ -11,6 +11,7 @@ import (
"github.com/kubescape/kubescape/v2/core/meta"
v1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
"golang.org/x/exp/slices"
)
var (
@@ -43,7 +44,7 @@ func GetListCmd(ks meta.IKubescape) *cobra.Command {
if len(args) < 1 {
return fmt.Errorf("policy type requeued, supported: %s", supported)
}
if cautils.StringInSlice(core.ListSupportActions(), args[0]) == cautils.ValueNotFound {
if !slices.Contains(core.ListSupportActions(), args[0]) {
return fmt.Errorf("invalid parameter '%s'. Supported parameters: %s", args[0], supported)
}
return nil

View File

@@ -11,6 +11,7 @@ import (
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
reporthandlingapis "github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"golang.org/x/exp/slices"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
@@ -83,7 +84,7 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
} else {
// Read frameworks from input args
frameworks = strings.Split(args[0], ",")
if cautils.StringInSlice(frameworks, "all") != cautils.ValueNotFound {
if slices.Contains(frameworks, "all") {
scanInfo.ScanAll = true
frameworks = getter.NativeFrameworks

View File

@@ -17,7 +17,7 @@ import (
// K8SResources map[<api group>/<api version>/<resource>][]<resourceID>
type K8SResources map[string][]string
type KSResources map[string][]string
type ExternalResources map[string][]string
type ImageScanData struct {
PresenterConfig *models.PresenterConfig
@@ -37,7 +37,7 @@ const (
type OPASessionObj struct {
K8SResources K8SResources // input k8s objects
KubescapeResource KSResources // input Kubescape objects
ExternalResources ExternalResources // input non-k8s objects (external resources)
AllPolicies *Policies // list of all frameworks
ExcludedRules map[string]bool // rules to exclude map[rule name>]X
AllResources map[string]workloadinterface.IMetadata // all scanned resources, map[<resource ID>]<resource>

View File

@@ -11,6 +11,7 @@ import (
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/workloadinterface"
"golang.org/x/exp/slices"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/opa-utils/objectsenvelopes"
@@ -292,11 +293,11 @@ func convertYamlToJson(i interface{}) interface{} {
}
func IsYaml(filePath string) bool {
return StringInSlice(YAML_PREFIX, strings.ReplaceAll(filepath.Ext(filePath), ".", "")) != ValueNotFound
return slices.Contains(YAML_PREFIX, strings.ReplaceAll(filepath.Ext(filePath), ".", ""))
}
func IsJson(filePath string) bool {
return StringInSlice(JSON_PREFIX, strings.ReplaceAll(filepath.Ext(filePath), ".", "")) != ValueNotFound
return slices.Contains(JSON_PREFIX, strings.ReplaceAll(filepath.Ext(filePath), ".", ""))
}
func glob(root, pattern string, onlyDirectories bool) ([]string, error) {

View File

@@ -8,8 +8,6 @@ import (
"strings"
)
const ValueNotFound = -1
func ConvertLabelsToString(labels map[string]string) string {
labelsStr := ""
delimiter := ""
@@ -37,15 +35,6 @@ func ConvertStringToLabels(labelsStr string) map[string]string {
return labels
}
func StringInSlice(strSlice []string, str string) int {
for i := range strSlice {
if strSlice[i] == str {
return i
}
}
return ValueNotFound
}
func StringSlicesAreEqual(a, b []string) bool {
if len(a) != len(b) {
return false

View File

@@ -32,9 +32,9 @@ var (
}
)
func MapKSResource(ksResourceMap KSResources, resources []string) []string {
func MapExternalResource(externalResourceMap ExternalResources, resources []string) []string {
var hostResources []string
for k := range ksResourceMap {
for k := range externalResourceMap {
for _, resource := range resources {
if strings.Contains(k, resource) {
hostResources = append(hostResources, k)
@@ -44,16 +44,16 @@ func MapKSResource(ksResourceMap KSResources, resources []string) []string {
return hostResources
}
func MapHostResources(ksResourceMap KSResources) []string {
return MapKSResource(ksResourceMap, HostSensorResources)
func MapHostResources(externalResourceMap ExternalResources) []string {
return MapExternalResource(externalResourceMap, HostSensorResources)
}
func MapImageVulnResources(ksResourceMap KSResources) []string {
return MapKSResource(ksResourceMap, ImageVulnResources)
func MapImageVulnResources(externalResourceMap ExternalResources) []string {
return MapExternalResource(externalResourceMap, ImageVulnResources)
}
func MapCloudResources(ksResourceMap KSResources) []string {
return MapKSResource(ksResourceMap, CloudResources)
func MapCloudResources(externalResourceMap ExternalResources) []string {
return MapExternalResource(externalResourceMap, CloudResources)
}
func SetInfoMapForResources(info string, resources []string, errorMap map[string]apis.StatusInfo) {

View File

@@ -95,12 +95,12 @@ func getResourceHandler(ctx context.Context, scanInfo *cautils.ScanInfo, tenantC
if len(scanInfo.InputPatterns) > 0 || k8s == nil {
// scanInfo.HostSensor.SetBool(false)
return resourcehandler.NewFileResourceHandler(ctx, scanInfo.InputPatterns, scanInfo.ScanObject)
return resourcehandler.NewFileResourceHandler()
}
getter.GetKSCloudAPIConnector()
rbacObjects := getRBACHandler(tenantConfig, k8s, scanInfo.Submit)
return resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo), hostSensorHandler, rbacObjects, registryAdaptors, scanInfo.ScanObject)
return resourcehandler.NewK8sResourceHandler(k8s, hostSensorHandler, rbacObjects, registryAdaptors)
}
// getHostSensorHandler yields a IHostSensor that knows how to collect a host's scanned resources.
@@ -135,17 +135,6 @@ func getHostSensorHandler(ctx context.Context, scanInfo *cautils.ScanInfo, k8s *
}
}
func getFieldSelector(scanInfo *cautils.ScanInfo) resourcehandler.IFieldSelector {
if scanInfo.IncludeNamespaces != "" {
return resourcehandler.NewIncludeSelector(scanInfo.IncludeNamespaces)
}
if scanInfo.ExcludedNamespaces != "" {
return resourcehandler.NewExcludeSelector(scanInfo.ExcludedNamespaces)
}
return &resourcehandler.EmptySelector{}
}
func policyIdentifierIdentities(pi []cautils.PolicyIdentifier) string {
policiesIdentities := ""
for i := range pi {

View File

@@ -171,7 +171,7 @@ func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*res
// ===================== resources =====================
ctxResources, spanResources := otel.Tracer("").Start(ctxInit, "resources")
err = resourcehandler.CollectResources(ctxResources, interfaces.resourceHandler, scanInfo.PolicyIdentifier, scanData, cautils.NewProgressHandler(""), *scanInfo)
err = resourcehandler.CollectResources(ctxResources, interfaces.resourceHandler, scanInfo.PolicyIdentifier, scanData, cautils.NewProgressHandler(""), scanInfo)
if err != nil {
spanInit.End()
return resultsHandling, err

View File

@@ -2,7 +2,7 @@ package metrics
import (
"context"
"fmt"
"strings"
"sync"
"github.com/kubescape/go-logger"
@@ -31,7 +31,7 @@ func Init() {
meterProvider := otel.GetMeterProvider()
meter := meterProvider.Meter(METER_NAME)
metricName := func(name string) string {
return fmt.Sprintf("%s_%s", METRIC_NAME_PREFIX, name)
return strings.Join([]string{METRIC_NAME_PREFIX, name}, "_")
}
if kubernetesResourcesCount, err = meter.Int64UpDownCounter(metricName("kubernetes_resources_count")); err != nil {

View File

@@ -271,7 +271,7 @@ func (opap *OPAProcessor) processRule(ctx context.Context, rule *reporthandling.
inputResources, err := reporthandling.RegoResourcesAggregator(
rule,
getAllSupportedObjects(opap.K8SResources, opap.KubescapeResource, opap.AllResources, rule), // NOTE: this uses the initial snapshot of AllResources
getAllSupportedObjects(opap.K8SResources, opap.ExternalResources, opap.AllResources, rule), // NOTE: this uses the initial snapshot of AllResources
)
if err != nil {
return nil, nil, fmt.Errorf("error getting aggregated k8sObjects: %w", err)

View File

@@ -14,6 +14,7 @@ import (
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
resources "github.com/kubescape/opa-utils/resources"
"go.opentelemetry.io/otel"
"golang.org/x/exp/slices"
)
// updateResults updates the results objects and report objects. This is a critical function - DO NOT CHANGE
@@ -87,14 +88,14 @@ func isEmptyResources(counters reportsummary.ICounters) bool {
return counters.Failed() == 0 && counters.Skipped() == 0 && counters.Passed() == 0
}
func getAllSupportedObjects(k8sResources cautils.K8SResources, ksResources cautils.KSResources, allResources map[string]workloadinterface.IMetadata, rule *reporthandling.PolicyRule) []workloadinterface.IMetadata {
func getAllSupportedObjects(k8sResources cautils.K8SResources, externalResources cautils.ExternalResources, allResources map[string]workloadinterface.IMetadata, rule *reporthandling.PolicyRule) []workloadinterface.IMetadata {
k8sObjects := []workloadinterface.IMetadata{}
k8sObjects = append(k8sObjects, getKubernetesObjects(k8sResources, allResources, rule.Match)...)
k8sObjects = append(k8sObjects, getKSObjects(ksResources, allResources, rule.DynamicMatch)...)
k8sObjects = append(k8sObjects, getKubenetesObjectsFromExternalResources(externalResources, allResources, rule.DynamicMatch)...)
return k8sObjects
}
func getKSObjects(k8sResources cautils.KSResources, allResources map[string]workloadinterface.IMetadata, match []reporthandling.RuleMatchObjects) []workloadinterface.IMetadata {
func getKubenetesObjectsFromExternalResources(externalResources cautils.ExternalResources, allResources map[string]workloadinterface.IMetadata, match []reporthandling.RuleMatchObjects) []workloadinterface.IMetadata {
k8sObjects := []workloadinterface.IMetadata{}
for m := range match {
@@ -103,7 +104,7 @@ func getKSObjects(k8sResources cautils.KSResources, allResources map[string]work
for _, resource := range match[m].Resources {
groupResources := k8sinterface.ResourceGroupToString(groups, version, resource)
for _, groupResource := range groupResources {
if k8sObj, ok := k8sResources[groupResource]; ok {
if k8sObj, ok := externalResources[groupResource]; ok {
for i := range k8sObj {
k8sObjects = append(k8sObjects, allResources[k8sObj[i]])
}
@@ -162,7 +163,7 @@ func filterOutChildResources(objects []workloadinterface.IMetadata, match []repo
ownerReferences, err := w.GetOwnerReferences()
if err != nil || len(ownerReferences) == 0 {
response = append(response, w)
} else if !k8sinterface.IsStringInSlice(owners, ownerReferences[0].Kind) {
} else if !slices.Contains(owners, ownerReferences[0].Kind) {
response = append(response, w)
}
}

View File

@@ -13,6 +13,7 @@ import (
"github.com/open-policy-agent/opa/rego"
"github.com/open-policy-agent/opa/topdown/builtins"
"github.com/open-policy-agent/opa/types"
"golang.org/x/exp/slices"
)
// ConvertFrameworksToPolicies convert list of frameworks to list of policies
@@ -50,7 +51,7 @@ func ConvertFrameworksToSummaryDetails(summaryDetails *reportsummary.SummaryDeta
summaryDetails.Controls[id] = c
}
}
if cautils.StringInSlice(policies.Frameworks, frameworks[i].Name) != cautils.ValueNotFound {
if slices.Contains(policies.Frameworks, frameworks[i].Name) {
summaryDetails.Frameworks = append(summaryDetails.Frameworks, reportsummary.FrameworkSummary{
Name: frameworks[i].Name,
Controls: controls,

View File

@@ -1,13 +1,18 @@
package resourcehandler
import (
"fmt"
"strings"
"github.com/kubescape/k8s-interface/k8sinterface"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const (
FieldSelectorsSeparator = ","
FieldSelectorsEqualsOperator = "=="
FieldSelectorsNotEqualsOperator = "!="
)
type IFieldSelector interface {
GetNamespacesSelectors(*schema.GroupVersionResource) []string
GetClusterScope(*schema.GroupVersionResource) bool
@@ -52,9 +57,9 @@ func (is *IncludeSelector) GetClusterScope(resource *schema.GroupVersionResource
func (es *ExcludeSelector) GetNamespacesSelectors(resource *schema.GroupVersionResource) []string {
fieldSelectors := ""
for _, n := range strings.Split(es.namespace, ",") {
for _, n := range strings.Split(es.namespace, FieldSelectorsSeparator) {
if n != "" {
fieldSelectors = combineFieldSelectors(fieldSelectors, getNamespacesSelector(resource.Resource, n, "!="))
fieldSelectors = combineFieldSelectors(fieldSelectors, getNamespacesSelector(resource.Resource, n, FieldSelectorsNotEqualsOperator))
}
}
return []string{fieldSelectors}
@@ -63,9 +68,9 @@ func (es *ExcludeSelector) GetNamespacesSelectors(resource *schema.GroupVersionR
func (is *IncludeSelector) GetNamespacesSelectors(resource *schema.GroupVersionResource) []string {
fieldSelectors := []string{}
for _, n := range strings.Split(is.namespace, ",") {
for _, n := range strings.Split(is.namespace, FieldSelectorsSeparator) {
if n != "" {
fieldSelectors = append(fieldSelectors, getNamespacesSelector(resource.Resource, n, "=="))
fieldSelectors = append(fieldSelectors, getNamespacesSelector(resource.Resource, n, FieldSelectorsEqualsOperator))
}
}
return fieldSelectors
@@ -88,11 +93,11 @@ func getNamespacesSelector(kind, ns, operator string) string {
}
func getNameFieldSelectorString(resourceName, operator string) string {
return fmt.Sprintf("metadata.name%s%s", operator, resourceName)
return strings.Join([]string{"metadata.name", resourceName}, operator)
}
func getNamespaceFieldSelectorString(namespace, operator string) string {
return fmt.Sprintf("metadata.namespace%s%s", operator, namespace)
return strings.Join([]string{"metadata.namespace", namespace}, operator)
}
func combineFieldSelectors(selectors ...string) string {
@@ -102,5 +107,5 @@ func combineFieldSelectors(selectors ...string) string {
nonEmptyStrings = append(nonEmptyStrings, selectors[i])
}
}
return strings.Join(nonEmptyStrings, ",")
return strings.Join(nonEmptyStrings, FieldSelectorsSeparator)
}

View File

@@ -8,7 +8,6 @@ import (
"strings"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/opa-utils/objectsenvelopes"
"github.com/kubescape/opa-utils/reporthandling"
"k8s.io/apimachinery/pkg/version"
@@ -20,24 +19,18 @@ import (
)
// FileResourceHandler handle resources from files and URLs
type FileResourceHandler struct {
singleResourceScan *objectsenvelopes.ScanObject
inputPatterns []string
}
type FileResourceHandler struct{}
func NewFileResourceHandler(_ context.Context, inputPatterns []string, singleResourceScan *objectsenvelopes.ScanObject) *FileResourceHandler {
func NewFileResourceHandler() *FileResourceHandler {
k8sinterface.InitializeMapResourcesMock() // initialize the resource map
return &FileResourceHandler{
inputPatterns: inputPatterns,
singleResourceScan: singleResourceScan,
}
return &FileResourceHandler{}
}
func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessionObj *cautils.OPASessionObj, progressListener opaprocessor.IJobProgressNotificationClient, scanInfo cautils.ScanInfo) (cautils.K8SResources, map[string]workloadinterface.IMetadata, cautils.KSResources, map[string]bool, error) {
func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessionObj *cautils.OPASessionObj, progressListener opaprocessor.IJobProgressNotificationClient, scanInfo *cautils.ScanInfo) (cautils.K8SResources, map[string]workloadinterface.IMetadata, cautils.ExternalResources, map[string]bool, error) {
allResources := map[string]workloadinterface.IMetadata{}
ksResources := cautils.KSResources{}
externalResources := cautils.ExternalResources{}
if len(fileHandler.inputPatterns) == 0 {
if len(scanInfo.InputPatterns) == 0 {
return nil, nil, nil, nil, fmt.Errorf("missing input")
}
@@ -46,7 +39,7 @@ func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessio
// load resources from all input paths
mappedResources := map[string][]workloadinterface.IMetadata{}
for path := range fileHandler.inputPatterns {
for path := range scanInfo.InputPatterns {
var workloadIDToSource map[string]reporthandling.Source
var workloads []workloadinterface.IMetadata
var err error
@@ -54,13 +47,13 @@ func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessio
if scanInfo.ChartPath != "" && scanInfo.FilePath != "" {
workloadIDToSource, workloads, err = getWorkloadFromHelmChart(ctx, scanInfo.ChartPath, scanInfo.FilePath)
} else {
workloadIDToSource, workloads, err = getResourcesFromPath(ctx, fileHandler.inputPatterns[path])
workloadIDToSource, workloads, err = getResourcesFromPath(ctx, scanInfo.InputPatterns[path])
if err != nil {
return nil, allResources, nil, nil, err
}
}
if len(workloads) == 0 {
logger.L().Debug("path ignored because contains only a non-kubernetes file", helpers.String("path", fileHandler.inputPatterns[path]))
logger.L().Debug("path ignored because contains only a non-kubernetes file", helpers.String("path", scanInfo.InputPatterns[path]))
}
for k, v := range workloadIDToSource {
@@ -73,7 +66,7 @@ func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessio
// locate input k8s object in the mapped resources - if not found or not a valid resource, return error
var err error
if sessionObj.SingleResourceScan, err = findScanObjectResource(mappedResources, fileHandler.singleResourceScan); err != nil {
if sessionObj.SingleResourceScan, err = findScanObjectResource(mappedResources, scanInfo.ScanObject); err != nil {
return nil, nil, nil, nil, err
}
@@ -104,7 +97,7 @@ func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessio
cautils.StopSpinner()
logger.L().Success("Done accessing local objects")
return k8sResources, allResources, ksResources, excludedRulesMap, nil
return k8sResources, allResources, externalResources, excludedRulesMap, nil
}
func getWorkloadFromHelmChart(ctx context.Context, helmPath, workloadPath string) (map[string]reporthandling.Source, []workloadinterface.IMetadata, error) {

View File

@@ -232,7 +232,7 @@ func getResourceHandlerMock() *K8sResourceHandler {
Context: context.Background(),
}
return NewK8sResourceHandler(k8s, &EmptySelector{}, nil, nil, nil, nil)
return NewK8sResourceHandler(k8s, nil, nil, nil)
}
func Test_CollectResources(t *testing.T) {
resourceHandler := getResourceHandlerMock()
@@ -248,12 +248,12 @@ func Test_CollectResources(t *testing.T) {
}
assert.NotPanics(t, func() {
CollectResources(context.TODO(), resourceHandler, []cautils.PolicyIdentifier{}, objSession, cautils.NewProgressHandler(""), cautils.ScanInfo{})
CollectResources(context.TODO(), resourceHandler, []cautils.PolicyIdentifier{}, objSession, cautils.NewProgressHandler(""), &cautils.ScanInfo{})
}, "Cluster named .*eks.* without a cloud config panics on cluster scan !")
assert.NotPanics(t, func() {
objSession.Metadata.ScanMetadata.ScanningTarget = reportv2.File
CollectResources(context.TODO(), resourceHandler, []cautils.PolicyIdentifier{}, objSession, cautils.NewProgressHandler(""), cautils.ScanInfo{})
CollectResources(context.TODO(), resourceHandler, []cautils.PolicyIdentifier{}, objSession, cautils.NewProgressHandler(""), &cautils.ScanInfo{})
}, "Cluster named .*eks.* without a cloud config panics on non-cluster scan !")
}

View File

@@ -19,7 +19,7 @@ import (
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)
func CollectResources(ctx context.Context, rsrcHandler IResourceHandler, policyIdentifier []cautils.PolicyIdentifier, opaSessionObj *cautils.OPASessionObj, progressListener opaprocessor.IJobProgressNotificationClient, scanInfo cautils.ScanInfo) error {
func CollectResources(ctx context.Context, rsrcHandler IResourceHandler, policyIdentifier []cautils.PolicyIdentifier, opaSessionObj *cautils.OPASessionObj, progressListener opaprocessor.IJobProgressNotificationClient, scanInfo *cautils.ScanInfo) error {
ctx, span := otel.Tracer("").Start(ctx, "resourcehandler.CollectResources")
defer span.End()
opaSessionObj.Report.ClusterAPIServerInfo = rsrcHandler.GetClusterAPIServerInfo(ctx)
@@ -29,17 +29,17 @@ func CollectResources(ctx context.Context, rsrcHandler IResourceHandler, policyI
setCloudMetadata(opaSessionObj)
}
resourcesMap, allResources, ksResources, excludedRulesMap, err := rsrcHandler.GetResources(ctx, opaSessionObj, progressListener, scanInfo)
resourcesMap, allResources, externalResources, excludedRulesMap, err := rsrcHandler.GetResources(ctx, opaSessionObj, progressListener, scanInfo)
if err != nil {
return err
}
opaSessionObj.K8SResources = resourcesMap
opaSessionObj.AllResources = allResources
opaSessionObj.KubescapeResource = ksResources
opaSessionObj.ExternalResources = externalResources
opaSessionObj.ExcludedRules = excludedRulesMap
if (opaSessionObj.K8SResources == nil || len(opaSessionObj.K8SResources) == 0) && (opaSessionObj.KubescapeResource == nil || len(opaSessionObj.KubescapeResource) == 0) {
if (opaSessionObj.K8SResources == nil || len(opaSessionObj.K8SResources) == 0) && (opaSessionObj.ExternalResources == nil || len(opaSessionObj.ExternalResources) == 0) {
return fmt.Errorf("empty list of resources")
}

View File

@@ -10,6 +10,6 @@ import (
)
type IResourceHandler interface {
GetResources(context.Context, *cautils.OPASessionObj, opaprocessor.IJobProgressNotificationClient, cautils.ScanInfo) (cautils.K8SResources, map[string]workloadinterface.IMetadata, cautils.KSResources, map[string]bool, error)
GetResources(context.Context, *cautils.OPASessionObj, opaprocessor.IJobProgressNotificationClient, *cautils.ScanInfo) (cautils.K8SResources, map[string]workloadinterface.IMetadata, cautils.ExternalResources, map[string]bool, error)
GetClusterAPIServerInfo(ctx context.Context) *version.Info
}

View File

@@ -38,28 +38,26 @@ var cloudResourceGetterMapping = map[string]cloudResourceGetter{
}
type K8sResourceHandler struct {
k8s *k8sinterface.KubernetesApi
hostSensorHandler hostsensorutils.IHostSensor
fieldSelector IFieldSelector
rbacObjectsAPI *cautils.RBACObjects
registryAdaptors *RegistryAdaptors
singleResourceScan *objectsenvelopes.ScanObject
k8s *k8sinterface.KubernetesApi
hostSensorHandler hostsensorutils.IHostSensor
rbacObjectsAPI *cautils.RBACObjects
registryAdaptors *RegistryAdaptors
}
func NewK8sResourceHandler(k8s *k8sinterface.KubernetesApi, fieldSelector IFieldSelector, hostSensorHandler hostsensorutils.IHostSensor, rbacObjects *cautils.RBACObjects, registryAdaptors *RegistryAdaptors, singleResourceScan *objectsenvelopes.ScanObject) *K8sResourceHandler {
func NewK8sResourceHandler(k8s *k8sinterface.KubernetesApi, hostSensorHandler hostsensorutils.IHostSensor, rbacObjects *cautils.RBACObjects, registryAdaptors *RegistryAdaptors) *K8sResourceHandler {
return &K8sResourceHandler{
k8s: k8s,
fieldSelector: fieldSelector,
hostSensorHandler: hostSensorHandler,
rbacObjectsAPI: rbacObjects,
registryAdaptors: registryAdaptors,
singleResourceScan: singleResourceScan,
k8s: k8s,
hostSensorHandler: hostSensorHandler,
rbacObjectsAPI: rbacObjects,
registryAdaptors: registryAdaptors,
}
}
func (k8sHandler *K8sResourceHandler) GetResources(ctx context.Context, sessionObj *cautils.OPASessionObj, progressListener opaprocessor.IJobProgressNotificationClient, _ cautils.ScanInfo) (cautils.K8SResources, map[string]workloadinterface.IMetadata, cautils.KSResources, map[string]bool, error) {
func (k8sHandler *K8sResourceHandler) GetResources(ctx context.Context, sessionObj *cautils.OPASessionObj, progressListener opaprocessor.IJobProgressNotificationClient, scanInfo *cautils.ScanInfo) (cautils.K8SResources, map[string]workloadinterface.IMetadata, cautils.ExternalResources, map[string]bool, error) {
var err error
sessionObj.SingleResourceScan, err = k8sHandler.findScanObjectResource(k8sHandler.singleResourceScan)
globalFieldSelectors := getFieldSelectorFromScanInfo(scanInfo)
sessionObj.SingleResourceScan, err = k8sHandler.findScanObjectResource(scanInfo.ScanObject, globalFieldSelectors)
if err != nil {
return nil, nil, nil, nil, err
}
@@ -79,7 +77,7 @@ func (k8sHandler *K8sResourceHandler) GetResources(ctx context.Context, sessionO
sessionObj.ResourceToControlsMap = resourceToControl
// pull k8s resources
k8sResourcesMap, allResources, err := k8sHandler.pullResources(queryableResources)
k8sResourcesMap, allResources, err := k8sHandler.pullResources(queryableResources, globalFieldSelectors)
if err != nil {
cautils.StopSpinner()
return k8sResourcesMap, allResources, ksResourceMap, excludedRulesMap, err
@@ -161,7 +159,7 @@ func (k8sHandler *K8sResourceHandler) GetResources(ctx context.Context, sessionO
}
// findScanObjectResource pulls the requested k8s object to be scanned from the api server
func (k8sHandler *K8sResourceHandler) findScanObjectResource(resource *objectsenvelopes.ScanObject) (workloadinterface.IWorkload, error) {
func (k8sHandler *K8sResourceHandler) findScanObjectResource(resource *objectsenvelopes.ScanObject, globalFieldSelector IFieldSelector) (workloadinterface.IWorkload, error) {
if resource == nil {
return nil, nil
}
@@ -170,7 +168,7 @@ func (k8sHandler *K8sResourceHandler) findScanObjectResource(resource *objectsen
var wlIdentifierString string
if resource.GetApiVersion() != "" {
wlIdentifierString = fmt.Sprintf("%s/%s", resource.GetApiVersion(), resource.GetKind())
wlIdentifierString = strings.Join([]string{resource.GetApiVersion(), resource.GetKind()}, "/")
} else {
wlIdentifierString = resource.GetKind()
}
@@ -180,11 +178,11 @@ func (k8sHandler *K8sResourceHandler) findScanObjectResource(resource *objectsen
return nil, err
}
fieldSelectors := getNameFieldSelectorString(resource.GetName(), "=")
fieldSelectors := getNameFieldSelectorString(resource.GetName(), FieldSelectorsEqualsOperator)
if resource.GetNamespace() != "" && k8sinterface.IsNamespaceScope(&gvr) {
fieldSelectors = combineFieldSelectors(fieldSelectors, getNamespaceFieldSelectorString(resource.GetNamespace(), "="))
fieldSelectors = combineFieldSelectors(fieldSelectors, getNamespaceFieldSelectorString(resource.GetNamespace(), FieldSelectorsEqualsOperator))
}
result, err := k8sHandler.pullSingleResource(&gvr, nil, fieldSelectors)
result, err := k8sHandler.pullSingleResource(&gvr, nil, fieldSelectors, globalFieldSelector)
if err != nil {
return nil, fmt.Errorf("failed to get resource %s, reason: %v", resource.GetID(), err)
}
@@ -206,7 +204,7 @@ func (k8sHandler *K8sResourceHandler) findScanObjectResource(resource *objectsen
return wl, nil
}
func (k8sHandler *K8sResourceHandler) collectCloudResources(ctx context.Context, sessionObj *cautils.OPASessionObj, allResources map[string]workloadinterface.IMetadata, ksResourceMap cautils.KSResources, cloudResources []string, progressListener opaprocessor.IJobProgressNotificationClient) error {
func (k8sHandler *K8sResourceHandler) collectCloudResources(ctx context.Context, sessionObj *cautils.OPASessionObj, allResources map[string]workloadinterface.IMetadata, externalResourceMap cautils.ExternalResources, cloudResources []string, progressListener opaprocessor.IJobProgressNotificationClient) error {
clusterName := cautils.ClusterName
provider := cloudsupport.GetCloudProvider(clusterName)
if provider == "" {
@@ -247,13 +245,13 @@ func (k8sHandler *K8sResourceHandler) collectCloudResources(ctx context.Context,
}
allResources[wl.GetID()] = wl
ksResourceMap[fmt.Sprintf("%s/%s", wl.GetApiVersion(), wl.GetKind())] = []string{wl.GetID()}
externalResourceMap[fmt.Sprintf("%s/%s", wl.GetApiVersion(), wl.GetKind())] = []string{wl.GetID()}
}
logger.L().Success("Downloaded cloud resources")
// get api server info resource
if cloudResourceRequired(cloudResources, string(cloudsupport.TypeApiServerInfo)) {
if err := k8sHandler.collectAPIServerInfoResource(allResources, ksResourceMap); err != nil {
if err := k8sHandler.collectAPIServerInfoResource(allResources, externalResourceMap); err != nil {
logger.L().Ctx(ctx).Warning("failed to collect api server info resource", helpers.Error(err))
return err
@@ -272,14 +270,14 @@ func cloudResourceRequired(cloudResources []string, resource string) bool {
return false
}
func (k8sHandler *K8sResourceHandler) collectAPIServerInfoResource(allResources map[string]workloadinterface.IMetadata, ksResourceMap cautils.KSResources) error {
func (k8sHandler *K8sResourceHandler) collectAPIServerInfoResource(allResources map[string]workloadinterface.IMetadata, externalResourceMap cautils.ExternalResources) error {
clusterAPIServerInfo, err := k8sHandler.k8s.DiscoveryClient.ServerVersion()
if err != nil {
return err
}
resource := cloudsupport.NewApiServerVersionInfo(clusterAPIServerInfo)
allResources[resource.GetID()] = resource
ksResourceMap[fmt.Sprintf("%s/%s", resource.GetApiVersion(), resource.GetKind())] = []string{resource.GetID()}
externalResourceMap[fmt.Sprintf("%s/%s", resource.GetApiVersion(), resource.GetKind())] = []string{resource.GetID()}
return nil
}
@@ -317,15 +315,15 @@ func setMapNamespaceToNumOfResources(ctx context.Context, allResources map[strin
sessionObj.SetMapNamespaceToNumberOfResources(mapNamespaceToNumberOfResources)
}
func (k8sHandler *K8sResourceHandler) pullResources(queryableResources QueryableResources) (cautils.K8SResources, map[string]workloadinterface.IMetadata, error) {
func (k8sHandler *K8sResourceHandler) pullResources(queryableResources QueryableResources, globalFieldSelectors IFieldSelector) (cautils.K8SResources, map[string]workloadinterface.IMetadata, error) {
k8sResources := queryableResources.ToK8sResourceMap()
allResources := map[string]workloadinterface.IMetadata{}
var errs error
for _, qr := range queryableResources {
apiGroup, apiVersion, resource := k8sinterface.StringToResourceGroup(qr.GroupVersionResourceTriplet)
for i := range queryableResources {
apiGroup, apiVersion, resource := k8sinterface.StringToResourceGroup(queryableResources[i].GroupVersionResourceTriplet)
gvr := schema.GroupVersionResource{Group: apiGroup, Version: apiVersion, Resource: resource}
result, err := k8sHandler.pullSingleResource(&gvr, nil, qr.FieldSelectors)
result, err := k8sHandler.pullSingleResource(&gvr, nil, queryableResources[i].FieldSelectors, globalFieldSelectors)
if err != nil {
if !strings.Contains(err.Error(), "the server could not find the requested resource") {
// handle error
@@ -343,7 +341,7 @@ func (k8sHandler *K8sResourceHandler) pullResources(queryableResources Queryable
allResources[metaObjs[i].GetID()] = metaObjs[i]
}
key := qr.GroupVersionResourceTriplet
key := queryableResources[i].GroupVersionResourceTriplet
if _, ok := k8sResources[key]; !ok {
k8sResources[key] = workloadinterface.ListMetaIDs(metaObjs)
} else {
@@ -353,11 +351,11 @@ func (k8sHandler *K8sResourceHandler) pullResources(queryableResources Queryable
return k8sResources, allResources, errs
}
func (k8sHandler *K8sResourceHandler) pullSingleResource(resource *schema.GroupVersionResource, labels map[string]string, fields string) ([]unstructured.Unstructured, error) {
func (k8sHandler *K8sResourceHandler) pullSingleResource(resource *schema.GroupVersionResource, labels map[string]string, fields string, fieldSelector IFieldSelector) ([]unstructured.Unstructured, error) {
resourceList := []unstructured.Unstructured{}
// set labels
listOptions := metav1.ListOptions{}
fieldSelectors := k8sHandler.fieldSelector.GetNamespacesSelectors(resource)
fieldSelectors := fieldSelector.GetNamespacesSelectors(resource)
for i := range fieldSelectors {
if fieldSelectors[i] != "" {
listOptions.FieldSelector = combineFieldSelectors(fieldSelectors[i], fields)
@@ -396,7 +394,7 @@ func ConvertMapListToMeta(resourceMap []map[string]interface{}) []workloadinterf
return workloads
}
func (k8sHandler *K8sResourceHandler) collectHostResources(ctx context.Context, allResources map[string]workloadinterface.IMetadata, ksResourceMap cautils.KSResources) (map[string]apis.StatusInfo, error) {
func (k8sHandler *K8sResourceHandler) collectHostResources(ctx context.Context, allResources map[string]workloadinterface.IMetadata, externalResourceMap cautils.ExternalResources) (map[string]apis.StatusInfo, error) {
logger.L().Debug("Collecting host scanner resources")
hostResources, infoMap, err := k8sHandler.hostSensorHandler.CollectResources(ctx)
if err != nil {
@@ -408,11 +406,11 @@ func (k8sHandler *K8sResourceHandler) collectHostResources(ctx context.Context,
groupResource := k8sinterface.JoinResourceTriplets(group, version, hostResources[rscIdx].GetKind())
allResources[hostResources[rscIdx].GetID()] = &hostResources[rscIdx]
grpResourceList, ok := ksResourceMap[groupResource]
grpResourceList, ok := externalResourceMap[groupResource]
if !ok {
grpResourceList = make([]string, 0)
}
ksResourceMap[groupResource] = append(grpResourceList, hostResources[rscIdx].GetID())
externalResourceMap[groupResource] = append(grpResourceList, hostResources[rscIdx].GetID())
}
return infoMap, nil
}

View File

@@ -51,10 +51,10 @@ var (
}
)
func isEmptyImgVulns(ksResourcesMap cautils.KSResources) bool {
imgVulnResources := cautils.MapImageVulnResources(ksResourcesMap)
func isEmptyImgVulns(externalResourcesMap cautils.ExternalResources) bool {
imgVulnResources := cautils.MapImageVulnResources(externalResourcesMap)
for _, resource := range imgVulnResources {
if val, ok := ksResourcesMap[resource]; ok {
if val, ok := externalResourcesMap[resource]; ok {
if len(val) > 0 {
return false
}
@@ -63,20 +63,20 @@ func isEmptyImgVulns(ksResourcesMap cautils.KSResources) bool {
return true
}
func setKSResourceMap(frameworks []reporthandling.Framework, resourceToControl map[string][]string) cautils.KSResources {
ksResources := make(cautils.KSResources)
func setKSResourceMap(frameworks []reporthandling.Framework, resourceToControl map[string][]string) cautils.ExternalResources {
externalResources := make(cautils.ExternalResources)
complexMap := setComplexKSResourceMap(frameworks, resourceToControl)
for group := range complexMap {
for version := range complexMap[group] {
for resource := range complexMap[group][version] {
groupResources := k8sinterface.ResourceGroupToString(group, version, resource)
for _, groupResource := range groupResources {
ksResources[groupResource] = nil
externalResources[groupResource] = nil
}
}
}
}
return ksResources
return externalResources
}
// [group][versionn][resource]
@@ -152,3 +152,14 @@ func getGroupNVersion(apiVersion string) (string, string) {
}
return group, version
}
func getFieldSelectorFromScanInfo(scanInfo *cautils.ScanInfo) IFieldSelector {
if scanInfo.IncludeNamespaces != "" {
return NewIncludeSelector(scanInfo.IncludeNamespaces)
}
if scanInfo.ExcludedNamespaces != "" {
return NewExcludeSelector(scanInfo.ExcludedNamespaces)
}
return &EmptySelector{}
}

View File

@@ -8,14 +8,14 @@ import (
)
func TestSsEmptyImgVulns(t *testing.T) {
ksResourcesMap := make(cautils.KSResources, 0)
ksResourcesMap["container.googleapis.com/v1"] = []string{"fsdfds"}
assert.Equal(t, true, isEmptyImgVulns(ksResourcesMap))
externalResourcesMap := make(cautils.ExternalResources, 0)
externalResourcesMap["container.googleapis.com/v1"] = []string{"fsdfds"}
assert.Equal(t, true, isEmptyImgVulns(externalResourcesMap))
ksResourcesMap["armo.vuln.images/v1/ImageVulnerabilities"] = []string{"dada"}
assert.Equal(t, false, isEmptyImgVulns(ksResourcesMap))
externalResourcesMap["armo.vuln.images/v1/ImageVulnerabilities"] = []string{"dada"}
assert.Equal(t, false, isEmptyImgVulns(externalResourcesMap))
ksResourcesMap["armo.vuln.images/v1/ImageVulnerabilities"] = []string{}
ksResourcesMap["bla"] = []string{"blu"}
assert.Equal(t, true, isEmptyImgVulns(ksResourcesMap))
externalResourcesMap["armo.vuln.images/v1/ImageVulnerabilities"] = []string{}
externalResourcesMap["bla"] = []string{"blu"}
assert.Equal(t, true, isEmptyImgVulns(externalResourcesMap))
}

View File

@@ -1,7 +1,7 @@
package resourcehandler
import (
"fmt"
"strings"
"github.com/kubescape/kubescape/v2/core/cautils"
)
@@ -20,7 +20,7 @@ func (qr *QueryableResource) String() string {
if qr.FieldSelectors == "" {
return qr.GroupVersionResourceTriplet
}
return fmt.Sprintf("%s/%s", qr.GroupVersionResourceTriplet, qr.FieldSelectors)
return strings.Join([]string{qr.GroupVersionResourceTriplet, qr.FieldSelectors}, "/")
}
func (qr *QueryableResource) Copy() QueryableResource {

View File

@@ -40,7 +40,7 @@ func NewRegistryAdaptors() (*RegistryAdaptors, error) {
return registryAdaptors, nil
}
func (registryAdaptors *RegistryAdaptors) collectImagesVulnerabilities(k8sResourcesMap cautils.K8SResources, allResources map[string]workloadinterface.IMetadata, ksResourceMap cautils.KSResources) error {
func (registryAdaptors *RegistryAdaptors) collectImagesVulnerabilities(k8sResourcesMap cautils.K8SResources, allResources map[string]workloadinterface.IMetadata, externalResourceMap cautils.ExternalResources) error {
logger.L().Debug("Collecting images vulnerabilities")
if len(registryAdaptors.adaptors) == 0 {
@@ -80,7 +80,7 @@ func (registryAdaptors *RegistryAdaptors) collectImagesVulnerabilities(k8sResour
for i := range metaObjs {
allResources[metaObjs[i].GetID()] = metaObjs[i]
}
ksResourceMap[k8sinterface.JoinResourceTriplets(ImagevulnerabilitiesObjectGroup, ImagevulnerabilitiesObjectVersion, ImagevulnerabilitiesObjectKind)] = workloadinterface.ListMetaIDs(metaObjs)
externalResourceMap[k8sinterface.JoinResourceTriplets(ImagevulnerabilitiesObjectGroup, ImagevulnerabilitiesObjectVersion, ImagevulnerabilitiesObjectKind)] = workloadinterface.ListMetaIDs(metaObjs)
return nil
}

View File

@@ -147,7 +147,7 @@ type (
// It may be unmarshaled from a JSON fixture.
mockableOPASessionObj struct {
K8SResources cautils.K8SResources
KubescapeResource cautils.KSResources
ExternalResources cautils.ExternalResources
AllPolicies *cautils.Policies
AllResources map[string]*workloadinterface.Workload
ResourcesResult map[string]resourcesresults.Result
@@ -194,7 +194,7 @@ func mockOPASessionObj(t testing.TB) *cautils.OPASessionObj {
o := cautils.OPASessionObj{
K8SResources: v.K8SResources,
KubescapeResource: v.KubescapeResource,
ExternalResources: v.ExternalResources,
AllPolicies: v.AllPolicies,
//AllResources map[string]*workloadinterface.Workload // all scanned resources, map[<resource ID>]<resource>
ResourcesResult: v.ResourcesResult,