Compare commits

...

33 Commits

Author SHA1 Message Date
David Wertenteil
0cfdabd25a remove v1 format 2022-04-10 16:26:59 +03:00
David Wertenteil
8b6cb6c5d8 adding logs 2022-04-10 11:55:35 +03:00
David Wertenteil
3df3b7766c save policy in file 2022-04-10 09:33:44 +03:00
David Wertenteil
0d83654197 update resource table 2022-04-07 16:44:56 +03:00
David Wertenteil
bd9e44382e format v2 2022-04-07 13:54:49 +03:00
David Wertenteil
2a7c20ea94 report format version 2022-04-07 11:59:19 +03:00
David Wertenteil
bde0dc9a17 convert score to int 2022-04-07 11:00:39 +03:00
David Wertenteil
7d7336ae01 support only one input 2022-04-07 09:53:44 +03:00
David Wertenteil
e5e608324d send file meta 2022-04-06 16:11:57 +03:00
David Wertenteil
569c1444f7 Merge remote-tracking branch 'armosec/dev' 2022-04-06 09:45:32 +03:00
dwertent
dc5ef28324 update output 2022-04-06 09:29:28 +03:00
David Wertenteil
aea6c0eab8 Merge pull request #477 from Daniel-GrunbergerCA/master
Fix error msg for cloud
2022-04-06 09:23:12 +03:00
DanielGrunbergerCA
c80a15d0cf Merge remote-tracking branch 'upstream/dev' 2022-04-05 17:19:11 +03:00
DanielGrunbergerCA
1ddd57aa1d fix error msg for cloud 2022-04-05 17:18:50 +03:00
David Wertenteil
55adb0da6b Merge pull request #474 from Daniel-GrunbergerCA/master
Fix posture control inputs for yaml scan
2022-04-05 16:28:39 +03:00
DanielGrunbergerCA
a716289cc8 go mod 2022-04-05 14:10:11 +03:00
DanielGrunbergerCA
093d71fff4 exit status 1 2022-04-05 14:05:31 +03:00
DanielGrunbergerCA
4fe40e348d Merge branch 'master' of github.com:Daniel-GrunbergerCA/kubescape 2022-04-05 13:40:41 +03:00
DanielGrunbergerCA
29a67b806d Merge remote-tracking branch 'upstream/dev' 2022-04-05 13:39:39 +03:00
DanielGrunbergerCA
0169f42747 Merge remote-tracking branch 'upstream/master' 2022-04-05 13:39:28 +03:00
David Wertenteil
92e100c497 Merge branch 'dev' into master 2022-04-04 13:39:17 +03:00
DanielGrunbergerCA
536fe970f7 fix go mod 2022-04-04 12:31:45 +03:00
DanielGrunbergerCA
85526b06b6 fix control input for yaml scan 2022-04-04 12:27:30 +03:00
dwertent
d9b6c048d5 Merge remote-tracking branch 'armosec/dev' 2022-04-04 12:14:45 +03:00
David Wertenteil
7e46a6529a Merge pull request #473 from Lucifergene/dev
Added Severity Column with colored text
2022-04-04 12:14:35 +03:00
dwertent
2dc5fd80da Merge remote-tracking branch 'armosec/dev' 2022-04-04 12:14:13 +03:00
dwertent
e89cc8ca24 register usesrs download from BE 2022-04-04 12:13:35 +03:00
dwertent
d39aeb0691 update metrics 2022-04-04 11:32:56 +03:00
Lucifergene
da9d98134a Added Severity Column with colored text 2022-04-04 03:46:05 +05:30
David Wertenteil
9992a9a0e4 Merge pull request #472 from falconcode16/master
Improved grammatical mistakes and typos
2022-04-03 10:03:15 +03:00
Rohit Patil
adc8a16e85 Improved grammatical mistakes and typos 2022-04-03 10:40:56 +05:30
dwertent
58b833c18a support yaml scan submit 2022-03-28 09:40:11 +03:00
DanielGrunbergerCA
6372ce5647 Merge branch 'dev' 2022-03-16 12:13:16 +02:00
49 changed files with 864 additions and 354 deletions

View File

@@ -38,7 +38,7 @@ curl -s https://raw.githubusercontent.com/armosec/kubescape/master/install.sh |
## Run:
```
kubescape scan --submit --enable-host-scan
kubescape scan --submit --enable-host-scan --format-version v2 --verbose
```
<img src="docs/summary.png">
@@ -115,7 +115,7 @@ Set-ExecutionPolicy RemoteSigned -scope CurrentUser
#### Scan a running Kubernetes cluster and submit results to the [Kubescape SaaS version](https://portal.armo.cloud/)
```
kubescape scan --submit --enable-host-scan
kubescape scan --submit --enable-host-scan --verbose
```
> Read [here](https://hub.armo.cloud/docs/host-sensor) more about the `enable-host-scan` flag

View File

@@ -7,8 +7,9 @@ replace github.com/armosec/kubescape/core => ../core
require (
github.com/armosec/k8s-interface v0.0.68
github.com/armosec/kubescape/core v0.0.0-00010101000000-000000000000
github.com/armosec/opa-utils v0.0.127
github.com/armosec/opa-utils v0.0.130
github.com/armosec/rbac-utils v0.0.14
github.com/enescakir/emoji v1.0.0
github.com/google/uuid v1.3.0
github.com/mattn/go-isatty v0.0.14
github.com/spf13/cobra v1.4.0
@@ -51,7 +52,6 @@ require (
github.com/docker/docker v20.10.9+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/enescakir/emoji v1.0.0 // indirect
github.com/envoyproxy/go-control-plane v0.10.1 // indirect
github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect
github.com/fatih/color v1.13.0 // indirect

View File

@@ -109,8 +109,8 @@ github.com/armosec/k8s-interface v0.0.66/go.mod h1:vwprS8qn/iowd5yf0JHpqDsLA5I8W
github.com/armosec/k8s-interface v0.0.68 h1:6CtSakISiI47YHkxh+Va9FzZQIBkWa6g9sbiNxq1Zkk=
github.com/armosec/k8s-interface v0.0.68/go.mod h1:PeWn41C2uenZi+xfZdyFF/zG5wXACA00htQyknDUWDE=
github.com/armosec/opa-utils v0.0.64/go.mod h1:6tQP8UDq2EvEfSqh8vrUdr/9QVSCG4sJfju1SXQOn4c=
github.com/armosec/opa-utils v0.0.127 h1:uVyH/+pjpiA0oBO6QODjtdCiPgHx7KQ/TcxxkPvAl4c=
github.com/armosec/opa-utils v0.0.127/go.mod h1:gap+EaLG5rnyqvIRGxtdNDC9y7VvoGNm90zK8Ls7avQ=
github.com/armosec/opa-utils v0.0.130 h1:uP60M0PzmDtLqvsA/jX8BED9/Ava4n2QG7VCkuI+hwI=
github.com/armosec/opa-utils v0.0.130/go.mod h1:gap+EaLG5rnyqvIRGxtdNDC9y7VvoGNm90zK8Ls7avQ=
github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40=
github.com/armosec/rbac-utils v0.0.14 h1:CKYKcgqJEXWF2Hen/B1pVGtS3nDAG1wp9dDv6oNtq90=
github.com/armosec/rbac-utils v0.0.14/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0=

View File

@@ -84,5 +84,8 @@ func getRootCmd(ks meta.IKubescape) *cobra.Command {
func main() {
ks := NewDefaultKubescapeCommand()
ks.Execute()
err := ks.Execute()
if err != nil {
logger.L().Fatal(err.Error())
}
}

View File

@@ -8,8 +8,10 @@ import (
"github.com/armosec/kubescape/core/cautils"
"github.com/armosec/kubescape/core/cautils/logger"
"github.com/armosec/kubescape/core/cautils/logger/helpers"
"github.com/armosec/kubescape/core/meta"
"github.com/armosec/opa-utils/reporthandling"
"github.com/enescakir/emoji"
"github.com/spf13/cobra"
)
@@ -66,7 +68,7 @@ func getControlCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comman
if len(args) > 1 {
if len(args[1:]) == 0 || args[1] != "-" {
scanInfo.InputPatterns = args[1:]
scanInfo.InputPatterns = []string{args[1]}
} else { // store stdin to file - do NOT move to separate function !!
tempFile, err := os.CreateTemp(".", "tmp-kubescape*.yaml")
if err != nil {
@@ -88,36 +90,16 @@ func getControlCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comman
if err != nil {
logger.L().Fatal(err.Error())
}
results.HandleResults()
if err := results.HandleResults(); err != nil {
logger.L().Fatal(err.Error())
}
if !scanInfo.VerboseMode {
cautils.SimpleDisplay(os.Stderr, "%s Run with '--verbose'/'-v' flag for detailed resources view\n\n", emoji.Detective)
}
if results.GetRiskScore() > float32(scanInfo.FailThreshold) {
return fmt.Errorf("scan risk-score %.2f is above permitted threshold %.2f", results.GetRiskScore(), scanInfo.FailThreshold)
logger.L().Fatal("scan risk-score is above permitted threshold", helpers.String("risk-score", fmt.Sprintf("%.2f", results.GetRiskScore())), helpers.String("fail-threshold", fmt.Sprintf("%.2f", scanInfo.FailThreshold)))
}
return nil
},
}
}
// func flagValidationControl() {
// if 100 < scanInfo.FailThreshold {
// logger.L().Fatal("bad argument: out of range threshold")
// }
// }
// func setScanForFirstControl(scanInfo, controls []string) []reporthandling.PolicyIdentifier {
// newPolicy := reporthandling.PolicyIdentifier{}
// newPolicy.Kind = reporthandling.KindControl
// newPolicy.Name = controls[0]
// scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy)
// return scanInfo.PolicyIdentifier
// }
// func SetScanForGivenControls(scanInfo, controls []string) []reporthandling.PolicyIdentifier {
// for _, control := range controls {
// control := strings.TrimLeft(control, " ")
// newPolicy := reporthandling.PolicyIdentifier{}
// newPolicy.Kind = reporthandling.KindControl
// newPolicy.Name = control
// scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy)
// }
// return scanInfo.PolicyIdentifier
// }

View File

@@ -8,8 +8,10 @@ import (
"github.com/armosec/kubescape/core/cautils"
"github.com/armosec/kubescape/core/cautils/logger"
"github.com/armosec/kubescape/core/cautils/logger/helpers"
"github.com/armosec/kubescape/core/meta"
"github.com/armosec/opa-utils/reporthandling"
"github.com/enescakir/emoji"
"github.com/spf13/cobra"
)
@@ -27,7 +29,7 @@ var (
# Scan all frameworks
kubescape scan framework all
# Scan kubernetes YAML manifest files
# Scan kubernetes YAML manifest files (single file or glob)
kubescape scan framework nsa *.yaml
Run 'kubescape list frameworks' for the list of supported frameworks
@@ -58,7 +60,9 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
},
RunE: func(cmd *cobra.Command, args []string) error {
flagValidationFramework(scanInfo)
if err := flagValidationFramework(scanInfo); err != nil {
return err
}
scanInfo.FrameworkScan = true
var frameworks []string
@@ -74,7 +78,7 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
}
if len(args) > 1 {
if len(args[1:]) == 0 || args[1] != "-" {
scanInfo.InputPatterns = args[1:]
scanInfo.InputPatterns = []string{args[1]}
} else { // store stdin to file - do NOT move to separate function !!
tempFile, err := os.CreateTemp(".", "tmp-kubescape*.yaml")
if err != nil {
@@ -97,34 +101,27 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
if err != nil {
logger.L().Fatal(err.Error())
}
results.HandleResults()
if err = results.HandleResults(); err != nil {
logger.L().Fatal(err.Error())
}
if !scanInfo.VerboseMode {
cautils.SimpleDisplay(os.Stderr, "%s Run with '--verbose'/'-v' flag for detailed resources view\n\n", emoji.Detective)
}
if results.GetRiskScore() > float32(scanInfo.FailThreshold) {
return fmt.Errorf("scan risk-score %.2f is above permitted threshold %.2f", results.GetRiskScore(), scanInfo.FailThreshold)
logger.L().Fatal("scan risk-score is above permitted threshold", helpers.String("risk-score", fmt.Sprintf("%.2f", results.GetRiskScore())), helpers.String("fail-threshold", fmt.Sprintf("%.2f", scanInfo.FailThreshold)))
}
return nil
},
}
}
// func init() {
// scanCmd.AddCommand(frameworkCmd)
// scanInfo = cautils.ScanInfo{}
// }
// func SetScanForFirstFramework(frameworks []string) []reporthandling.PolicyIdentifier {
// newPolicy := reporthandling.PolicyIdentifier{}
// newPolicy.Kind = reporthandling.KindFramework
// newPolicy.Name = frameworks[0]
// scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy)
// return scanInfo.PolicyIdentifier
// }
func flagValidationFramework(scanInfo *cautils.ScanInfo) {
func flagValidationFramework(scanInfo *cautils.ScanInfo) error {
if scanInfo.Submit && scanInfo.Local {
logger.L().Fatal("you can use `keep-local` or `submit`, but not both")
return fmt.Errorf("you can use `keep-local` or `submit`, but not both")
}
if 100 < scanInfo.FailThreshold {
logger.L().Fatal("bad argument: out of range threshold")
if 100 < scanInfo.FailThreshold || 0 > scanInfo.FailThreshold {
return fmt.Errorf("bad argument: out of range threshold")
}
return nil
}

View File

@@ -11,7 +11,7 @@ var scanCmdExamples = `
Scan command is for scanning an existing cluster or kubernetes manifest files based on pre-defind frameworks
# Scan current cluster with all frameworks
kubescape scan --submit --enable-host-scan
kubescape scan --submit --enable-host-scan --verbose
# Scan kubernetes YAML manifest files
kubescape scan *.yaml
@@ -72,7 +72,7 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
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")
scanCmd.PersistentFlags().StringVarP(&scanInfo.Output, "output", "o", "", "Output file. Print output to file and not stdout")
scanCmd.PersistentFlags().BoolVar(&scanInfo.VerboseMode, "verbose", false, "Display all of the input resources and not only failed resources")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.VerboseMode, "verbose", "v", false, "Display all of the input resources and not only failed resources")
scanCmd.PersistentFlags().BoolVar(&scanInfo.UseDefault, "use-default", false, "Load local policy object from default path. If not used will download latest")
scanCmd.PersistentFlags().StringSliceVar(&scanInfo.UseFrom, "use-from", nil, "Load local policy object from specified path. If not used will download latest")
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")

View File

@@ -69,6 +69,7 @@ type ITenantConfig interface {
// getters
GetClusterName() string
GetAccountID() string
GetTennatEmail() string
GetConfigObj() *ConfigObj
// GetBackendAPI() getter.IBackend
// GenerateURL()
@@ -118,6 +119,7 @@ func NewLocalConfig(
}
func (lc *LocalConfig) GetConfigObj() *ConfigObj { return lc.configObj }
func (lc *LocalConfig) GetTennatEmail() string { return lc.configObj.CustomerAdminEMail }
func (lc *LocalConfig) GetAccountID() string { return lc.configObj.AccountID }
func (lc *LocalConfig) GetClusterName() string { return lc.configObj.ClusterName }
func (lc *LocalConfig) IsConfigFound() bool { return existsConfigFile() }
@@ -232,6 +234,7 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken
func (c *ClusterConfig) GetConfigObj() *ConfigObj { return c.configObj }
func (c *ClusterConfig) GetDefaultNS() string { return c.configMapNamespace }
func (c *ClusterConfig) GetAccountID() string { return c.configObj.AccountID }
func (c *ClusterConfig) GetTennatEmail() string { return c.configObj.CustomerAdminEMail }
func (c *ClusterConfig) IsConfigFound() bool { return existsConfigFile() || c.existsConfigMap() }
func (c *ClusterConfig) SetTenant() error {

View File

@@ -0,0 +1,18 @@
package cautils
import "math"
// Float64ToInt convert float64 to int
func Float64ToInt(x float64) int {
return int(math.Round(x))
}
// Float32ToInt convert float32 to int
func Float32ToInt(x float32) int {
return Float64ToInt(float64(x))
}
// Float16ToInt convert float16 to int
func Float16ToInt(x float32) int {
return Float64ToInt(float64(x))
}

View File

@@ -0,0 +1,24 @@
package cautils
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestFloat64ToInt(t *testing.T) {
assert.Equal(t, 3, Float64ToInt(3.49))
assert.Equal(t, 4, Float64ToInt(3.5))
assert.Equal(t, 4, Float64ToInt(3.51))
}
func TestFloat32ToInt(t *testing.T) {
assert.Equal(t, 3, Float32ToInt(3.49))
assert.Equal(t, 4, Float32ToInt(3.5))
assert.Equal(t, 4, Float32ToInt(3.51))
}
func TestFloat16ToInt(t *testing.T) {
assert.Equal(t, 3, Float16ToInt(3.49))
assert.Equal(t, 4, Float16ToInt(3.5))
assert.Equal(t, 4, Float16ToInt(3.51))
}

View File

@@ -163,7 +163,6 @@ func (armoAPI *ArmoAPI) GetFramework(name string) (*reporthandling.Framework, er
if err = JSONDecoder(respStr).Decode(framework); err != nil {
return nil, err
}
SaveInFile(framework, GetDefaultPath(name+".json"))
return framework, err
}
@@ -233,7 +232,14 @@ func (armoAPI *ArmoAPI) GetAccountConfig(clusterName string) (*armotypes.Custome
}
if err = JSONDecoder(respStr).Decode(&accountConfig); err != nil {
return nil, err
// try with default scope
respStr, err = armoAPI.Get(armoAPI.getAccountConfigDefault(clusterName), nil)
if err != nil {
return nil, err
}
if err = JSONDecoder(respStr).Decode(&accountConfig); err != nil {
return nil, err
}
}
return accountConfig, nil

View File

@@ -73,6 +73,12 @@ func (armoAPI *ArmoAPI) exceptionsURL(exceptionsPolicyName string) string {
return u.String()
}
func (armoAPI *ArmoAPI) getAccountConfigDefault(clusterName string) string {
config := armoAPI.getAccountConfig(clusterName)
url := config + "&scope=default"
return url
}
func (armoAPI *ArmoAPI) getAccountConfig(clusterName string) string {
u := url.URL{}
u.Scheme = "https"

View File

@@ -14,6 +14,7 @@ import (
"github.com/armosec/kubescape/core/cautils/logger/helpers"
"github.com/armosec/opa-utils/reporthandling"
reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2"
"github.com/google/uuid"
)
const (
@@ -100,6 +101,10 @@ func (scanInfo *ScanInfo) Init() {
scanInfo.setUseFrom()
scanInfo.setOutputFile()
scanInfo.setUseArtifactsFrom()
if scanInfo.ScanID == "" {
scanInfo.ScanID = uuid.NewString()
}
}
func (scanInfo *ScanInfo) setUseArtifactsFrom() {
@@ -193,8 +198,8 @@ func (scanInfo *ScanInfo) contains(policyName string) bool {
func scanInfoToScanMetadata(scanInfo *ScanInfo) *reporthandlingv2.Metadata {
metadata := &reporthandlingv2.Metadata{}
metadata.ClusterMetadata.ContextName = k8sinterface.GetClusterName()
metadata.ScanMetadata.Format = scanInfo.Format
metadata.ScanMetadata.FormatVersion = scanInfo.FormatVersion
metadata.ScanMetadata.Submit = scanInfo.Submit
// TODO - Add excluded and included namespaces
@@ -222,8 +227,61 @@ func scanInfoToScanMetadata(scanInfo *ScanInfo) *reporthandlingv2.Metadata {
metadata.ScanMetadata.ScanningTarget = reporthandlingv2.Cluster
if scanInfo.GetScanningEnvironment() == ScanLocalFiles {
metadata.ScanMetadata.ScanningTarget = reporthandlingv2.Files
metadata.ScanMetadata.ScanningTarget = reporthandlingv2.File
}
inputFiles := ""
if len(scanInfo.InputPatterns) > 0 {
inputFiles = scanInfo.InputPatterns[0]
}
setContextMetadata(&metadata.ContextMetadata, inputFiles)
return metadata
}
func setContextMetadata(contextMetadata *reporthandlingv2.ContextMetadata, input string) {
// if cluster
if input == "" {
contextMetadata.ClusterContextMetadata = &reporthandlingv2.ClusterMetadata{
ContextName: k8sinterface.GetClusterName(),
}
return
}
// if url
if strings.HasPrefix(input, "http") { // TODO - check if can parse
return
}
if !filepath.IsAbs(input) {
if o, err := os.Getwd(); err == nil {
input = filepath.Join(o, input)
}
}
// if single file
if IsFile(input) {
contextMetadata.FileContextMetadata = &reporthandlingv2.FileContextMetadata{
FilePath: input,
HostName: getHostname(),
}
return
}
// if dir/glob
if !IsFile(input) {
contextMetadata.DirectoryContextMetadata = &reporthandlingv2.DirectoryContextMetadata{
BasePath: input,
HostName: getHostname(),
}
return
}
}
func getHostname() string {
if h, e := os.Hostname(); e == nil {
return h
}
return ""
}

View File

@@ -0,0 +1,65 @@
package cautils
import (
"testing"
reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2"
"github.com/stretchr/testify/assert"
)
// func TestSetInputPatterns(t *testing.T) { //Unitest
// {
// scanInfo := ScanInfo{
// InputPatterns: []string{"file"},
// }
// scanInfo.setInputPatterns()
// assert.Equal(t, "file", scanInfo.InputPatterns[0])
// }
// }
func TestSetContextMetadata(t *testing.T) {
{
ctx := reporthandlingv2.ContextMetadata{}
setContextMetadata(&ctx, "")
assert.NotNil(t, ctx.ClusterContextMetadata)
assert.Nil(t, ctx.DirectoryContextMetadata)
assert.Nil(t, ctx.FileContextMetadata)
assert.Nil(t, ctx.HelmContextMetadata)
assert.Nil(t, ctx.RepoContextMetadata)
}
{
ctx := reporthandlingv2.ContextMetadata{}
setContextMetadata(&ctx, "file")
assert.Nil(t, ctx.ClusterContextMetadata)
assert.NotNil(t, ctx.DirectoryContextMetadata)
assert.Nil(t, ctx.FileContextMetadata)
assert.Nil(t, ctx.HelmContextMetadata)
assert.Nil(t, ctx.RepoContextMetadata)
}
{
ctx := reporthandlingv2.ContextMetadata{}
setContextMetadata(&ctx, "scaninfo_test.go")
assert.Nil(t, ctx.ClusterContextMetadata)
assert.Nil(t, ctx.DirectoryContextMetadata)
assert.NotNil(t, ctx.FileContextMetadata)
assert.Nil(t, ctx.HelmContextMetadata)
assert.Nil(t, ctx.RepoContextMetadata)
}
{
ctx := reporthandlingv2.ContextMetadata{}
setContextMetadata(&ctx, "https://github.com/armosec/kubescape")
assert.Nil(t, ctx.ClusterContextMetadata)
assert.Nil(t, ctx.DirectoryContextMetadata)
assert.Nil(t, ctx.FileContextMetadata)
assert.Nil(t, ctx.HelmContextMetadata)
assert.Nil(t, ctx.RepoContextMetadata) // TODO
}
}
func TestGetHostname(t *testing.T) {
assert.NotEqual(t, "", getHostname())
}

View File

@@ -130,7 +130,7 @@ func downloadFramework(downloadInfo *metav1.DownloadInfo) error {
tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi())
g := getPolicyGetter(nil, tenant.GetAccountID(), true, nil)
g := getPolicyGetter(nil, tenant.GetTennatEmail(), true, nil)
if downloadInfo.Name == "" {
// if framework name not specified - download all frameworks
@@ -172,7 +172,7 @@ func downloadControl(downloadInfo *metav1.DownloadInfo) error {
tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi())
g := getPolicyGetter(nil, tenant.GetAccountID(), false, nil)
g := getPolicyGetter(nil, tenant.GetTennatEmail(), false, nil)
if downloadInfo.Name == "" {
// TODO - support

View File

@@ -2,7 +2,6 @@ package core
import (
"fmt"
"os"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/core/cautils"
@@ -48,11 +47,11 @@ func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.Kubern
return nil
}
func getReporter(tenantConfig cautils.ITenantConfig, reportID string, submit, fwScan, clusterScan bool) reporter.IReport {
if submit && clusterScan {
func getReporter(tenantConfig cautils.ITenantConfig, reportID string, submit, fwScan bool) reporter.IReport {
if submit {
return reporterv2.NewReportEventReceiver(tenantConfig.GetConfigObj(), reportID)
}
if tenantConfig.GetAccountID() == "" && fwScan && clusterScan {
if tenantConfig.GetAccountID() == "" {
// Add link only when scanning a cluster using a framework
return reporterv2.NewReportMock(reporterv2.NO_SUBMIT_QUERY, "run kubescape with the '--submit' flag")
}
@@ -60,9 +59,7 @@ func getReporter(tenantConfig cautils.ITenantConfig, reportID string, submit, fw
if !fwScan {
message = "Kubescape does not submit scan results when scanning controls"
}
if !clusterScan {
message = "Kubescape will submit scan results only when scanning a cluster (not YAML files)"
}
return reporterv2.NewReportMock("", message)
}
@@ -153,11 +150,11 @@ func setSubmitBehavior(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantC
}
// setPolicyGetter set the policy getter - local file/github release/ArmoAPI
func getPolicyGetter(loadPoliciesFromFile []string, accountID string, frameworkScope bool, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IPolicyGetter {
func getPolicyGetter(loadPoliciesFromFile []string, tennatEmail string, frameworkScope bool, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IPolicyGetter {
if len(loadPoliciesFromFile) > 0 {
return getter.NewLoadPolicy(loadPoliciesFromFile)
}
if accountID != "" && frameworkScope {
if tennatEmail != "" && frameworkScope {
g := getter.GetArmoAPIConnector() // download policy from ARMO backend
return g
}
@@ -195,7 +192,7 @@ func getConfigInputsGetter(ControlsInputs string, accountID string, downloadRele
downloadReleasedPolicy = getter.NewDownloadReleasedPolicy()
}
if err := downloadReleasedPolicy.SetRegoObjects(); err != nil { // if failed to pull config inputs, fallback to BE
cautils.WarningDisplay(os.Stderr, "Warning: failed to get config inputs from github release, this may affect the scanning results\n")
logger.L().Warning("failed to get config inputs from github release, this may affect the scanning results", helpers.Error(err))
}
return downloadReleasedPolicy
}

View File

@@ -45,7 +45,7 @@ func (ks *Kubescape) List(listPolicies *metav1.ListPolicies) error {
func listFrameworks(listPolicies *metav1.ListPolicies) ([]string, error) {
tenant := getTenantConfig(listPolicies.Account, "", getKubernetesApi()) // change k8sinterface
g := getPolicyGetter(nil, tenant.GetAccountID(), true, nil)
g := getPolicyGetter(nil, tenant.GetTennatEmail(), true, nil)
return listFrameworksNames(g), nil
}
@@ -53,7 +53,7 @@ func listFrameworks(listPolicies *metav1.ListPolicies) ([]string, error) {
func listControls(listPolicies *metav1.ListPolicies) ([]string, error) {
tenant := getTenantConfig(listPolicies.Account, "", getKubernetesApi()) // change k8sinterface
g := getPolicyGetter(nil, tenant.GetAccountID(), false, nil)
g := getPolicyGetter(nil, tenant.GetTennatEmail(), false, nil)
l := getter.ListName
if listPolicies.ListIDs {
l = getter.ListID

View File

@@ -5,7 +5,6 @@ import (
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/google/uuid"
"github.com/armosec/kubescape/core/cautils"
"github.com/armosec/kubescape/core/cautils/getter"
@@ -32,9 +31,6 @@ type componentInterfaces struct {
}
func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
if scanInfo.ScanID == "" {
scanInfo.ScanID = uuid.NewString()
}
// ================== setup k8s interface object ======================================
var k8s *k8sinterface.KubernetesApi
@@ -52,6 +48,11 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
// Set submit behavior AFTER loading tenant config
setSubmitBehavior(scanInfo, tenantConfig)
// Do not submit yaml scanning
if len(scanInfo.InputPatterns) > 0 {
scanInfo.Submit = false
}
if scanInfo.Submit {
// submit - Create tenant & Submit report
if err := tenantConfig.SetTenant(); err != nil {
@@ -90,7 +91,7 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
// ================== setup reporter & printer objects ======================================
// reporting behavior - setup reporter
reportHandler := getReporter(tenantConfig, scanInfo.ScanID, scanInfo.Submit, scanInfo.FrameworkScan, len(scanInfo.InputPatterns) == 0)
reportHandler := getReporter(tenantConfig, scanInfo.ScanID, scanInfo.Submit, scanInfo.FrameworkScan)
// setup printer
printerHandler := resultshandling.NewPrinter(scanInfo.Format, scanInfo.FormatVersion, scanInfo.VerboseMode)
@@ -123,7 +124,7 @@ func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsH
downloadReleasedPolicy := getter.NewDownloadReleasedPolicy() // download config inputs from github release
// set policy getter only after setting the customerGUID
scanInfo.Getters.PolicyGetter = getPolicyGetter(scanInfo.UseFrom, interfaces.tenantConfig.GetAccountID(), scanInfo.FrameworkScan, downloadReleasedPolicy)
scanInfo.Getters.PolicyGetter = getPolicyGetter(scanInfo.UseFrom, interfaces.tenantConfig.GetTennatEmail(), scanInfo.FrameworkScan, downloadReleasedPolicy)
scanInfo.Getters.ControlsInputsGetter = getConfigInputsGetter(scanInfo.ControlsInputs, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
scanInfo.Getters.ExceptionsGetter = getExceptionsGetter(scanInfo.UseExceptions)

View File

@@ -5,7 +5,7 @@ go 1.17
require (
github.com/armosec/armoapi-go v0.0.58
github.com/armosec/k8s-interface v0.0.68
github.com/armosec/opa-utils v0.0.127
github.com/armosec/opa-utils v0.0.130
github.com/armosec/rbac-utils v0.0.14
github.com/armosec/utils-go v0.0.3
github.com/armosec/utils-k8s-go v0.0.3

View File

@@ -109,8 +109,8 @@ github.com/armosec/k8s-interface v0.0.66/go.mod h1:vwprS8qn/iowd5yf0JHpqDsLA5I8W
github.com/armosec/k8s-interface v0.0.68 h1:6CtSakISiI47YHkxh+Va9FzZQIBkWa6g9sbiNxq1Zkk=
github.com/armosec/k8s-interface v0.0.68/go.mod h1:PeWn41C2uenZi+xfZdyFF/zG5wXACA00htQyknDUWDE=
github.com/armosec/opa-utils v0.0.64/go.mod h1:6tQP8UDq2EvEfSqh8vrUdr/9QVSCG4sJfju1SXQOn4c=
github.com/armosec/opa-utils v0.0.127 h1:uVyH/+pjpiA0oBO6QODjtdCiPgHx7KQ/TcxxkPvAl4c=
github.com/armosec/opa-utils v0.0.127/go.mod h1:gap+EaLG5rnyqvIRGxtdNDC9y7VvoGNm90zK8Ls7avQ=
github.com/armosec/opa-utils v0.0.130 h1:uP60M0PzmDtLqvsA/jX8BED9/Ava4n2QG7VCkuI+hwI=
github.com/armosec/opa-utils v0.0.130/go.mod h1:gap+EaLG5rnyqvIRGxtdNDC9y7VvoGNm90zK8Ls7avQ=
github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40=
github.com/armosec/rbac-utils v0.0.14 h1:CKYKcgqJEXWF2Hen/B1pVGtS3nDAG1wp9dDv6oNtq90=
github.com/armosec/rbac-utils v0.0.14/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0=

View File

@@ -3,7 +3,6 @@ package opaprocessor
import (
"github.com/armosec/kubescape/core/cautils"
"github.com/armosec/kubescape/core/cautils/logger"
"github.com/armosec/kubescape/core/cautils/logger/helpers"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/k8s-interface/workloadinterface"
@@ -116,7 +115,7 @@ func getKubernetesObjects(k8sResources *cautils.K8SResources, allResources map[s
for _, groupResource := range groupResources {
if k8sObj, ok := (*k8sResources)[groupResource]; ok {
if k8sObj == nil {
logger.L().Debug("skipping", helpers.String("resource", groupResource))
// logger.L().Debug("skipping", helpers.String("resource", groupResource))
}
for i := range k8sObj {
k8sObjects = append(k8sObjects, allResources[k8sObj[i]])

View File

@@ -5,7 +5,9 @@ import (
"strings"
"github.com/armosec/kubescape/core/cautils"
"github.com/armosec/kubescape/core/cautils/getter"
"github.com/armosec/kubescape/core/cautils/logger"
"github.com/armosec/kubescape/core/cautils/logger/helpers"
"github.com/armosec/opa-utils/reporthandling"
)
@@ -54,6 +56,11 @@ func (policyHandler *PolicyHandler) getScanPolicies(notification *reporthandling
}
if receivedFramework != nil {
frameworks = append(frameworks, *receivedFramework)
cache := getter.GetDefaultPath(rule.Name + ".json")
if err := getter.SaveInFile(receivedFramework, cache); err != nil {
logger.L().Warning("failed to cache file", helpers.String("file", cache), helpers.Error(err))
}
}
}
case reporthandling.KindControl: // Download controls
@@ -67,6 +74,11 @@ func (policyHandler *PolicyHandler) getScanPolicies(notification *reporthandling
}
if receivedControl != nil {
f.Controls = append(f.Controls, *receivedControl)
cache := getter.GetDefaultPath(rule.Name + ".json")
if err := getter.SaveInFile(receivedControl, cache); err != nil {
logger.L().Warning("failed to cache file", helpers.String("file", cache), helpers.Error(err))
}
}
}
frameworks = append(frameworks, f)

View File

@@ -2,7 +2,6 @@ package resourcehandler
import (
"fmt"
"os"
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/k8s-interface/workloadinterface"
@@ -10,6 +9,8 @@ import (
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/core/cautils"
"github.com/armosec/kubescape/core/cautils/logger"
"github.com/armosec/kubescape/core/cautils/logger/helpers"
)
// FileResourceHandler handle resources from files and URLs
@@ -38,23 +39,24 @@ func (fileHandler *FileResourceHandler) GetResources(sessionObj *cautils.OPASess
workloads := []workloadinterface.IMetadata{}
// load resource from local file system
w, err := cautils.LoadResourcesFromFiles(fileHandler.inputPatterns)
sourceToWorkloads, err := cautils.LoadResourcesFromFiles(fileHandler.inputPatterns)
if err != nil {
return nil, allResources, nil, err
}
for source, ws := range w {
for source, ws := range sourceToWorkloads {
workloads = append(workloads, ws...)
for i := range ws {
workloadIDToSource[ws[i].GetID()] = source
}
}
logger.L().Debug("files found in local storage", helpers.Int("files", len(sourceToWorkloads)), helpers.Int("workloads", len(workloads)))
// load resources from url
w, err = loadResourcesFromUrl(fileHandler.inputPatterns)
sourceToWorkloads, err = loadResourcesFromUrl(fileHandler.inputPatterns)
if err != nil {
return nil, allResources, nil, err
}
for source, ws := range w {
for source, ws := range sourceToWorkloads {
workloads = append(workloads, ws...)
for i := range ws {
workloadIDToSource[ws[i].GetID()] = source
@@ -64,6 +66,8 @@ func (fileHandler *FileResourceHandler) GetResources(sessionObj *cautils.OPASess
if len(workloads) == 0 {
return nil, allResources, nil, fmt.Errorf("empty list of workloads - no workloads found")
}
logger.L().Debug("files found in git repo", helpers.Int("files", len(sourceToWorkloads)), helpers.Int("workloads", len(workloads)))
sessionObj.ResourceSource = workloadIDToSource
// map all resources: map["/group/version/kind"][]<k8s workloads>
@@ -82,7 +86,7 @@ func (fileHandler *FileResourceHandler) GetResources(sessionObj *cautils.OPASess
}
if err := fileHandler.registryAdaptors.collectImagesVulnerabilities(k8sResources, allResources, armoResources); err != nil {
cautils.WarningDisplay(os.Stderr, "Warning: failed to collect images vulnerabilities: %s\n", err.Error())
logger.L().Warning("failed to collect images vulnerabilities", helpers.Error(err))
}
return k8sResources, allResources, armoResources, nil

View File

@@ -75,7 +75,9 @@ func (k8sHandler *K8sResourceHandler) GetResources(sessionObj *cautils.OPASessio
if err != nil {
logger.L().Debug("failed to collect worker nodes number", helpers.Error(err))
} else {
sessionObj.Metadata.ClusterMetadata.NumberOfWorkerNodes = numberOfWorkerNodes
if sessionObj.Metadata != nil && sessionObj.Metadata.ContextMetadata.ClusterContextMetadata != nil {
sessionObj.Metadata.ContextMetadata.ClusterContextMetadata.NumberOfWorkerNodes = numberOfWorkerNodes
}
}
imgVulnResources := cautils.MapImageVulnResources(armoResourceMap)
@@ -118,7 +120,9 @@ func (k8sHandler *K8sResourceHandler) GetResources(sessionObj *cautils.OPASessio
logger.L().Warning("failed to collect cloud data", helpers.Error(err))
}
if provider != "" {
sessionObj.Metadata.ClusterMetadata.CloudProvider = provider
if sessionObj.Metadata != nil && sessionObj.Metadata.ContextMetadata.ClusterContextMetadata != nil {
sessionObj.Metadata.ContextMetadata.ClusterContextMetadata.CloudProvider = provider
}
}
}
@@ -280,15 +284,8 @@ func getCloudProviderDescription(allResources map[string]workloadinterface.IMeta
wl, err := cloudsupport.GetDescriptiveInfoFromCloudProvider(clusterName, provider, region, project)
if err != nil {
// Return error with useful info on how to configure credentials for getting cloud provider info
switch provider {
case "gke":
return provider, 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 provider, 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 provider, fmt.Errorf("could not get descriptive information about aks cluster: %s. %v", cluster, err.Error())
}
return provider, err
logger.L().Debug("failed to get descriptive information", helpers.Error(err))
return provider, fmt.Errorf("failed to get %s descriptive information. Read more: https://hub.armo.cloud/docs/kubescape-integration-with-cloud-providers", strings.ToUpper(provider))
}
allResources[wl.GetID()] = wl
(*armoResourceMap)[fmt.Sprintf("%s/%s", wl.GetApiVersion(), wl.GetKind())] = []string{wl.GetID()}

View File

@@ -23,7 +23,7 @@ func (jsonPrinter *JsonPrinter) SetWriter(outputFile string) {
}
func (jsonPrinter *JsonPrinter) Score(score float32) {
fmt.Fprintf(os.Stderr, "\nOverall risk-score (0- Excellent, 100- All failed): %d\n", int(score))
fmt.Fprintf(os.Stderr, "\nOverall risk-score (0- Excellent, 100- All failed): %d\n", cautils.Float32ToInt(score))
}
func (jsonPrinter *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {

View File

@@ -27,7 +27,7 @@ func (prometheusPrinter *PrometheusPrinter) SetWriter(outputFile string) {
}
func (prometheusPrinter *PrometheusPrinter) Score(score float32) {
fmt.Printf("\n# Overall risk-score (0- Excellent, 100- All failed)\nkubescape_score %d\n", int(score))
fmt.Printf("\n# Overall risk-score (0- Excellent, 100- All failed)\nkubescape_score %d\n", cautils.Float32ToInt(score))
}
func (printer *PrometheusPrinter) printResources(allResources map[string]workloadinterface.IMetadata, resourcesIDs *reporthandling.ResourcesIDs, frameworkName, controlName string) {

View File

@@ -4,39 +4,111 @@ import (
"fmt"
"sort"
"github.com/armosec/kubescape/core/cautils"
"github.com/armosec/opa-utils/reporthandling/apis"
"github.com/armosec/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/fatih/color"
"github.com/olekukonko/tablewriter"
)
func generateRow(controlSummary reportsummary.IControlSummary, infoToPrintInfoMap map[string]string) []string {
row := []string{controlSummary.GetName()}
row = append(row, fmt.Sprintf("%d", controlSummary.NumberOfResources().Failed()))
row = append(row, fmt.Sprintf("%d", controlSummary.NumberOfResources().Excluded()))
row = append(row, fmt.Sprintf("%d", controlSummary.NumberOfResources().All()))
const (
columnSeverity = iota
columnName = iota
columnCounterFailed = iota
columnCounterExclude = iota
columnCounterAll = iota
columnRiskScore = iota
_rowLen = iota
)
if !controlSummary.GetStatus().IsSkipped() {
row = append(row, fmt.Sprintf("%d", int(controlSummary.GetScore()))+"%")
row = append(row, "")
} else {
row = append(row, string(controlSummary.GetStatus().Status()))
if controlSummary.GetStatus().IsSkipped() {
row = append(row, infoToPrintInfoMap[controlSummary.GetStatus().Info()])
} else {
row = append(row, "")
}
func generateRow(controlSummary reportsummary.IControlSummary, infoToPrintInfo []infoStars, verbose bool) []string {
row := make([]string, _rowLen)
// ignore passed results
if !verbose && (controlSummary.GetStatus().IsPassed()) {
return []string{}
}
// ignore irelevant results
if !verbose && (controlSummary.GetStatus().IsSkipped() && controlSummary.GetStatus().Status() == apis.StatusIrrelevant) {
return []string{}
}
row[columnSeverity] = getSeverityColumn(controlSummary)
row[columnName] = controlSummary.GetName()
row[columnCounterFailed] = fmt.Sprintf("%d", controlSummary.NumberOfResources().Failed())
row[columnCounterExclude] = fmt.Sprintf("%d", controlSummary.NumberOfResources().Excluded())
row[columnCounterAll] = fmt.Sprintf("%d", controlSummary.NumberOfResources().All())
row[columnRiskScore] = getRiskScoreColumn(controlSummary, infoToPrintInfo)
return row
}
func getSortedControlsNames(controls reportsummary.ControlSummaries) []string {
controlNames := make([]string, 0, len(controls))
func getInfoColumn(controlSummary reportsummary.IControlSummary, infoToPrintInfo []infoStars) string {
for i := range infoToPrintInfo {
if infoToPrintInfo[i].info == controlSummary.GetStatus().Info() {
return infoToPrintInfo[i].stars
}
}
return ""
}
func getRiskScoreColumn(controlSummary reportsummary.IControlSummary, infoToPrintInfo []infoStars) string {
if controlSummary.GetStatus().IsSkipped() {
return fmt.Sprintf("%s%s", controlSummary.GetStatus().Status(), getInfoColumn(controlSummary, infoToPrintInfo))
}
return fmt.Sprintf("%d", cautils.Float32ToInt(controlSummary.GetScore())) + "%"
}
func getSeverityColumn(controlSummary reportsummary.IControlSummary) string {
return color.New(getColor(apis.ControlSeverityToInt(controlSummary.GetScoreFactor())), color.Bold).SprintFunc()(apis.ControlSeverityToString(controlSummary.GetScoreFactor()))
}
func getColor(controlSeverity int) color.Attribute {
switch controlSeverity {
case apis.SeverityCritical:
return color.FgRed
case apis.SeverityHigh:
return color.FgYellow
case apis.SeverityMedium:
return color.FgCyan
case apis.SeverityLow:
return color.FgWhite
default:
return color.FgWhite
}
}
func getSortedControlsNames(controls reportsummary.ControlSummaries) [][]string {
controlNames := make([][]string, 5)
for k := range controls {
c := controls[k]
controlNames = append(controlNames, c.GetName())
i := apis.ControlSeverityToInt(c.GetScoreFactor())
controlNames[i] = append(controlNames[i], c.GetName())
}
for i := range controlNames {
sort.Strings(controlNames[i])
}
sort.Strings(controlNames)
return controlNames
}
func getControlTableHeaders() []string {
return []string{"CONTROL NAME", "FAILED RESOURCES", "EXCLUDED RESOURCES", "ALL RESOURCES", "% RISK-SCORE", "INFO"}
headers := make([]string, _rowLen)
headers[columnName] = "CONTROL NAME"
headers[columnCounterFailed] = "FAILED RESOURCES"
headers[columnCounterExclude] = "EXCLUDED RESOURCES"
headers[columnCounterAll] = "ALL RESOURCES"
headers[columnSeverity] = "SEVERITY"
headers[columnRiskScore] = "% RISK-SCORE"
return headers
}
func getColumnsAlignments() []int {
alignments := make([]int, _rowLen)
alignments[columnName] = tablewriter.ALIGN_LEFT
alignments[columnCounterFailed] = tablewriter.ALIGN_CENTER
alignments[columnCounterExclude] = tablewriter.ALIGN_CENTER
alignments[columnCounterAll] = tablewriter.ALIGN_CENTER
alignments[columnSeverity] = tablewriter.ALIGN_LEFT
alignments[columnRiskScore] = tablewriter.ALIGN_CENTER
return alignments
}

View File

@@ -24,7 +24,7 @@ func (jsonPrinter *JsonPrinter) SetWriter(outputFile string) {
}
func (jsonPrinter *JsonPrinter) Score(score float32) {
fmt.Fprintf(os.Stderr, "\nOverall risk-score (0- Excellent, 100- All failed): %d\n", int(score))
fmt.Fprintf(os.Stderr, "\nOverall risk-score (0- Excellent, 100- All failed): %d\n", cautils.Float32ToInt(score))
}
func (jsonPrinter *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {

View File

@@ -100,7 +100,7 @@ func (junitPrinter *JunitPrinter) SetWriter(outputFile string) {
}
func (junitPrinter *JunitPrinter) Score(score float32) {
fmt.Fprintf(os.Stderr, "\nOverall risk-score (0- Excellent, 100- All failed): %d\n", int(score))
fmt.Fprintf(os.Stderr, "\nOverall risk-score (0- Excellent, 100- All failed): %d\n", cautils.Float32ToInt(score))
}
func (junitPrinter *JunitPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {

View File

@@ -31,8 +31,7 @@ var (
)
type PdfPrinter struct {
writer *os.File
sortedControlNames []string
writer *os.File
}
func NewPdfPrinter() *PdfPrinter {
@@ -52,18 +51,18 @@ func (pdfPrinter *PdfPrinter) SetWriter(outputFile string) {
}
func (pdfPrinter *PdfPrinter) Score(score float32) {
fmt.Fprintf(os.Stderr, "\nOverall risk-score (0- Excellent, 100- All failed): %d\n", int(score))
fmt.Fprintf(os.Stderr, "\nOverall risk-score (0- Excellent, 100- All failed): %d\n", cautils.Float32ToInt(score))
}
func (pdfPrinter *PdfPrinter) printInfo(m pdf.Maroto, summaryDetails *reportsummary.SummaryDetails, infoMap map[string]string) {
func (pdfPrinter *PdfPrinter) printInfo(m pdf.Maroto, summaryDetails *reportsummary.SummaryDetails, infoMap []infoStars) {
emptyRowCounter := 1
for key, val := range infoMap {
if val != "" {
for i := range infoMap {
if infoMap[i].info != "" {
m.Row(5, func() {
m.Col(1, func() {
m.Text(fmt.Sprintf("%v", val))
m.Text(fmt.Sprintf("%v", infoMap[i].info))
})
m.Col(12, func() {
m.Text(fmt.Sprintf("%v", key))
m.Text(fmt.Sprintf("%v", infoMap[i].stars))
})
})
if emptyRowCounter < len(infoMap) {
@@ -76,15 +75,15 @@ func (pdfPrinter *PdfPrinter) printInfo(m pdf.Maroto, summaryDetails *reportsumm
}
func (pdfPrinter *PdfPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
pdfPrinter.sortedControlNames = getSortedControlsNames(opaSessionObj.Report.SummaryDetails.Controls)
sortedControlNames := getSortedControlsNames(opaSessionObj.Report.SummaryDetails.Controls)
infoToPrintInfoMap := mapInfoToPrintInfo(opaSessionObj.Report.SummaryDetails.Controls)
infoToPrintInfo := mapInfoToPrintInfo(opaSessionObj.Report.SummaryDetails.Controls)
m := pdf.NewMaroto(consts.Portrait, consts.A4)
pdfPrinter.printHeader(m)
pdfPrinter.printFramework(m, opaSessionObj.Report.SummaryDetails.ListFrameworks())
pdfPrinter.printTable(m, &opaSessionObj.Report.SummaryDetails)
pdfPrinter.printTable(m, &opaSessionObj.Report.SummaryDetails, sortedControlNames)
pdfPrinter.printFinalResult(m, &opaSessionObj.Report.SummaryDetails)
pdfPrinter.printInfo(m, &opaSessionObj.Report.SummaryDetails, infoToPrintInfoMap)
pdfPrinter.printInfo(m, &opaSessionObj.Report.SummaryDetails, infoToPrintInfo)
// Extrat output buffer.
outBuff, err := m.Output()
@@ -149,15 +148,17 @@ func (pdfPrinter *PdfPrinter) printFramework(m pdf.Maroto, frameworks []reportsu
}
// Create pdf table
func (pdfPrinter *PdfPrinter) printTable(m pdf.Maroto, summaryDetails *reportsummary.SummaryDetails) {
func (pdfPrinter *PdfPrinter) printTable(m pdf.Maroto, summaryDetails *reportsummary.SummaryDetails, sortedControlNames [][]string) {
headers := getControlTableHeaders()
infoToPrintInfoMap := mapInfoToPrintInfo(summaryDetails.Controls)
controls := make([][]string, len(pdfPrinter.sortedControlNames))
controls := make([][]string, len(sortedControlNames))
for i := range controls {
controls[i] = make([]string, len(headers))
}
for i := 0; i < len(pdfPrinter.sortedControlNames); i++ {
controls[i] = generateRow(summaryDetails.Controls.GetControl(reportsummary.EControlCriteriaName, pdfPrinter.sortedControlNames[i]), infoToPrintInfoMap)
for i := len(sortedControlNames) - 1; i >= 0; i-- {
for _, c := range sortedControlNames[i] {
controls[i] = generateRow(summaryDetails.Controls.GetControl(reportsummary.EControlCriteriaName, c), infoToPrintInfoMap, true)
}
}
m.TableList(headers, controls, props.TableList{
@@ -186,7 +187,7 @@ func (pdfPrinter *PdfPrinter) printTable(m pdf.Maroto, summaryDetails *reportsum
// Add final results.
func (pdfPrinter *PdfPrinter) printFinalResult(m pdf.Maroto, summaryDetails *reportsummary.SummaryDetails) {
m.Row(5, func() {
m.Row(_rowLen, func() {
m.Col(3, func() {
m.Text("Resource summary", props.Text{
Align: consts.Left,

View File

@@ -11,16 +11,16 @@ import (
"github.com/armosec/kubescape/core/pkg/resultshandling/printer"
"github.com/armosec/opa-utils/objectsenvelopes"
"github.com/armosec/opa-utils/reporthandling/apis"
helpersv1 "github.com/armosec/opa-utils/reporthandling/helpers/v1"
"github.com/armosec/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/enescakir/emoji"
"github.com/olekukonko/tablewriter"
)
type PrettyPrinter struct {
formatVersion string
writer *os.File
verboseMode bool
sortedControlNames []string
formatVersion string
writer *os.File
verboseMode bool
}
func NewPrettyPrinter(verboseMode bool, formatVersion string) *PrettyPrinter {
@@ -31,14 +31,14 @@ func NewPrettyPrinter(verboseMode bool, formatVersion string) *PrettyPrinter {
}
func (prettyPrinter *PrettyPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
prettyPrinter.sortedControlNames = getSortedControlsNames(opaSessionObj.Report.SummaryDetails.Controls) // ListControls().All())
fmt.Fprintf(prettyPrinter.writer, "\n"+getSperator("^")+"\n")
if prettyPrinter.formatVersion == "v1" {
prettyPrinter.printResults(&opaSessionObj.Report.SummaryDetails.Controls, opaSessionObj.AllResources)
} else if prettyPrinter.formatVersion == "v2" {
prettyPrinter.resourceTable(opaSessionObj.ResourcesResult, opaSessionObj.AllResources)
sortedControlNames := getSortedControlsNames(opaSessionObj.Report.SummaryDetails.Controls) // ListControls().All())
if prettyPrinter.verboseMode {
prettyPrinter.resourceTable(opaSessionObj)
}
prettyPrinter.printSummaryTable(&opaSessionObj.Report.SummaryDetails)
prettyPrinter.printSummaryTable(&opaSessionObj.Report.SummaryDetails, sortedControlNames)
}
@@ -49,13 +49,14 @@ func (prettyPrinter *PrettyPrinter) SetWriter(outputFile string) {
func (prettyPrinter *PrettyPrinter) Score(score float32) {
}
func (prettyPrinter *PrettyPrinter) printResults(controls *reportsummary.ControlSummaries, allResources map[string]workloadinterface.IMetadata) {
for i := 0; i < len(prettyPrinter.sortedControlNames); i++ {
controlSummary := controls.GetControl(reportsummary.EControlCriteriaName, prettyPrinter.sortedControlNames[i]) // summaryDetails.Controls ListControls().All() Controls.GetControl(ca)
prettyPrinter.printTitle(controlSummary)
prettyPrinter.printResources(controlSummary, allResources)
prettyPrinter.printSummary(prettyPrinter.sortedControlNames[i], controlSummary)
func (prettyPrinter *PrettyPrinter) printResults(controls *reportsummary.ControlSummaries, allResources map[string]workloadinterface.IMetadata, sortedControlNames [][]string) {
for i := len(sortedControlNames) - 1; i >= 0; i-- {
for _, c := range sortedControlNames[i] {
controlSummary := controls.GetControl(reportsummary.EControlCriteriaName, c) // summaryDetails.Controls ListControls().All() Controls.GetControl(ca)
prettyPrinter.printTitle(controlSummary)
prettyPrinter.printResources(controlSummary, allResources)
prettyPrinter.printSummary(c, controlSummary)
}
}
}
@@ -167,53 +168,62 @@ func generateRelatedObjectsStr(workload WorkloadSummary) string {
}
func generateFooter(summaryDetails *reportsummary.SummaryDetails) []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", summaryDetails.NumberOfResources().Failed()))
row = append(row, fmt.Sprintf("%d", summaryDetails.NumberOfResources().Excluded()))
row = append(row, fmt.Sprintf("%d", summaryDetails.NumberOfResources().All()))
row = append(row, fmt.Sprintf("%.2f%s", summaryDetails.Score, "%"))
row = append(row, " ")
row := make([]string, _rowLen)
row[columnName] = "Resource Summary"
row[columnCounterFailed] = fmt.Sprintf("%d", summaryDetails.NumberOfResources().Failed())
row[columnCounterExclude] = fmt.Sprintf("%d", summaryDetails.NumberOfResources().Excluded())
row[columnCounterAll] = fmt.Sprintf("%d", summaryDetails.NumberOfResources().All())
row[columnSeverity] = " "
row[columnRiskScore] = fmt.Sprintf("%.2f%s", summaryDetails.Score, "%")
return row
}
func (prettyPrinter *PrettyPrinter) printSummaryTable(summaryDetails *reportsummary.SummaryDetails) {
func (prettyPrinter *PrettyPrinter) printSummaryTable(summaryDetails *reportsummary.SummaryDetails, sortedControlNames [][]string) {
cautils.InfoTextDisplay(prettyPrinter.writer, "\n"+controlCountersForSummary(summaryDetails.NumberOfControls())+"\n\n")
summaryTable := tablewriter.NewWriter(prettyPrinter.writer)
summaryTable.SetAutoWrapText(false)
summaryTable.SetHeader(getControlTableHeaders())
summaryTable.SetHeaderLine(true)
alignments := []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER}
summaryTable.SetColumnAlignment(alignments)
infoToPrintInfoMap := mapInfoToPrintInfo(summaryDetails.Controls)
for i := 0; i < len(prettyPrinter.sortedControlNames); i++ {
summaryTable.Append(generateRow(summaryDetails.Controls.GetControl(reportsummary.EControlCriteriaName, prettyPrinter.sortedControlNames[i]), infoToPrintInfoMap))
summaryTable.SetColumnAlignment(getColumnsAlignments())
infoToPrintInfo := mapInfoToPrintInfo(summaryDetails.Controls)
for i := len(sortedControlNames) - 1; i >= 0; i-- {
for _, c := range sortedControlNames[i] {
row := generateRow(summaryDetails.Controls.GetControl(reportsummary.EControlCriteriaName, c), infoToPrintInfo, prettyPrinter.verboseMode)
if len(row) > 0 {
summaryTable.Append(row)
}
}
}
summaryTable.SetFooter(generateFooter(summaryDetails))
// summaryTable.SetFooter(generateFooter())
cautils.InfoTextDisplay(prettyPrinter.writer, frameworksScoresToString(summaryDetails.ListFrameworks()))
summaryTable.Render()
prettyPrinter.printInfo(infoToPrintInfoMap)
// For control scan framework will be nil
// When scanning controls the framework list will be empty
cautils.InfoTextDisplay(prettyPrinter.writer, frameworksScoresToString(summaryDetails.ListFrameworks()))
prettyPrinter.printInfo(infoToPrintInfo)
}
func (prettyPrinter *PrettyPrinter) printInfo(infoToPrintInfoMap map[string]string) {
for info, stars := range infoToPrintInfoMap {
cautils.WarningDisplay(prettyPrinter.writer, fmt.Sprintf("%s - %s\n", stars, info))
func (prettyPrinter *PrettyPrinter) printInfo(infoToPrintInfo []infoStars) {
fmt.Println()
for i := range infoToPrintInfo {
cautils.InfoDisplay(prettyPrinter.writer, fmt.Sprintf("%s %s\n", infoToPrintInfo[i].stars, infoToPrintInfo[i].info))
}
}
func frameworksScoresToString(frameworks []reportsummary.IFrameworkSummary) string {
if len(frameworks) == 1 {
if frameworks[0].GetName() != "" {
return fmt.Sprintf("\nFRAMEWORK %s\n", frameworks[0].GetName())
return fmt.Sprintf("FRAMEWORK %s\n", frameworks[0].GetName())
// cautils.InfoTextDisplay(prettyPrinter.writer, ))
}
} else if len(frameworks) > 1 {
p := "\nFRAMEWORKS: "
p := "FRAMEWORKS: "
i := 0
for ; i < len(frameworks)-1; i++ {
p += fmt.Sprintf("%s (risk: %.2f), ", frameworks[i].GetName(), frameworks[i].GetScore())
@@ -224,14 +234,21 @@ func frameworksScoresToString(frameworks []reportsummary.IFrameworkSummary) stri
return ""
}
// func getSortedControlsNames(controls []reportsummary.IPolicies) []string {
// controlNames := make([]string, 0, len(controls))
// for k := range controls {
// controlNames = append(controlNames, controls[k].Get())
// }
// sort.Strings(controlNames)
// return controlNames
// }
func getControlLink(controlID string) string {
return fmt.Sprintf("https://hub.armo.cloud/docs/%s", strings.ToLower(controlID))
}
func controlCountersForSummary(counters reportsummary.ICounters) string {
return fmt.Sprintf("Controls: %d (Failed: %d, Excluded: %d, Skipped: %d)", counters.All(), counters.Failed(), counters.Excluded(), counters.Skipped())
}
func controlCountersForResource(l *helpersv1.AllLists) string {
return fmt.Sprintf("Controls: %d (Failed: %d, Excluded: %d)", len(l.All()), len(l.Failed()), len(l.Excluded()))
}
func getSperator(sep string) string {
s := ""
for i := 0; i < 80; i++ {
s += sep
}
return s
}

View File

@@ -29,7 +29,7 @@ func (prometheusPrinter *PrometheusPrinter) SetWriter(outputFile string) {
}
func (prometheusPrinter *PrometheusPrinter) Score(score float32) {
fmt.Printf("\n# Overall risk-score (0- Excellent, 100- All failed)\nkubescape_score %d\n", int(score))
fmt.Printf("\n# Overall risk-score (0- Excellent, 100- All failed)\nkubescape_score %d\n", cautils.Float32ToInt(score))
}
func (printer *PrometheusPrinter) generatePrometheusFormat(
@@ -39,7 +39,7 @@ func (printer *PrometheusPrinter) generatePrometheusFormat(
m := &Metrics{}
m.setRiskScores(summaryDetails)
m.setResourcesCounters(resources, results)
// m.setResourcesCounters(resources, results)
return m
}

View File

@@ -2,8 +2,10 @@ package v2
import (
"fmt"
"strings"
"github.com/armosec/k8s-interface/workloadinterface"
"github.com/armosec/kubescape/core/cautils"
"github.com/armosec/opa-utils/reporthandling/apis"
"github.com/armosec/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/armosec/opa-utils/reporthandling/results/v1/resourcesresults"
@@ -12,88 +14,183 @@ import (
type metricsName string
const (
metricsFrameworkScore metricsName = "kubescape_risk_score_framework"
metricsControlScore metricsName = "kubescape_risk_score_control"
metricsScore metricsName = "kubescape_risk_score"
metricsresourceFailed metricsName = "kubescape_resource_controls_number_of_failed"
metricsresourcePassed metricsName = "kubescape_resource_controls_number_of_passed"
metricsresourceExcluded metricsName = "kubescape_resource_controls_number_of_exclude"
ksMetrics metricsName = "kubescape"
metricsCluster metricsName = "cluster"
metricsScore metricsName = "riskScore"
metricsCount metricsName = "count"
metricsFailed metricsName = "failed"
metricsExcluded metricsName = "exclude"
metricsPassed metricsName = "passed"
metricsControl metricsName = "control"
metricsControls metricsName = "controls"
metricsResource metricsName = "resource"
metricsResources metricsName = "resources"
metricsFramework metricsName = "framework"
)
func (mrs *mRiskScore) string() string {
r := fmt.Sprintf("resourcesCountFailed=\"%d\"", mrs.resourcesCountFailed) + ","
r += fmt.Sprintf("resourcesCountExcluded=\"%d\"", mrs.resourcesCountExcluded) + ","
r += fmt.Sprintf("resourcesCountPassed=\"%d\"", mrs.resourcesCountPassed) + ","
r += fmt.Sprintf("controlsCountFailed=\"%d\"", mrs.controlsCountFailed) + ","
r += fmt.Sprintf("controlsCountExcluded=\"%d\"", mrs.controlsCountExcluded) + ","
r += fmt.Sprintf("controlsCountPassed=\"%d\"", mrs.controlsCountPassed) + ","
r += fmt.Sprintf("controlsCountSkipped=\"%d\"", mrs.controlsCountSkipped)
return r
// ============================================ CLUSTER ============================================================
func (mrs *mRiskScore) metrics() []string {
/*
##### Overall risk score
kubescape_cluster_riskScore{} <risk score>
###### Overall resources counters
kubescape_cluster_count_resources_failed{} <counter>
kubescape_cluster_count_resources_excluded{} <counter>
kubescape_cluster_count_resources_passed{} <counter>
###### Overall controls counters
kubescape_cluster_count_controls_failed{} <counter>
kubescape_cluster_count_controls_excluded{} <counter>
kubescape_cluster_count_controls_passed{} <counter>
*/
m := []string{}
// overall
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s", mrs.prefix(), metricsScore), mrs.labels(), mrs.riskScore))
// resources
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s_%s_%s", mrs.prefix(), metricsCount, metricsResources, metricsFailed), mrs.labels(), mrs.resourcesCountFailed))
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s_%s_%s", mrs.prefix(), metricsCount, metricsResources, metricsExcluded), mrs.labels(), mrs.resourcesCountExcluded))
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s_%s_%s", mrs.prefix(), metricsCount, metricsResources, metricsPassed), mrs.labels(), mrs.resourcesCountPassed))
// controls
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s_%s_%s", mrs.prefix(), metricsCount, metricsControl, metricsFailed), mrs.labels(), mrs.controlsCountFailed))
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s_%s_%s", mrs.prefix(), metricsCount, metricsControl, metricsExcluded), mrs.labels(), mrs.controlsCountExcluded))
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s_%s_%s", mrs.prefix(), metricsCount, metricsControl, metricsPassed), mrs.labels(), mrs.controlsCountPassed))
return m
}
func (mrs *mRiskScore) value() int {
return mrs.riskScore
func (mrs *mRiskScore) labels() string {
return ""
}
func (mcrs *mControlRiskScore) string() string {
r := fmt.Sprintf("controlName=\"%s\"", mcrs.controlName) + ","
r += fmt.Sprintf("controlID=\"%s\"", mcrs.controlID) + ","
func (mrs *mRiskScore) prefix() string {
return fmt.Sprintf("%s_%s", ksMetrics, metricsCluster)
}
// ============================================ CONTROL ============================================================
func (mcrs *mControlRiskScore) metrics() []string {
/*
# Risk score
kubescape_control_riskScore{name="<control name>",url="<docs url>",severity="<control severity>"} <risk score>
# Resources counters
kubescape_control_count_resources_failed{name="<control name>",url="<docs url>",severity="<control severity>"} <counter>
kubescape_control_count_resources_excluded{name="<control name>",url="<docs url>",severity="<control severity>"} <counter>
kubescape_control_count_resources_passed{name="<control name>",url="<docs url>",severity="<control severity>"} <counter>
*/
m := []string{}
// overall
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s", mcrs.prefix(), metricsScore), mcrs.labels(), mcrs.riskScore))
// resources
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s_%s_%s", mcrs.prefix(), metricsCount, metricsResources, metricsFailed), mcrs.labels(), mcrs.resourcesCountFailed))
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s_%s_%s", mcrs.prefix(), metricsCount, metricsResources, metricsExcluded), mcrs.labels(), mcrs.resourcesCountExcluded))
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s_%s_%s", mcrs.prefix(), metricsCount, metricsResources, metricsPassed), mcrs.labels(), mcrs.resourcesCountPassed))
return m
}
func (mcrs *mControlRiskScore) labels() string {
r := fmt.Sprintf("name=\"%s\"", mcrs.controlName) + ","
r += fmt.Sprintf("severity=\"%s\"", mcrs.severity) + ","
r += fmt.Sprintf("resourcesCountFailed=\"%d\"", mcrs.resourcesCountFailed) + ","
r += fmt.Sprintf("resourcesCountExcluded=\"%d\"", mcrs.resourcesCountExcluded) + ","
r += fmt.Sprintf("resourcesCountPassed=\"%d\"", mcrs.resourcesCountPassed) + ","
r += fmt.Sprintf("link=\"%s\"", mcrs.link) + ","
r += fmt.Sprintf("remediation=\"%s\"", mcrs.remediation)
r += fmt.Sprintf("link=\"%s\"", mcrs.link)
return r
}
func (mcrs *mControlRiskScore) value() int {
return mcrs.riskScore
func (mcrs *mControlRiskScore) prefix() string {
return fmt.Sprintf("%s_%s", ksMetrics, metricsControl)
}
func (mfrs *mFrameworkRiskScore) string() string {
r := fmt.Sprintf("frameworkName=\"%s\"", mfrs.frameworkName) + ","
r += fmt.Sprintf("resourcesCountFailed=\"%d\"", mfrs.resourcesCountFailed) + ","
r += fmt.Sprintf("resourcesCountExcluded=\"%d\"", mfrs.resourcesCountExcluded) + ","
r += fmt.Sprintf("resourcesCountPassed=\"%d\"", mfrs.resourcesCountPassed) + ","
r += fmt.Sprintf("controlsCountFailed=\"%d\"", mfrs.controlsCountFailed) + ","
r += fmt.Sprintf("controlsCountExcluded=\"%d\"", mfrs.controlsCountExcluded) + ","
r += fmt.Sprintf("controlsCountPassed=\"%d\"", mfrs.controlsCountPassed) + ","
r += fmt.Sprintf("controlsCountSkipped=\"%d\"", mfrs.controlsCountSkipped)
// ============================================ FRAMEWORK ============================================================
func (mfrs *mFrameworkRiskScore) metrics() []string {
/*
#### Frameworks metrics
kubescape_framework_riskScore{name="<framework name>"} <risk score>
###### Frameworks resources counters
kubescape_framework_count_resources_failed{} <counter>
kubescape_framework_count_resources_excluded{} <counter>
kubescape_framework_count_resources_passed{} <counter>
###### Frameworks controls counters
kubescape_framework_count_controls_failed{name="<framework name>"} <counter>
kubescape_framework_count_controls_excluded{name="<framework name>"} <counter>
kubescape_framework_count_controls_passed{name="<framework name>"} <counter>
*/
m := []string{}
// overall
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s", mfrs.prefix(), metricsScore), mfrs.labels(), mfrs.riskScore))
// resources
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s_%s_%s", mfrs.prefix(), metricsCount, metricsResources, metricsFailed), mfrs.labels(), mfrs.resourcesCountFailed))
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s_%s_%s", mfrs.prefix(), metricsCount, metricsResources, metricsExcluded), mfrs.labels(), mfrs.resourcesCountExcluded))
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s_%s_%s", mfrs.prefix(), metricsCount, metricsResources, metricsPassed), mfrs.labels(), mfrs.resourcesCountPassed))
// controls
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s_%s_%s", mfrs.prefix(), metricsCount, metricsControl, metricsFailed), mfrs.labels(), mfrs.controlsCountFailed))
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s_%s_%s", mfrs.prefix(), metricsCount, metricsControl, metricsExcluded), mfrs.labels(), mfrs.controlsCountExcluded))
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s_%s_%s", mfrs.prefix(), metricsCount, metricsControl, metricsPassed), mfrs.labels(), mfrs.controlsCountPassed))
return m
}
func (mfrs *mFrameworkRiskScore) labels() string {
r := fmt.Sprintf("name=\"%s\"", mfrs.frameworkName)
return r
}
func (mfrs *mFrameworkRiskScore) value() int {
return mfrs.riskScore
func (mfrs *mFrameworkRiskScore) prefix() string {
return fmt.Sprintf("%s_%s", ksMetrics, metricsFramework)
}
func (mrc *mResourceControls) string() string {
// ============================================ RESOURCES ============================================================
func (mrc *mResources) metrics() []string {
/*
#### Resources metrics
kubescape_resource_count_controls_failed{apiVersion="<>",kind="<>",namespace="<>",name="<>"} <counter>
kubescape_resource_count_controls_excluded{apiVersion="<>",kind="<>",namespace="<>",name="<>"} <counter>
*/
m := []string{}
// controls
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s_%s_%s", mrc.prefix(), metricsCount, metricsControls, metricsFailed), mrc.labels(), mrc.controlsCountFailed))
m = append(m, toRowInMetrics(fmt.Sprintf("%s_%s_%s_%s", mrc.prefix(), metricsCount, metricsControls, metricsExcluded), mrc.labels(), mrc.controlsCountExcluded))
return m
}
func (mrc *mResources) labels() string {
r := fmt.Sprintf("apiVersion=\"%s\"", mrc.apiVersion) + ","
r += fmt.Sprintf("kind=\"%s\"", mrc.kind) + ","
r += fmt.Sprintf("namespace=\"%s\"", mrc.namespace) + ","
r += fmt.Sprintf("name=\"%s\"", mrc.name)
return r
}
func (mrc *mResourceControls) value() int {
return mrc.controls
func (mrc *mResources) prefix() string {
return fmt.Sprintf("%s_%s", ksMetrics, metricsResource)
}
func toRowInMetrics(name metricsName, row string, value int) string {
return fmt.Sprintf("%s{%s} %d\n", name, row, value)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
func toRowInMetrics(name string, row string, value int) string {
return fmt.Sprintf("%s{%s} %d", name, row, value)
}
func (m *Metrics) String() string {
r := toRowInMetrics(metricsScore, m.rs.string(), m.rs.value())
r := strings.Join(m.rs.metrics(), "\n") + "\n"
for i := range m.listFrameworks {
r += toRowInMetrics(metricsFrameworkScore, m.listFrameworks[i].string(), m.listFrameworks[i].value())
r += strings.Join(m.listFrameworks[i].metrics(), "\n") + "\n"
}
for i := range m.listControls {
r += toRowInMetrics(metricsControlScore, m.listControls[i].string(), m.listControls[i].value())
r += strings.Join(m.listControls[i].metrics(), "\n") + "\n"
}
for i := range m.listResourcesControlsFiled {
r += toRowInMetrics(metricsresourceFailed, m.listResourcesControlsFiled[i].string(), m.listResourcesControlsFiled[i].value())
}
for i := range m.listResourcesControlsExcluded {
r += toRowInMetrics(metricsresourceExcluded, m.listResourcesControlsExcluded[i].string(), m.listResourcesControlsExcluded[i].value())
}
for i := range m.listResourcesControlsPassed {
r += toRowInMetrics(metricsresourcePassed, m.listResourcesControlsPassed[i].string(), m.listResourcesControlsPassed[i].value())
for i := range m.listResources {
r += strings.Join(m.listResources[i].metrics(), "\n") + "\n"
}
return r
}
@@ -106,7 +203,7 @@ type mRiskScore struct {
controlsCountFailed int
controlsCountExcluded int
controlsCountSkipped int
riskScore int // metric
riskScore int
}
type mControlRiskScore struct {
@@ -118,7 +215,7 @@ type mControlRiskScore struct {
resourcesCountPassed int
resourcesCountFailed int
resourcesCountExcluded int
riskScore int // metric
riskScore int
}
type mFrameworkRiskScore struct {
@@ -130,23 +227,23 @@ type mFrameworkRiskScore struct {
controlsCountFailed int
controlsCountExcluded int
controlsCountSkipped int
riskScore int // metric
riskScore int
}
type mResourceControls struct {
name string
namespace string
apiVersion string
kind string
controls int // metric
type mResources struct {
name string
namespace string
apiVersion string
kind string
controlsCountPassed int
controlsCountFailed int
controlsCountExcluded int
}
type Metrics struct {
rs mRiskScore
listFrameworks []mFrameworkRiskScore
listControls []mControlRiskScore
listResourcesControlsFiled []mResourceControls
listResourcesControlsPassed []mResourceControls
listResourcesControlsExcluded []mResourceControls
rs mRiskScore
listFrameworks []mFrameworkRiskScore
listControls []mControlRiskScore
listResources []mResources
}
func (mrs *mRiskScore) set(resources reportsummary.ICounters, controls reportsummary.ICounters) {
@@ -176,12 +273,12 @@ func (mcrs *mControlRiskScore) set(resources reportsummary.ICounters) {
}
func (m *Metrics) setRiskScores(summaryDetails *reportsummary.SummaryDetails) {
m.rs.set(summaryDetails.NumberOfResources(), summaryDetails.NumberOfControls())
m.rs.riskScore = int(summaryDetails.GetScore())
m.rs.riskScore = cautils.Float32ToInt(summaryDetails.GetScore())
for _, fw := range summaryDetails.ListFrameworks() {
mfrs := mFrameworkRiskScore{
frameworkName: fw.GetName(),
riskScore: int(fw.GetScore()),
riskScore: cautils.Float32ToInt(fw.GetScore()),
}
mfrs.set(fw.NumberOfResources(), fw.NumberOfControls())
m.listFrameworks = append(m.listFrameworks, mfrs)
@@ -191,7 +288,7 @@ func (m *Metrics) setRiskScores(summaryDetails *reportsummary.SummaryDetails) {
mcrs := mControlRiskScore{
controlName: control.GetName(),
controlID: control.GetID(),
riskScore: int(control.GetScore()),
riskScore: cautils.Float32ToInt(control.GetScore()),
link: getControlLink(control.GetID()),
severity: apis.ControlSeverityToString(control.GetScoreFactor()),
remediation: control.GetRemediation(),
@@ -229,24 +326,18 @@ func (m *Metrics) setResourcesCounters(
}
passed, excluded, failed := resourceControlStatusCounters(&result)
mrc := mResourceControls{}
mrc := mResources{}
mrc.apiVersion = r.GetApiVersion()
mrc.namespace = r.GetNamespace()
mrc.kind = r.GetKind()
mrc.name = r.GetName()
// append
if passed > 0 {
mrc.controls = passed
m.listResourcesControlsPassed = append(m.listResourcesControlsPassed, mrc)
}
if failed > 0 {
mrc.controls = failed
m.listResourcesControlsFiled = append(m.listResourcesControlsFiled, mrc)
}
if excluded > 0 {
mrc.controls = excluded
m.listResourcesControlsExcluded = append(m.listResourcesControlsExcluded, mrc)
}
mrc.controlsCountPassed = passed
mrc.controlsCountFailed = failed
mrc.controlsCountExcluded = excluded
m.listResources = append(m.listResources, mrc)
}
}

View File

@@ -5,58 +5,82 @@ import (
"sort"
"strings"
"github.com/armosec/k8s-interface/workloadinterface"
"github.com/armosec/kubescape/core/cautils"
"github.com/armosec/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/armosec/opa-utils/reporthandling/results/v1/resourcesresults"
"github.com/olekukonko/tablewriter"
)
func (prettyPrinter *PrettyPrinter) resourceTable(results map[string]resourcesresults.Result, allResources map[string]workloadinterface.IMetadata) {
const (
resourceColumnSeverity = iota
resourceColumnName = iota
resourceColumnURL = iota
resourceColumnPath = iota
_resourceRowLen = iota
)
summaryTable := tablewriter.NewWriter(prettyPrinter.writer)
summaryTable.SetAutoWrapText(true)
summaryTable.SetAutoMergeCells(true)
summaryTable.SetHeader(generateResourceHeader())
summaryTable.SetHeaderLine(true)
summaryTable.SetRowLine(true)
// summaryTable.SetFooter([]string{"", "", "Total", "", "$146.93"})
// For control scan framework will be nil
data := Matrix{}
for i := range results {
resource, ok := allResources[i]
func (prettyPrinter *PrettyPrinter) resourceTable(opaSessionObj *cautils.OPASessionObj) {
for resourceID, result := range opaSessionObj.ResourcesResult {
if !result.GetStatus(nil).IsFailed() {
continue
}
resource, ok := opaSessionObj.AllResources[resourceID]
if !ok {
continue
}
s := results[i]
if raw := generateResourceRows(resource, s.ListControls(), prettyPrinter.verboseMode); len(raw) > 0 {
fmt.Fprintf(prettyPrinter.writer, "\n"+getSperator("#")+"\n\n")
if source, ok := opaSessionObj.ResourceSource[resourceID]; ok {
fmt.Fprintf(prettyPrinter.writer, "Source: %s\n", source)
}
fmt.Fprintf(prettyPrinter.writer, "ApiVersion: %s\n", resource.GetApiVersion())
fmt.Fprintf(prettyPrinter.writer, "Kind: %s\n", resource.GetKind())
fmt.Fprintf(prettyPrinter.writer, "Name: %s\n", resource.GetName())
if resource.GetNamespace() != "" {
fmt.Fprintf(prettyPrinter.writer, "Namespace: %s\n", resource.GetNamespace())
}
fmt.Fprintf(prettyPrinter.writer, "\n"+controlCountersForResource(result.ListControlsIDs(nil))+"\n\n")
summaryTable := tablewriter.NewWriter(prettyPrinter.writer)
summaryTable.SetAutoWrapText(true)
summaryTable.SetAutoMergeCells(true)
summaryTable.SetHeader(generateResourceHeader())
summaryTable.SetHeaderLine(true)
summaryTable.SetRowLine(true)
data := Matrix{}
if raw := generateResourceRows(result.ListControls(), &opaSessionObj.Report.SummaryDetails); len(raw) > 0 {
data = append(data, raw...)
}
}
sort.Sort(data)
summaryTable.AppendBulk(data)
// For control scan framework will be nil
sort.Sort(data)
summaryTable.AppendBulk(data)
summaryTable.Render()
}
summaryTable.Render()
}
func generateResourceRows(resource workloadinterface.IMetadata, controls []resourcesresults.ResourceAssociatedControl, verboseMode bool) [][]string {
func generateResourceRows(controls []resourcesresults.ResourceAssociatedControl, summaryDetails *reportsummary.SummaryDetails) [][]string {
rows := [][]string{}
for i := range controls {
row := make([]string, _resourceRowLen)
if controls[i].GetName() == "" {
continue
}
row := []string{}
if !verboseMode && controls[i].GetStatus(nil).IsPassed() {
if !controls[i].GetStatus(nil).IsFailed() {
continue
}
row = append(row, fmt.Sprintf("%s\nhttps://hub.armo.cloud/docs/%s", controls[i].GetName(), strings.ToLower(controls[i].GetID())))
row = append(row, resource.GetNamespace())
paths := failedPathsToString(&controls[i])
row[resourceColumnURL] = fmt.Sprintf("https://hub.armo.cloud/docs/%s", strings.ToLower(controls[i].GetID()))
row[resourceColumnPath] = strings.Join(failedPathsToString(&controls[i]), "\n")
row[resourceColumnName] = controls[i].GetName()
if c := summaryDetails.Controls.GetControl(reportsummary.EControlCriteriaName, controls[i].GetName()); c != nil {
row[resourceColumnSeverity] = getSeverityColumn(c)
}
row = append(row, fmt.Sprintf("%s/%s\n%s", resource.GetKind(), resource.GetName(), strings.Join(paths, ";\n")))
row = append(row, string(controls[i].GetStatus(nil).Status()))
rows = append(rows, row)
}
@@ -64,7 +88,12 @@ func generateResourceRows(resource workloadinterface.IMetadata, controls []resou
}
func generateResourceHeader() []string {
return []string{"Control", "Namespace", "Kind/Name", "Statues"}
headers := make([]string, _resourceRowLen)
headers[resourceColumnSeverity] = "Severity"
headers[resourceColumnName] = "Control Name"
headers[resourceColumnURL] = "Docs"
headers[resourceColumnPath] = "Assistant Remediation"
return headers
}
type Matrix [][]string

View File

@@ -1 +0,0 @@
package v2

View File

@@ -26,7 +26,7 @@ func DataToJson(data *cautils.OPASessionObj) *reporthandlingv2.PostureReport {
report.Results = make([]resourcesresults.Result, len(data.ResourcesResult))
finalizeResults(report.Results, data.ResourcesResult)
report.Resources = finalizeResources(report.Results, data.AllResources)
report.Resources = finalizeResources(report.Results, data.AllResources, data.ResourceSource)
return &report
}
@@ -38,27 +38,39 @@ func finalizeResults(results []resourcesresults.Result, resourcesResult map[stri
}
}
func mapInfoToPrintInfo(controls reportsummary.ControlSummaries) map[string]string {
infoToPrintInfoMap := make(map[string]string)
type infoStars struct {
stars string
info string
}
func mapInfoToPrintInfo(controls reportsummary.ControlSummaries) []infoStars {
infoToPrintInfo := []infoStars{}
infoToPrintInfoMap := map[string]interface{}{}
starCount := "*"
for _, control := range controls {
if control.GetStatus().IsSkipped() && control.GetStatus().Info() != "" {
if _, ok := infoToPrintInfoMap[control.GetStatus().Info()]; !ok {
infoToPrintInfoMap[control.GetStatus().Info()] = starCount
infoToPrintInfo = append(infoToPrintInfo, infoStars{
info: control.GetStatus().Info(),
stars: starCount,
})
starCount += starCount
infoToPrintInfoMap[control.GetStatus().Info()] = nil
}
}
}
return infoToPrintInfoMap
return infoToPrintInfo
}
func finalizeResources(results []resourcesresults.Result, allResources map[string]workloadinterface.IMetadata) []reporthandling.Resource {
func finalizeResources(results []resourcesresults.Result, allResources map[string]workloadinterface.IMetadata, resourcesSource map[string]string) []reporthandling.Resource {
resources := make([]reporthandling.Resource, 0)
for i := range results {
if obj, ok := allResources[results[i].ResourceID]; ok {
r := *reporthandling.NewResource(obj.GetObject())
r.ResourceID = results[i].ResourceID
resources = append(resources, r)
resource := *reporthandling.NewResourceIMetadata(obj)
if r, ok := resourcesSource[results[i].ResourceID]; ok {
resource.SetSource(&reporthandling.Source{Path: r})
}
resources = append(resources, resource)
}
}
return resources

View File

@@ -48,8 +48,7 @@ func (report *ReportEventReceiver) Submit(opaSessionObj *cautils.OPASessionObj)
logger.L().Warning("failed to publish results. Reason: Unknown accout ID. Run kubescape with the '--account <account ID>' flag. Contact ARMO team for more details")
return nil
}
// if opaSessionObj.Metadata.ScanMetadata.ScanningTarget == reporthandlingv2.Cluster && report.clusterName == "" {
if report.clusterName == "" {
if opaSessionObj.Metadata.ScanMetadata.ScanningTarget == reporthandlingv2.Cluster && report.clusterName == "" {
logger.L().Warning("failed to publish results because the cluster name is Unknown. If you are scanning YAML files the results are not submitted to the Kubescape SaaS")
return nil
}
@@ -58,7 +57,7 @@ func (report *ReportEventReceiver) Submit(opaSessionObj *cautils.OPASessionObj)
if err == nil {
report.generateMessage()
} else {
logger.L().Debug(err.Error()) // print original error only in debug mode
err = fmt.Errorf("failed to submit scan results. url: '%s'", report.GetURL())
}
@@ -93,6 +92,8 @@ func (report *ReportEventReceiver) GetURL() string {
u.Scheme = "https"
u.Host = getter.GetArmoAPIConnector().GetFrontendURL()
q := u.Query()
if report.customerAdminEMail != "" || report.token == "" { // data has been submitted
u.Path = fmt.Sprintf("configuration-scanning/%s", report.clusterName)
} else {
@@ -100,9 +101,14 @@ func (report *ReportEventReceiver) GetURL() string {
q := u.Query()
q.Add("invitationToken", report.token)
q.Add("customerGUID", report.customerGUID)
u.RawQuery = q.Encode()
}
q.Add("utm_source", "GitHub")
q.Add("utm_medium", "CLI")
q.Add("utm_campaign", "Submit")
u.RawQuery = q.Encode()
return u.String()
}
@@ -110,7 +116,7 @@ func (report *ReportEventReceiver) sendResources(host string, opaSessionObj *cau
splittedPostureReport := report.setSubReport(opaSessionObj)
counter := 0
reportCounter := 0
if err := report.setResources(splittedPostureReport, opaSessionObj.AllResources, &counter, &reportCounter, host); err != nil {
if err := report.setResources(splittedPostureReport, opaSessionObj.AllResources, opaSessionObj.ResourceSource, &counter, &reportCounter, host); err != nil {
return err
}
if err := report.setResults(splittedPostureReport, opaSessionObj.ResourcesResult, &counter, &reportCounter, host); err != nil {
@@ -148,9 +154,12 @@ func (report *ReportEventReceiver) setResults(reportObj *reporthandlingv2.Postur
return nil
}
func (report *ReportEventReceiver) setResources(reportObj *reporthandlingv2.PostureReport, allResources map[string]workloadinterface.IMetadata, counter, reportCounter *int, host string) error {
func (report *ReportEventReceiver) setResources(reportObj *reporthandlingv2.PostureReport, allResources map[string]workloadinterface.IMetadata, resourcesSource map[string]string, counter, reportCounter *int, host string) error {
for resourceID, v := range allResources {
resource := reporthandling.NewResourceIMetadata(v)
if r, ok := resourcesSource[resourceID]; ok {
resource.SetSource(&reporthandling.Source{Path: r})
}
r, err := json.Marshal(resource)
if err != nil {
return fmt.Errorf("failed to unmarshal resource '%s', reason: %v", resourceID, err)

View File

@@ -43,8 +43,11 @@ func (report *ReportEventReceiver) setSubReport(opaSessionObj *cautils.OPASessio
ClusterAPIServerInfo: opaSessionObj.Report.ClusterAPIServerInfo,
}
if opaSessionObj.Metadata != nil {
reportObj.ClusterCloudProvider = opaSessionObj.Metadata.ClusterMetadata.CloudProvider
reportObj.Metadata = *opaSessionObj.Metadata
if opaSessionObj.Metadata.ContextMetadata.ClusterContextMetadata != nil {
reportObj.ClusterCloudProvider = opaSessionObj.Metadata.ContextMetadata.ClusterContextMetadata.CloudProvider // DEPRECATED
reportObj.Metadata.ClusterMetadata = *opaSessionObj.Metadata.ContextMetadata.ClusterContextMetadata
}
}
return reportObj
}

View File

@@ -56,17 +56,19 @@ func (resultsHandler *ResultsHandler) ToJson() ([]byte, error) {
}
// HandleResults handle the scan results according to the pre defind interfaces
func (resultsHandler *ResultsHandler) HandleResults() {
func (resultsHandler *ResultsHandler) HandleResults() error {
resultsHandler.printerObj.ActionPrint(resultsHandler.scanData)
if err := resultsHandler.reporterObj.Submit(resultsHandler.scanData); err != nil {
logger.L().Error(err.Error())
return err
}
resultsHandler.printerObj.Score(resultsHandler.GetRiskScore())
resultsHandler.reporterObj.DisplayReportURL()
return nil
}
// NewPrinter defind output format

View File

@@ -5,33 +5,33 @@
Kubescape roadmap items are labeled based on where the feature is used and by their maturity.
The features serve different stages of the workflow of the users:
* **Development phase** (writing Kubernetes manifests) - example: VS Code extension is used while editing YAMLs
* **Development phase** (writing Kubernetes manifests) - example: The VS Code extension is used while editing YAMLs.
* **CI phase** (integrating manifests to GIT repo) - example: GitHub action validating HELM charts on PRs
* **CD phase** (deploying applications in Kubernetes) - example: running cluster scan after a new deployment
* **Monitoring phase** (scanning application in Kubernetes) - example: Prometheus scraping the cluster security risk
* **CD phase** (deploying applications in Kubernetes) - example: running a cluster scan after a new deployment
* **Monitoring phase** (scanning application in Kubernetes) - example: Prometheus scraping the cluster security risk
The items in Kubescape roadmap are split to 3 major groups based on the feature planning maturity:
The items in the Kubescape roadmap are split into 3 major groups based on the feature planning maturity:
* [Planning](#planning) - we have tickets open for these issues with more or less clear vision of design
* [Backlog](#backlog) - feature which were discussed at a high level but are not ready for development
* [Wishlist](#wishlist) - features we are dreaming of 😀 and want to push them gradually forward
* [Planning](#planning) - we have tickets open for these issues with a more or less clear vision of design.
* [Backlog](#backlog) - features that were discussed at a high level but are not ready for development
* [Wishlist](#wishlist) - features we are dreaming of in 😀 and want to push them gradually forward
## Planning 👷
* ##### Integration with image registries
We want to expand Kubescape to integrate with differnet image registries and read image vulnerability information from there. This will allow Kubescape to give contextual security information about vulnerabilities [Container registry integration](/docs/proposals/container-image-vulnerability-adaptor.md)
We want to expand Kubescape to integrate with different image registries and read image vulnerability information from there. This will allow Kubescape to give contextual security information about vulnerabilities. Container registry integration
* ##### Kubescape as a microservice
Create a REST API for Kubescape so it can run constantly in a cluster and other components like Prometheus can scrape results
Create a REST API for Kubescape so it can constantly run in a cluster, and other components like Prometheus can scrape results.
* ##### Kubescape CLI control over cluster operations
Add functionality to Kubescape CLI to trigger operations in Kubescape cluster components (example: trigger images scans and etc.)
Add functionality to Kubescape CLI to trigger operations in Kubescape cluster components (example: trigger image scans, etc.)
* ##### Produce md/HTML reports
Create scan reports for different output formats
Create scan reports for different output formats.
* ##### Git integration for pull requests
Create insightful GitHub actions for Kubescape
## Backlog 📅
* ##### JSON path for HELM charts
Today Kubescape can point to issues in the Kubernetes object, we want to develop this feature so Kubescape will be able to point to the misconfigured source file (HELM)
Today, Kubescape can point to issues in the Kubernetes object. We want to develop this feature so Kubescape will be able to point to the misconfigured source file (HELM).
* ##### Create Kubescape HELM plugin
* ##### Kubescape based admission controller
Implement admission controller API for Kubescape microservice to enable users to use Kubescape rules as policies

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 163 KiB

View File

@@ -12,7 +12,7 @@ Running `kubescape` will start up a webserver on port `8080` which will serve th
* DELETE `/v1/results` - Delete kubescape scan results from storage. ~If empty will delete latest results~ (not supported)
* * query `id=<string>`: Delete ID of specific results
* * query `all`: Delete all cached results
* GET/POST `/metrics` - will trigger cluster scan. will respond with prometheus metrics once they have been scanned. This will respond 503 if the scan failed.
* GET/POST `/v1/metrics` - will trigger cluster scan. will respond with prometheus metrics once they have been scanned. This will respond 503 if the scan failed.
* `/livez` - will respond 200 is server is alive
* `/readyz` - will respond 200 if server can receive requests

View File

@@ -17,4 +17,106 @@
```bash
kubectl apply -f podmonitor.yaml
```
## Metrics
All kubescape related metrics begin with `kubescape`
> `riskScore` is the output of an algorithm calculating the risk of the vulinrability. `0` indicates there is no risk and `100` indicates highest risk.
#### Cluster scope metrics
##### Overall risk score
```
# Overall riskScore of the scan
kubescape_cluster_riskScore{} <risk score>
```
###### Overall resources counters
```
# Number of resources that failed
kubescape_cluster_count_resources_failed{} <counter>
# Number of resources that where excluded
kubescape_cluster_count_resources_excluded{} <counter>
# Number of resources that passed
kubescape_cluster_count_resources_passed{} <counter>
```
###### Overall controls counters
```
# Number of controls that failed
kubescape_cluster_count_controls_failed{} <counter>
# Number of controls that where excluded
kubescape_cluster_count_controls_excluded{} <counter>
# Number of controls that passed
kubescape_cluster_count_controls_passed{} <counter>
```
#### Frameworks metrics
##### Frameworks risk score
```
kubescape_framework_riskScore{name="<framework name>"} <risk score>
```
###### Frameworks resources counters
```
# Number of resources that failed
kubescape_framework_count_resources_failed{} <counter>
# Number of resources that where excluded
kubescape_framework_count_resources_excluded{} <counter>
# Number of resources that passed
kubescape_framework_count_resources_passed{} <counter>
```
###### Frameworks controls counters
```
# Number of controls that failed
kubescape_framework_count_controls_failed{name="<framework name>"} <counter>
# Number of controls that where excluded
kubescape_framework_count_controls_excluded{name="<framework name>"} <counter>
# Number of controls that passed
kubescape_framework_count_controls_passed{name="<framework name>"} <counter>
```
#### Controls metrics
##### Controls risk score
```
kubescape_control_riskScore{name="<control name>",url="<docs url>",severity="<control severity>"} <risk score>
```
###### Controls resources counters
```
# Number of resources that failed
kubescape_control_count_resources_failed{name="<control name>",url="<docs url>",severity="<control severity>"} <counter>
# Number of resources that where excluded
kubescape_control_count_resources_excluded{name="<control name>",url="<docs url>",severity="<control severity>"} <counter>
# Number of resources that passed
kubescape_control_count_resources_passed{name="<control name>",url="<docs url>",severity="<control severity>"} <counter>
```
#### Resources metrics
The resources metrics give you the ability to prioritize fixing the resources by the number of controls that failed
```
# Number of controls that failed for this particular resource
kubescape_resource_count_controls_failed{apiVersion="<>",kind="<>",namespace="<>",name="<>"} <counter>
# Number of controls that where excluded for this particular resource
kubescape_resource_count_controls_excluded{apiVersion="<>",kind="<>",namespace="<>",name="<>"} <counter>
```

View File

@@ -89,7 +89,8 @@ spec:
port: 8080
initialDelaySeconds: 3
periodSeconds: 3
image: quay.io/armosec/kubescape:prometheus.v2
image: quay.io/armosec/kubescape:latest
imagePullPolicy: Always
env:
- name: KS_DEFAULT_CONFIGMAP_NAMESPACE
valueFrom:

View File

@@ -11,6 +11,6 @@ spec:
app: kubescape
podMetricsEndpoints:
- port: http
# path: v1
path: /v1/metrics
interval: 120s
scrapeTimeout: 100s

View File

@@ -6,7 +6,7 @@ replace github.com/armosec/kubescape/core => ../core
require (
github.com/armosec/kubescape/core v0.0.0-00010101000000-000000000000
github.com/armosec/opa-utils v0.0.127
github.com/armosec/opa-utils v0.0.130
github.com/armosec/utils-go v0.0.3
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0

View File

@@ -109,8 +109,8 @@ github.com/armosec/k8s-interface v0.0.66/go.mod h1:vwprS8qn/iowd5yf0JHpqDsLA5I8W
github.com/armosec/k8s-interface v0.0.68 h1:6CtSakISiI47YHkxh+Va9FzZQIBkWa6g9sbiNxq1Zkk=
github.com/armosec/k8s-interface v0.0.68/go.mod h1:PeWn41C2uenZi+xfZdyFF/zG5wXACA00htQyknDUWDE=
github.com/armosec/opa-utils v0.0.64/go.mod h1:6tQP8UDq2EvEfSqh8vrUdr/9QVSCG4sJfju1SXQOn4c=
github.com/armosec/opa-utils v0.0.127 h1:uVyH/+pjpiA0oBO6QODjtdCiPgHx7KQ/TcxxkPvAl4c=
github.com/armosec/opa-utils v0.0.127/go.mod h1:gap+EaLG5rnyqvIRGxtdNDC9y7VvoGNm90zK8Ls7avQ=
github.com/armosec/opa-utils v0.0.130 h1:uP60M0PzmDtLqvsA/jX8BED9/Ava4n2QG7VCkuI+hwI=
github.com/armosec/opa-utils v0.0.130/go.mod h1:gap+EaLG5rnyqvIRGxtdNDC9y7VvoGNm90zK8Ls7avQ=
github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40=
github.com/armosec/rbac-utils v0.0.14 h1:CKYKcgqJEXWF2Hen/B1pVGtS3nDAG1wp9dDv6oNtq90=
github.com/armosec/rbac-utils v0.0.14/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0=

View File

@@ -17,7 +17,7 @@ import (
const (
scanPath = "/v1/scan"
resultsPath = "/v1/results"
prometheusMmeticsPath = "/metrics"
prometheusMmeticsPath = "/v1/metrics"
livePath = "/livez"
readyPath = "/readyz"
)

View File

@@ -54,6 +54,6 @@ echo -e "\033[0m"
$KUBESCAPE_EXEC version
echo
echo -e "\033[35mUsage: $ $KUBESCAPE_EXEC scan --submit --enable-host-scan"
echo -e "\033[35mUsage: $ $KUBESCAPE_EXEC scan --submit --enable-host-scan --format-version v2 --verbose"
echo -e "\033[0m"