From 9ae21b064a9754ffc7d2b23cfed9a8566720954e Mon Sep 17 00:00:00 2001 From: dwertent Date: Sun, 6 Mar 2022 09:32:26 +0200 Subject: [PATCH 1/2] update readme --- README.md | 100 +++----------------- build/README.md | 13 +++ clihandler/initcli.go | 2 +- clihandler/initcliutils.go | 18 +++- resultshandling/reporter/v2/mockreporter.go | 10 +- 5 files changed, 47 insertions(+), 96 deletions(-) create mode 100644 build/README.md diff --git a/README.md b/README.md index 07c4d229..09404b16 100644 --- a/README.md +++ b/README.md @@ -103,9 +103,11 @@ Set-ExecutionPolicy RemoteSigned -scope CurrentUser #### Scan a running Kubernetes cluster and submit results to the [Kubescape SaaS version](https://portal.armo.cloud/) ``` -kubescape scan --submit +kubescape scan --submit --enable-host-scan ``` +> Read [here](https://hub.armo.cloud/docs/host-sensor) more about the `enable-host-scan` flag + #### Scan a running Kubernetes cluster with [`nsa`](https://www.nsa.gov/Press-Room/News-Highlights/Article/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/) framework and submit results to the [Kubescape SaaS version](https://portal.armo.cloud/) ``` kubescape scan framework nsa --submit @@ -149,8 +151,11 @@ kubescape scan --verbose ``` #### Output in `json` format + +> Add the `--format-version v2` flag + ``` -kubescape scan --format json --format-version v2 --output results.json +kubescape scan --format json --format-version v2 --output results.json ``` #### Output in `junit xml` format @@ -233,106 +238,29 @@ Official Docker image `quay.io/armosec/kubescape` docker run -v "$(pwd)/example.yaml:/app/example.yaml quay.io/armosec/kubescape scan /app/example.yaml ``` +If you wish, you can [build the docker image on your own](build/README.md) + # Submit data manually Use the `submit` command if you wish to submit data manually ## Submit scan results manually -First, scan your cluster using the `json` format flag: `kubescape scan framework --format json --output path/to/results.json`. +> Support forward compatibility by using the `--format-version v2` flag -Now you can submit the results to the Kubaescape SaaS version - +First, scan your cluster using the `json` format flag: `kubescape scan framework --format json --format-version v2 --output path/to/results.json`. + +Now you can submit the results to the Kubescape SaaS version - ``` kubescape submit results path/to/results.json ``` -# How to build - -## Build using python (3.7^) script - -Kubescape can be built using: - -``` sh -python build.py -``` - -Note: In order to built using the above script, one must set the environment -variables in this script: - -+ RELEASE -+ ArmoBEServer -+ ArmoERServer -+ ArmoWebsite -+ ArmoAuthServer - - -## Build using go - -Note: development (and the release process) is done with Go `1.17` - -1. Clone Project -``` -git clone https://github.com/armosec/kubescape.git kubescape && cd "$_" -``` - -2. Build -``` -go build -o kubescape . -``` - -3. Run -``` -./kubescape scan --submit --enable-host-scan -``` - -4. Enjoy :zany_face: - -## Docker Build - -### Build your own Docker image - -1. Clone Project -``` -git clone https://github.com/armosec/kubescape.git kubescape && cd "$_" -``` - -2. Build -``` -docker build -t kubescape -f build/Dockerfile . -``` - # Under the hood -## Tests -Kubescape is running the following tests according to what is defined by [Kubernetes Hardening Guidance by NSA and CISA](https://www.nsa.gov/Press-Room/News-Highlights/Article/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/) -* Non-root containers -* Immutable container filesystem -* Privileged containers -* hostPID, hostIPC privileges -* hostNetwork access -* allowedHostPaths field -* Protecting pod service account tokens -* Resource policies -* Control plane hardening -* Exposed dashboard -* Allow privilege escalation -* Applications credentials in configuration files -* Cluster-admin binding -* Exec into container -* Dangerous capabilities -* Insecure capabilities -* Linux hardening -* Ingress and Egress blocked -* Container hostPort -* Network policies -* Symlink Exchange Can Allow Host Filesystem Access (CVE-2021-25741) - - - ## Technology Kubescape based on OPA engine: https://github.com/open-policy-agent/opa and ARMO's posture controls. -The tools retrieves Kubernetes objects from the API server and runs a set of [regos snippets](https://www.openpolicyagent.org/docs/latest/policy-language/) developed by [ARMO](https://www.armosec.io/). +The tools retrieves Kubernetes objects from the API server and runs a set of [rego's snippets](https://www.openpolicyagent.org/docs/latest/policy-language/) developed by [ARMO](https://www.armosec.io/). The results by default printed in a pretty "console friendly" manner, but they can be retrieved in JSON format for further processing. diff --git a/build/README.md b/build/README.md new file mode 100644 index 00000000..581416f2 --- /dev/null +++ b/build/README.md @@ -0,0 +1,13 @@ +## Docker Build + +### Build your own Docker image + +1. Clone Project +``` +git clone https://github.com/armosec/kubescape.git kubescape && cd "$_" +``` + +2. Build +``` +docker build -t kubescape -f build/Dockerfile . +``` \ No newline at end of file diff --git a/clihandler/initcli.go b/clihandler/initcli.go index f7cfac5f..c3d400b8 100644 --- a/clihandler/initcli.go +++ b/clihandler/initcli.go @@ -80,7 +80,7 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces { // ================== setup reporter & printer objects ====================================== // reporting behavior - setup reporter - reportHandler := getReporter(tenantConfig, scanInfo.Submit) + reportHandler := getReporter(tenantConfig, scanInfo.Submit, scanInfo.FrameworkScan, len(scanInfo.InputPatterns) == 0) // setup printer printerHandler := resultshandling.NewPrinter(scanInfo.Format, scanInfo.FormatVersion, scanInfo.VerboseMode) diff --git a/clihandler/initcliutils.go b/clihandler/initcliutils.go index cbb68eaf..a3b3217a 100644 --- a/clihandler/initcliutils.go +++ b/clihandler/initcliutils.go @@ -48,14 +48,22 @@ func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.Kubern return nil } -func getReporter(tenantConfig cautils.ITenantConfig, submit bool) reporter.IReport { - if submit { +func getReporter(tenantConfig cautils.ITenantConfig, submit, fwScan, clusterScan bool) reporter.IReport { + if submit && clusterScan { return reporterv2.NewReportEventReceiver(tenantConfig.GetConfigObj()) } - if tenantConfig.GetAccountID() == "" { - return reporterv2.NewReportMock(reporterv2.NO_SUBMIT_QUERY) + if tenantConfig.GetAccountID() == "" && fwScan && clusterScan { + // Add link only when scanning a cluster using a framework + return reporterv2.NewReportMock(reporterv2.NO_SUBMIT_QUERY, "run kubescape with the '--submit' flag") } - return reporterv2.NewReportMock("") + var message string + if !fwScan { + message = "Kubescape does not submit scan results when scanning controls" + } + if !clusterScan { + message = "Kubescape will submit scan results only when scanning a cluster (not YAML files)" + } + return reporterv2.NewReportMock("", message) } func getResourceHandler(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, hostSensorHandler hostsensorutils.IHostSensor, registryAdaptors *resourcehandler.RegistryAdaptors) resourcehandler.IResourceHandler { diff --git a/resultshandling/reporter/v2/mockreporter.go b/resultshandling/reporter/v2/mockreporter.go index 7837f3fd..16e9ea8f 100644 --- a/resultshandling/reporter/v2/mockreporter.go +++ b/resultshandling/reporter/v2/mockreporter.go @@ -11,12 +11,14 @@ import ( const NO_SUBMIT_QUERY = "utm_source=GitHub&utm_medium=CLI&utm_campaign=no_submit" type ReportMock struct { - query string + query string + message string } -func NewReportMock(query string) *ReportMock { +func NewReportMock(query, message string) *ReportMock { return &ReportMock{ - query: query, + query: query, + message: message, } } func (reportMock *ReportMock) ActionSendReport(opaSessionObj *cautils.OPASessionObj) error { @@ -36,7 +38,7 @@ func (reportMock *ReportMock) DisplayReportURL() { } sep := "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" message := sep + "\n" - message += "Scan results have not been submitted." + "\n" + message += "Scan results have not been submitted: " + reportMock.message + "\n" message += "Sign up for free: " message += u + "\n" message += sep + "\n" From 4c0e358afc5dafa351cbbe7cf7927f49d54b03ec Mon Sep 17 00:00:00 2001 From: dwertent Date: Sun, 6 Mar 2022 09:51:05 +0200 Subject: [PATCH 2/2] support submitting v2 --- clihandler/cmd/results.go | 16 ++++++++++++++-- clihandler/cmd/scan.go | 5 ++--- resultshandling/results.go | 2 ++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/clihandler/cmd/results.go b/clihandler/cmd/results.go index 73b090ac..ed3d1d02 100644 --- a/clihandler/cmd/results.go +++ b/clihandler/cmd/results.go @@ -11,12 +11,16 @@ import ( "github.com/armosec/kubescape/cautils/logger/helpers" "github.com/armosec/kubescape/clihandler" "github.com/armosec/kubescape/clihandler/cliinterfaces" + "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/google/uuid" "github.com/spf13/cobra" ) +var formatVersion string + type ResultsObject struct { filePath string customerGUID string @@ -51,7 +55,7 @@ func (resultsObject *ResultsObject) ListAllResources() (map[string]workloadinter } var resultsCmd = &cobra.Command{ - Use: "results \nExample:\n$ kubescape submit results path/to/results.json", + Use: "results \nExample:\n$ kubescape submit results path/to/results.json --format-version v2", Short: "Submit a pre scanned results file. The file must be in json format", Long: ``, RunE: func(cmd *cobra.Command, args []string) error { @@ -70,7 +74,14 @@ var resultsCmd = &cobra.Command{ resultsObjects := NewResultsObject(clusterConfig.GetAccountID(), clusterConfig.GetClusterName(), args[0]) // submit resources - r := reporterv1.NewReportEventReceiver(clusterConfig.GetConfigObj()) + var r reporter.IReport + switch formatVersion { + case "v2": + r = reporterv2.NewReportEventReceiver(clusterConfig.GetConfigObj()) + default: + logger.L().Warning("Deprecated results version. run with '--format-version' flag", helpers.String("your version", formatVersion), helpers.String("latest version", "v2")) + r = reporterv1.NewReportEventReceiver(clusterConfig.GetConfigObj()) + } submitInterfaces := cliinterfaces.SubmitInterfaces{ ClusterConfig: clusterConfig, @@ -87,6 +98,7 @@ var resultsCmd = &cobra.Command{ func init() { submitCmd.AddCommand(resultsCmd) + resultsCmd.PersistentFlags().StringVar(&formatVersion, "format-version", "v1", "Output object can be differnet between versions, this is for maintaining backward and forward compatibility. Supported:'v1'/'v2'") } func loadResultsFromFile(filePath string) ([]reporthandling.FrameworkReport, error) { diff --git a/clihandler/cmd/scan.go b/clihandler/cmd/scan.go index 6e39856e..5a0f66eb 100644 --- a/clihandler/cmd/scan.go +++ b/clihandler/cmd/scan.go @@ -55,7 +55,6 @@ var scanCmd = &cobra.Command{ func frameworkInitConfig() { k8sinterface.SetClusterContextName(scanInfo.KubeContext) } - func init() { cobra.OnInitialize(frameworkInitConfig) @@ -79,12 +78,12 @@ func init() { scanCmd.PersistentFlags().BoolVarP(&scanInfo.Silent, "silent", "s", false, "Silent progress messages") scanCmd.PersistentFlags().BoolVarP(&scanInfo.Submit, "submit", "", false, "Send the scan results to Armo management portal where you can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more. By default the results are not submitted") scanCmd.PersistentFlags().StringVar(&scanInfo.HostSensorYamlPath, "host-scan-yaml", "", "Override default host sensor DaemonSet. Use this flag cautiously") - scanCmd.PersistentFlags().StringVar(&scanInfo.FormatVersion, "format-version", "v1", "Output object can be differnet between versions, this is for testing and backward compatibility") + scanCmd.PersistentFlags().StringVar(&scanInfo.FormatVersion, "format-version", "v1", "Output object can be differnet between versions, this is for maintaining backward and forward compatibility. Supported:'v1'/'v2'") // hidden flags scanCmd.PersistentFlags().MarkHidden("host-scan-yaml") // this flag should be used very cautiously. We prefer users will not use it at all unless the DaemoSet can not run pods on the nodes scanCmd.PersistentFlags().MarkHidden("silent") // this flag should be deprecated since we added the --logger support - scanCmd.PersistentFlags().MarkHidden("format-version") // meant for testing different output approaches and not for common use + // scanCmd.PersistentFlags().MarkHidden("format-version") // meant for testing different output approaches and not for common use hostF := scanCmd.PersistentFlags().VarPF(&scanInfo.HostSensorEnabled, "enable-host-scan", "", "Deploy ARMO K8s host-sensor daemonset in the scanned cluster. Deleting it right after we collecting the data. Required to collect valueable data from cluster nodes for certain controls. Yaml file: https://raw.githubusercontent.com/armosec/kubescape/master/hostsensorutils/hostsensor.yaml") hostF.NoOptDefVal = "true" diff --git a/resultshandling/results.go b/resultshandling/results.go index b74f04d9..a93dd63d 100644 --- a/resultshandling/results.go +++ b/resultshandling/results.go @@ -3,6 +3,7 @@ package resultshandling import ( "github.com/armosec/kubescape/cautils" "github.com/armosec/kubescape/cautils/logger" + "github.com/armosec/kubescape/cautils/logger/helpers" "github.com/armosec/kubescape/resultshandling/printer" printerv1 "github.com/armosec/kubescape/resultshandling/printer/v1" printerv2 "github.com/armosec/kubescape/resultshandling/printer/v2" @@ -61,6 +62,7 @@ func NewPrinter(printFormat, formatVersion string, verboseMode bool) printer.IPr case "v2": return printerv2.NewJsonPrinter() default: + logger.L().Warning("Deprecated format version. run with '--format-version' flag", helpers.String("your version", formatVersion), helpers.String("latest version", "v2")) return printerv1.NewJsonPrinter() } case printer.JunitResultFormat: