diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 03c6b1ac..57845193 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -16,8 +16,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
- tag_name: v1.0.${{ github.run_number }}
- release_name: Release v1.0.${{ github.run_number }}
+ tag_name: v2.0.${{ github.run_number }}
+ release_name: Release v2.0.${{ github.run_number }}
draft: false
prerelease: false
build:
@@ -39,7 +39,7 @@ jobs:
- name: Build
env:
- RELEASE: v1.0.${{ github.run_number }}
+ RELEASE: v2.0.${{ github.run_number }}
ArmoBEServer: api.armo.cloud
ArmoERServer: report.armo.cloud
ArmoWebsite: portal.armo.cloud
@@ -48,7 +48,7 @@ jobs:
- name: Smoke Testing
env:
- RELEASE: v1.0.${{ github.run_number }}
+ RELEASE: v2.0.${{ github.run_number }}
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape
@@ -74,7 +74,7 @@ jobs:
- uses: actions/checkout@v2
- name: Set name
- run: echo quay.io/armosec/kubescape:v1.0.${{ github.run_number }} > build_tag.txt
+ run: echo quay.io/armosec/kubescape:v2.0.${{ github.run_number }} > build_tag.txt
- name: Build the Docker image
run: docker build . --file build/Dockerfile --tag $(cat build_tag.txt) --build-arg run_number=${{ github.run_number }}
diff --git a/.github/workflows/build_dev.yaml b/.github/workflows/build_dev.yaml
index 2ee5e2b0..8cd5da5c 100644
--- a/.github/workflows/build_dev.yaml
+++ b/.github/workflows/build_dev.yaml
@@ -23,7 +23,7 @@ jobs:
- name: Build
env:
- RELEASE: v1.0.${{ github.run_number }}
+ RELEASE: v2.0.${{ github.run_number }}
ArmoBEServer: api.armo.cloud
ArmoERServer: report.euprod1.cyberarmorsoft.com
ArmoWebsite: portal.armo.cloud
@@ -32,7 +32,7 @@ jobs:
- name: Smoke Testing
env:
- RELEASE: v1.0.${{ github.run_number }}
+ RELEASE: v2.0.${{ github.run_number }}
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape
@@ -53,7 +53,7 @@ jobs:
- uses: actions/checkout@v2
- name: Set name
- run: echo quay.io/armosec/kubescape:dev-v1.0.${{ github.run_number }} > build_tag.txt
+ run: echo quay.io/armosec/kubescape:dev-v2.0.${{ github.run_number }} > build_tag.txt
- name: Build the Docker image
run: docker build . --file build/Dockerfile --tag $(cat build_tag.txt) --build-arg run_number=${{ github.run_number }}
diff --git a/.github/workflows/master_pr_checks.yaml b/.github/workflows/master_pr_checks.yaml
index b4a3bbca..bc88bc45 100644
--- a/.github/workflows/master_pr_checks.yaml
+++ b/.github/workflows/master_pr_checks.yaml
@@ -24,7 +24,7 @@ jobs:
- name: Build
env:
- RELEASE: v1.0.${{ github.run_number }}
+ RELEASE: v2.0.${{ github.run_number }}
ArmoBEServer: api.armo.cloud
ArmoERServer: report.armo.cloud
ArmoWebsite: portal.armo.cloud
@@ -33,7 +33,7 @@ jobs:
- name: Smoke Testing
env:
- RELEASE: v1.0.${{ github.run_number }}
+ RELEASE: v2.0.${{ github.run_number }}
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 3c3ed147..a52b4539 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@
*debug*
*vender*
*.pyc*
-.idea
\ No newline at end of file
+.idea
+ca.srl
\ No newline at end of file
diff --git a/README.md b/README.md
index a9d30e55..5bf1a38b 100644
--- a/README.md
+++ b/README.md
@@ -3,10 +3,10 @@
[](https://github.com/armosec/kubescape/actions/workflows/build.yaml)
[](https://goreportcard.com/report/github.com/armosec/kubescape)
-Kubescape is the first open-source tool for testing if Kubernetes is deployed securely according to multiple frameworks:
-regulatory, customized company policies and DevSecOps best practices, such as the [NSA-CISA](https://www.armosec.io/blog/kubernetes-hardening-guidance-summary-by-armo) and the [MITRE ATT&CK®](https://www.microsoft.com/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/) .
-Kubescape scans K8s clusters, YAML files, and HELM charts, and detect misconfigurations and software vulnerabilities at early stages of the CI/CD pipeline and provides a risk score instantly and risk trends over time.
-Kubescape integrates natively with other DevOps tools, including Jenkins, CircleCI and Github workflows.
+Kubescape is a K8s open-source tool providing a multi-cloud K8s single pane of glass, including risk analysis, security compliance, RBAC visualizer and image vulnerabilities scanning.
+Kubescape scans K8s clusters, YAML files, and HELM charts, detecting misconfigurations according to multiple frameworks (such as the [NSA-CISA](https://www.armosec.io/blog/kubernetes-hardening-guidance-summary-by-armo) , [MITRE ATT&CK®](https://www.microsoft.com/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/)), software vulnerabilities, and RBAC (role-based-access-control) violations at early stages of the CI/CD pipeline, calculates risk score instantly and shows risk trends over time.
+It became one of the fastest-growing Kubernetes tools among developers due to its easy-to-use CLI interface, flexible output formats, and automated scanning capabilities, saving Kubernetes users and admins’ precious time, effort, and resources.
+Kubescape integrates natively with other DevOps tools, including Jenkins, CircleCI, Github workflows, Prometheus, and Slack, and supports multi-cloud K8s deployments like EKS, GKE, and AKS.
@@ -24,7 +24,7 @@ curl -s https://raw.githubusercontent.com/armosec/kubescape/master/install.sh |
## Run:
```
-kubescape scan --submit
+kubescape scan --submit --enable-host-scan
```
@@ -54,9 +54,13 @@ Want to contribute? Want to discuss something? Have an issue?
# Options and examples
+## Playground
+* [Kubescape playground](https://www.katacoda.com/pathaksaiyam/scenarios/kubescape)
+
## Tutorials
* [Overview](https://youtu.be/wdBkt_0Qhbg)
+* [How To Secure Kubernetes Clusters With Kubescape And Armo](https://youtu.be/ZATGiDIDBQk)
* [Scanning Kubernetes YAML files](https://youtu.be/Ox6DaR7_4ZI)
* [Scan Kubescape on an air-gapped environment (offline support)](https://youtu.be/IGXL9s37smM)
* [Managing exceptions in the Kubescape SaaS version](https://youtu.be/OzpvxGmCR80)
@@ -94,13 +98,15 @@ Set-ExecutionPolicy RemoteSigned -scope CurrentUser
| `-t`/`--fail-threshold` | `100` (do not fail) | fail command (return exit code 1) if result is above threshold | `0` -> `100` |
| `-f`/`--format` | `pretty-printer` | Output format | `pretty-printer`/`json`/`junit`/`prometheus` |
| `-o`/`--output` | print to stdout | Save scan result in file | |
-| `--use-from` | | Load local framework object from specified path. If not used will download latest | |
+| `--use-from` | | Load local framework object from specified path. If not used will download latest |
+| `--use-artifacts-from` | | Load artifacts (frameworks, control-config, exceptions) from local directory. If not used will download them | |
| `--use-default` | `false` | Load local framework object from default path. If not used will download latest | `true`/`false` |
-| `--exceptions` | | Path to an [exceptions obj](examples/exceptions.json). If not set will download exceptions from Armo management portal | |
+| `--exceptions` | | Path to an [exceptions obj](examples/exceptions.json). If not set will download exceptions from Armo management portal |
+| `--controls-config` | | Path to a controls-config obj. If not set will download controls-config from ARMO management portal | |
| `--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 | |
+| `--kube-context` | current-context | Cluster context to scan | |
| `--verbose` | `false` | Display all of the input resources and not only failed resources | `true`/`false` |
@@ -190,7 +196,7 @@ It is possible to run Kubescape offline!
First download the framework and then scan with `--use-from` flag
-1. Download and save in file, if file name not specified, will store save to `~/.kubescape/.json`
+1. Download and save in file, if file name not specified, will save in `~/.kubescape/.json`
```
kubescape download framework nsa --output nsa.json
```
@@ -201,6 +207,19 @@ kubescape scan framework nsa --use-from nsa.json
```
+
+You can also download all artifacts to a local path and then load them using `--use-artifacts-from` flag
+
+1. Download and save in local directory, if path not specified, will save all in `~/.kubescape`
+```
+kubescape download artifacts --output path/to/local/dir
+```
+
+2. Scan using the downloaded artifacts
+```
+kubescape scan framework nsa --use-artifacts-from path/to/local/dir
+```
+
## Scan Periodically using Helm - Contributed by [@yonahd](https://github.com/yonahd)
You can scan your cluster periodically by adding a `CronJob` that will repeatedly trigger kubescape
@@ -264,7 +283,7 @@ go build -o kubescape .
3. Run
```
-./kubescape scan framework nsa
+./kubescape scan --submit --enable-host-scan
```
4. Enjoy :zany_face:
@@ -320,3 +339,9 @@ The tools retrieves Kubernetes objects from the API server and runs a set of [re
The results by default printed in a pretty "console friendly" manner, but they can be retrieved in JSON format for further processing.
Kubescape is an open source project, we welcome your feedback and ideas for improvement. We’re also aiming to collaborate with the Kubernetes community to help make the tests themselves more robust and complete as Kubernetes develops.
+
+## Thanks to all the contributors ❤️
+
+
+
+
diff --git a/cautils/getter/loadpolicy.go b/cautils/getter/loadpolicy.go
index 9cde9a0f..7ae8c8f5 100644
--- a/cautils/getter/loadpolicy.go
+++ b/cautils/getter/loadpolicy.go
@@ -85,8 +85,19 @@ func (lp *LoadPolicy) GetFrameworks() ([]reporthandling.Framework, error) {
}
func (lp *LoadPolicy) ListFrameworks() ([]string, error) {
- // TODO - Support
- return []string{}, fmt.Errorf("loading frameworks list from file is not supported")
+ fwNames := []string{}
+ framework := &reporthandling.Framework{}
+ for _, f := range lp.filePaths {
+ file, err := os.ReadFile(f)
+ if err == nil {
+ if err := json.Unmarshal(file, framework); err == nil {
+ if !contains(fwNames, framework.Name) {
+ fwNames = append(fwNames, framework.Name)
+ }
+ }
+ }
+ }
+ return fwNames, nil
}
func (lp *LoadPolicy) ListControls(listType ListType) ([]string, error) {
@@ -114,7 +125,7 @@ func (lp *LoadPolicy) GetControlsInputs(clusterName string) (map[string][]string
return nil, err
}
- if err = json.Unmarshal(f, &accountConfig); err == nil {
+ if err = json.Unmarshal(f, &accountConfig.Settings.PostureControlInputs); err == nil {
return accountConfig.Settings.PostureControlInputs, nil
}
return nil, err
diff --git a/cautils/rbac.go b/cautils/rbac.go
index 64854016..a9d8bfbc 100644
--- a/cautils/rbac.go
+++ b/cautils/rbac.go
@@ -42,6 +42,17 @@ func (rbacObjects *RBACObjects) ListAllResources() (map[string]workloadinterface
func (rbacObjects *RBACObjects) rbacObjectsToResources(resources *rbacutils.RbacObjects) (map[string]workloadinterface.IMetadata, error) {
allresources := map[string]workloadinterface.IMetadata{}
+
+ /*
+ ************************************************************************************************************************
+ This code is adding a non valid ID ->
+ (github.com/armosec/rbac-utils v0.0.11): "//SA2WLIDmap/SA2WLIDmap"
+ (github.com/armosec/rbac-utils v0.0.12): "armo.rbac.com/v0beta1//SAID2WLIDmap/SAID2WLIDmap"
+
+ Should be investigated
+ ************************************************************************************************************************
+ */
+
// wrap rbac aggregated objects in IMetadata and add to allresources
// TODO - DEPRECATE SA2WLIDmap
SA2WLIDmapIMeta, err := rbacutils.SA2WLIDmapIMetadataWrapper(resources.SA2WLIDmap)
@@ -62,7 +73,7 @@ func (rbacObjects *RBACObjects) rbacObjectsToResources(resources *rbacutils.Rbac
if err != nil {
return nil, err
}
- crmap["apiVersion"] = "rbac.authorization.k8s.io/v1"
+ crmap["apiVersion"] = "rbac.authorization.k8s.io/v1" // TODO - is the the correct apiVersion?
crIMeta := workloadinterface.NewWorkloadObj(crmap)
crIMeta.SetKind("ClusterRole")
allresources[crIMeta.GetID()] = crIMeta
@@ -72,7 +83,7 @@ func (rbacObjects *RBACObjects) rbacObjectsToResources(resources *rbacutils.Rbac
if err != nil {
return nil, err
}
- crmap["apiVersion"] = "rbac.authorization.k8s.io/v1"
+ crmap["apiVersion"] = "rbac.authorization.k8s.io/v1" // TODO - is the the correct apiVersion?
crIMeta := workloadinterface.NewWorkloadObj(crmap)
crIMeta.SetKind("Role")
allresources[crIMeta.GetID()] = crIMeta
@@ -82,7 +93,7 @@ func (rbacObjects *RBACObjects) rbacObjectsToResources(resources *rbacutils.Rbac
if err != nil {
return nil, err
}
- crmap["apiVersion"] = "rbac.authorization.k8s.io/v1"
+ crmap["apiVersion"] = "rbac.authorization.k8s.io/v1" // TODO - is the the correct apiVersion?
crIMeta := workloadinterface.NewWorkloadObj(crmap)
crIMeta.SetKind("ClusterRoleBinding")
allresources[crIMeta.GetID()] = crIMeta
@@ -92,7 +103,7 @@ func (rbacObjects *RBACObjects) rbacObjectsToResources(resources *rbacutils.Rbac
if err != nil {
return nil, err
}
- crmap["apiVersion"] = "rbac.authorization.k8s.io/v1"
+ crmap["apiVersion"] = "rbac.authorization.k8s.io/v1" // TODO - is the the correct apiVersion?
crIMeta := workloadinterface.NewWorkloadObj(crmap)
crIMeta.SetKind("RoleBinding")
allresources[crIMeta.GetID()] = crIMeta
diff --git a/cautils/scaninfo.go b/cautils/scaninfo.go
index 3175f1d9..472f007d 100644
--- a/cautils/scaninfo.go
+++ b/cautils/scaninfo.go
@@ -1,16 +1,23 @@
package cautils
import (
+ "encoding/json"
"fmt"
+ "io/ioutil"
+ "log"
+ "os"
"path/filepath"
+ "strings"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/opa-utils/reporthandling"
)
const (
- ScanCluster string = "cluster"
- ScanLocalFiles string = "yaml"
+ ScanCluster string = "cluster"
+ ScanLocalFiles string = "yaml"
+ localControlInputsFilename string = "controls-inputs.json"
+ localExceptionsFilename string = "exceptions.json"
)
type BoolPtrFlag struct {
@@ -52,6 +59,7 @@ type ScanInfo struct {
ControlsInputs string // Load file with inputs for controls
UseFrom []string // Load framework from local file (instead of download). Use when running offline
UseDefault bool // Load framework from cached file (instead of download). Use when running offline
+ UseArtifactsFrom string // Load artifacts from local path. Use when running offline
VerboseMode bool // Display all of the input resources and not only failed resources
Format string // Format results (table, json, junit ...)
Output string // Store results in an output file, Output file name
@@ -64,7 +72,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
+ KubeContext string // context name
FrameworkScan bool // false if scanning control
ScanAll bool // true if scan all frameworks
}
@@ -78,7 +86,48 @@ type Getters struct {
func (scanInfo *ScanInfo) Init() {
scanInfo.setUseFrom()
scanInfo.setOutputFile()
+ scanInfo.setUseArtifactsFrom()
+}
+func (scanInfo *ScanInfo) setUseArtifactsFrom() {
+ if scanInfo.UseArtifactsFrom == "" {
+ return
+ }
+ // UseArtifactsFrom must be a path without a filename
+ dir, file := filepath.Split(scanInfo.UseArtifactsFrom)
+ if dir == "" {
+ scanInfo.UseArtifactsFrom = file
+ } else if strings.Contains(file, ".json") {
+ scanInfo.UseArtifactsFrom = dir
+ }
+ // set frameworks files
+ files, err := ioutil.ReadDir(scanInfo.UseArtifactsFrom)
+ if err != nil {
+ log.Fatal(err)
+ }
+ framework := &reporthandling.Framework{}
+ for _, f := range files {
+ filePath := filepath.Join(scanInfo.UseArtifactsFrom, f.Name())
+ file, err := os.ReadFile(filePath)
+ if err == nil {
+ if err := json.Unmarshal(file, framework); err == nil {
+ scanInfo.UseFrom = append(scanInfo.UseFrom, filepath.Join(scanInfo.UseArtifactsFrom, f.Name()))
+ }
+ }
+ }
+ // set config-inputs file
+ scanInfo.ControlsInputs = filepath.Join(scanInfo.UseArtifactsFrom, localControlInputsFilename)
+ // set exceptions
+ scanInfo.UseExceptions = filepath.Join(scanInfo.UseArtifactsFrom, localExceptionsFilename)
+}
+
+func (scanInfo *ScanInfo) setUseExceptions() {
+ if scanInfo.UseExceptions != "" {
+ // load exceptions from file
+ scanInfo.ExceptionsGetter = getter.NewLoadPolicy([]string{scanInfo.UseExceptions})
+ } else {
+ scanInfo.ExceptionsGetter = getter.GetArmoAPIConnector()
+ }
}
func (scanInfo *ScanInfo) setUseFrom() {
diff --git a/cautils/versioncheck.go b/cautils/versioncheck.go
index 22f88c83..55f6417e 100644
--- a/cautils/versioncheck.go
+++ b/cautils/versioncheck.go
@@ -96,7 +96,7 @@ func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckReques
if latestVersion.ClientUpdate != "" {
if BuildNumber != "" && BuildNumber < latestVersion.ClientUpdate {
- WarningDisplay(os.Stderr, warningMessage(latestVersion.Client, latestVersion.ClientUpdate), "\n")
+ WarningDisplay(os.Stderr, warningMessage(latestVersion.Client, latestVersion.ClientUpdate)+"\n")
}
}
@@ -106,7 +106,7 @@ func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckReques
// }
if latestVersion.Message != "" {
- InfoDisplay(os.Stderr, latestVersion.Message, "\n")
+ InfoDisplay(os.Stderr, latestVersion.Message+"\n")
}
return nil
diff --git a/clihandler/clidownload.go b/clihandler/clidownload.go
index 9129344d..ca07847a 100644
--- a/clihandler/clidownload.go
+++ b/clihandler/clidownload.go
@@ -51,7 +51,7 @@ func setPathandFilename(downloadInfo *cautils.DownloadInfo) {
dir, file := filepath.Split(downloadInfo.Path)
if dir == "" {
downloadInfo.Path = file
- } else {
+ } else if strings.Contains(file, ".json") {
downloadInfo.Path = dir
downloadInfo.FileName = file
}
diff --git a/clihandler/cmd/scan.go b/clihandler/cmd/scan.go
index 00440d62..76761bc7 100644
--- a/clihandler/cmd/scan.go
+++ b/clihandler/cmd/scan.go
@@ -35,16 +35,17 @@ var scanCmd = &cobra.Command{
}
func frameworkInitConfig() {
- k8sinterface.SetClusterContextName(scanInfo.ClusterName)
+ k8sinterface.SetClusterContextName(scanInfo.KubeContext)
}
func init() {
cobra.OnInitialize(frameworkInitConfig)
rootCmd.AddCommand(scanCmd)
- rootCmd.PersistentFlags().StringVarP(&scanInfo.ClusterName, "cluster", "", "", "Cluster name. Default will use the current-context")
+ rootCmd.PersistentFlags().StringVarP(&scanInfo.KubeContext, "kube-context", "", "", "Kube context. 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().StringVar(&scanInfo.UseArtifactsFrom, "use-artifacts-from", "", "Load artifacts from local directory. If not used will download them")
scanCmd.PersistentFlags().StringVarP(&scanInfo.ExcludedNamespaces, "exclude-namespaces", "e", "", "Namespaces to exclude from scanning. Recommended: kube-system,kube-public")
scanCmd.PersistentFlags().Uint16VarP(&scanInfo.FailThreshold, "fail-threshold", "t", 100, "Failure threshold is the percent above which the command fails and returns exit code 1")
scanCmd.PersistentFlags().StringVarP(&scanInfo.Format, "format", "f", "pretty-printer", `Output format. Supported formats: "pretty-printer"/"json"/"junit"/"prometheus"`)
diff --git a/clihandler/cmd/submit.go b/clihandler/cmd/submit.go
index 8e412064..1f27b4eb 100644
--- a/clihandler/cmd/submit.go
+++ b/clihandler/cmd/submit.go
@@ -20,7 +20,7 @@ func init() {
}
func getSubmittedClusterConfig(k8s *k8sinterface.KubernetesApi) (*cautils.ClusterConfig, error) {
- clusterConfig := cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account, scanInfo.ClusterName) // TODO - support none cluster env submit
+ clusterConfig := cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account, scanInfo.KubeContext) // TODO - support none cluster env submit
if clusterConfig.GetCustomerGUID() != "" {
if err := clusterConfig.SetTenant(); err != nil {
return clusterConfig, err
diff --git a/clihandler/initcli.go b/clihandler/initcli.go
index 58fcc1b9..429cb854 100644
--- a/clihandler/initcli.go
+++ b/clihandler/initcli.go
@@ -44,7 +44,7 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
}
}
- tenantConfig := getTenantConfig(scanInfo.Account, scanInfo.ClusterName, k8s)
+ tenantConfig := getTenantConfig(scanInfo.Account, scanInfo.KubeContext, k8s)
// Set submit behavior AFTER loading tenant config
setSubmitBehavior(scanInfo, tenantConfig)
diff --git a/clihandler/initcliutils.go b/clihandler/initcliutils.go
index 6ac729a2..fd60a02b 100644
--- a/clihandler/initcliutils.go
+++ b/clihandler/initcliutils.go
@@ -11,9 +11,10 @@ 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
@@ -48,7 +49,8 @@ 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()
}
diff --git a/docs/proposals/container-image-vulnerability-adaptor.md b/docs/proposals/container-image-vulnerability-adaptor.md
index 35976514..f9ef0741 100644
--- a/docs/proposals/container-image-vulnerability-adaptor.md
+++ b/docs/proposals/container-image-vulnerability-adaptor.md
@@ -1,4 +1,4 @@
-# Container image vulnerabilty adaptor interface proposal
+# Container image vulnerability adaptor interface proposal
## Rationale
@@ -6,7 +6,7 @@ source #287
### Big picture
-* Kubescape team planning to create controls which take into account image vulnerabilities, example: looking for public internet facing workloads with critical vulnerabilities. These are seriously effecting the security health of a cluster and therefore we think it is important to cover it. We think that most container registries are/will support image scanning like Harbor and therefore the ability to get information from them is important.
+* Kubescape team is planning to create controls which take into account image vulnerabilities, example: looking for public internet facing workloads with critical vulnerabilities. These are seriously effecting the security health of a cluster and therefore we think it is important to cover it. We think that most container registries are/will support image scanning like Harbor and therefore the ability to get information from them is important.
* There are information in the image repository which is important for existing controls as well. They are incomplete without it, example see this issue: Non-root containers check is broken #19 . These are not necessarily image vulnerability related. Can be information in the image manifest (like the issue before), but it can be the image BOM related.
### Relation to this proposal
@@ -114,4 +114,63 @@ type IContainerImageVulnerabilityAdaptor interface {
GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error)
}
+```
+
+
+
+# Integration
+
+# Input
+
+The objects received from the interface will be converted to an Imetadata compatible objects as following
+
+```
+{
+ "apiVersion": "image.vulnscan.com/v1",
+ "kind": "VulnScan",
+ "metadata": {
+ "name": "nginx:latest"
+ },
+ "data": {
+ // returned by the adaptor API (structure like our backend gives for an image
+ }
+}
+```
+
+
+# Output
+
+The rego results will be a combination of the k8s artifact and the list of relevant CVEs for the control
+
+```
+{
+ "apiVersion": "result.vulnscan.com/v1",
+ "kind": "Pod",
+ "metadata": {
+ "name": "nginx"
+ },
+ "relatedObjects": [
+ {
+ "apiVersion": "v1",
+ "kind": "Pod",
+ "metadata": {
+ "name": "nginx"
+ },
+ "spec": {
+ // podSpec
+ },
+ },
+ {
+ "apiVersion": "container.vulnscan.com/v1",
+ "kind": "VulnScan",
+ "metadata": {
+ "name": "nginx:latest",
+ },
+ "data": {
+
+ // returned by the adaptor API (structure like our backend gives for an image
+ }
+ }
+ ]
+}
```
\ No newline at end of file
diff --git a/examples/cloud-vendor-integration/aws.sh b/examples/cloud-vendor-integration/aws.sh
new file mode 100755
index 00000000..67f36bfe
--- /dev/null
+++ b/examples/cloud-vendor-integration/aws.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+
+# AWS
+# Attach the Kubescape service account to an AWS IAM role with the described cluster permission
+
+# Prerequisites:
+# eksctl, awscli v2
+
+# Set environment variables
+echo 'Set environment variables'
+export kubescape_namespace=armo-system
+export kubescape_serviceaccount=armo-kubescape-service-account
+
+# Get current context
+echo 'Get current context'
+export context=$(kubectl config current-context)
+
+# Get cluster arn
+echo 'Get cluster arn'
+export cluster_arn=$(kubectl config view -o jsonpath="{.contexts[?(@.name == \"$context\")].context.cluster}")
+
+# Get cluster name
+echo 'Get cluster name'
+export cluster_name=$(echo "$cluster_arn" | awk -F'/' '{print $NF}')
+
+# Get cluster region
+echo 'Get cluster region'
+export cluster_region=$(echo "$cluster_arn" | awk -F':' '{print $4}')
+
+# First step, Create IAM OIDC provider for the cluster (Not required if the third step runs as is):
+echo 'Create IAM OIDC provider for the cluster'
+eksctl utils associate-iam-oidc-provider --cluster $cluster_name --approve
+
+# Second step, Create a policy and service account role:
+# Create a kubescape policy
+echo 'Create a kubescape policy'
+export kubescape_policy_arn=$(aws iam create-policy \
+ --output yaml \
+ --query 'Policy.Arn' \
+ --policy-name kubescape \
+ --policy-document \
+"$(cat <&2 echo "Please set the CLUSTER_NAME environment variable" && exit 1
+[ -z $CLUSTER_REGION ] && >&2 echo "Please set the CLUSTER_REGION environment variable" && exit 1
+
+# Create GCP service account
+gcloud iam service-accounts create kubescape --display-name=kubescape
+
+# Set environment variables
+echo 'Set environment variables'
+export kubescape_namespace=armo-system
+export kubescape_serviceaccount=armo-kubescape-service-account
+
+# Get current GCP project
+echo 'Get current GCP project'
+export gcp_project=$(gcloud config get-value project)
+
+sleep 5
+# Get service account email
+echo 'Get service account email'
+export gcp_service_account=$(gcloud iam service-accounts list --filter="email ~ kubescape@" --format="value(email)")
+
+# Create custome cluster.get role
+echo 'Create custome cluster.get role'
+export custom_role_name=$(gcloud iam roles create kubescape --project=$gcp_project --title='Armo kubernetes' --description='Allow clusters.get to Kubernetes armo service account' --permissions=container.clusters.get --stage=GA --format='value(name)')
+
+# Attach policies to the service account
+echo 'Attach policies to the service account'
+gcloud --quiet projects add-iam-policy-binding $gcp_project --member serviceAccount:$gcp_service_account --role $custom_role_name >/dev/null
+gcloud --quiet projects add-iam-policy-binding $gcp_project --member serviceAccount:$gcp_service_account --role roles/storage.objectViewer >/dev/null
+
+# If there are missing permissions, use this role instead
+# gcloud --quiet projects add-iam-policy-binding $gcp_project --member serviceAccount:$gcp_service_account --role roles/container.clusterViewer
+
+# Bind the GCP kubescape service account to kubescape kubernetes service account
+gcloud iam service-accounts add-iam-policy-binding $gcp_service_account --role roles/iam.workloadIdentityUser --member "serviceAccount:${gcp_project}.svc.id.goog[${kubescape_namespace}/${kubescape_serviceaccount}]"
+
+# Install/Upgrade Kubescape chart
+echo 'Install/Upgrade Kubescape chart'
+helm upgrade --install armo armo-components/ -n armo-system --create-namespace --set cloud_provider_engine=gke --set gke_service_account=$gcp_service_account --set cloudRegion=$CLUSTER_REGION --set clusterName=$CLUSTER_NAME --set gkeProject=$gcp_project
diff --git a/go.mod b/go.mod
index 73276b8a..9c71297f 100644
--- a/go.mod
+++ b/go.mod
@@ -3,10 +3,10 @@ module github.com/armosec/kubescape
go 1.17
require (
- github.com/armosec/armoapi-go v0.0.40
- github.com/armosec/k8s-interface v0.0.54
- github.com/armosec/opa-utils v0.0.92
- github.com/armosec/rbac-utils v0.0.11
+ github.com/armosec/armoapi-go v0.0.41
+ github.com/armosec/k8s-interface v0.0.56
+ github.com/armosec/opa-utils v0.0.99
+ github.com/armosec/rbac-utils v0.0.12
github.com/armosec/utils-go v0.0.3
github.com/armosec/utils-k8s-go v0.0.1
github.com/briandowns/spinner v1.18.0
@@ -37,6 +37,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/aws/aws-sdk-go v1.41.11 // indirect
github.com/coreos/go-oidc v2.2.1+incompatible // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
diff --git a/go.sum b/go.sum
index 77624138..c2c78393 100644
--- a/go.sum
+++ b/go.sum
@@ -83,21 +83,23 @@ 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/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/k8s-interface v0.0.56 h1:7dOgc3qZaI7ReLRZcJa2JZKk0rliyYi05l1vuHc6gcE=
+github.com/armosec/k8s-interface v0.0.56/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.92 h1:RzzORhfLx9Evc2ceFtNRoehxUFzwlvK5iMtR6fLWzZc=
-github.com/armosec/opa-utils v0.0.92/go.mod h1:ZOXYVTtuyrV4TldcfbzgRqP6F9Drlf4hB0zr210OXgM=
+github.com/armosec/opa-utils v0.0.99 h1:ZuoIPg6vbgO4J09xJZDO/yIRD59odwmK2Bm55uTvkU8=
+github.com/armosec/opa-utils v0.0.99/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=
+github.com/armosec/rbac-utils v0.0.12 h1:uJpMGDyLAX129PrKHp6NPNB6lVRhE0OZIwV6ywzSDrs=
+github.com/armosec/rbac-utils v0.0.12/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0=
github.com/armosec/utils-go v0.0.2/go.mod h1:itWmRLzRdsnwjpEOomL0mBWGnVNNIxSjDAdyc+b0iUo=
github.com/armosec/utils-go v0.0.3 h1:uyQI676yRciQM0sSN9uPoqHkbspTxHO0kmzXhBeE/xU=
github.com/armosec/utils-go v0.0.3/go.mod h1:itWmRLzRdsnwjpEOomL0mBWGnVNNIxSjDAdyc+b0iUo=
diff --git a/hostsensorutils/hostsensoryamls.go b/hostsensorutils/hostsensor.yaml
similarity index 94%
rename from hostsensorutils/hostsensoryamls.go
rename to hostsensorutils/hostsensor.yaml
index a29bcce8..305bd09b 100644
--- a/hostsensorutils/hostsensoryamls.go
+++ b/hostsensorutils/hostsensor.yaml
@@ -1,6 +1,4 @@
-package hostsensorutils
-
-const hostSensorYAML = `apiVersion: v1
+apiVersion: v1
kind: Namespace
metadata:
labels:
@@ -62,4 +60,4 @@ spec:
name: host-filesystem
hostNetwork: true
hostPID: true
- hostIPC: true`
+ hostIPC: true
\ No newline at end of file
diff --git a/hostsensorutils/hostsensordeploy.go b/hostsensorutils/hostsensordeploy.go
index 06e1c008..3d7042e8 100644
--- a/hostsensorutils/hostsensordeploy.go
+++ b/hostsensorutils/hostsensordeploy.go
@@ -1,6 +1,7 @@
package hostsensorutils
import (
+ _ "embed"
"fmt"
"io"
"strings"
@@ -18,6 +19,11 @@ import (
coreapplyv1 "k8s.io/client-go/applyconfigurations/core/v1"
)
+var (
+ //go:embed hostsensor.yaml
+ hostSensorYAML string
+)
+
type HostSensorHandler struct {
HostSensorPort int32
HostSensorPodNames map[string]string //map from pod names to node names
diff --git a/opaprocessor/processorhandler.go b/opaprocessor/processorhandler.go
index d9c53fbc..e1a4d73c 100644
--- a/opaprocessor/processorhandler.go
+++ b/opaprocessor/processorhandler.go
@@ -6,6 +6,7 @@ import (
"time"
"github.com/armosec/kubescape/cautils"
+ ksscore "github.com/armosec/kubescape/score"
"github.com/armosec/opa-utils/objectsenvelopes"
"github.com/armosec/opa-utils/reporthandling"
"github.com/armosec/opa-utils/reporthandling/apis"
@@ -71,6 +72,9 @@ func (opaHandler *OPAProcessorHandler) ProcessRulesListenner() {
// edit results
opap.updateResults()
+ //TODO: review this location
+ scorewrapper := ksscore.NewScoreWrapper(opaSessionObj)
+ scorewrapper.Calculate(ksscore.EPostureReportV2)
// report
*opaHandler.reportResults <- opaSessionObj
}
diff --git a/resourcehandler/cloudproviderhandler.go b/resourcehandler/cloudproviderhandler.go
new file mode 100644
index 00000000..aa101766
--- /dev/null
+++ b/resourcehandler/cloudproviderhandler.go
@@ -0,0 +1,110 @@
+package resourcehandler
+
+import (
+ "os"
+ "strings"
+
+ "github.com/armosec/k8s-interface/cloudsupport"
+ "github.com/armosec/k8s-interface/k8sinterface"
+)
+
+var (
+ KS_KUBE_CLUSTER_ENV_VAR = "KS_KUBE_CLUSTER"
+ KS_CLOUD_PROVIDER_ENV_VAR = "KS_CLOUD_PROVIDER"
+ KS_CLOUD_REGION_ENV_VAR = "KS_CLOUD_REGION"
+ KS_GKE_PROJECT_ENV_VAR = "KS_GKE_PROJECT"
+)
+
+type ICloudProvider interface {
+ getKubeCluster() string
+ getRegion(cluster string, provider string) (string, error)
+ getProject(cluster string, provider string) (string, error)
+ getKubeClusterName() string
+}
+
+func initCloudProvider() ICloudProvider {
+
+ switch getCloudProvider() {
+ case "gke", "gcp":
+ if isEnvVars() {
+ return NewGKEProviderEnvVar()
+ }
+ return NewGKEProviderContext()
+ case "eks", "aws":
+ if isEnvVars() {
+ return NewEKSProviderEnvVar()
+ }
+ return NewEKSProviderContext()
+ }
+ return NewEmptyCloudProvider()
+}
+
+func getCloudProvider() string {
+ var provider string
+ if isEnvVars() {
+ provider = getCloudProviderFromEnvVar()
+ } else {
+ provider = getCloudProviderFromContext()
+ }
+ return strings.ToLower(provider)
+}
+
+func getCloudProviderFromContext() string {
+ return cloudsupport.GetCloudProvider(getClusterFromContext())
+}
+
+func getClusterFromContext() string {
+ context := k8sinterface.GetCurrentContext()
+ if context == nil {
+ return ""
+ }
+ cluster := context.Cluster
+ if cluster != "" {
+ return cluster
+ }
+ return k8sinterface.GetClusterName()
+}
+
+func getCloudProviderFromEnvVar() string {
+ val, present := os.LookupEnv(KS_CLOUD_PROVIDER_ENV_VAR)
+ if present {
+ return val
+ }
+ return ""
+}
+
+func isEnvVars() bool {
+ _, present := os.LookupEnv(KS_KUBE_CLUSTER_ENV_VAR)
+ if !present {
+ return false
+ }
+ _, present = os.LookupEnv(KS_CLOUD_PROVIDER_ENV_VAR)
+ if !present {
+ return false
+ }
+ _, present = os.LookupEnv(KS_CLOUD_REGION_ENV_VAR)
+ return present
+}
+
+type EmptyCloudProvider struct {
+}
+
+func NewEmptyCloudProvider() *EmptyCloudProvider {
+ return &EmptyCloudProvider{}
+}
+
+func (emptyCloudProvider *EmptyCloudProvider) getKubeCluster() string {
+ return getClusterFromContext()
+}
+
+func (emptyCloudProvider *EmptyCloudProvider) getKubeClusterName() string {
+ return emptyCloudProvider.getKubeCluster()
+}
+
+func (emptyCloudProvider *EmptyCloudProvider) getRegion(cluster string, provider string) (string, error) {
+ return "", nil
+}
+
+func (emptyCloudProvider *EmptyCloudProvider) getProject(cluster string, provider string) (string, error) {
+ return "", nil
+}
diff --git a/resourcehandler/ekssupport.go b/resourcehandler/ekssupport.go
new file mode 100644
index 00000000..91f4b1a8
--- /dev/null
+++ b/resourcehandler/ekssupport.go
@@ -0,0 +1,95 @@
+package resourcehandler
+
+import (
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/armosec/k8s-interface/k8sinterface"
+)
+
+type EKSProviderEnvVar struct {
+}
+
+func NewEKSProviderEnvVar() *EKSProviderEnvVar {
+ return &EKSProviderEnvVar{}
+}
+
+func (eksProviderEnvVar *EKSProviderEnvVar) getKubeClusterName() string {
+ return eksProviderEnvVar.getKubeCluster()
+}
+
+func (eksProviderEnvVar *EKSProviderEnvVar) getKubeCluster() string {
+ val, present := os.LookupEnv(KS_KUBE_CLUSTER_ENV_VAR)
+ if present {
+ return val
+ }
+ return ""
+}
+
+func (eksProviderEnvVar *EKSProviderEnvVar) getRegion(cluster string, provider string) (string, error) {
+ return eksProviderEnvVar.getRegionForEKS(cluster)
+}
+
+func (eksProviderEnvVar *EKSProviderEnvVar) getProject(cluster string, provider string) (string, error) {
+ return "", nil
+}
+
+func (eksProviderEnvVar *EKSProviderEnvVar) getRegionForEKS(cluster string) (string, error) {
+ region, present := os.LookupEnv(KS_CLOUD_REGION_ENV_VAR)
+ if present {
+ return region, nil
+ }
+ splittedClusterContext := strings.Split(cluster, ".")
+ if len(splittedClusterContext) < 2 {
+ return "", fmt.Errorf("error: failed to get region")
+ }
+ region = splittedClusterContext[1]
+ return region, nil
+}
+
+// ------------------------------------- EKSProviderContext -------------------------
+
+type EKSProviderContext struct {
+}
+
+func NewEKSProviderContext() *EKSProviderContext {
+ return &EKSProviderContext{}
+}
+
+func (eksProviderContext *EKSProviderContext) getKubeClusterName() string {
+ cluster := k8sinterface.GetCurrentContext().Cluster
+ var splittedCluster []string
+ if cluster != "" {
+ splittedCluster = strings.Split(cluster, ".")
+ if len(splittedCluster) > 1 {
+ return splittedCluster[0]
+ }
+ }
+ splittedCluster = strings.Split(k8sinterface.GetClusterName(), ".")
+ if len(splittedCluster) > 1 {
+ return splittedCluster[0]
+ }
+ return ""
+}
+
+func (eksProviderContext *EKSProviderContext) getKubeCluster() string {
+ cluster := k8sinterface.GetCurrentContext().Cluster
+ if cluster != "" {
+ return cluster
+ }
+ return k8sinterface.GetClusterName()
+}
+
+func (eksProviderContext *EKSProviderContext) getRegion(cluster string, provider string) (string, error) {
+ splittedClusterContext := strings.Split(cluster, ".")
+ if len(splittedClusterContext) < 2 {
+ return "", fmt.Errorf("error: failed to get region")
+ }
+ region := splittedClusterContext[1]
+ return region, nil
+}
+
+func (eksProviderContext *EKSProviderContext) getProject(cluster string, provider string) (string, error) {
+ return "", nil
+}
diff --git a/resourcehandler/gkesupport.go b/resourcehandler/gkesupport.go
new file mode 100644
index 00000000..0dda579e
--- /dev/null
+++ b/resourcehandler/gkesupport.go
@@ -0,0 +1,133 @@
+package resourcehandler
+
+import (
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/armosec/k8s-interface/k8sinterface"
+)
+
+type GKEProviderEnvVar struct {
+}
+
+func NewGKEProviderEnvVar() *GKEProviderEnvVar {
+ return &GKEProviderEnvVar{}
+}
+func (gkeProvider *GKEProviderEnvVar) getKubeClusterName() string {
+ return gkeProvider.getKubeCluster()
+}
+
+func (gkeProvider *GKEProviderEnvVar) getKubeCluster() string {
+ val, present := os.LookupEnv(KS_KUBE_CLUSTER_ENV_VAR)
+ if present {
+ return val
+ }
+ return ""
+}
+
+func (gkeProvider *GKEProviderEnvVar) getRegion(cluster string, provider string) (string, error) {
+ return gkeProvider.getRegionForGKE(cluster)
+}
+
+func (gkeProvider *GKEProviderEnvVar) getProject(cluster string, provider string) (string, error) {
+ return gkeProvider.getProjectForGKE(cluster)
+}
+
+func (gkeProvider *GKEProviderEnvVar) getProjectForGKE(cluster string) (string, error) {
+ project, present := os.LookupEnv(KS_GKE_PROJECT_ENV_VAR)
+ if present {
+ return project, nil
+ }
+ parsedName := strings.Split(cluster, "_")
+ if len(parsedName) < 3 {
+ return "", fmt.Errorf("failed to parse project name from cluster name: '%s'", cluster)
+ }
+ project = parsedName[1]
+ return project, nil
+}
+
+func (gkeProvider *GKEProviderEnvVar) getRegionForGKE(cluster string) (string, error) {
+ region, present := os.LookupEnv(KS_CLOUD_REGION_ENV_VAR)
+ if present {
+ return region, nil
+ }
+ parsedName := strings.Split(cluster, "_")
+ if len(parsedName) < 3 {
+ return "", fmt.Errorf("failed to parse region name from cluster name: '%s'", cluster)
+ }
+ region = parsedName[2]
+ return region, nil
+
+}
+
+// ------------------------------ GKEProviderContext --------------------------------------------------------
+
+type GKEProviderContext struct {
+}
+
+func NewGKEProviderContext() *GKEProviderContext {
+ return &GKEProviderContext{}
+}
+
+func (gkeProviderContext *GKEProviderContext) getKubeClusterName() string {
+ context := k8sinterface.GetCurrentContext()
+ if context == nil {
+ return ""
+ }
+ cluster := context.Cluster
+ parsedName := strings.Split(cluster, "_")
+ if len(parsedName) < 3 {
+ return ""
+ }
+ clusterName := parsedName[3]
+ if clusterName != "" {
+ return clusterName
+ }
+ cluster = k8sinterface.GetClusterName()
+ parsedName = strings.Split(cluster, "_")
+ if len(parsedName) < 3 {
+ return ""
+ }
+ clusterName = parsedName[3]
+ return clusterName
+}
+
+func (gkeProviderContext *GKEProviderContext) getKubeCluster() string {
+ context := k8sinterface.GetCurrentContext()
+ if context == nil {
+ return ""
+ }
+ cluster := context.Cluster
+ if cluster != "" {
+ return cluster
+ }
+ return k8sinterface.GetClusterName()
+
+}
+
+func (gkeProviderContext *GKEProviderContext) getRegion(cluster string, provider string) (string, error) {
+ return gkeProviderContext.getRegionForGKE(cluster)
+}
+
+func (gkeProviderContext *GKEProviderContext) getProject(cluster string, provider string) (string, error) {
+ return gkeProviderContext.getProjectForGKE(cluster)
+}
+
+func (gkeProviderContext *GKEProviderContext) getProjectForGKE(cluster string) (string, error) {
+ parsedName := strings.Split(cluster, "_")
+ if len(parsedName) < 3 {
+ return "", fmt.Errorf("failed to parse project name from cluster name: '%s'", cluster)
+ }
+ project := parsedName[1]
+ return project, nil
+}
+
+func (gkeProviderContext *GKEProviderContext) getRegionForGKE(cluster string) (string, error) {
+ parsedName := strings.Split(cluster, "_")
+ if len(parsedName) < 3 {
+ return "", fmt.Errorf("failed to parse region name from cluster name: '%s'", cluster)
+ }
+ region := parsedName[2]
+ return region, nil
+}
diff --git a/resourcehandler/k8sresources.go b/resourcehandler/k8sresources.go
index aa308f76..6a21f1fd 100644
--- a/resourcehandler/k8sresources.go
+++ b/resourcehandler/k8sresources.go
@@ -193,11 +193,21 @@ func (k8sHandler *K8sResourceHandler) collectRbacResources(allResources map[stri
}
func getCloudProviderDescription(allResources map[string]workloadinterface.IMetadata, k8sResourcesMap *cautils.K8SResources) error {
- if cloudsupport.IsRunningInCloudProvider() {
- wl, err := cloudsupport.GetDescriptiveInfoFromCloudProvider()
+ cloudProvider := initCloudProvider()
+ cluster := cloudProvider.getKubeCluster()
+ clusterName := cloudProvider.getKubeClusterName()
+ provider := getCloudProvider()
+ region, err := cloudProvider.getRegion(cluster, provider)
+ if err != nil {
+ return err
+ }
+ project, err := cloudProvider.getProject(cluster, provider)
+ if err != nil {
+ return err
+ }
+ if provider != "" {
+ wl, err := cloudsupport.GetDescriptiveInfoFromCloudProvider(clusterName, provider, region, project)
if err != nil {
- cluster := k8sinterface.GetCurrentContext().Cluster
- provider := cloudsupport.GetCloudProvider(cluster)
// Return error with useful info on how to configure credentials for getting cloud provider info
switch provider {
case "gke":
diff --git a/resultshandling/printer/v1/junit.go b/resultshandling/printer/v1/junit.go
index 8ec5f17c..749b5d85 100644
--- a/resultshandling/printer/v1/junit.go
+++ b/resultshandling/printer/v1/junit.go
@@ -107,7 +107,7 @@ func convertPostureReportToJunitResult(postureResult *reporthandling.PostureRepo
testCase.Name = controlReports.Name
testCase.Classname = "Kubescape"
testCase.Time = postureResult.ReportGenerationTime.String()
- if 0 < len(controlReports.RuleReports[0].RuleResponses) {
+ if len(controlReports.RuleReports) > 0 {
testCase.Resources = controlReports.GetNumberOfResources()
testCase.Excluded = controlReports.GetNumberOfWarningResources()
diff --git a/resultshandling/printer/v1/summeryhelpers.go b/resultshandling/printer/v1/summeryhelpers.go
index 93c8fb2f..cff9c74d 100644
--- a/resultshandling/printer/v1/summeryhelpers.go
+++ b/resultshandling/printer/v1/summeryhelpers.go
@@ -22,7 +22,7 @@ func groupByNamespaceOrKind(resources []WorkloadSummary, status func(workloadSum
case workloadinterface.TypeWorkloadObject:
ns := ""
if resources[i].resource.GetNamespace() != "" {
- ns = "Namescape " + resources[i].resource.GetNamespace()
+ ns = "Namespace " + resources[i].resource.GetNamespace()
}
if r, ok := mapResources[ns]; ok {
r = append(r, resources[i])
diff --git a/resultshandling/printer/v2/controlmapping/prettyprinter.go b/resultshandling/printer/v2/controlmapping/prettyprinter.go
index bbb3a7e2..dec73734 100644
--- a/resultshandling/printer/v2/controlmapping/prettyprinter.go
+++ b/resultshandling/printer/v2/controlmapping/prettyprinter.go
@@ -49,10 +49,7 @@ func (prettyPrinter *PrettyPrinter) printResults(controls *reportsummary.Control
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)
- }
+ prettyPrinter.printSummary(prettyPrinter.sortedControlNames[i], controlSummary)
}
}
diff --git a/resultshandling/printer/v2/controlmapping/summeryhelpers.go b/resultshandling/printer/v2/controlmapping/summeryhelpers.go
index ce74551d..cc606f8a 100644
--- a/resultshandling/printer/v2/controlmapping/summeryhelpers.go
+++ b/resultshandling/printer/v2/controlmapping/summeryhelpers.go
@@ -40,7 +40,7 @@ func groupByNamespaceOrKind(resources []WorkloadSummary, status func(workloadSum
case workloadinterface.TypeWorkloadObject:
ns := ""
if resources[i].resource.GetNamespace() != "" {
- ns = "Namescape " + resources[i].resource.GetNamespace()
+ ns = "Namespace " + resources[i].resource.GetNamespace()
}
if r, ok := mapResources[ns]; ok {
r = append(r, resources[i])
diff --git a/resultshandling/printer/v2/utils.go b/resultshandling/printer/v2/utils.go
index 304301da..c432bcac 100644
--- a/resultshandling/printer/v2/utils.go
+++ b/resultshandling/printer/v2/utils.go
@@ -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,13 +30,15 @@ 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{
- ResourceID: resourceID,
- Object: allResources[resourceID],
+ if obj, ok := allResources[resourceID]; ok {
+ r := *reporthandling.NewResource(obj.GetObject())
+ r.ResourceID = resourceID
+ resources[index] = r
}
+
index++
}
}
diff --git a/resultshandling/reporter/v1/reporteventreceiver.go b/resultshandling/reporter/v1/reporteventreceiver.go
index 3d7bf16b..853700ef 100644
--- a/resultshandling/reporter/v1/reporteventreceiver.go
+++ b/resultshandling/reporter/v1/reporteventreceiver.go
@@ -38,14 +38,16 @@ func NewReportEventReceiver(tenantConfig *cautils.ConfigObj) *ReportEventReceive
}
func (report *ReportEventReceiver) ActionSendReport(opaSessionObj *cautils.OPASessionObj) error {
- cautils.ReportV2ToV1(opaSessionObj)
+ if opaSessionObj.PostureReport == nil && opaSessionObj.Report != nil {
+ cautils.ReportV2ToV1(opaSessionObj)
+ }
if report.customerGUID == "" {
report.message = "WARNING: Failed to publish results. Reason: Unknown accout ID. Run kubescape with the '--account ' 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 ' flag"
+ report.message = "WARNING: Failed to publish results because the cluster name is Unknown. If you are scanning YAML files the results are not submitted to the Kubescape SaaS"
return nil
}
diff --git a/resultshandling/reporter/v2/reporteventreceiver.go b/resultshandling/reporter/v2/reporteventreceiver.go
index 71f681df..03664b97 100644
--- a/resultshandling/reporter/v2/reporteventreceiver.go
+++ b/resultshandling/reporter/v2/reporteventreceiver.go
@@ -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"
)
@@ -45,7 +46,7 @@ func (report *ReportEventReceiver) ActionSendReport(opaSessionObj *cautils.OPASe
return nil
}
if report.clusterName == "" {
- report.message = "WARNING: Failed to publish results. Reason: Unknown cluster name. Run kubescape with the '--cluster ' flag"
+ report.message = "WARNING: Failed to publish results because the cluster name is Unknown. If you are scanning YAML files the results are not submitted to the Kubescape SaaS"
return nil
}
opaSessionObj.Report.ReportID = uuid.NewV4().String()
@@ -77,27 +78,21 @@ func (report *ReportEventReceiver) prepareReport(postureReport *reporthandlingv2
reportCounter := 0
- // send results
- if err := report.sendResults(host, postureReport, &reportCounter); err != nil {
- return err
- }
- reportCounter++
-
// send resources
- if err := report.sendResources(host, postureReport, &reportCounter); err != nil {
+ if err := report.sendResources(host, postureReport, &reportCounter, false); err != nil {
return err
}
- reportCounter++
+ // reportCounter++
- // send framework results
- if err := report.sendSummary(host, postureReport, &reportCounter); err != nil {
- return err
- }
+ // // send results
+ // if err := report.sendResults(host, postureReport, &reportCounter, true); err != nil {
+ // return err
+ // }
return nil
}
-func (report *ReportEventReceiver) sendResources(host string, postureReport *reporthandlingv2.PostureReport, reportCounter *int) error {
+func (report *ReportEventReceiver) sendResources(host string, postureReport *reporthandlingv2.PostureReport, reportCounter *int, isLastReport bool) error {
splittedPostureReport := setSubReport(postureReport)
counter := 0
@@ -116,7 +111,8 @@ func (report *ReportEventReceiver) sendResources(host string, postureReport *rep
*reportCounter++
// delete resources
- splittedPostureReport.Resources = []reporthandlingv2.Resource{}
+ splittedPostureReport.Resources = []reporthandling.Resource{}
+ splittedPostureReport.Results = []resourcesresults.Result{}
// restart counter
counter = 0
@@ -126,20 +122,13 @@ func (report *ReportEventReceiver) sendResources(host string, postureReport *rep
splittedPostureReport.Resources = append(splittedPostureReport.Resources, v)
}
- return report.sendReport(host, splittedPostureReport, *reportCounter, false)
-}
-
-func (report *ReportEventReceiver) sendResults(host string, postureReport *reporthandlingv2.PostureReport, reportCounter *int) error {
- splittedPostureReport := setSubReport(postureReport)
- counter := 0
-
for _, v := range postureReport.Results {
r, err := json.Marshal(v)
if err != nil {
return fmt.Errorf("failed to unmarshal resource '%s', reason: %v", v.GetResourceID(), err)
}
- if counter+len(r) >= MAX_REPORT_SIZE && len(splittedPostureReport.Resources) > 0 {
+ if counter+len(r) >= MAX_REPORT_SIZE && len(splittedPostureReport.Results) > 0 {
// send report
if err := report.sendReport(host, splittedPostureReport, *reportCounter, false); err != nil {
@@ -149,6 +138,7 @@ func (report *ReportEventReceiver) sendResults(host string, postureReport *repor
// delete results
splittedPostureReport.Results = []resourcesresults.Result{}
+ splittedPostureReport.Resources = []reporthandling.Resource{}
// restart counter
counter = 0
@@ -158,16 +148,46 @@ func (report *ReportEventReceiver) sendResults(host string, postureReport *repor
splittedPostureReport.Results = append(splittedPostureReport.Results, v)
}
- return report.sendReport(host, splittedPostureReport, *reportCounter, false)
-}
-
-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) sendResults(host string, postureReport *reporthandlingv2.PostureReport, reportCounter *int, isLastReport bool) error {
+// splittedPostureReport := setSubReport(postureReport)
+// counter := 0
+
+// for _, v := range postureReport.Results {
+// r, err := json.Marshal(v)
+// if err != nil {
+// return fmt.Errorf("failed to unmarshal resource '%s', reason: %v", v.GetResourceID(), err)
+// }
+
+// if counter+len(r) >= MAX_REPORT_SIZE && len(splittedPostureReport.Resources) > 0 {
+
+// // send report
+// if err := report.sendReport(host, splittedPostureReport, *reportCounter, false); err != nil {
+// return err
+// }
+// *reportCounter++
+
+// // delete results
+// splittedPostureReport.Results = []resourcesresults.Result{}
+
+// // restart counter
+// counter = 0
+// }
+
+// counter += len(r)
+// splittedPostureReport.Results = append(splittedPostureReport.Results, v)
+// }
+
+// return report.sendReport(host, splittedPostureReport, *reportCounter, isLastReport)
+// }
+
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)
diff --git a/resultshandling/reporter/v2/reporteventreceiverutils.go b/resultshandling/reporter/v2/reporteventreceiverutils.go
index 4740d2c9..66c823f3 100644
--- a/resultshandling/reporter/v2/reporteventreceiverutils.go
+++ b/resultshandling/reporter/v2/reporteventreceiverutils.go
@@ -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 {
diff --git a/resultshandling/reporter/v2/reporteventreceiverutils_test.go b/resultshandling/reporter/v2/reporteventreceiverutils_test.go
index f788a0f1..efd1f512 100644
--- a/resultshandling/reporter/v2/reporteventreceiverutils_test.go
+++ b/resultshandling/reporter/v2/reporteventreceiverutils_test.go
@@ -9,10 +9,10 @@ func TestHostToString(t *testing.T) {
host := url.URL{
Scheme: "https",
Host: "report.eudev3.cyberarmorsoft.com",
- Path: "k8srestapi/v1/postureReport",
+ Path: "k8srestapi/v2/postureReport",
RawQuery: "cluster=openrasty_seal-7fvz&customerGUID=5d817063-096f-4d91-b39b-8665240080af",
}
- expectedHost := "https://report.eudev3.cyberarmorsoft.com/k8srestapi/v1/postureReport?cluster=openrasty_seal-7fvz&customerGUID=5d817063-096f-4d91-b39b-8665240080af&reportID=ffdd2a00-4dc8-4bf3-b97a-a6d4fd198a41"
+ expectedHost := "https://report.eudev3.cyberarmorsoft.com/k8srestapi/v2/postureReport?cluster=openrasty_seal-7fvz&customerGUID=5d817063-096f-4d91-b39b-8665240080af&reportGUID=ffdd2a00-4dc8-4bf3-b97a-a6d4fd198a41"
receivedHost := hostToString(&host, "ffdd2a00-4dc8-4bf3-b97a-a6d4fd198a41")
if receivedHost != expectedHost {
t.Errorf("%s != %s", receivedHost, expectedHost)
diff --git a/resultshandling/reporter/v2/utils.go b/resultshandling/reporter/v2/utils.go
index 0f3ebc03..e50713d9 100644
--- a/resultshandling/reporter/v2/utils.go
+++ b/resultshandling/reporter/v2/utils.go
@@ -5,8 +5,8 @@ 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
@@ -19,7 +19,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
}
@@ -33,13 +33,15 @@ 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{
- ResourceID: resourceID,
- Object: allResources[resourceID],
+ if obj, ok := allResources[resourceID]; ok {
+ r := *reporthandling.NewResource(obj.GetObject())
+ r.ResourceID = resourceID
+ resources[index] = r
}
+
index++
}
}
diff --git a/resultshandling/results.go b/resultshandling/results.go
index 5dea6e4f..b20d9bb2 100644
--- a/resultshandling/results.go
+++ b/resultshandling/results.go
@@ -33,12 +33,7 @@ func (resultsHandler *ResultsHandler) HandleResults(scanInfo *cautils.ScanInfo)
fmt.Println(err)
}
- // TODO - get score from table
- var score float32 = 0
- for i := range opaSessionObj.PostureReport.FrameworkReports {
- score += opaSessionObj.PostureReport.FrameworkReports[i].Score
- }
- score /= float32(len(opaSessionObj.PostureReport.FrameworkReports))
+ score := opaSessionObj.Report.SummaryDetails.Score
resultsHandler.printerObj.Score(score)
return score
diff --git a/score/score.go b/score/score.go
new file mode 100644
index 00000000..6e78d858
--- /dev/null
+++ b/score/score.go
@@ -0,0 +1,43 @@
+package score
+
+import (
+ "fmt"
+
+ "github.com/armosec/opa-utils/score"
+
+ "github.com/armosec/kubescape/cautils"
+)
+
+/* provides a wrapper for scoreUtils, since there's no common interface between postureReportV1 and PostureReportV2
+and the need of concrete objects
+ I've decided to create scoreWrapper that will allow calculating score regardless (as long as opaSessionObj is there)
+*/
+type ScoreWrapper struct {
+ scoreUtil *score.ScoreUtil
+ opaSessionObj *cautils.OPASessionObj
+}
+
+type PostureReportVersion string
+
+const (
+ EPostureReportV1 PostureReportVersion = "v1"
+ EPostureReportV2 PostureReportVersion = "V2"
+)
+
+func (su *ScoreWrapper) Calculate(reportVersion PostureReportVersion) error {
+ switch reportVersion {
+ case EPostureReportV1:
+ return su.scoreUtil.Calculate(su.opaSessionObj.PostureReport.FrameworkReports)
+ case EPostureReportV2:
+ return su.scoreUtil.CalculatePostureReportV2(su.opaSessionObj.Report)
+ }
+
+ return fmt.Errorf("unsupported score calculator")
+}
+
+func NewScoreWrapper(opaSessionObj *cautils.OPASessionObj) *ScoreWrapper {
+ return &ScoreWrapper{
+ scoreUtil: score.NewScore(opaSessionObj.AllResources),
+ opaSessionObj: opaSessionObj,
+ }
+}
diff --git a/score/score_test.go b/score/score_test.go
new file mode 100644
index 00000000..323f8eed
--- /dev/null
+++ b/score/score_test.go
@@ -0,0 +1 @@
+package score