mirror of
https://github.com/kubescape/kubescape.git
synced 2026-02-26 07:43:51 +00:00
Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
033e8f6b44 | ||
|
|
bef40f0e6c | ||
|
|
aa2f69125f | ||
|
|
d30f3960a7 | ||
|
|
5f43da94ba | ||
|
|
2aa8a0c935 | ||
|
|
c02f8c6cb5 | ||
|
|
aa0be474e2 | ||
|
|
c0161c9b33 | ||
|
|
514da1e2db | ||
|
|
75dfceb5da | ||
|
|
1ae76b4377 | ||
|
|
b6f90cba8e | ||
|
|
62af441a1d | ||
|
|
228b8957d3 | ||
|
|
b4ce999ab3 | ||
|
|
cc06a414fe | ||
|
|
d3c37c4e5f | ||
|
|
3b448b62b1 | ||
|
|
6a3f5658b1 | ||
|
|
f65e791522 | ||
|
|
d91304f9ad | ||
|
|
61ce00108e | ||
|
|
a4eb773eee | ||
|
|
cfc69f5a0f | ||
|
|
a44823c3ed | ||
|
|
8a166e5ba5 | ||
|
|
9a7aeff870 | ||
|
|
cb3bdb9df2 | ||
|
|
0be8d57eaa | ||
|
|
79b9cbf1d6 | ||
|
|
500df8737e | ||
|
|
b8acbd1bee | ||
|
|
0bde8a65ba | ||
|
|
d2884b8936 | ||
|
|
e692359b47 | ||
|
|
473746eab0 | ||
|
|
050878cbd6 | ||
|
|
e100f18bb0 | ||
|
|
05c82fc166 | ||
|
|
839c3e261f | ||
|
|
95b579d191 | ||
|
|
8656715753 | ||
|
|
05b6394c5c | ||
|
|
d3bdbf31ac | ||
|
|
995f615b10 | ||
|
|
392625b774 | ||
|
|
3f6cbd57b2 | ||
|
|
2c9524ed45 | ||
|
|
384922680a | ||
|
|
d2e9f8f4f8 | ||
|
|
b4f10f854e | ||
|
|
8ce64d2a7f | ||
|
|
2bb612ca3f | ||
|
|
35534112c6 | ||
|
|
f51e531f3a | ||
|
|
2490856ccb | ||
|
|
9a5a87b027 | ||
|
|
45b8c89865 |
10
.github/workflows/build.yaml
vendored
10
.github/workflows/build.yaml
vendored
@@ -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 }}
|
||||
|
||||
6
.github/workflows/build_dev.yaml
vendored
6
.github/workflows/build_dev.yaml
vendored
@@ -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 }}
|
||||
|
||||
4
.github/workflows/master_pr_checks.yaml
vendored
4
.github/workflows/master_pr_checks.yaml
vendored
@@ -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
|
||||
|
||||
127
CODE_OF_CONDUCT.md
Normal file
127
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement [here](mailto:ben@armosec.io).
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
9
MAINTAINERS.md
Normal file
9
MAINTAINERS.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Maintainers
|
||||
|
||||
The following table lists Kubescape project maintainers
|
||||
|
||||
| Name | GitHub | Email | Organization | Repositories/Area of Expertise | Added/Renewed On |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| Ben Hirschberg | @slashben | ben@armosec.io | ARMO | Kubescape CLI | 2021-09-01 |
|
||||
| Rotem Refael | @rotemamsa | rrefael@armosec.io | ARMO | Kubescape CLI | 2021-10-11 |
|
||||
| David Wertenteil | @dwertent | dwertent@armosec.io | ARMO | Kubescape CLI | 2021-09-01 |
|
||||
45
README.md
45
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.
|
||||
|
||||
</br>
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
<img src="docs/summary.png">
|
||||
@@ -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/<framework name>.json`
|
||||
1. Download and save in file, if file name not specified, will save in `~/.kubescape/<framework name>.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 ❤️
|
||||
<a href = "https://github.com/armosec/kubescape/graphs/contributors">
|
||||
<img src = "https://contrib.rocks/image?repo=armosec/kubescape"/>
|
||||
</a>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"`)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
23
docs/roadmap.md
Normal file
23
docs/roadmap.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Kubescape project roadmap
|
||||
|
||||
|
||||
## Proposals
|
||||
* [Container registry integration](/docs/proposals/container-image-vulnerability-adaptor.md)
|
||||
|
||||
## Planed features
|
||||
* Image vulnerablity scanning based controls
|
||||
* Assited remidiation (telling where/what to fix)
|
||||
* Git integration for pull requests
|
||||
* Integration with container registries
|
||||
* Custom controls and regos
|
||||
* API server configuration validation
|
||||
* Kubelet configuration validation
|
||||
|
||||
## Completed features
|
||||
* Integration with Prometheus
|
||||
* Confiugration of controls (customizing rules for a given environment)
|
||||
* Installation in the cluster for continous monitoring
|
||||
* Host scanner
|
||||
* Cloud vendor API integration
|
||||
* Custom exceptions
|
||||
* Custom frameworks
|
||||
68
examples/cloud-vendor-integration/aws.sh
Executable file
68
examples/cloud-vendor-integration/aws.sh
Executable file
@@ -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 <<EOF
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "eks:DescribeCluster",
|
||||
"Resource": "$cluster_arn"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
)")
|
||||
|
||||
# Create Kubernetes Kubescape service account, and AWS IAM attachment role
|
||||
echo 'Create Kubernetes Kubescape service account, and AWS IAM attachment role'
|
||||
eksctl create iamserviceaccount \
|
||||
--name $kubescape_serviceaccount \
|
||||
--namespace $kubescape_namespace \
|
||||
--cluster $cluster_name \
|
||||
--attach-policy-arn $kubescape_policy_arn \
|
||||
--approve \
|
||||
--override-existing-serviceaccounts
|
||||
|
||||
# Install/Upgrade Kubescape chart
|
||||
echo 'Install/Upgrade Kubescape chart'
|
||||
helm upgrade --install armo armo-components/ -n armo-system --create-namespace --set clusterName=$cluster_name --set cloud_provider_engine=eks --set createKubescapeServiceAccount=false --set cloudRegion=$cluster_region
|
||||
49
examples/cloud-vendor-integration/gcp.sh
Executable file
49
examples/cloud-vendor-integration/gcp.sh
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
# GCP
|
||||
# Attach the Kubescape service account to a GCP service account with the get cluster permission
|
||||
|
||||
# Prerequisites:
|
||||
# gcloud
|
||||
# Workload Identity enabled on the cluster.
|
||||
# Node pool with --workload-metadata=GKE_METADATA where the pod can run.
|
||||
# CLUSTER_NAME and CLUSTER_REGION environment variables.
|
||||
|
||||
[ -z $CLUSTER_NAME ] && >&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
|
||||
11
go.mod
11
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/briandowns/spinner v1.18.0
|
||||
github.com/enescakir/emoji v1.0.0
|
||||
@@ -19,6 +19,7 @@ require (
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/spf13/cobra v1.2.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
go.uber.org/zap v1.19.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
k8s.io/api v0.22.2
|
||||
k8s.io/apimachinery v0.22.2
|
||||
@@ -35,6 +36,7 @@ require (
|
||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/OneOfOne/xxhash v1.2.8 // indirect
|
||||
github.com/armosec/armo-interfaces v0.0.3 // indirect
|
||||
github.com/armosec/utils-k8s-go v0.0.1 // indirect
|
||||
github.com/aws/aws-sdk-go v1.41.11 // indirect
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible // indirect
|
||||
@@ -76,7 +78,6 @@ require (
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.19.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 // indirect
|
||||
|
||||
18
go.sum
18
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=
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
104
resourcehandler/cloudproviderhandler.go
Normal file
104
resourcehandler/cloudproviderhandler.go
Normal file
@@ -0,0 +1,104 @@
|
||||
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":
|
||||
return NewGKEProviderContext()
|
||||
case "eks", "aws":
|
||||
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
|
||||
}
|
||||
95
resourcehandler/ekssupport.go
Normal file
95
resourcehandler/ekssupport.go
Normal file
@@ -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
|
||||
}
|
||||
125
resourcehandler/gkesupport.go
Normal file
125
resourcehandler/gkesupport.go
Normal file
@@ -0,0 +1,125 @@
|
||||
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("error: failed to parse cluster name")
|
||||
}
|
||||
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("error: failed to parse cluster name")
|
||||
}
|
||||
region = parsedName[2]
|
||||
return region, nil
|
||||
|
||||
}
|
||||
|
||||
// ------------------------------ GKEProviderContext --------------------------------------------------------
|
||||
|
||||
type GKEProviderContext struct {
|
||||
}
|
||||
|
||||
func NewGKEProviderContext() *GKEProviderContext {
|
||||
return &GKEProviderContext{}
|
||||
}
|
||||
|
||||
func (gkeProviderContext *GKEProviderContext) getKubeClusterName() string {
|
||||
cluster := k8sinterface.GetCurrentContext().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 {
|
||||
cluster := k8sinterface.GetCurrentContext().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("error: failed to parse cluster name")
|
||||
}
|
||||
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("error: failed to parse cluster name")
|
||||
}
|
||||
region := parsedName[2]
|
||||
return region, nil
|
||||
}
|
||||
@@ -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":
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <account ID>' flag. Contact ARMO team for more details"
|
||||
return nil
|
||||
}
|
||||
if report.clusterName == "" {
|
||||
report.message = "WARNING: Failed to publish results. Reason: Unknown cluster name. Run kubescape with the '--cluster <cluster name>' flag"
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <cluster name>' 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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,15 +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))
|
||||
resultsHandler.printerObj.Score(score)
|
||||
|
||||
return score
|
||||
return opaSessionObj.Report.SummaryDetails.Score
|
||||
}
|
||||
|
||||
// CalculatePostureScore calculate final score
|
||||
|
||||
43
score/score.go
Normal file
43
score/score.go
Normal file
@@ -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,
|
||||
}
|
||||
}
|
||||
1
score/score_test.go
Normal file
1
score/score_test.go
Normal file
@@ -0,0 +1 @@
|
||||
package score
|
||||
Reference in New Issue
Block a user