mirror of
https://github.com/kubescape/kubescape.git
synced 2026-02-19 20:39:56 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05b6394c5c | ||
|
|
306b9d28ca | ||
|
|
6fe87bba20 | ||
|
|
c0d534072d | ||
|
|
009221aa98 | ||
|
|
46e5aff5f9 | ||
|
|
59498361e7 | ||
|
|
c652da130d | ||
|
|
9e524ffc34 | ||
|
|
004cc0c469 | ||
|
|
bd089d76af | ||
|
|
d5025b54bf | ||
|
|
740497047d | ||
|
|
d917e21364 | ||
|
|
32cedaf565 | ||
|
|
4c2a5e9a11 | ||
|
|
a41d2a46ff | ||
|
|
4794cbfb36 | ||
|
|
d021217cf7 | ||
|
|
4573d83831 | ||
|
|
e68e6dcd3d |
@@ -100,6 +100,7 @@ Set-ExecutionPolicy RemoteSigned -scope CurrentUser
|
||||
| `--submit` | `false` | If set, Kubescape will 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 sent | `true`/`false` |
|
||||
| `--keep-local` | `false` | Kubescape will not send scan results to Armo management portal. Use this flag if you ran with the `--submit` flag in the past and you do not want to submit your current scan results | `true`/`false` |
|
||||
| `--account` | | Armo portal account ID. Default will load account ID from configMap or config file | |
|
||||
| `--cluster` | current-context | Cluster context to scan | |
|
||||
| `--verbose` | `false` | Display all of the input resources and not only failed resources | `true`/`false` |
|
||||
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ type LocalConfig struct {
|
||||
configObj *ConfigObj
|
||||
}
|
||||
|
||||
func NewLocalConfig(backendAPI getter.IBackend, customerGUID string) *LocalConfig {
|
||||
func NewLocalConfig(backendAPI getter.IBackend, customerGUID, clusterName string) *LocalConfig {
|
||||
var configObj *ConfigObj
|
||||
|
||||
lc := &LocalConfig{
|
||||
@@ -95,6 +95,9 @@ func NewLocalConfig(backendAPI getter.IBackend, customerGUID string) *LocalConfi
|
||||
if customerGUID != "" {
|
||||
lc.configObj.CustomerGUID = customerGUID // override config customerGUID
|
||||
}
|
||||
if clusterName != "" {
|
||||
lc.configObj.ClusterName = AdoptClusterName(clusterName) // override config clusterName
|
||||
}
|
||||
if lc.configObj.CustomerGUID != "" {
|
||||
if err := lc.SetTenant(); err != nil {
|
||||
fmt.Println(err)
|
||||
@@ -107,7 +110,7 @@ func NewLocalConfig(backendAPI getter.IBackend, customerGUID string) *LocalConfi
|
||||
func (lc *LocalConfig) GetConfigObj() *ConfigObj { return lc.configObj }
|
||||
func (lc *LocalConfig) GetCustomerGUID() string { return lc.configObj.CustomerGUID }
|
||||
func (lc *LocalConfig) SetCustomerGUID(customerGUID string) { lc.configObj.CustomerGUID = customerGUID }
|
||||
func (lc *LocalConfig) GetClusterName() string { return "" }
|
||||
func (lc *LocalConfig) GetClusterName() string { return lc.configObj.ClusterName }
|
||||
func (lc *LocalConfig) IsConfigFound() bool { return existsConfigFile() }
|
||||
func (lc *LocalConfig) SetTenant() error {
|
||||
// ARMO tenant GUID
|
||||
@@ -163,7 +166,7 @@ type ClusterConfig struct {
|
||||
configObj *ConfigObj
|
||||
}
|
||||
|
||||
func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBackend, customerGUID string) *ClusterConfig {
|
||||
func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBackend, customerGUID, clusterName string) *ClusterConfig {
|
||||
var configObj *ConfigObj
|
||||
c := &ClusterConfig{
|
||||
k8s: k8s,
|
||||
@@ -176,7 +179,8 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken
|
||||
// get from configMap
|
||||
if c.existsConfigMap() {
|
||||
configObj, _ = c.loadConfigFromConfigMap()
|
||||
} else if existsConfigFile() { // get from file
|
||||
}
|
||||
if configObj == nil && existsConfigFile() { // get from file
|
||||
configObj, _ = loadConfigFromFile()
|
||||
}
|
||||
if configObj != nil {
|
||||
@@ -185,6 +189,9 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken
|
||||
if customerGUID != "" {
|
||||
c.configObj.CustomerGUID = customerGUID // override config customerGUID
|
||||
}
|
||||
if clusterName != "" {
|
||||
c.configObj.ClusterName = AdoptClusterName(clusterName) // override config clusterName
|
||||
}
|
||||
if c.configObj.CustomerGUID != "" {
|
||||
if err := c.SetTenant(); err != nil {
|
||||
fmt.Println(err)
|
||||
@@ -399,9 +406,10 @@ func readConfig(dat []byte) (*ConfigObj, error) {
|
||||
return nil, nil
|
||||
}
|
||||
configObj := &ConfigObj{}
|
||||
err := json.Unmarshal(dat, configObj)
|
||||
|
||||
return configObj, err
|
||||
if err := json.Unmarshal(dat, configObj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return configObj, nil
|
||||
}
|
||||
|
||||
// Check if the customer is submitted
|
||||
|
||||
@@ -25,8 +25,10 @@ func (policies *Policies) Set(frameworks []reporthandling.Framework, version str
|
||||
compatibleRules = append(compatibleRules, frameworks[i].Controls[j].Rules[r])
|
||||
}
|
||||
}
|
||||
frameworks[i].Controls[j].Rules = compatibleRules
|
||||
policies.Controls[frameworks[i].Controls[j].ControlID] = frameworks[i].Controls[j]
|
||||
if len(compatibleRules) > 0 {
|
||||
frameworks[i].Controls[j].Rules = compatibleRules
|
||||
policies.Controls[frameworks[i].Controls[j].ControlID] = frameworks[i].Controls[j]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
package cautils
|
||||
|
||||
type DownloadInfo struct {
|
||||
Path string
|
||||
Target string
|
||||
Name string
|
||||
Account string
|
||||
Path string // directory to save artifact. Default is "~/.kubescape/"
|
||||
FileName string // can be empty
|
||||
Target string // type of artifact to download
|
||||
Name string // name of artifact to download
|
||||
Account string // customerGUID
|
||||
}
|
||||
|
||||
@@ -112,6 +112,21 @@ func (armoAPI *ArmoAPI) GetFramework(name string) (*reporthandling.Framework, er
|
||||
return framework, err
|
||||
}
|
||||
|
||||
func (armoAPI *ArmoAPI) GetFrameworks() ([]reporthandling.Framework, error) {
|
||||
respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getListFrameworkURL(), nil)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
frameworks := []reporthandling.Framework{}
|
||||
if err = JSONDecoder(respStr).Decode(&frameworks); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// SaveInFile(framework, GetDefaultPath(name+".json"))
|
||||
|
||||
return frameworks, err
|
||||
}
|
||||
|
||||
func (armoAPI *ArmoAPI) GetControl(policyName string) (*reporthandling.Control, error) {
|
||||
return nil, fmt.Errorf("control api is not public")
|
||||
}
|
||||
|
||||
@@ -41,6 +41,14 @@ func (drp *DownloadReleasedPolicy) GetFramework(name string) (*reporthandling.Fr
|
||||
return framework, err
|
||||
}
|
||||
|
||||
func (drp *DownloadReleasedPolicy) GetFrameworks() ([]reporthandling.Framework, error) {
|
||||
frameworks, err := drp.gs.GetOPAFrameworks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return frameworks, err
|
||||
}
|
||||
|
||||
func (drp *DownloadReleasedPolicy) ListFrameworks() ([]string, error) {
|
||||
return drp.gs.GetOPAFrameworksNamesList()
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ const ListName ListType = "name"
|
||||
|
||||
type IPolicyGetter interface {
|
||||
GetFramework(name string) (*reporthandling.Framework, error)
|
||||
GetFrameworks() ([]reporthandling.Framework, error)
|
||||
GetControl(name string) (*reporthandling.Control, error)
|
||||
|
||||
ListFrameworks() ([]string, error)
|
||||
|
||||
@@ -78,6 +78,12 @@ func (lp *LoadPolicy) GetFramework(frameworkName string) (*reporthandling.Framew
|
||||
return framework, err
|
||||
}
|
||||
|
||||
func (lp *LoadPolicy) GetFrameworks() ([]reporthandling.Framework, error) {
|
||||
frameworks := []reporthandling.Framework{}
|
||||
var err error
|
||||
return frameworks, err
|
||||
}
|
||||
|
||||
func (lp *LoadPolicy) ListFrameworks() ([]string, error) {
|
||||
// TODO - Support
|
||||
return []string{}, fmt.Errorf("loading frameworks list from file is not supported")
|
||||
|
||||
7
cautils/listpolicies.go
Normal file
7
cautils/listpolicies.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package cautils
|
||||
|
||||
type ListPolicies struct {
|
||||
Target string
|
||||
ListIDs bool
|
||||
Account string
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
package v1
|
||||
package cautils
|
||||
|
||||
import (
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/k8s-interface/workloadinterface"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
helpersv1 "github.com/armosec/opa-utils/reporthandling/helpers/v1"
|
||||
"github.com/armosec/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
"github.com/armosec/opa-utils/reporthandling/results/v1/resourcesresults"
|
||||
"github.com/armosec/opa-utils/score"
|
||||
)
|
||||
|
||||
func reportV2ToV1(opaSessionObj *cautils.OPASessionObj) {
|
||||
func ReportV2ToV1(opaSessionObj *OPASessionObj) {
|
||||
if len(opaSessionObj.PostureReport.FrameworkReports) > 0 {
|
||||
return // report already converted
|
||||
}
|
||||
|
||||
opaSessionObj.PostureReport.ClusterCloudProvider = opaSessionObj.Report.ClusterCloudProvider
|
||||
|
||||
@@ -34,9 +36,9 @@ func reportV2ToV1(opaSessionObj *cautils.OPASessionObj) {
|
||||
frameworks = append(frameworks, fwv1)
|
||||
}
|
||||
|
||||
// remove unused data
|
||||
opaSessionObj.Report = nil
|
||||
opaSessionObj.ResourcesResult = nil
|
||||
// // remove unused data
|
||||
// opaSessionObj.Report = nil
|
||||
// opaSessionObj.ResourcesResult = nil
|
||||
|
||||
// setup counters and score
|
||||
for f := range frameworks {
|
||||
@@ -56,6 +58,7 @@ func reportV2ToV1(opaSessionObj *cautils.OPASessionObj) {
|
||||
|
||||
opaSessionObj.PostureReport.FrameworkReports = frameworks
|
||||
|
||||
// opaSessionObj.Report.SummaryDetails.Score = 0
|
||||
// for i := range frameworks {
|
||||
// for j := range frameworks[i].ControlReports {
|
||||
// // frameworks[i].ControlReports[j].Score
|
||||
@@ -73,29 +76,41 @@ func reportV2ToV1(opaSessionObj *cautils.OPASessionObj) {
|
||||
// opaSessionObj.Report.SummaryDetails.Controls[frameworks[i].ControlReports[j].ControlID] = c
|
||||
// }
|
||||
// }
|
||||
// opaSessionObj.Report.SummaryDetails.Score += opaSessionObj.PostureReport.FrameworkReports[i].Score
|
||||
// }
|
||||
// opaSessionObj.Report.SummaryDetails.Score /= float32(len(opaSessionObj.Report.SummaryDetails.Frameworks))
|
||||
}
|
||||
|
||||
func controlReportV2ToV1(opaSessionObj *cautils.OPASessionObj, frameworkName string, controls map[string]reportsummary.ControlSummary) []reporthandling.ControlReport {
|
||||
func controlReportV2ToV1(opaSessionObj *OPASessionObj, frameworkName string, controls map[string]reportsummary.ControlSummary) []reporthandling.ControlReport {
|
||||
controlRepors := []reporthandling.ControlReport{}
|
||||
for controlID, crv2 := range controls {
|
||||
crv1 := reporthandling.ControlReport{}
|
||||
crv1.ControlID = controlID
|
||||
crv1.BaseScore = crv2.ScoreFactor
|
||||
crv1.Name = crv2.GetName()
|
||||
|
||||
crv1.Control_ID = controlID
|
||||
// crv1.Attributes = crv2.
|
||||
crv1.Score = crv2.GetScore()
|
||||
|
||||
// TODO - add fields
|
||||
crv1.Description = crv2.Description
|
||||
crv1.Remediation = crv2.Remediation
|
||||
|
||||
rulesv1 := initializeRuleList(&crv2, opaSessionObj.ResourcesResult)
|
||||
rulesv1 := map[string]reporthandling.RuleReport{}
|
||||
|
||||
for _, resourceID := range crv2.List().All() {
|
||||
for _, resourceID := range crv2.ListResourcesIDs().All() {
|
||||
if result, ok := opaSessionObj.ResourcesResult[resourceID]; ok {
|
||||
for _, rulev2 := range result.ListRulesOfControl(crv2.GetID(), "") {
|
||||
|
||||
if _, ok := rulesv1[rulev2.GetName()]; !ok {
|
||||
rulesv1[rulev2.GetName()] = reporthandling.RuleReport{
|
||||
Name: rulev2.GetName(),
|
||||
RuleStatus: reporthandling.RuleStatus{
|
||||
Status: "success",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
rulev1 := rulesv1[rulev2.GetName()]
|
||||
status := rulev2.GetStatus(&helpersv1.Filters{FrameworkNames: []string{frameworkName}})
|
||||
|
||||
@@ -113,10 +128,11 @@ func controlReportV2ToV1(opaSessionObj *cautils.OPASessionObj, frameworkName str
|
||||
}
|
||||
|
||||
if fullRessource, ok := opaSessionObj.AllResources[resourceID]; ok {
|
||||
ruleResponse.AlertObject.K8SApiObjects = append(ruleResponse.AlertObject.K8SApiObjects, fullRessource.GetObject())
|
||||
tmp := fullRessource.GetObject()
|
||||
workloadinterface.RemoveFromMap(tmp, "spec")
|
||||
ruleResponse.AlertObject.K8SApiObjects = append(ruleResponse.AlertObject.K8SApiObjects, tmp)
|
||||
}
|
||||
rulev1.RuleResponses = append(rulev1.RuleResponses, ruleResponse)
|
||||
|
||||
}
|
||||
|
||||
rulev1.ListInputKinds = append(rulev1.ListInputKinds, resourceID)
|
||||
@@ -129,25 +145,10 @@ func controlReportV2ToV1(opaSessionObj *cautils.OPASessionObj, frameworkName str
|
||||
crv1.RuleReports = append(crv1.RuleReports, rulesv1[i])
|
||||
}
|
||||
}
|
||||
if len(crv1.RuleReports) == 0 {
|
||||
crv1.RuleReports = []reporthandling.RuleReport{}
|
||||
}
|
||||
controlRepors = append(controlRepors, crv1)
|
||||
}
|
||||
return controlRepors
|
||||
}
|
||||
|
||||
func initializeRuleList(crv2 *reportsummary.ControlSummary, resourcesResult map[string]resourcesresults.Result) map[string]reporthandling.RuleReport {
|
||||
rulesv1 := map[string]reporthandling.RuleReport{} // ruleName: rules
|
||||
|
||||
for _, resourceID := range crv2.List().All() {
|
||||
if result, ok := resourcesResult[resourceID]; ok {
|
||||
for _, rulev2 := range result.ListRulesOfControl(crv2.GetID(), "") {
|
||||
// add to rule
|
||||
if _, ok := rulesv1[rulev2.GetName()]; !ok {
|
||||
rulesv1[rulev2.GetName()] = reporthandling.RuleReport{
|
||||
Name: rulev2.GetName(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rulesv1
|
||||
}
|
||||
@@ -64,6 +64,7 @@ type ScanInfo struct {
|
||||
HostSensor BoolPtrFlag // Deploy ARMO K8s host sensor to collect data from certain controls
|
||||
Local bool // Do not submit results
|
||||
Account string // account ID
|
||||
ClusterName string // cluster name
|
||||
FrameworkScan bool // false if scanning control
|
||||
ScanAll bool // true if scan all frameworks
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@ package clihandler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/armoapi-go/armotypes"
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/kubescape/cautils/getter"
|
||||
)
|
||||
@@ -13,6 +15,7 @@ var downloadFunc = map[string]func(*cautils.DownloadInfo) error{
|
||||
"exceptions": downloadExceptions,
|
||||
"control": downloadControl,
|
||||
"framework": downloadFramework,
|
||||
"artifacts": downloadArtifacts,
|
||||
}
|
||||
|
||||
func DownloadSupportCommands() []string {
|
||||
@@ -24,93 +27,150 @@ func DownloadSupportCommands() []string {
|
||||
}
|
||||
|
||||
func CliDownload(downloadInfo *cautils.DownloadInfo) error {
|
||||
if f, ok := downloadFunc[downloadInfo.Target]; ok {
|
||||
setPathandFilename(downloadInfo)
|
||||
if err := downloadArtifact(downloadInfo, downloadFunc); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func downloadArtifact(downloadInfo *cautils.DownloadInfo, downloadArtifactFunc map[string]func(*cautils.DownloadInfo) error) error {
|
||||
if f, ok := downloadArtifactFunc[downloadInfo.Target]; ok {
|
||||
if err := f(downloadInfo); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
fmt.Println(fmt.Sprintf("'%s' downloaded successfully and saved at: '%s'", downloadInfo.Target, downloadInfo.Path))
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unknown command to download")
|
||||
}
|
||||
|
||||
func setPathandFilename(downloadInfo *cautils.DownloadInfo) {
|
||||
if downloadInfo.Path == "" {
|
||||
downloadInfo.Path = getter.GetDefaultPath("")
|
||||
} else {
|
||||
dir, file := filepath.Split(downloadInfo.Path)
|
||||
if dir == "" {
|
||||
downloadInfo.Path = file
|
||||
} else {
|
||||
downloadInfo.Path = dir
|
||||
downloadInfo.FileName = file
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func downloadArtifacts(downloadInfo *cautils.DownloadInfo) error {
|
||||
downloadInfo.FileName = ""
|
||||
var artifacts = map[string]func(*cautils.DownloadInfo) error{
|
||||
"controls-inputs": downloadConfigInputs,
|
||||
"exceptions": downloadExceptions,
|
||||
"framework": downloadFramework,
|
||||
}
|
||||
for artifact := range artifacts {
|
||||
if err := downloadArtifact(&cautils.DownloadInfo{Target: artifact, Path: downloadInfo.Path, FileName: fmt.Sprintf("%s.json", artifact)}, artifacts); err != nil {
|
||||
fmt.Printf("error downloading %s, error: %s", artifact, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func downloadConfigInputs(downloadInfo *cautils.DownloadInfo) error {
|
||||
tenant := getTenantConfig(downloadInfo.Account, getKubernetesApi()) // change k8sinterface
|
||||
tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi())
|
||||
controlsInputsGetter := getConfigInputsGetter(downloadInfo.Name, tenant.GetCustomerGUID(), nil)
|
||||
controlInputs, err := controlsInputsGetter.GetControlsInputs(tenant.GetClusterName())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if downloadInfo.Path == "" {
|
||||
downloadInfo.Path = getter.GetDefaultPath(fmt.Sprintf("%s.json", downloadInfo.Target))
|
||||
if downloadInfo.FileName == "" {
|
||||
downloadInfo.FileName = fmt.Sprintf("%s.json", downloadInfo.Target)
|
||||
}
|
||||
// save in file
|
||||
err = getter.SaveInFile(controlInputs, downloadInfo.Path)
|
||||
err = getter.SaveInFile(controlInputs, filepath.Join(downloadInfo.Path, downloadInfo.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("'%s' downloaded successfully and saved at: '%s'\n", downloadInfo.Target, filepath.Join(downloadInfo.Path, downloadInfo.FileName))
|
||||
return nil
|
||||
}
|
||||
|
||||
func downloadExceptions(downloadInfo *cautils.DownloadInfo) error {
|
||||
tenant := getTenantConfig(downloadInfo.Account, getKubernetesApi()) // change k8sinterface
|
||||
var err error
|
||||
tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi())
|
||||
exceptionsGetter := getExceptionsGetter("")
|
||||
exceptions, err := exceptionsGetter.GetExceptions(tenant.GetClusterName())
|
||||
if err != nil {
|
||||
return err
|
||||
exceptions := []armotypes.PostureExceptionPolicy{}
|
||||
if tenant.GetCustomerGUID() != "" {
|
||||
exceptions, err = exceptionsGetter.GetExceptions(tenant.GetClusterName())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if downloadInfo.Path == "" {
|
||||
downloadInfo.Path = getter.GetDefaultPath(fmt.Sprintf("%s.json", downloadInfo.Target))
|
||||
if downloadInfo.FileName == "" {
|
||||
downloadInfo.FileName = fmt.Sprintf("%s.json", downloadInfo.Target)
|
||||
}
|
||||
// save in file
|
||||
err = getter.SaveInFile(exceptions, downloadInfo.Path)
|
||||
err = getter.SaveInFile(exceptions, filepath.Join(downloadInfo.Path, downloadInfo.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("'%s' downloaded successfully and saved at: '%s'\n", downloadInfo.Target, filepath.Join(downloadInfo.Path, downloadInfo.FileName))
|
||||
return nil
|
||||
}
|
||||
|
||||
func downloadFramework(downloadInfo *cautils.DownloadInfo) error {
|
||||
tenant := getTenantConfig(downloadInfo.Account, getKubernetesApi()) // change k8sinterface
|
||||
|
||||
tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi())
|
||||
g := getPolicyGetter(nil, tenant.GetCustomerGUID(), true, nil)
|
||||
|
||||
if downloadInfo.Name == "" {
|
||||
// TODO - support
|
||||
return fmt.Errorf("missing framework name")
|
||||
}
|
||||
if downloadInfo.Path == "" {
|
||||
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.Name + ".json")
|
||||
}
|
||||
frameworks, err := g.GetFramework(downloadInfo.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = getter.SaveInFile(frameworks, downloadInfo.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
// if framework name not specified - download all frameworks
|
||||
frameworks, err := g.GetFrameworks()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, fw := range frameworks {
|
||||
err = getter.SaveInFile(fw, filepath.Join(downloadInfo.Path, (strings.ToLower(fw.Name)+".json")))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("'%s': '%s' downloaded successfully and saved at: '%s'\n", downloadInfo.Target, fw.Name, filepath.Join(downloadInfo.Path, (strings.ToLower(fw.Name)+".json")))
|
||||
}
|
||||
// return fmt.Errorf("missing framework name")
|
||||
} else {
|
||||
if downloadInfo.FileName == "" {
|
||||
downloadInfo.FileName = fmt.Sprintf("%s.json", downloadInfo.Name)
|
||||
}
|
||||
framework, err := g.GetFramework(downloadInfo.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = getter.SaveInFile(framework, filepath.Join(downloadInfo.Path, downloadInfo.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("'%s' downloaded successfully and saved at: '%s'\n", downloadInfo.Target, filepath.Join(downloadInfo.Path, downloadInfo.FileName))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func downloadControl(downloadInfo *cautils.DownloadInfo) error {
|
||||
tenant := getTenantConfig(downloadInfo.Account, getKubernetesApi()) // change k8sinterface
|
||||
g := getPolicyGetter(nil, tenant.GetCustomerGUID(), true, nil)
|
||||
|
||||
tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi())
|
||||
g := getPolicyGetter(nil, tenant.GetCustomerGUID(), false, nil)
|
||||
|
||||
if downloadInfo.Name == "" {
|
||||
// TODO - support
|
||||
return fmt.Errorf("missing control name")
|
||||
}
|
||||
if downloadInfo.Path == "" {
|
||||
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.Name + ".json")
|
||||
if downloadInfo.FileName == "" {
|
||||
downloadInfo.FileName = fmt.Sprintf("%s.json", downloadInfo.Name)
|
||||
}
|
||||
controls, err := g.GetControl(downloadInfo.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = getter.SaveInFile(controls, downloadInfo.Path)
|
||||
err = getter.SaveInFile(controls, filepath.Join(downloadInfo.Path, downloadInfo.FileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("'%s' downloaded successfully and saved at: '%s'\n", downloadInfo.Target, filepath.Join(downloadInfo.Path, downloadInfo.FileName))
|
||||
return nil
|
||||
}
|
||||
|
||||
58
clihandler/clilist.go
Normal file
58
clihandler/clilist.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package clihandler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/kubescape/cautils/getter"
|
||||
)
|
||||
|
||||
var listFunc = map[string]func(*cautils.ListPolicies) ([]string, error){
|
||||
"controls": listControls,
|
||||
"frameworks": listFrameworks,
|
||||
}
|
||||
|
||||
func ListSupportCommands() []string {
|
||||
commands := []string{}
|
||||
for k := range listFunc {
|
||||
commands = append(commands, k)
|
||||
}
|
||||
return commands
|
||||
}
|
||||
func CliList(listPolicies *cautils.ListPolicies) error {
|
||||
if f, ok := listFunc[listPolicies.Target]; ok {
|
||||
policies, err := f(listPolicies)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sort.Strings(policies)
|
||||
|
||||
sep := "\n * "
|
||||
usageCmd := strings.TrimSuffix(listPolicies.Target, "s")
|
||||
fmt.Printf("Supported %s:%s%s\n", listPolicies.Target, sep, strings.Join(policies, sep))
|
||||
fmt.Printf("\nUseage:\n")
|
||||
fmt.Printf("$ kubescape scan %s \"name\"\n", usageCmd)
|
||||
fmt.Printf("$ kubescape scan %s \"name-0\",\"name-1\"\n\n", usageCmd)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unknown command to download")
|
||||
}
|
||||
|
||||
func listFrameworks(listPolicies *cautils.ListPolicies) ([]string, error) {
|
||||
tenant := getTenantConfig(listPolicies.Account, "", getKubernetesApi()) // change k8sinterface
|
||||
g := getPolicyGetter(nil, tenant.GetCustomerGUID(), true, nil)
|
||||
|
||||
return listFrameworksNames(g), nil
|
||||
}
|
||||
|
||||
func listControls(listPolicies *cautils.ListPolicies) ([]string, error) {
|
||||
tenant := getTenantConfig(listPolicies.Account, "", getKubernetesApi()) // change k8sinterface
|
||||
g := getPolicyGetter(nil, tenant.GetCustomerGUID(), false, nil)
|
||||
l := getter.ListName
|
||||
if listPolicies.ListIDs {
|
||||
l = getter.ListID
|
||||
}
|
||||
return g.ListControls(l)
|
||||
}
|
||||
@@ -11,10 +11,9 @@ import (
|
||||
)
|
||||
|
||||
var getCmd = &cobra.Command{
|
||||
Use: "get <key>",
|
||||
Short: "Get configuration in cluster",
|
||||
Long: ``,
|
||||
ValidArgs: getter.NativeFrameworks,
|
||||
Use: "get <key>",
|
||||
Short: "Get configuration in cluster",
|
||||
Long: ``,
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 || len(args) > 1 {
|
||||
return fmt.Errorf("requires one argument")
|
||||
@@ -31,7 +30,7 @@ var getCmd = &cobra.Command{
|
||||
key := keyValue[0]
|
||||
|
||||
k8s := k8sinterface.NewKubernetesApi()
|
||||
clusterConfig := cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account)
|
||||
clusterConfig := cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account, "")
|
||||
val, err := clusterConfig.GetValueByKeyFromConfigMap(key)
|
||||
if err != nil {
|
||||
if err.Error() == "value does not exist." {
|
||||
|
||||
@@ -30,7 +30,7 @@ var setCmd = &cobra.Command{
|
||||
data := keyValue[1]
|
||||
|
||||
k8s := k8sinterface.NewKubernetesApi()
|
||||
clusterConfig := cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account)
|
||||
clusterConfig := cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account, "")
|
||||
if err := clusterConfig.SetKeyValueInConfigmap(key, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/kubescape/cautils/getter"
|
||||
"github.com/armosec/kubescape/clihandler"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -16,14 +15,16 @@ import (
|
||||
var (
|
||||
controlExample = `
|
||||
# Scan the 'privileged container' control
|
||||
kubescape scan control 'privileged container'
|
||||
kubescape scan control "privileged container"
|
||||
|
||||
# Scan list of controls separated with a comma
|
||||
kubescape scan control 'privileged container,allowed hostpath'
|
||||
kubescape scan control "privileged container","allowed hostpath"
|
||||
|
||||
# Scan list of controls using the control ID separated with a comma
|
||||
kubescape scan control C-0058,C-0057
|
||||
|
||||
Run 'kubescape list controls' for the list of supported controls
|
||||
|
||||
Control documentation:
|
||||
https://hub.armo.cloud/docs/controls
|
||||
`
|
||||
@@ -52,7 +53,7 @@ var controlCmd = &cobra.Command{
|
||||
scanInfo.PolicyIdentifier = []reporthandling.PolicyIdentifier{}
|
||||
|
||||
if len(args) == 0 {
|
||||
scanInfo.SetPolicyIdentifiers(getter.NativeFrameworks, reporthandling.KindFramework)
|
||||
// scanInfo.SetPolicyIdentifiers(getter.NativeFrameworks, reporthandling.KindFramework)
|
||||
scanInfo.ScanAll = true
|
||||
} else { // expected control or list of control sepparated by ","
|
||||
|
||||
|
||||
@@ -12,14 +12,40 @@ import (
|
||||
|
||||
var downloadInfo = cautils.DownloadInfo{}
|
||||
|
||||
var (
|
||||
downloadExample = `
|
||||
# Download all artifacts and save them in the default path (~/.kubescape)
|
||||
kubescape download artifacts
|
||||
|
||||
# Download all artifacts and save them in /tmp path
|
||||
kubescape download artifacts --output /tmp
|
||||
|
||||
# Download the NSA framework. Run 'kubescape list frameworks' for all frameworks names
|
||||
kubescape download frameworks nsa
|
||||
|
||||
# Download the "Allowed hostPath" control. Run 'kubescape list controls' for all controls names
|
||||
kubescape download control "Allowed hostPath"
|
||||
|
||||
# Download the "C-0001" control. Run 'kubescape list controls --id' for all controls ids
|
||||
kubescape download control C-0001
|
||||
|
||||
# Download the configured exceptions
|
||||
kubescape download exceptions
|
||||
|
||||
# Download the configured controls-inputs
|
||||
kubescape download controls-inputs
|
||||
|
||||
`
|
||||
)
|
||||
var downloadCmd = &cobra.Command{
|
||||
Use: "download <policy> <policy name>",
|
||||
Short: fmt.Sprintf("Download %s", strings.Join(clihandler.DownloadSupportCommands(), "/")),
|
||||
Long: ``,
|
||||
Use: "download <policy> <policy name>",
|
||||
Short: fmt.Sprintf("Download %s", strings.Join(clihandler.DownloadSupportCommands(), ",")),
|
||||
Long: ``,
|
||||
Example: downloadExample,
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
supported := strings.Join(clihandler.DownloadSupportCommands(), ",")
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("policy type requeued, supported: %v", supported)
|
||||
return fmt.Errorf("policy type required, supported: %v", supported)
|
||||
}
|
||||
if cautils.StringInSlice(clihandler.DownloadSupportCommands(), args[0]) == cautils.ValueNotFound {
|
||||
return fmt.Errorf("invalid parameter '%s'. Supported parameters: %s", args[0], supported)
|
||||
@@ -43,7 +69,7 @@ func init() {
|
||||
// cobra.OnInitialize(initConfig)
|
||||
|
||||
rootCmd.AddCommand(downloadCmd)
|
||||
downloadCmd.Flags().StringVarP(&downloadInfo.Path, "output", "o", "", "Output file. If specified, will store save to `~/.kubescape/<policy name>.json`")
|
||||
downloadCmd.Flags().StringVarP(&downloadInfo.Path, "output", "o", "", "Output file. If not specified, will save in `~/.kubescape/<policy name>.json`")
|
||||
downloadCmd.PersistentFlags().StringVarP(&downloadInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file")
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/kubescape/cautils/getter"
|
||||
"github.com/armosec/kubescape/clihandler"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -24,6 +23,9 @@ var (
|
||||
# Scan the NSA and MITRE framework
|
||||
kubescape scan framework nsa,mitre
|
||||
|
||||
# Scan all frameworks
|
||||
kubescape scan framework all
|
||||
|
||||
# Scan kubernetes YAML manifest files
|
||||
kubescape scan framework nsa *.yaml
|
||||
|
||||
@@ -35,14 +37,16 @@ var (
|
||||
|
||||
# Display all resources
|
||||
kubescape scan --verbose
|
||||
|
||||
Run 'kubescape list frameworks' for the list of supported frameworks
|
||||
`
|
||||
)
|
||||
var frameworkCmd = &cobra.Command{
|
||||
Use: "framework <framework names list> [`<glob pattern>`/`-`] [flags]",
|
||||
Short: "The framework you wish to use. Run 'kubescape list frameworks' for the list of supported frameworks",
|
||||
Example: frameworkExample,
|
||||
Long: "Execute a scan on a running Kubernetes cluster or `yaml`/`json` files (use glob) or `-` for stdin",
|
||||
ValidArgs: getter.NativeFrameworks,
|
||||
Use: "framework <framework names list> [`<glob pattern>`/`-`] [flags]",
|
||||
Short: "The framework you wish to use. Run 'kubescape list frameworks' for the list of supported frameworks",
|
||||
Example: frameworkExample,
|
||||
Long: "Execute a scan on a running Kubernetes cluster or `yaml`/`json` files (use glob) or `-` for stdin",
|
||||
// ValidArgs: getter.NativeFrameworks,
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 0 {
|
||||
frameworks := strings.Split(args[0], ",")
|
||||
@@ -61,12 +65,15 @@ var frameworkCmd = &cobra.Command{
|
||||
var frameworks []string
|
||||
|
||||
if len(args) == 0 { // scan all frameworks
|
||||
frameworks = getter.NativeFrameworks
|
||||
// frameworks = getter.NativeFrameworks
|
||||
scanInfo.ScanAll = true
|
||||
} else {
|
||||
// Read frameworks from input args
|
||||
frameworks = strings.Split(args[0], ",")
|
||||
|
||||
if cautils.StringInSlice(frameworks, "all") != cautils.ValueNotFound {
|
||||
scanInfo.ScanAll = true
|
||||
frameworks = []string{}
|
||||
}
|
||||
if len(args) > 1 {
|
||||
if len(args[1:]) == 0 || args[1] != "-" {
|
||||
scanInfo.InputPatterns = args[1:]
|
||||
@@ -84,6 +91,8 @@ var frameworkCmd = &cobra.Command{
|
||||
}
|
||||
}
|
||||
}
|
||||
scanInfo.FrameworkScan = true
|
||||
|
||||
scanInfo.SetPolicyIdentifiers(frameworks, reporthandling.KindFramework)
|
||||
|
||||
scanInfo.Init()
|
||||
|
||||
66
clihandler/cmd/list.go
Normal file
66
clihandler/cmd/list.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/kubescape/clihandler"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
listExample = `
|
||||
# List default supported frameworks names
|
||||
kubescape list frameworks
|
||||
|
||||
# List all supported frameworks names
|
||||
kubescape list frameworks --account <account id>
|
||||
|
||||
# List all supported controls names
|
||||
kubescape list controls
|
||||
|
||||
# List all supported controls ids
|
||||
kubescape list controls --id
|
||||
|
||||
Control documentation:
|
||||
https://hub.armo.cloud/docs/controls
|
||||
`
|
||||
)
|
||||
var listPolicies = cautils.ListPolicies{}
|
||||
|
||||
var listCmd = &cobra.Command{
|
||||
Use: "list <policy> [flags]",
|
||||
Short: "List frameworks/controls will list the supported frameworks and controls",
|
||||
Long: ``,
|
||||
Example: listExample,
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
supported := strings.Join(clihandler.ListSupportCommands(), ",")
|
||||
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("policy type requeued, supported: %s", supported)
|
||||
}
|
||||
if cautils.StringInSlice(clihandler.ListSupportCommands(), args[0]) == cautils.ValueNotFound {
|
||||
return fmt.Errorf("invalid parameter '%s'. Supported parameters: %s", args[0], supported)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
listPolicies.Target = args[0]
|
||||
|
||||
if err := clihandler.CliList(&listPolicies); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
// cobra.OnInitialize(initConfig)
|
||||
|
||||
rootCmd.AddCommand(listCmd)
|
||||
listCmd.PersistentFlags().StringVarP(&listPolicies.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file")
|
||||
listCmd.PersistentFlags().BoolVarP(&listPolicies.ListIDs, "id", "", false, "List control ID's instead of controls names")
|
||||
}
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/k8s-interface/k8sinterface"
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/kubescape/cautils/getter"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -27,15 +27,22 @@ var scanCmd = &cobra.Command{
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
scanInfo.ScanAll = true
|
||||
frameworks := getter.NativeFrameworks
|
||||
frameworkArgs := []string{strings.Join(frameworks, ",")}
|
||||
frameworkCmd.RunE(cmd, frameworkArgs)
|
||||
// frameworks := getter.NativeFrameworks
|
||||
// frameworkArgs := []string{strings.Join(frameworks, ",")}
|
||||
frameworkCmd.RunE(cmd, []string{"all"})
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func frameworkInitConfig() {
|
||||
k8sinterface.SetClusterContextName(scanInfo.ClusterName)
|
||||
}
|
||||
|
||||
func init() {
|
||||
cobra.OnInitialize(frameworkInitConfig)
|
||||
|
||||
rootCmd.AddCommand(scanCmd)
|
||||
rootCmd.PersistentFlags().StringVarP(&scanInfo.ClusterName, "cluster", "", "", "Cluster name. Default will use the current-context")
|
||||
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")
|
||||
|
||||
@@ -20,7 +20,7 @@ func init() {
|
||||
}
|
||||
|
||||
func getSubmittedClusterConfig(k8s *k8sinterface.KubernetesApi) (*cautils.ClusterConfig, error) {
|
||||
clusterConfig := cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account) // TODO - support none cluster env submit
|
||||
clusterConfig := cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account, scanInfo.ClusterName) // TODO - support none cluster env submit
|
||||
if clusterConfig.GetCustomerGUID() != "" {
|
||||
if err := clusterConfig.SetTenant(); err != nil {
|
||||
return clusterConfig, err
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
"github.com/armosec/k8s-interface/k8sinterface"
|
||||
"github.com/armosec/kubescape/resultshandling/printer"
|
||||
printerv1 "github.com/armosec/kubescape/resultshandling/printer/v1"
|
||||
|
||||
@@ -34,9 +35,16 @@ type componentInterfaces struct {
|
||||
|
||||
func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
|
||||
|
||||
k8s := getKubernetesApi()
|
||||
var k8s *k8sinterface.KubernetesApi
|
||||
if scanInfo.GetScanningEnvironment() == cautils.ScanCluster {
|
||||
k8s = getKubernetesApi()
|
||||
if k8s == nil {
|
||||
fmt.Println("Failed connecting to Kubernetes cluster")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
tenantConfig := getTenantConfig(scanInfo.Account, k8s)
|
||||
tenantConfig := getTenantConfig(scanInfo.Account, scanInfo.ClusterName, k8s)
|
||||
|
||||
// Set submit behavior AFTER loading tenant config
|
||||
setSubmitBehavior(scanInfo, tenantConfig)
|
||||
@@ -99,6 +107,9 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error {
|
||||
scanInfo.Getters.ExceptionsGetter = getExceptionsGetter(scanInfo.UseExceptions)
|
||||
|
||||
// TODO - list supported frameworks/controls
|
||||
if scanInfo.ScanAll {
|
||||
scanInfo.SetPolicyIdentifiers(listFrameworksNames(scanInfo.Getters.PolicyGetter), reporthandling.KindFramework)
|
||||
}
|
||||
|
||||
//
|
||||
defer func() {
|
||||
|
||||
@@ -11,22 +11,23 @@ import (
|
||||
"github.com/armosec/kubescape/resourcehandler"
|
||||
"github.com/armosec/kubescape/resultshandling/reporter"
|
||||
reporterv1 "github.com/armosec/kubescape/resultshandling/reporter/v1"
|
||||
reporterv2 "github.com/armosec/kubescape/resultshandling/reporter/v2"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"github.com/armosec/rbac-utils/rbacscanner"
|
||||
// reporterv2 "github.com/armosec/kubescape/resultshandling/reporter/v2"
|
||||
)
|
||||
|
||||
// getKubernetesApi
|
||||
func getKubernetesApi() *k8sinterface.KubernetesApi {
|
||||
if !k8sinterface.IsConnectedToCluster() {
|
||||
return nil
|
||||
}
|
||||
return k8sinterface.NewKubernetesApi()
|
||||
}
|
||||
func getTenantConfig(Account string, k8s *k8sinterface.KubernetesApi) cautils.ITenantConfig {
|
||||
if !k8sinterface.IsConnectedToCluster() {
|
||||
return cautils.NewLocalConfig(getter.GetArmoAPIConnector(), Account)
|
||||
func getTenantConfig(Account, clusterName string, k8s *k8sinterface.KubernetesApi) cautils.ITenantConfig {
|
||||
if !k8sinterface.IsConnectedToCluster() || k8s == nil {
|
||||
return cautils.NewLocalConfig(getter.GetArmoAPIConnector(), Account, clusterName)
|
||||
}
|
||||
return cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), Account)
|
||||
return cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), Account, clusterName)
|
||||
}
|
||||
|
||||
func getExceptionsGetter(useExceptions string) getter.IExceptionsGetter {
|
||||
@@ -47,12 +48,14 @@ func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.Kubern
|
||||
|
||||
func getReporter(tenantConfig cautils.ITenantConfig, submit bool) reporter.IReport {
|
||||
if submit {
|
||||
return reporterv1.NewReportEventReceiver(tenantConfig.GetConfigObj())
|
||||
// return reporterv1.NewReportEventReceiver(tenantConfig.GetConfigObj())
|
||||
return reporterv2.NewReportEventReceiver(tenantConfig.GetConfigObj())
|
||||
}
|
||||
return reporterv1.NewReportMock()
|
||||
}
|
||||
|
||||
func getResourceHandler(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, hostSensorHandler hostsensorutils.IHostSensor) resourcehandler.IResourceHandler {
|
||||
if scanInfo.GetScanningEnvironment() == cautils.ScanLocalFiles {
|
||||
if len(scanInfo.InputPatterns) > 0 || k8s == nil {
|
||||
return resourcehandler.NewFileResourceHandler(scanInfo.InputPatterns)
|
||||
}
|
||||
rbacObjects := getRBACHandler(tenantConfig, k8s, scanInfo.Submit)
|
||||
@@ -60,9 +63,10 @@ func getResourceHandler(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenant
|
||||
}
|
||||
|
||||
func getHostSensorHandler(scanInfo *cautils.ScanInfo, k8s *k8sinterface.KubernetesApi) hostsensorutils.IHostSensor {
|
||||
if scanInfo.GetScanningEnvironment() == cautils.ScanLocalFiles {
|
||||
if !k8sinterface.IsConnectedToCluster() || k8s == nil {
|
||||
return &hostsensorutils.HostSensorHandlerMock{}
|
||||
}
|
||||
|
||||
hasHostSensorControls := true
|
||||
// we need to determined which controls needs host sensor
|
||||
if scanInfo.HostSensor.Get() == nil && hasHostSensorControls {
|
||||
@@ -125,12 +129,6 @@ func setSubmitBehavior(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantC
|
||||
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
|
||||
@@ -212,3 +210,11 @@ func getDefaultFrameworksPaths() []string {
|
||||
}
|
||||
return fwPaths
|
||||
}
|
||||
|
||||
func listFrameworksNames(policyGetter getter.IPolicyGetter) []string {
|
||||
fw, err := policyGetter.ListFrameworks()
|
||||
if err != nil {
|
||||
fw = getDefaultFrameworksPaths()
|
||||
}
|
||||
return fw
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 53 KiB |
7
go.mod
7
go.mod
@@ -3,9 +3,9 @@ module github.com/armosec/kubescape
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/armosec/armoapi-go v0.0.40
|
||||
github.com/armosec/k8s-interface v0.0.50
|
||||
github.com/armosec/opa-utils v0.0.88
|
||||
github.com/armosec/armoapi-go v0.0.41
|
||||
github.com/armosec/k8s-interface v0.0.54
|
||||
github.com/armosec/opa-utils v0.0.95
|
||||
github.com/armosec/rbac-utils v0.0.11
|
||||
github.com/armosec/utils-go v0.0.3
|
||||
github.com/briandowns/spinner v1.18.0
|
||||
@@ -35,6 +35,7 @@ require (
|
||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/OneOfOne/xxhash v1.2.8 // indirect
|
||||
github.com/armosec/armo-interfaces v0.0.3 // 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
|
||||
|
||||
13
go.sum
13
go.sum
@@ -83,17 +83,20 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armosec/armo-interfaces v0.0.3 h1:kG4mJIPgWBJvQFDDy8JzdqX3ASbyl8t32IuJYqB31Pk=
|
||||
github.com/armosec/armo-interfaces v0.0.3/go.mod h1:7XYefhcBCFYoF5LflCZHWuUHu+JrSJbmzk0zoNv2WlU=
|
||||
github.com/armosec/armoapi-go v0.0.2/go.mod h1:vIK17yoKbJRQyZXWWLe3AqfqCRITxW8qmSkApyq5xFs=
|
||||
github.com/armosec/armoapi-go v0.0.23/go.mod h1:iaVVGyc23QGGzAdv4n+szGQg3Rbpixn9yQTU3qWRpaw=
|
||||
github.com/armosec/armoapi-go v0.0.40 h1:KQRJXFqw95s6cV7HoGgw1x8qrRZ9eNVze//yQbo24Lk=
|
||||
github.com/armosec/armoapi-go v0.0.40/go.mod h1:iaVVGyc23QGGzAdv4n+szGQg3Rbpixn9yQTU3qWRpaw=
|
||||
github.com/armosec/armoapi-go v0.0.41 h1:iMkaCsME+zhE6vnCOMaqfqc0cp7pste8QFHojeGKfGg=
|
||||
github.com/armosec/armoapi-go v0.0.41/go.mod h1:exk1O3rK6V+X8SSyxc06lwb0j9ILQuKAoIdz9hs6Ndw=
|
||||
github.com/armosec/k8s-interface v0.0.8/go.mod h1:xxS+V5QT3gVQTwZyAMMDrYLWGrfKOpiJ7Jfhfa0w9sM=
|
||||
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/k8s-interface v0.0.54 h1:1sQeoEZA5bgpXVibXhEiTSeLd3GKY5NkTOeewdgR0Bs=
|
||||
github.com/armosec/k8s-interface v0.0.54/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.88 h1:IxIml3w7l0HFqbb+XzKuXf+Pw78DHIxPwRIkgudKQRw=
|
||||
github.com/armosec/opa-utils v0.0.88/go.mod h1:ZOXYVTtuyrV4TldcfbzgRqP6F9Drlf4hB0zr210OXgM=
|
||||
github.com/armosec/opa-utils v0.0.95 h1:tGFJQChy8Yn+8HhLbQHUfa6HmbrtBFBOlIgkO/9IFY8=
|
||||
github.com/armosec/opa-utils v0.0.95/go.mod h1:BNTjeianyXlflJMz3bZM0GimBWqmzirUf1whWR6Os04=
|
||||
github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40=
|
||||
github.com/armosec/rbac-utils v0.0.11 h1:SCiVLqUeV+WGpUsWbOBt6jKkFAd62jztuzB6PIgHz7w=
|
||||
github.com/armosec/rbac-utils v0.0.11/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0=
|
||||
|
||||
@@ -59,10 +59,10 @@ func (opaHandler *OPAProcessorHandler) ProcessRulesListenner() {
|
||||
opaSessionObj := <-*opaHandler.processedPolicy
|
||||
opap := NewOPAProcessor(opaSessionObj, opaHandler.regoDependenciesData)
|
||||
|
||||
ConvertFrameworksToSummaryDetails(&opap.Report.SummaryDetails, opap.Frameworks)
|
||||
|
||||
policies := ConvertFrameworksToPolicies(opap.Frameworks, cautils.BuildNumber)
|
||||
|
||||
ConvertFrameworksToSummaryDetails(&opap.Report.SummaryDetails, opap.Frameworks, policies)
|
||||
|
||||
// process
|
||||
if err := opap.Process(policies); err != nil {
|
||||
// fmt.Println(err)
|
||||
@@ -197,6 +197,7 @@ func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule) (map[stri
|
||||
if r, k := resources[failedResources[j].GetID()]; k {
|
||||
ruleResult = r
|
||||
}
|
||||
|
||||
ruleResult.Status = apis.StatusFailed
|
||||
for j := range ruleResponses[i].FailedPaths {
|
||||
ruleResult.Paths = append(ruleResult.Paths, resourcesresults.Path{FailedPath: ruleResponses[i].FailedPaths[j]})
|
||||
|
||||
@@ -71,7 +71,7 @@ func TestProcessResourcesResult(t *testing.T) {
|
||||
opaSessionObj.Frameworks = frameworks
|
||||
|
||||
policies := ConvertFrameworksToPolicies(opaSessionObj.Frameworks, "")
|
||||
ConvertFrameworksToSummaryDetails(&opaSessionObj.Report.SummaryDetails, opaSessionObj.Frameworks)
|
||||
ConvertFrameworksToSummaryDetails(&opaSessionObj.Report.SummaryDetails, opaSessionObj.Frameworks, policies)
|
||||
|
||||
opaSessionObj.K8SResources = &k8sResources
|
||||
opaSessionObj.AllResources[deployment.GetID()] = deployment
|
||||
|
||||
@@ -201,6 +201,7 @@ func removeSecretData(workload workloadinterface.IWorkload) {
|
||||
func removePodData(workload workloadinterface.IWorkload) {
|
||||
workload.RemoveAnnotation("kubectl.kubernetes.io/last-applied-configuration")
|
||||
workloadinterface.RemoveFromMap(workload.GetObject(), "metadata", "managedFields")
|
||||
workloadinterface.RemoveFromMap(workload.GetObject(), "status")
|
||||
|
||||
containers, err := workload.GetContainers()
|
||||
if err != nil || len(containers) == 0 {
|
||||
|
||||
@@ -14,7 +14,7 @@ func ConvertFrameworksToPolicies(frameworks []reporthandling.Framework, version
|
||||
}
|
||||
|
||||
// ConvertFrameworksToSummaryDetails initialize the summary details for the report object
|
||||
func ConvertFrameworksToSummaryDetails(summaryDetails *reportsummary.SummaryDetails, frameworks []reporthandling.Framework) {
|
||||
func ConvertFrameworksToSummaryDetails(summaryDetails *reportsummary.SummaryDetails, frameworks []reporthandling.Framework, policies *cautils.Policies) {
|
||||
if summaryDetails.Controls == nil {
|
||||
summaryDetails.Controls = make(map[string]reportsummary.ControlSummary)
|
||||
}
|
||||
@@ -22,17 +22,19 @@ func ConvertFrameworksToSummaryDetails(summaryDetails *reportsummary.SummaryDeta
|
||||
controls := map[string]reportsummary.ControlSummary{}
|
||||
for j := range frameworks[i].Controls {
|
||||
id := frameworks[i].Controls[j].ControlID
|
||||
c := reportsummary.ControlSummary{
|
||||
Name: frameworks[i].Controls[j].Name,
|
||||
ControlID: id,
|
||||
ScoreFactor: frameworks[i].Controls[j].BaseScore,
|
||||
Description: frameworks[i].Controls[j].Description,
|
||||
Remediation: frameworks[i].Controls[j].Remediation,
|
||||
if _, ok := policies.Controls[id]; ok {
|
||||
c := reportsummary.ControlSummary{
|
||||
Name: frameworks[i].Controls[j].Name,
|
||||
ControlID: id,
|
||||
ScoreFactor: frameworks[i].Controls[j].BaseScore,
|
||||
Description: frameworks[i].Controls[j].Description,
|
||||
Remediation: frameworks[i].Controls[j].Remediation,
|
||||
}
|
||||
controls[frameworks[i].Controls[j].ControlID] = c
|
||||
summaryDetails.Controls[id] = c
|
||||
}
|
||||
controls[frameworks[i].Controls[j].ControlID] = c
|
||||
summaryDetails.Controls[id] = c
|
||||
}
|
||||
if frameworks[i].Name != "" {
|
||||
if cautils.StringInSlice(policies.Frameworks, frameworks[i].Name) != cautils.ValueNotFound {
|
||||
summaryDetails.Frameworks = append(summaryDetails.Frameworks, reportsummary.FrameworkSummary{
|
||||
Name: frameworks[i].Name,
|
||||
Controls: controls,
|
||||
|
||||
@@ -23,7 +23,8 @@ func TestInitializeSummaryDetails(t *testing.T) {
|
||||
|
||||
summaryDetails := reportsummary.SummaryDetails{}
|
||||
frameworks := []reporthandling.Framework{*fw0, *fw1}
|
||||
ConvertFrameworksToSummaryDetails(&summaryDetails, frameworks)
|
||||
policies := ConvertFrameworksToPolicies([]reporthandling.Framework{*fw0, *fw1}, "")
|
||||
ConvertFrameworksToSummaryDetails(&summaryDetails, frameworks, policies)
|
||||
assert.Equal(t, 2, len(summaryDetails.Frameworks))
|
||||
assert.Equal(t, 3, len(summaryDetails.Controls))
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package policyhandler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
@@ -15,7 +16,7 @@ func (policyHandler *PolicyHandler) getPolicies(notification *reporthandling.Pol
|
||||
return err
|
||||
}
|
||||
if len(frameworks) == 0 {
|
||||
return fmt.Errorf("failed to download policies, please ARMO team for more information")
|
||||
return fmt.Errorf("failed to download policies: '%s'. Make sure the policy exist and you spelled it correctly. For more information, please feel free to contact ARMO team", strings.Join(policyIdentifierToSlice(notification.Rules), ","))
|
||||
}
|
||||
|
||||
policiesAndResources.Frameworks = frameworks
|
||||
@@ -70,3 +71,11 @@ func (policyHandler *PolicyHandler) getScanPolicies(notification *reporthandling
|
||||
}
|
||||
return frameworks, nil
|
||||
}
|
||||
|
||||
func policyIdentifierToSlice(rules []reporthandling.PolicyIdentifier) []string {
|
||||
s := []string{}
|
||||
for i := range rules {
|
||||
s = append(s, fmt.Sprintf("%s: %s", rules[i].Kind, rules[i].Name))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -20,9 +20,6 @@ type IPrinter interface {
|
||||
ActionPrint(opaSessionObj *cautils.OPASessionObj)
|
||||
SetWriter(outputFile string)
|
||||
Score(score float32)
|
||||
|
||||
// FinalizeData convert 'opaSessionObj' data to be ready for printing/reporting
|
||||
FinalizeData(opaSessionObj *cautils.OPASessionObj)
|
||||
}
|
||||
|
||||
func GetWriter(outputFile string) *os.File {
|
||||
|
||||
@@ -26,6 +26,8 @@ func (jsonPrinter *JsonPrinter) Score(score float32) {
|
||||
}
|
||||
|
||||
func (jsonPrinter *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
|
||||
cautils.ReportV2ToV1(opaSessionObj)
|
||||
|
||||
var postureReportStr []byte
|
||||
var err error
|
||||
|
||||
@@ -41,6 +43,3 @@ func (jsonPrinter *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj
|
||||
}
|
||||
jsonPrinter.writer.Write(postureReportStr)
|
||||
}
|
||||
func (jsonPrinter *JsonPrinter) FinalizeData(opaSessionObj *cautils.OPASessionObj) {
|
||||
reportV2ToV1(opaSessionObj)
|
||||
}
|
||||
|
||||
@@ -26,11 +26,9 @@ func (junitPrinter *JunitPrinter) Score(score float32) {
|
||||
fmt.Fprintf(os.Stderr, "\nOverall risk-score (0- Excellent, 100- All failed): %d\n", int(score))
|
||||
}
|
||||
|
||||
func (junitPrinter *JunitPrinter) FinalizeData(opaSessionObj *cautils.OPASessionObj) {
|
||||
reportV2ToV1(opaSessionObj)
|
||||
}
|
||||
|
||||
func (junitPrinter *JunitPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
|
||||
cautils.ReportV2ToV1(opaSessionObj)
|
||||
|
||||
junitResult, err := convertPostureReportToJunitResult(opaSessionObj.PostureReport)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to convert posture report object!")
|
||||
|
||||
@@ -31,6 +31,8 @@ func NewPrettyPrinter(verboseMode bool) *PrettyPrinter {
|
||||
}
|
||||
|
||||
func (prettyPrinter *PrettyPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
|
||||
cautils.ReportV2ToV1(opaSessionObj)
|
||||
|
||||
// score := calculatePostureScore(opaSessionObj.PostureReport)
|
||||
failedResources := []string{}
|
||||
warningResources := []string{}
|
||||
@@ -67,18 +69,15 @@ func (prettyPrinter *PrettyPrinter) SetWriter(outputFile string) {
|
||||
prettyPrinter.writer = printer.GetWriter(outputFile)
|
||||
}
|
||||
|
||||
func (prettyPrinter *PrettyPrinter) FinalizeData(opaSessionObj *cautils.OPASessionObj) {
|
||||
reportV2ToV1(opaSessionObj)
|
||||
}
|
||||
func (prettyPrinter *PrettyPrinter) Score(score float32) {
|
||||
}
|
||||
|
||||
func (prettyPrinter *PrettyPrinter) summarySetup(fr reporthandling.FrameworkReport, allResources map[string]workloadinterface.IMetadata) {
|
||||
|
||||
for _, cr := range fr.ControlReports {
|
||||
if len(cr.RuleReports) == 0 {
|
||||
continue
|
||||
}
|
||||
// if len(cr.RuleReports) == 0 {
|
||||
// continue
|
||||
// }
|
||||
workloadsSummary := listResultSummary(cr.RuleReports, allResources)
|
||||
|
||||
var passedWorkloads map[string][]WorkloadSummary
|
||||
@@ -248,7 +247,9 @@ func (prettyPrinter *PrettyPrinter) printSummaryTable(frameworksNames []string,
|
||||
|
||||
func (prettyPrinter *PrettyPrinter) printFramework(frameworksNames []string, frameworkScores []float32) {
|
||||
if len(frameworksNames) == 1 {
|
||||
cautils.InfoTextDisplay(prettyPrinter.writer, fmt.Sprintf("FRAMEWORK %s\n", frameworksNames[0]))
|
||||
if frameworksNames[0] != "" {
|
||||
cautils.InfoTextDisplay(prettyPrinter.writer, fmt.Sprintf("FRAMEWORK %s\n", frameworksNames[0]))
|
||||
}
|
||||
} else if len(frameworksNames) > 1 {
|
||||
p := "FRAMEWORKS: "
|
||||
for i := 0; i < len(frameworksNames)-1; i++ {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package v1
|
||||
|
||||
import "github.com/armosec/kubescape/resultshandling/printer"
|
||||
import (
|
||||
"github.com/armosec/kubescape/resultshandling/printer"
|
||||
"github.com/armosec/kubescape/resultshandling/printer/v2/controlmapping"
|
||||
)
|
||||
|
||||
var INDENT = " "
|
||||
|
||||
@@ -13,6 +16,6 @@ func GetPrinter(printFormat string, verboseMode bool) printer.IPrinter {
|
||||
case printer.PrometheusFormat:
|
||||
return NewPrometheusPrinter(verboseMode)
|
||||
default:
|
||||
return NewPrettyPrinter(verboseMode)
|
||||
return controlmapping.NewPrettyPrinter(verboseMode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,10 +29,6 @@ func (prometheusPrinter *PrometheusPrinter) Score(score float32) {
|
||||
fmt.Printf("\n# Overall risk-score (0- Excellent, 100- All failed)\nkubescape_score %d\n", int(score))
|
||||
}
|
||||
|
||||
func (prometheusPrinter *PrometheusPrinter) FinalizeData(opaSessionObj *cautils.OPASessionObj) {
|
||||
reportV2ToV1(opaSessionObj)
|
||||
}
|
||||
|
||||
func (printer *PrometheusPrinter) printResources(allResources map[string]workloadinterface.IMetadata, resourcesIDs *reporthandling.ResourcesIDs, frameworkName, controlName string) {
|
||||
printer.printDetails(allResources, resourcesIDs.GetFailedResources(), frameworkName, controlName, "failed")
|
||||
printer.printDetails(allResources, resourcesIDs.GetWarningResources(), frameworkName, controlName, "excluded")
|
||||
@@ -90,6 +86,8 @@ func (printer *PrometheusPrinter) printReports(allResources map[string]workloadi
|
||||
}
|
||||
|
||||
func (printer *PrometheusPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
|
||||
cautils.ReportV2ToV1(opaSessionObj)
|
||||
|
||||
err := printer.printReports(opaSessionObj.AllResources, opaSessionObj.PostureReport.FrameworkReports)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
|
||||
244
resultshandling/printer/v2/controlmapping/prettyprinter.go
Normal file
244
resultshandling/printer/v2/controlmapping/prettyprinter.go
Normal file
@@ -0,0 +1,244 @@
|
||||
package controlmapping
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/k8s-interface/workloadinterface"
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/kubescape/resultshandling/printer"
|
||||
"github.com/armosec/opa-utils/objectsenvelopes"
|
||||
"github.com/armosec/opa-utils/reporthandling/apis"
|
||||
"github.com/armosec/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
"github.com/enescakir/emoji"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
type PrettyPrinter struct {
|
||||
writer *os.File
|
||||
verboseMode bool
|
||||
sortedControlNames []string
|
||||
}
|
||||
|
||||
func NewPrettyPrinter(verboseMode bool) *PrettyPrinter {
|
||||
return &PrettyPrinter{
|
||||
verboseMode: verboseMode,
|
||||
}
|
||||
}
|
||||
|
||||
func (prettyPrinter *PrettyPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
|
||||
prettyPrinter.sortedControlNames = getSortedControlsNames(opaSessionObj.Report.SummaryDetails.Controls) // ListControls().All())
|
||||
|
||||
prettyPrinter.printResults(&opaSessionObj.Report.SummaryDetails.Controls, opaSessionObj.AllResources)
|
||||
prettyPrinter.printSummaryTable(&opaSessionObj.Report.SummaryDetails)
|
||||
|
||||
}
|
||||
|
||||
func (prettyPrinter *PrettyPrinter) SetWriter(outputFile string) {
|
||||
prettyPrinter.writer = printer.GetWriter(outputFile)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
if controlSummary.GetStatus().IsSkipped() {
|
||||
prettyPrinter.printSummary(prettyPrinter.sortedControlNames[i], controlSummary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (prettyPrinter *PrettyPrinter) printSummary(controlName string, controlSummary reportsummary.IControlSummary) {
|
||||
if controlSummary.GetStatus().IsSkipped() {
|
||||
return
|
||||
}
|
||||
cautils.SimpleDisplay(prettyPrinter.writer, "Summary - ")
|
||||
cautils.SuccessDisplay(prettyPrinter.writer, "Passed:%v ", controlSummary.NumberOfResources().Passed())
|
||||
cautils.WarningDisplay(prettyPrinter.writer, "Excluded:%v ", controlSummary.NumberOfResources().Excluded())
|
||||
cautils.FailureDisplay(prettyPrinter.writer, "Failed:%v ", controlSummary.NumberOfResources().Failed())
|
||||
cautils.InfoDisplay(prettyPrinter.writer, "Total:%v\n", controlSummary.NumberOfResources().All())
|
||||
if controlSummary.GetStatus().IsFailed() {
|
||||
cautils.DescriptionDisplay(prettyPrinter.writer, "Remediation: %v\n", controlSummary.GetRemediation())
|
||||
}
|
||||
cautils.DescriptionDisplay(prettyPrinter.writer, "\n")
|
||||
|
||||
}
|
||||
func (prettyPrinter *PrettyPrinter) printTitle(controlSummary reportsummary.IControlSummary) {
|
||||
cautils.InfoDisplay(prettyPrinter.writer, "[control: %s - %s] ", controlSummary.GetName(), getControlURL(controlSummary.GetID()))
|
||||
switch controlSummary.GetStatus().Status() {
|
||||
case apis.StatusSkipped:
|
||||
cautils.InfoDisplay(prettyPrinter.writer, "skipped %v\n", emoji.ConfusedFace)
|
||||
case apis.StatusFailed:
|
||||
cautils.FailureDisplay(prettyPrinter.writer, "failed %v\n", emoji.SadButRelievedFace)
|
||||
case apis.StatusExcluded:
|
||||
cautils.WarningDisplay(prettyPrinter.writer, "excluded %v\n", emoji.NeutralFace)
|
||||
default:
|
||||
cautils.SuccessDisplay(prettyPrinter.writer, "passed %v\n", emoji.ThumbsUp)
|
||||
}
|
||||
cautils.DescriptionDisplay(prettyPrinter.writer, "Description: %s\n", controlSummary.GetDescription())
|
||||
}
|
||||
func (prettyPrinter *PrettyPrinter) printResources(controlSummary reportsummary.IControlSummary, allResources map[string]workloadinterface.IMetadata) {
|
||||
|
||||
workloadsSummary := listResultSummary(controlSummary, allResources)
|
||||
|
||||
failedWorkloads := groupByNamespaceOrKind(workloadsSummary, workloadSummaryFailed)
|
||||
excludedWorkloads := groupByNamespaceOrKind(workloadsSummary, workloadSummaryExclude)
|
||||
|
||||
var passedWorkloads map[string][]WorkloadSummary
|
||||
if prettyPrinter.verboseMode {
|
||||
passedWorkloads = groupByNamespaceOrKind(workloadsSummary, workloadSummaryPassed)
|
||||
}
|
||||
if len(failedWorkloads) > 0 {
|
||||
cautils.FailureDisplay(prettyPrinter.writer, "Failed:\n")
|
||||
prettyPrinter.printGroupedResources(failedWorkloads)
|
||||
}
|
||||
if len(excludedWorkloads) > 0 {
|
||||
cautils.WarningDisplay(prettyPrinter.writer, "Excluded:\n")
|
||||
prettyPrinter.printGroupedResources(excludedWorkloads)
|
||||
}
|
||||
if len(passedWorkloads) > 0 {
|
||||
cautils.SuccessDisplay(prettyPrinter.writer, "Passed:\n")
|
||||
prettyPrinter.printGroupedResources(passedWorkloads)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (prettyPrinter *PrettyPrinter) printGroupedResources(workloads map[string][]WorkloadSummary) {
|
||||
indent := " "
|
||||
for title, rsc := range workloads {
|
||||
prettyPrinter.printGroupedResource(indent, title, rsc)
|
||||
}
|
||||
}
|
||||
|
||||
func (prettyPrinter *PrettyPrinter) printGroupedResource(indent string, title string, rsc []WorkloadSummary) {
|
||||
preIndent := indent
|
||||
if title != "" {
|
||||
cautils.SimpleDisplay(prettyPrinter.writer, "%s%s\n", indent, title)
|
||||
indent += indent
|
||||
}
|
||||
|
||||
resources := []string{}
|
||||
for r := range rsc {
|
||||
relatedObjectsStr := generateRelatedObjectsStr(rsc[r]) // TODO -
|
||||
resources = append(resources, fmt.Sprintf("%s%s - %s %s", indent, rsc[r].resource.GetKind(), rsc[r].resource.GetName(), relatedObjectsStr))
|
||||
}
|
||||
|
||||
sort.Strings(resources)
|
||||
for i := range resources {
|
||||
cautils.SimpleDisplay(prettyPrinter.writer, resources[i]+"\n")
|
||||
}
|
||||
|
||||
indent = preIndent
|
||||
}
|
||||
|
||||
func generateRelatedObjectsStr(workload WorkloadSummary) string {
|
||||
relatedStr := ""
|
||||
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)
|
||||
}
|
||||
relatedStr += fmt.Sprintf("%s - %s, ", related.GetKind(), related.GetName())
|
||||
}
|
||||
}
|
||||
if relatedStr != "" {
|
||||
relatedStr = fmt.Sprintf(" [%s]", relatedStr[:len(relatedStr)-2])
|
||||
}
|
||||
return relatedStr
|
||||
}
|
||||
|
||||
func generateRow(controlSummary reportsummary.IControlSummary) []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()))
|
||||
|
||||
if !controlSummary.GetStatus().IsSkipped() {
|
||||
row = append(row, fmt.Sprintf("%d", int(controlSummary.GetScore()))+"%")
|
||||
} else {
|
||||
row = append(row, "skipped")
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
func generateHeader() []string {
|
||||
return []string{"Control Name", "Failed Resources", "Excluded Resources", "All Resources", "% risk-score"}
|
||||
}
|
||||
|
||||
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, "%"))
|
||||
|
||||
return row
|
||||
}
|
||||
func (prettyPrinter *PrettyPrinter) printSummaryTable(summaryDetails *reportsummary.SummaryDetails) {
|
||||
// For control scan framework will be nil
|
||||
prettyPrinter.printFramework(summaryDetails.ListFrameworks().All())
|
||||
|
||||
summaryTable := tablewriter.NewWriter(prettyPrinter.writer)
|
||||
summaryTable.SetAutoWrapText(false)
|
||||
summaryTable.SetHeader(generateHeader())
|
||||
summaryTable.SetHeaderLine(true)
|
||||
alignments := []int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER, tablewriter.ALIGN_CENTER}
|
||||
summaryTable.SetColumnAlignment(alignments)
|
||||
|
||||
for i := 0; i < len(prettyPrinter.sortedControlNames); i++ {
|
||||
summaryTable.Append(generateRow(summaryDetails.Controls.GetControl(reportsummary.EControlCriteriaName, prettyPrinter.sortedControlNames[i])))
|
||||
}
|
||||
|
||||
summaryTable.SetFooter(generateFooter(summaryDetails))
|
||||
|
||||
// summaryTable.SetFooter(generateFooter())
|
||||
summaryTable.Render()
|
||||
}
|
||||
|
||||
func (prettyPrinter *PrettyPrinter) printFramework(frameworks []reportsummary.IPolicies) {
|
||||
if len(frameworks) == 1 {
|
||||
if frameworks[0].GetName() != "" {
|
||||
cautils.InfoTextDisplay(prettyPrinter.writer, fmt.Sprintf("FRAMEWORK %s\n", frameworks[0].GetName()))
|
||||
}
|
||||
} else if len(frameworks) > 1 {
|
||||
p := "FRAMEWORKS: "
|
||||
i := 0
|
||||
for ; i < len(frameworks)-1; i++ {
|
||||
p += fmt.Sprintf("%s (risk: %.2f), ", frameworks[i].GetName(), frameworks[i].GetScore())
|
||||
}
|
||||
p += fmt.Sprintf("%s (risk: %.2f)\n", frameworks[i].GetName(), frameworks[i].GetScore())
|
||||
cautils.InfoTextDisplay(prettyPrinter.writer, p)
|
||||
}
|
||||
}
|
||||
func getSortedControlsNames(controls reportsummary.ControlSummaries) []string {
|
||||
controlNames := make([]string, 0, len(controls))
|
||||
for k := range controls {
|
||||
c := controls[k]
|
||||
controlNames = append(controlNames, c.GetName())
|
||||
}
|
||||
sort.Strings(controlNames)
|
||||
return controlNames
|
||||
}
|
||||
|
||||
// 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 getControlURL(controlID string) string {
|
||||
return fmt.Sprintf("https://hub.armo.cloud/docs/%s", strings.ToLower(controlID))
|
||||
}
|
||||
101
resultshandling/printer/v2/controlmapping/summeryhelpers.go
Normal file
101
resultshandling/printer/v2/controlmapping/summeryhelpers.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package controlmapping
|
||||
|
||||
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/apis"
|
||||
"github.com/armosec/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
)
|
||||
|
||||
type WorkloadSummary struct {
|
||||
resource workloadinterface.IMetadata
|
||||
status apis.ScanningStatus
|
||||
}
|
||||
|
||||
func workloadSummaryFailed(workloadSummary *WorkloadSummary) bool {
|
||||
return workloadSummary.status == apis.StatusFailed
|
||||
}
|
||||
|
||||
func workloadSummaryExclude(workloadSummary *WorkloadSummary) bool {
|
||||
return workloadSummary.status == apis.StatusExcluded
|
||||
}
|
||||
|
||||
func workloadSummaryPassed(workloadSummary *WorkloadSummary) bool {
|
||||
return workloadSummary.status == apis.StatusPassed
|
||||
}
|
||||
|
||||
// Group workloads by namespace - return {"namespace": <[]WorkloadSummary>}
|
||||
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]) {
|
||||
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[ns] = r
|
||||
} else {
|
||||
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]}
|
||||
}
|
||||
}
|
||||
}
|
||||
return mapResources
|
||||
}
|
||||
|
||||
func isKindToBeGrouped(kind string) bool {
|
||||
if kind == "Group" || kind == "User" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func listResultSummary(controlSummary reportsummary.IControlSummary, allResources map[string]workloadinterface.IMetadata) []WorkloadSummary {
|
||||
workloadsSummary := []WorkloadSummary{}
|
||||
|
||||
workloadsSummary = append(workloadsSummary, newListWorkloadsSummary(allResources, controlSummary.ListResourcesIDs().Failed(), apis.StatusFailed)...)
|
||||
workloadsSummary = append(workloadsSummary, newListWorkloadsSummary(allResources, controlSummary.ListResourcesIDs().Excluded(), apis.StatusExcluded)...)
|
||||
workloadsSummary = append(workloadsSummary, newListWorkloadsSummary(allResources, controlSummary.ListResourcesIDs().Passed(), apis.StatusPassed)...)
|
||||
|
||||
return workloadsSummary
|
||||
}
|
||||
|
||||
func newListWorkloadsSummary(allResources map[string]workloadinterface.IMetadata, resourcesIDs []string, status apis.ScanningStatus) []WorkloadSummary {
|
||||
workloadsSummary := []WorkloadSummary{}
|
||||
|
||||
for _, i := range resourcesIDs {
|
||||
if r, ok := allResources[i]; ok {
|
||||
workloadsSummary = append(workloadsSummary, WorkloadSummary{
|
||||
resource: r,
|
||||
status: status,
|
||||
})
|
||||
}
|
||||
}
|
||||
return workloadsSummary
|
||||
}
|
||||
@@ -1,18 +1,21 @@
|
||||
package v2
|
||||
|
||||
import "github.com/armosec/kubescape/resultshandling/printer"
|
||||
import (
|
||||
"github.com/armosec/kubescape/resultshandling/printer"
|
||||
"github.com/armosec/kubescape/resultshandling/printer/v2/resourcemapping"
|
||||
)
|
||||
|
||||
var INDENT = " "
|
||||
|
||||
func GetPrinter(printFormat string, verboseMode bool) printer.IPrinter {
|
||||
switch printFormat {
|
||||
case printer.JsonFormat:
|
||||
return NewJsonPrinter()
|
||||
return resourcemapping.NewJsonPrinter()
|
||||
case printer.JunitResultFormat:
|
||||
return NewJunitPrinter()
|
||||
// case printer.PrometheusFormat:
|
||||
// return NewPrometheusPrinter(verboseMode)
|
||||
default:
|
||||
return NewPrettyPrinter(verboseMode)
|
||||
return resourcemapping.NewPrettyPrinter(verboseMode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package v2
|
||||
package resourcemapping
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -37,5 +37,5 @@ func (jsonPrinter *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj
|
||||
}
|
||||
|
||||
func (jsonPrinter *JsonPrinter) FinalizeData(opaSessionObj *cautils.OPASessionObj) {
|
||||
finalizeReport(opaSessionObj)
|
||||
// finalizeReport(opaSessionObj)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package v2
|
||||
package resourcemapping
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -89,7 +89,7 @@ func (prettyPrinter *PrettyPrinter) SetWriter(outputFile string) {
|
||||
}
|
||||
|
||||
func (prettyPrinter *PrettyPrinter) FinalizeData(opaSessionObj *cautils.OPASessionObj) {
|
||||
finalizeReport(opaSessionObj)
|
||||
// finalizeReport(opaSessionObj)
|
||||
}
|
||||
func (prettyPrinter *PrettyPrinter) Score(score float32) {
|
||||
}
|
||||
@@ -3,8 +3,8 @@ package v2
|
||||
import (
|
||||
"github.com/armosec/k8s-interface/workloadinterface"
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"github.com/armosec/opa-utils/reporthandling/results/v1/resourcesresults"
|
||||
reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2"
|
||||
)
|
||||
|
||||
// finalizeV2Report finalize the results objects by copying data from map to lists
|
||||
@@ -16,7 +16,7 @@ func finalizeReport(opaSessionObj *cautils.OPASessionObj) {
|
||||
}
|
||||
|
||||
if len(opaSessionObj.Report.Resources) == 0 {
|
||||
opaSessionObj.Report.Resources = make([]reporthandlingv2.Resource, len(opaSessionObj.AllResources))
|
||||
opaSessionObj.Report.Resources = make([]reporthandling.Resource, len(opaSessionObj.AllResources))
|
||||
finalizeResources(opaSessionObj.Report.Resources, opaSessionObj.AllResources)
|
||||
opaSessionObj.AllResources = nil
|
||||
}
|
||||
@@ -30,10 +30,10 @@ func finalizeResults(results []resourcesresults.Result, resourcesResult map[stri
|
||||
}
|
||||
}
|
||||
|
||||
func finalizeResources(resources []reporthandlingv2.Resource, allResources map[string]workloadinterface.IMetadata) {
|
||||
func finalizeResources(resources []reporthandling.Resource, allResources map[string]workloadinterface.IMetadata) {
|
||||
index := 0
|
||||
for resourceID := range allResources {
|
||||
resources[index] = reporthandlingv2.Resource{
|
||||
resources[index] = reporthandling.Resource{
|
||||
ResourceID: resourceID,
|
||||
Object: allResources[resourceID],
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/k8s-interface/workloadinterface"
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
@@ -23,6 +24,7 @@ type ReportEventReceiver struct {
|
||||
eventReceiverURL *url.URL
|
||||
token string
|
||||
customerAdminEMail string
|
||||
message string
|
||||
}
|
||||
|
||||
func NewReportEventReceiver(tenantConfig *cautils.ConfigObj) *ReportEventReceiver {
|
||||
@@ -36,16 +38,24 @@ func NewReportEventReceiver(tenantConfig *cautils.ConfigObj) *ReportEventReceive
|
||||
}
|
||||
|
||||
func (report *ReportEventReceiver) ActionSendReport(opaSessionObj *cautils.OPASessionObj) error {
|
||||
cautils.ReportV2ToV1(opaSessionObj)
|
||||
|
||||
if report.customerGUID == "" || report.clusterName == "" {
|
||||
return fmt.Errorf("missing account ID or cluster name. AccountID: '%s', Cluster name: '%s'", report.customerGUID, report.clusterName)
|
||||
if report.customerGUID == "" {
|
||||
report.message = "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 report.clusterName == "" {
|
||||
report.message = "WARNING: Failed to publish results. Reason: Unknown cluster name. Run kubescape with the '--cluster <cluster name>' flag"
|
||||
return nil
|
||||
}
|
||||
|
||||
opaSessionObj.PostureReport.ReportID = uuid.NewV4().String()
|
||||
opaSessionObj.PostureReport.CustomerGUID = report.clusterName
|
||||
opaSessionObj.PostureReport.ClusterName = report.customerGUID
|
||||
opaSessionObj.PostureReport.CustomerGUID = report.customerGUID
|
||||
opaSessionObj.PostureReport.ClusterName = report.clusterName
|
||||
|
||||
if err := report.prepareReport(opaSessionObj.PostureReport, opaSessionObj.AllResources); err != nil {
|
||||
return err
|
||||
report.message = err.Error()
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -74,6 +84,8 @@ func (report *ReportEventReceiver) prepareReport(postureReport *reporthandling.P
|
||||
if err := report.sendResources(host, postureReport, allResources); err != nil {
|
||||
return err
|
||||
}
|
||||
report.generateMessage()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -112,14 +124,16 @@ func (report *ReportEventReceiver) sendReport(host string, postureReport *report
|
||||
if err != nil {
|
||||
return fmt.Errorf("in 'sendReport' failed to json.Marshal, reason: %v", err)
|
||||
}
|
||||
// fmt.Printf("\n\n%s\n\n", reqBody)
|
||||
|
||||
msg, err := getter.HttpPost(report.httpClient, host, nil, reqBody)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s, %v:%s", host, err, msg)
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (report *ReportEventReceiver) DisplayReportURL() {
|
||||
func (report *ReportEventReceiver) generateMessage() {
|
||||
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{}
|
||||
@@ -127,7 +141,7 @@ func (report *ReportEventReceiver) DisplayReportURL() {
|
||||
u.Host = getter.GetArmoAPIConnector().GetFrontendURL()
|
||||
|
||||
if report.customerAdminEMail != "" {
|
||||
cautils.InfoTextDisplay(os.Stderr, fmt.Sprintf("\n\n%s %s/risk/%s\n(Account: %s)\n\n", message, u.String(), report.clusterName, report.customerGUID))
|
||||
report.message = fmt.Sprintf("%s %s/risk/%s\n(Account: %s)", message, u.String(), report.clusterName, maskID(report.customerGUID))
|
||||
return
|
||||
}
|
||||
u.Path = "account/sign-up"
|
||||
@@ -136,5 +150,27 @@ func (report *ReportEventReceiver) DisplayReportURL() {
|
||||
q.Add("customerGUID", report.customerGUID)
|
||||
|
||||
u.RawQuery = q.Encode()
|
||||
cautils.InfoTextDisplay(os.Stderr, fmt.Sprintf("\n\n%s %s\n\n", message, u.String()))
|
||||
report.message = fmt.Sprintf("%s %s", message, u.String())
|
||||
}
|
||||
|
||||
func (report *ReportEventReceiver) DisplayReportURL() {
|
||||
cautils.InfoTextDisplay(os.Stderr, fmt.Sprintf("\n\n%s\n\n", report.message))
|
||||
}
|
||||
|
||||
func maskID(id string) string {
|
||||
sep := "-"
|
||||
splitted := strings.Split(id, sep)
|
||||
if len(splitted) != 5 {
|
||||
return ""
|
||||
}
|
||||
str := splitted[0][:4]
|
||||
splitted[0] = splitted[0][4:]
|
||||
for i := range splitted {
|
||||
for j := 0; j < len(splitted[i]); j++ {
|
||||
str += "X"
|
||||
}
|
||||
str += sep
|
||||
}
|
||||
|
||||
return strings.TrimSuffix(str, sep)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/armosec/kubescape/cautils/getter"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"github.com/armosec/opa-utils/reporthandling/results/v1/resourcesresults"
|
||||
reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2"
|
||||
)
|
||||
@@ -24,6 +25,7 @@ type ReportEventReceiver struct {
|
||||
eventReceiverURL *url.URL
|
||||
token string
|
||||
customerAdminEMail string
|
||||
message string
|
||||
}
|
||||
|
||||
func NewReportEventReceiver(tenantConfig *cautils.ConfigObj) *ReportEventReceiver {
|
||||
@@ -37,16 +39,24 @@ func NewReportEventReceiver(tenantConfig *cautils.ConfigObj) *ReportEventReceive
|
||||
}
|
||||
|
||||
func (report *ReportEventReceiver) ActionSendReport(opaSessionObj *cautils.OPASessionObj) error {
|
||||
finalizeReport(opaSessionObj)
|
||||
|
||||
if report.customerGUID == "" || report.clusterName == "" {
|
||||
return fmt.Errorf("missing accout ID or cluster name. AccountID: '%s', Cluster name: '%s'", report.customerGUID, report.clusterName)
|
||||
if report.customerGUID == "" {
|
||||
report.message = "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 report.clusterName == "" {
|
||||
report.message = "WARNING: Failed to publish results. Reason: Unknown cluster name. Run kubescape with the '--cluster <cluster name>' flag"
|
||||
return nil
|
||||
}
|
||||
opaSessionObj.Report.ReportID = uuid.NewV4().String()
|
||||
opaSessionObj.Report.CustomerGUID = report.clusterName
|
||||
opaSessionObj.Report.ClusterName = report.customerGUID
|
||||
opaSessionObj.Report.CustomerGUID = report.customerGUID
|
||||
opaSessionObj.Report.ClusterName = report.clusterName
|
||||
|
||||
if err := report.prepareReport(opaSessionObj.Report); err != nil {
|
||||
return err
|
||||
report.message = err.Error()
|
||||
} else {
|
||||
report.generateMessage()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -66,24 +76,29 @@ func (report *ReportEventReceiver) prepareReport(postureReport *reporthandlingv2
|
||||
cautils.StartSpinner()
|
||||
defer cautils.StopSpinner()
|
||||
|
||||
// send framework results
|
||||
if err := report.sendReport(host, postureReport); err != nil {
|
||||
reportCounter := 0
|
||||
|
||||
// send results
|
||||
if err := report.sendResults(host, postureReport, &reportCounter); err != nil {
|
||||
return err
|
||||
}
|
||||
reportCounter++
|
||||
|
||||
// send resources
|
||||
if err := report.sendResults(host, postureReport); err != nil {
|
||||
if err := report.sendResources(host, postureReport, &reportCounter); err != nil {
|
||||
return err
|
||||
}
|
||||
// reportCounter++
|
||||
|
||||
// // send framework results
|
||||
// if err := report.sendSummary(host, postureReport, &reportCounter); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// send resources
|
||||
if err := report.sendResources(host, postureReport); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (report *ReportEventReceiver) sendResources(host string, postureReport *reporthandlingv2.PostureReport) error {
|
||||
func (report *ReportEventReceiver) sendResources(host string, postureReport *reporthandlingv2.PostureReport, reportCounter *int) error {
|
||||
splittedPostureReport := setSubReport(postureReport)
|
||||
counter := 0
|
||||
|
||||
@@ -96,12 +111,13 @@ func (report *ReportEventReceiver) sendResources(host string, postureReport *rep
|
||||
if counter+len(r) >= MAX_REPORT_SIZE && len(splittedPostureReport.Resources) > 0 {
|
||||
|
||||
// send report
|
||||
if err := report.sendReport(host, splittedPostureReport); err != nil {
|
||||
if err := report.sendReport(host, splittedPostureReport, *reportCounter, false); err != nil {
|
||||
return err
|
||||
}
|
||||
*reportCounter++
|
||||
|
||||
// delete resources
|
||||
splittedPostureReport.Resources = []reporthandlingv2.Resource{}
|
||||
splittedPostureReport.Resources = []reporthandling.Resource{}
|
||||
|
||||
// restart counter
|
||||
counter = 0
|
||||
@@ -111,10 +127,10 @@ func (report *ReportEventReceiver) sendResources(host string, postureReport *rep
|
||||
splittedPostureReport.Resources = append(splittedPostureReport.Resources, v)
|
||||
}
|
||||
|
||||
return report.sendReport(host, splittedPostureReport)
|
||||
return report.sendReport(host, splittedPostureReport, *reportCounter, true)
|
||||
}
|
||||
|
||||
func (report *ReportEventReceiver) sendResults(host string, postureReport *reporthandlingv2.PostureReport) error {
|
||||
func (report *ReportEventReceiver) sendResults(host string, postureReport *reporthandlingv2.PostureReport, reportCounter *int) error {
|
||||
splittedPostureReport := setSubReport(postureReport)
|
||||
counter := 0
|
||||
|
||||
@@ -127,9 +143,10 @@ func (report *ReportEventReceiver) sendResults(host string, postureReport *repor
|
||||
if counter+len(r) >= MAX_REPORT_SIZE && len(splittedPostureReport.Resources) > 0 {
|
||||
|
||||
// send report
|
||||
if err := report.sendReport(host, splittedPostureReport); err != nil {
|
||||
if err := report.sendReport(host, splittedPostureReport, *reportCounter, false); err != nil {
|
||||
return err
|
||||
}
|
||||
*reportCounter++
|
||||
|
||||
// delete results
|
||||
splittedPostureReport.Results = []resourcesresults.Result{}
|
||||
@@ -142,12 +159,20 @@ func (report *ReportEventReceiver) sendResults(host string, postureReport *repor
|
||||
splittedPostureReport.Results = append(splittedPostureReport.Results, v)
|
||||
}
|
||||
|
||||
return report.sendReport(host, splittedPostureReport)
|
||||
return report.sendReport(host, splittedPostureReport, *reportCounter, false)
|
||||
}
|
||||
func (report *ReportEventReceiver) sendReport(host string, postureReport *reporthandlingv2.PostureReport) error {
|
||||
|
||||
func (report *ReportEventReceiver) sendSummary(host string, postureReport *reporthandlingv2.PostureReport, reportCounter *int) error {
|
||||
splittedPostureReport := setSubReport(postureReport)
|
||||
splittedPostureReport.SummaryDetails = postureReport.SummaryDetails
|
||||
|
||||
return report.sendReport(host, splittedPostureReport, *reportCounter, true)
|
||||
}
|
||||
func (report *ReportEventReceiver) sendReport(host string, postureReport *reporthandlingv2.PostureReport, counter int, isLastReport bool) error {
|
||||
postureReport.PaginationInfo = reporthandlingv2.PaginationMarks{
|
||||
ReportNumber: counter,
|
||||
IsLastReport: isLastReport,
|
||||
}
|
||||
reqBody, err := json.Marshal(postureReport)
|
||||
if err != nil {
|
||||
return fmt.Errorf("in 'sendReport' failed to json.Marshal, reason: %v", err)
|
||||
@@ -159,7 +184,7 @@ func (report *ReportEventReceiver) sendReport(host string, postureReport *report
|
||||
return err
|
||||
}
|
||||
|
||||
func (report *ReportEventReceiver) DisplayReportURL() {
|
||||
func (report *ReportEventReceiver) generateMessage() {
|
||||
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{}
|
||||
@@ -167,7 +192,7 @@ func (report *ReportEventReceiver) DisplayReportURL() {
|
||||
u.Host = getter.GetArmoAPIConnector().GetFrontendURL()
|
||||
|
||||
if report.customerAdminEMail != "" {
|
||||
cautils.InfoTextDisplay(os.Stderr, fmt.Sprintf("\n\n%s %s/risk/%s\n(Account: %s)\n\n", message, u.String(), report.clusterName, report.customerGUID))
|
||||
report.message = fmt.Sprintf("%s %s/risk/%s\n(Account: %s)", message, u.String(), report.clusterName, maskID(report.customerGUID))
|
||||
return
|
||||
}
|
||||
u.Path = "account/sign-up"
|
||||
@@ -176,5 +201,9 @@ func (report *ReportEventReceiver) DisplayReportURL() {
|
||||
q.Add("customerGUID", report.customerGUID)
|
||||
|
||||
u.RawQuery = q.Encode()
|
||||
cautils.InfoTextDisplay(os.Stderr, fmt.Sprintf("\n\n%s %s\n\n", message, u.String()))
|
||||
report.message = fmt.Sprintf("%s %s", message, u.String())
|
||||
}
|
||||
|
||||
func (report *ReportEventReceiver) DisplayReportURL() {
|
||||
cautils.InfoTextDisplay(os.Stderr, fmt.Sprintf("\n\n%s\n\n", report.message))
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ func (report *ReportEventReceiver) initEventReceiverURL() {
|
||||
|
||||
urlObj.Scheme = "https"
|
||||
urlObj.Host = getter.GetArmoAPIConnector().GetReportReceiverURL()
|
||||
urlObj.Path = "/k8s/postureReport"
|
||||
urlObj.Path = "/k8s/v2/postureReport"
|
||||
|
||||
q := urlObj.Query()
|
||||
q.Add("customerGUID", uuid.FromStringOrNil(report.customerGUID).String())
|
||||
q.Add("clusterName", report.clusterName)
|
||||
@@ -27,7 +28,7 @@ func (report *ReportEventReceiver) initEventReceiverURL() {
|
||||
|
||||
func hostToString(host *url.URL, reportID string) string {
|
||||
q := host.Query()
|
||||
q.Add("reportID", reportID) // TODO - do we add the reportID?
|
||||
q.Add("reportGUID", reportID) // TODO - do we add the reportID?
|
||||
host.RawQuery = q.Encode()
|
||||
return host.String()
|
||||
}
|
||||
@@ -38,6 +39,11 @@ func setSubReport(postureReport *reporthandlingv2.PostureReport) *reporthandling
|
||||
ClusterName: postureReport.ClusterName,
|
||||
ReportID: postureReport.ReportID,
|
||||
ReportGenerationTime: postureReport.ReportGenerationTime,
|
||||
SummaryDetails: postureReport.SummaryDetails,
|
||||
Attributes: postureReport.Attributes,
|
||||
ClusterCloudProvider: postureReport.ClusterCloudProvider,
|
||||
JobID: postureReport.JobID,
|
||||
ClusterAPIServerInfo: postureReport.ClusterAPIServerInfo,
|
||||
}
|
||||
}
|
||||
func iMetaToResource(obj workloadinterface.IMetadata) *reporthandling.Resource {
|
||||
|
||||
63
resultshandling/reporter/v2/utils.go
Normal file
63
resultshandling/reporter/v2/utils.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/k8s-interface/workloadinterface"
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"github.com/armosec/opa-utils/reporthandling/results/v1/resourcesresults"
|
||||
)
|
||||
|
||||
// finalizeV2Report finalize the results objects by copying data from map to lists
|
||||
func finalizeReport(opaSessionObj *cautils.OPASessionObj) {
|
||||
opaSessionObj.PostureReport = nil
|
||||
if len(opaSessionObj.Report.Results) == 0 {
|
||||
opaSessionObj.Report.Results = make([]resourcesresults.Result, len(opaSessionObj.ResourcesResult))
|
||||
finalizeResults(opaSessionObj.Report.Results, opaSessionObj.ResourcesResult)
|
||||
opaSessionObj.ResourcesResult = nil
|
||||
}
|
||||
|
||||
if len(opaSessionObj.Report.Resources) == 0 {
|
||||
opaSessionObj.Report.Resources = make([]reporthandling.Resource, len(opaSessionObj.AllResources))
|
||||
finalizeResources(opaSessionObj.Report.Resources, opaSessionObj.AllResources)
|
||||
opaSessionObj.AllResources = nil
|
||||
}
|
||||
|
||||
}
|
||||
func finalizeResults(results []resourcesresults.Result, resourcesResult map[string]resourcesresults.Result) {
|
||||
index := 0
|
||||
for resourceID := range resourcesResult {
|
||||
results[index] = resourcesResult[resourceID]
|
||||
index++
|
||||
}
|
||||
}
|
||||
|
||||
func finalizeResources(resources []reporthandling.Resource, allResources map[string]workloadinterface.IMetadata) {
|
||||
index := 0
|
||||
for resourceID := range allResources {
|
||||
resources[index] = reporthandling.Resource{
|
||||
ResourceID: resourceID,
|
||||
Object: allResources[resourceID],
|
||||
}
|
||||
index++
|
||||
}
|
||||
}
|
||||
|
||||
func maskID(id string) string {
|
||||
sep := "-"
|
||||
splitted := strings.Split(id, sep)
|
||||
if len(splitted) != 5 {
|
||||
return ""
|
||||
}
|
||||
str := splitted[0][:4]
|
||||
splitted[0] = splitted[0][4:]
|
||||
for i := range splitted {
|
||||
for j := 0; j < len(splitted[i]); j++ {
|
||||
str += "X"
|
||||
}
|
||||
str += sep
|
||||
}
|
||||
|
||||
return strings.TrimSuffix(str, sep)
|
||||
}
|
||||
@@ -27,7 +27,6 @@ func (resultsHandler *ResultsHandler) HandleResults(scanInfo *cautils.ScanInfo)
|
||||
|
||||
opaSessionObj := <-*resultsHandler.opaSessionObj
|
||||
|
||||
resultsHandler.printerObj.FinalizeData(opaSessionObj)
|
||||
resultsHandler.printerObj.ActionPrint(opaSessionObj)
|
||||
|
||||
if err := resultsHandler.reporterObj.ActionSendReport(opaSessionObj); err != nil {
|
||||
@@ -36,11 +35,13 @@ func (resultsHandler *ResultsHandler) HandleResults(scanInfo *cautils.ScanInfo)
|
||||
|
||||
// TODO - get score from table
|
||||
var score float32 = 0
|
||||
for i := range opaSessionObj.PostureReport.FrameworkReports {
|
||||
score += opaSessionObj.PostureReport.FrameworkReports[i].Score
|
||||
if opaSessionObj.PostureReport != nil {
|
||||
for i := range opaSessionObj.PostureReport.FrameworkReports {
|
||||
score += opaSessionObj.PostureReport.FrameworkReports[i].Score
|
||||
}
|
||||
score /= float32(len(opaSessionObj.PostureReport.FrameworkReports))
|
||||
resultsHandler.printerObj.Score(score)
|
||||
}
|
||||
score /= float32(len(opaSessionObj.PostureReport.FrameworkReports))
|
||||
resultsHandler.printerObj.Score(score)
|
||||
|
||||
return score
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user