mirror of
https://github.com/kubescape/kubescape.git
synced 2026-04-15 06:58:11 +00:00
Merge pull request #213 from dwertent/master
support include namespaces
This commit is contained in:
18
README.md
18
README.md
@@ -78,6 +78,7 @@ Set-ExecutionPolicy RemoteSigned -scope CurrentUser
|
||||
| flag | default | description | options |
|
||||
|-----------------------------|---------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------|
|
||||
| `-e`/`--exclude-namespaces` | Scan all namespaces | Namespaces to exclude from scanning. Recommended to exclude `kube-system` and `kube-public` namespaces | |
|
||||
| `--include-namespaces` | Scan all namespaces | Scan specific namespaces | |
|
||||
| `-s`/`--silent` | Display progress messages | Silent progress messages | |
|
||||
| `-t`/`--fail-threshold` | `0` (do not fail) | fail command (return exit code 1) if result bellow threshold | `0` -> `100` |
|
||||
| `-f`/`--format` | `pretty-printer` | Output format | `pretty-printer`/`json`/`junit`/`prometheus` |
|
||||
@@ -110,12 +111,21 @@ kubescape scan framework mitre --submit
|
||||
kubescape scan control "Privileged container"
|
||||
```
|
||||
|
||||
* Scan specific namespaces
|
||||
```
|
||||
kubescape scan framework nsa --include-namespaces development,staging,production
|
||||
```
|
||||
|
||||
* Scan cluster and exclude some namespaces
|
||||
```
|
||||
kubescape scan framework nsa --exclude-namespaces kube-system,kube-public
|
||||
```
|
||||
|
||||
* Scan local `yaml`/`json` files before deploying. [Take a look at the demonstration](https://youtu.be/Ox6DaR7_4ZI)
|
||||
```
|
||||
kubescape scan framework nsa *.yaml
|
||||
```
|
||||
|
||||
|
||||
* Scan kubernetes manifest files from a public github repository
|
||||
```
|
||||
kubescape scan framework nsa https://github.com/armosec/kubescape
|
||||
@@ -123,17 +133,17 @@ kubescape scan framework nsa https://github.com/armosec/kubescape
|
||||
|
||||
* Output in `json` format
|
||||
```
|
||||
kubescape scan framework nsa --exclude-namespaces kube-system,kube-public --format json --output results.json
|
||||
kubescape scan framework nsa --format json --output results.json
|
||||
```
|
||||
|
||||
* Output in `junit xml` format
|
||||
```
|
||||
kubescape scan framework nsa --exclude-namespaces kube-system,kube-public --format junit --output results.xml
|
||||
kubescape scan framework nsa --format junit --output results.xml
|
||||
```
|
||||
|
||||
* Output in `prometheus` metrics format
|
||||
```
|
||||
kubescape scan framework nsa --exclude-namespaces kube-system,kube-public --format prometheus
|
||||
kubescape scan framework nsa --format prometheus
|
||||
```
|
||||
|
||||
* Scan with exceptions, objects with exceptions will be presented as `exclude` and not `fail`
|
||||
|
||||
@@ -19,6 +19,7 @@ type ScanInfo struct {
|
||||
Format string // Format results (table, json, junit ...)
|
||||
Output string // Store results in an output file, Output file name
|
||||
ExcludedNamespaces string // DEPRECATED?
|
||||
IncludeNamespaces string // DEPRECATED?
|
||||
InputPatterns []string // Yaml files input patterns
|
||||
Silent bool // Silent mode - Do not print progress logs
|
||||
FailThreshold uint16 // Failure score threshold
|
||||
|
||||
@@ -37,7 +37,8 @@ func init() {
|
||||
rootCmd.AddCommand(scanCmd)
|
||||
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Submit, "submit", "", false, "Send the scan results to Armo management portal where you can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more. By default the results are not submitted")
|
||||
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Local, "keep-local", "", false, "If you do not want your Kubescape results reported to Armo backend. Use this flag if you ran with the '--submit' flag in the past and you do not want to submit your current scan results")
|
||||
scanCmd.PersistentFlags().StringVarP(&scanInfo.ExcludedNamespaces, "exclude-namespaces", "e", "", "Namespaces to exclude from scanning. Recommended: kube-system, kube-public")
|
||||
scanCmd.PersistentFlags().StringVarP(&scanInfo.ExcludedNamespaces, "exclude-namespaces", "e", "", "Namespaces to exclude from scanning. Recommended: kube-system,kube-public")
|
||||
scanCmd.PersistentFlags().StringVar(&scanInfo.IncludeNamespaces, "include-namespaces", "", "scan specific namespaces. e.g: --include-namespaces ns-a,ns-b")
|
||||
scanCmd.PersistentFlags().StringVarP(&scanInfo.Format, "format", "f", "pretty-printer", `Output format. Supported formats: "pretty-printer"/"json"/"junit"/"prometheus"`)
|
||||
scanCmd.PersistentFlags().StringVarP(&scanInfo.Output, "output", "o", "", "Output file. Print output to file and not stdout")
|
||||
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Silent, "silent", "s", false, "Silent progress messages")
|
||||
|
||||
@@ -39,6 +39,17 @@ func getReporter(scanInfo *cautils.ScanInfo) reporter.IReport {
|
||||
|
||||
return reporter.NewReportEventReceiver("", "")
|
||||
}
|
||||
|
||||
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 getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
|
||||
var resourceHandler resourcehandler.IResourceHandler
|
||||
var clusterConfig cautils.IClusterConfig
|
||||
@@ -55,7 +66,7 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
|
||||
reportHandler = reporter.NewReportMock()
|
||||
} else {
|
||||
k8s := k8sinterface.NewKubernetesApi()
|
||||
resourceHandler = resourcehandler.NewK8sResourceHandler(k8s, scanInfo.ExcludedNamespaces)
|
||||
resourceHandler = resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo))
|
||||
clusterConfig = cautils.ClusterConfigSetup(scanInfo, k8s, getter.GetArmoAPIConnector())
|
||||
|
||||
// setup reporter
|
||||
|
||||
76
resourcehandler/fieldselector.go
Normal file
76
resourcehandler/fieldselector.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package resourcehandler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/k8s-interface/k8sinterface"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type IFieldSelector interface {
|
||||
GetNamespacesSelector(*schema.GroupVersionResource) string
|
||||
}
|
||||
|
||||
type EmptySelector struct {
|
||||
}
|
||||
|
||||
func (es *EmptySelector) GetNamespacesSelector(resource *schema.GroupVersionResource) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type ExcludeSelector struct {
|
||||
namespace string
|
||||
}
|
||||
|
||||
func NewExcludeSelector(ns string) *ExcludeSelector {
|
||||
return &ExcludeSelector{namespace: ns}
|
||||
}
|
||||
|
||||
type IncludeSelector struct {
|
||||
namespace string
|
||||
}
|
||||
|
||||
func NewIncludeSelector(ns string) *IncludeSelector {
|
||||
return &IncludeSelector{namespace: ns}
|
||||
}
|
||||
func (es *ExcludeSelector) GetNamespacesSelector(resource *schema.GroupVersionResource) string {
|
||||
return getNamespacesSelector(resource, es.namespace, "!=")
|
||||
}
|
||||
|
||||
func (is *IncludeSelector) GetNamespacesSelector(resource *schema.GroupVersionResource) string {
|
||||
return getNamespacesSelector(resource, is.namespace, "==")
|
||||
}
|
||||
|
||||
func getNamespacesSelector(resource *schema.GroupVersionResource, ns, operator string) string {
|
||||
fieldSelectors := ""
|
||||
fieldSelector := "metadata."
|
||||
if resource.Resource == "namespaces" {
|
||||
fieldSelector += "name"
|
||||
} else if k8sinterface.IsNamespaceScope(resource.Group, resource.Resource) {
|
||||
fieldSelector += "namespace"
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
namespacesSlice := strings.Split(ns, ",")
|
||||
for _, n := range namespacesSlice {
|
||||
fieldSelectors += fmt.Sprintf("%s!=%s,", fieldSelector, n)
|
||||
}
|
||||
return fieldSelectors
|
||||
|
||||
}
|
||||
func setFieldSelector(listOptions *metav1.ListOptions, resource *schema.GroupVersionResource, excludedNamespaces string) {
|
||||
fieldSelector := "metadata."
|
||||
if resource.Resource == "namespaces" {
|
||||
fieldSelector += "name"
|
||||
} else if k8sinterface.IsNamespaceScope(resource.Group, resource.Resource) {
|
||||
fieldSelector += "namespace"
|
||||
} else {
|
||||
return
|
||||
}
|
||||
excludedNamespacesSlice := strings.Split(excludedNamespaces, ",")
|
||||
for _, excludedNamespace := range excludedNamespacesSlice {
|
||||
listOptions.FieldSelector += fmt.Sprintf("%s!=%s,", fieldSelector, excludedNamespace)
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package resourcehandler
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
@@ -21,14 +20,14 @@ import (
|
||||
)
|
||||
|
||||
type K8sResourceHandler struct {
|
||||
k8s *k8sinterface.KubernetesApi
|
||||
excludedNamespaces string // excluded namespaces (separated by comma)
|
||||
k8s *k8sinterface.KubernetesApi
|
||||
fieldSelector IFieldSelector
|
||||
}
|
||||
|
||||
func NewK8sResourceHandler(k8s *k8sinterface.KubernetesApi, excludedNamespaces string) *K8sResourceHandler {
|
||||
func NewK8sResourceHandler(k8s *k8sinterface.KubernetesApi, fieldSelector IFieldSelector) *K8sResourceHandler {
|
||||
return &K8sResourceHandler{
|
||||
k8s: k8s,
|
||||
excludedNamespaces: excludedNamespaces,
|
||||
k8s: k8s,
|
||||
fieldSelector: fieldSelector,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +42,7 @@ func (k8sHandler *K8sResourceHandler) GetResources(frameworks []reporthandling.F
|
||||
_, namespace, labels := armotypes.DigestPortalDesignator(designator)
|
||||
|
||||
// pull k8s recourses
|
||||
if err := k8sHandler.pullResources(k8sResourcesMap, namespace, labels, k8sHandler.excludedNamespaces); err != nil {
|
||||
if err := k8sHandler.pullResources(k8sResourcesMap, namespace, labels); err != nil {
|
||||
return k8sResourcesMap, err
|
||||
}
|
||||
|
||||
@@ -59,13 +58,13 @@ func (k8sHandler *K8sResourceHandler) GetClusterAPIServerInfo() *version.Info {
|
||||
}
|
||||
return clusterAPIServerInfo
|
||||
}
|
||||
func (k8sHandler *K8sResourceHandler) pullResources(k8sResources *cautils.K8SResources, namespace string, labels map[string]string, excludedNamespaces string) error {
|
||||
func (k8sHandler *K8sResourceHandler) pullResources(k8sResources *cautils.K8SResources, namespace string, labels map[string]string) error {
|
||||
|
||||
var errs error
|
||||
for groupResource := range *k8sResources {
|
||||
apiGroup, apiVersion, resource := k8sinterface.StringToResourceGroup(groupResource)
|
||||
gvr := schema.GroupVersionResource{Group: apiGroup, Version: apiVersion, Resource: resource}
|
||||
result, err := k8sHandler.pullSingleResource(&gvr, namespace, labels, excludedNamespaces)
|
||||
result, err := k8sHandler.pullSingleResource(&gvr, namespace, labels)
|
||||
if err != nil {
|
||||
// handle error
|
||||
if errs == nil {
|
||||
@@ -81,13 +80,13 @@ func (k8sHandler *K8sResourceHandler) pullResources(k8sResources *cautils.K8SRes
|
||||
return errs
|
||||
}
|
||||
|
||||
func (k8sHandler *K8sResourceHandler) pullSingleResource(resource *schema.GroupVersionResource, namespace string, labels map[string]string, excludedNamespaces string) ([]unstructured.Unstructured, error) {
|
||||
func (k8sHandler *K8sResourceHandler) pullSingleResource(resource *schema.GroupVersionResource, namespace string, labels map[string]string) ([]unstructured.Unstructured, error) {
|
||||
|
||||
// set labels
|
||||
listOptions := metav1.ListOptions{}
|
||||
if excludedNamespaces != "" {
|
||||
setFieldSelector(&listOptions, resource, excludedNamespaces)
|
||||
}
|
||||
|
||||
listOptions.FieldSelector += k8sHandler.fieldSelector.GetNamespacesSelector(resource)
|
||||
|
||||
if len(labels) > 0 {
|
||||
set := k8slabels.Set(labels)
|
||||
listOptions.LabelSelector = set.AsSelector().String()
|
||||
@@ -110,18 +109,3 @@ func (k8sHandler *K8sResourceHandler) pullSingleResource(resource *schema.GroupV
|
||||
return result.Items, nil
|
||||
|
||||
}
|
||||
|
||||
func setFieldSelector(listOptions *metav1.ListOptions, resource *schema.GroupVersionResource, excludedNamespaces string) {
|
||||
fieldSelector := "metadata."
|
||||
if resource.Resource == "namespaces" {
|
||||
fieldSelector += "name"
|
||||
} else if k8sinterface.IsNamespaceScope(resource.Group, resource.Resource) {
|
||||
fieldSelector += "namespace"
|
||||
} else {
|
||||
return
|
||||
}
|
||||
excludedNamespacesSlice := strings.Split(excludedNamespaces, ",")
|
||||
for _, excludedNamespace := range excludedNamespacesSlice {
|
||||
listOptions.FieldSelector += fmt.Sprintf("%s!=%s,", fieldSelector, excludedNamespace)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user