Compare commits

..

167 Commits

Author SHA1 Message Date
David Wertenteil
20d65f2ed3 Merge remote-tracking branch 'armosec/dev' 2022-05-03 14:27:39 +03:00
David Wertenteil
46a559fb1d reprot ks version 2022-05-03 14:23:23 +03:00
David Wertenteil
2769b22721 Merge pull request #499 from dwertent/master
return response object
2022-05-03 13:28:02 +03:00
David Wertenteil
63520f9aff return response object 2022-05-02 15:28:29 +03:00
David Wertenteil
333b55a9f2 Merge pull request #497 from Daniel-GrunbergerCA/master
support fixCommand
2022-05-02 13:35:49 +03:00
DanielGrunbergerCA
c6d3fd1a82 check that fixCOmmand is not nul 2022-05-02 13:26:15 +03:00
DanielGrunbergerCA
8106133ed0 go mod 2022-05-02 12:15:25 +03:00
DanielGrunbergerCA
b36111f63e Merge remote-tracking branch 'upstream/dev' 2022-05-02 12:10:13 +03:00
DanielGrunbergerCA
3ad0284394 support fixCOmmand 2022-05-02 11:50:12 +03:00
shm12
245ebf8c41 Merge pull request #496 from dwertent/master
fixed saving error files
2022-05-02 10:42:28 +03:00
David Wertenteil
8309562da1 fixed saving error files 2022-05-01 22:33:37 +03:00
David Wertenteil
de807a65a6 Merge pull request #492 from dwertent/master
Update docker build
2022-04-28 13:50:27 +03:00
David Wertenteil
92fe583421 Merge remote-tracking branch 'armosec/dev' 2022-04-28 13:04:20 +03:00
David Wertenteil
b7ec05e88a adding tests 2022-04-28 13:03:51 +03:00
Daniel Grunberger
203e925888 Merge pull request #491 from dwertent/master
Minor microservice features
2022-04-27 15:28:35 +03:00
David Wertenteil
fde5453bf3 docker user name 2022-04-27 15:25:27 +03:00
David Wertenteil
4c6a65565b support keep in query 2022-04-26 09:31:48 +03:00
David Wertenteil
e60ecfb8f5 Merge pull request #488 from Daniel-GrunbergerCA/master
Check for empty k8s resources in controls related to armo resources
2022-04-25 14:28:52 +03:00
DanielGrunbergerCA
b72e2610ca check tat control is not nil 2022-04-25 12:14:48 +03:00
DanielGrunbergerCA
8d4bae06bc check that control is present 2022-04-25 12:11:05 +03:00
DanielGrunbergerCA
847b597d0f use iface 2022-04-25 09:32:28 +03:00
David Wertenteil
db1743f617 update docker user name 2022-04-19 16:04:02 +03:00
David Wertenteil
7ac1b8aacf return resp object from http req 2022-04-17 14:40:24 +03:00
David Wertenteil
55f8cb1f0e Merge pull request #489 from dwertent/master
Submit repo metadata in context
2022-04-17 08:10:13 +03:00
David Wertenteil
93574736cd update http handler 2022-04-14 11:05:45 +03:00
David Wertenteil
e43f4b1a37 update go mod 2022-04-13 15:47:26 +03:00
David Wertenteil
4ba33578ce sent git repo context 2022-04-13 15:31:48 +03:00
DanielGrunbergerCA
ae00866005 Merge remote-tracking branch 'upstream/dev' 2022-04-13 11:31:03 +03:00
DanielGrunbergerCA
21cb4dae29 fix skipped for controlsd which use both armo and k8s resources 2022-04-13 11:30:51 +03:00
Daniel Grunberger
7d3ac98998 Merge pull request #486 from dwertent/master
removed binary
2022-04-11 16:52:43 +03:00
David Wertenteil
5e9d01aec2 removed binary 2022-04-11 16:45:03 +03:00
Daniel Grunberger
c09eabf347 Merge pull request #483 from dwertent/master
update version check
2022-04-11 13:28:25 +03:00
David Wertenteil
38c2aed74a update version check 2022-04-11 12:16:44 +03:00
David Wertenteil
cf70671dba Merge pull request #482 from Daniel-GrunbergerCA/master
Delete cloud logic from kubescape
2022-04-11 10:57:28 +03:00
Daniel Grunberger
f90ce83a74 Merge pull request #481 from dwertent/master
fixed submit url
2022-04-11 10:56:51 +03:00
DanielGrunbergerCA
fab594ee32 rm context name 2022-04-11 10:56:30 +03:00
DanielGrunbergerCA
d25cefe355 rm error msg 2022-04-11 10:49:51 +03:00
DanielGrunbergerCA
747eee1d29 merge 2022-04-11 10:13:41 +03:00
DanielGrunbergerCA
0c43ee9ab8 Merge remote-tracking branch 'upstream/dev' 2022-04-11 10:13:20 +03:00
DanielGrunbergerCA
466f3acd71 change to ge contextname 2022-04-11 09:49:32 +03:00
DanielGrunbergerCA
80add4ef12 Merge branch 'errors-fix' 2022-04-11 09:18:59 +03:00
David Wertenteil
959319c335 fixed submit url 2022-04-11 09:12:30 +03:00
Rotem Refael
0e9ca547cb Merge pull request #480 from dwertent/master
update pkg name to v2
2022-04-11 08:35:18 +03:00
David Wertenteil
6e17e5ce7e Call cmd pkg from root 2022-04-11 07:53:22 +03:00
David Wertenteil
858d7ac2ef update pkg struct 2022-04-10 18:06:28 +03:00
Rotem Refael
4046321297 Merge pull request #467 from dwertent/master
support yaml scan submit
update metrics
update cmd format
2022-04-10 17:03:21 +03:00
David Wertenteil
0cfdabd25a remove v1 format 2022-04-10 16:26:59 +03:00
David Wertenteil
8b6cb6c5d8 adding logs 2022-04-10 11:55:35 +03:00
David Wertenteil
3df3b7766c save policy in file 2022-04-10 09:33:44 +03:00
David Wertenteil
0d83654197 update resource table 2022-04-07 16:44:56 +03:00
David Wertenteil
bd9e44382e format v2 2022-04-07 13:54:49 +03:00
David Wertenteil
2a7c20ea94 report format version 2022-04-07 11:59:19 +03:00
David Wertenteil
bde0dc9a17 convert score to int 2022-04-07 11:00:39 +03:00
David Wertenteil
7d7336ae01 support only one input 2022-04-07 09:53:44 +03:00
David Wertenteil
e5e608324d send file meta 2022-04-06 16:11:57 +03:00
David Wertenteil
569c1444f7 Merge remote-tracking branch 'armosec/dev' 2022-04-06 09:45:32 +03:00
dwertent
dc5ef28324 update output 2022-04-06 09:29:28 +03:00
David Wertenteil
aea6c0eab8 Merge pull request #477 from Daniel-GrunbergerCA/master
Fix error msg for cloud
2022-04-06 09:23:12 +03:00
DanielGrunbergerCA
c80a15d0cf Merge remote-tracking branch 'upstream/dev' 2022-04-05 17:19:11 +03:00
DanielGrunbergerCA
1ddd57aa1d fix error msg for cloud 2022-04-05 17:18:50 +03:00
David Wertenteil
55adb0da6b Merge pull request #474 from Daniel-GrunbergerCA/master
Fix posture control inputs for yaml scan
2022-04-05 16:28:39 +03:00
DanielGrunbergerCA
a716289cc8 go mod 2022-04-05 14:10:11 +03:00
DanielGrunbergerCA
093d71fff4 exit status 1 2022-04-05 14:05:31 +03:00
DanielGrunbergerCA
4fe40e348d Merge branch 'master' of github.com:Daniel-GrunbergerCA/kubescape 2022-04-05 13:40:41 +03:00
DanielGrunbergerCA
29a67b806d Merge remote-tracking branch 'upstream/dev' 2022-04-05 13:39:39 +03:00
DanielGrunbergerCA
0169f42747 Merge remote-tracking branch 'upstream/master' 2022-04-05 13:39:28 +03:00
DanielGrunbergerCA
ed45b09241 Merge remote-tracking branch 'upstream/dev' into errors-fix 2022-04-05 09:25:56 +03:00
DanielGrunbergerCA
107903cc99 Merge branch 'dev' into fix-errors 2022-04-05 09:24:32 +03:00
David Wertenteil
92e100c497 Merge branch 'dev' into master 2022-04-04 13:39:17 +03:00
DanielGrunbergerCA
536fe970f7 fix go mod 2022-04-04 12:31:45 +03:00
DanielGrunbergerCA
85526b06b6 fix control input for yaml scan 2022-04-04 12:27:30 +03:00
dwertent
d9b6c048d5 Merge remote-tracking branch 'armosec/dev' 2022-04-04 12:14:45 +03:00
David Wertenteil
7e46a6529a Merge pull request #473 from Lucifergene/dev
Added Severity Column with colored text
2022-04-04 12:14:35 +03:00
dwertent
2dc5fd80da Merge remote-tracking branch 'armosec/dev' 2022-04-04 12:14:13 +03:00
dwertent
e89cc8ca24 register usesrs download from BE 2022-04-04 12:13:35 +03:00
dwertent
d39aeb0691 update metrics 2022-04-04 11:32:56 +03:00
Lucifergene
da9d98134a Added Severity Column with colored text 2022-04-04 03:46:05 +05:30
David Wertenteil
9992a9a0e4 Merge pull request #472 from falconcode16/master
Improved grammatical mistakes and typos
2022-04-03 10:03:15 +03:00
Rohit Patil
adc8a16e85 Improved grammatical mistakes and typos 2022-04-03 10:40:56 +05:30
DanielGrunbergerCA
13fb586ded Merge remote-tracking branch 'upstream/dev' into dev 2022-03-30 17:37:01 +03:00
DanielGrunbergerCA
2e63982f5a mv cloud logic to k8s-interface 2022-03-30 17:36:15 +03:00
dwertent
58b833c18a support yaml scan submit 2022-03-28 09:40:11 +03:00
Rotem Refael
cb424eab00 Merge pull request #461 from armosec/dev
Microservice support
2022-03-27 17:20:35 +03:00
Rotem Refael
9f2e18c3ee Merge pull request #466 from dwertent/master
udpate armo api types
2022-03-27 17:09:52 +03:00
dwertent
b44a73aea5 udpate armo api types 2022-03-27 17:07:17 +03:00
Rotem Refael
9c5759286f Merge pull request #465 from dwertent/master
Update url support
2022-03-27 16:29:01 +03:00
dwertent
74dc714736 use scan ID 2022-03-27 15:51:29 +03:00
dwertent
83751e22cc fixed report sending 2022-03-27 15:37:11 +03:00
dwertent
db5fdd75c4 inserting scan source 2022-03-25 16:08:07 +03:00
dwertent
4be2104d4b adding githubusercontent tests 2022-03-24 12:37:00 +02:00
dwertent
b6bab7618f loading github token from env 2022-03-24 12:15:54 +02:00
dwertent
3e1fda6f3b fixed test 2022-03-24 11:12:28 +02:00
dwertent
8487a031ee fixed url scanning 2022-03-24 09:45:35 +02:00
David Wertenteil
efbb123fce Merge pull request #464 from dwertent/master
update error display
2022-03-23 09:27:58 +02:00
dwertent
5a335d4f1c update error display 2022-03-23 09:05:36 +02:00
dwertent
5770a823d6 update readme 2022-03-23 08:47:24 +02:00
Rotem Refael
52d7be9108 Add kubescape covarage 2022-03-22 17:42:20 +02:00
David Wertenteil
9512b9c6c4 Merge pull request #463 from Daniel-GrunbergerCA/dev
fix json printer
2022-03-22 09:14:59 +02:00
DanielGrunbergerCA
da9ab642ec rm list initializtion 2022-03-22 09:07:55 +02:00
DanielGrunbergerCA
718ca1c7ab fix json printer 2022-03-22 08:55:36 +02:00
DanielGrunbergerCA
ee3742c5a0 update opa-utils version 2022-03-21 15:36:17 +02:00
DanielGrunbergerCA
7eef843a7a fix resources in report 2022-03-21 15:11:26 +02:00
David Wertenteil
b4a8b06f07 Merge pull request #462 from dwertent/master
update get context
2022-03-21 09:09:12 +02:00
dwertent
4e13609985 update get context 2022-03-21 09:08:01 +02:00
David Wertenteil
2e5e4328f6 Merge pull request #460 from dwertent/master
update readme
2022-03-20 11:30:37 +02:00
dwertent
d98a11a8fa udpate badges 2022-03-20 11:28:56 +02:00
dwertent
bdb25cbb66 adding vs code to readme 2022-03-20 11:08:48 +02:00
David Wertenteil
369804cb6e Merge pull request #459 from shm12/dev
send scan metadata
2022-03-20 10:45:19 +02:00
shm12
1b08a92095 send scan metadata 2022-03-20 10:05:40 +02:00
David Wertenteil
e787454d53 Merge pull request #458 from dwertent/master
fixed json output
2022-03-16 16:58:54 +02:00
dwertent
31d1ba663a json output 2022-03-16 16:58:05 +02:00
David Wertenteil
c3731d8ff6 Merge pull request #457 from dwertent/master
support frameworks from http request
2022-03-16 16:37:40 +02:00
dwertent
c5b46beb1a support frameworks from http request 2022-03-16 16:33:03 +02:00
dwertent
c5ca576c98 Merge remote-tracking branch 'armosec/dev' 2022-03-16 15:27:10 +02:00
dwertent
eae6458b42 fixed cmd init 2022-03-16 15:26:59 +02:00
David Wertenteil
aa1aa913b6 Merge pull request #456 from Daniel-GrunbergerCA/dev
Support status information
2022-03-16 15:17:49 +02:00
DanielGrunbergerCA
44084592cb fixes 2022-03-16 14:16:48 +02:00
DanielGrunbergerCA
6cacfb7b16 refactor 2022-03-16 12:22:21 +02:00
DanielGrunbergerCA
6372ce5647 Merge branch 'dev' 2022-03-16 12:13:16 +02:00
DanielGrunbergerCA
306d3a7081 fix table display 2022-03-16 12:12:39 +02:00
DanielGrunbergerCA
442530061f rm space 2022-03-16 12:09:53 +02:00
DanielGrunbergerCA
961a6f6ebc Merge remote-tracking branch 'upstream/dev' into dev 2022-03-16 12:08:38 +02:00
DanielGrunbergerCA
0d0c8e1b97 support status info 2022-03-16 12:08:04 +02:00
David Wertenteil
5b843ba2c4 Merge pull request #455 from dwertent/master
update prometheus format
2022-03-16 09:32:23 +02:00
dwertent
8f9b46cdbe update prometheus format 2022-03-16 09:25:45 +02:00
David Wertenteil
e16885a044 Merge pull request #452 from dwertent/master
update dockerfile and scan triggering
2022-03-15 22:12:31 +02:00
dwertent
06a2fa05be add ks user to dockerfile 2022-03-15 22:10:27 +02:00
dwertent
d26f90b98e update Prometheus yaml 2022-03-15 18:40:42 +02:00
David Wertenteil
b47c128eb3 Merge pull request #451 from dwertent/master
update prometheus format
2022-03-15 17:12:07 +02:00
dwertent
9d957b3c77 update output 2022-03-15 17:04:59 +02:00
dwertent
8ec5615569 junit format 2022-03-15 16:50:39 +02:00
David Wertenteil
fae73b827a Merge pull request #450 from dwertent/master
build each pkg
2022-03-14 19:27:47 +02:00
dwertent
6477437872 update readme 2022-03-14 19:14:31 +02:00
dwertent
6099f46dea adding docker build 2022-03-14 18:34:34 +02:00
Rotem Refael
5009e6ef47 change gif 2022-03-14 11:33:56 +02:00
Rotem Refael
c4450d3259 add web & CLI Interfaces 2022-03-14 11:27:57 +02:00
Rotem Refael
0c3339f1c9 update gif 2022-03-14 10:28:32 +02:00
Rotem Refael
faee3d5ad6 Add new video to readme 2022-03-14 10:14:55 +02:00
David Wertenteil
a279963b28 Merge pull request #449 from dwertent/master
split to packages
2022-03-13 19:37:02 +02:00
dwertent
f7c0c95d3b adding tests to build 2022-03-13 19:05:06 +02:00
dwertent
b8df07b547 fixed unitest 2022-03-13 18:59:39 +02:00
dwertent
d0e2730518 add cautils to core 2022-03-13 18:14:48 +02:00
dwertent
6dab82f01a fixed output format 2022-03-13 15:03:22 +02:00
dwertent
7a01116db5 adding ks interface 2022-03-13 10:59:38 +02:00
dwertent
8f1e4ceff0 split pkg 2022-03-13 09:59:57 +02:00
David Wertenteil
353a39d66a Merge pull request #448 from dwertent/master
microservice support
2022-03-10 17:17:35 +02:00
dwertent
66196c0d56 pass info in call 2022-03-10 17:13:51 +02:00
dwertent
2d59ba0943 update cmd struct 2022-03-10 17:02:09 +02:00
dwertent
33f92d1a5f update logger support 2022-03-10 11:19:05 +02:00
dwertent
4bd468f03e Merge remote-tracking branch 'armosec/dev' 2022-03-09 20:54:22 +02:00
dwertent
c6eaecd596 export to json 2022-03-09 20:53:55 +02:00
dwertent
a2a5b06024 adding http handler 2022-03-09 20:51:55 +02:00
David Wertenteil
825732f60f Merge pull request #444 from Akasurde/misspell_dev
Misc typo fixes
2022-03-09 20:12:19 +02:00
David Wertenteil
596ec17106 Merge pull request #445 from Akasurde/no_color_dev
cli: added support for no color
2022-03-09 20:01:24 +02:00
Abhijeet Kasurde
fbd0f352c4 cli: added support for no color
Using commandline flag users can now disable colored output
in logging.

Fixes: #434

Signed-off-by: Abhijeet Kasurde <akasurde@redhat.com>
2022-03-09 21:25:48 +05:30
Abhijeet Kasurde
2600052735 Misc typo fixes
Signed-off-by: Abhijeet Kasurde <akasurde@redhat.com>
2022-03-09 21:22:41 +05:30
dwertent
a985b2ce09 Add mock logger 2022-03-08 21:39:09 +02:00
dwertent
829c176644 Merge remote-tracking branch 'armosec/dev' into server-support 2022-03-08 14:52:00 +02:00
dwertent
7d7d247bc2 udpate go mod 2022-03-08 14:51:24 +02:00
dwertent
43ae8e2a81 Merge branch 'master' into server-support 2022-03-08 14:49:41 +02:00
David Wertenteil
b0f37e9465 Merge pull request #441 from xdavidel/update_io_apis
update vulns input / output api
2022-03-08 14:34:45 +02:00
David Wertenteil
396ef55267 Merge pull request #440 from dwertent/master
Use semver.Compare for version check
2022-03-08 10:12:59 +02:00
dwertent
4b07469bb2 use semver.Compare for version check 2022-03-08 10:10:06 +02:00
David Delarosa
260f7b06c1 update vulns input / output api 2022-03-07 17:03:14 +02:00
dwertent
37644e1f57 update readme 2022-02-10 21:05:08 +02:00
dwertent
8a04934fbd Adding readme and yaml 2022-02-10 20:41:28 +02:00
dwertent
31e1b3055f Prometheus support 2022-02-10 20:11:00 +02:00
226 changed files with 8287 additions and 3595 deletions

View File

@@ -33,10 +33,16 @@ jobs:
uses: actions/setup-go@v2
with:
go-version: 1.17
- name: Test
# - name: Test cmd pkg
# run: cd cmd && go test -v ./...
- name: Test core pkg
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: go test -v ./...
- name: Test httphandler pkg
run: cd httphandler && go test -v ./...
- name: Build
env:
RELEASE: v2.0.${{ github.run_number }}
@@ -74,9 +80,6 @@ jobs:
asset_path: build/${{ matrix.os }}/kubescape.sha256
asset_name: kubescape-${{ matrix.os }}-sha256
asset_content_type: application/octet-stream
build-docker:
name: Build docker container, tag and upload to registry
needs: build

View File

@@ -18,9 +18,25 @@ jobs:
with:
go-version: 1.17
- name: Test
# - name: Test cmd pkg
# run: cd cmd && go test -v ./...
# - name: Test core pkg
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# run: cd core && go test -v ./...
# - name: Test cmd pkg
# run: cd cmd && go test -v ./...
- name: Test core pkg
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: go test -v ./...
- name: Test httphandler pkg
run: cd httphandler && go test -v ./...
- name: Build
env:
RELEASE: v2.0.${{ github.run_number }}

View File

@@ -19,9 +19,17 @@ jobs:
with:
go-version: 1.17
- name: Test
# - name: Test cmd pkg
# run: cd cmd && go test -v ./...
- name: Test core pkg
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: go test -v ./...
- name: Test httphandler pkg
run: cd httphandler && go test -v ./...
- name: Build
env:
RELEASE: v2.0.${{ github.run_number }}

View File

@@ -12,8 +12,20 @@ Kubescape integrates natively with other DevOps tools, including Jenkins, Circle
</br>
<!-- # Kubescape Coverage
<img src="docs/ksfromcodetodeploy.png">
</br> -->
# Kubescape CLI:
<img src="docs/demo.gif">
</br>
<!-- # Kubescape overview:
<img src="docs/ARMO-header-2022.gif"> -->
# TL;DR
## Install:
```
@@ -26,7 +38,7 @@ curl -s https://raw.githubusercontent.com/armosec/kubescape/master/install.sh |
## Run:
```
kubescape scan --submit --enable-host-scan
kubescape scan --submit --enable-host-scan --format-version v2 --verbose
```
<img src="docs/summary.png">
@@ -103,7 +115,7 @@ Set-ExecutionPolicy RemoteSigned -scope CurrentUser
#### Scan a running Kubernetes cluster and submit results to the [Kubescape SaaS version](https://portal.armo.cloud/)
```
kubescape scan --submit --enable-host-scan
kubescape scan --submit --enable-host-scan --verbose
```
> Read [here](https://hub.armo.cloud/docs/host-sensor) more about the `enable-host-scan` flag
@@ -255,6 +267,16 @@ Now you can submit the results to the Kubescape SaaS version -
kubescape submit results path/to/results.json
```
# Integrations
## VS Code Extension
![Visual Studio Marketplace Downloads](https://img.shields.io/visual-studio-marketplace/d/kubescape.kubescape?label=VScode) ![Open VSX](https://img.shields.io/open-vsx/dt/kubescape/kubescape?label=openVSX&color=yellowgreen)
Scan the YAML files while writing them using the [vs code extension](https://github.com/armosec/vscode-kubescape/blob/master/README.md)
# Under the hood
## Technology

View File

@@ -4,7 +4,7 @@ import hashlib
import platform
import subprocess
BASE_GETTER_CONST = "github.com/armosec/kubescape/cautils/getter"
BASE_GETTER_CONST = "github.com/armosec/kubescape/v2/core/cautils/getter"
BE_SERVER_CONST = BASE_GETTER_CONST + ".ArmoBEURL"
ER_SERVER_CONST = BASE_GETTER_CONST + ".ArmoERURL"
WEBSITE_CONST = BASE_GETTER_CONST + ".ArmoFEURL"
@@ -18,7 +18,7 @@ def checkStatus(status, msg):
def getBuildDir():
currentPlatform = platform.system()
buildDir = "build/"
buildDir = "./build/"
if currentPlatform == "Windows": buildDir += "windows-latest"
elif currentPlatform == "Linux": buildDir += "ubuntu-latest"
@@ -42,7 +42,7 @@ def main():
# Set some variables
packageName = getPackageName()
buildUrl = "github.com/armosec/kubescape/cautils.BuildNumber"
buildUrl = "github.com/armosec/kubescape/v2/core/cautils.BuildNumber"
releaseVersion = os.getenv("RELEASE")
ArmoBEServer = os.getenv("ArmoBEServer")
ArmoERServer = os.getenv("ArmoERServer")
@@ -70,7 +70,7 @@ def main():
ldflags += " -X {}={}".format(WEBSITE_CONST, ArmoWebsite)
if ArmoAuthServer:
ldflags += " -X {}={}".format(AUTH_SERVER_CONST, ArmoAuthServer)
build_command = ["go", "build", "-o", ks_file, "-ldflags" ,ldflags]
print("Building kubescape and saving here: {}".format(ks_file))

View File

@@ -18,14 +18,30 @@ RUN pip3 install --no-cache --upgrade pip setuptools
WORKDIR /work
ADD . .
# build kubescape server
WORKDIR /work/httphandler
RUN python build.py
RUN ls -ltr build/ubuntu-latest
# build kubescape cmd
WORKDIR /work
RUN python build.py
RUN /work/build/ubuntu-latest/kubescape download artifacts -o /work/artifacts
FROM alpine
RUN addgroup -S armo && adduser -S armo -G armo
RUN mkdir /home/armo/.kubescape
COPY --from=builder /work/artifacts/ /home/armo/.kubescape
RUN chown -R armo:armo /home/armo/.kubescape
USER armo
WORKDIR /home/armo
COPY --from=builder /work/httphandler/build/ubuntu-latest/kubescape /usr/bin/ksserver
COPY --from=builder /work/build/ubuntu-latest/kubescape /usr/bin/kubescape
# # Download the frameworks. Use the "--use-default" flag when running kubescape
# RUN kubescape download framework nsa && kubescape download framework mitre
ENTRYPOINT ["kubescape"]
ENTRYPOINT ["ksserver"]

View File

@@ -1,75 +0,0 @@
package cautils
import (
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/k8s-interface/workloadinterface"
"github.com/armosec/opa-utils/reporthandling"
"github.com/armosec/opa-utils/reporthandling/results/v1/resourcesresults"
reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2"
)
// K8SResources map[<api group>/<api version>/<resource>][]<resourceID>
type K8SResources map[string][]string
type OPASessionObj struct {
K8SResources *K8SResources // input k8s objects
Frameworks []reporthandling.Framework // list of frameworks to scan
AllResources map[string]workloadinterface.IMetadata // all scanned resources, map[<rtesource ID>]<resource>
ResourcesResult map[string]resourcesresults.Result // resources scan results, map[<rtesource ID>]<resource result>
PostureReport *reporthandling.PostureReport // scan results v1
Report *reporthandlingv2.PostureReport // scan results v2
Exceptions []armotypes.PostureExceptionPolicy // list of exceptions to apply on scan results
RegoInputData RegoInputData // input passed to rgo for scanning. map[<control name>][<input arguments>]
}
func NewOPASessionObj(frameworks []reporthandling.Framework, k8sResources *K8SResources) *OPASessionObj {
return &OPASessionObj{
Report: &reporthandlingv2.PostureReport{},
Frameworks: frameworks,
K8SResources: k8sResources,
AllResources: make(map[string]workloadinterface.IMetadata),
ResourcesResult: make(map[string]resourcesresults.Result),
PostureReport: &reporthandling.PostureReport{
ClusterName: ClusterName,
CustomerGUID: CustomerGUID,
},
}
}
func NewOPASessionObjMock() *OPASessionObj {
return &OPASessionObj{
Frameworks: nil,
K8SResources: nil,
AllResources: make(map[string]workloadinterface.IMetadata),
ResourcesResult: make(map[string]resourcesresults.Result),
Report: &reporthandlingv2.PostureReport{},
PostureReport: &reporthandling.PostureReport{
ClusterName: "",
CustomerGUID: "",
ReportID: "",
JobID: "",
},
}
}
type ComponentConfig struct {
Exceptions Exception `json:"exceptions"`
}
type Exception struct {
Ignore *bool `json:"ignore"` // ignore test results
MultipleScore *reporthandling.AlertScore `json:"multipleScore"` // MultipleScore number - float32
Namespaces []string `json:"namespaces"`
Regex string `json:"regex"` // not supported
}
type RegoInputData struct {
PostureControlInputs map[string][]string `json:"postureControlInputs"`
// ClusterName string `json:"clusterName"`
// K8sConfig RegoK8sConfig `json:"k8sconfig"`
}
type Policies struct {
Frameworks []string
Controls map[string]reporthandling.Control // map[<control ID>]<control>
}

View File

@@ -1,11 +0,0 @@
package cautils
// CA environment vars
var (
CustomerGUID = ""
ClusterName = ""
EventReceiverURL = ""
NotificationServerURL = ""
DashboardBackendURL = ""
RestAPIPort = "4001"
)

View File

@@ -1,51 +0,0 @@
package logger
import (
"os"
"strings"
"github.com/armosec/kubescape/cautils/logger/helpers"
"github.com/armosec/kubescape/cautils/logger/prettylogger"
"github.com/armosec/kubescape/cautils/logger/zaplogger"
"github.com/mattn/go-isatty"
)
type ILogger interface {
Fatal(msg string, details ...helpers.IDetails) // print log and exit 1
Error(msg string, details ...helpers.IDetails)
Success(msg string, details ...helpers.IDetails)
Warning(msg string, details ...helpers.IDetails)
Info(msg string, details ...helpers.IDetails)
Debug(msg string, details ...helpers.IDetails)
SetLevel(level string) error
GetLevel() string
SetWriter(w *os.File)
GetWriter() *os.File
}
var l ILogger
func L() ILogger {
if l == nil {
InitializeLogger("")
}
return l
}
func InitializeLogger(loggerName string) {
switch strings.ToLower(loggerName) {
case "zap":
l = zaplogger.NewZapLogger()
case "pretty":
l = prettylogger.NewPrettyLogger()
default:
if isatty.IsTerminal(os.Stdout.Fd()) {
l = prettylogger.NewPrettyLogger()
} else {
l = zaplogger.NewZapLogger()
}
}
}

View File

@@ -1,194 +0,0 @@
package cautils
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/cautils/logger/helpers"
"github.com/armosec/opa-utils/reporthandling"
)
const (
ScanCluster string = "cluster"
ScanLocalFiles string = "yaml"
localControlInputsFilename string = "controls-inputs.json"
localExceptionsFilename string = "exceptions.json"
)
type BoolPtrFlag struct {
valPtr *bool
}
func (bpf *BoolPtrFlag) Type() string {
return "bool"
}
func (bpf *BoolPtrFlag) String() string {
if bpf.valPtr != nil {
return fmt.Sprintf("%v", *bpf.valPtr)
}
return ""
}
func (bpf *BoolPtrFlag) Get() *bool {
return bpf.valPtr
}
func (bpf *BoolPtrFlag) SetBool(val bool) {
bpf.valPtr = &val
}
func (bpf *BoolPtrFlag) Set(val string) error {
switch val {
case "true":
bpf.SetBool(true)
case "false":
bpf.SetBool(false)
}
return nil
}
type RootInfo struct {
Logger string // logger level
CacheDir string // cached dir
}
type ScanInfo struct {
Getters
PolicyIdentifier []reporthandling.PolicyIdentifier
UseExceptions string // Load file with exceptions configuration
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
FormatVersion string // Output object can be differnet between versions, this is for testing and backward compatibility
ExcludedNamespaces string // used for host sensor namespace
IncludeNamespaces string // DEPRECATED?
InputPatterns []string // Yaml files input patterns
Silent bool // Silent mode - Do not print progress logs
FailThreshold float32 // Failure score threshold
Submit bool // Submit results to Armo BE
HostSensorEnabled BoolPtrFlag // Deploy ARMO K8s host sensor to collect data from certain controls
HostSensorYamlPath string // Path to hostsensor file
Local bool // Do not submit results
Account string // account ID
KubeContext string // context name
FrameworkScan bool // false if scanning control
ScanAll bool // true if scan all frameworks
}
type Getters struct {
ExceptionsGetter getter.IExceptionsGetter
ControlsInputsGetter getter.IControlsInputsGetter
PolicyGetter getter.IPolicyGetter
}
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 {
logger.L().Fatal("failed to read files from directory", helpers.String("dir", scanInfo.UseArtifactsFrom), helpers.Error(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() {
if scanInfo.UseDefault {
for _, policy := range scanInfo.PolicyIdentifier {
scanInfo.UseFrom = append(scanInfo.UseFrom, getter.GetDefaultPath(policy.Name+".json"))
}
}
}
func (scanInfo *ScanInfo) setOutputFile() {
if scanInfo.Output == "" {
return
}
if scanInfo.Format == "json" {
if filepath.Ext(scanInfo.Output) != ".json" {
scanInfo.Output += ".json"
}
}
if scanInfo.Format == "junit" {
if filepath.Ext(scanInfo.Output) != ".xml" {
scanInfo.Output += ".xml"
}
}
if scanInfo.Format == "pdf" {
if filepath.Ext(scanInfo.Output) != ".pdf" {
scanInfo.Output += ".pdf"
}
}
}
func (scanInfo *ScanInfo) GetScanningEnvironment() string {
if len(scanInfo.InputPatterns) != 0 {
return ScanLocalFiles
}
return ScanCluster
}
func (scanInfo *ScanInfo) SetPolicyIdentifiers(policies []string, kind reporthandling.NotificationPolicyKind) {
for _, policy := range policies {
if !scanInfo.contains(policy) {
newPolicy := reporthandling.PolicyIdentifier{}
newPolicy.Kind = kind // reporthandling.KindFramework
newPolicy.Name = policy
scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy)
}
}
}
func (scanInfo *ScanInfo) contains(policyName string) bool {
for _, policy := range scanInfo.PolicyIdentifier {
if policy.Name == policyName {
return true
}
}
return false
}

View File

@@ -1,7 +0,0 @@
package clihandler
func CliDelete() error {
tenant := getTenantConfig("", "", getKubernetesApi()) // change k8sinterface
return tenant.DeleteCachedConfig()
}

View File

@@ -1,7 +0,0 @@
package cliobjects
type SetConfig struct {
Account string
ClientID string
SecretKey string
}

View File

@@ -1,22 +0,0 @@
package clihandler
import (
"github.com/armosec/kubescape/clihandler/cliobjects"
)
func CliSetConfig(setConfig *cliobjects.SetConfig) error {
tenant := getTenantConfig("", "", getKubernetesApi())
if setConfig.Account != "" {
tenant.GetConfigObj().AccountID = setConfig.Account
}
if setConfig.SecretKey != "" {
tenant.GetConfigObj().SecretKey = setConfig.SecretKey
}
if setConfig.ClientID != "" {
tenant.GetConfigObj().ClientID = setConfig.ClientID
}
return tenant.UpdateCachedConfig()
}

View File

@@ -1,12 +0,0 @@
package clihandler
import (
"fmt"
"os"
)
func CliView() error {
tenant := getTenantConfig("", "", getKubernetesApi()) // change k8sinterface
fmt.Fprintf(os.Stderr, "%s\n", tenant.GetConfigObj().Config())
return nil
}

View File

@@ -1,19 +0,0 @@
package cmd
import (
"github.com/spf13/cobra"
)
// clusterCmd represents the cluster command
var clusterCmd = &cobra.Command{
Use: "cluster",
Short: "Set configuration for cluster",
Long: ``,
Deprecated: "use the 'set' command instead",
Run: func(cmd *cobra.Command, args []string) {
},
}
func init() {
configCmd.AddCommand(clusterCmd)
}

View File

@@ -1,50 +0,0 @@
package cmd
import (
"fmt"
"strings"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/cautils/logger"
"github.com/spf13/cobra"
)
var getCmd = &cobra.Command{
Use: "get <key>",
Short: "Get configuration in cluster",
Long: ``,
Deprecated: "use the 'view' command instead",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 || len(args) > 1 {
return fmt.Errorf("requires one argument")
}
keyValue := strings.Split(args[0], "=")
if len(keyValue) != 1 {
return fmt.Errorf("requires one argument")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
keyValue := strings.Split(args[0], "=")
key := keyValue[0]
k8s := k8sinterface.NewKubernetesApi()
clusterConfig := cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account, "")
val, err := clusterConfig.GetValueByKeyFromConfigMap(key)
if err != nil {
if err.Error() == "value does not exist." {
return fmt.Errorf("failed to get value from configmap, reason: %s", err.Error())
}
return err
}
logger.L().Info(key + "=" + val)
return nil
},
}
func init() {
clusterCmd.AddCommand(getCmd)
}

View File

@@ -1,46 +0,0 @@
package cmd
import (
"fmt"
"strings"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/cautils/logger"
"github.com/spf13/cobra"
)
var setClusterCmd = &cobra.Command{
Use: "set <key>=<value>",
Short: "Set configuration in cluster",
Long: ``,
Deprecated: "use the 'set' command instead",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 || len(args) > 1 {
return fmt.Errorf("requires one argument: <key>=<value>")
}
keyValue := strings.Split(args[0], "=")
if len(keyValue) != 2 {
return fmt.Errorf("requires one argument: <key>=<value>")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
keyValue := strings.Split(args[0], "=")
key := keyValue[0]
data := keyValue[1]
k8s := k8sinterface.NewKubernetesApi()
clusterConfig := cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), scanInfo.Account, "")
if err := clusterConfig.SetKeyValueInConfigmap(key, data); err != nil {
return err
}
logger.L().Info("value added successfully.")
return nil
},
}
func init() {
clusterCmd.AddCommand(setClusterCmd)
}

View File

@@ -1,45 +0,0 @@
package cmd
import (
"os"
"strings"
"github.com/spf13/cobra"
)
var completionCmdExamples = `
# Enable BASH shell autocompletion
$ source <(kubescape completion bash)
$ echo 'source <(kubescape completion bash)' >> ~/.bashrc
# Enable ZSH shell autocompletion
$ source <(kubectl completion zsh)
$ echo 'source <(kubectl completion zsh)' >> "${fpath[1]}/_kubectl"
`
var completionCmd = &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate autocompletion script",
Long: "To load completions",
Example: completionCmdExamples,
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.ExactValidArgs(1),
Run: func(cmd *cobra.Command, args []string) {
switch strings.ToLower(args[0]) {
case "bash":
cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
}
},
}
func init() {
rootCmd.AddCommand(completionCmd)
}

View File

@@ -1,124 +0,0 @@
package cmd
import (
"fmt"
"strings"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/clihandler"
"github.com/armosec/kubescape/clihandler/cliobjects"
"github.com/spf13/cobra"
)
var (
configExample = `
# View cached configurations
kubescape config view
# Delete cached configurations
kubescape config delete
# Set cached configurations
kubescape config set --help
`
setConfigExample = `
# Set account id
kubescape config set accountID <account id>
# Set client id
kubescape config set clientID <client id>
# Set access key
kubescape config set secretKey <access key>
`
)
// configCmd represents the config command
var configCmd = &cobra.Command{
Use: "config",
Short: "handle cached configurations",
Example: configExample,
}
var setConfig = cliobjects.SetConfig{}
// configCmd represents the config command
var configSetCmd = &cobra.Command{
Use: "set",
Short: fmt.Sprintf("Set configurations, supported: %s", strings.Join(stringKeysToSlice(supportConfigSet), "/")),
Example: setConfigExample,
ValidArgs: stringKeysToSlice(supportConfigSet),
RunE: func(cmd *cobra.Command, args []string) error {
if err := parseSetArgs(args); err != nil {
return err
}
if err := clihandler.CliSetConfig(&setConfig); err != nil {
logger.L().Fatal(err.Error())
}
return nil
},
}
var supportConfigSet = map[string]func(*cliobjects.SetConfig, string){
"accountID": func(s *cliobjects.SetConfig, account string) { s.Account = account },
"clientID": func(s *cliobjects.SetConfig, clientID string) { s.ClientID = clientID },
"secretKey": func(s *cliobjects.SetConfig, secretKey string) { s.SecretKey = secretKey },
}
func stringKeysToSlice(m map[string]func(*cliobjects.SetConfig, string)) []string {
l := []string{}
for i := range m {
l = append(l, i)
}
return l
}
func parseSetArgs(args []string) error {
var key string
var value string
if len(args) == 1 {
if keyValue := strings.Split(args[0], "="); len(keyValue) == 2 {
key = keyValue[0]
value = keyValue[1]
}
} else if len(args) == 2 {
key = args[0]
value = args[1]
}
if setConfigFunc, ok := supportConfigSet[key]; ok {
setConfigFunc(&setConfig, value)
} else {
return fmt.Errorf("key '%s' unknown . supported: %s", key, strings.Join(stringKeysToSlice(supportConfigSet), "/"))
}
return nil
}
var configDeleteCmd = &cobra.Command{
Use: "delete",
Short: "Delete cached configurations",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
if err := clihandler.CliDelete(); err != nil {
logger.L().Fatal(err.Error())
}
},
}
// configCmd represents the config command
var configViewCmd = &cobra.Command{
Use: "view",
Short: "View cached configurations",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
if err := clihandler.CliView(); err != nil {
logger.L().Fatal(err.Error())
}
},
}
func init() {
rootCmd.AddCommand(configCmd)
configCmd.AddCommand(configSetCmd)
configCmd.AddCommand(configDeleteCmd)
configCmd.AddCommand(configViewCmd)
}

View File

@@ -1,122 +0,0 @@
package cmd
import (
"fmt"
"io"
"os"
"strings"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/clihandler"
"github.com/armosec/opa-utils/reporthandling"
"github.com/spf13/cobra"
)
var (
controlExample = `
# Scan the 'privileged container' control
kubescape scan control "privileged container"
# Scan list of controls separated with a comma
kubescape scan control "privileged container","allowed hostpath"
# Scan list of controls using the control ID separated with a comma
kubescape scan control C-0058,C-0057
Run 'kubescape list controls' for the list of supported controls
Control documentation:
https://hub.armo.cloud/docs/controls
`
)
// controlCmd represents the control command
var controlCmd = &cobra.Command{
Use: "control <control names list>/<control ids list>",
Short: "The controls you wish to use. Run 'kubescape list controls' for the list of supported controls",
Example: controlExample,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
controls := strings.Split(args[0], ",")
if len(controls) > 1 {
for _, control := range controls {
if control == "" {
return fmt.Errorf("usage: <control-0>,<control-1>")
}
}
}
} else {
return fmt.Errorf("requires at least one control name")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
flagValidationControl()
scanInfo.PolicyIdentifier = []reporthandling.PolicyIdentifier{}
if len(args) == 0 {
scanInfo.ScanAll = true
} else { // expected control or list of control sepparated by ","
// Read controls from input args
scanInfo.SetPolicyIdentifiers(strings.Split(args[0], ","), reporthandling.KindControl)
if len(args) > 1 {
if len(args[1:]) == 0 || args[1] != "-" {
scanInfo.InputPatterns = args[1:]
} else { // store stdin to file - do NOT move to separate function !!
tempFile, err := os.CreateTemp(".", "tmp-kubescape*.yaml")
if err != nil {
return err
}
defer os.Remove(tempFile.Name())
if _, err := io.Copy(tempFile, os.Stdin); err != nil {
return err
}
scanInfo.InputPatterns = []string{tempFile.Name()}
}
}
}
scanInfo.FrameworkScan = false
scanInfo.Init()
cautils.SetSilentMode(scanInfo.Silent)
err := clihandler.ScanCliSetup(&scanInfo)
if err != nil {
logger.L().Fatal(err.Error())
}
return nil
},
}
func init() {
scanInfo = cautils.ScanInfo{}
scanCmd.AddCommand(controlCmd)
}
func flagValidationControl() {
if 100 < scanInfo.FailThreshold {
logger.L().Fatal("bad argument: out of range threshold")
}
}
func setScanForFirstControl(controls []string) []reporthandling.PolicyIdentifier {
newPolicy := reporthandling.PolicyIdentifier{}
newPolicy.Kind = reporthandling.KindControl
newPolicy.Name = controls[0]
scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy)
return scanInfo.PolicyIdentifier
}
func SetScanForGivenControls(controls []string) []reporthandling.PolicyIdentifier {
for _, control := range controls {
control := strings.TrimLeft(control, " ")
newPolicy := reporthandling.PolicyIdentifier{}
newPolicy.Kind = reporthandling.KindControl
newPolicy.Name = control
scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy)
}
return scanInfo.PolicyIdentifier
}

View File

@@ -1,57 +0,0 @@
package cmd
import (
"fmt"
"strings"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/clihandler"
"github.com/armosec/kubescape/clihandler/cliobjects"
"github.com/spf13/cobra"
)
var deleteInfo cliobjects.Delete
var deleteExceptionsExamples = `
# Delete single exception
kubescape delete exceptions "exception name"
# Delete multiple exceptions
kubescape delete exceptions "first exception;second exception;third exception"
`
var deleteCmd = &cobra.Command{
Use: "delete <command>",
Short: "Delete configurations in Kubescape SaaS version",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
},
}
var deleteExceptionsCmd = &cobra.Command{
Use: "exceptions <exception name>",
Short: "Delete exceptions from Kubescape SaaS version. Run 'kubescape list exceptions' for all exceptions names",
Example: deleteExceptionsExamples,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("missing exceptions names")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
exceptionsNames := strings.Split(args[0], ";")
if len(exceptionsNames) == 0 {
logger.L().Fatal("missing exceptions names")
}
if err := clihandler.DeleteExceptions(deleteInfo.Account, exceptionsNames); err != nil {
logger.L().Fatal(err.Error())
}
},
}
func init() {
deleteCmd.PersistentFlags().StringVarP(&deleteInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file")
rootCmd.AddCommand(deleteCmd)
deleteCmd.AddCommand(deleteExceptionsCmd)
}

View File

@@ -1,81 +0,0 @@
package cmd
import (
"fmt"
"path/filepath"
"strings"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/clihandler"
"github.com/spf13/cobra"
)
var downloadInfo = cautils.DownloadInfo{}
var (
downloadExample = `
# Download all artifacts and save them in the default path (~/.kubescape)
kubescape download artifacts
# Download all artifacts and save them in /tmp path
kubescape download artifacts --output /tmp
# Download the NSA framework. Run 'kubescape list frameworks' for all frameworks names
kubescape download framework nsa
# Download the "Allowed hostPath" control. Run 'kubescape list controls' for all controls names
kubescape download control "Allowed hostPath"
# Download the "C-0001" control. Run 'kubescape list controls --id' for all controls ids
kubescape download control C-0001
# Download the configured exceptions
kubescape download exceptions
# Download the configured controls-inputs
kubescape download controls-inputs
`
)
var downloadCmd = &cobra.Command{
Use: "download <policy> <policy name>",
Short: fmt.Sprintf("Download %s", strings.Join(clihandler.DownloadSupportCommands(), ",")),
Long: ``,
Example: downloadExample,
Args: func(cmd *cobra.Command, args []string) error {
supported := strings.Join(clihandler.DownloadSupportCommands(), ",")
if len(args) < 1 {
return fmt.Errorf("policy type required, supported: %v", supported)
}
if cautils.StringInSlice(clihandler.DownloadSupportCommands(), args[0]) == cautils.ValueNotFound {
return fmt.Errorf("invalid parameter '%s'. Supported parameters: %s", args[0], supported)
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
downloadInfo.Target = args[0]
if len(args) >= 2 {
downloadInfo.Name = args[1]
}
if err := clihandler.CliDownload(&downloadInfo); err != nil {
logger.L().Fatal(err.Error())
}
return nil
},
}
func init() {
cobra.OnInitialize(initDownload)
rootCmd.AddCommand(downloadCmd)
downloadCmd.PersistentFlags().StringVarP(&downloadInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file")
downloadCmd.Flags().StringVarP(&downloadInfo.Path, "output", "o", "", "Output file. If not specified, will save in `~/.kubescape/<policy name>.json`")
}
func initDownload() {
if filepath.Ext(downloadInfo.Path) == ".json" {
downloadInfo.Path, downloadInfo.FileName = filepath.Split(downloadInfo.Path)
}
}

View File

@@ -1,121 +0,0 @@
package cmd
import (
"fmt"
"io"
"os"
"strings"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/clihandler"
"github.com/armosec/opa-utils/reporthandling"
"github.com/spf13/cobra"
)
var (
frameworkExample = `
# Scan all frameworks and submit the results
kubescape scan framework all --submit
# Scan the NSA framework
kubescape scan framework nsa
# Scan the NSA and MITRE framework
kubescape scan framework nsa,mitre
# Scan all frameworks
kubescape scan framework all
# Scan kubernetes YAML manifest files
kubescape scan framework nsa *.yaml
Run 'kubescape list frameworks' for the list of supported frameworks
`
)
var frameworkCmd = &cobra.Command{
Use: "framework <framework names list> [`<glob pattern>`/`-`] [flags]",
Short: "The framework you wish to use. Run 'kubescape list frameworks' for the list of supported frameworks",
Example: frameworkExample,
Long: "Execute a scan on a running Kubernetes cluster or `yaml`/`json` files (use glob) or `-` for stdin",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
frameworks := strings.Split(args[0], ",")
if len(frameworks) > 1 {
for _, framework := range frameworks {
if framework == "" {
return fmt.Errorf("usage: <framework-0>,<framework-1>")
}
}
}
} else {
return fmt.Errorf("requires at least one framework name")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
flagValidationFramework()
var frameworks []string
if len(args) == 0 { // scan all frameworks
scanInfo.ScanAll = true
} else {
// Read frameworks from input args
frameworks = strings.Split(args[0], ",")
if cautils.StringInSlice(frameworks, "all") != cautils.ValueNotFound {
scanInfo.ScanAll = true
frameworks = []string{}
}
if len(args) > 1 {
if len(args[1:]) == 0 || args[1] != "-" {
scanInfo.InputPatterns = args[1:]
} else { // store stdin to file - do NOT move to separate function !!
tempFile, err := os.CreateTemp(".", "tmp-kubescape*.yaml")
if err != nil {
return err
}
defer os.Remove(tempFile.Name())
if _, err := io.Copy(tempFile, os.Stdin); err != nil {
return err
}
scanInfo.InputPatterns = []string{tempFile.Name()}
}
}
}
scanInfo.FrameworkScan = true
scanInfo.SetPolicyIdentifiers(frameworks, reporthandling.KindFramework)
scanInfo.Init()
cautils.SetSilentMode(scanInfo.Silent)
err := clihandler.ScanCliSetup(&scanInfo)
if err != nil {
logger.L().Fatal(err.Error())
}
return nil
},
}
func init() {
scanCmd.AddCommand(frameworkCmd)
scanInfo = cautils.ScanInfo{}
scanInfo.FrameworkScan = true
}
// func SetScanForFirstFramework(frameworks []string) []reporthandling.PolicyIdentifier {
// newPolicy := reporthandling.PolicyIdentifier{}
// newPolicy.Kind = reporthandling.KindFramework
// newPolicy.Name = frameworks[0]
// scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy)
// return scanInfo.PolicyIdentifier
// }
func flagValidationFramework() {
if scanInfo.Submit && scanInfo.Local {
logger.L().Fatal("you can use `keep-local` or `submit`, but not both")
}
if 100 < scanInfo.FailThreshold {
logger.L().Fatal("bad argument: out of range threshold")
}
}

View File

@@ -1,67 +0,0 @@
package cmd
import (
"fmt"
"strings"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/clihandler"
"github.com/armosec/kubescape/clihandler/cliobjects"
"github.com/spf13/cobra"
)
var (
listExample = `
# List default supported frameworks names
kubescape list frameworks
# List all supported frameworks names
kubescape list frameworks --account <account id>
# List all supported controls names
kubescape list controls
# List all supported controls ids
kubescape list controls --id
Control documentation:
https://hub.armo.cloud/docs/controls
`
)
var listPolicies = cliobjects.ListPolicies{}
var listCmd = &cobra.Command{
Use: "list <policy> [flags]",
Short: "List frameworks/controls will list the supported frameworks and controls",
Long: ``,
Example: listExample,
Args: func(cmd *cobra.Command, args []string) error {
supported := strings.Join(clihandler.ListSupportCommands(), ",")
if len(args) < 1 {
return fmt.Errorf("policy type requeued, supported: %s", supported)
}
if cautils.StringInSlice(clihandler.ListSupportCommands(), args[0]) == cautils.ValueNotFound {
return fmt.Errorf("invalid parameter '%s'. Supported parameters: %s", args[0], supported)
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
listPolicies.Target = args[0]
if err := clihandler.CliList(&listPolicies); err != nil {
logger.L().Fatal(err.Error())
}
return nil
},
}
func init() {
// cobra.OnInitialize(initConfig)
rootCmd.AddCommand(listCmd)
listCmd.PersistentFlags().StringVar(&listPolicies.Account, "account", "", "Armo portal account ID. Default will load account ID from configMap or config file")
listCmd.PersistentFlags().StringVar(&listPolicies.Format, "format", "pretty-print", "output format. supported: 'pretty-printer'/'json'")
listCmd.PersistentFlags().BoolVarP(&listPolicies.ListIDs, "id", "", false, "List control ID's instead of controls names")
}

View File

@@ -1,18 +0,0 @@
package cmd
import (
"github.com/spf13/cobra"
)
var localCmd = &cobra.Command{
Use: "local",
Short: "Set configuration locally (for config.json)",
Long: ``,
Deprecated: "use the 'set' command instead",
Run: func(cmd *cobra.Command, args []string) {
},
}
func init() {
configCmd.AddCommand(localCmd)
}

View File

@@ -1,45 +0,0 @@
package cmd
import (
"fmt"
"strings"
"github.com/armosec/kubescape/cautils"
"github.com/spf13/cobra"
)
var localGetCmd = &cobra.Command{
Use: "get <key>",
Short: "Get configuration locally",
Long: ``,
Deprecated: "use the 'view' command instead",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 || len(args) > 1 {
return fmt.Errorf("requires one argument")
}
keyValue := strings.Split(args[0], "=")
if len(keyValue) != 1 {
return fmt.Errorf("requires one argument")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
keyValue := strings.Split(args[0], "=")
key := keyValue[0]
val, err := cautils.GetValueFromConfigJson(key)
if err != nil {
if err.Error() == "value does not exist." {
return fmt.Errorf("failed to get value from: %s, reason: %s", cautils.ConfigFileFullPath(), err.Error())
}
return err
}
fmt.Println(key + "=" + val)
return nil
},
}
func init() {
localCmd.AddCommand(localGetCmd)
}

View File

@@ -1,41 +0,0 @@
package cmd
import (
"fmt"
"strings"
"github.com/armosec/kubescape/cautils"
"github.com/spf13/cobra"
)
var localSetCmd = &cobra.Command{
Use: "set <key>=<value>",
Short: "Set configuration locally",
Long: ``,
Deprecated: "use the 'set' command instead",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 || len(args) > 1 {
return fmt.Errorf("requires one argument: <key>=<value>")
}
keyValue := strings.Split(args[0], "=")
if len(keyValue) != 2 {
return fmt.Errorf("requires one argument: <key>=<value>")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
keyValue := strings.Split(args[0], "=")
key := keyValue[0]
data := keyValue[1]
if err := cautils.SetKeyValueInConfigJson(key, data); err != nil {
return err
}
fmt.Println("Value added successfully.")
return nil
},
}
func init() {
localCmd.AddCommand(localSetCmd)
}

View File

@@ -1,66 +0,0 @@
package cmd
import (
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/cautils/logger/helpers"
"github.com/armosec/kubescape/clihandler"
"github.com/armosec/kubescape/clihandler/cliinterfaces"
reporterv1 "github.com/armosec/kubescape/resultshandling/reporter/v1"
"github.com/armosec/rbac-utils/rbacscanner"
"github.com/spf13/cobra"
)
// rabcCmd represents the RBAC command
var rabcCmd = &cobra.Command{
Use: "rbac \nExample:\n$ kubescape submit rbac",
Short: "Submit cluster's Role-Based Access Control(RBAC)",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
k8s := k8sinterface.NewKubernetesApi()
// get config
clusterConfig := getTenantConfig(submitInfo.Account, "", k8s)
if err := clusterConfig.SetTenant(); err != nil {
logger.L().Error("failed setting account ID", helpers.Error(err))
}
// list RBAC
rbacObjects := cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, clusterConfig.GetAccountID(), clusterConfig.GetClusterName()))
// submit resources
r := reporterv1.NewReportEventReceiver(clusterConfig.GetConfigObj())
submitInterfaces := cliinterfaces.SubmitInterfaces{
ClusterConfig: clusterConfig,
SubmitObjects: rbacObjects,
Reporter: r,
}
if err := clihandler.Submit(submitInterfaces); err != nil {
logger.L().Fatal(err.Error())
}
return nil
},
}
func init() {
submitCmd.AddCommand(rabcCmd)
}
// getKubernetesApi
func getKubernetesApi() *k8sinterface.KubernetesApi {
if !k8sinterface.IsConnectedToCluster() {
return nil
}
return k8sinterface.NewKubernetesApi()
}
func getTenantConfig(Account, clusterName string, k8s *k8sinterface.KubernetesApi) cautils.ITenantConfig {
if !k8sinterface.IsConnectedToCluster() || k8s == nil {
return cautils.NewLocalConfig(getter.GetArmoAPIConnector(), Account, clusterName)
}
return cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), Account, clusterName)
}

View File

@@ -1,118 +0,0 @@
package cmd
import (
"encoding/json"
"fmt"
"os"
"time"
"github.com/armosec/k8s-interface/workloadinterface"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/cautils/logger/helpers"
"github.com/armosec/kubescape/clihandler"
"github.com/armosec/kubescape/clihandler/cliinterfaces"
"github.com/armosec/kubescape/resultshandling/reporter"
reporterv1 "github.com/armosec/kubescape/resultshandling/reporter/v1"
reporterv2 "github.com/armosec/kubescape/resultshandling/reporter/v2"
"github.com/armosec/opa-utils/reporthandling"
"github.com/google/uuid"
"github.com/spf13/cobra"
)
var formatVersion string
type ResultsObject struct {
filePath string
customerGUID string
clusterName string
}
func NewResultsObject(customerGUID, clusterName, filePath string) *ResultsObject {
return &ResultsObject{
filePath: filePath,
customerGUID: customerGUID,
clusterName: clusterName,
}
}
func (resultsObject *ResultsObject) SetResourcesReport() (*reporthandling.PostureReport, error) {
// load framework results from json file
frameworkReports, err := loadResultsFromFile(resultsObject.filePath)
if err != nil {
return nil, err
}
return &reporthandling.PostureReport{
FrameworkReports: frameworkReports,
ReportID: uuid.NewString(),
ReportGenerationTime: time.Now().UTC(),
CustomerGUID: resultsObject.customerGUID,
ClusterName: resultsObject.clusterName,
}, nil
}
func (resultsObject *ResultsObject) ListAllResources() (map[string]workloadinterface.IMetadata, error) {
return map[string]workloadinterface.IMetadata{}, nil
}
var resultsCmd = &cobra.Command{
Use: "results <json file>\nExample:\n$ kubescape submit results path/to/results.json --format-version v2",
Short: "Submit a pre scanned results file. The file must be in json format",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("missing results file")
}
k8s := getKubernetesApi()
// get config
clusterConfig := getTenantConfig(submitInfo.Account, "", k8s)
if err := clusterConfig.SetTenant(); err != nil {
logger.L().Error("failed setting account ID", helpers.Error(err))
}
resultsObjects := NewResultsObject(clusterConfig.GetAccountID(), clusterConfig.GetClusterName(), args[0])
// submit resources
var r reporter.IReport
switch formatVersion {
case "v2":
r = reporterv2.NewReportEventReceiver(clusterConfig.GetConfigObj())
default:
logger.L().Warning("Deprecated results version. run with '--format-version' flag", helpers.String("your version", formatVersion), helpers.String("latest version", "v2"))
r = reporterv1.NewReportEventReceiver(clusterConfig.GetConfigObj())
}
submitInterfaces := cliinterfaces.SubmitInterfaces{
ClusterConfig: clusterConfig,
SubmitObjects: resultsObjects,
Reporter: r,
}
if err := clihandler.Submit(submitInterfaces); err != nil {
logger.L().Fatal(err.Error())
}
return nil
},
}
func init() {
submitCmd.AddCommand(resultsCmd)
resultsCmd.PersistentFlags().StringVar(&formatVersion, "format-version", "v1", "Output object can be differnet between versions, this is for maintaining backward and forward compatibility. Supported:'v1'/'v2'")
}
func loadResultsFromFile(filePath string) ([]reporthandling.FrameworkReport, error) {
frameworkReports := []reporthandling.FrameworkReport{}
f, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
if err = json.Unmarshal(f, &frameworkReports); err != nil {
frameworkReport := reporthandling.FrameworkReport{}
if err = json.Unmarshal(f, &frameworkReport); err != nil {
return frameworkReports, err
}
frameworkReports = append(frameworkReports, frameworkReport)
}
return frameworkReports, nil
}

View File

@@ -1,119 +0,0 @@
package cmd
import (
"fmt"
"os"
"strings"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/cautils/logger/helpers"
"github.com/spf13/cobra"
)
var armoBEURLs = ""
var armoBEURLsDep = ""
var rootInfo cautils.RootInfo
const envFlagUsage = "Send report results to specific URL. Format:<ReportReceiver>,<Backend>,<Frontend>.\n\t\tExample:report.armo.cloud,api.armo.cloud,portal.armo.cloud"
var ksExamples = `
# Scan command
kubescape scan --submit
# List supported frameworks
kubescape list frameworks
# Download artifacts (air-gapped environment support)
kubescape download artifacts
# View cached configurations
kubescape config view
`
var rootCmd = &cobra.Command{
Use: "kubescape",
Version: cautils.BuildNumber,
Short: "Kubescape is a tool for testing Kubernetes security posture",
Long: `Based on NSA \ MITRE ATT&CK® and other frameworks specifications`,
Example: ksExamples,
}
func Execute() {
rootCmd.Execute()
}
func init() {
cobra.OnInitialize(initLogger, initLoggerLevel, initEnvironment, initCacheDir)
rootCmd.PersistentFlags().StringVar(&armoBEURLsDep, "environment", "", envFlagUsage)
rootCmd.PersistentFlags().StringVar(&armoBEURLs, "env", "", envFlagUsage)
rootCmd.PersistentFlags().MarkDeprecated("environment", "use 'env' instead")
rootCmd.PersistentFlags().MarkHidden("environment")
rootCmd.PersistentFlags().MarkHidden("env")
rootCmd.PersistentFlags().StringVarP(&rootInfo.Logger, "logger", "l", helpers.InfoLevel.String(), fmt.Sprintf("Logger level. Supported: %s [$KS_LOGGER]", strings.Join(helpers.SupportedLevels(), "/")))
rootCmd.PersistentFlags().StringVar(&rootInfo.CacheDir, "cache-dir", getter.DefaultLocalStore, "Cache directory [$KS_CACHE_DIR]")
}
func initLogger() {
if l := os.Getenv("KS_LOGGER_NAME"); l != "" {
logger.InitializeLogger(l)
}
}
func initLoggerLevel() {
if rootInfo.Logger != helpers.InfoLevel.String() {
} else if l := os.Getenv("KS_LOGGER"); l != "" {
rootInfo.Logger = l
}
if err := logger.L().SetLevel(rootInfo.Logger); err != nil {
logger.L().Fatal(fmt.Sprintf("supported levels: %s", strings.Join(helpers.SupportedLevels(), "/")), helpers.Error(err))
}
}
func initCacheDir() {
if rootInfo.CacheDir != getter.DefaultLocalStore {
getter.DefaultLocalStore = rootInfo.CacheDir
} else if cacheDir := os.Getenv("KS_CACHE_DIR"); cacheDir != "" {
getter.DefaultLocalStore = cacheDir
} else {
return // using default cache di location
}
logger.L().Debug("cache dir updated", helpers.String("path", getter.DefaultLocalStore))
}
func initEnvironment() {
if armoBEURLsDep != "" {
armoBEURLs = armoBEURLsDep
}
urlSlices := strings.Split(armoBEURLs, ",")
if len(urlSlices) != 1 && len(urlSlices) < 3 {
logger.L().Fatal("expected at least 3 URLs (report, api, frontend, auth)")
}
switch len(urlSlices) {
case 1:
switch urlSlices[0] {
case "dev", "development":
getter.SetARMOAPIConnector(getter.NewARMOAPIDev())
case "stage", "staging":
getter.SetARMOAPIConnector(getter.NewARMOAPIStaging())
case "":
getter.SetARMOAPIConnector(getter.NewARMOAPIProd())
default:
logger.L().Fatal("--environment flag usage: " + envFlagUsage)
}
case 2:
logger.L().Fatal("--environment flag usage: " + envFlagUsage)
case 3, 4:
var armoAUTHURL string
armoERURL := urlSlices[0] // mandatory
armoBEURL := urlSlices[1] // mandatory
armoFEURL := urlSlices[2] // mandatory
if len(urlSlices) <= 4 {
armoAUTHURL = urlSlices[3]
}
getter.SetARMOAPIConnector(getter.NewARMOAPICustomized(armoERURL, armoBEURL, armoFEURL, armoAUTHURL))
}
}

View File

@@ -1,46 +0,0 @@
package cmd
import (
"fmt"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/clihandler"
"github.com/armosec/kubescape/clihandler/cliobjects"
"github.com/spf13/cobra"
)
var submitInfo cliobjects.Submit
var submitCmdExamples = `
`
var submitCmd = &cobra.Command{
Use: "submit <command>",
Short: "Submit an object to the Kubescape SaaS version",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
},
}
var submitExceptionsCmd = &cobra.Command{
Use: "exceptions <full path to exceptins file>",
Short: "Submit exceptions to the Kubescape SaaS version",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("missing full path to exceptions file")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
if err := clihandler.SubmitExceptions(submitInfo.Account, args[0]); err != nil {
logger.L().Fatal(err.Error())
}
},
}
func init() {
submitCmd.PersistentFlags().StringVarP(&submitInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file")
rootCmd.AddCommand(submitCmd)
submitCmd.AddCommand(submitExceptionsCmd)
}

View File

@@ -1,25 +0,0 @@
package cmd
import (
"fmt"
"os"
"github.com/armosec/kubescape/cautils"
"github.com/spf13/cobra"
)
var versionCmd = &cobra.Command{
Use: "version",
Short: "Get current version",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
v := cautils.NewIVersionCheckHandler()
v.CheckLatestVersion(cautils.NewVersionCheckRequest(cautils.BuildNumber, "", "", "version"))
fmt.Fprintln(os.Stdout, "Your current version is: "+cautils.BuildNumber)
return nil
},
}
func init() {
rootCmd.AddCommand(versionCmd)
}

View File

@@ -0,0 +1,49 @@
package completion
import (
"os"
"strings"
"github.com/spf13/cobra"
)
var completionCmdExamples = `
# Enable BASH shell autocompletion
$ source <(kubescape completion bash)
$ echo 'source <(kubescape completion bash)' >> ~/.bashrc
# Enable ZSH shell autocompletion
$ source <(kubectl completion zsh)
$ echo 'source <(kubectl completion zsh)' >> "${fpath[1]}/_kubectl"
`
func GetCompletionCmd() *cobra.Command {
completionCmd := &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate autocompletion script",
Long: "To load completions",
Example: completionCmdExamples,
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.ExactValidArgs(1),
Run: func(cmd *cobra.Command, args []string) {
switch strings.ToLower(args[0]) {
case "bash":
cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
}
},
}
return completionCmd
}
// func init() {
// rootCmd.AddCommand(completionCmd)
// }

45
cmd/config/config.go Normal file
View File

@@ -0,0 +1,45 @@
package config
import (
"github.com/armosec/kubescape/v2/core/meta"
"github.com/spf13/cobra"
)
var (
configExample = `
# View cached configurations
kubescape config view
# Delete cached configurations
kubescape config delete
# Set cached configurations
kubescape config set --help
`
setConfigExample = `
# Set account id
kubescape config set accountID <account id>
# Set client id
kubescape config set clientID <client id>
# Set access key
kubescape config set secretKey <access key>
`
)
func GetConfigCmd(ks meta.IKubescape) *cobra.Command {
// configCmd represents the config command
configCmd := &cobra.Command{
Use: "config",
Short: "Handle cached configurations",
Example: configExample,
}
configCmd.AddCommand(getDeleteCmd(ks))
configCmd.AddCommand(getSetCmd(ks))
configCmd.AddCommand(getViewCmd(ks))
return configCmd
}

21
cmd/config/delete.go Normal file
View File

@@ -0,0 +1,21 @@
package config
import (
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/meta"
v1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
)
func getDeleteCmd(ks meta.IKubescape) *cobra.Command {
return &cobra.Command{
Use: "delete",
Short: "Delete cached configurations",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
if err := ks.DeleteCachedConfig(&v1.DeleteConfig{}); err != nil {
logger.L().Fatal(err.Error())
}
},
}
}

69
cmd/config/set.go Normal file
View File

@@ -0,0 +1,69 @@
package config
import (
"fmt"
"strings"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/meta"
metav1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
)
func getSetCmd(ks meta.IKubescape) *cobra.Command {
// configCmd represents the config command
configSetCmd := &cobra.Command{
Use: "set",
Short: fmt.Sprintf("Set configurations, supported: %s", strings.Join(stringKeysToSlice(supportConfigSet), "/")),
Example: setConfigExample,
ValidArgs: stringKeysToSlice(supportConfigSet),
RunE: func(cmd *cobra.Command, args []string) error {
setConfig, err := parseSetArgs(args)
if err != nil {
return err
}
if err := ks.SetCachedConfig(setConfig); err != nil {
logger.L().Fatal(err.Error())
}
return nil
},
}
return configSetCmd
}
var supportConfigSet = map[string]func(*metav1.SetConfig, string){
"accountID": func(s *metav1.SetConfig, account string) { s.Account = account },
"clientID": func(s *metav1.SetConfig, clientID string) { s.ClientID = clientID },
"secretKey": func(s *metav1.SetConfig, secretKey string) { s.SecretKey = secretKey },
}
func stringKeysToSlice(m map[string]func(*metav1.SetConfig, string)) []string {
l := []string{}
for i := range m {
l = append(l, i)
}
return l
}
func parseSetArgs(args []string) (*metav1.SetConfig, error) {
var key string
var value string
if len(args) == 1 {
if keyValue := strings.Split(args[0], "="); len(keyValue) == 2 {
key = keyValue[0]
value = keyValue[1]
}
} else if len(args) == 2 {
key = args[0]
value = args[1]
}
setConfig := &metav1.SetConfig{}
if setConfigFunc, ok := supportConfigSet[key]; ok {
setConfigFunc(setConfig, value)
} else {
return setConfig, fmt.Errorf("key '%s' unknown . supported: %s", key, strings.Join(stringKeysToSlice(supportConfigSet), "/"))
}
return setConfig, nil
}

25
cmd/config/view.go Normal file
View File

@@ -0,0 +1,25 @@
package config
import (
"os"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/meta"
v1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
)
func getViewCmd(ks meta.IKubescape) *cobra.Command {
// configCmd represents the config command
return &cobra.Command{
Use: "view",
Short: "View cached configurations",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
if err := ks.ViewCachedConfig(&v1.ViewConfig{Writer: os.Stdout}); err != nil {
logger.L().Fatal(err.Error())
}
},
}
}

32
cmd/delete/delete.go Normal file
View File

@@ -0,0 +1,32 @@
package delete
import (
"github.com/armosec/kubescape/v2/core/meta"
v1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
)
var deleteExceptionsExamples = `
# Delete single exception
kubescape delete exceptions "exception name"
# Delete multiple exceptions
kubescape delete exceptions "first exception;second exception;third exception"
`
func GetDeleteCmd(ks meta.IKubescape) *cobra.Command {
var deleteInfo v1.Delete
var deleteCmd = &cobra.Command{
Use: "delete <command>",
Short: "Delete configurations in Kubescape SaaS version",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
},
}
deleteCmd.PersistentFlags().StringVarP(&deleteInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file")
deleteCmd.AddCommand(getExceptionsCmd(ks, &deleteInfo))
return deleteCmd
}

34
cmd/delete/exceptions.go Normal file
View File

@@ -0,0 +1,34 @@
package delete
import (
"fmt"
"strings"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/meta"
v1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
)
func getExceptionsCmd(ks meta.IKubescape, deleteInfo *v1.Delete) *cobra.Command {
return &cobra.Command{
Use: "exceptions <exception name>",
Short: "Delete exceptions from Kubescape SaaS version. Run 'kubescape list exceptions' for all exceptions names",
Example: deleteExceptionsExamples,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("missing exceptions names")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
exceptionsNames := strings.Split(args[0], ";")
if len(exceptionsNames) == 0 {
logger.L().Fatal("missing exceptions names")
}
if err := ks.DeleteExceptions(&v1.DeleteExceptions{Account: deleteInfo.Account, Exceptions: exceptionsNames}); err != nil {
logger.L().Fatal(err.Error())
}
},
}
}

79
cmd/download/download.go Normal file
View File

@@ -0,0 +1,79 @@
package download
import (
"fmt"
"path/filepath"
"strings"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/core"
"github.com/armosec/kubescape/v2/core/meta"
v1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
)
var (
downloadExample = `
# Download all artifacts and save them in the default path (~/.kubescape)
kubescape download artifacts
download
# Download all artifacts and save them in /tmp path
kubescape download artifacts --output /tmp
# Download the NSA framework. Run 'kubescape list frameworks' for all frameworks names
kubescape download framework nsa
# Download the "Allowed hostPath" control. Run 'kubescape list controls' for all controls names
kubescape download control "Allowed hostPath"
# Download the "C-0001" control. Run 'kubescape list controls --id' for all controls ids
kubescape download control C-0001
# Download the configured exceptions
kubescape download exceptions
# Download the configured controls-inputs
kubescape download controls-inputs
`
)
func GeDownloadCmd(ks meta.IKubescape) *cobra.Command {
var downloadInfo = v1.DownloadInfo{}
downloadCmd := &cobra.Command{
Use: "download <policy> <policy name>",
Short: fmt.Sprintf("Download %s", strings.Join(core.DownloadSupportCommands(), ",")),
Long: ``,
Example: downloadExample,
Args: func(cmd *cobra.Command, args []string) error {
supported := strings.Join(core.DownloadSupportCommands(), ",")
if len(args) < 1 {
return fmt.Errorf("policy type required, supported: %v", supported)
}
if cautils.StringInSlice(core.DownloadSupportCommands(), args[0]) == cautils.ValueNotFound {
return fmt.Errorf("invalid parameter '%s'. Supported parameters: %s", args[0], supported)
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if filepath.Ext(downloadInfo.Path) == ".json" {
downloadInfo.Path, downloadInfo.FileName = filepath.Split(downloadInfo.Path)
}
downloadInfo.Target = args[0]
if len(args) >= 2 {
downloadInfo.Name = args[1]
}
if err := ks.Download(&downloadInfo); err != nil {
logger.L().Fatal(err.Error())
}
return nil
},
}
downloadCmd.PersistentFlags().StringVarP(&downloadInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file")
downloadCmd.Flags().StringVarP(&downloadInfo.Path, "output", "o", "", "Output file. If not specified, will save in `~/.kubescape/<policy name>.json`")
return downloadCmd
}

67
cmd/list/list.go Normal file
View File

@@ -0,0 +1,67 @@
package list
import (
"fmt"
"strings"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/core"
"github.com/armosec/kubescape/v2/core/meta"
v1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
)
var (
listExample = `
# List default supported frameworks names
kubescape list frameworks
# List all supported frameworks names
kubescape list frameworks --account <account id>
# List all supported controls names
kubescape list controls
# List all supported controls ids
kubescape list controls --id
Control documentation:
https://hub.armo.cloud/docs/controls
`
)
func GetListCmd(ks meta.IKubescape) *cobra.Command {
var listPolicies = v1.ListPolicies{}
listCmd := &cobra.Command{
Use: "list <policy> [flags]",
Short: "List frameworks/controls will list the supported frameworks and controls",
Long: ``,
Example: listExample,
Args: func(cmd *cobra.Command, args []string) error {
supported := strings.Join(core.ListSupportActions(), ",")
if len(args) < 1 {
return fmt.Errorf("policy type requeued, supported: %s", supported)
}
if cautils.StringInSlice(core.ListSupportActions(), args[0]) == cautils.ValueNotFound {
return fmt.Errorf("invalid parameter '%s'. Supported parameters: %s", args[0], supported)
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
listPolicies.Target = args[0]
if err := ks.List(&listPolicies); err != nil {
logger.L().Fatal(err.Error())
}
return nil
},
}
listCmd.PersistentFlags().StringVar(&listPolicies.Account, "account", "", "Armo portal account ID. Default will load account ID from configMap or config file")
listCmd.PersistentFlags().StringVar(&listPolicies.Format, "format", "pretty-print", "output format. supported: 'pretty-printer'/'json'")
listCmd.PersistentFlags().BoolVarP(&listPolicies.ListIDs, "id", "", false, "List control ID's instead of controls names")
return listCmd
}

87
cmd/root.go Normal file
View File

@@ -0,0 +1,87 @@
package cmd
import (
"fmt"
"strings"
"github.com/armosec/kubescape/v2/cmd/completion"
"github.com/armosec/kubescape/v2/cmd/config"
"github.com/armosec/kubescape/v2/cmd/delete"
"github.com/armosec/kubescape/v2/cmd/download"
"github.com/armosec/kubescape/v2/cmd/list"
"github.com/armosec/kubescape/v2/cmd/scan"
"github.com/armosec/kubescape/v2/cmd/submit"
"github.com/armosec/kubescape/v2/cmd/version"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/armosec/kubescape/v2/core/cautils/getter"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
"github.com/armosec/kubescape/v2/core/core"
"github.com/armosec/kubescape/v2/core/meta"
"github.com/spf13/cobra"
)
var rootInfo cautils.RootInfo
var ksExamples = `
# Scan command
kubescape scan --submit
# List supported frameworks
kubescape list frameworks
# Download artifacts (air-gapped environment support)
kubescape download artifacts
# View cached configurations
kubescape config view
`
func NewDefaultKubescapeCommand() *cobra.Command {
ks := core.NewKubescape()
return getRootCmd(ks)
}
func getRootCmd(ks meta.IKubescape) *cobra.Command {
rootCmd := &cobra.Command{
Use: "kubescape",
Version: cautils.BuildNumber,
Short: "Kubescape is a tool for testing Kubernetes security posture",
Long: `Based on NSA \ MITRE ATT&CK® and other frameworks specifications`,
Example: ksExamples,
}
rootCmd.PersistentFlags().StringVar(&rootInfo.ArmoBEURLsDep, "environment", "", envFlagUsage)
rootCmd.PersistentFlags().StringVar(&rootInfo.ArmoBEURLs, "env", "", envFlagUsage)
rootCmd.PersistentFlags().MarkDeprecated("environment", "use 'env' instead")
rootCmd.PersistentFlags().MarkHidden("environment")
rootCmd.PersistentFlags().MarkHidden("env")
rootCmd.PersistentFlags().StringVar(&rootInfo.LoggerName, "logger-name", "", fmt.Sprintf("Logger name. Supported: %s [$KS_LOGGER_NAME]", strings.Join(logger.ListLoggersNames(), "/")))
rootCmd.PersistentFlags().MarkHidden("logger-name")
rootCmd.PersistentFlags().StringVarP(&rootInfo.Logger, "logger", "l", helpers.InfoLevel.String(), fmt.Sprintf("Logger level. Supported: %s [$KS_LOGGER]", strings.Join(helpers.SupportedLevels(), "/")))
rootCmd.PersistentFlags().StringVar(&rootInfo.CacheDir, "cache-dir", getter.DefaultLocalStore, "Cache directory [$KS_CACHE_DIR]")
rootCmd.PersistentFlags().BoolVarP(&rootInfo.DisableColor, "disable-color", "", false, "Disable Color output for logging")
cobra.OnInitialize(initLogger, initLoggerLevel, initEnvironment, initCacheDir)
// Supported commands
rootCmd.AddCommand(scan.GetScanCommand(ks))
rootCmd.AddCommand(download.GeDownloadCmd(ks))
rootCmd.AddCommand(delete.GetDeleteCmd(ks))
rootCmd.AddCommand(list.GetListCmd(ks))
rootCmd.AddCommand(submit.GetSubmitCmd(ks))
rootCmd.AddCommand(completion.GetCompletionCmd())
rootCmd.AddCommand(version.GetVersionCmd())
rootCmd.AddCommand(config.GetConfigCmd(ks))
return rootCmd
}
func Execute() error {
ks := NewDefaultKubescapeCommand()
return ks.Execute()
}

89
cmd/rootutils.go Normal file
View File

@@ -0,0 +1,89 @@
package cmd
import (
"fmt"
"os"
"strings"
"github.com/armosec/kubescape/v2/core/cautils/getter"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
"github.com/mattn/go-isatty"
)
const envFlagUsage = "Send report results to specific URL. Format:<ReportReceiver>,<Backend>,<Frontend>.\n\t\tExample:report.armo.cloud,api.armo.cloud,portal.armo.cloud"
func initLogger() {
logger.DisableColor(rootInfo.DisableColor)
if rootInfo.LoggerName == "" {
if l := os.Getenv("KS_LOGGER_NAME"); l != "" {
rootInfo.LoggerName = l
} else {
if isatty.IsTerminal(os.Stdout.Fd()) {
rootInfo.LoggerName = "pretty"
} else {
rootInfo.LoggerName = "zap"
}
}
}
logger.InitLogger(rootInfo.LoggerName)
}
func initLoggerLevel() {
if rootInfo.Logger == helpers.InfoLevel.String() {
} else if l := os.Getenv("KS_LOGGER"); l != "" {
rootInfo.Logger = l
}
if err := logger.L().SetLevel(rootInfo.Logger); err != nil {
logger.L().Fatal(fmt.Sprintf("supported levels: %s", strings.Join(helpers.SupportedLevels(), "/")), helpers.Error(err))
}
}
func initCacheDir() {
if rootInfo.CacheDir != getter.DefaultLocalStore {
getter.DefaultLocalStore = rootInfo.CacheDir
} else if cacheDir := os.Getenv("KS_CACHE_DIR"); cacheDir != "" {
getter.DefaultLocalStore = cacheDir
} else {
return // using default cache dir location
}
logger.L().Debug("cache dir updated", helpers.String("path", getter.DefaultLocalStore))
}
func initEnvironment() {
if rootInfo.ArmoBEURLs == "" {
rootInfo.ArmoBEURLs = rootInfo.ArmoBEURLsDep
}
urlSlices := strings.Split(rootInfo.ArmoBEURLs, ",")
if len(urlSlices) != 1 && len(urlSlices) < 3 {
logger.L().Fatal("expected at least 3 URLs (report, api, frontend, auth)")
}
switch len(urlSlices) {
case 1:
switch urlSlices[0] {
case "dev", "development":
getter.SetARMOAPIConnector(getter.NewARMOAPIDev())
case "stage", "staging":
getter.SetARMOAPIConnector(getter.NewARMOAPIStaging())
case "":
getter.SetARMOAPIConnector(getter.NewARMOAPIProd())
default:
logger.L().Fatal("--environment flag usage: " + envFlagUsage)
}
case 2:
logger.L().Fatal("--environment flag usage: " + envFlagUsage)
case 3, 4:
var armoAUTHURL string
armoERURL := urlSlices[0] // mandatory
armoBEURL := urlSlices[1] // mandatory
armoFEURL := urlSlices[2] // mandatory
if len(urlSlices) >= 4 {
armoAUTHURL = urlSlices[3]
}
getter.SetARMOAPIConnector(getter.NewARMOAPICustomized(armoERURL, armoBEURL, armoFEURL, armoAUTHURL))
}
}

107
cmd/scan/control.go Normal file
View File

@@ -0,0 +1,107 @@
package scan
import (
"fmt"
"io"
"os"
"strings"
apisv1 "github.com/armosec/opa-utils/httpserver/apis/v1"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
"github.com/armosec/kubescape/v2/core/meta"
"github.com/armosec/opa-utils/reporthandling"
"github.com/enescakir/emoji"
"github.com/spf13/cobra"
)
var (
controlExample = `
# Scan the 'privileged container' control
kubescape scan control "privileged container"
# Scan list of controls separated with a comma
kubescape scan control "privileged container","allowed hostpath"
# Scan list of controls using the control ID separated with a comma
kubescape scan control C-0058,C-0057
Run 'kubescape list controls' for the list of supported controls
Control documentation:
https://hub.armo.cloud/docs/controls
`
)
// controlCmd represents the control command
func getControlCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Command {
return &cobra.Command{
Use: "control <control names list>/<control ids list>",
Short: "The controls you wish to use. Run 'kubescape list controls' for the list of supported controls",
Example: controlExample,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
controls := strings.Split(args[0], ",")
if len(controls) > 1 {
for _, control := range controls {
if control == "" {
return fmt.Errorf("usage: <control-0>,<control-1>")
}
}
}
} else {
return fmt.Errorf("requires at least one control name")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
// flagValidationControl(scanInfo)
scanInfo.PolicyIdentifier = []reporthandling.PolicyIdentifier{}
if len(args) == 0 {
scanInfo.ScanAll = true
} else { // expected control or list of control sepparated by ","
// Read controls from input args
scanInfo.SetPolicyIdentifiers(strings.Split(args[0], ","), apisv1.KindControl)
if len(args) > 1 {
if len(args[1:]) == 0 || args[1] != "-" {
scanInfo.InputPatterns = []string{args[1]}
} else { // store stdin to file - do NOT move to separate function !!
tempFile, err := os.CreateTemp(".", "tmp-kubescape*.yaml")
if err != nil {
return err
}
defer os.Remove(tempFile.Name())
if _, err := io.Copy(tempFile, os.Stdin); err != nil {
return err
}
scanInfo.InputPatterns = []string{tempFile.Name()}
}
}
}
scanInfo.FrameworkScan = false
results, err := ks.Scan(scanInfo)
if err != nil {
logger.L().Fatal(err.Error())
}
if err := results.HandleResults(); err != nil {
logger.L().Fatal(err.Error())
}
if !scanInfo.VerboseMode {
cautils.SimpleDisplay(os.Stderr, "%s Run with '--verbose'/'-v' flag for detailed resources view\n\n", emoji.Detective)
}
if results.GetRiskScore() > float32(scanInfo.FailThreshold) {
logger.L().Fatal("scan risk-score is above permitted threshold", helpers.String("risk-score", fmt.Sprintf("%.2f", results.GetRiskScore())), helpers.String("fail-threshold", fmt.Sprintf("%.2f", scanInfo.FailThreshold)))
}
return nil
},
}
}

128
cmd/scan/framework.go Normal file
View File

@@ -0,0 +1,128 @@
package scan
import (
"fmt"
"io"
"os"
"strings"
apisv1 "github.com/armosec/opa-utils/httpserver/apis/v1"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
"github.com/armosec/kubescape/v2/core/meta"
"github.com/enescakir/emoji"
"github.com/spf13/cobra"
)
var (
frameworkExample = `
# Scan all frameworks and submit the results
kubescape scan framework all --submit
# Scan the NSA framework
kubescape scan framework nsa
# Scan the NSA and MITRE framework
kubescape scan framework nsa,mitre
# Scan all frameworks
kubescape scan framework all
# Scan kubernetes YAML manifest files (single file or glob)
kubescape scan framework nsa *.yaml
Run 'kubescape list frameworks' for the list of supported frameworks
`
)
func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Command {
return &cobra.Command{
Use: "framework <framework names list> [`<glob pattern>`/`-`] [flags]",
Short: "The framework you wish to use. Run 'kubescape list frameworks' for the list of supported frameworks",
Example: frameworkExample,
Long: "Execute a scan on a running Kubernetes cluster or `yaml`/`json` files (use glob) or `-` for stdin",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
frameworks := strings.Split(args[0], ",")
if len(frameworks) > 1 {
for _, framework := range frameworks {
if framework == "" {
return fmt.Errorf("usage: <framework-0>,<framework-1>")
}
}
}
} else {
return fmt.Errorf("requires at least one framework name")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := flagValidationFramework(scanInfo); err != nil {
return err
}
scanInfo.FrameworkScan = true
var frameworks []string
if len(args) == 0 { // scan all frameworks
scanInfo.ScanAll = true
} else {
// Read frameworks from input args
frameworks = strings.Split(args[0], ",")
if cautils.StringInSlice(frameworks, "all") != cautils.ValueNotFound {
scanInfo.ScanAll = true
frameworks = []string{}
}
if len(args) > 1 {
if len(args[1:]) == 0 || args[1] != "-" {
scanInfo.InputPatterns = []string{args[1]}
} else { // store stdin to file - do NOT move to separate function !!
tempFile, err := os.CreateTemp(".", "tmp-kubescape*.yaml")
if err != nil {
return err
}
defer os.Remove(tempFile.Name())
if _, err := io.Copy(tempFile, os.Stdin); err != nil {
return err
}
scanInfo.InputPatterns = []string{tempFile.Name()}
}
}
}
scanInfo.FrameworkScan = true
scanInfo.SetPolicyIdentifiers(frameworks, apisv1.KindFramework)
results, err := ks.Scan(scanInfo)
if err != nil {
logger.L().Fatal(err.Error())
}
if err = results.HandleResults(); err != nil {
logger.L().Fatal(err.Error())
}
if !scanInfo.VerboseMode {
cautils.SimpleDisplay(os.Stderr, "%s Run with '--verbose'/'-v' flag for detailed resources view\n\n", emoji.Detective)
}
if results.GetRiskScore() > float32(scanInfo.FailThreshold) {
logger.L().Fatal("scan risk-score is above permitted threshold", helpers.String("risk-score", fmt.Sprintf("%.2f", results.GetRiskScore())), helpers.String("fail-threshold", fmt.Sprintf("%.2f", scanInfo.FailThreshold)))
}
return nil
},
}
}
func flagValidationFramework(scanInfo *cautils.ScanInfo) error {
if scanInfo.Submit && scanInfo.Local {
return fmt.Errorf("you can use `keep-local` or `submit`, but not both")
}
if 100 < scanInfo.FailThreshold || 0 > scanInfo.FailThreshold {
return fmt.Errorf("bad argument: out of range threshold")
}
return nil
}

View File

@@ -1,18 +1,17 @@
package cmd
package scan
import (
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/armosec/kubescape/v2/core/meta"
"github.com/spf13/cobra"
)
var scanInfo cautils.ScanInfo
var scanCmdExamples = `
Scan command is for scanning an existing cluster or kubernetes manifest files based on pre-defind frameworks
# Scan current cluster with all frameworks
kubescape scan --submit --enable-host-scan
kubescape scan --submit --enable-host-scan --verbose
# Scan kubernetes YAML manifest files
kubescape scan *.yaml
@@ -28,40 +27,41 @@ var scanCmdExamples = `
`
// scanCmd represents the scan command
var scanCmd = &cobra.Command{
Use: "scan",
Short: "Scan the current running cluster or yaml files",
Long: `The action you want to perform`,
Example: scanCmdExamples,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
if args[0] != "framework" && args[0] != "control" {
scanInfo.ScanAll = true
return frameworkCmd.RunE(cmd, append([]string{"all"}, args...))
func GetScanCommand(ks meta.IKubescape) *cobra.Command {
var scanInfo cautils.ScanInfo
// scanCmd represents the scan command
scanCmd := &cobra.Command{
Use: "scan",
Short: "Scan the current running cluster or yaml files",
Long: `The action you want to perform`,
Example: scanCmdExamples,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
if args[0] != "framework" && args[0] != "control" {
scanInfo.ScanAll = true
return getFrameworkCmd(ks, &scanInfo).RunE(cmd, append([]string{"all"}, args...))
}
}
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
scanInfo.ScanAll = true
return frameworkCmd.RunE(cmd, []string{"all"})
}
return nil
},
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
func frameworkInitConfig() {
k8sinterface.SetClusterContextName(scanInfo.KubeContext)
}
func init() {
if len(args) == 0 {
scanInfo.ScanAll = true
return getFrameworkCmd(ks, &scanInfo).RunE(cmd, []string{"all"})
}
return nil
},
PreRun: func(cmd *cobra.Command, args []string) {
k8sinterface.SetClusterContextName(scanInfo.KubeContext)
},
PostRun: func(cmd *cobra.Command, args []string) {
// TODO - revert context
},
}
cobra.OnInitialize(frameworkInitConfig)
rootCmd.AddCommand(scanCmd)
scanCmd.PersistentFlags().StringVarP(&scanInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file")
scanCmd.PersistentFlags().StringVarP(&scanInfo.Account, "account", "", "", "ARMO portal account ID. Default will load account ID from configMap or config file")
scanCmd.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")
@@ -70,23 +70,30 @@ func init() {
scanCmd.PersistentFlags().Float32VarP(&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","pdf"`)
scanCmd.PersistentFlags().StringVar(&scanInfo.IncludeNamespaces, "include-namespaces", "", "scan specific namespaces. e.g: --include-namespaces ns-a,ns-b")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Local, "keep-local", "", false, "If you do not want your Kubescape results reported to Armo backend. Use this flag if you ran with the '--submit' flag in the past and you do not want to submit your current scan results")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Local, "keep-local", "", false, "If you do not want your Kubescape results reported to ARMO backend. Use this flag if you ran with the '--submit' flag in the past and you do not want to submit your current scan results")
scanCmd.PersistentFlags().StringVarP(&scanInfo.Output, "output", "o", "", "Output file. Print output to file and not stdout")
scanCmd.PersistentFlags().BoolVar(&scanInfo.VerboseMode, "verbose", false, "Display all of the input resources and not only failed resources")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.VerboseMode, "verbose", "v", false, "Display all of the input resources and not only failed resources")
scanCmd.PersistentFlags().BoolVar(&scanInfo.UseDefault, "use-default", false, "Load local policy object from default path. If not used will download latest")
scanCmd.PersistentFlags().StringSliceVar(&scanInfo.UseFrom, "use-from", nil, "Load local policy object from specified path. If not used will download latest")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Silent, "silent", "s", false, "Silent progress messages")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Submit, "submit", "", false, "Send the scan results to Armo management portal where you can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more. By default the results are not submitted")
scanCmd.PersistentFlags().StringVar(&scanInfo.HostSensorYamlPath, "host-scan-yaml", "", "Override default host sensor DaemonSet. Use this flag cautiously")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Submit, "submit", "", false, "Send the scan results to ARMO management portal where you can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more. By default the results are not submitted")
scanCmd.PersistentFlags().StringVar(&scanInfo.HostSensorYamlPath, "host-scan-yaml", "", "Override default host scanner DaemonSet. Use this flag cautiously")
scanCmd.PersistentFlags().StringVar(&scanInfo.FormatVersion, "format-version", "v1", "Output object can be differnet between versions, this is for maintaining backward and forward compatibility. Supported:'v1'/'v2'")
// Deprecated flags - remove 1.May.2022
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Silent, "silent", "s", false, "Silent progress messages")
scanCmd.PersistentFlags().MarkDeprecated("silent", "use '--logger' flag instead. Flag will be removed at 1.May.2022")
// hidden flags
scanCmd.PersistentFlags().MarkHidden("host-scan-yaml") // this flag should be used very cautiously. We prefer users will not use it at all unless the DaemoSet can not run pods on the nodes
scanCmd.PersistentFlags().MarkHidden("host-scan-yaml") // this flag should be used very cautiously. We prefer users will not use it at all unless the DaemonSet can not run pods on the nodes
scanCmd.PersistentFlags().MarkHidden("silent") // this flag should be deprecated since we added the --logger support
// scanCmd.PersistentFlags().MarkHidden("format-version") // meant for testing different output approaches and not for common use
hostF := scanCmd.PersistentFlags().VarPF(&scanInfo.HostSensorEnabled, "enable-host-scan", "", "Deploy ARMO K8s host-sensor daemonset in the scanned cluster. Deleting it right after we collecting the data. Required to collect valueable data from cluster nodes for certain controls. Yaml file: https://raw.githubusercontent.com/armosec/kubescape/master/hostsensorutils/hostsensor.yaml")
hostF := scanCmd.PersistentFlags().VarPF(&scanInfo.HostSensorEnabled, "enable-host-scan", "", "Deploy ARMO K8s host-sensor daemonset in the scanned cluster. Deleting it right after we collecting the data. Required to collect valuable data from cluster nodes for certain controls. Yaml file: https://raw.githubusercontent.com/armosec/kubescape/master/hostsensorutils/hostsensor.yaml")
hostF.NoOptDefVal = "true"
hostF.DefValue = "false, for no TTY in stdin"
scanCmd.AddCommand(getControlCmd(ks, &scanInfo))
scanCmd.AddCommand(getFrameworkCmd(ks, &scanInfo))
return scanCmd
}

29
cmd/submit/exceptions.go Normal file
View File

@@ -0,0 +1,29 @@
package submit
import (
"fmt"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/meta"
metav1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
)
func getExceptionsCmd(ks meta.IKubescape, submitInfo *metav1.Submit) *cobra.Command {
return &cobra.Command{
Use: "exceptions <full path to exceptins file>",
Short: "Submit exceptions to the Kubescape SaaS version",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("missing full path to exceptions file")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
if err := ks.SubmitExceptions(submitInfo.Account, args[0]); err != nil {
logger.L().Fatal(err.Error())
}
},
}
}

68
cmd/submit/rbac.go Normal file
View File

@@ -0,0 +1,68 @@
package submit
import (
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/armosec/kubescape/v2/core/cautils/getter"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
"github.com/armosec/kubescape/v2/core/meta"
"github.com/armosec/kubescape/v2/core/meta/cliinterfaces"
v1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1"
reporterv1 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v1"
"github.com/armosec/rbac-utils/rbacscanner"
"github.com/spf13/cobra"
)
// getRBACCmd represents the RBAC command
func getRBACCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
return &cobra.Command{
Use: "rbac \nExample:\n$ kubescape submit rbac",
Short: "Submit cluster's Role-Based Access Control(RBAC)",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
k8s := k8sinterface.NewKubernetesApi()
// get config
clusterConfig := getTenantConfig(submitInfo.Account, "", k8s)
if err := clusterConfig.SetTenant(); err != nil {
logger.L().Error("failed setting account ID", helpers.Error(err))
}
// list RBAC
rbacObjects := cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, clusterConfig.GetAccountID(), clusterConfig.GetContextName()))
// submit resources
r := reporterv1.NewReportEventReceiver(clusterConfig.GetConfigObj())
submitInterfaces := cliinterfaces.SubmitInterfaces{
ClusterConfig: clusterConfig,
SubmitObjects: rbacObjects,
Reporter: r,
}
if err := ks.Submit(submitInterfaces); err != nil {
logger.L().Fatal(err.Error())
}
return nil
},
}
}
// getKubernetesApi
func getKubernetesApi() *k8sinterface.KubernetesApi {
if !k8sinterface.IsConnectedToCluster() {
return nil
}
return k8sinterface.NewKubernetesApi()
}
func getTenantConfig(Account, clusterName string, k8s *k8sinterface.KubernetesApi) cautils.ITenantConfig {
if !k8sinterface.IsConnectedToCluster() || k8s == nil {
return cautils.NewLocalConfig(getter.GetArmoAPIConnector(), Account, clusterName)
}
return cautils.NewClusterConfig(k8s, getter.GetArmoAPIConnector(), Account, clusterName)
}

119
cmd/submit/results.go Normal file
View File

@@ -0,0 +1,119 @@
package submit
import (
"encoding/json"
"fmt"
"os"
"time"
"github.com/armosec/k8s-interface/workloadinterface"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
"github.com/armosec/kubescape/v2/core/meta"
"github.com/armosec/kubescape/v2/core/meta/cliinterfaces"
v1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1"
"github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter"
reporterv1 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v1"
reporterv2 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v2"
"github.com/armosec/opa-utils/reporthandling"
"github.com/google/uuid"
"github.com/spf13/cobra"
)
var formatVersion string
type ResultsObject struct {
filePath string
customerGUID string
clusterName string
}
func NewResultsObject(customerGUID, clusterName, filePath string) *ResultsObject {
return &ResultsObject{
filePath: filePath,
customerGUID: customerGUID,
clusterName: clusterName,
}
}
func (resultsObject *ResultsObject) SetResourcesReport() (*reporthandling.PostureReport, error) {
// load framework results from json file
frameworkReports, err := loadResultsFromFile(resultsObject.filePath)
if err != nil {
return nil, err
}
return &reporthandling.PostureReport{
FrameworkReports: frameworkReports,
ReportID: uuid.NewString(),
ReportGenerationTime: time.Now().UTC(),
CustomerGUID: resultsObject.customerGUID,
ClusterName: resultsObject.clusterName,
}, nil
}
func (resultsObject *ResultsObject) ListAllResources() (map[string]workloadinterface.IMetadata, error) {
return map[string]workloadinterface.IMetadata{}, nil
}
func getResultsCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
var resultsCmd = &cobra.Command{
Use: "results <json file>\nExample:\n$ kubescape submit results path/to/results.json --format-version v2",
Short: "Submit a pre scanned results file. The file must be in json format",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("missing results file")
}
k8s := getKubernetesApi()
// get config
clusterConfig := getTenantConfig(submitInfo.Account, "", k8s)
if err := clusterConfig.SetTenant(); err != nil {
logger.L().Error("failed setting account ID", helpers.Error(err))
}
resultsObjects := NewResultsObject(clusterConfig.GetAccountID(), clusterConfig.GetContextName(), args[0])
// submit resources
var r reporter.IReport
switch formatVersion {
case "v2":
r = reporterv2.NewReportEventReceiver(clusterConfig.GetConfigObj(), "")
default:
logger.L().Warning("Deprecated results version. run with '--format-version' flag", helpers.String("your version", formatVersion), helpers.String("latest version", "v2"))
r = reporterv1.NewReportEventReceiver(clusterConfig.GetConfigObj())
}
submitInterfaces := cliinterfaces.SubmitInterfaces{
ClusterConfig: clusterConfig,
SubmitObjects: resultsObjects,
Reporter: r,
}
if err := ks.Submit(submitInterfaces); err != nil {
logger.L().Fatal(err.Error())
}
return nil
},
}
resultsCmd.PersistentFlags().StringVar(&formatVersion, "format-version", "v1", "Output object can be differnet between versions, this is for maintaining backward and forward compatibility. Supported:'v1'/'v2'")
return resultsCmd
}
func loadResultsFromFile(filePath string) ([]reporthandling.FrameworkReport, error) {
frameworkReports := []reporthandling.FrameworkReport{}
f, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
if err = json.Unmarshal(f, &frameworkReports); err != nil {
frameworkReport := reporthandling.FrameworkReport{}
if err = json.Unmarshal(f, &frameworkReport); err != nil {
return frameworkReports, err
}
frameworkReports = append(frameworkReports, frameworkReport)
}
return frameworkReports, nil
}

30
cmd/submit/submit.go Normal file
View File

@@ -0,0 +1,30 @@
package submit
import (
"github.com/armosec/kubescape/v2/core/meta"
metav1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
)
var submitCmdExamples = `
`
func GetSubmitCmd(ks meta.IKubescape) *cobra.Command {
var submitInfo metav1.Submit
submitCmd := &cobra.Command{
Use: "submit <command>",
Short: "Submit an object to the Kubescape SaaS version",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
},
}
submitCmd.PersistentFlags().StringVarP(&submitInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file")
submitCmd.AddCommand(getExceptionsCmd(ks, &submitInfo))
submitCmd.AddCommand(getResultsCmd(ks, &submitInfo))
submitCmd.AddCommand(getRBACCmd(ks, &submitInfo))
return submitCmd
}

24
cmd/version/version.go Normal file
View File

@@ -0,0 +1,24 @@
package version
import (
"fmt"
"os"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/spf13/cobra"
)
func GetVersionCmd() *cobra.Command {
versionCmd := &cobra.Command{
Use: "version",
Short: "Get current version",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
v := cautils.NewIVersionCheckHandler()
v.CheckLatestVersion(cautils.NewVersionCheckRequest(cautils.BuildNumber, "", "", "version"))
fmt.Fprintln(os.Stdout, "Your current version is: "+cautils.BuildNumber)
return nil
},
}
return versionCmd
}

14
core/README.md Normal file
View File

@@ -0,0 +1,14 @@
# Kubescape core package
```go
// initialize kubescape
ks := core.NewKubescape()
// scan cluster
results, err := ks.Scan(&cautils.ScanInfo{})
// convert scan results to json
jsonRes, err := results.ToJson()
```

View File

@@ -10,7 +10,8 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/v2/core/cautils/getter"
"github.com/armosec/kubescape/v2/core/cautils/logger"
corev1 "k8s.io/api/core/v1"
)
@@ -66,8 +67,9 @@ type ITenantConfig interface {
DeleteCachedConfig() error
// getters
GetClusterName() string
GetContextName() string
GetAccountID() string
GetTennatEmail() string
GetConfigObj() *ConfigObj
// GetBackendAPI() getter.IBackend
// GenerateURL()
@@ -84,7 +86,8 @@ type LocalConfig struct {
configObj *ConfigObj
}
func NewLocalConfig(backendAPI getter.IBackend, customerGUID, clusterName string) *LocalConfig {
func NewLocalConfig(
backendAPI getter.IBackend, customerGUID, clusterName string) *LocalConfig {
var configObj *ConfigObj
lc := &LocalConfig{
@@ -116,8 +119,9 @@ func NewLocalConfig(backendAPI getter.IBackend, customerGUID, clusterName string
}
func (lc *LocalConfig) GetConfigObj() *ConfigObj { return lc.configObj }
func (lc *LocalConfig) GetTennatEmail() string { return lc.configObj.CustomerAdminEMail }
func (lc *LocalConfig) GetAccountID() string { return lc.configObj.AccountID }
func (lc *LocalConfig) GetClusterName() string { return lc.configObj.ClusterName }
func (lc *LocalConfig) GetContextName() string { return lc.configObj.ClusterName }
func (lc *LocalConfig) IsConfigFound() bool { return existsConfigFile() }
func (lc *LocalConfig) SetTenant() error {
@@ -134,7 +138,10 @@ func (lc *LocalConfig) UpdateCachedConfig() error {
}
func (lc *LocalConfig) DeleteCachedConfig() error {
return DeleteConfigFile()
if err := DeleteConfigFile(); err != nil {
logger.L().Warning(err.Error())
}
return nil
}
func getTenantConfigFromBE(backendAPI getter.IBackend, configObj *ConfigObj) error {
@@ -212,7 +219,7 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken
getAccountFromEnv(c.configObj)
if c.configObj.ClusterName == "" {
c.configObj.ClusterName = AdoptClusterName(k8sinterface.GetClusterName())
c.configObj.ClusterName = AdoptClusterName(k8sinterface.GetContextName())
} else { // override the cluster name if it has unwanted characters
c.configObj.ClusterName = AdoptClusterName(c.configObj.ClusterName)
}
@@ -227,6 +234,7 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken
func (c *ClusterConfig) GetConfigObj() *ConfigObj { return c.configObj }
func (c *ClusterConfig) GetDefaultNS() string { return c.configMapNamespace }
func (c *ClusterConfig) GetAccountID() string { return c.configObj.AccountID }
func (c *ClusterConfig) GetTennatEmail() string { return c.configObj.CustomerAdminEMail }
func (c *ClusterConfig) IsConfigFound() bool { return existsConfigFile() || c.existsConfigMap() }
func (c *ClusterConfig) SetTenant() error {
@@ -256,14 +264,14 @@ func (c *ClusterConfig) UpdateCachedConfig() error {
func (c *ClusterConfig) DeleteCachedConfig() error {
if err := c.deleteConfigMap(); err != nil {
return err
logger.L().Warning(err.Error())
}
if err := DeleteConfigFile(); err != nil {
return err
logger.L().Warning(err.Error())
}
return nil
}
func (c *ClusterConfig) GetClusterName() string {
func (c *ClusterConfig) GetContextName() string {
return c.configObj.ClusterName
}
@@ -323,27 +331,6 @@ func GetValueFromConfigJson(key string) (string, error) {
}
func SetKeyValueInConfigJson(key string, value string) error {
data, err := os.ReadFile(ConfigFileFullPath())
if err != nil {
return err
}
var obj map[string]interface{}
err = json.Unmarshal(data, &obj)
if err != nil {
return err
}
obj[key] = value
newData, err := json.Marshal(obj)
if err != nil {
return err
}
return os.WriteFile(ConfigFileFullPath(), newData, 0664)
}
func (c *ClusterConfig) SetKeyValueInConfigmap(key string, value string) error {
configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Get(context.Background(), c.configMapName, metav1.GetOptions{})

View File

@@ -0,0 +1,88 @@
package cautils
import (
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/k8s-interface/workloadinterface"
"github.com/armosec/opa-utils/reporthandling"
apis "github.com/armosec/opa-utils/reporthandling/apis"
"github.com/armosec/opa-utils/reporthandling/results/v1/resourcesresults"
reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2"
)
// K8SResources map[<api group>/<api version>/<resource>][]<resourceID>
type K8SResources map[string][]string
type ArmoResources map[string][]string
type OPASessionObj struct {
K8SResources *K8SResources // input k8s objects
ArmoResource *ArmoResources // input ARMO objects
Policies []reporthandling.Framework // list of frameworks to scan
AllResources map[string]workloadinterface.IMetadata // all scanned resources, map[<rtesource ID>]<resource>
ResourcesResult map[string]resourcesresults.Result // resources scan results, map[<rtesource ID>]<resource result>
ResourceSource map[string]string // resources sources, map[<rtesource ID>]<resource result>
PostureReport *reporthandling.PostureReport // scan results v1 - Remove
Report *reporthandlingv2.PostureReport // scan results v2 - Remove
Exceptions []armotypes.PostureExceptionPolicy // list of exceptions to apply on scan results
RegoInputData RegoInputData // input passed to rgo for scanning. map[<control name>][<input arguments>]
Metadata *reporthandlingv2.Metadata
InfoMap map[string]apis.StatusInfo // Map errors of resources to StatusInfo
ResourceToControlsMap map[string][]string // map[<apigroup/apiversion/resource>] = [<control_IDs>]
SessionID string // SessionID
}
func NewOPASessionObj(frameworks []reporthandling.Framework, k8sResources *K8SResources, scanInfo *ScanInfo) *OPASessionObj {
return &OPASessionObj{
Report: &reporthandlingv2.PostureReport{},
Policies: frameworks,
K8SResources: k8sResources,
AllResources: make(map[string]workloadinterface.IMetadata),
ResourcesResult: make(map[string]resourcesresults.Result),
InfoMap: make(map[string]apis.StatusInfo),
ResourceToControlsMap: make(map[string][]string),
ResourceSource: make(map[string]string),
SessionID: scanInfo.ScanID,
PostureReport: &reporthandling.PostureReport{
ClusterName: ClusterName,
CustomerGUID: CustomerGUID,
},
Metadata: scanInfoToScanMetadata(scanInfo),
}
}
func NewOPASessionObjMock() *OPASessionObj {
return &OPASessionObj{
Policies: nil,
K8SResources: nil,
AllResources: make(map[string]workloadinterface.IMetadata),
ResourcesResult: make(map[string]resourcesresults.Result),
Report: &reporthandlingv2.PostureReport{},
PostureReport: &reporthandling.PostureReport{
ClusterName: "",
CustomerGUID: "",
ReportID: "",
JobID: "",
},
}
}
type ComponentConfig struct {
Exceptions Exception `json:"exceptions"`
}
type Exception struct {
Ignore *bool `json:"ignore"` // ignore test results
MultipleScore *reporthandling.AlertScore `json:"multipleScore"` // MultipleScore number - float32
Namespaces []string `json:"namespaces"`
Regex string `json:"regex"` // not supported
}
type RegoInputData struct {
PostureControlInputs map[string][]string `json:"postureControlInputs"`
// ClusterName string `json:"clusterName"`
// K8sConfig RegoK8sConfig `json:"k8sconfig"`
}
type Policies struct {
Frameworks []string
Controls map[string]reporthandling.Control // map[<control ID>]<control>
}

View File

@@ -1,9 +1,10 @@
package cautils
import (
pkgcautils "github.com/armosec/utils-go/utils"
"golang.org/x/mod/semver"
"github.com/armosec/opa-utils/reporthandling"
"github.com/armosec/utils-go/boolutils"
)
func NewPolicies() *Policies {
@@ -39,7 +40,7 @@ func ruleWithArmoOpaDependency(attributes map[string]interface{}) bool {
return false
}
if s, ok := attributes["armoOpa"]; ok { // TODO - make global
return pkgcautils.StringToBool(s.(string))
return boolutils.StringToBool(s.(string))
}
return false
}
@@ -50,17 +51,16 @@ func ruleWithArmoOpaDependency(attributes map[string]interface{}) bool {
func isRuleKubescapeVersionCompatible(attributes map[string]interface{}, version string) bool {
if from, ok := attributes["useFromKubescapeVersion"]; ok && from != nil {
if version != "" {
if from.(string) > BuildNumber {
if semver.Compare(version, from.(string)) == -1 {
return false
}
}
}
if until, ok := attributes["useUntilKubescapeVersion"]; ok && until != nil {
if version != "" {
if until.(string) <= BuildNumber {
return false
}
} else {
if version == "" {
return false
}
if semver.Compare(version, until.(string)) >= 0 {
return false
}
}

View File

@@ -4,21 +4,11 @@ import (
"os"
"time"
"github.com/briandowns/spinner"
spinnerpkg "github.com/briandowns/spinner"
"github.com/fatih/color"
"github.com/mattn/go-isatty"
)
var silent = false
func SetSilentMode(s bool) {
silent = s
}
func IsSilent() bool {
return silent
}
var FailureDisplay = color.New(color.Bold, color.FgHiRed).FprintfFunc()
var WarningDisplay = color.New(color.Bold, color.FgHiYellow).FprintfFunc()
var FailureTextDisplay = color.New(color.Faint, color.FgHiRed).FprintfFunc()
@@ -28,18 +18,24 @@ var SimpleDisplay = color.New().FprintfFunc()
var SuccessDisplay = color.New(color.Bold, color.FgHiGreen).FprintfFunc()
var DescriptionDisplay = color.New(color.Faint, color.FgWhite).FprintfFunc()
var Spinner *spinner.Spinner
var spinner *spinnerpkg.Spinner
func StartSpinner() {
if !IsSilent() && isatty.IsTerminal(os.Stdout.Fd()) {
Spinner = spinner.New(spinner.CharSets[7], 100*time.Millisecond) // Build our new spinner
Spinner.Start()
if spinner != nil {
if !spinner.Active() {
spinner.Start()
}
return
}
if isatty.IsTerminal(os.Stdout.Fd()) {
spinner = spinnerpkg.New(spinnerpkg.CharSets[7], 100*time.Millisecond) // Build our new spinner
spinner.Start()
}
}
func StopSpinner() {
if Spinner == nil {
if spinner == nil || !spinner.Active() {
return
}
Spinner.Stop()
spinner.Stop()
}

View File

@@ -0,0 +1,7 @@
package cautils
// CA environment vars
var (
CustomerGUID = ""
ClusterName = ""
)

View File

@@ -9,14 +9,14 @@ import (
"strings"
"github.com/armosec/k8s-interface/workloadinterface"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/opa-utils/objectsenvelopes"
"gopkg.in/yaml.v2"
)
var (
YAML_PREFIX = []string{".yaml", ".yml"}
JSON_PREFIX = []string{".json"}
YAML_PREFIX = []string{"yaml", "yml"}
JSON_PREFIX = []string{"json"}
)
type FileFormat string
@@ -26,7 +26,7 @@ const (
JSON_FILE_FORMAT FileFormat = "json"
)
func LoadResourcesFromFiles(inputPatterns []string) ([]workloadinterface.IMetadata, error) {
func LoadResourcesFromFiles(inputPatterns []string) (map[string][]workloadinterface.IMetadata, error) {
files, errs := listFiles(inputPatterns)
if len(errs) > 0 {
logger.L().Error(fmt.Sprintf("%v", errs))
@@ -42,8 +42,8 @@ func LoadResourcesFromFiles(inputPatterns []string) ([]workloadinterface.IMetada
return workloads, nil
}
func loadFiles(filePaths []string) ([]workloadinterface.IMetadata, []error) {
workloads := []workloadinterface.IMetadata{}
func loadFiles(filePaths []string) (map[string][]workloadinterface.IMetadata, []error) {
workloads := make(map[string][]workloadinterface.IMetadata, 0)
errs := []error{}
for i := range filePaths {
f, err := loadFile(filePaths[i])
@@ -54,7 +54,12 @@ func loadFiles(filePaths []string) ([]workloadinterface.IMetadata, []error) {
w, e := ReadFile(f, GetFileFormat(filePaths[i]))
errs = append(errs, e...)
if w != nil {
workloads = append(workloads, w...)
if _, ok := workloads[filePaths[i]]; !ok {
workloads[filePaths[i]] = []workloadinterface.IMetadata{}
}
wSlice := workloads[filePaths[i]]
wSlice = append(wSlice, w...)
workloads[filePaths[i]] = wSlice
}
}
return workloads, errs
@@ -73,7 +78,6 @@ func ReadFile(fileContent []byte, fileFromat FileFormat) ([]workloadinterface.IM
default:
return nil, nil // []error{fmt.Errorf("file extension %s not supported", fileFromat)}
}
}
func listFiles(patterns []string) ([]string, []error) {
@@ -173,11 +177,11 @@ func convertYamlToJson(i interface{}) interface{} {
}
func IsYaml(filePath string) bool {
return StringInSlice(YAML_PREFIX, filepath.Ext(filePath)) != ValueNotFound
return StringInSlice(YAML_PREFIX, strings.ReplaceAll(filepath.Ext(filePath), ".", "")) != ValueNotFound
}
func IsJson(filePath string) bool {
return StringInSlice(JSON_PREFIX, filepath.Ext(filePath)) != ValueNotFound
return StringInSlice(JSON_PREFIX, strings.ReplaceAll(filepath.Ext(filePath), ".", "")) != ValueNotFound
}
func glob(root, pattern string) ([]string, error) {

View File

@@ -11,7 +11,7 @@ import (
func onlineBoutiquePath() string {
o, _ := os.Getwd()
return filepath.Join(filepath.Dir(o), "examples/online-boutique/*")
return filepath.Join(filepath.Dir(o), "../examples/online-boutique/*")
}
func TestListFiles(t *testing.T) {
@@ -23,6 +23,20 @@ func TestListFiles(t *testing.T) {
assert.Equal(t, 12, len(files))
}
func TestLoadResourcesFromFiles(t *testing.T) {
workloads, err := LoadResourcesFromFiles([]string{onlineBoutiquePath()})
assert.NoError(t, err)
assert.Equal(t, 12, len(workloads))
for i, w := range workloads {
switch filepath.Base(i) {
case "adservice.yaml":
assert.Equal(t, 2, len(w))
assert.Equal(t, "apps/v1//Deployment/adservice", w[0].GetID())
assert.Equal(t, "/v1//Service/adservice", w[1].GetID())
}
}
}
func TestLoadFiles(t *testing.T) {
files, _ := listFiles([]string{onlineBoutiquePath()})
_, err := loadFiles(files)

View File

@@ -0,0 +1,18 @@
package cautils
import "math"
// Float64ToInt convert float64 to int
func Float64ToInt(x float64) int {
return int(math.Round(x))
}
// Float32ToInt convert float32 to int
func Float32ToInt(x float32) int {
return Float64ToInt(float64(x))
}
// Float16ToInt convert float16 to int
func Float16ToInt(x float32) int {
return Float64ToInt(float64(x))
}

View File

@@ -0,0 +1,24 @@
package cautils
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestFloat64ToInt(t *testing.T) {
assert.Equal(t, 3, Float64ToInt(3.49))
assert.Equal(t, 4, Float64ToInt(3.5))
assert.Equal(t, 4, Float64ToInt(3.51))
}
func TestFloat32ToInt(t *testing.T) {
assert.Equal(t, 3, Float32ToInt(3.49))
assert.Equal(t, 4, Float32ToInt(3.5))
assert.Equal(t, 4, Float32ToInt(3.51))
}
func TestFloat16ToInt(t *testing.T) {
assert.Equal(t, 3, Float16ToInt(3.49))
assert.Equal(t, 4, Float16ToInt(3.5))
assert.Equal(t, 4, Float16ToInt(3.51))
}

View File

@@ -10,8 +10,8 @@ import (
"time"
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/cautils/logger/helpers"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
"github.com/armosec/opa-utils/reporthandling"
)
@@ -21,7 +21,7 @@ import (
var (
// ATTENTION!!!
// Changes in this URLs variable names, or in the usage is affecting the build process! BE CAREFULL
// Changes in this URLs variable names, or in the usage is affecting the build process! BE CAREFUL
armoERURL = "report.armo.cloud"
armoBEURL = "api.armo.cloud"
armoFEURL = "portal.armo.cloud"
@@ -62,7 +62,8 @@ func SetARMOAPIConnector(armoAPI *ArmoAPI) {
func GetArmoAPIConnector() *ArmoAPI {
if globalArmoAPIConnector == nil {
logger.L().Error("returning nil API connector")
// logger.L().Error("returning nil API connector")
SetARMOAPIConnector(NewARMOAPIProd())
}
return globalArmoAPIConnector
}
@@ -162,7 +163,6 @@ func (armoAPI *ArmoAPI) GetFramework(name string) (*reporthandling.Framework, er
if err = JSONDecoder(respStr).Decode(framework); err != nil {
return nil, err
}
SaveInFile(framework, GetDefaultPath(name+".json"))
return framework, err
}
@@ -232,7 +232,14 @@ func (armoAPI *ArmoAPI) GetAccountConfig(clusterName string) (*armotypes.Custome
}
if err = JSONDecoder(respStr).Decode(&accountConfig); err != nil {
return nil, err
// try with default scope
respStr, err = armoAPI.Get(armoAPI.getAccountConfigDefault(clusterName), nil)
if err != nil {
return nil, err
}
if err = JSONDecoder(respStr).Decode(&accountConfig); err != nil {
return nil, err
}
}
return accountConfig, nil

View File

@@ -73,6 +73,12 @@ func (armoAPI *ArmoAPI) exceptionsURL(exceptionsPolicyName string) string {
return u.String()
}
func (armoAPI *ArmoAPI) getAccountConfigDefault(clusterName string) string {
config := armoAPI.getAccountConfig(clusterName)
url := config + "&scope=customer"
return url
}
func (armoAPI *ArmoAPI) getAccountConfig(clusterName string) string {
u := url.URL{}
u.Scheme = "https"

View File

@@ -1,5 +1,7 @@
package helpers
import "time"
type StringObj struct {
key string
value string
@@ -24,3 +26,6 @@ func Error(e error) *ErrorObj { return &ErrorObj{key: "e
func Int(k string, v int) *IntObj { return &IntObj{key: k, value: v} }
func String(k, v string) *StringObj { return &StringObj{key: k, value: v} }
func Interface(k string, v interface{}) *InterfaceObj { return &InterfaceObj{key: k, value: v} }
func Time() *StringObj {
return &StringObj{key: "time", value: time.Now().Format("2006-01-02 15:04:05")}
}

View File

@@ -0,0 +1,81 @@
package logger
import (
"os"
"strings"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
"github.com/armosec/kubescape/v2/core/cautils/logger/nonelogger"
"github.com/armosec/kubescape/v2/core/cautils/logger/prettylogger"
"github.com/armosec/kubescape/v2/core/cautils/logger/zaplogger"
)
type ILogger interface {
Fatal(msg string, details ...helpers.IDetails) // print log and exit 1
Error(msg string, details ...helpers.IDetails)
Success(msg string, details ...helpers.IDetails)
Warning(msg string, details ...helpers.IDetails)
Info(msg string, details ...helpers.IDetails)
Debug(msg string, details ...helpers.IDetails)
SetLevel(level string) error
GetLevel() string
SetWriter(w *os.File)
GetWriter() *os.File
LoggerName() string
}
var l ILogger
// Return initialized logger. If logger not initialized, will call InitializeLogger() with the default value
func L() ILogger {
if l == nil {
InitDefaultLogger()
}
return l
}
/* InitLogger initialize desired logger
Use:
InitLogger("<logger name>")
Supported logger names (call ListLoggersNames() for listing supported loggers)
- "zap": Logger from package "go.uber.org/zap"
- "pretty", "colorful": Human friendly colorful logger
- "none", "mock", "empty", "ignore": Logger will not print anything
Default:
- "pretty"
e.g.
InitLogger("none") -> will initialize the mock logger
*/
func InitLogger(loggerName string) {
switch strings.ToLower(loggerName) {
case zaplogger.LoggerName:
l = zaplogger.NewZapLogger()
case prettylogger.LoggerName, "colorful":
l = prettylogger.NewPrettyLogger()
case nonelogger.LoggerName, "mock", "empty", "ignore":
l = nonelogger.NewNoneLogger()
default:
InitDefaultLogger()
}
}
func InitDefaultLogger() {
l = prettylogger.NewPrettyLogger()
}
func DisableColor(flag bool) {
prettylogger.DisableColor(flag)
}
func ListLoggersNames() []string {
return []string{prettylogger.LoggerName, zaplogger.LoggerName, nonelogger.LoggerName}
}

View File

@@ -0,0 +1,28 @@
package nonelogger
import (
"os"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
)
const LoggerName string = "none"
type NoneLogger struct {
}
func NewNoneLogger() *NoneLogger {
return &NoneLogger{}
}
func (nl *NoneLogger) GetLevel() string { return "" }
func (nl *NoneLogger) LoggerName() string { return LoggerName }
func (nl *NoneLogger) SetWriter(w *os.File) {}
func (nl *NoneLogger) GetWriter() *os.File { return nil }
func (nl *NoneLogger) SetLevel(level string) error { return nil }
func (nl *NoneLogger) Fatal(msg string, details ...helpers.IDetails) {}
func (nl *NoneLogger) Error(msg string, details ...helpers.IDetails) {}
func (nl *NoneLogger) Warning(msg string, details ...helpers.IDetails) {}
func (nl *NoneLogger) Success(msg string, details ...helpers.IDetails) {}
func (nl *NoneLogger) Info(msg string, details ...helpers.IDetails) {}
func (nl *NoneLogger) Debug(msg string, details ...helpers.IDetails) {}

View File

@@ -3,7 +3,7 @@ package prettylogger
import (
"io"
"github.com/armosec/kubescape/cautils/logger/helpers"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
"github.com/fatih/color"
)
@@ -29,3 +29,9 @@ func prefix(l helpers.Level) func(w io.Writer, format string, a ...interface{})
}
return message
}
func DisableColor(flag bool) {
if flag {
color.NoColor = true
}
}

View File

@@ -5,9 +5,11 @@ import (
"os"
"sync"
"github.com/armosec/kubescape/cautils/logger/helpers"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
)
const LoggerName string = "pretty"
type PrettyLogger struct {
writer *os.File
level helpers.Level
@@ -15,6 +17,7 @@ type PrettyLogger struct {
}
func NewPrettyLogger() *PrettyLogger {
return &PrettyLogger{
writer: os.Stderr, // default to stderr
level: helpers.InfoLevel,
@@ -25,6 +28,7 @@ func NewPrettyLogger() *PrettyLogger {
func (pl *PrettyLogger) GetLevel() string { return pl.level.String() }
func (pl *PrettyLogger) SetWriter(w *os.File) { pl.writer = w }
func (pl *PrettyLogger) GetWriter() *os.File { return pl.writer }
func (pl *PrettyLogger) LoggerName() string { return LoggerName }
func (pl *PrettyLogger) SetLevel(level string) error {
pl.level = helpers.ToLevel(level)
@@ -69,7 +73,7 @@ func (pl *PrettyLogger) print(level helpers.Level, msg string, details ...helper
func detailsToString(details []helpers.IDetails) string {
s := ""
for i := range details {
s += fmt.Sprintf("%s: %s", details[i].Key(), details[i].Value())
s += fmt.Sprintf("%s: %v", details[i].Key(), details[i].Value())
if i < len(details)-1 {
s += "; "
}

View File

@@ -3,11 +3,13 @@ package zaplogger
import (
"os"
"github.com/armosec/kubescape/cautils/logger/helpers"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
const LoggerName string = "zap"
type ZapLogger struct {
zapL *zap.Logger
cfg zap.Config
@@ -35,8 +37,7 @@ func NewZapLogger() *ZapLogger {
func (zl *ZapLogger) GetLevel() string { return zl.cfg.Level.Level().String() }
func (zl *ZapLogger) SetWriter(w *os.File) {}
func (zl *ZapLogger) GetWriter() *os.File { return nil }
func GetWriter() *os.File { return nil }
func (zl *ZapLogger) LoggerName() string { return LoggerName }
func (zl *ZapLogger) SetLevel(level string) error {
l := zapcore.Level(1)
err := l.Set(level)

View File

@@ -11,8 +11,7 @@ func ReportV2ToV1(opaSessionObj *OPASessionObj) {
if len(opaSessionObj.PostureReport.FrameworkReports) > 0 {
return // report already converted
}
opaSessionObj.PostureReport.ClusterCloudProvider = opaSessionObj.Report.ClusterCloudProvider
// opaSessionObj.PostureReport.ClusterCloudProvider = opaSessionObj.Report.ClusterCloudProvider
frameworks := []reporthandling.FrameworkReport{}

89
core/cautils/rootinfo.go Normal file
View File

@@ -0,0 +1,89 @@
package cautils
type RootInfo struct {
Logger string // logger level
LoggerName string // logger name ("pretty"/"zap"/"none")
CacheDir string // cached dir
DisableColor bool // Disable Color
ArmoBEURLs string // armo url
ArmoBEURLsDep string // armo url
}
// func (rootInfo *RootInfo) InitLogger() {
// logger.DisableColor(rootInfo.DisableColor)
// if rootInfo.LoggerName == "" {
// if l := os.Getenv("KS_LOGGER_NAME"); l != "" {
// rootInfo.LoggerName = l
// } else {
// if isatty.IsTerminal(os.Stdout.Fd()) {
// rootInfo.LoggerName = "pretty"
// } else {
// rootInfo.LoggerName = "zap"
// }
// }
// }
// logger.InitLogger(rootInfo.LoggerName)
// }
// func (rootInfo *RootInfo) InitLoggerLevel() error {
// if rootInfo.Logger == helpers.InfoLevel.String() {
// } else if l := os.Getenv("KS_LOGGER"); l != "" {
// rootInfo.Logger = l
// }
// if err := logger.L().SetLevel(rootInfo.Logger); err != nil {
// return fmt.Errorf("supported levels: %s", strings.Join(helpers.SupportedLevels(), "/"))
// }
// return nil
// }
// func (rootInfo *RootInfo) InitCacheDir() error {
// if rootInfo.CacheDir == getter.DefaultLocalStore {
// getter.DefaultLocalStore = rootInfo.CacheDir
// } else if cacheDir := os.Getenv("KS_CACHE_DIR"); cacheDir != "" {
// getter.DefaultLocalStore = cacheDir
// } else {
// return nil // using default cache dir location
// }
// // TODO create dir if not found exist
// // logger.L().Debug("cache dir updated", helpers.String("path", getter.DefaultLocalStore))
// return nil
// }
// func (rootInfo *RootInfo) InitEnvironment() error {
// urlSlices := strings.Split(rootInfo.ArmoBEURLs, ",")
// if len(urlSlices) != 1 && len(urlSlices) < 3 {
// return fmt.Errorf("expected at least 2 URLs (report,api,frontend,auth)")
// }
// switch len(urlSlices) {
// case 1:
// switch urlSlices[0] {
// case "dev", "development":
// getter.SetARMOAPIConnector(getter.NewARMOAPIDev())
// case "stage", "staging":
// getter.SetARMOAPIConnector(getter.NewARMOAPIStaging())
// case "":
// getter.SetARMOAPIConnector(getter.NewARMOAPIProd())
// default:
// return fmt.Errorf("unknown environment")
// }
// case 2:
// armoERURL := urlSlices[0] // mandatory
// armoBEURL := urlSlices[1] // mandatory
// getter.SetARMOAPIConnector(getter.NewARMOAPICustomized(armoERURL, armoBEURL, "", ""))
// case 3, 4:
// var armoAUTHURL string
// armoERURL := urlSlices[0] // mandatory
// armoBEURL := urlSlices[1] // mandatory
// armoFEURL := urlSlices[2] // mandatory
// if len(urlSlices) <= 4 {
// armoAUTHURL = urlSlices[3]
// }
// getter.SetARMOAPIConnector(getter.NewARMOAPICustomized(armoERURL, armoBEURL, armoFEURL, armoAUTHURL))
// }
// return nil
// }

303
core/cautils/scaninfo.go Normal file
View File

@@ -0,0 +1,303 @@
package cautils
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
apisv1 "github.com/armosec/opa-utils/httpserver/apis/v1"
giturl "github.com/armosec/go-git-url"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/v2/core/cautils/getter"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
"github.com/armosec/opa-utils/reporthandling"
reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2"
"github.com/google/uuid"
)
const (
ScanCluster string = "cluster"
ScanLocalFiles string = "yaml"
localControlInputsFilename string = "controls-inputs.json"
localExceptionsFilename string = "exceptions.json"
)
type BoolPtrFlag struct {
valPtr *bool
}
func NewBoolPtr(b *bool) BoolPtrFlag {
return BoolPtrFlag{valPtr: b}
}
func (bpf *BoolPtrFlag) Type() string {
return "bool"
}
func (bpf *BoolPtrFlag) String() string {
if bpf.valPtr != nil {
return fmt.Sprintf("%v", *bpf.valPtr)
}
return ""
}
func (bpf *BoolPtrFlag) Get() *bool {
return bpf.valPtr
}
func (bpf *BoolPtrFlag) GetBool() bool {
if bpf.valPtr == nil {
return false
}
return *bpf.valPtr
}
func (bpf *BoolPtrFlag) SetBool(val bool) {
bpf.valPtr = &val
}
func (bpf *BoolPtrFlag) Set(val string) error {
switch val {
case "true":
bpf.SetBool(true)
case "false":
bpf.SetBool(false)
}
return nil
}
// TODO - UPDATE
type ScanInfo struct {
Getters // TODO - remove from object
PolicyIdentifier []reporthandling.PolicyIdentifier // TODO - remove from object
UseExceptions string // Load file with exceptions configuration
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
FormatVersion string // Output object can be differnet between versions, this is for testing and backward compatibility
ExcludedNamespaces string // used for host scanner namespace
IncludeNamespaces string // DEPRECATED?
InputPatterns []string // Yaml files input patterns
Silent bool // Silent mode - Do not print progress logs
FailThreshold float32 // Failure score threshold
Submit bool // Submit results to Armo BE
ScanID string // Report id of the current scan
HostSensorEnabled BoolPtrFlag // Deploy ARMO K8s host scanner to collect data from certain controls
HostSensorYamlPath string // Path to hostsensor file
Local bool // Do not submit results
Account string // account ID
KubeContext string // context name
FrameworkScan bool // false if scanning control
ScanAll bool // true if scan all frameworks
}
type Getters struct {
ExceptionsGetter getter.IExceptionsGetter
ControlsInputsGetter getter.IControlsInputsGetter
PolicyGetter getter.IPolicyGetter
}
func (scanInfo *ScanInfo) Init() {
scanInfo.setUseFrom()
scanInfo.setOutputFile()
scanInfo.setUseArtifactsFrom()
if scanInfo.ScanID == "" {
scanInfo.ScanID = uuid.NewString()
}
}
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 {
logger.L().Fatal("failed to read files from directory", helpers.String("dir", scanInfo.UseArtifactsFrom), helpers.Error(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) setUseFrom() {
if scanInfo.UseDefault {
for _, policy := range scanInfo.PolicyIdentifier {
scanInfo.UseFrom = append(scanInfo.UseFrom, getter.GetDefaultPath(policy.Name+".json"))
}
}
}
func (scanInfo *ScanInfo) setOutputFile() {
if scanInfo.Output == "" {
return
}
if scanInfo.Format == "json" {
if filepath.Ext(scanInfo.Output) != ".json" {
scanInfo.Output += ".json"
}
}
if scanInfo.Format == "junit" {
if filepath.Ext(scanInfo.Output) != ".xml" {
scanInfo.Output += ".xml"
}
}
if scanInfo.Format == "pdf" {
if filepath.Ext(scanInfo.Output) != ".pdf" {
scanInfo.Output += ".pdf"
}
}
}
func (scanInfo *ScanInfo) GetScanningEnvironment() string {
if len(scanInfo.InputPatterns) != 0 {
return ScanLocalFiles
}
return ScanCluster
}
func (scanInfo *ScanInfo) SetPolicyIdentifiers(policies []string, kind apisv1.NotificationPolicyKind) {
for _, policy := range policies {
if !scanInfo.contains(policy) {
newPolicy := reporthandling.PolicyIdentifier{}
newPolicy.Kind = reporthandling.NotificationPolicyKind(kind) // reporthandling.KindFramework
newPolicy.Name = policy
scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy)
}
}
}
func (scanInfo *ScanInfo) contains(policyName string) bool {
for _, policy := range scanInfo.PolicyIdentifier {
if policy.Name == policyName {
return true
}
}
return false
}
func scanInfoToScanMetadata(scanInfo *ScanInfo) *reporthandlingv2.Metadata {
metadata := &reporthandlingv2.Metadata{}
metadata.ScanMetadata.Format = scanInfo.Format
metadata.ScanMetadata.FormatVersion = scanInfo.FormatVersion
metadata.ScanMetadata.Submit = scanInfo.Submit
// TODO - Add excluded and included namespaces
// if len(scanInfo.ExcludedNamespaces) > 1 {
// opaSessionObj.Metadata.ScanMetadata.ExcludedNamespaces = strings.Split(scanInfo.ExcludedNamespaces[1:], ",")
// }
// if len(scanInfo.IncludeNamespaces) > 1 {
// opaSessionObj.Metadata.ScanMetadata.IncludeNamespaces = strings.Split(scanInfo.IncludeNamespaces[1:], ",")
// }
// scan type
if len(scanInfo.PolicyIdentifier) > 0 {
metadata.ScanMetadata.TargetType = string(scanInfo.PolicyIdentifier[0].Kind)
}
// append frameworks
for _, policy := range scanInfo.PolicyIdentifier {
metadata.ScanMetadata.TargetNames = append(metadata.ScanMetadata.TargetNames, policy.Name)
}
metadata.ScanMetadata.KubescapeVersion = BuildNumber
metadata.ScanMetadata.VerboseMode = scanInfo.VerboseMode
metadata.ScanMetadata.FailThreshold = scanInfo.FailThreshold
metadata.ScanMetadata.HostScanner = scanInfo.HostSensorEnabled.GetBool()
metadata.ScanMetadata.VerboseMode = scanInfo.VerboseMode
metadata.ScanMetadata.ControlsInputs = scanInfo.ControlsInputs
metadata.ScanMetadata.ScanningTarget = reporthandlingv2.Cluster
if scanInfo.GetScanningEnvironment() == ScanLocalFiles {
metadata.ScanMetadata.ScanningTarget = reporthandlingv2.File
}
inputFiles := ""
if len(scanInfo.InputPatterns) > 0 {
inputFiles = scanInfo.InputPatterns[0]
}
setContextMetadata(&metadata.ContextMetadata, inputFiles)
return metadata
}
func setContextMetadata(contextMetadata *reporthandlingv2.ContextMetadata, input string) {
// cluster
if input == "" {
contextMetadata.ClusterContextMetadata = &reporthandlingv2.ClusterMetadata{
ContextName: k8sinterface.GetContextName(),
}
return
}
// url
if gitParser, err := giturl.NewGitURL(input); err == nil {
if gitParser.GetBranch() == "" {
gitParser.SetDefaultBranch()
}
contextMetadata.RepoContextMetadata = &reporthandlingv2.RepoContextMetadata{
Repo: gitParser.GetRepo(),
Owner: gitParser.GetOwner(),
Branch: gitParser.GetBranch(),
}
return
}
if !filepath.IsAbs(input) {
if o, err := os.Getwd(); err == nil {
input = filepath.Join(o, input)
}
}
// single file
if IsFile(input) {
contextMetadata.FileContextMetadata = &reporthandlingv2.FileContextMetadata{
FilePath: input,
HostName: getHostname(),
}
return
}
// dir/glob
if !IsFile(input) {
contextMetadata.DirectoryContextMetadata = &reporthandlingv2.DirectoryContextMetadata{
BasePath: input,
HostName: getHostname(),
}
return
}
}
func getHostname() string {
if h, e := os.Hostname(); e == nil {
return h
}
return ""
}

View File

@@ -0,0 +1,67 @@
package cautils
import (
"testing"
reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2"
"github.com/stretchr/testify/assert"
)
func TestSetContextMetadata(t *testing.T) {
{
ctx := reporthandlingv2.ContextMetadata{}
setContextMetadata(&ctx, "")
assert.NotNil(t, ctx.ClusterContextMetadata)
assert.Nil(t, ctx.DirectoryContextMetadata)
assert.Nil(t, ctx.FileContextMetadata)
assert.Nil(t, ctx.HelmContextMetadata)
assert.Nil(t, ctx.RepoContextMetadata)
}
{
ctx := reporthandlingv2.ContextMetadata{}
setContextMetadata(&ctx, "file")
assert.Nil(t, ctx.ClusterContextMetadata)
assert.NotNil(t, ctx.DirectoryContextMetadata)
assert.Nil(t, ctx.FileContextMetadata)
assert.Nil(t, ctx.HelmContextMetadata)
assert.Nil(t, ctx.RepoContextMetadata)
hostName := getHostname()
assert.Contains(t, ctx.DirectoryContextMetadata.BasePath, "file")
assert.Equal(t, hostName, ctx.DirectoryContextMetadata.HostName)
}
{
ctx := reporthandlingv2.ContextMetadata{}
setContextMetadata(&ctx, "scaninfo_test.go")
assert.Nil(t, ctx.ClusterContextMetadata)
assert.Nil(t, ctx.DirectoryContextMetadata)
assert.NotNil(t, ctx.FileContextMetadata)
assert.Nil(t, ctx.HelmContextMetadata)
assert.Nil(t, ctx.RepoContextMetadata)
hostName := getHostname()
assert.Contains(t, ctx.FileContextMetadata.FilePath, "scaninfo_test.go")
assert.Equal(t, hostName, ctx.FileContextMetadata.HostName)
}
{
ctx := reporthandlingv2.ContextMetadata{}
setContextMetadata(&ctx, "https://github.com/armosec/kubescape")
assert.Nil(t, ctx.ClusterContextMetadata)
assert.Nil(t, ctx.DirectoryContextMetadata)
assert.Nil(t, ctx.FileContextMetadata)
assert.Nil(t, ctx.HelmContextMetadata)
assert.NotNil(t, ctx.RepoContextMetadata)
assert.Equal(t, "kubescape", ctx.RepoContextMetadata.Repo)
assert.Equal(t, "armosec", ctx.RepoContextMetadata.Owner)
assert.Equal(t, "master", ctx.RepoContextMetadata.Branch)
}
}
func TestGetHostname(t *testing.T) {
assert.NotEqual(t, "", getHostname())
}

View File

@@ -6,13 +6,15 @@ import (
"net/http"
"os"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/cautils/logger/helpers"
pkgutils "github.com/armosec/utils-go/utils"
"github.com/armosec/kubescape/v2/core/cautils/getter"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
"github.com/armosec/utils-go/boolutils"
"golang.org/x/mod/semver"
)
const SKIP_VERSION_CHECK = "KUBESCAPE_SKIP_UPDATE_CHECK"
const SKIP_VERSION_CHECK_DEPRECATED = "KUBESCAPE_SKIP_UPDATE_CHECK"
const SKIP_VERSION_CHECK = "KS_SKIP_UPDATE_CHECK"
var BuildNumber string
@@ -26,7 +28,9 @@ func NewIVersionCheckHandler() IVersionCheckHandler {
if BuildNumber == "" {
logger.L().Warning("unknown build number, this might affect your scan results. Please make sure you are updated to latest version")
}
if v, ok := os.LookupEnv(SKIP_VERSION_CHECK); ok && pkgutils.StringToBool(v) {
if v, ok := os.LookupEnv(SKIP_VERSION_CHECK); ok && boolutils.StringToBool(v) {
return NewVersionCheckHandlerMock()
} else if v, ok := os.LookupEnv(SKIP_VERSION_CHECK_DEPRECATED); ok && boolutils.StringToBool(v) {
return NewVersionCheckHandlerMock()
}
return NewVersionCheckHandler()
@@ -97,7 +101,7 @@ func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckReques
}
if latestVersion.ClientUpdate != "" {
if BuildNumber != "" && BuildNumber < latestVersion.ClientUpdate {
if BuildNumber != "" && semver.Compare(BuildNumber, latestVersion.ClientUpdate) == -1 {
logger.L().Warning(warningMessage(latestVersion.ClientUpdate))
}
}

View File

@@ -0,0 +1,68 @@
package cautils
import (
"testing"
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/opa-utils/reporthandling"
"github.com/stretchr/testify/assert"
"golang.org/x/mod/semver"
)
func TestGetKubernetesObjects(t *testing.T) {
}
var rule_v1_0_131 = &reporthandling.PolicyRule{PortalBase: armotypes.PortalBase{
Attributes: map[string]interface{}{"useUntilKubescapeVersion": "v1.0.132"}}}
var rule_v1_0_132 = &reporthandling.PolicyRule{PortalBase: armotypes.PortalBase{
Attributes: map[string]interface{}{"useFromKubescapeVersion": "v1.0.132", "useUntilKubescapeVersion": "v1.0.133"}}}
var rule_v1_0_133 = &reporthandling.PolicyRule{PortalBase: armotypes.PortalBase{
Attributes: map[string]interface{}{"useFromKubescapeVersion": "v1.0.133", "useUntilKubescapeVersion": "v1.0.134"}}}
var rule_v1_0_134 = &reporthandling.PolicyRule{PortalBase: armotypes.PortalBase{
Attributes: map[string]interface{}{"useFromKubescapeVersion": "v1.0.134"}}}
func TestIsRuleKubescapeVersionCompatible(t *testing.T) {
// local build- no build number
// should use only rules that don't have "until"
buildNumberMock := ""
assert.False(t, isRuleKubescapeVersionCompatible(rule_v1_0_131.Attributes, buildNumberMock))
assert.False(t, isRuleKubescapeVersionCompatible(rule_v1_0_132.Attributes, buildNumberMock))
assert.False(t, isRuleKubescapeVersionCompatible(rule_v1_0_133.Attributes, buildNumberMock))
assert.True(t, isRuleKubescapeVersionCompatible(rule_v1_0_134.Attributes, buildNumberMock))
// should only use rules that version is in range of use
buildNumberMock = "v1.0.130"
assert.True(t, isRuleKubescapeVersionCompatible(rule_v1_0_131.Attributes, buildNumberMock))
assert.False(t, isRuleKubescapeVersionCompatible(rule_v1_0_132.Attributes, buildNumberMock))
assert.False(t, isRuleKubescapeVersionCompatible(rule_v1_0_133.Attributes, buildNumberMock))
assert.False(t, isRuleKubescapeVersionCompatible(rule_v1_0_134.Attributes, buildNumberMock))
// should only use rules that version is in range of use
buildNumberMock = "v1.0.132"
assert.False(t, isRuleKubescapeVersionCompatible(rule_v1_0_131.Attributes, buildNumberMock))
assert.True(t, isRuleKubescapeVersionCompatible(rule_v1_0_132.Attributes, buildNumberMock))
assert.False(t, isRuleKubescapeVersionCompatible(rule_v1_0_133.Attributes, buildNumberMock))
assert.False(t, isRuleKubescapeVersionCompatible(rule_v1_0_134.Attributes, buildNumberMock))
// should only use rules that version is in range of use
buildNumberMock = "v1.0.133"
assert.False(t, isRuleKubescapeVersionCompatible(rule_v1_0_131.Attributes, buildNumberMock))
assert.False(t, isRuleKubescapeVersionCompatible(rule_v1_0_132.Attributes, buildNumberMock))
assert.True(t, isRuleKubescapeVersionCompatible(rule_v1_0_133.Attributes, buildNumberMock))
assert.False(t, isRuleKubescapeVersionCompatible(rule_v1_0_134.Attributes, buildNumberMock))
// should only use rules that version is in range of use
buildNumberMock = "v1.0.135"
assert.False(t, isRuleKubescapeVersionCompatible(rule_v1_0_131.Attributes, buildNumberMock))
assert.False(t, isRuleKubescapeVersionCompatible(rule_v1_0_132.Attributes, buildNumberMock))
assert.False(t, isRuleKubescapeVersionCompatible(rule_v1_0_133.Attributes, buildNumberMock))
assert.True(t, isRuleKubescapeVersionCompatible(rule_v1_0_134.Attributes, buildNumberMock))
}
func TestCheckLatestVersion(t *testing.T) {
assert.Equal(t, -1, semver.Compare("v2.0.150", "v2.0.151"))
assert.Equal(t, 0, semver.Compare("v2.0.150", "v2.0.150"))
assert.Equal(t, 1, semver.Compare("v2.0.150", "v2.0.149"))
assert.Equal(t, -1, semver.Compare("v2.0.150", "v3.0.150"))
}

View File

@@ -0,0 +1,52 @@
package cautils
import (
"strings"
"github.com/armosec/opa-utils/reporthandling/apis"
)
var (
ImageVulnResources = []string{"ImageVulnerabilities"}
HostSensorResources = []string{"KubeletConfiguration",
"KubeletCommandLine",
"OsReleaseFile",
"KernelVersion",
"LinuxSecurityHardeningStatus",
"OpenPortsList",
"LinuxKernelVariables"}
CloudResources = []string{"ClusterDescribe"}
)
func MapArmoResource(armoResourceMap *ArmoResources, resources []string) []string {
var hostResources []string
for k := range *armoResourceMap {
for _, resource := range resources {
if strings.Contains(k, resource) {
hostResources = append(hostResources, k)
}
}
}
return hostResources
}
func MapHostResources(armoResourceMap *ArmoResources) []string {
return MapArmoResource(armoResourceMap, HostSensorResources)
}
func MapImageVulnResources(armoResourceMap *ArmoResources) []string {
return MapArmoResource(armoResourceMap, ImageVulnResources)
}
func MapCloudResources(armoResourceMap *ArmoResources) []string {
return MapArmoResource(armoResourceMap, CloudResources)
}
func SetInfoMapForResources(info string, resources []string, errorMap map[string]apis.StatusInfo) {
for _, resource := range resources {
errorMap[resource] = apis.StatusInfo{
InnerInfo: info,
InnerStatus: apis.StatusSkipped,
}
}
}

37
core/core/cachedconfig.go Normal file
View File

@@ -0,0 +1,37 @@
package core
import (
"fmt"
metav1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1"
)
func (ks *Kubescape) SetCachedConfig(setConfig *metav1.SetConfig) error {
tenant := getTenantConfig("", "", getKubernetesApi())
if setConfig.Account != "" {
tenant.GetConfigObj().AccountID = setConfig.Account
}
if setConfig.SecretKey != "" {
tenant.GetConfigObj().SecretKey = setConfig.SecretKey
}
if setConfig.ClientID != "" {
tenant.GetConfigObj().ClientID = setConfig.ClientID
}
return tenant.UpdateCachedConfig()
}
// View cached configurations
func (ks *Kubescape) ViewCachedConfig(viewConfig *metav1.ViewConfig) error {
tenant := getTenantConfig("", "", getKubernetesApi()) // change k8sinterface
fmt.Fprintf(viewConfig.Writer, "%s\n", tenant.GetConfigObj().Config())
return nil
}
func (ks *Kubescape) DeleteCachedConfig(deleteConfig *metav1.DeleteConfig) error {
tenant := getTenantConfig("", "", getKubernetesApi()) // change k8sinterface
return tenant.DeleteCachedConfig()
}

View File

@@ -1,17 +1,18 @@
package clihandler
package core
import (
"fmt"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/cautils/logger/helpers"
"github.com/armosec/kubescape/v2/core/cautils/getter"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
v1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1"
)
func DeleteExceptions(accountID string, exceptions []string) error {
func (ks *Kubescape) DeleteExceptions(delExceptions *v1.DeleteExceptions) error {
// load cached config
getTenantConfig(accountID, "", getKubernetesApi())
getTenantConfig(delExceptions.Account, "", getKubernetesApi())
// login kubescape SaaS
armoAPI := getter.GetArmoAPIConnector()
@@ -19,8 +20,8 @@ func DeleteExceptions(accountID string, exceptions []string) error {
return err
}
for i := range exceptions {
exceptionName := exceptions[i]
for i := range delExceptions.Exceptions {
exceptionName := delExceptions.Exceptions[i]
if exceptionName == "" {
continue
}

View File

@@ -1,18 +1,19 @@
package clihandler
package core
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/cautils/logger/helpers"
"github.com/armosec/kubescape/v2/core/cautils/getter"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
metav1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1"
)
var downloadFunc = map[string]func(*cautils.DownloadInfo) error{
var downloadFunc = map[string]func(*metav1.DownloadInfo) error{
"controls-inputs": downloadConfigInputs,
"exceptions": downloadExceptions,
"control": downloadControl,
@@ -28,15 +29,18 @@ func DownloadSupportCommands() []string {
return commands
}
func CliDownload(downloadInfo *cautils.DownloadInfo) error {
func (ks *Kubescape) Download(downloadInfo *metav1.DownloadInfo) error {
setPathandFilename(downloadInfo)
if err := os.MkdirAll(downloadInfo.Path, os.ModePerm); err != nil {
return err
}
if err := downloadArtifact(downloadInfo, downloadFunc); err != nil {
return err
}
return nil
}
func downloadArtifact(downloadInfo *cautils.DownloadInfo, downloadArtifactFunc map[string]func(*cautils.DownloadInfo) error) error {
func downloadArtifact(downloadInfo *metav1.DownloadInfo, downloadArtifactFunc map[string]func(*metav1.DownloadInfo) error) error {
if f, ok := downloadArtifactFunc[downloadInfo.Target]; ok {
if err := f(downloadInfo); err != nil {
return err
@@ -46,7 +50,7 @@ func downloadArtifact(downloadInfo *cautils.DownloadInfo, downloadArtifactFunc m
return fmt.Errorf("unknown command to download")
}
func setPathandFilename(downloadInfo *cautils.DownloadInfo) {
func setPathandFilename(downloadInfo *metav1.DownloadInfo) {
if downloadInfo.Path == "" {
downloadInfo.Path = getter.GetDefaultPath("")
} else {
@@ -60,32 +64,35 @@ func setPathandFilename(downloadInfo *cautils.DownloadInfo) {
}
}
func downloadArtifacts(downloadInfo *cautils.DownloadInfo) error {
func downloadArtifacts(downloadInfo *metav1.DownloadInfo) error {
downloadInfo.FileName = ""
var artifacts = map[string]func(*cautils.DownloadInfo) error{
var artifacts = map[string]func(*metav1.DownloadInfo) error{
"controls-inputs": downloadConfigInputs,
"exceptions": downloadExceptions,
"framework": downloadFramework,
}
for artifact := range artifacts {
if err := downloadArtifact(&cautils.DownloadInfo{Target: artifact, Path: downloadInfo.Path, FileName: fmt.Sprintf("%s.json", artifact)}, artifacts); err != nil {
if err := downloadArtifact(&metav1.DownloadInfo{Target: artifact, Path: downloadInfo.Path, FileName: fmt.Sprintf("%s.json", artifact)}, artifacts); err != nil {
logger.L().Error("error downloading", helpers.String("artifact", artifact), helpers.Error(err))
}
}
return nil
}
func downloadConfigInputs(downloadInfo *cautils.DownloadInfo) error {
func downloadConfigInputs(downloadInfo *metav1.DownloadInfo) error {
tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi())
controlsInputsGetter := getConfigInputsGetter(downloadInfo.Name, tenant.GetAccountID(), nil)
controlInputs, err := controlsInputsGetter.GetControlsInputs(tenant.GetClusterName())
controlInputs, err := controlsInputsGetter.GetControlsInputs(tenant.GetContextName())
if err != nil {
return err
}
if downloadInfo.FileName == "" {
downloadInfo.FileName = fmt.Sprintf("%s.json", downloadInfo.Target)
}
if controlInputs == nil {
return fmt.Errorf("failed to download controlInputs - received an empty objects")
}
// save in file
err = getter.SaveInFile(controlInputs, filepath.Join(downloadInfo.Path, downloadInfo.FileName))
if err != nil {
@@ -95,14 +102,14 @@ func downloadConfigInputs(downloadInfo *cautils.DownloadInfo) error {
return nil
}
func downloadExceptions(downloadInfo *cautils.DownloadInfo) error {
func downloadExceptions(downloadInfo *metav1.DownloadInfo) error {
var err error
tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi())
exceptionsGetter := getExceptionsGetter("")
exceptions := []armotypes.PostureExceptionPolicy{}
if tenant.GetAccountID() != "" {
exceptions, err = exceptionsGetter.GetExceptions(tenant.GetClusterName())
exceptions, err = exceptionsGetter.GetExceptions(tenant.GetContextName())
if err != nil {
return err
}
@@ -119,11 +126,11 @@ func downloadExceptions(downloadInfo *cautils.DownloadInfo) error {
return nil
}
func downloadFramework(downloadInfo *cautils.DownloadInfo) error {
func downloadFramework(downloadInfo *metav1.DownloadInfo) error {
tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi())
g := getPolicyGetter(nil, tenant.GetAccountID(), true, nil)
g := getPolicyGetter(nil, tenant.GetTennatEmail(), true, nil)
if downloadInfo.Name == "" {
// if framework name not specified - download all frameworks
@@ -148,6 +155,9 @@ func downloadFramework(downloadInfo *cautils.DownloadInfo) error {
if err != nil {
return err
}
if framework == nil {
return fmt.Errorf("failed to download framework - received an empty objects")
}
downloadTo := filepath.Join(downloadInfo.Path, downloadInfo.FileName)
err = getter.SaveInFile(framework, downloadTo)
if err != nil {
@@ -158,11 +168,11 @@ func downloadFramework(downloadInfo *cautils.DownloadInfo) error {
return nil
}
func downloadControl(downloadInfo *cautils.DownloadInfo) error {
func downloadControl(downloadInfo *metav1.DownloadInfo) error {
tenant := getTenantConfig(downloadInfo.Account, "", getKubernetesApi())
g := getPolicyGetter(nil, tenant.GetAccountID(), false, nil)
g := getPolicyGetter(nil, tenant.GetTennatEmail(), false, nil)
if downloadInfo.Name == "" {
// TODO - support
@@ -175,6 +185,9 @@ func downloadControl(downloadInfo *cautils.DownloadInfo) error {
if err != nil {
return err
}
if controls == nil {
return fmt.Errorf("failed to download control - received an empty objects")
}
downloadTo := filepath.Join(downloadInfo.Path, downloadInfo.FileName)
err = getter.SaveInFile(controls, downloadTo)
if err != nil {

View File

@@ -1,18 +1,17 @@
package clihandler
package core
import (
"fmt"
"os"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/cautils/logger/helpers"
"github.com/armosec/kubescape/hostsensorutils"
"github.com/armosec/kubescape/resourcehandler"
"github.com/armosec/kubescape/resultshandling/reporter"
reporterv2 "github.com/armosec/kubescape/resultshandling/reporter/v2"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/armosec/kubescape/v2/core/cautils/getter"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
"github.com/armosec/kubescape/v2/core/pkg/hostsensorutils"
"github.com/armosec/kubescape/v2/core/pkg/resourcehandler"
"github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter"
reporterv2 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v2"
"github.com/armosec/opa-utils/reporthandling"
"github.com/armosec/rbac-utils/rbacscanner"
@@ -43,16 +42,16 @@ func getExceptionsGetter(useExceptions string) getter.IExceptionsGetter {
func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, submit bool) *cautils.RBACObjects {
if submit {
return cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, tenantConfig.GetAccountID(), tenantConfig.GetClusterName()))
return cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, tenantConfig.GetAccountID(), tenantConfig.GetContextName()))
}
return nil
}
func getReporter(tenantConfig cautils.ITenantConfig, submit, fwScan, clusterScan bool) reporter.IReport {
if submit && clusterScan {
return reporterv2.NewReportEventReceiver(tenantConfig.GetConfigObj())
func getReporter(tenantConfig cautils.ITenantConfig, reportID string, submit, fwScan bool) reporter.IReport {
if submit {
return reporterv2.NewReportEventReceiver(tenantConfig.GetConfigObj(), reportID)
}
if tenantConfig.GetAccountID() == "" && fwScan && clusterScan {
if tenantConfig.GetAccountID() == "" {
// Add link only when scanning a cluster using a framework
return reporterv2.NewReportMock(reporterv2.NO_SUBMIT_QUERY, "run kubescape with the '--submit' flag")
}
@@ -60,9 +59,7 @@ func getReporter(tenantConfig cautils.ITenantConfig, submit, fwScan, clusterScan
if !fwScan {
message = "Kubescape does not submit scan results when scanning controls"
}
if !clusterScan {
message = "Kubescape will submit scan results only when scanning a cluster (not YAML files)"
}
return reporterv2.NewReportMock("", message)
}
@@ -82,15 +79,15 @@ func getHostSensorHandler(scanInfo *cautils.ScanInfo, k8s *k8sinterface.Kubernet
}
hasHostSensorControls := true
// we need to determined which controls needs host sensor
// we need to determined which controls needs host scanner
if scanInfo.HostSensorEnabled.Get() == nil && hasHostSensorControls {
scanInfo.HostSensorEnabled.SetBool(askUserForHostSensor())
scanInfo.HostSensorEnabled.SetBool(false) // default - do not run host scanner
logger.L().Warning("Kubernetes cluster nodes scanning is disabled. This is required to collect valuable data for certain controls. You can enable it using the --enable-host-scan flag")
}
if hostSensorVal := scanInfo.HostSensorEnabled.Get(); hostSensorVal != nil && *hostSensorVal {
hostSensorHandler, err := hostsensorutils.NewHostSensorHandler(k8s, scanInfo.HostSensorYamlPath)
if err != nil {
logger.L().Warning(fmt.Sprintf("failed to create host sensor: %s", err.Error()))
logger.L().Warning(fmt.Sprintf("failed to create host scanner: %s", err.Error()))
return &hostsensorutils.HostSensorHandlerMock{}
}
return hostSensorHandler
@@ -122,7 +119,7 @@ func policyIdentifierNames(pi []reporthandling.PolicyIdentifier) string {
return policiesNames
}
// setSubmitBehavior - Setup the desired cluster behavior regarding submittion to the Armo BE
// setSubmitBehavior - Setup the desired cluster behavior regarding submitting to the Armo BE
func setSubmitBehavior(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig) {
/*
@@ -153,11 +150,11 @@ func setSubmitBehavior(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantC
}
// setPolicyGetter set the policy getter - local file/github release/ArmoAPI
func getPolicyGetter(loadPoliciesFromFile []string, accountID string, frameworkScope bool, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IPolicyGetter {
func getPolicyGetter(loadPoliciesFromFile []string, tennatEmail string, frameworkScope bool, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IPolicyGetter {
if len(loadPoliciesFromFile) > 0 {
return getter.NewLoadPolicy(loadPoliciesFromFile)
}
if accountID != "" && frameworkScope {
if tennatEmail != "" && frameworkScope {
g := getter.GetArmoAPIConnector() // download policy from ARMO backend
return g
}
@@ -195,7 +192,7 @@ func getConfigInputsGetter(ControlsInputs string, accountID string, downloadRele
downloadReleasedPolicy = getter.NewDownloadReleasedPolicy()
}
if err := downloadReleasedPolicy.SetRegoObjects(); err != nil { // if failed to pull config inputs, fallback to BE
cautils.WarningDisplay(os.Stderr, "Warning: failed to get config inputs from github release, this may affect the scanning results\n")
logger.L().Warning("failed to get config inputs from github release, this may affect the scanning results", helpers.Error(err))
}
return downloadReleasedPolicy
}

7
core/core/kscore.go Normal file
View File

@@ -0,0 +1,7 @@
package core
type Kubescape struct{}
func NewKubescape() *Kubescape {
return &Kubescape{}
}

View File

@@ -1,4 +1,4 @@
package clihandler
package core
import (
"encoding/json"
@@ -6,29 +6,29 @@ import (
"sort"
"strings"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/clihandler/cliobjects"
"github.com/armosec/kubescape/v2/core/cautils/getter"
metav1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1"
)
var listFunc = map[string]func(*cliobjects.ListPolicies) ([]string, error){
var listFunc = map[string]func(*metav1.ListPolicies) ([]string, error){
"controls": listControls,
"frameworks": listFrameworks,
"exceptions": listExceptions,
}
var listFormatFunc = map[string]func(*cliobjects.ListPolicies, []string){
var listFormatFunc = map[string]func(*metav1.ListPolicies, []string){
"pretty-print": prettyPrintListFormat,
"json": jsonListFormat,
}
func ListSupportCommands() []string {
func ListSupportActions() []string {
commands := []string{}
for k := range listFunc {
commands = append(commands, k)
}
return commands
}
func CliList(listPolicies *cliobjects.ListPolicies) error {
func (ks *Kubescape) List(listPolicies *metav1.ListPolicies) error {
if f, ok := listFunc[listPolicies.Target]; ok {
policies, err := f(listPolicies)
if err != nil {
@@ -43,17 +43,17 @@ func CliList(listPolicies *cliobjects.ListPolicies) error {
return fmt.Errorf("unknown command to download")
}
func listFrameworks(listPolicies *cliobjects.ListPolicies) ([]string, error) {
func listFrameworks(listPolicies *metav1.ListPolicies) ([]string, error) {
tenant := getTenantConfig(listPolicies.Account, "", getKubernetesApi()) // change k8sinterface
g := getPolicyGetter(nil, tenant.GetAccountID(), true, nil)
g := getPolicyGetter(nil, tenant.GetTennatEmail(), true, nil)
return listFrameworksNames(g), nil
}
func listControls(listPolicies *cliobjects.ListPolicies) ([]string, error) {
func listControls(listPolicies *metav1.ListPolicies) ([]string, error) {
tenant := getTenantConfig(listPolicies.Account, "", getKubernetesApi()) // change k8sinterface
g := getPolicyGetter(nil, tenant.GetAccountID(), false, nil)
g := getPolicyGetter(nil, tenant.GetTennatEmail(), false, nil)
l := getter.ListName
if listPolicies.ListIDs {
l = getter.ListID
@@ -61,8 +61,8 @@ func listControls(listPolicies *cliobjects.ListPolicies) ([]string, error) {
return g.ListControls(l)
}
func listExceptions(listPolicies *cliobjects.ListPolicies) ([]string, error) {
// load tenant config
func listExceptions(listPolicies *metav1.ListPolicies) ([]string, error) {
// load tenant metav1
getTenantConfig(listPolicies.Account, "", getKubernetesApi())
var exceptionsNames []string
@@ -77,12 +77,12 @@ func listExceptions(listPolicies *cliobjects.ListPolicies) ([]string, error) {
return exceptionsNames, nil
}
func prettyPrintListFormat(listPolicies *cliobjects.ListPolicies, policies []string) {
func prettyPrintListFormat(listPolicies *metav1.ListPolicies, policies []string) {
sep := "\n * "
fmt.Printf("Supported %s:%s%s\n", listPolicies.Target, sep, strings.Join(policies, sep))
}
func jsonListFormat(listPolicies *cliobjects.ListPolicies, policies []string) {
func jsonListFormat(listPolicies *metav1.ListPolicies, policies []string) {
j, _ := json.MarshalIndent(policies, "", " ")
fmt.Printf("%s\n", j)
}

View File

@@ -1,26 +1,27 @@
package clihandler
package core
import (
"fmt"
"io/fs"
"os"
apisv1 "github.com/armosec/opa-utils/httpserver/apis/v1"
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/resultshandling/printer"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/cautils/logger/helpers"
"github.com/armosec/kubescape/hostsensorutils"
"github.com/armosec/kubescape/opaprocessor"
"github.com/armosec/kubescape/policyhandler"
"github.com/armosec/kubescape/resourcehandler"
"github.com/armosec/kubescape/resultshandling"
"github.com/armosec/kubescape/resultshandling/reporter"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/armosec/kubescape/v2/core/cautils/getter"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
"github.com/armosec/kubescape/v2/core/pkg/hostsensorutils"
"github.com/armosec/kubescape/v2/core/pkg/opaprocessor"
"github.com/armosec/kubescape/v2/core/pkg/policyhandler"
"github.com/armosec/kubescape/v2/core/pkg/resourcehandler"
"github.com/armosec/kubescape/v2/core/pkg/resultshandling"
"github.com/armosec/kubescape/v2/core/pkg/resultshandling/printer"
"github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter"
"github.com/armosec/opa-utils/reporthandling"
"github.com/mattn/go-isatty"
"github.com/armosec/opa-utils/resources"
)
type componentInterfaces struct {
@@ -49,6 +50,11 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
// Set submit behavior AFTER loading tenant config
setSubmitBehavior(scanInfo, tenantConfig)
// Do not submit yaml scanning
if len(scanInfo.InputPatterns) > 0 {
scanInfo.Submit = false
}
if scanInfo.Submit {
// submit - Create tenant & Submit report
if err := tenantConfig.SetTenant(); err != nil {
@@ -61,11 +67,11 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
v := cautils.NewIVersionCheckHandler()
v.CheckLatestVersion(cautils.NewVersionCheckRequest(cautils.BuildNumber, policyIdentifierNames(scanInfo.PolicyIdentifier), "", scanInfo.GetScanningEnvironment()))
// ================== setup host sensor object ======================================
// ================== setup host scanner object ======================================
hostSensorHandler := getHostSensorHandler(scanInfo, k8s)
if err := hostSensorHandler.Init(); err != nil {
logger.L().Error("failed to init host sensor", helpers.Error(err))
logger.L().Error("failed to init host scanner", helpers.Error(err))
hostSensorHandler = &hostsensorutils.HostSensorHandlerMock{}
}
// excluding hostsensor namespace
@@ -87,7 +93,7 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
// ================== setup reporter & printer objects ======================================
// reporting behavior - setup reporter
reportHandler := getReporter(tenantConfig, scanInfo.Submit, scanInfo.FrameworkScan, len(scanInfo.InputPatterns) == 0)
reportHandler := getReporter(tenantConfig, scanInfo.ScanID, scanInfo.Submit, scanInfo.FrameworkScan)
// setup printer
printerHandler := resultshandling.NewPrinter(scanInfo.Format, scanInfo.FormatVersion, scanInfo.VerboseMode)
@@ -104,69 +110,67 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
}
}
func ScanCliSetup(scanInfo *cautils.ScanInfo) error {
func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsHandler, error) {
logger.L().Info("ARMO security scanner starting")
// ===================== Initialization =====================
scanInfo.Init() // initialize scan info
interfaces := getInterfaces(scanInfo)
// setPolicyGetter(scanInfo, interfaces.clusterConfig.GetCustomerGUID())
processNotification := make(chan *cautils.OPASessionObj)
reportResults := make(chan *cautils.OPASessionObj)
cautils.ClusterName = interfaces.tenantConfig.GetClusterName() // TODO - Deprecated
cautils.ClusterName = interfaces.tenantConfig.GetContextName() // TODO - Deprecated
cautils.CustomerGUID = interfaces.tenantConfig.GetAccountID() // TODO - Deprecated
interfaces.report.SetClusterName(interfaces.tenantConfig.GetClusterName())
interfaces.report.SetClusterName(interfaces.tenantConfig.GetContextName())
interfaces.report.SetCustomerGUID(interfaces.tenantConfig.GetAccountID())
downloadReleasedPolicy := getter.NewDownloadReleasedPolicy() // download config inputs from github release
// set policy getter only after setting the customerGUID
scanInfo.Getters.PolicyGetter = getPolicyGetter(scanInfo.UseFrom, interfaces.tenantConfig.GetAccountID(), scanInfo.FrameworkScan, downloadReleasedPolicy)
scanInfo.Getters.PolicyGetter = getPolicyGetter(scanInfo.UseFrom, interfaces.tenantConfig.GetTennatEmail(), scanInfo.FrameworkScan, downloadReleasedPolicy)
scanInfo.Getters.ControlsInputsGetter = getConfigInputsGetter(scanInfo.ControlsInputs, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
scanInfo.Getters.ExceptionsGetter = getExceptionsGetter(scanInfo.UseExceptions)
// TODO - list supported frameworks/controls
if scanInfo.ScanAll {
scanInfo.SetPolicyIdentifiers(listFrameworksNames(scanInfo.Getters.PolicyGetter), reporthandling.KindFramework)
scanInfo.SetPolicyIdentifiers(listFrameworksNames(scanInfo.Getters.PolicyGetter), apisv1.KindFramework)
}
//
// remove host scanner components
defer func() {
if err := interfaces.hostSensorHandler.TearDown(); err != nil {
logger.L().Error("failed to tear down host sensor", helpers.Error(err))
logger.L().Error("failed to tear down host scanner", helpers.Error(err))
}
}()
// cli handler setup
go func() {
// policy handler setup
policyHandler := policyhandler.NewPolicyHandler(&processNotification, interfaces.resourceHandler)
resultsHandling := resultshandling.NewResultsHandler(interfaces.report, interfaces.printerHandler)
if err := Scan(policyHandler, scanInfo); err != nil {
logger.L().Fatal(err.Error())
}
}()
// processor setup - rego run
go func() {
opaprocessorObj := opaprocessor.NewOPAProcessorHandler(&processNotification, &reportResults)
opaprocessorObj.ProcessRulesListenner()
}()
resultsHandling := resultshandling.NewResultsHandler(&reportResults, interfaces.report, interfaces.printerHandler)
score := resultsHandling.HandleResults(scanInfo)
// print report url
interfaces.report.DisplayReportURL()
if score > float32(scanInfo.FailThreshold) {
return fmt.Errorf("scan risk-score %.2f is above permitted threshold %.2f", score, scanInfo.FailThreshold)
// ===================== policies & resources =====================
policyHandler := policyhandler.NewPolicyHandler(interfaces.resourceHandler)
scanData, err := collectResources(policyHandler, scanInfo)
if err != nil {
return resultsHandling, err
}
return nil
// ========================= opa testing =====================
deps := resources.NewRegoDependenciesData(k8sinterface.GetK8sConfig(), interfaces.tenantConfig.GetContextName())
reportResults := opaprocessor.NewOPAProcessor(scanData, deps)
if err := reportResults.ProcessRulesListenner(); err != nil {
// TODO - do something
return resultsHandling, err
}
// ========================= results handling =====================
resultsHandling.SetData(scanData)
// if resultsHandling.GetRiskScore() > float32(scanInfo.FailThreshold) {
// return resultsHandling, fmt.Errorf("scan risk-score %.2f is above permitted threshold %.2f", resultsHandling.GetRiskScore(), scanInfo.FailThreshold)
// }
return resultsHandling, nil
}
func Scan(policyHandler *policyhandler.PolicyHandler, scanInfo *cautils.ScanInfo) error {
// TODO - remove function
func collectResources(policyHandler *policyhandler.PolicyHandler, scanInfo *cautils.ScanInfo) (*cautils.OPASessionObj, error) {
policyNotification := &reporthandling.PolicyNotification{
Rules: scanInfo.PolicyIdentifier,
KubescapeNotification: reporthandling.KubescapeNotification{
@@ -176,34 +180,35 @@ func Scan(policyHandler *policyhandler.PolicyHandler, scanInfo *cautils.ScanInfo
}
switch policyNotification.KubescapeNotification.NotificationType {
case reporthandling.TypeExecPostureScan:
if err := policyHandler.HandleNotificationRequest(policyNotification, scanInfo); err != nil {
return err
collectedResources, err := policyHandler.CollectResources(policyNotification, scanInfo)
if err != nil {
return nil, err
}
return collectedResources, nil
default:
return fmt.Errorf("notification type '%s' Unknown", policyNotification.KubescapeNotification.NotificationType)
return nil, fmt.Errorf("notification type '%s' Unknown", policyNotification.KubescapeNotification.NotificationType)
}
return nil
}
func askUserForHostSensor() bool {
return false
// func askUserForHostSensor() bool {
// return false
if !isatty.IsTerminal(os.Stdin.Fd()) {
return false
}
if ssss, err := os.Stdin.Stat(); err == nil {
// fmt.Printf("Found stdin type: %s\n", ssss.Mode().Type())
if ssss.Mode().Type()&(fs.ModeDevice|fs.ModeCharDevice) > 0 { //has TTY
fmt.Fprintf(os.Stderr, "Would you like to scan K8s nodes? [y/N]. This is required to collect valuable data for certain controls\n")
fmt.Fprintf(os.Stderr, "Use --enable-host-scan flag to suppress this message\n")
var b []byte = make([]byte, 1)
if n, err := os.Stdin.Read(b); err == nil {
if n > 0 && len(b) > 0 && (b[0] == 'y' || b[0] == 'Y') {
return true
}
}
}
}
return false
}
// if !isatty.IsTerminal(os.Stdin.Fd()) {
// return false
// }
// if ssss, err := os.Stdin.Stat(); err == nil {
// // fmt.Printf("Found stdin type: %s\n", ssss.Mode().Type())
// if ssss.Mode().Type()&(fs.ModeDevice|fs.ModeCharDevice) > 0 { //has TTY
// fmt.Fprintf(os.Stderr, "Would you like to scan K8s nodes? [y/N]. This is required to collect valuable data for certain controls\n")
// fmt.Fprintf(os.Stderr, "Use --enable-host-scan flag to suppress this message\n")
// var b []byte = make([]byte, 1)
// if n, err := os.Stdin.Read(b); err == nil {
// if n > 0 && len(b) > 0 && (b[0] == 'y' || b[0] == 'Y') {
// return true
// }
// }
// }
// }
// return false
// }

View File

@@ -1,14 +1,14 @@
package clihandler
package core
import (
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/cautils/logger"
"github.com/armosec/kubescape/cautils/logger/helpers"
"github.com/armosec/kubescape/clihandler/cliinterfaces"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/armosec/kubescape/v2/core/cautils/getter"
"github.com/armosec/kubescape/v2/core/cautils/logger"
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
"github.com/armosec/kubescape/v2/core/meta/cliinterfaces"
)
func Submit(submitInterfaces cliinterfaces.SubmitInterfaces) error {
func (ks *Kubescape) Submit(submitInterfaces cliinterfaces.SubmitInterfaces) error {
// list resources
postureReport, err := submitInterfaces.SubmitObjects.SetResourcesReport()
@@ -20,7 +20,7 @@ func Submit(submitInterfaces cliinterfaces.SubmitInterfaces) error {
return err
}
// report
if err := submitInterfaces.Reporter.ActionSendReport(&cautils.OPASessionObj{PostureReport: postureReport, AllResources: allresources}); err != nil {
if err := submitInterfaces.Reporter.Submit(&cautils.OPASessionObj{PostureReport: postureReport, AllResources: allresources}); err != nil {
return err
}
logger.L().Success("Data has been submitted successfully")
@@ -29,7 +29,7 @@ func Submit(submitInterfaces cliinterfaces.SubmitInterfaces) error {
return nil
}
func SubmitExceptions(accountID, excPath string) error {
func (ks *Kubescape) SubmitExceptions(accountID, excPath string) error {
logger.L().Info("submitting exceptions", helpers.String("path", excPath))
// load cached config

View File

@@ -2,8 +2,8 @@ package cliinterfaces
import (
"github.com/armosec/k8s-interface/workloadinterface"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/resultshandling/reporter"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter"
"github.com/armosec/opa-utils/reporthandling"
)

View File

@@ -0,0 +1,16 @@
package v1
import "io"
type SetConfig struct {
Account string
ClientID string
SecretKey string
}
type ViewConfig struct {
Writer io.Writer
}
type DeleteConfig struct {
}

View File

@@ -0,0 +1,6 @@
package v1
type DeleteExceptions struct {
Account string
Exceptions []string
}

View File

@@ -1,9 +1,9 @@
package cautils
package v1
type DownloadInfo struct {
Path string // directory to save artifact. Default is "~/.kubescape/"
FileName string // can be empty
Target string // type of artifact to download
Name string // name of artifact to download
Account string // customerGUID
Account string // AccountID
}

Some files were not shown because too many files have changed in this diff Show More