Merge pull request #279 from armosec/dev

Cli improvements
This commit is contained in:
Rotem Refael
2021-12-22 21:16:54 +02:00
committed by GitHub
36 changed files with 486 additions and 401 deletions
+1 -1
View File
@@ -91,7 +91,7 @@ Set-ExecutionPolicy RemoteSigned -scope CurrentUser
| `-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 is below threshold | `0` -> `100` |
| `-t`/`--fail-threshold` | `100` (do not fail) | fail command (return exit code 1) if result is above threshold | `0` -> `100` |
| `-f`/`--format` | `pretty-printer` | Output format | `pretty-printer`/`json`/`junit`/`prometheus` |
| `-o`/`--output` | print to stdout | Save scan result in file | |
| `--use-from` | | Load local framework object from specified path. If not used will download latest | |
+4 -2
View File
@@ -180,7 +180,9 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken
}
}
if c.configObj.ClusterName == "" {
c.configObj.ClusterName = adoptClusterName(k8sinterface.GetClusterName())
c.configObj.ClusterName = AdoptClusterName(k8sinterface.GetClusterName())
} else { // override the cluster name if it has unwanted characters
c.configObj.ClusterName = AdoptClusterName(c.configObj.ClusterName)
}
return c
@@ -425,6 +427,6 @@ func DeleteConfigFile() error {
return os.Remove(ConfigFileFullPath())
}
func adoptClusterName(clusterName string) string {
func AdoptClusterName(clusterName string) string {
return strings.ReplaceAll(clusterName, "/", "-")
}
+2 -2
View File
@@ -51,8 +51,8 @@ func ErrorDisplay(str string) {
if IsSilent() {
return
}
SuccessDisplay(os.Stdout, "[Error] ")
SimpleDisplay(os.Stdout, fmt.Sprintf("%s\n", str))
FailureDisplay(os.Stderr, "[Error] ")
SimpleDisplay(os.Stderr, fmt.Sprintf("%s\n", str))
}
+6 -3
View File
@@ -65,6 +65,9 @@ func NewVersionCheckRequest(buildNumber, frameworkName, frameworkVersion, scanni
if buildNumber == "" {
buildNumber = UnknownBuildNumber
}
if scanningTarget == "" {
scanningTarget = "unknown"
}
return &VersionCheckRequest{
Client: "kubescape",
ClientVersion: buildNumber,
@@ -82,7 +85,7 @@ func (v *VersionCheckHandlerMock) CheckLatestVersion(versionData *VersionCheckRe
func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckRequest) error {
defer func() {
if err := recover(); err != nil {
fmt.Println("failed to get latest version")
WarningDisplay(os.Stderr, "failed to get latest version\n")
}
}()
@@ -93,7 +96,7 @@ func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckReques
if latestVersion.ClientUpdate != "" {
if BuildNumber != "" && BuildNumber < latestVersion.ClientUpdate {
fmt.Println(warningMessage(latestVersion.Client, latestVersion.ClientUpdate))
WarningDisplay(os.Stderr, warningMessage(latestVersion.Client, latestVersion.ClientUpdate), "\n")
}
}
@@ -103,7 +106,7 @@ func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckReques
// }
if latestVersion.Message != "" {
fmt.Println(latestVersion.Message)
InfoDisplay(os.Stderr, latestVersion.Message, "\n")
}
return nil
+58 -36
View File
@@ -2,6 +2,7 @@ package cmd
import (
"fmt"
"os"
"strings"
"github.com/armosec/kubescape/cautils"
@@ -25,43 +26,10 @@ var downloadCmd = &cobra.Command{
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if strings.EqualFold(args[0], "framework") {
downloadInfo.FrameworkName = strings.ToLower(args[1])
g := getter.NewDownloadReleasedPolicy()
if err := g.SetRegoObjects(); err != nil {
return err
}
if downloadInfo.Path == "" {
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.FrameworkName + ".json")
}
frameworks, err := g.GetFramework(downloadInfo.FrameworkName)
if err != nil {
return err
}
err = getter.SaveFrameworkInFile(frameworks, downloadInfo.Path)
if err != nil {
return err
}
} else if strings.EqualFold(args[0], "control") {
downloadInfo.ControlName = strings.ToLower(args[1])
g := getter.NewDownloadReleasedPolicy()
if err := g.SetRegoObjects(); err != nil {
return err
}
if downloadInfo.Path == "" {
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.ControlName + ".json")
}
controls, err := g.GetControl(downloadInfo.ControlName)
if err != nil {
return err
}
err = getter.SaveControlInFile(controls, downloadInfo.Path)
if err != nil {
return err
}
if err := download(args); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
return nil
},
}
@@ -71,3 +39,57 @@ func init() {
downloadInfo = cautils.DownloadInfo{}
downloadCmd.Flags().StringVarP(&downloadInfo.Path, "output", "o", "", "Output file. If specified, will store save to `~/.kubescape/<framework name>.json`")
}
func download(args []string) error {
switch strings.ToLower(args[0]) {
case "framework":
return downloadFramework(args[1])
case "control":
return downloadControl(args[1])
// case "exceptions":
// case "artifacts":
default:
return fmt.Errorf("unknown command to download")
}
}
func downloadFramework(frameworkName string) error {
downloadInfo.FrameworkName = strings.ToLower(frameworkName)
g := getter.NewDownloadReleasedPolicy()
if err := g.SetRegoObjects(); err != nil {
return err
}
if downloadInfo.Path == "" {
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.FrameworkName + ".json")
}
frameworks, err := g.GetFramework(downloadInfo.FrameworkName)
if err != nil {
return err
}
err = getter.SaveFrameworkInFile(frameworks, downloadInfo.Path)
if err != nil {
return err
}
return nil
}
func downloadControl(controlName string) error {
downloadInfo.ControlName = strings.ToLower(controlName)
g := getter.NewDownloadReleasedPolicy()
if err := g.SetRegoObjects(); err != nil {
return err
}
if downloadInfo.Path == "" {
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.ControlName + ".json")
}
controls, err := g.GetControl(downloadInfo.ControlName)
if err != nil {
return err
}
err = getter.SaveControlInFile(controls, downloadInfo.Path)
if err != nil {
return err
}
return nil
}
+2 -1
View File
@@ -90,7 +90,8 @@ var frameworkCmd = &cobra.Command{
cautils.SetSilentMode(scanInfo.Silent)
err := clihandler.ScanCliSetup(&scanInfo)
if err != nil {
return err
fmt.Fprintf(os.Stderr, "error: %v\n\n", err)
os.Exit(1)
}
return nil
},
+2 -2
View File
@@ -39,7 +39,7 @@ func init() {
scanCmd.PersistentFlags().StringVar(&scanInfo.ControlsInputs, "controls-config", "", "Path to an controls-config obj. If not set will download controls-config from ARMO management portal")
scanCmd.PersistentFlags().StringVar(&scanInfo.UseExceptions, "exceptions", "", "Path to an exceptions obj. If not set will download exceptions from ARMO management portal")
scanCmd.PersistentFlags().StringVarP(&scanInfo.ExcludedNamespaces, "exclude-namespaces", "e", "", "Namespaces to exclude from scanning. Recommended: kube-system,kube-public")
scanCmd.PersistentFlags().Uint16VarP(&scanInfo.FailThreshold, "fail-threshold", "t", 0, "Failure threshold is the percent below which the command fails and returns exit code 1")
scanCmd.PersistentFlags().Uint16VarP(&scanInfo.FailThreshold, "fail-threshold", "t", 100, "Failure threshold is the percent above which the command fails and returns exit code 1")
scanCmd.PersistentFlags().StringVarP(&scanInfo.Format, "format", "f", "pretty-printer", `Output format. Supported formats: "pretty-printer"/"json"/"junit"/"prometheus"`)
scanCmd.PersistentFlags().StringVar(&scanInfo.IncludeNamespaces, "include-namespaces", "", "scan specific namespaces. e.g: --include-namespaces ns-a,ns-b")
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")
@@ -50,7 +50,7 @@ func init() {
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Silent, "silent", "s", false, "Silent progress messages")
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")
hostF := scanCmd.PersistentFlags().VarPF(&scanInfo.HostSensor, "enable-host-scan", "", "Deploy ARMO K8s host-sensor daemonset in the scanned cluster. Deleting it right after we collecting the data. Required to collect valueable data from cluster nodes for certain controls")
hostF := scanCmd.PersistentFlags().VarPF(&scanInfo.HostSensor, "enable-host-scan", "", "Deploy ARMO K8s host-sensor daemonset in the scanned cluster. Deleting it right after we collecting the data. Required to collect valueable data from cluster nodes for certain controls")
hostF.NoOptDefVal = "true"
hostF.DefValue = "false, for no TTY in stdin"
}
+33 -71
View File
@@ -6,9 +6,7 @@ import (
"os"
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/clihandler/cliinterfaces"
"github.com/armosec/kubescape/hostsensorutils"
"github.com/armosec/kubescape/opaprocessor"
@@ -18,8 +16,6 @@ import (
"github.com/armosec/kubescape/resultshandling/printer"
"github.com/armosec/kubescape/resultshandling/reporter"
"github.com/armosec/opa-utils/reporthandling"
"github.com/armosec/rbac-utils/rbacscanner"
"github.com/golang/glog"
"github.com/mattn/go-isatty"
)
@@ -31,60 +27,36 @@ type componentInterfaces struct {
hostSensorHandler hostsensorutils.IHostSensor
}
func initHostSensor(scanInfo *cautils.ScanInfo, k8s *k8sinterface.KubernetesApi) hostsensorutils.IHostSensor {
hasHostSensorControls := true
// we need to determined which controls needs host sensor
if scanInfo.HostSensor.Get() == nil && hasHostSensorControls {
scanInfo.HostSensor.SetBool(askUserForHostSensor())
}
if hostSensorVal := scanInfo.HostSensor.Get(); hostSensorVal != nil && *hostSensorVal {
hostSensorHandler, err := hostsensorutils.NewHostSensorHandler(k8s)
if err != nil {
glog.Errorf("failed to create host sensor: %v", err)
return &hostsensorutils.HostSensorHandlerMock{}
}
return hostSensorHandler
}
return &hostsensorutils.HostSensorHandlerMock{}
}
func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
var resourceHandler resourcehandler.IResourceHandler
var hostSensorHandler hostsensorutils.IHostSensor
var tenantConfig cautils.ITenantConfig
hostSensorHandler = &hostsensorutils.HostSensorHandlerMock{}
k8s := getKubernetesApi(scanInfo)
// scanning environment
scanningTarget := scanInfo.GetScanningEnvironment()
switch scanningTarget {
case cautils.ScanLocalFiles:
k8sinterface.ConnectedToCluster = false // DEPRECATED ?
scanInfo.Local = true // do not submit results when scanning YAML files
tenantConfig := getTenantConfig(scanInfo, k8s)
// not scanning a cluster - use localConfig struct
tenantConfig = cautils.NewLocalConfig(getter.GetArmoAPIConnector(), scanInfo.Account)
// load resources from file
resourceHandler = resourcehandler.NewFileResourceHandler(scanInfo.InputPatterns)
case cautils.ScanCluster:
k8s := k8sinterface.NewKubernetesApi() // initialize kubernetes api object
// use clusterConfig struct
tenantConfig = cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account)
// pull k8s resources
hostSensorHandler = initHostSensor(scanInfo, k8s)
rbacObjects := cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, tenantConfig.GetCustomerGUID(), tenantConfig.GetClusterName()))
resourceHandler = resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo), hostSensorHandler, rbacObjects)
// Set submit behavior AFTER loading tenant config
setSubmitBehavior(scanInfo, tenantConfig)
hostSensorHandler := getHostSensorHandler(scanInfo, k8s)
if err := hostSensorHandler.Init(); err != nil {
errMsg := "failed to init host sensor"
if scanInfo.VerboseMode {
errMsg = fmt.Sprintf("%s: %v", errMsg, err)
}
cautils.ErrorDisplay(errMsg)
hostSensorHandler = &hostsensorutils.HostSensorHandlerMock{}
}
// excluding hostsensor namespace
if len(scanInfo.IncludeNamespaces) == 0 && hostSensorHandler.GetNamespace() != "" {
scanInfo.ExcludedNamespaces = fmt.Sprintf("%s,%s", scanInfo.ExcludedNamespaces, hostSensorHandler.GetNamespace())
}
resourceHandler := getResourceHandler(scanInfo, tenantConfig, k8s, hostSensorHandler)
// reporting behavior - setup reporter
reportHandler := getReporter(scanInfo, tenantConfig)
reportHandler := getReporter(tenantConfig, scanInfo.Submit)
v := cautils.NewIVersionCheckHandler()
v.CheckLatestVersion(cautils.NewVersionCheckRequest(cautils.BuildNumber, policyIdentifierNames(scanInfo.PolicyIdentifier), "", scanningTarget))
v.CheckLatestVersion(cautils.NewVersionCheckRequest(cautils.BuildNumber, policyIdentifierNames(scanInfo.PolicyIdentifier), "", scanInfo.GetScanningEnvironment()))
// setup printer
printerHandler := printer.GetPrinter(scanInfo.Format, scanInfo.VerboseMode)
@@ -113,28 +85,19 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error {
interfaces.report.SetClusterName(interfaces.tenantConfig.GetClusterName())
interfaces.report.SetCustomerGUID(interfaces.tenantConfig.GetCustomerGUID())
if err := interfaces.hostSensorHandler.Init(); err != nil {
errMsg := "failed to init host sensor"
if scanInfo.VerboseMode {
errMsg = fmt.Sprintf("%s: %v", errMsg, err)
}
cautils.ErrorDisplay(errMsg)
} else if len(scanInfo.IncludeNamespaces) == 0 && interfaces.hostSensorHandler.GetNamespace() != "" {
scanInfo.ExcludedNamespaces = fmt.Sprintf("%s,%s", scanInfo.ExcludedNamespaces, interfaces.hostSensorHandler)
defer func() {
if err := interfaces.hostSensorHandler.TearDown(); err != nil {
errMsg := "failed to tear down host sensor"
if scanInfo.VerboseMode {
errMsg = fmt.Sprintf("%s: %v", errMsg, err)
}
cautils.ErrorDisplay(errMsg)
}
}()
}
// set policy getter only after setting the customerGUID
setPolicyGetter(scanInfo, interfaces.tenantConfig.GetCustomerGUID())
defer func() {
if err := interfaces.hostSensorHandler.TearDown(); err != nil {
errMsg := "failed to tear down host sensor"
if scanInfo.VerboseMode {
errMsg = fmt.Sprintf("%s: %v", errMsg, err)
}
cautils.ErrorDisplay(errMsg)
}
}()
// cli handler setup
go func() {
// policy handler setup
@@ -158,9 +121,8 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error {
// print report url
interfaces.report.DisplayReportURL()
adjustedFailThreshold := float32(scanInfo.FailThreshold) / 100
if score < adjustedFailThreshold {
return fmt.Errorf("Scan score is below threshold")
if score >= float32(scanInfo.FailThreshold) {
return fmt.Errorf("scan risk-score %.2f is above permitted threshold %d", score, scanInfo.FailThreshold)
}
return nil
+64 -9
View File
@@ -4,27 +4,70 @@ import (
"fmt"
"os"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/hostsensorutils"
"github.com/armosec/kubescape/resourcehandler"
"github.com/armosec/kubescape/resultshandling/reporter"
"github.com/armosec/opa-utils/reporthandling"
"github.com/armosec/rbac-utils/rbacscanner"
"github.com/golang/glog"
)
func getReporter(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig) reporter.IReport {
setSubmitBehavior(scanInfo, tenantConfig)
if !scanInfo.Submit {
return reporter.NewReportMock()
func getKubernetesApi(scanInfo *cautils.ScanInfo) *k8sinterface.KubernetesApi {
if scanInfo.GetScanningEnvironment() == cautils.ScanLocalFiles {
return nil
}
if !scanInfo.FrameworkScan {
return reporter.NewReportMock()
return k8sinterface.NewKubernetesApi()
}
func getTenantConfig(scanInfo *cautils.ScanInfo, k8s *k8sinterface.KubernetesApi) cautils.ITenantConfig {
if scanInfo.GetScanningEnvironment() == cautils.ScanLocalFiles {
return cautils.NewLocalConfig(getter.GetArmoAPIConnector(), scanInfo.Account)
}
return reporter.NewReportEventReceiver(tenantConfig.GetConfigObj())
return cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account)
}
func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, submit bool) *cautils.RBACObjects {
if submit {
return cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, tenantConfig.GetCustomerGUID(), tenantConfig.GetClusterName()))
}
return nil
}
func getReporter(tenantConfig cautils.ITenantConfig, submit bool) reporter.IReport {
if submit {
return reporter.NewReportEventReceiver(tenantConfig.GetConfigObj())
}
return reporter.NewReportMock()
}
func getResourceHandler(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, hostSensorHandler hostsensorutils.IHostSensor) resourcehandler.IResourceHandler {
if scanInfo.GetScanningEnvironment() == cautils.ScanLocalFiles {
return resourcehandler.NewFileResourceHandler(scanInfo.InputPatterns)
}
rbacObjects := getRBACHandler(tenantConfig, k8s, scanInfo.Submit)
return resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo), hostSensorHandler, rbacObjects)
}
func getHostSensorHandler(scanInfo *cautils.ScanInfo, k8s *k8sinterface.KubernetesApi) hostsensorutils.IHostSensor {
if scanInfo.GetScanningEnvironment() == cautils.ScanLocalFiles {
return &hostsensorutils.HostSensorHandlerMock{}
}
hasHostSensorControls := true
// we need to determined which controls needs host sensor
if scanInfo.HostSensor.Get() == nil && hasHostSensorControls {
scanInfo.HostSensor.SetBool(askUserForHostSensor())
}
if hostSensorVal := scanInfo.HostSensor.Get(); hostSensorVal != nil && *hostSensorVal {
hostSensorHandler, err := hostsensorutils.NewHostSensorHandler(k8s)
if err != nil || hostSensorHandler == nil {
glog.Errorf("failed to create host sensor: %v", err)
return &hostsensorutils.HostSensorHandlerMock{}
}
return hostSensorHandler
}
return &hostsensorutils.HostSensorHandlerMock{}
}
func getFieldSelector(scanInfo *cautils.ScanInfo) resourcehandler.IFieldSelector {
if scanInfo.IncludeNamespaces != "" {
return resourcehandler.NewIncludeSelector(scanInfo.IncludeNamespaces)
@@ -65,6 +108,18 @@ func setSubmitBehavior(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantC
*/
// do not submit control scanning
if !scanInfo.FrameworkScan {
scanInfo.Submit = false
return
}
// do not submit yaml/url scanning
if scanInfo.GetScanningEnvironment() == cautils.ScanLocalFiles {
scanInfo.Submit = false
return
}
if tenantConfig.IsConfigFound() { // config found in cache (submitted)
if !scanInfo.Local {
// Submit report
+13 -5
View File
@@ -4,11 +4,11 @@ go 1.17
require (
github.com/armosec/armoapi-go v0.0.23
github.com/armosec/k8s-interface v0.0.35
github.com/armosec/opa-utils v0.0.62
github.com/armosec/rbac-utils v0.0.8
github.com/armosec/k8s-interface v0.0.50
github.com/armosec/opa-utils v0.0.75
github.com/armosec/rbac-utils v0.0.9
github.com/armosec/utils-go v0.0.3
github.com/briandowns/spinner v1.16.0
github.com/briandowns/spinner v1.18.0
github.com/enescakir/emoji v1.0.0
github.com/fatih/color v1.13.0
github.com/gofrs/uuid v4.1.0+incompatible
@@ -36,6 +36,7 @@ require (
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/armosec/utils-k8s-go v0.0.1 // indirect
github.com/aws/aws-sdk-go v1.41.11 // indirect
github.com/coreos/go-oidc v2.2.1+incompatible // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/docker v20.10.9+incompatible // indirect
@@ -48,12 +49,15 @@ require (
github.com/go-logr/logr v0.4.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.5 // indirect
github.com/google/gofuzz v1.1.0 // indirect
github.com/googleapis/gax-go/v2 v2.0.5 // indirect
github.com/googleapis/gnostic v0.5.5 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.11 // indirect
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
@@ -69,10 +73,11 @@ require (
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b // indirect
go.opencensus.io v0.23.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.19.1 // indirect
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 // indirect
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
golang.org/x/net v0.0.0-20210825183410-e898025ed96a // indirect
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 // indirect
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect
@@ -80,7 +85,10 @@ require (
golang.org/x/text v0.3.6 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
gonum.org/v1/gonum v0.9.1 // indirect
google.golang.org/api v0.44.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
google.golang.org/grpc v1.38.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
+21 -11
View File
@@ -87,15 +87,15 @@ github.com/armosec/armoapi-go v0.0.2/go.mod h1:vIK17yoKbJRQyZXWWLe3AqfqCRITxW8qm
github.com/armosec/armoapi-go v0.0.23 h1:jqoLIWM5CR7DCD9fpFgN0ePqtHvOCoZv/XzCwsUluJU=
github.com/armosec/armoapi-go v0.0.23/go.mod h1:iaVVGyc23QGGzAdv4n+szGQg3Rbpixn9yQTU3qWRpaw=
github.com/armosec/k8s-interface v0.0.8/go.mod h1:xxS+V5QT3gVQTwZyAMMDrYLWGrfKOpiJ7Jfhfa0w9sM=
github.com/armosec/k8s-interface v0.0.19/go.mod h1:0ztwEWLez2lpMMKHbs0DoZV64S0YIA4s72vCs9iAAiE=
github.com/armosec/k8s-interface v0.0.26/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU=
github.com/armosec/k8s-interface v0.0.35 h1:8kfQT7MxNMPb5HL3Sv3PriI4eVQzogECbBzckso/HkA=
github.com/armosec/k8s-interface v0.0.35/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU=
github.com/armosec/opa-utils v0.0.62 h1:C/VN1HUDoa0BX2CTSqgjuKEIlb/rkIwGY2/UlvfS154=
github.com/armosec/opa-utils v0.0.62/go.mod h1:P63qZDPG3vjPtSQqwx2N4Nfm8VaboDul7R7kmnUvvWc=
github.com/armosec/k8s-interface v0.0.37/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU=
github.com/armosec/k8s-interface v0.0.50 h1:iLPGI0j85vwKANr9QDAnba4Efjg3DyIJg15jRJdvOnc=
github.com/armosec/k8s-interface v0.0.50/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU=
github.com/armosec/opa-utils v0.0.64/go.mod h1:6tQP8UDq2EvEfSqh8vrUdr/9QVSCG4sJfju1SXQOn4c=
github.com/armosec/opa-utils v0.0.75 h1:GBI3K18xc3WXJHIorIu4bGNAsfMYHUc1x7zueDz2ZbY=
github.com/armosec/opa-utils v0.0.75/go.mod h1:L7d+uiIIXAZ3LEyKtmEIbMcI1hWgWaXGpn5zVCqzwSU=
github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40=
github.com/armosec/rbac-utils v0.0.8 h1:Hpr2p9F3Elp9NxbDAZiMlNUcQOdfyu9rWFE2fM2nNGw=
github.com/armosec/rbac-utils v0.0.8/go.mod h1:fmLZDgT5ILNo5gzR19boIIn7TOpfWh2pIgprxLrYN/g=
github.com/armosec/rbac-utils v0.0.9 h1:rIOWp4K7BELUNX32ktSjVbb8d/0SpH7W76W6Tf+8rzw=
github.com/armosec/rbac-utils v0.0.9/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0=
github.com/armosec/utils-go v0.0.2/go.mod h1:itWmRLzRdsnwjpEOomL0mBWGnVNNIxSjDAdyc+b0iUo=
github.com/armosec/utils-go v0.0.3 h1:uyQI676yRciQM0sSN9uPoqHkbspTxHO0kmzXhBeE/xU=
github.com/armosec/utils-go v0.0.3/go.mod h1:itWmRLzRdsnwjpEOomL0mBWGnVNNIxSjDAdyc+b0iUo=
@@ -103,6 +103,7 @@ github.com/armosec/utils-k8s-go v0.0.1 h1:Ay3y7fW+4+FjVc0+obOWm8YsnEvM31vPAVoKTy
github.com/armosec/utils-k8s-go v0.0.1/go.mod h1:qrU4pmY2iZsOb39Eltpm0sTTNM3E4pmeyWx4dgDUC2U=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws/aws-sdk-go v1.41.1/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go v1.41.11 h1:QLouWsiYQ8i22kD8k58Dpdhio1A0MpT7bg9ZNXqEjuI=
github.com/aws/aws-sdk-go v1.41.11/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
@@ -116,8 +117,8 @@ github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqO
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/briandowns/spinner v1.16.0 h1:DFmp6hEaIx2QXXuqSJmtfSBSAjRmpGiKG6ip2Wm/yOs=
github.com/briandowns/spinner v1.16.0/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ=
github.com/briandowns/spinner v1.18.0 h1:SJs0maNOs4FqhBwiJ3Gr7Z1D39/rukIVGQvpNZVHVcM=
github.com/briandowns/spinner v1.18.0/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/bytecodealliance/wasmtime-go v0.30.0 h1:WfYpr4WdqInt8m5/HvYinf+HrSEAIhItKIcth+qb1h4=
github.com/bytecodealliance/wasmtime-go v0.30.0/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI=
@@ -335,9 +336,11 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=
@@ -382,7 +385,9 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
@@ -690,8 +695,9 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -887,6 +893,7 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1013,6 +1020,7 @@ google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34q
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.44.0 h1:URs6qR1lAxDsqWITsQXI4ZkGiYJ5dHtRNiCpfs2OeKA=
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1070,6 +1078,7 @@ google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
@@ -1094,6 +1103,7 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
-67
View File
@@ -1,67 +0,0 @@
package hostsensorutils
import (
"encoding/json"
"fmt"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type HostSensorDataEnvelope struct {
schema.GroupVersionResource
NodeName string `json:"nodeName"`
Data json.RawMessage `json:"data"`
}
func (hsde *HostSensorDataEnvelope) SetNamespace(string) {
}
func (hsde *HostSensorDataEnvelope) SetName(val string) {
hsde.NodeName = val
}
func (hsde *HostSensorDataEnvelope) SetKind(val string) {
hsde.Resource = val
}
func (hsde *HostSensorDataEnvelope) SetWorkload(val map[string]interface{}) { //deprecated
hsde.Data, _ = json.Marshal(val)
}
func (hsde *HostSensorDataEnvelope) SetObject(val map[string]interface{}) {
hsde.Data, _ = json.Marshal(val)
}
func (hsde *HostSensorDataEnvelope) GetNamespace() string {
return ""
}
func (hsde *HostSensorDataEnvelope) GetName() string {
return hsde.NodeName
}
func (hsde *HostSensorDataEnvelope) GetKind() string {
return hsde.Resource
}
func (hsde *HostSensorDataEnvelope) GetApiVersion() string {
return hsde.Version
}
func (hsde *HostSensorDataEnvelope) GetWorkload() map[string]interface{} { // DEPRECATED
res := map[string]interface{}{}
json.Unmarshal(hsde.Data, &res)
return res
}
func (hsde *HostSensorDataEnvelope) GetObject() map[string]interface{} {
res := map[string]interface{}{}
json.Unmarshal(hsde.Data, &res)
return res
}
func (hsde *HostSensorDataEnvelope) GetID() string { // -> <api-group>/<api-version>/<kind>/<name>
return fmt.Sprintf("%s/%s/%s/%s", hsde.Group, hsde.GetApiVersion(), hsde.GetKind(), hsde.GetName())
}
+3
View File
@@ -201,5 +201,8 @@ func (hsh *HostSensorHandler) TearDown() error {
}
func (hsh *HostSensorHandler) GetNamespace() string {
if hsh.DaemonSet == nil {
return ""
}
return hsh.DaemonSet.Namespace
}
+34 -66
View File
@@ -5,8 +5,9 @@ import (
"fmt"
"sync"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/cautils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/armosec/opa-utils/objectsenvelopes/hostsensor"
"sigs.k8s.io/yaml"
)
@@ -56,12 +57,12 @@ func (hsh *HostSensorHandler) ForwardToPod(podName, path string) ([]byte, error)
// 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
func (hsh *HostSensorHandler) sendAllPodsHTTPGETRequest(path string) ([]HostSensorDataEnvelope, error) {
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([]HostSensorDataEnvelope, 0, len(podList))
res := make([]hostsensor.HostSensorDataEnvelope, 0, len(podList))
resLock := sync.Mutex{}
wg := sync.WaitGroup{}
wg.Add(len(podList))
@@ -74,7 +75,12 @@ func (hsh *HostSensorHandler) sendAllPodsHTTPGETRequest(path string) ([]HostSens
} else {
resLock.Lock()
defer resLock.Unlock()
res = append(res, HostSensorDataEnvelope{NodeName: podList[podName], Data: resBytes})
hostSensorDataEnvelope := hostsensor.HostSensorDataEnvelope{}
hostSensorDataEnvelope.SetApiVersion(k8sinterface.JoinGroupVersion(hostsensor.GroupHostSensor, hostsensor.Version))
hostSensorDataEnvelope.SetKind(requestKind)
hostSensorDataEnvelope.SetName(podList[podName])
hostSensorDataEnvelope.SetData(resBytes)
res = append(res, hostSensorDataEnvelope)
}
}(podName, path)
@@ -83,91 +89,53 @@ func (hsh *HostSensorHandler) sendAllPodsHTTPGETRequest(path string) ([]HostSens
return res, nil
}
// return list of
func (hsh *HostSensorHandler) GetOpenPortsList() ([]HostSensorDataEnvelope, error) {
// return list of OpenPortsList
func (hsh *HostSensorHandler) GetOpenPortsList() ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
res, err := hsh.sendAllPodsHTTPGETRequest("/openedPorts")
for resIdx := range res {
res[resIdx].GroupVersionResource.Resource = "OpenPortsList"
res[resIdx].GroupVersionResource.Group = "hostdata.armo.cloud"
res[resIdx].GroupVersionResource.Version = "v1beta0"
}
return res, err
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 KubeletCommandLine
func (hsh *HostSensorHandler) GetKubeletCommandLine() ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
return hsh.sendAllPodsHTTPGETRequest("/kubeletCommandLine", "KubeletCommandLine")
}
// return list of
func (hsh *HostSensorHandler) GetLinuxSecurityHardeningStatus() ([]HostSensorDataEnvelope, error) {
func (hsh *HostSensorHandler) GetKernelVersion() ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
res, err := hsh.sendAllPodsHTTPGETRequest("/linuxSecurityHardening")
for resIdx := range res {
res[resIdx].GroupVersionResource.Resource = "LinuxSecurityHardeningStatus"
res[resIdx].GroupVersionResource.Group = "hostdata.armo.cloud"
res[resIdx].GroupVersionResource.Version = "v1beta0"
}
return res, err
return hsh.sendAllPodsHTTPGETRequest("/kernelVersion", "KernelVersion")
}
// return list of
func (hsh *HostSensorHandler) GetKubeletCommandLine() ([]HostSensorDataEnvelope, error) {
func (hsh *HostSensorHandler) GetOsReleaseFile() ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
res, err := hsh.sendAllPodsHTTPGETRequest("/kubeletCommandLine")
for resIdx := range res {
res[resIdx].GroupVersionResource.Resource = "KubeletCommandLine"
res[resIdx].GroupVersionResource.Group = "hostdata.armo.cloud"
res[resIdx].GroupVersionResource.Version = "v1beta0"
}
return res, err
return hsh.sendAllPodsHTTPGETRequest("/osRelease", "OsReleaseFile")
}
// return list of
func (hsh *HostSensorHandler) GetKernelVersion() ([]HostSensorDataEnvelope, error) {
func (hsh *HostSensorHandler) GetKubeletConfigurations() ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
res, err := hsh.sendAllPodsHTTPGETRequest("/kernelVersion")
for resIdx := range res {
res[resIdx].GroupVersionResource.Resource = "KernelVersion"
res[resIdx].GroupVersionResource.Group = "hostdata.armo.cloud"
res[resIdx].GroupVersionResource.Version = "v1beta0"
}
return res, err
}
// return list of
func (hsh *HostSensorHandler) GetOsReleaseFile() ([]HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
res, err := hsh.sendAllPodsHTTPGETRequest("/osRelease")
for resIdx := range res {
res[resIdx].GroupVersionResource.Resource = "OsReleaseFile"
res[resIdx].GroupVersionResource.Group = "hostdata.armo.cloud"
res[resIdx].GroupVersionResource.Version = "v1beta0"
}
return res, err
}
// return list of
func (hsh *HostSensorHandler) GetKubeletConfigurations() ([]HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
res, err := hsh.sendAllPodsHTTPGETRequest("/kubeletConfigurations")
res, err := hsh.sendAllPodsHTTPGETRequest("/kubeletConfigurations", "KubeletConfigurations") // empty kind, will be overridden
for resIdx := range res {
jsonBytes, err := yaml.YAMLToJSON(res[resIdx].Data)
if err != nil {
fmt.Printf("In GetKubeletConfigurations failed to YAMLToJSON: %v;\n%v", err, res[resIdx])
continue
}
res[resIdx].Data = jsonBytes
kindDet := metav1.TypeMeta{}
if err = json.Unmarshal(jsonBytes, &kindDet); err != nil {
fmt.Printf("In GetKubeletConfigurations failed to Unmarshal GroupVersionKind: %v;\n%v", err, jsonBytes)
continue
}
res[resIdx].GroupVersionResource.Resource = kindDet.Kind
res[resIdx].GroupVersionResource.Group = kindDet.GroupVersionKind().Group
res[resIdx].GroupVersionResource.Version = kindDet.GroupVersionKind().Version
res[resIdx].SetData(jsonBytes)
}
return res, err
}
func (hsh *HostSensorHandler) CollectResources() ([]HostSensorDataEnvelope, error) {
res := make([]HostSensorDataEnvelope, 0)
func (hsh *HostSensorHandler) CollectResources() ([]hostsensor.HostSensorDataEnvelope, error) {
res := make([]hostsensor.HostSensorDataEnvelope, 0)
if hsh.DaemonSet == nil {
return res, nil
}
+3 -1
View File
@@ -1,8 +1,10 @@
package hostsensorutils
import "github.com/armosec/opa-utils/objectsenvelopes/hostsensor"
type IHostSensor interface {
Init() error
TearDown() error
CollectResources() ([]HostSensorDataEnvelope, error)
CollectResources() ([]hostsensor.HostSensorDataEnvelope, error)
GetNamespace() string
}
+6 -2
View File
@@ -1,5 +1,9 @@
package hostsensorutils
import (
"github.com/armosec/opa-utils/objectsenvelopes/hostsensor"
)
type HostSensorHandlerMock struct {
}
@@ -11,8 +15,8 @@ func (hshm *HostSensorHandlerMock) TearDown() error {
return nil
}
func (hshm *HostSensorHandlerMock) CollectResources() ([]HostSensorDataEnvelope, error) {
return []HostSensorDataEnvelope{}, nil
func (hshm *HostSensorHandlerMock) CollectResources() ([]hostsensor.HostSensorDataEnvelope, error) {
return []hostsensor.HostSensorDataEnvelope{}, nil
}
func (hshm *HostSensorHandlerMock) GetNamespace() string {
+9 -5
View File
@@ -6,7 +6,10 @@ import (
"time"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/opa-utils/objectsenvelopes"
"github.com/armosec/opa-utils/reporthandling"
"github.com/armosec/opa-utils/score"
"github.com/golang/glog"
"github.com/armosec/k8s-interface/k8sinterface"
@@ -64,7 +67,8 @@ func (opaHandler *OPAProcessorHandler) ProcessRulesListenner() {
opap.updateResults()
// update score
// opap.updateScore()
scoreutil := score.NewScore(opaSessionObj.AllResources)
scoreutil.Calculate(opaSessionObj.PostureReport.FrameworkReports)
// report
*opaHandler.reportResults <- opaSessionObj
@@ -161,7 +165,7 @@ func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule) (*reporth
return nil, nil
}
inputResources, err := reporthandling.RegoResourcesAggregator(rule, getKubernetesObjects(opap.K8SResources, opap.AllResources, rule.Match))
inputResources, err := reporthandling.RegoResourcesAggregator(rule, getAllSupportedObjects(opap.K8SResources, opap.AllResources, rule))
if err != nil {
return nil, fmt.Errorf("error getting aggregated k8sObjects: %s", err.Error())
}
@@ -183,20 +187,20 @@ func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule) (*reporth
if err != nil {
return nil, err
}
inputResources = workloadinterface.ListMapToMeta(enumeratedData)
inputResources = objectsenvelopes.ListMapToMeta(enumeratedData)
ruleReport.ListInputKinds = workloadinterface.ListMetaIDs(inputResources)
for i := range inputResources {
opap.AllResources[inputResources[i].GetID()] = inputResources[i]
}
failedResources := workloadinterface.ListMapToMeta(ruleReport.GetFailedResources())
failedResources := objectsenvelopes.ListMapToMeta(ruleReport.GetFailedResources())
for i := range failedResources {
if r, ok := opap.AllResources[failedResources[i].GetID()]; !ok {
opap.AllResources[failedResources[i].GetID()] = r
}
}
warningResources := workloadinterface.ListMapToMeta(ruleReport.GetWarnignResources())
warningResources := objectsenvelopes.ListMapToMeta(ruleReport.GetWarnignResources())
for i := range warningResources {
if r, ok := opap.AllResources[warningResources[i].GetID()]; !ok {
opap.AllResources[warningResources[i].GetID()] = r
+2 -1
View File
@@ -4,6 +4,7 @@ import (
"testing"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/opa-utils/objectsenvelopes"
"github.com/armosec/opa-utils/reporthandling"
"github.com/armosec/opa-utils/resources"
@@ -20,7 +21,7 @@ func TestProcess(t *testing.T) {
// set k8s
k8sResources := make(cautils.K8SResources)
allResources := make(map[string]workloadinterface.IMetadata)
imetaObj := workloadinterface.ListMapToMeta(k8sinterface.ConvertUnstructuredSliceToMap(k8sinterface.V1KubeSystemNamespaceMock().Items))
imetaObj := objectsenvelopes.ListMapToMeta(k8sinterface.ConvertUnstructuredSliceToMap(k8sinterface.V1KubeSystemNamespaceMock().Items))
for i := range imetaObj {
allResources[imetaObj[i].GetID()] = imetaObj[i]
}
+26 -6
View File
@@ -28,10 +28,17 @@ func (opap *OPAProcessor) updateResults() {
reporthandling.SetUniqueResourcesCounter(&opap.PostureReport.FrameworkReports[f])
// set default score
reporthandling.SetDefaultScore(&opap.PostureReport.FrameworkReports[f])
// reporthandling.SetDefaultScore(&opap.PostureReport.FrameworkReports[f])
}
}
func getAllSupportedObjects(k8sResources *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata, rule *reporthandling.PolicyRule) []workloadinterface.IMetadata {
k8sObjects := []workloadinterface.IMetadata{}
k8sObjects = append(k8sObjects, getKubernetesObjects(k8sResources, allResources, rule.Match)...)
k8sObjects = append(k8sObjects, getKubernetesObjects(k8sResources, allResources, rule.DynamicMatch)...)
return k8sObjects
}
func getKubernetesObjects(k8sResources *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata, match []reporthandling.RuleMatchObjects) []workloadinterface.IMetadata {
k8sObjects := []workloadinterface.IMetadata{}
for m := range match {
@@ -100,7 +107,7 @@ func isRuleKubescapeVersionCompatible(rule *reporthandling.PolicyRule) bool {
}
func removeData(obj workloadinterface.IMetadata) {
if !workloadinterface.IsTypeWorkload(obj.GetObject()) {
if !k8sinterface.IsTypeWorkload(obj.GetObject()) {
return // remove data only from kubernetes objects
}
workload := workloadinterface.NewWorkloadObj(obj.GetObject())
@@ -116,13 +123,26 @@ func removeData(obj workloadinterface.IMetadata) {
func removeConfigMapData(workload workloadinterface.IWorkload) {
workload.RemoveAnnotation("kubectl.kubernetes.io/last-applied-configuration")
workloadinterface.RemoveFromMap(workload.GetObject(), "data")
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) {
workloadinterface.NewWorkloadObj(workload.GetObject()).RemoveSecretData()
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")
@@ -134,7 +154,7 @@ func removePodData(workload workloadinterface.IWorkload) {
}
for i := range containers {
for j := range containers[i].Env {
containers[i].Env[j].Value = ""
containers[i].Env[j].Value = "XXXXXX"
}
}
workloadinterface.SetInMap(workload.GetObject(), workloadinterface.PodSpec(workload.GetKind()), "containers", containers)
+4 -1
View File
@@ -6,6 +6,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/k8s-interface/workloadinterface"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/opa-utils/reporthandling"
@@ -57,6 +58,8 @@ func TestIsRuleKubescapeVersionCompatible(t *testing.T) {
}
func TestRemoveData(t *testing.T) {
k8sinterface.InitializeMapResourcesMock()
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"demoservice-server"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"demoservice-server"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}}}`
obj, _ := workloadinterface.NewWorkload([]byte(w))
removeData(obj)
@@ -65,7 +68,7 @@ func TestRemoveData(t *testing.T) {
c, _ := workload.GetContainers()
for i := range c {
for _, e := range c[i].Env {
assert.Equal(t, "", e.Value)
assert.Equal(t, "XXXXXX", e.Value)
}
}
}
-1
View File
@@ -59,6 +59,5 @@ func (policyHandler *PolicyHandler) getResources(notification *reporthandling.Po
opaSessionObj.K8SResources = resourcesMap
opaSessionObj.AllResources = allResources
cautils.SuccessTextDisplay("Lets start!!!")
return nil
}
+1 -1
View File
@@ -59,7 +59,7 @@ func getNamespacesSelector(resource *schema.GroupVersionResource, ns, operator s
fieldSelector := "metadata."
if resource.Resource == "namespaces" {
fieldSelector += "name"
} else if k8sinterface.IsNamespaceScope(resource) {
} else if k8sinterface.IsResourceInNamespaceScope(resource.Resource) {
fieldSelector += "namespace"
} else {
return ""
+4
View File
@@ -15,6 +15,8 @@ func TestGetNamespacesSelector(t *testing.T) {
}
func TestExcludedNamespacesSelectors(t *testing.T) {
k8sinterface.InitializeMapResourcesMock()
es := NewExcludeSelector("default,ingress")
selectors := es.GetNamespacesSelectors(&schema.GroupVersionResource{Resource: "pods"})
assert.Equal(t, 1, len(selectors))
@@ -26,6 +28,8 @@ func TestExcludedNamespacesSelectors(t *testing.T) {
}
func TestIncludeNamespacesSelectors(t *testing.T) {
k8sinterface.InitializeMapResourcesMock()
is := NewIncludeSelector("default,ingress")
selectors := is.GetNamespacesSelectors(&schema.GroupVersionResource{Resource: "pods"})
assert.Equal(t, 2, len(selectors))
+4 -3
View File
@@ -14,6 +14,7 @@ import (
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/opa-utils/objectsenvelopes"
"github.com/armosec/opa-utils/reporthandling"
"gopkg.in/yaml.v2"
@@ -124,7 +125,7 @@ func mapResources(workloads []workloadinterface.IMetadata) map[string][]workload
continue
}
if workloadinterface.IsTypeWorkload(workloads[i].GetObject()) {
if k8sinterface.IsTypeWorkload(workloads[i].GetObject()) {
w := workloadinterface.NewWorkloadObj(workloads[i].GetObject())
if groupVersionResource.Group != w.GetGroup() || groupVersionResource.Version != w.GetVersion() {
// TODO - print warning
@@ -211,7 +212,7 @@ func readYamlFile(yamlFile []byte) ([]workloadinterface.IMetadata, []error) {
continue
}
if obj, ok := j.(map[string]interface{}); ok {
if o := workloadinterface.NewObject(obj); o != nil {
if o := objectsenvelopes.NewObject(obj); o != nil {
yamlObjs = append(yamlObjs, o)
}
} else {
@@ -237,7 +238,7 @@ func convertJsonToWorkload(jsonObj interface{}, workloads *[]workloadinterface.I
switch x := jsonObj.(type) {
case map[string]interface{}:
if o := workloadinterface.NewObject(x); o != nil {
if o := objectsenvelopes.NewObject(x); o != nil {
(*workloads) = append(*workloads, o)
}
case []interface{}:
+52 -15
View File
@@ -3,11 +3,15 @@ package resourcehandler
import (
"context"
"fmt"
"os"
"strings"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/hostsensorutils"
"github.com/armosec/opa-utils/objectsenvelopes"
"github.com/armosec/opa-utils/reporthandling"
"github.com/armosec/k8s-interface/cloudsupport"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/k8s-interface/workloadinterface"
@@ -43,6 +47,8 @@ func (k8sHandler *K8sResourceHandler) GetResources(frameworks []reporthandling.F
// get k8s resources
cautils.ProgressTextDisplay("Accessing Kubernetes objects")
cautils.StartSpinner()
// build resources map
// map resources based on framework required resources: map["/group/version/kind"][]<k8s workloads ids>
k8sResourcesMap := setResourceMap(frameworks)
@@ -59,8 +65,13 @@ func (k8sHandler *K8sResourceHandler) GetResources(frameworks []reporthandling.F
}
if err := k8sHandler.collectRbacResources(allResources); err != nil {
fmt.Println("failed to collect rbac resources")
cautils.WarningDisplay(os.Stdout, "Warning: failed to collect rbac resources\n")
}
if err := getCloudProviderDescription(allResources, k8sResourcesMap); err != nil {
cautils.WarningDisplay(os.Stdout, fmt.Sprintf("Warning: %v\n", err.Error()))
}
cautils.StopSpinner()
cautils.SuccessTextDisplay("Accessed successfully to Kubernetes objects")
return k8sResourcesMap, allResources, nil
@@ -74,6 +85,7 @@ func (k8sHandler *K8sResourceHandler) GetClusterAPIServerInfo() *version.Info {
}
return clusterAPIServerInfo
}
func (k8sHandler *K8sResourceHandler) pullResources(k8sResources *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata, namespace string, labels map[string]string) error {
var errs error
@@ -82,11 +94,13 @@ func (k8sHandler *K8sResourceHandler) pullResources(k8sResources *cautils.K8SRes
gvr := schema.GroupVersionResource{Group: apiGroup, Version: apiVersion, Resource: resource}
result, err := k8sHandler.pullSingleResource(&gvr, namespace, labels)
if err != nil {
// handle error
if errs == nil {
errs = err
} else {
errs = fmt.Errorf("%s\n%s", errs, err.Error())
if !strings.Contains(err.Error(), "the server could not find the requested resource") {
// handle error
if errs == nil {
errs = err
} else {
errs = fmt.Errorf("%s\n%s", errs, err.Error())
}
}
continue
}
@@ -138,7 +152,7 @@ func (k8sHandler *K8sResourceHandler) pullSingleResource(resource *schema.GroupV
func ConvertMapListToMeta(resourceMap []map[string]interface{}) []workloadinterface.IMetadata {
workloads := []workloadinterface.IMetadata{}
for i := range resourceMap {
if w := workloadinterface.NewObject(resourceMap[i]); w != nil {
if w := objectsenvelopes.NewObject(resourceMap[i]); w != nil {
workloads = append(workloads, w)
}
}
@@ -151,16 +165,15 @@ func (k8sHandler *K8sResourceHandler) collectHostResources(allResources map[stri
return err
}
for rscIdx := range hostResources {
groupResources := k8sinterface.ResourceGroupToString(hostResources[rscIdx].Group, hostResources[rscIdx].GetApiVersion(), hostResources[rscIdx].GetKind())
for _, groupResource := range groupResources {
allResources[hostResources[rscIdx].GetID()] = &hostResources[rscIdx]
group, version := getGroupNVersion(hostResources[rscIdx].GetApiVersion())
groupResource := k8sinterface.JoinResourceTriplets(group, version, hostResources[rscIdx].GetKind())
allResources[hostResources[rscIdx].GetID()] = &hostResources[rscIdx]
grpResourceList, ok := (*resourcesMap)[groupResource]
if !ok {
grpResourceList = make([]string, 0)
}
(*resourcesMap)[groupResource] = append(grpResourceList, hostResources[rscIdx].GetID())
grpResourceList, ok := (*resourcesMap)[groupResource]
if !ok {
grpResourceList = make([]string, 0)
}
(*resourcesMap)[groupResource] = append(grpResourceList, hostResources[rscIdx].GetID())
}
return nil
}
@@ -178,3 +191,27 @@ func (k8sHandler *K8sResourceHandler) collectRbacResources(allResources map[stri
}
return nil
}
func getCloudProviderDescription(allResources map[string]workloadinterface.IMetadata, k8sResourcesMap *cautils.K8SResources) error {
if cloudsupport.IsRunningInCloudProvider() {
wl, err := cloudsupport.GetDescriptiveInfoFromCloudProvider()
if err != nil {
cluster := k8sinterface.GetCurrentContext().Cluster
provider := cloudsupport.GetCloudProvider(cluster)
// Return error with useful info on how to configure credentials for getting cloud provider info
switch provider {
case "gke":
return fmt.Errorf("could not get descriptive information about gke cluster: %s using sdk client. See https://developers.google.com/accounts/docs/application-default-credentials for more information", cluster)
case "eks":
return fmt.Errorf("could not get descriptive information about eks cluster: %s using sdk client. Check out how to configure credentials in https://docs.aws.amazon.com/sdk-for-go/api/", cluster)
case "aks":
return fmt.Errorf("could not get descriptive information about aks cluster: %s. %v", cluster, err.Error())
}
return err
}
allResources[wl.GetID()] = wl
(*k8sResourcesMap)[fmt.Sprintf("%s/%s", wl.GetApiVersion(), wl.GetKind())] = []string{wl.GetID()}
}
return nil
}
+14
View File
@@ -1,6 +1,8 @@
package resourcehandler
import (
"strings"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/opa-utils/reporthandling"
@@ -66,3 +68,15 @@ func insertK8sResources(k8sResources map[string]map[string]map[string]interface{
}
}
}
func getGroupNVersion(apiVersion string) (string, string) {
gv := strings.Split(apiVersion, "/")
group, version := "", ""
if len(gv) >= 1 {
group = gv[0]
}
if len(gv) >= 2 {
version = gv[1]
}
return group, version
}
+1 -1
View File
@@ -21,7 +21,7 @@ func (jsonPrinter *JsonPrinter) SetWriter(outputFile string) {
}
func (jsonPrinter *JsonPrinter) Score(score float32) {
fmt.Printf("\nFinal score: %d", int(score*100))
fmt.Printf("\nOverall risk-score (0- Excellent, 100- All failed): %d\n", int(score))
}
func (jsonPrinter *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
+1 -1
View File
@@ -22,7 +22,7 @@ func (junitPrinter *JunitPrinter) SetWriter(outputFile string) {
}
func (junitPrinter *JunitPrinter) Score(score float32) {
fmt.Printf("\nFinal score: %d", int(score*100))
fmt.Printf("\nOverall risk-score (0- Excellent, 100- All failed): %d\n", int(score))
}
func (junitPrinter *JunitPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
+58 -63
View File
@@ -4,9 +4,11 @@ import (
"fmt"
"os"
"sort"
"strings"
"github.com/armosec/k8s-interface/workloadinterface"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/opa-utils/objectsenvelopes"
"github.com/armosec/opa-utils/reporthandling"
"github.com/enescakir/emoji"
"github.com/olekukonko/tablewriter"
@@ -17,7 +19,7 @@ type PrettyPrinter struct {
summary Summary
verboseMode bool
sortedControlNames []string
frameworkSummary ControlSummary
frameworkSummary ResultSummary
}
func NewPrettyPrinter(verboseMode bool) *PrettyPrinter {
@@ -33,23 +35,30 @@ func (printer *PrettyPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj)
warningResources := []string{}
allResources := []string{}
frameworkNames := []string{}
frameworkScores := []float32{}
var overallRiskScore float32 = 0
for _, frameworkReport := range opaSessionObj.PostureReport.FrameworkReports {
frameworkNames = append(frameworkNames, frameworkReport.Name)
frameworkScores = append(frameworkScores, frameworkReport.Score)
failedResources = reporthandling.GetUniqueResourcesIDs(append(failedResources, frameworkReport.ListResourcesIDs().GetFailedResources()...))
warningResources = reporthandling.GetUniqueResourcesIDs(append(warningResources, frameworkReport.ListResourcesIDs().GetWarningResources()...))
allResources = reporthandling.GetUniqueResourcesIDs(append(allResources, frameworkReport.ListResourcesIDs().GetAllResources()...))
printer.summarySetup(frameworkReport, opaSessionObj.AllResources)
overallRiskScore += frameworkReport.Score
}
printer.frameworkSummary = ControlSummary{
overallRiskScore /= float32(len(opaSessionObj.PostureReport.FrameworkReports))
printer.frameworkSummary = ResultSummary{
RiskScore: overallRiskScore,
TotalResources: len(allResources),
TotalFailed: len(failedResources),
TotalWarning: len(warningResources),
}
printer.printResults()
printer.printSummaryTable(frameworkNames)
printer.printSummaryTable(frameworkNames, frameworkScores)
}
@@ -72,7 +81,11 @@ func (printer *PrettyPrinter) summarySetup(fr reporthandling.FrameworkReport, al
if printer.verboseMode {
passedWorkloads = groupByNamespaceOrKind(workloadsSummary, workloadSummaryPassed)
}
printer.summary[cr.Name] = ControlSummary{
//controlSummary
printer.summary[cr.Name] = ResultSummary{
ID: cr.ControlID,
RiskScore: cr.Score,
TotalResources: cr.GetNumberOfResources(),
TotalFailed: cr.GetNumberOfFailedResources(),
TotalWarning: cr.GetNumberOfWarningResources(),
@@ -99,7 +112,7 @@ func (printer *PrettyPrinter) printResults() {
}
}
func (printer *PrettyPrinter) printSummary(controlName string, controlSummary *ControlSummary) {
func (printer *PrettyPrinter) printSummary(controlName string, controlSummary *ResultSummary) {
cautils.SimpleDisplay(printer.writer, "Summary - ")
cautils.SuccessDisplay(printer.writer, "Passed:%v ", controlSummary.TotalResources-controlSummary.TotalFailed-controlSummary.TotalWarning)
cautils.WarningDisplay(printer.writer, "Excluded:%v ", controlSummary.TotalWarning)
@@ -111,11 +124,10 @@ func (printer *PrettyPrinter) printSummary(controlName string, controlSummary *C
cautils.DescriptionDisplay(printer.writer, "\n")
}
func (printer *PrettyPrinter) printTitle(controlName string, controlSummary *ControlSummary) {
cautils.InfoDisplay(printer.writer, "[control: %s] ", controlName)
func (printer *PrettyPrinter) printTitle(controlName string, controlSummary *ResultSummary) {
cautils.InfoDisplay(printer.writer, "[control: %s - %s] ", controlName, getControlURL(controlSummary.ID))
if controlSummary.TotalResources == 0 {
cautils.InfoDisplay(printer.writer, "resources not found %v\n", emoji.ConfusedFace)
cautils.InfoDisplay(printer.writer, "skipped %v\n", emoji.ConfusedFace)
} else if controlSummary.TotalFailed != 0 {
cautils.FailureDisplay(printer.writer, "failed %v\n", emoji.SadButRelievedFace)
} else if controlSummary.TotalWarning != 0 {
@@ -127,7 +139,7 @@ func (printer *PrettyPrinter) printTitle(controlName string, controlSummary *Con
cautils.DescriptionDisplay(printer.writer, "Description: %s\n", controlSummary.Description)
}
func (printer *PrettyPrinter) printResources(controlSummary *ControlSummary) {
func (printer *PrettyPrinter) printResources(controlSummary *ResultSummary) {
if len(controlSummary.FailedWorkloads) > 0 {
cautils.FailureDisplay(printer.writer, "Failed:\n")
@@ -146,41 +158,29 @@ func (printer *PrettyPrinter) printResources(controlSummary *ControlSummary) {
func (printer *PrettyPrinter) printGroupedResources(workloads map[string][]WorkloadSummary) {
indent := INDENT
for ns, rsc := range workloads {
if !isKindToBeGrouped(ns) {
printer.printGroupedResource(indent, ns, rsc)
}
}
if rsc, ok := workloads["User"]; ok {
printer.printGroupedResource(indent, "User", rsc)
}
if rsc, ok := workloads["Group"]; ok {
printer.printGroupedResource(indent, "Group", rsc)
for title, rsc := range workloads {
printer.printGroupedResource(indent, title, rsc)
}
}
func (printer *PrettyPrinter) printGroupedResource(indent string, ns string, rsc []WorkloadSummary) {
func (printer *PrettyPrinter) printGroupedResource(indent string, title string, rsc []WorkloadSummary) {
preIndent := indent
if isKindToBeGrouped(ns) {
cautils.SimpleDisplay(printer.writer, "%s%ss\n", indent, ns)
} else if ns != "" {
cautils.SimpleDisplay(printer.writer, "%sNamespace %s\n", indent, ns)
}
preIndent2 := indent
for r := range rsc {
if title != "" {
cautils.SimpleDisplay(printer.writer, "%s%s\n", indent, title)
indent += indent
}
for r := range rsc {
relatedObjectsStr := generateRelatedObjectsStr(rsc[r])
cautils.SimpleDisplay(printer.writer, fmt.Sprintf("%s%s - %s %s\n", indent, rsc[r].resource.GetKind(), rsc[r].resource.GetName(), relatedObjectsStr))
indent = preIndent2
}
indent = preIndent
}
func generateRelatedObjectsStr(workload WorkloadSummary) string {
relatedStr := ""
w := workload.resource.GetObject()
if workloadinterface.IsTypeRegoResponseVector(w) {
relatedObjects := workloadinterface.NewRegoResponseVectorObject(w).GetRelatedObjects()
if workload.resource.GetObjectType() == workloadinterface.TypeWorkloadObject {
relatedObjects := objectsenvelopes.NewRegoResponseVectorObject(workload.resource.GetObject()).GetRelatedObjects()
for i, related := range relatedObjects {
if ns := related.GetNamespace(); i == 0 && ns != "" {
relatedStr += fmt.Sprintf("Namespace - %s, ", ns)
@@ -194,47 +194,35 @@ func generateRelatedObjectsStr(workload WorkloadSummary) string {
return relatedStr
}
func generateRow(control string, cs ControlSummary) []string {
func generateRow(control string, cs ResultSummary) []string {
row := []string{control}
row = append(row, cs.ToSlice()...)
if cs.TotalResources != 0 {
row = append(row, fmt.Sprintf("%d%s", percentage(cs.TotalResources, cs.TotalFailed), "%"))
row = append(row, fmt.Sprintf("%d", int(cs.RiskScore))+"%")
} else {
row = append(row, EmptyPercentage)
row = append(row, "skipped")
}
return row
}
func generateHeader() []string {
return []string{"Control Name", "Failed Resources", "Excluded Resources", "All Resources", "% success"}
return []string{"Control Name", "Failed Resources", "Excluded Resources", "All Resources", "% risk-score"}
}
func percentage(big, small int) int {
if big == 0 {
if small == 0 {
return 100
}
return 0
}
return int(float64(float64(big-small)/float64(big)) * 100)
}
func generateFooter(numControlers, sumFailed, sumWarning, sumTotal int) []string {
func generateFooter(printer *PrettyPrinter) []string {
// Control name | # failed resources | all resources | % success
row := []string{}
row = append(row, "Resource Summary") //fmt.Sprintf(""%d", numControlers"))
row = append(row, fmt.Sprintf("%d", sumFailed))
row = append(row, fmt.Sprintf("%d", sumWarning))
row = append(row, fmt.Sprintf("%d", sumTotal))
if sumTotal != 0 {
row = append(row, fmt.Sprintf("%d%s", percentage(sumTotal, sumFailed), "%"))
} else {
row = append(row, EmptyPercentage)
}
row = append(row, fmt.Sprintf("%d", printer.frameworkSummary.TotalFailed))
row = append(row, fmt.Sprintf("%d", printer.frameworkSummary.TotalWarning))
row = append(row, fmt.Sprintf("%d", printer.frameworkSummary.TotalResources))
row = append(row, fmt.Sprintf("%.2f%s", printer.frameworkSummary.RiskScore, "%"))
return row
}
func (printer *PrettyPrinter) printSummaryTable(frameworksNames []string) {
func (printer *PrettyPrinter) printSummaryTable(frameworksNames []string, frameworkScores []float32) {
// For control scan framework will be nil
printer.printFramework(frameworksNames)
printer.printFramework(frameworksNames, frameworkScores)
summaryTable := tablewriter.NewWriter(printer.writer)
summaryTable.SetAutoWrapText(false)
@@ -247,20 +235,23 @@ func (printer *PrettyPrinter) printSummaryTable(frameworksNames []string) {
controlSummary := printer.summary[printer.sortedControlNames[i]]
summaryTable.Append(generateRow(printer.sortedControlNames[i], controlSummary))
}
summaryTable.SetFooter(generateFooter(len(printer.summary), printer.frameworkSummary.TotalFailed, printer.frameworkSummary.TotalWarning, printer.frameworkSummary.TotalResources))
summaryTable.SetFooter(generateFooter(printer))
// summaryTable.SetFooter(generateFooter())
summaryTable.Render()
}
func (printer *PrettyPrinter) printFramework(frameworksNames []string) {
func (printer *PrettyPrinter) printFramework(frameworksNames []string, frameworkScores []float32) {
if len(frameworksNames) == 1 {
cautils.InfoTextDisplay(printer.writer, fmt.Sprintf("%s FRAMEWORK\n", frameworksNames[0]))
cautils.InfoTextDisplay(printer.writer, fmt.Sprintf("FRAMEWORK %s\n", frameworksNames[0]))
} else if len(frameworksNames) > 1 {
p := ""
p := "FRAMEWORKS: "
for i := 0; i < len(frameworksNames)-1; i++ {
p += frameworksNames[i] + ", "
p += fmt.Sprintf("%s (risk: %.2f), ", frameworksNames[i], frameworkScores[i])
}
p += frameworksNames[len(frameworksNames)-1]
cautils.InfoTextDisplay(printer.writer, fmt.Sprintf("%s FRAMEWORKS\n", p))
p += fmt.Sprintf("%s (risk: %.2f)\n", frameworksNames[len(frameworksNames)-1], frameworkScores[len(frameworkScores)-1])
cautils.InfoTextDisplay(printer.writer, p)
}
}
@@ -286,3 +277,7 @@ func getWriter(outputFile string) *os.File {
return os.Stdout
}
func getControlURL(controlID string) string {
return fmt.Sprintf("https://hub.armo.cloud/docs/%s", strings.ToLower(controlID))
}
-2
View File
@@ -6,8 +6,6 @@ import (
var INDENT = " "
const EmptyPercentage = "NaN"
const (
PrettyFormat string = "pretty-printer"
JsonFormat string = "json"
+1 -1
View File
@@ -25,7 +25,7 @@ func (prometheusPrinter *PrometheusPrinter) SetWriter(outputFile string) {
}
func (prometheusPrinter *PrometheusPrinter) Score(score float32) {
fmt.Printf("\n# Overall score out of 100\nkubescape_score %f\n", score*100)
fmt.Printf("\n# Overall risk-score (0- Excellent, 100- All failed)\nkubescape_score %d\n", int(score))
}
func (printer *PrometheusPrinter) printResources(allResources map[string]workloadinterface.IMetadata, resourcesIDs *reporthandling.ResourcesIDs, frameworkName, controlName string) {
+6 -4
View File
@@ -7,13 +7,15 @@ import (
"github.com/armosec/opa-utils/reporthandling"
)
type Summary map[string]ControlSummary
type Summary map[string]ResultSummary
func NewSummary() Summary {
return make(map[string]ControlSummary)
return make(map[string]ResultSummary)
}
type ControlSummary struct {
type ResultSummary struct {
ID string
RiskScore float32
TotalResources int
TotalFailed int
TotalWarning int
@@ -31,7 +33,7 @@ type WorkloadSummary struct {
status string
}
func (controlSummary *ControlSummary) ToSlice() []string {
func (controlSummary *ResultSummary) ToSlice() []string {
s := []string{}
s = append(s, fmt.Sprintf("%d", controlSummary.TotalFailed))
s = append(s, fmt.Sprintf("%d", controlSummary.TotalWarning))
+34 -11
View File
@@ -1,7 +1,9 @@
package printer
import (
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/k8s-interface/workloadinterface"
"github.com/armosec/opa-utils/objectsenvelopes"
"github.com/armosec/opa-utils/reporthandling"
)
@@ -9,19 +11,40 @@ import (
func groupByNamespaceOrKind(resources []WorkloadSummary, status func(workloadSummary *WorkloadSummary) bool) map[string][]WorkloadSummary {
mapResources := make(map[string][]WorkloadSummary)
for i := range resources {
if status(&resources[i]) {
if isKindToBeGrouped(resources[i].resource.GetKind()) {
if r, ok := mapResources[resources[i].resource.GetKind()]; ok {
r = append(r, resources[i])
mapResources[resources[i].resource.GetKind()] = r
} else {
mapResources[resources[i].resource.GetKind()] = []WorkloadSummary{resources[i]}
}
} else if r, ok := mapResources[resources[i].resource.GetNamespace()]; ok {
if !status(&resources[i]) {
continue
}
t := resources[i].resource.GetObjectType()
if t == objectsenvelopes.TypeRegoResponseVectorObject && !isKindToBeGrouped(resources[i].resource.GetKind()) {
t = workloadinterface.TypeWorkloadObject
}
switch t { // TODO - find a better way to defind the groups
case workloadinterface.TypeWorkloadObject:
ns := ""
if resources[i].resource.GetNamespace() != "" {
ns = "Namescape " + resources[i].resource.GetNamespace()
}
if r, ok := mapResources[ns]; ok {
r = append(r, resources[i])
mapResources[resources[i].resource.GetNamespace()] = r
mapResources[ns] = r
} else {
mapResources[resources[i].resource.GetNamespace()] = []WorkloadSummary{resources[i]}
mapResources[ns] = []WorkloadSummary{resources[i]}
}
case objectsenvelopes.TypeRegoResponseVectorObject:
group := resources[i].resource.GetKind() + "s"
if r, ok := mapResources[group]; ok {
r = append(r, resources[i])
mapResources[group] = r
} else {
mapResources[group] = []WorkloadSummary{resources[i]}
}
default:
group, _ := k8sinterface.SplitApiVersion(resources[i].resource.GetApiVersion())
if r, ok := mapResources[group]; ok {
r = append(r, resources[i])
mapResources[group] = r
} else {
mapResources[group] = []WorkloadSummary{resources[i]}
}
}
}
+1 -1
View File
@@ -25,6 +25,6 @@ func (reportMock *ReportMock) SetClusterName(clusterName string) {
}
func (reportMock *ReportMock) DisplayReportURL() {
message := fmt.Sprintf("\nYou 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 registering here: https://%s\n", getter.GetArmoAPIConnector().GetFrontendURL())
message := fmt.Sprintf("\nYou 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 registering here: https://%s/cli-signup \n", getter.GetArmoAPIConnector().GetFrontendURL())
cautils.InfoTextDisplay(os.Stdout, fmt.Sprintf("\n%s\n", message))
}
@@ -43,6 +43,10 @@ func NewReportEventReceiver(tenantConfig *cautils.ConfigObj) *ReportEventReceive
func (report *ReportEventReceiver) ActionSendReport(opaSessionObj *cautils.OPASessionObj) error {
if report.customerGUID == "" || report.clusterName == "" {
return fmt.Errorf("missing accout ID or cluster name. AccountID: '%s', Cluster name: '%s'", report.customerGUID, report.clusterName)
}
if err := report.prepareReport(opaSessionObj.PostureReport, opaSessionObj.AllResources); err != nil {
return err
}
@@ -54,13 +58,16 @@ func (report *ReportEventReceiver) SetCustomerGUID(customerGUID string) {
}
func (report *ReportEventReceiver) SetClusterName(clusterName string) {
report.clusterName = clusterName
report.clusterName = cautils.AdoptClusterName(clusterName) // clean cluster name
}
func (report *ReportEventReceiver) prepareReport(postureReport *reporthandling.PostureReport, allResources map[string]workloadinterface.IMetadata) error {
report.initEventReceiverURL()
host := hostToString(report.eventReceiverURL, postureReport.ReportID)
cautils.StartSpinner()
defer cautils.StopSpinner()
// send framework results
if err := report.sendReport(host, postureReport); err != nil {
return err
@@ -116,14 +123,14 @@ func (report *ReportEventReceiver) sendReport(host string, postureReport *report
}
func (report *ReportEventReceiver) DisplayReportURL() {
message := "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 registering here: "
message := "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 registering here:"
u := url.URL{}
u.Scheme = "https"
u.Host = getter.GetArmoAPIConnector().GetFrontendURL()
if report.customerAdminEMail != "" {
cautils.InfoTextDisplay(os.Stdout, "\n\n"+message+u.String()+"\n\n")
cautils.InfoTextDisplay(os.Stdout, fmt.Sprintf("\n\n%s %s/risk/%s\n(Account: %s)\n\n", message, u.String(), report.clusterName, report.customerGUID))
return
}
u.Path = "account/sign-up"
@@ -132,5 +139,5 @@ func (report *ReportEventReceiver) DisplayReportURL() {
q.Add("customerGUID", report.customerGUID)
u.RawQuery = q.Encode()
cautils.InfoTextDisplay(os.Stdout, "\n\n"+message+u.String()+"\n\n")
cautils.InfoTextDisplay(os.Stdout, fmt.Sprintf("\n\n%s %s\n\n", message, u.String()))
}
+5 -1
View File
@@ -34,7 +34,11 @@ func (resultsHandler *ResultsHandler) HandleResults(scanInfo *cautils.ScanInfo)
}
// TODO - get score from table
score := CalculatePostureScore(opaSessionObj.PostureReport)
var score float32 = 0
for i := range opaSessionObj.PostureReport.FrameworkReports {
score += opaSessionObj.PostureReport.FrameworkReports[i].Score
}
score /= float32(len(opaSessionObj.PostureReport.FrameworkReports))
resultsHandler.printerObj.Score(score)
return score