From 44803ab91561189d518223124417f8f95973054a Mon Sep 17 00:00:00 2001
From: David Wertenteil <64066841+dwertent@users.noreply.github.com>
Date: Mon, 30 Aug 2021 11:06:13 +0300
Subject: [PATCH] Support glob file loading (#41)
* recursive glob
* adding helm support to readme
* update glob function
---
README.md | 14 +++++++++-
policyhandler/filesloader.go | 46 +++++++++++++++++++++++++------
policyhandler/filesloader_test.go | 2 +-
policyhandler/urlloader.go | 3 --
4 files changed, 52 insertions(+), 13 deletions(-)
diff --git a/README.md b/README.md
index cf66bb23..4a414e09 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
[](https://github.com/armosec/kubescape/actions/workflows/build.yaml)
-[]()
+[](https://github.com/armosec/kubescape)
[](https://goreportcard.com/report/github.com/armosec/kubescape)
Kubescape is the first tool for testing if Kubernetes is deployed securely as defined in [Kubernetes Hardening Guidance by NSA and CISA](https://www.nsa.gov/News-Features/Feature-Stories/Article-View/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/)
@@ -77,6 +77,18 @@ kubescape scan framework nsa --exclude-namespaces kube-system,kube-public --sile
kubescape scan framework nsa --exclude-namespaces kube-system,kube-public --silence -o junit > results.xml
```
+### Helm Support
+
+1. Render the helm template to an output yaml
+```
+helm template [CHART] [flags] --generate-name --dry-run --output-dir helm-output
+```
+
+2. Run `kubescape` with rended yaml files
+```
+kubescape scan framework nsa helm-output/*
+```
+
# How to build
Note: development (and the release process) is done with Go `1.16`
diff --git a/policyhandler/filesloader.go b/policyhandler/filesloader.go
index e65a608e..57969138 100644
--- a/policyhandler/filesloader.go
+++ b/policyhandler/filesloader.go
@@ -8,6 +8,7 @@ import (
"kubescape/cautils"
"kubescape/cautils/k8sinterface"
"kubescape/cautils/opapolicy"
+ "os"
"path/filepath"
"strings"
@@ -38,7 +39,7 @@ func (policyHandler *PolicyHandler) loadResources(frameworks []opapolicy.Framewo
workloads = append(workloads, w...)
}
- // load resource from url
+ // load resources from url
w, err = loadResourcesFromUrl(scanInfo.InputPatterns)
if err != nil {
return nil, err
@@ -47,11 +48,15 @@ func (policyHandler *PolicyHandler) loadResources(frameworks []opapolicy.Framewo
workloads = append(workloads, w...)
}
+ if len(workloads) == 0 {
+ return nil, fmt.Errorf("empty list of workloads - no workloads found")
+ }
+
// map all resources: map["/group/version/kind"][]
allResources := mapResources(workloads)
// build resources map
- // map resources based on framework requrid resources: map["/group/version/kind"][]
+ // map resources based on framework required resources: map["/group/version/kind"][]
k8sResources := setResourceMap(frameworks)
// save only relevant resources
@@ -78,9 +83,6 @@ func loadResourcesFromFiles(inputPatterns []string) ([]k8sinterface.IWorkload, e
if len(errs) > 0 {
cautils.ErrorDisplay(fmt.Sprintf("%v", errs)) // TODO - print error
}
- if len(workloads) == 0 {
- return workloads, fmt.Errorf("empty list of workloads - no workloads found")
- }
return workloads, nil
}
@@ -138,10 +140,11 @@ func readFile(fileContent []byte, fileFromat FileFormat) ([]k8sinterface.IWorklo
case JSON_FILE_FORMAT:
return readJsonFile(fileContent)
default:
- return nil, []error{fmt.Errorf("file extension %s not supported", fileFromat)}
+ return nil, nil // []error{fmt.Errorf("file extension %s not supported", fileFromat)}
}
}
+
func listFiles(patterns []string) ([]string, []error) {
files := []string{}
errs := []error{}
@@ -149,7 +152,11 @@ func listFiles(patterns []string) ([]string, []error) {
if strings.HasPrefix(patterns[i], "http") {
continue
}
- f, err := filepath.Glob(patterns[i])
+ if !filepath.IsAbs(patterns[i]) {
+ o, _ := os.Getwd()
+ patterns[i] = filepath.Join(o, patterns[i])
+ }
+ f, err := glob(filepath.Split(patterns[i])) //filepath.Glob(patterns[i])
if err != nil {
errs = append(errs, err)
} else {
@@ -209,7 +216,9 @@ func convertYamlToJson(i interface{}) interface{} {
case map[interface{}]interface{}:
m2 := map[string]interface{}{}
for k, v := range x {
- m2[k.(string)] = convertYamlToJson(v)
+ if s, ok := k.(string); ok {
+ m2[s] = convertYamlToJson(v)
+ }
}
return m2
case []interface{}:
@@ -220,6 +229,27 @@ func convertYamlToJson(i interface{}) interface{} {
return i
}
+func glob(root, pattern string) ([]string, error) {
+ var matches []string
+ err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+ if info.IsDir() {
+ return nil
+ }
+ if matched, err := filepath.Match(pattern, filepath.Base(path)); err != nil {
+ return err
+ } else if matched {
+ matches = append(matches, path)
+ }
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+ return matches, nil
+}
func isYaml(filePath string) bool {
return cautils.StringInSlice(YAML_PREFIX, filepath.Ext(filePath)) != cautils.ValueNotFound
}
diff --git a/policyhandler/filesloader_test.go b/policyhandler/filesloader_test.go
index c41b6993..40107267 100644
--- a/policyhandler/filesloader_test.go
+++ b/policyhandler/filesloader_test.go
@@ -31,7 +31,7 @@ func TestListFiles(t *testing.T) {
if len(errs) > 0 {
t.Error(errs)
}
- expected := 14
+ expected := 12
if len(files) != expected {
t.Errorf("wrong number of files, expected: %d, found: %d", expected, len(files))
}
diff --git a/policyhandler/urlloader.go b/policyhandler/urlloader.go
index d1de20db..9d2e14bf 100644
--- a/policyhandler/urlloader.go
+++ b/policyhandler/urlloader.go
@@ -20,9 +20,6 @@ func loadResourcesFromUrl(inputPatterns []string) ([]k8sinterface.IWorkload, err
if len(errs) > 0 {
cautils.ErrorDisplay(fmt.Sprintf("%v", errs)) // TODO - print error
}
- if len(workloads) == 0 {
- return workloads, fmt.Errorf("empty list of workloads - no workloads valid workloads found")
- }
return workloads, nil
}