mirror of
https://github.com/kubescape/kubescape.git
synced 2026-02-14 18:09:55 +00:00
Merge remote-tracking branch 'upstream/dev'
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
[]()
|
||||
[](https://goreportcard.com/report/github.com/armosec/kubescape)
|
||||
|
||||
Kubescape is the first tool for testing if Kubernetes is deployed securely as defined in [Kubernetes Hardening Guidance by to NSA and CISA](https://www.nsa.gov/News-Features/Feature-Stories/Article-View/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/)
|
||||
Kubescape is the first tool for testing if Kubernetes is deployed securely as defined in [Kubernetes Hardening Guidance by NSA and CISA](https://www.nsa.gov/News-Features/Feature-Stories/Article-View/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/)
|
||||
Tests are configured with YAML files, making this tool easy to update as test specifications evolve.
|
||||
|
||||
<img src="docs/demo.gif">
|
||||
@@ -28,10 +28,12 @@ kubescape scan framework nsa --exclude-namespaces kube-system,kube-public
|
||||
|
||||
If you wish to scan all namespaces in your cluster, remove the `--exclude-namespaces` flag.
|
||||
|
||||
<img src="docs/summery.PNG">
|
||||
<img src="docs/summary.png">
|
||||
|
||||
# How to build
|
||||
|
||||
Note: development (and the release process) is done with Go 1.16
|
||||
|
||||
1. Clone Project
|
||||
```
|
||||
git clone git@github.com:armosec/kubescape.git kubescape && cd "$_"
|
||||
@@ -52,7 +54,7 @@ go mod tidy && go build -o kubescape .
|
||||
# Under the hood
|
||||
|
||||
## Tests
|
||||
Kubescape is running the following tests according to what is defined by [Kubernetes Hardening Guidance by to NSA and CISA](https://www.nsa.gov/News-Features/Feature-Stories/Article-View/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/)
|
||||
Kubescape is running the following tests according to what is defined by [Kubernetes Hardening Guidance by NSA and CISA](https://www.nsa.gov/News-Features/Feature-Stories/Article-View/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/)
|
||||
* Non-root containers
|
||||
* Immutable container filesystem
|
||||
* Privileged containers
|
||||
|
||||
|
Before Width: | Height: | Size: 206 KiB After Width: | Height: | Size: 206 KiB |
78
printer/junit.go
Normal file
78
printer/junit.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package printer
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"kube-escape/cautils/opapolicy"
|
||||
)
|
||||
|
||||
type JUnitTestSuites struct {
|
||||
XMLName xml.Name `xml:"testsuites"`
|
||||
Suites []JUnitTestSuite `xml:"testsuite"`
|
||||
}
|
||||
|
||||
// JUnitTestSuite is a single JUnit test suite which may contain many
|
||||
// testcases.
|
||||
type JUnitTestSuite struct {
|
||||
XMLName xml.Name `xml:"testsuite"`
|
||||
Tests int `xml:"tests,attr"`
|
||||
Failures int `xml:"failures,attr"`
|
||||
Time string `xml:"time,attr"`
|
||||
Name string `xml:"name,attr"`
|
||||
Properties []JUnitProperty `xml:"properties>property,omitempty"`
|
||||
TestCases []JUnitTestCase `xml:"testcase"`
|
||||
}
|
||||
|
||||
// JUnitTestCase is a single test case with its result.
|
||||
type JUnitTestCase struct {
|
||||
XMLName xml.Name `xml:"testcase"`
|
||||
Classname string `xml:"classname,attr"`
|
||||
Name string `xml:"name,attr"`
|
||||
Time string `xml:"time,attr"`
|
||||
SkipMessage *JUnitSkipMessage `xml:"skipped,omitempty"`
|
||||
Failure *JUnitFailure `xml:"failure,omitempty"`
|
||||
}
|
||||
|
||||
// JUnitSkipMessage contains the reason why a testcase was skipped.
|
||||
type JUnitSkipMessage struct {
|
||||
Message string `xml:"message,attr"`
|
||||
}
|
||||
|
||||
// JUnitProperty represents a key/value pair used to define properties.
|
||||
type JUnitProperty struct {
|
||||
Name string `xml:"name,attr"`
|
||||
Value string `xml:"value,attr"`
|
||||
}
|
||||
|
||||
// JUnitFailure contains data related to a failed test.
|
||||
type JUnitFailure struct {
|
||||
Message string `xml:"message,attr"`
|
||||
Type string `xml:"type,attr"`
|
||||
Contents string `xml:",chardata"`
|
||||
}
|
||||
|
||||
func convertPostureReportToJunitResult(postureResult *opapolicy.PostureReport) (*JUnitTestSuites, error) {
|
||||
juResult := JUnitTestSuites{XMLName: xml.Name{Local: "Kubescape scan results"}}
|
||||
for _, framework := range postureResult.FrameworkReports {
|
||||
suite := JUnitTestSuite{Name: framework.Name}
|
||||
for _, controlReports := range framework.ControlReports {
|
||||
suite.Tests = suite.Tests + 1
|
||||
testCase := JUnitTestCase{}
|
||||
testCase.Name = controlReports.Name
|
||||
testCase.Classname = "Kubescape"
|
||||
testCase.Time = "0"
|
||||
if 0 < len(controlReports.RuleReports[0].RuleResponses) {
|
||||
suite.Failures = suite.Failures + 1
|
||||
failure := JUnitFailure{}
|
||||
failure.Message = fmt.Sprintf("%d resources failed", len(controlReports.RuleReports[0].RuleResponses))
|
||||
for _, ruleResponses := range controlReports.RuleReports[0].RuleResponses {
|
||||
failure.Contents = fmt.Sprintf("%s\n%s", failure.Contents, ruleResponses.AlertMessage)
|
||||
}
|
||||
testCase.Failure = &failure
|
||||
}
|
||||
suite.TestCases = append(suite.TestCases, testCase)
|
||||
}
|
||||
juResult.Suites = append(juResult.Suites, suite)
|
||||
}
|
||||
return &juResult, nil
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package printer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"kube-escape/cautils"
|
||||
"os"
|
||||
@@ -15,16 +17,24 @@ import (
|
||||
|
||||
var INDENT = " "
|
||||
|
||||
const (
|
||||
PrettyPrinter string = "pretty-printer"
|
||||
JsonPrinter string = "json-printer"
|
||||
JunitResultPrinter string = "junit-result-printer"
|
||||
)
|
||||
|
||||
type Printer struct {
|
||||
opaSessionObj *chan *cautils.OPASessionObj
|
||||
summery Summery
|
||||
summary Summary
|
||||
sortedControlNames []string
|
||||
printerType string
|
||||
}
|
||||
|
||||
func NewPrinter(opaSessionObj *chan *cautils.OPASessionObj) *Printer {
|
||||
func NewPrinter(opaSessionObj *chan *cautils.OPASessionObj, printerType string) *Printer {
|
||||
return &Printer{
|
||||
opaSessionObj: opaSessionObj,
|
||||
summery: NewSummery(),
|
||||
summary: NewSummery(),
|
||||
printerType: printerType,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,9 +43,33 @@ func (printer *Printer) ActionPrint() {
|
||||
for {
|
||||
opaSessionObj := <-*printer.opaSessionObj
|
||||
|
||||
printer.SummerySetup(opaSessionObj.PostureReport)
|
||||
printer.PrintResults()
|
||||
printer.PrintSummaryTable()
|
||||
if printer.printerType == PrettyPrinter {
|
||||
printer.SummerySetup(opaSessionObj.PostureReport)
|
||||
printer.PrintResults()
|
||||
printer.PrintSummaryTable()
|
||||
} else if printer.printerType == JsonPrinter {
|
||||
postureReportStr, err := json.Marshal(opaSessionObj.PostureReport.FrameworkReports[0])
|
||||
if err != nil {
|
||||
fmt.Println("Failed to convert posture report object!")
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Stdout.Write(postureReportStr)
|
||||
} else if printer.printerType == JunitResultPrinter {
|
||||
junitResult, err := convertPostureReportToJunitResult(opaSessionObj.PostureReport)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to convert posture report object!")
|
||||
os.Exit(1)
|
||||
}
|
||||
postureReportStr, err := xml.Marshal(junitResult)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to convert posture report object!")
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Stdout.Write(postureReportStr)
|
||||
} else {
|
||||
fmt.Println("unknown output printer")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !k8sinterface.RunningIncluster {
|
||||
break
|
||||
@@ -52,7 +86,7 @@ func (printer *Printer) SummerySetup(postureReport *opapolicy.PostureReport) {
|
||||
workloadsSummery := listResultSummery(cr.RuleReports)
|
||||
mapResources := groupByNamespace(workloadsSummery)
|
||||
|
||||
printer.summery[cr.Name] = ControlSummery{
|
||||
printer.summary[cr.Name] = ControlSummery{
|
||||
TotalResources: cr.GetNumberOfResources(),
|
||||
TotalFailed: len(workloadsSummery),
|
||||
WorkloadSummery: mapResources,
|
||||
@@ -67,11 +101,11 @@ func (printer *Printer) SummerySetup(postureReport *opapolicy.PostureReport) {
|
||||
|
||||
func (printer *Printer) PrintResults() {
|
||||
for i := 0; i < len(printer.sortedControlNames); i++ {
|
||||
controlSummery := printer.summery[printer.sortedControlNames[i]]
|
||||
controlSummery := printer.summary[printer.sortedControlNames[i]]
|
||||
printer.printTitle(printer.sortedControlNames[i], &controlSummery)
|
||||
printer.printResult(printer.sortedControlNames[i], &controlSummery)
|
||||
|
||||
if printer.summery[printer.sortedControlNames[i]].TotalResources > 0 {
|
||||
if printer.summary[printer.sortedControlNames[i]].TotalResources > 0 {
|
||||
printer.printSummery(printer.sortedControlNames[i], &controlSummery)
|
||||
}
|
||||
|
||||
@@ -161,18 +195,18 @@ func (printer *Printer) PrintSummaryTable() {
|
||||
sumFailed := 0
|
||||
|
||||
for i := 0; i < len(printer.sortedControlNames); i++ {
|
||||
controlSummery := printer.summery[printer.sortedControlNames[i]]
|
||||
controlSummery := printer.summary[printer.sortedControlNames[i]]
|
||||
summaryTable.Append(generateRow(printer.sortedControlNames[i], controlSummery))
|
||||
sumFailed += controlSummery.TotalFailed
|
||||
sumTotal += controlSummery.TotalResources
|
||||
}
|
||||
summaryTable.SetFooter(generateFooter(len(printer.summery), sumFailed, sumTotal))
|
||||
summaryTable.SetFooter(generateFooter(len(printer.summary), sumFailed, sumTotal))
|
||||
summaryTable.Render()
|
||||
}
|
||||
|
||||
func (printer *Printer) getSortedControlsNames() []string {
|
||||
controlNames := make([]string, 0, len(printer.summery))
|
||||
for k := range printer.summery {
|
||||
controlNames := make([]string, 0, len(printer.summary))
|
||||
for k := range printer.summary {
|
||||
controlNames = append(controlNames, k)
|
||||
}
|
||||
sort.Strings(controlNames)
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Summery map[string]ControlSummery
|
||||
type Summary map[string]ControlSummery
|
||||
|
||||
func NewSummery() Summery {
|
||||
func NewSummery() Summary {
|
||||
return make(map[string]ControlSummery)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user