feat: unified k8s scan resources (#4188)

This commit is contained in:
chenk
2023-05-09 16:52:02 +03:00
committed by GitHub
parent f2188eb56d
commit 090a00e717
5 changed files with 87 additions and 104 deletions

View File

@@ -95,10 +95,7 @@ func (r *runner) run(ctx context.Context, artifacts []*artifacts.Artifact) error
if r.flagOpts.Compliance.Spec.ID != "" { if r.flagOpts.Compliance.Spec.ID != "" {
var scanResults []types.Results var scanResults []types.Results
for _, rss := range rpt.Vulnerabilities { for _, rss := range rpt.Resources {
scanResults = append(scanResults, rss.Results)
}
for _, rss := range rpt.Misconfigurations {
scanResults = append(scanResults, rss.Results) scanResults = append(scanResults, rss.Results)
} }
complianceReport, err := cr.BuildComplianceReport(scanResults, r.flagOpts.Compliance) complianceReport, err := cr.BuildComplianceReport(scanResults, r.flagOpts.Compliance)

View File

@@ -40,11 +40,10 @@ type Option struct {
// Report represents a kubernetes scan report // Report represents a kubernetes scan report
type Report struct { type Report struct {
SchemaVersion int `json:",omitempty"` SchemaVersion int `json:",omitempty"`
ClusterName string ClusterName string
Vulnerabilities []Resource `json:",omitempty"` Resources []Resource `json:",omitempty"`
Misconfigurations []Resource `json:",omitempty"` name string
name string
} }
// ConsolidatedReport represents a kubernetes scan report with consolidated findings // ConsolidatedReport represents a kubernetes scan report with consolidated findings
@@ -74,18 +73,11 @@ func (r Resource) fullname() string {
// Failed returns whether the k8s report includes any vulnerabilities or misconfigurations // Failed returns whether the k8s report includes any vulnerabilities or misconfigurations
func (r Report) Failed() bool { func (r Report) Failed() bool {
for _, v := range r.Vulnerabilities { for _, v := range r.Resources {
if v.Results.Failed() { if v.Results.Failed() {
return true return true
} }
} }
for _, m := range r.Misconfigurations {
if m.Results.Failed() {
return true
}
}
return false return false
} }
@@ -96,12 +88,16 @@ func (r Report) consolidate() ConsolidatedReport {
} }
index := make(map[string]Resource) index := make(map[string]Resource)
vulnerabilities := make([]Resource, 0)
for _, m := range r.Misconfigurations { for _, m := range r.Resources {
index[m.fullname()] = m if vulnerabilitiesOrSecretResource(m) {
vulnerabilities = append(vulnerabilities, m)
} else {
index[m.fullname()] = m
}
} }
for _, v := range r.Vulnerabilities { for _, v := range vulnerabilities {
key := v.fullname() key := v.fullname()
if res, ok := index[key]; ok { if res, ok := index[key]; ok {
@@ -181,13 +177,19 @@ func separateMisconfigReports(k8sReport Report, scanners types.Scanners, compone
workloadMisconfig := make([]Resource, 0) workloadMisconfig := make([]Resource, 0)
infraMisconfig := make([]Resource, 0) infraMisconfig := make([]Resource, 0)
rbacAssessment := make([]Resource, 0) rbacAssessment := make([]Resource, 0)
workloadVulnerabilities := make([]Resource, 0)
workloadResource := make([]Resource, 0)
for _, resource := range k8sReport.Resources {
if vulnerabilitiesOrSecretResource(resource) {
workloadVulnerabilities = append(workloadVulnerabilities, resource)
continue
}
for _, misConfig := range k8sReport.Misconfigurations {
switch { switch {
case scanners.Enabled(types.RBACScanner) && rbacResource(misConfig): case scanners.Enabled(types.RBACScanner) && rbacResource(resource):
rbacAssessment = append(rbacAssessment, misConfig) rbacAssessment = append(rbacAssessment, resource)
case infraResource(misConfig): case infraResource(resource):
workload, infra := splitInfraAndWorkloadResources(misConfig) workload, infra := splitInfraAndWorkloadResources(resource)
if slices.Contains(components, infraComponent) { if slices.Contains(components, infraComponent) {
infraMisconfig = append(infraMisconfig, infra) infraMisconfig = append(infraMisconfig, infra)
@@ -197,27 +199,27 @@ func separateMisconfigReports(k8sReport Report, scanners types.Scanners, compone
workloadMisconfig = append(workloadMisconfig, workload) workloadMisconfig = append(workloadMisconfig, workload)
} }
case scanners.Enabled(types.MisconfigScanner) && !rbacResource(misConfig): case scanners.Enabled(types.MisconfigScanner) && !rbacResource(resource):
if slices.Contains(components, workloadComponent) { if slices.Contains(components, workloadComponent) {
workloadMisconfig = append(workloadMisconfig, misConfig) workloadMisconfig = append(workloadMisconfig, resource)
} }
} }
} }
r := make([]reports, 0) r := make([]reports, 0)
workloadResource = append(workloadResource, workloadVulnerabilities...)
workloadResource = append(workloadResource, workloadMisconfig...)
if shouldAddWorkloadReport(scanners) { if shouldAddWorkloadReport(scanners) {
workloadReport := Report{ workloadReport := Report{
SchemaVersion: 0, SchemaVersion: 0,
ClusterName: k8sReport.ClusterName, ClusterName: k8sReport.ClusterName,
Misconfigurations: workloadMisconfig, Resources: workloadResource,
Vulnerabilities: k8sReport.Vulnerabilities, name: "Workload Assessment",
name: "Workload Assessment",
} }
if (slices.Contains(components, workloadComponent) && if (slices.Contains(components, workloadComponent) &&
len(workloadMisconfig) > 0) || len(workloadMisconfig) > 0) ||
len(k8sReport.Vulnerabilities) > 0 { len(workloadVulnerabilities) > 0 {
r = append(r, reports{ r = append(r, reports{
report: workloadReport, report: workloadReport,
columns: WorkloadColumns(), columns: WorkloadColumns(),
@@ -228,10 +230,10 @@ func separateMisconfigReports(k8sReport Report, scanners types.Scanners, compone
if scanners.Enabled(types.RBACScanner) && len(rbacAssessment) > 0 { if scanners.Enabled(types.RBACScanner) && len(rbacAssessment) > 0 {
r = append(r, reports{ r = append(r, reports{
report: Report{ report: Report{
SchemaVersion: 0, SchemaVersion: 0,
ClusterName: k8sReport.ClusterName, ClusterName: k8sReport.ClusterName,
Misconfigurations: rbacAssessment, Resources: rbacAssessment,
name: "RBAC Assessment", name: "RBAC Assessment",
}, },
columns: RoleColumns(), columns: RoleColumns(),
}) })
@@ -243,10 +245,10 @@ func separateMisconfigReports(k8sReport Report, scanners types.Scanners, compone
r = append(r, reports{ r = append(r, reports{
report: Report{ report: Report{
SchemaVersion: 0, SchemaVersion: 0,
ClusterName: k8sReport.ClusterName, ClusterName: k8sReport.ClusterName,
Misconfigurations: infraMisconfig, Resources: infraMisconfig,
name: "Infra Assessment", name: "Infra Assessment",
}, },
columns: InfraColumns(), columns: InfraColumns(),
}) })
@@ -292,15 +294,9 @@ func CreateResource(artifact *artifacts.Artifact, report types.Report, err error
} }
func (r Report) printErrors() { func (r Report) printErrors() {
for _, resource := range r.Vulnerabilities { for _, resource := range r.Resources {
if resource.Error != "" { if resource.Error != "" {
log.Logger.Errorf("Error during vulnerabilities scan: %s", resource.Error) log.Logger.Errorf("Error during vulnerabilities or misconfiguration scan: %s", resource.Error)
}
}
for _, resource := range r.Misconfigurations {
if resource.Error != "" {
log.Logger.Errorf("Error during misconfiguration scan: %s", resource.Error)
} }
} }
} }
@@ -366,3 +362,7 @@ func copyResult(r types.Result, misconfigs []types.DetectedMisconfiguration) typ
func shouldAddWorkloadReport(scanners types.Scanners) bool { func shouldAddWorkloadReport(scanners types.Scanners) bool {
return scanners.AnyEnabled(types.MisconfigScanner, types.VulnerabilityScanner, types.SecretScanner) return scanners.AnyEnabled(types.MisconfigScanner, types.VulnerabilityScanner, types.SecretScanner)
} }
func vulnerabilitiesOrSecretResource(resource Resource) bool {
return len(resource.Results) > 0 && (len(resource.Results[0].Vulnerabilities) > 0 || len(resource.Results[0].Secrets) > 0)
}

View File

@@ -283,11 +283,9 @@ func TestReport_consolidate(t *testing.T) {
{ {
name: "report with both misconfigs and vulnerabilities", name: "report with both misconfigs and vulnerabilities",
report: Report{ report: Report{
Vulnerabilities: []Resource{ Resources: []Resource{
deployOrionWithVulns, deployOrionWithVulns,
cronjobHelloWithVulns, cronjobHelloWithVulns,
},
Misconfigurations: []Resource{
deployOrionWithMisconfigs, deployOrionWithMisconfigs,
podPrometheusWithMisconfigs, podPrometheusWithMisconfigs,
}, },
@@ -301,7 +299,7 @@ func TestReport_consolidate(t *testing.T) {
{ {
name: "report with only misconfigurations", name: "report with only misconfigurations",
report: Report{ report: Report{
Misconfigurations: []Resource{ Resources: []Resource{
deployOrionWithMisconfigs, deployOrionWithMisconfigs,
podPrometheusWithMisconfigs, podPrometheusWithMisconfigs,
}, },
@@ -314,7 +312,7 @@ func TestReport_consolidate(t *testing.T) {
{ {
name: "report with only vulnerabilities", name: "report with only vulnerabilities",
report: Report{ report: Report{
Vulnerabilities: []Resource{ Resources: []Resource{
deployOrionWithVulns, deployOrionWithVulns,
cronjobHelloWithVulns, cronjobHelloWithVulns,
}, },
@@ -382,11 +380,9 @@ func TestResourceFailed(t *testing.T) {
{ {
name: "report with both misconfigs and vulnerabilities", name: "report with both misconfigs and vulnerabilities",
report: Report{ report: Report{
Vulnerabilities: []Resource{ Resources: []Resource{
deployOrionWithVulns, deployOrionWithVulns,
cronjobHelloWithVulns, cronjobHelloWithVulns,
},
Misconfigurations: []Resource{
deployOrionWithMisconfigs, deployOrionWithMisconfigs,
podPrometheusWithMisconfigs, podPrometheusWithMisconfigs,
}, },
@@ -396,7 +392,7 @@ func TestResourceFailed(t *testing.T) {
{ {
name: "report with only misconfigurations", name: "report with only misconfigurations",
report: Report{ report: Report{
Misconfigurations: []Resource{ Resources: []Resource{
deployOrionWithMisconfigs, deployOrionWithMisconfigs,
podPrometheusWithMisconfigs, podPrometheusWithMisconfigs,
}, },
@@ -406,7 +402,7 @@ func TestResourceFailed(t *testing.T) {
{ {
name: "report with only vulnerabilities", name: "report with only vulnerabilities",
report: Report{ report: Report{
Vulnerabilities: []Resource{ Resources: []Resource{
deployOrionWithVulns, deployOrionWithVulns,
cronjobHelloWithVulns, cronjobHelloWithVulns,
}, },
@@ -464,7 +460,7 @@ func Test_rbacResource(t *testing.T) {
func Test_separateMisconfigReports(t *testing.T) { func Test_separateMisconfigReports(t *testing.T) {
k8sReport := Report{ k8sReport := Report{
Misconfigurations: []Resource{ Resources: []Resource{
{Kind: "Role"}, {Kind: "Role"},
{Kind: "Deployment"}, {Kind: "Deployment"},
{Kind: "StatefulSet"}, {Kind: "StatefulSet"},
@@ -500,14 +496,14 @@ func Test_separateMisconfigReports(t *testing.T) {
expectedReports: []Report{ expectedReports: []Report{
// the order matter for the test // the order matter for the test
{ {
Misconfigurations: []Resource{ Resources: []Resource{
{Kind: "Deployment"}, {Kind: "Deployment"},
{Kind: "StatefulSet"}, {Kind: "StatefulSet"},
{Kind: "Pod"}, {Kind: "Pod"},
}, },
}, },
{Misconfigurations: []Resource{{Kind: "Role"}}}, {Resources: []Resource{{Kind: "Role"}}},
{Misconfigurations: []Resource{{Kind: "Pod"}}}, {Resources: []Resource{{Kind: "Pod"}}},
}, },
}, },
{ {
@@ -521,13 +517,13 @@ func Test_separateMisconfigReports(t *testing.T) {
expectedReports: []Report{ expectedReports: []Report{
// the order matter for the test // the order matter for the test
{ {
Misconfigurations: []Resource{ Resources: []Resource{
{Kind: "Deployment"}, {Kind: "Deployment"},
{Kind: "StatefulSet"}, {Kind: "StatefulSet"},
{Kind: "Pod"}, {Kind: "Pod"},
}, },
}, },
{Misconfigurations: []Resource{{Kind: "Pod"}}}, {Resources: []Resource{{Kind: "Pod"}}},
}, },
}, },
{ {
@@ -535,7 +531,7 @@ func Test_separateMisconfigReports(t *testing.T) {
k8sReport: k8sReport, k8sReport: k8sReport,
scanners: types.Scanners{types.RBACScanner}, scanners: types.Scanners{types.RBACScanner},
expectedReports: []Report{ expectedReports: []Report{
{Misconfigurations: []Resource{{Kind: "Role"}}}, {Resources: []Resource{{Kind: "Role"}}},
}, },
}, },
{ {
@@ -545,7 +541,7 @@ func Test_separateMisconfigReports(t *testing.T) {
components: []string{workloadComponent}, components: []string{workloadComponent},
expectedReports: []Report{ expectedReports: []Report{
{ {
Misconfigurations: []Resource{ Resources: []Resource{
{Kind: "Deployment"}, {Kind: "Deployment"},
{Kind: "StatefulSet"}, {Kind: "StatefulSet"},
{Kind: "Pod"}, {Kind: "Pod"},
@@ -559,7 +555,7 @@ func Test_separateMisconfigReports(t *testing.T) {
scanners: types.Scanners{types.MisconfigScanner}, scanners: types.Scanners{types.MisconfigScanner},
components: []string{infraComponent}, components: []string{infraComponent},
expectedReports: []Report{ expectedReports: []Report{
{Misconfigurations: []Resource{{Kind: "Pod"}}}, {Resources: []Resource{{Kind: "Pod"}}},
}, },
}, },
@@ -572,9 +568,9 @@ func Test_separateMisconfigReports(t *testing.T) {
assert.Equal(t, len(tt.expectedReports), len(reports)) assert.Equal(t, len(tt.expectedReports), len(reports))
for i := range reports { for i := range reports {
assert.Equal(t, len(tt.expectedReports[i].Misconfigurations), len(reports[i].report.Misconfigurations)) assert.Equal(t, len(tt.expectedReports[i].Resources), len(reports[i].report.Resources))
for j, m := range tt.expectedReports[i].Misconfigurations { for j, m := range tt.expectedReports[i].Resources {
assert.Equal(t, m.Kind, reports[i].report.Misconfigurations[j].Kind) assert.Equal(t, m.Kind, reports[i].report.Resources[j].Kind)
} }
} }
}) })
@@ -602,8 +598,8 @@ func TestReportWrite_Summary(t *testing.T) {
{ {
name: "Only config, all serverities", name: "Only config, all serverities",
report: Report{ report: Report{
ClusterName: "test", ClusterName: "test",
Misconfigurations: []Resource{deployOrionWithMisconfigs}, Resources: []Resource{deployOrionWithMisconfigs},
}, },
scanners: types.Scanners{types.MisconfigScanner}, scanners: types.Scanners{types.MisconfigScanner},
components: []string{workloadComponent}, components: []string{workloadComponent},
@@ -624,8 +620,8 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
{ {
name: "Only vuln, all serverities", name: "Only vuln, all serverities",
report: Report{ report: Report{
ClusterName: "test", ClusterName: "test",
Vulnerabilities: []Resource{deployOrionWithVulns}, Resources: []Resource{deployOrionWithVulns},
}, },
scanners: types.Scanners{types.VulnerabilityScanner}, scanners: types.Scanners{types.VulnerabilityScanner},
severities: allSeverities, severities: allSeverities,
@@ -645,8 +641,8 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
{ {
name: "Only rbac, all serverities", name: "Only rbac, all serverities",
report: Report{ report: Report{
ClusterName: "test", ClusterName: "test",
Misconfigurations: []Resource{roleWithMisconfig}, Resources: []Resource{roleWithMisconfig},
}, },
scanners: types.Scanners{types.RBACScanner}, scanners: types.Scanners{types.RBACScanner},
severities: allSeverities, severities: allSeverities,
@@ -666,8 +662,8 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
{ {
name: "Only secret, all serverities", name: "Only secret, all serverities",
report: Report{ report: Report{
ClusterName: "test", ClusterName: "test",
Vulnerabilities: []Resource{deployLuaWithSecrets}, Resources: []Resource{deployLuaWithSecrets},
}, },
scanners: types.Scanners{types.SecretScanner}, scanners: types.Scanners{types.SecretScanner},
severities: allSeverities, severities: allSeverities,
@@ -687,8 +683,8 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
{ {
name: "apiserver, only infra and serverities", name: "apiserver, only infra and serverities",
report: Report{ report: Report{
ClusterName: "test", ClusterName: "test",
Misconfigurations: []Resource{apiseverPodWithMisconfigAndInfra}, Resources: []Resource{apiseverPodWithMisconfigAndInfra},
}, },
scanners: types.Scanners{types.MisconfigScanner}, scanners: types.Scanners{types.MisconfigScanner},
components: []string{infraComponent}, components: []string{infraComponent},
@@ -709,8 +705,8 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
{ {
name: "apiserver, vuln,config,secret and serverities", name: "apiserver, vuln,config,secret and serverities",
report: Report{ report: Report{
ClusterName: "test", ClusterName: "test",
Misconfigurations: []Resource{apiseverPodWithMisconfigAndInfra}, Resources: []Resource{apiseverPodWithMisconfigAndInfra},
}, },
scanners: types.Scanners{ scanners: types.Scanners{
types.VulnerabilityScanner, types.VulnerabilityScanner,
@@ -735,8 +731,8 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`,
{ {
name: "apiserver, all scanners and serverities", name: "apiserver, all scanners and serverities",
report: Report{ report: Report{
ClusterName: "test", ClusterName: "test",
Misconfigurations: []Resource{apiseverPodWithMisconfigAndInfra}, Resources: []Resource{apiseverPodWithMisconfigAndInfra},
}, },
scanners: types.Scanners{ scanners: types.Scanners{
types.MisconfigScanner, types.MisconfigScanner,

View File

@@ -43,15 +43,7 @@ func (tw TableWriter) Write(report Report) error {
switch tw.Report { switch tw.Report {
case allReport: case allReport:
t := pkgReport.Writer{Output: tw.Output, Severities: tw.Severities, ShowMessageOnce: &sync.Once{}} t := pkgReport.Writer{Output: tw.Output, Severities: tw.Severities, ShowMessageOnce: &sync.Once{}}
for _, r := range report.Vulnerabilities { for _, r := range report.Resources {
if r.Report.Results.Failed() {
err := t.Write(r.Report)
if err != nil {
return err
}
}
}
for _, r := range report.Misconfigurations {
if r.Report.Results.Failed() { if r.Report.Results.Failed() {
err := t.Write(r.Report) err := t.Write(r.Report)
if err != nil { if err != nil {

View File

@@ -47,7 +47,7 @@ func (s *Scanner) Scan(ctx context.Context, artifactsData []*artifacts.Artifact)
log.Fatal(xerrors.Errorf("can't enable logger error: %w", err)) log.Fatal(xerrors.Errorf("can't enable logger error: %w", err))
} }
}() }()
var vulns, misconfigs []report.Resource var resources []report.Resource
type scanResult struct { type scanResult struct {
vulns []report.Resource vulns []report.Resource
@@ -74,8 +74,8 @@ func (s *Scanner) Scan(ctx context.Context, artifactsData []*artifacts.Artifact)
} }
onResult := func(result scanResult) error { onResult := func(result scanResult) error {
vulns = append(vulns, result.vulns...) resources = append(resources, result.vulns...)
misconfigs = append(misconfigs, result.misconfig) resources = append(resources, result.misconfig)
return nil return nil
} }
@@ -84,12 +84,10 @@ func (s *Scanner) Scan(ctx context.Context, artifactsData []*artifacts.Artifact)
if err != nil { if err != nil {
return report.Report{}, err return report.Report{}, err
} }
return report.Report{ return report.Report{
SchemaVersion: 0, SchemaVersion: 0,
ClusterName: s.cluster, ClusterName: s.cluster,
Vulnerabilities: vulns, Resources: resources,
Misconfigurations: misconfigs,
}, nil }, nil
} }