Compare commits

...

388 Commits

Author SHA1 Message Date
David Wertenteil
f1514d6e76 Merge pull request #1002 from kubescape/hot-fix-windows-url
update opa-utils pkg with URL parsing fixed
2023-01-03 22:39:07 +02:00
David Wertenteil
3a038c9a0e update opa-utils pkg with URL parsing fixed 2023-01-03 22:24:37 +02:00
David Wertenteil
b4bdf4d860 Release (#1000)
* fixed flaky loop(cautils): loadpolicy getter

We should not inject pointers to the variable iterated over by the
"range" operator.

Signed-off-by: Frédéric BIDON <fredbi@yahoo.com>

* fixed more flaky pointers in loops (registryadaptors, opaprocessor)

Signed-off-by: Frédéric BIDON <fredbi@yahoo.com>

* fixed more flaky pointers in loops (resultshandling)

Signed-off-by: Frédéric BIDON <fredbi@yahoo.com>

* enabled golangci linter in CI

Signed-off-by: Frédéric BIDON <fredbi@yahoo.com>

* fixed linting issues with minimal linters config

Signed-off-by: Frédéric BIDON <fredbi@yahoo.com>

* bump go version to 1.19

* English and typos

* Support AKS parser (#994)

* support GKE parser

* update go mod

* support GKE parser

* update go mod

* update k8s-interface pkg

* Added KS desgin.drawio

* revert k8s.io to v0.25.3

* ran go mod tidy

* update sign-up url

* [wip] Adding CreateAccount support

* revert to docs URL

* update opa-utils pkg

* Print attack tree (optional, with argument) (#997)

* Print attack tree with the argument


Signed-off-by: Frédéric BIDON <fredbi@yahoo.com>
Co-authored-by: Frédéric BIDON <frederic@oneconcern.com>
Co-authored-by: Frédéric BIDON <fredbi@yahoo.com>
Co-authored-by: Oshrat Nir <45561829+Oshratn@users.noreply.github.com>
Co-authored-by: Amir Malka <amirm@armosec.io>
Co-authored-by: David Wertenteil <dwertent@armosec.io>
2023-01-03 11:30:09 +02:00
David Wertenteil
08e7108dc0 Merge pull request #991 from kubescape/dev
Release
2022-12-22 18:12:37 +02:00
David Wertenteil
108a2d6dd8 Merge pull request #962 from anubhav06/gitlab-scan
added GitLab repo scanning support
2022-12-22 17:29:57 +02:00
David Wertenteil
2c28286bb1 update httphandler go mod 2022-12-22 17:07:47 +02:00
David Wertenteil
79858b7ed7 Merge pull request #975 from kooomix/dev
control scan and download only by id
2022-12-22 16:56:40 +02:00
David Wertenteil
bb2e83eb3b update go-git pkg 2022-12-22 16:55:11 +02:00
David Wertenteil
282a29b971 Merge pull request #990 from dwertent/cloud-name-breakdown
update config scanning path
2022-12-22 16:37:27 +02:00
David Wertenteil
60b9edc463 update config scanning path 2022-12-22 16:14:44 +02:00
David Wertenteil
0f9a5e3127 Merge pull request #989 from dwertent/cloud-name-breakdown
Breakdown cloud-cluster name
2022-12-22 16:03:31 +02:00
David Wertenteil
7c79c14363 Update core/pkg/resultshandling/results.go 2022-12-22 15:00:59 +02:00
Vlad Klokun
fe84225252 feat: notify about writing to an output file in PrettyPrinter 2022-12-22 15:00:59 +02:00
Vlad Klokun
56da8d8d92 style: tidy up the PDFPrinter
- Shorten receiver names
- Modify comments to follow Go Doc convention
2022-12-22 15:00:59 +02:00
Vlad Klokun
f135e95d2c style: shorten receiver names in JUnitPrinter 2022-12-22 15:00:59 +02:00
Vlad Klokun
db34183fc1 style: shorten receiver names in JSONPrinter 2022-12-22 15:00:59 +02:00
Vlad Klokun
8f3af71c84 style: shorten receiver names in HTML Printer 2022-12-22 15:00:59 +02:00
Vlad Klokun
116aee0c9c style: shorten receiver names in PrettyPrinter 2022-12-22 15:00:59 +02:00
Vlad Klokun
e5d44f741d docs: clarify new meaning of the --format CLI flag 2022-12-22 15:00:59 +02:00
Vlad Klokun
f005cb7f80 feat: always print to (T)UI using PrettyPrinter
Prior to this change, `pretty-printer` was a special type of Printer
that wrote output to `Stdout`, unless explicitly asked to write to a
given file. Kubescape used `pretty-printer` as an output format by
default. This behavior created the following inconsistencies:
- When invoked as `kubescape scan`, Kubescape would use `pretty-printer`
  by default, and it would output the scan resluts in the
  `pretty-printer` format to `Stdout`.
- When invoked as `kubescape scan --format=pretty-printer`, the behavior
  would be as above.
- When invoked as `kubescape scan --format=FORMAT`, where `FORMAT` is any
  format except for `pretty-printer`, Kubescape would write the results
  to a sensible default file for the selected format. This is in
  contrast to how `--format=pretty-printer` would still output to
  `os.Stdout`, and not an output file.
- When invoked as `kubescape scan --format=ANY_FORMAT --output=FILENAME`, where
  `ANY_FORMAT` is any format, including `pretty-printer`, Kubescape
  would write the results to the provided `FILENAME` in the given
  `ANY_FORMAT`, and not write any results to `Stdout`.

The aforementioned situation complicates life for users running
Kubescape in CI, where Kubescape would skip writing the results to
`Stdout` and only write to the provided output file.

Moreover, with the addition of support for multiple output formats and,
hence, files, this introduces the following ambiguity:
- When invoked as `kubescape scan --format=json,pdf,pretty-printer
  --output=FILENAME`, should Kubescape treat `pretty-printer` as a
  format for the output file, or just an instruction to also print the
  results to `Stdout`?

To fix these inconsistencies and ambiguities, this commit introduces the
following changes:

- Kubescape will always print results to `Stdout` using the
  PrettyPrinter format.

- The `--format` CLI flag will control the format(s) in which the results
  will be written to one or many *output* files. This breaks the
  previous behavior that running `kubescape scan
  --format=pretty-printer` would not produce an output file, and only
  write to `Stdout`. After this change, the same invocation will still
  write to `Stdout`, but also produce a `report.txt` file in the
  PrettyPrinter format.
2022-12-22 15:00:59 +02:00
Vlad Klokun
9ae9d35ccb style: GetFormatsFormats 2022-12-22 15:00:59 +02:00
Vlad Klokun
cb38a4e8a1 style: go fmt the project
- Fixes style inside the project with `go fmt`
2022-12-22 15:00:59 +02:00
Vlad Klokun
eb6d39be42 style: shorten receiver names in ResultsHandler 2022-12-22 15:00:59 +02:00
Vlad Klokun
3160d74c42 style: shorten receiver names for Prometheus printer 2022-12-22 15:00:59 +02:00
Vlad Klokun
5076c38482 refactor: tidy up printing to multiple outputs
This change:

- Simplifies printing to multiple outputs.
- Adds a comment on why we keep the Print → Score → Submit order when
  outputting results.
2022-12-22 15:00:59 +02:00
Vlad Klokun
73c55fe253 fix: revert the overriden ScanningTarget when submitting reports
Before this change, we used to override a scan info `ScanningTarget` to
submit a result that is compatible with the backend for Kubescape.
However, previously we forgot to change back to the original value.

When printing scan results, if the correct order of events (Print →
Score → Submit) was not enforced, this broke the SARIF printer so that
it did not output results due to incorrect `basePath` for the results.

This change reverts to the original `ScanningTarget` value after
submitting the results and fixes the SARIF printer.
2022-12-22 15:00:59 +02:00
suhasgumma
f48f81c0b5 Add logs for some formats 2022-12-22 15:00:59 +02:00
Suhas Gumma
81c1c29b7c Update core/pkg/resultshandling/printer/printresults.go
Co-authored-by: Vlad Klokun <vladklokun@users.noreply.github.com>
2022-12-22 15:00:59 +02:00
suhasgumma
874aa38f68 Handle Output Extensions Gracefully 2022-12-22 15:00:59 +02:00
suhasgumma
b9caaf5025 Add logs for some formats 2022-12-22 15:00:59 +02:00
suhasgumma
61c120de0e Support getting outputs in multiple formats 2022-12-22 15:00:59 +02:00
kooomix
de3408bf57 minor fix 2022-12-22 14:09:27 +02:00
David Wertenteil
8d32032ec1 Merge branch 'cloud-name-breakdown' of github.com:dwertent/kubescape into cloud-name-breakdown 2022-12-22 13:33:23 +02:00
David Wertenteil
42ed787f7b update go mod in httphandler 2022-12-22 13:32:27 +02:00
David Wertenteil
ccdba85b3c Merge branch 'dev' into cloud-name-breakdown 2022-12-22 11:57:47 +02:00
David Wertenteil
c59f7691dc Breakdown cloud-cluster name 2022-12-22 11:43:45 +02:00
kooomix
cf87c2d30b Fixed test 2022-12-21 19:25:22 +02:00
kooomix
b547814dec DownloadInfo, PolicyIdentifier add Identity, remove ID and Name 2022-12-21 19:17:29 +02:00
kooomix
b476a72e04 Merge branch 'dev' of github.com:kooomix/kubescape into dev 2022-12-21 17:02:24 +02:00
kooomix
4f6f85710a Merge pull request #988 from kooomix/opa_utils-v.0.0.216
opa-utils v0.0.216
2022-12-21 16:06:34 +02:00
kooomix
47c23de160 opa-utils v0.0.216 2022-12-21 15:46:53 +02:00
kooomix
bc85844ec0 Merge branch 'kubescape:dev' into dev 2022-12-21 15:31:07 +02:00
kooomix
134d854722 opa-utils v0.0.216 2022-12-21 15:29:58 +02:00
David Wertenteil
e3522c19cc Merge pull request #986 from dwertent/master
Cosmetic changes
2022-12-20 10:33:21 +02:00
David Wertenteil
967fc3fe81 ignore resource if it is not found 2022-12-19 19:00:21 +02:00
David Wertenteil
896a0699ec remove image vuln warning 2022-12-18 13:45:43 +02:00
David Wertenteil
a53375204e remove --verbose flag from default 2022-12-18 13:44:12 +02:00
David Wertenteil
b1392361f8 remove emoji from display 2022-12-18 13:42:58 +02:00
David Wertenteil
7b4fbffae2 Merge pull request #976 from mkilchhofer/explicit_allowPrivilegeEscalation
chore: Explicit set allowPrivilegeEscalation=true
2022-12-18 08:09:35 +02:00
David Wertenteil
34e7b9f2ad Merge pull request #978 from kubescape/maintainers
Update maintainers
2022-12-18 08:08:20 +02:00
David Wertenteil
f0080bdeae Merge pull request #979 from craigbox/code-of-conduct
Adopt CNCF Code of Conduct.
2022-12-18 08:07:39 +02:00
Craig Box
0eb27389da Adopt CNCF Code of Conduct.
Signed-off-by: Craig Box <craigb@armosec.io>
2022-12-16 21:46:31 +13:00
craigbox
2c5eed9ee2 Update maintainers
- Add self
- Remove e-mail addresses & job roles
2022-12-16 21:19:02 +13:00
David Wertenteil
2c1a5bd032 Merge pull request #977 from kubescape/revert-973-dev
Revert "Excluding controlPlaneInfo from error message in case no data recieved."
2022-12-15 17:48:32 +02:00
David Wertenteil
298f8346e9 validate downloaded framework 2022-12-15 17:13:14 +02:00
kooomix
1897c5a4ba Revert "Excluding controlPlaneInfo from error message in case no data recieved." 2022-12-15 16:17:39 +02:00
Marco Kilchhofer
57e435271e chore: Explicit set allowPrivilegeEscalation=true
The value of allowPrivilegeEscalation followed implicit default of Kubernetes:
> AllowPrivilegeEscalation is true always when the container is:
> 1) run as Privileged
> 2) has CAP_SYS_ADMIN

For users still using PodSecurityPolicy (or a follow-up product like OPA Gatekeeper or
Kyverno), there might be mutating admission controllers which defaults this field to
`false` if unset. A value of `false` would then conflict with `privileged: true`.

Signed-off-by: Marco Kilchhofer <mkilchhofer@users.noreply.github.com>
2022-12-14 22:27:05 +01:00
kooomix
7e9b430347 test fix 2022-12-14 14:22:46 +02:00
kooomix
ca5b3e626b test fix 2022-12-14 14:08:32 +02:00
kooomix
3a404f29fa control scan by id 2022-12-14 13:42:52 +02:00
kooomix
16073d6872 download control only by id 2022-12-14 13:06:04 +02:00
Rotem Refael
dce563d2f5 Merge pull request #973 from kooomix/dev
Excluding controlPlaneInfo from error message in case no data recieved.
2022-12-14 11:02:55 +02:00
kooomix
8d556a5b84 minor 2022-12-14 10:48:01 +02:00
kooomix
a61063e5b8 revert opa-utils version 2022-12-14 10:45:24 +02:00
kooomix
94973867db Merge branch 'kubescape:dev' into dev 2022-12-14 10:23:11 +02:00
kooomix
214c2dcae8 patch to filter out "controlPlaneInfo" from error messages in case no data 2022-12-14 10:19:24 +02:00
David Wertenteil
72b36bf012 Merge pull request #968 from fredbi/chore/package-name
chore(style): renamed versioned packages to stick to idiomatic conventions
2022-12-13 16:52:57 +02:00
Frederic BIDON
4335e6ceac chore(style): renamed versioned packages to stick to idiomatic conventions
* fixes: #967

Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2022-12-13 14:27:21 +01:00
kooomix
b5f92a7d54 go mod tidy 2022-12-13 11:32:23 +02:00
kooomix
41ec75d264 update opa-utils v0.0.209 2022-12-13 11:20:17 +02:00
kooomix
6d6ad1f487 Merge pull request #963 from kooomix/outputs_to_get_controls_only_by_ids
All prints and outputs to get data only by control ID
2022-12-13 08:32:01 +02:00
kooomix
3ac33d21ac All prints and outputs to get data by control ID 2022-12-12 15:20:48 +02:00
Anubhav Gupta
04e4b37f6f added GitLab repo scanning support
Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>
2022-12-11 21:39:28 +05:30
David Wertenteil
3e5903de6a Merge pull request #961 from kubescape/dev
change linux runner to 20.04 instead of ubuntu-latest (#960)
2022-12-11 15:02:04 +02:00
Moshe Rappaport
04ea0fe524 change linux runner to 20.04 instead of ubuntu-latest (#960)
Co-authored-by: Amir Malka <amirm@armosec.io>
2022-12-11 14:20:28 +02:00
David Wertenteil
955d6751a9 Merge pull request #956 from kubescape/dev
Enhance `host-scanner`
2022-12-08 22:51:26 +02:00
David Wertenteil
30c43bff10 Merge pull request #958 from Moshe-Rappaport-CA/dev
Fix Junit format
2022-12-08 19:41:31 +02:00
Moshe-Rappaport-CA
e009244566 Fix Junit format 2022-12-08 17:56:16 +02:00
David Wertenteil
3d3cd2c2d8 Added Kubescape flow.drawio 2022-12-06 15:44:34 +02:00
David Wertenteil
f5498371ec Merge pull request #942 from kooomix/eran-dev
new host-scanner endpoint - cloudProviderInfo
2022-12-06 15:20:24 +02:00
David Wertenteil
c3b95bed8c Merge branch 'dev' into eran-dev 2022-12-06 14:17:49 +02:00
David Wertenteil
8ce7d6c0f6 Merge pull request #930 from JusteenR/issue929
Issue929
2022-12-06 14:15:35 +02:00
David Wertenteil
e875f429a9 Merge pull request #948 from YiscahLevySilas1/dev
Print host scanner version
2022-12-06 14:13:47 +02:00
David Wertenteil
b6beff0488 Merge pull request #946 from suhasgumma/dev
Fixed: CIS control link not working for html output format
2022-12-06 14:13:06 +02:00
David Wertenteil
60c69ac3f0 Merge pull request #950 from fredbi/fix-789
fix(giturlparse): fixes panic on unexpected gitlab remote URL
2022-12-06 14:12:25 +02:00
David Wertenteil
1fb9320421 Merge pull request #941 from dwertent/master
Updating examples
2022-12-06 14:11:07 +02:00
David Wertenteil
9a176f6667 remove tag latest 2022-12-06 11:42:34 +02:00
David Wertenteil
96ea9a9e42 fixed scanning example 2022-12-06 11:41:12 +02:00
David Wertenteil
e39fca0c11 do not build dev images 2022-12-06 11:05:21 +02:00
David Wertenteil
2ec035005d fixed echo command 2022-12-04 15:45:23 +02:00
Frederic BIDON
b734b3aef0 go mod tidy ancillary modules manifest
Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2022-12-04 12:39:34 +01:00
yiscah
0f5635f42d move parsing of version to GetVersion 2022-12-04 12:17:04 +02:00
Frederic BIDON
8557075b7c fix(giturlparse): fixes panic on unexpected gitlab remote URL
* replaced dependencies to github.com/armosec/go-git-url by
github.com/kubescape/go-git-url
* fixes #789

NOTE: this requires kubescape/go-git-url#2 to be merged, a new release
of that repo to be cut, in order to finalize the dependency update.

Signed-off-by: Frederic BIDON <fredbi@yahoo.com>
2022-12-02 16:09:25 +01:00
David Wertenteil
bc0f0e7087 Merge branch 'master' of github.com:dwertent/kubescape 2022-12-02 02:31:14 +02:00
David Wertenteil
8ce5f9aea3 fixed typo 2022-12-02 02:30:35 +02:00
David Wertenteil
050f9d3a4e Update cmd/scan/framework.go
Co-authored-by: craigbox <craig.box@gmail.com>
2022-12-02 02:16:43 +02:00
David Wertenteil
a81bf0deb4 deprecate set-output 2022-12-02 01:43:45 +02:00
David Wertenteil
2059324c27 testing release 2022-12-02 01:35:57 +02:00
David Wertenteil
a09a0a1bca Merge pull request #9 from dwertent/fix-dev-image
run build only if secret is set
2022-12-02 01:32:26 +02:00
David Wertenteil
83712bb9f5 run build only if secret is set 2022-12-02 01:30:24 +02:00
David Wertenteil
728ae47b9a Merge pull request #8 from dwertent/fix-dev-image
Fix dev image
2022-12-02 00:56:12 +02:00
David Wertenteil
2a9b272a14 tagging only main image as latest 2022-12-02 00:54:03 +02:00
David Wertenteil
8662deac43 update repository scanning URL 2022-12-02 00:42:28 +02:00
yiscah
e42644bbd8 update hostscanner version 2022-12-01 08:57:58 +02:00
YiscahLevySilas1
07d30b6272 Merge branch 'kubescape:dev' into dev 2022-11-30 20:52:48 +02:00
yiscah
2a4f8543cc added logs of host scanner version 2022-11-30 20:51:45 +02:00
suhasgumma
186b293cce fix link for cis controls in html output 2022-11-30 01:23:45 +05:30
David Wertenteil
2bfe72f39d Merge pull request #944 from kooomix/dev
opa-utils adjustments + dataControlInputs support
2022-11-29 19:11:49 +02:00
kooomix
f99f955223 go mod tidy 2022-11-29 15:26:30 +02:00
kooomix
ec56e69a3c minor fix 2022-11-29 14:55:30 +02:00
kooomix
3942583b1d Merge pull request #1 from kooomix/dataControlInputs
update opa-utils functions
2022-11-29 14:35:08 +02:00
kooomix
a10b15ba4b update opa-utils functions 2022-11-29 14:29:33 +02:00
David Wertenteil
5003cbd7a8 Merge pull request #943 from suhasgumma/invalidformat
Handle Invalid Formats
2022-11-28 17:39:14 +02:00
kooomix
481a137c23 Update host-scanner image version to v1.0.38 2022-11-28 16:46:32 +02:00
suhasgumma
c3f7f0938d Handle Invalid Formats 2022-11-28 19:56:27 +05:30
kooomix
b1925fa38d Support in new host-scanner endpoint - cloudProviderInfo 2022-11-28 09:18:43 +02:00
David Wertenteil
d9f8a7a46f Merge pull request #918 from suhasgumma/dev1
Store Git Repo's root path as localRootPath
2022-11-27 16:25:24 +02:00
David Wertenteil
846a072bf9 Merge pull request #917 from suhasgumma/dev
Fixed: Wrong Relative Path When scanning Local Directory
2022-11-27 16:24:19 +02:00
kooomix
5dd7bbd8a7 Merge pull request #938 from kooomix/eran-dev
Added cloudProvider to postureControlInputs
2022-11-27 09:06:56 +02:00
kooomix
e1773acf24 Getting cloud provider from gitversion of discovered API version 2022-11-25 09:27:27 +02:00
kooomix
03a0f97669 Getting cluster name from context 2022-11-24 16:09:05 +02:00
David Wertenteil
917a3f41e8 Merge pull request #925 from amirmalka/dev
Omit raw resources flag in json output
2022-11-24 14:47:14 +02:00
David Wertenteil
3c8da1b299 supporting client type from env 2022-11-24 11:09:30 +02:00
David Wertenteil
c61c7edbd0 update examples 2022-11-24 11:06:37 +02:00
kooomix
53402d9a1c Added "CloudProvider" to postureControlInputs 2022-11-23 11:57:36 +02:00
David Wertenteil
de9278b388 Merge pull request #935 from mkilchhofer/bugfix/use_correct_directory
fix: filepath.Dir requires trailing slash
2022-11-23 10:49:16 +02:00
Marco Kilchhofer
4fef6200f8 fix: filepath.Dir requires trailing slash
Signed-off-by: Marco Kilchhofer <mkilchhofer@users.noreply.github.com>
2022-11-22 21:26:37 +01:00
JusteenR
81771b7bd7 Adding frameworks column to control command 2022-11-20 15:42:13 -08:00
Moshe Rappaport
2fee77c42c Merge pull request #928 from Moshe-Rappaport-CA/PER-633-support-loading-exceptions-from-cache-kubescape
PER-633 support loading exceptions from cache
2022-11-20 14:09:30 +02:00
Moshe-Rappaport-CA
968ecdb31d PER-633 support loading exceptions from cache 2022-11-20 12:22:15 +02:00
David Wertenteil
af7b36a88b Merge pull request #927 from Moshe-Rappaport-CA/PER-550-support-loading-attack-tracks-from-cache-kubescape
Per 550 support loading attack tracks from cache kubescape
2022-11-20 11:24:38 +02:00
Moshe-Rappaport-CA
6ad58d38e2 PER-550 Support loading attack tracks from cache 2022-11-17 16:31:51 +02:00
Moshe-Rappaport-CA
681b4ce155 stash 2022-11-17 10:49:36 +02:00
Moshe Rappaport
9d21ac1b16 Merge pull request #924 from Moshe-Rappaport-CA/dev
revert change in Junit format
2022-11-16 15:36:48 +02:00
Amir Malka
2b3fcca7e8 omit raw resources flag in json output 2022-11-16 12:15:17 +02:00
David Wertenteil
af8e786ab5 Merge pull request #914 from kubescape/dev
Closing issues
2022-11-16 10:59:59 +02:00
Moshe-Rappaport-CA
c8df1b8f1f Merge remote-tracking branch 'armo/dev' into dev 2022-11-15 17:34:45 +02:00
Moshe-Rappaport-CA
4f921ddf6f Revert PR #802 to the old junit format 2022-11-15 16:59:37 +02:00
David Wertenteil
4f5839870b Merge pull request #920 from amirmalka/dev
Fixed docker build to support ARM #919
2022-11-15 14:53:20 +02:00
Amir Malka
c0d7f51d6c test build flow 2022-11-15 13:29:35 +02:00
Amir Malka
a81d770360 fixed docker build to support arm 2022-11-15 10:57:29 +02:00
suhasgumma
f64d5eab50 Fix RootDir Info 2022-11-15 12:38:57 +05:30
suhasgumma
d773397fe9 replace src with RelSrc 2022-11-15 10:34:36 +05:30
suhasgumma
2e30995bfc Relative Path When scanning Local Repos 2022-11-15 10:22:04 +05:30
David Wertenteil
17a2547f18 Merge pull request #915 from kubescape/change-test-control-name
replace control 0006 by 0048
2022-11-14 14:56:34 +02:00
David Wertenteil
87a5cd66c8 replace control 0006 by 0048 2022-11-14 14:36:37 +02:00
David Wertenteil
9436ace64f continue when resource not found 2022-11-14 13:52:46 +02:00
David Wertenteil
fde00f6bd8 Merge pull request #909 from suhasgumma/dev
pretty-print Controls format  made Pretty
2022-11-13 17:04:46 +02:00
David Wertenteil
04a72a069a Merge pull request #913 from dwertent/ignore-missing-resource
Do not exit on error
2022-11-13 16:04:15 +02:00
David Wertenteil
e2dcb5bc15 Merge pull request #912 from dwertent/dep-rbac-submit
Deprecate rbac submit
2022-11-13 16:03:51 +02:00
suhasgumma
c7040a257c Pretty Print frameworks and exceptions 2022-11-13 19:29:26 +05:30
suhasgumma
602dc00c65 Shift GetControlLink to cautils 2022-11-13 19:09:30 +05:30
David Wertenteil
0339691571 Merge pull request #911 from dwertent/adding-remidiation
Adding remediation
2022-11-13 15:12:21 +02:00
David Wertenteil
9e1f3ec131 remove from smoke test 2022-11-13 15:10:05 +02:00
David Wertenteil
b8589819dc Do not exit on error 2022-11-13 15:06:32 +02:00
David Wertenteil
a3e87f4c01 Updating json v1 deprecation message 2022-11-13 15:03:22 +02:00
David Wertenteil
21ab5a602e Deprecate rbac submit 2022-11-13 15:01:32 +02:00
David Wertenteil
5d97d7b4b2 adding Remediation to message 2022-11-13 14:55:52 +02:00
suhasgumma
d8d7d0b372 Updated and Used GetControlLink 2022-11-13 17:56:39 +05:30
suhasgumma
b8323d41fc Modified Link Convention for CIS Controls 2022-11-13 17:22:37 +05:30
suhasgumma
d0b5314201 Improve Code Quality 2022-11-13 15:39:04 +05:30
suhasgumma
547e36e73f Pretty Print Controls made Pretty 2022-11-13 14:29:30 +05:30
David Wertenteil
e593a772cb Merge pull request #908 from Moshe-Rappaport-CA/update-k8s-interface-version
Update k8s-interface version and rbac-utils
2022-11-13 09:31:00 +02:00
Moshe-Rappaport-CA
4da09529b6 Update rbac-utils tag 2022-11-10 18:56:28 +02:00
Moshe-Rappaport-CA
de375992e8 Fix go.mod in httphandler 2022-11-10 17:54:44 +02:00
Moshe-Rappaport-CA
0bc4a29881 Update k8s-interface version 2022-11-10 17:38:32 +02:00
David Wertenteil
9575c92713 Merge pull request #906 from suhasgumma/dev
Fixed: Empty Lines before printing Controls and Added Invalid Format Error
2022-11-10 11:27:22 +02:00
David Wertenteil
cf277874eb Merge pull request #907 from matthyx/ioutil
remove deprecated ioutil package
2022-11-10 11:23:10 +02:00
Matthias Bertschy
746e060402 remove deprecated ioutil package 2022-11-10 09:58:07 +01:00
suhasgumma
dd3a7c816e Invalid Format Error 2022-11-10 11:57:57 +05:30
suhasgumma
814bc3ab2c Solved: Empty Lines before printing Controls 2022-11-10 11:17:48 +05:30
David Wertenteil
dbaf6761df Merge pull request #905 from matthyx/900
900
2022-11-10 06:52:34 +02:00
Matthias Bertschy
580e45827d add IDs to controls list, deprecate id flag 2022-11-09 22:08:04 +01:00
David Wertenteil
f3b8de9d1f fixing readme (#899) 2022-11-08 12:02:52 +02:00
David Wertenteil
fb1c728b12 Merge pull request #897 from Moshe-Rappaport-CA/fix-use-artifact-from
Fix reading frameworks from local file
2022-11-06 17:34:44 +02:00
Moshe-Rappaport-CA
6964ca0d18 Fix reading frameworks from local file 2022-11-06 17:06:27 +02:00
David Wertenteil
6e9a2f55fd Merge pull request #894 from kubescape/dev
Enhancing CLI capabilities and SARIF output
2022-11-06 15:40:00 +02:00
David Wertenteil
691fa61362 Merge pull request #896 from kubescape/revert-submit-dep
Revert submit deprecation
2022-11-06 15:06:49 +02:00
David Wertenteil
0c1eda0d08 retrieve submit flag 2022-11-03 20:33:10 +02:00
David Wertenteil
767eac2fa6 fixed indentation 2022-11-03 20:31:59 +02:00
David Wertenteil
6f651fa2d0 Merge pull request #893 from amirmalka/dev
Added line and column information when using the SARIF format
2022-11-03 15:38:33 +02:00
Amir Malka
e3362c2e3d updated go.mod 2022-11-03 14:38:07 +02:00
Amir Malka
08b8b68f9a added line and column information when using the sarif format 2022-11-03 14:08:53 +02:00
Amir Malka
daf9ca9e7f Merge pull request #825 from AvineshTripathi/feature
added adaptor
2022-11-03 12:48:23 +02:00
Amir Malka
d1024359c9 fix dependencies 2022-11-03 12:15:13 +02:00
Amir Malka
ed6070aff9 fix go.sum 2022-11-03 12:10:34 +02:00
Amir Malka
e4dbfa3534 Merge branch 'dev' into feature 2022-11-03 11:05:10 +02:00
Avinesh Tripathi
ddd2b707c0 updated go.mod 2022-11-03 14:17:53 +05:30
David Wertenteil
cd4f1077c2 Merge pull request #892 from Moshe-Rappaport-CA/dev
Add support to exceptions on repo (files) scan
2022-11-03 10:07:10 +02:00
Moshe-Rappaport-CA
b472d1cb9d Remove irrelevant code 2022-11-03 09:52:36 +02:00
David Wertenteil
922e2548f4 Merge pull request #888 from suhasgumma/dev
Inform User if something is wrong with "controls-inputs" file they provided
2022-11-03 09:38:27 +02:00
suhasgumma
45caa7c120 Adding Log in Caller Function 2022-11-03 07:35:52 +05:30
Moshe-Rappaport-CA
670ae45d62 remove --submit from msg when not submitted 2022-11-02 16:27:40 +02:00
Moshe-Rappaport-CA
05bcf018d1 Merge remote-tracking branch 'armo/dev' into dev 2022-11-02 15:02:30 +02:00
David Wertenteil
0af5d2e0bb Merge pull request #876 from Moshe-Rappaport-CA/PER-335-removing-support-for-submitting-results-without-accountid
Per 335 removing support for submitting results without accountid
2022-11-02 12:55:20 +02:00
Moshe-Rappaport-CA
eaf05fe9be Add support to exceptions on repo (files) scan 2022-11-02 12:49:14 +02:00
David Wertenteil
e97b23f345 remove platforms build 2022-11-02 12:45:18 +02:00
David Wertenteil
83a00ded3d removed log 2022-11-02 12:43:03 +02:00
Avinesh Tripathi
78f81cc968 Merge branch 'dev' into feature 2022-10-31 21:56:13 +05:30
Avinesh Tripathi
5d3347b4fe Added Path variables for credentials 2022-10-31 21:54:10 +05:30
David Wertenteil
64d2ef8170 Merge pull request #887 from Moshe-Rappaport-CA/dev
Support downloading system-exception from GitHub
2022-10-30 12:57:49 +02:00
suhasgumma
7c1e360b9a Error Info Added 2022-10-28 18:25:10 +05:30
Moshe Rappaport
575d36dcde Update core/core/initutils.go
Co-authored-by: David Wertenteil <dwertent@armosec.io>
2022-10-27 18:30:06 +03:00
Moshe-Rappaport-CA
8dba8f7491 Add go sum 2022-10-27 18:25:46 +03:00
Moshe-Rappaport-CA
cc39e5b905 Fix go mod and go sum 2022-10-27 18:18:26 +03:00
Moshe-Rappaport-CA
0be7e6018f Support downloading system-exception from GitHub 2022-10-27 17:59:41 +03:00
David Wertenteil
7697e3f0c4 Merge pull request #884 from Moshe-Rappaport-CA/dev
Scanning with include or exclude namespace, is only scan namespaced scope
2022-10-26 19:04:02 +03:00
David Wertenteil
379800c49f Merge pull request #885 from vladklokun/per-519-add-sarif-printer
feat: support the SARIF output format
2022-10-26 19:02:21 +03:00
Vlad Klokun
79e2515807 feat: support the SARIF output format 2022-10-26 18:13:37 +03:00
Moshe-Rappaport-CA
342f5743e2 Fix when running with include or exclude namespace
scanning only namespaced scope
2022-10-26 16:29:55 +03:00
David Wertenteil
0e81870b85 Merge pull request #799 from itsCheithanya/master
Add update command for kubescape
2022-10-26 15:46:03 +03:00
David Wertenteil
dd7a8fd0c1 Merge pull request #883 from kubescape/dev
Minor changes
2022-10-26 13:31:04 +03:00
David Wertenteil
4277331ee2 Merge pull request #882 from dwertent/master
Update README links
2022-10-26 13:04:00 +03:00
David Wertenteil
53561a728f Merge pull request #872 from BhairaviSanskriti/dev
Added tutorial for scanning container image registry
2022-10-26 13:00:45 +03:00
David Wertenteil
d0fd8c4fe4 Merge pull request #869 from shm12/master
APIServerInfo resource added to the scan
2022-10-26 12:59:51 +03:00
David Wertenteil
398989510b Merge pull request #881 from YiscahLevySilas1/dev
update hostsensor version
2022-10-26 12:56:55 +03:00
Amir Malka
f8e3ad5685 Merge pull request #845 from mrueg/Yamlv3
Upgrade to gopkg.in/yaml.v3
2022-10-26 12:56:31 +03:00
Moshe-Rappaport-CA
fbea7ef874 Merge remote-tracking branch 'armo/dev' into dev 2022-10-26 12:11:42 +03:00
YiscahLevySilas1
dc2c6f8a21 update hostsensor version 2022-10-26 11:40:28 +03:00
Amir Malka
5ee08583b6 Merge pull request #880 from amirmalka/dev
commented out tests which made actual http calls
2022-10-25 18:45:38 +03:00
Amir Malka
bfbd278e7c commented out tests which made actual http calls 2022-10-25 18:26:07 +03:00
David Wertenteil
4c6e5903e3 Adding links to readme 2022-10-25 09:49:43 +03:00
David Wertenteil
a7cd5672c1 Merge remote-tracking branch 'armosec/dev' 2022-10-24 17:23:41 +03:00
David Wertenteil
3373b728b7 Merge pull request #877 from kubescape/dev
Enhance configuration usage
2022-10-24 12:00:27 +03:00
David Wertenteil
22521b7159 use os path join in build file 2022-10-24 11:39:37 +03:00
David Wertenteil
e5fb14138e adding github token 2022-10-23 20:48:36 +03:00
David Wertenteil
1b2242330c comment out image release 2022-10-23 20:35:13 +03:00
David Wertenteil
356958cc55 Merge branch 'dev' of https://github.com/kubescape/kubescape into dev 2022-10-23 20:30:18 +03:00
Moshe Rappaport
8f1da32001 Store default URLs in cache (#875) 2022-10-23 20:29:25 +03:00
Moshe-Rappaport-CA
686352a397 PER-335 Fix comments 2022-10-23 16:54:16 +03:00
Moshe-Rappaport-CA
ef79c42ebc PER-335 Add comment 2022-10-23 16:25:47 +03:00
Moshe-Rappaport-CA
c8fc5378c1 PER-335 Remove submit option from kubescape docs 2022-10-23 16:19:38 +03:00
David Wertenteil
c296666d8e removed path from dockerfile 2022-10-23 16:00:10 +03:00
Moshe-Rappaport-CA
f193e260b0 Store default URLs in cache 2022-10-23 15:56:49 +03:00
Moshe-Rappaport-CA
82981a9a54 PER-335 Removing support for submitting results without accountID 2022-10-23 13:48:50 +03:00
David Wertenteil
3be54ca484 fixed typo 2022-10-23 12:35:54 +03:00
shm12
2f2c177674 Merge pull request #873 from shm12/dev
Fixed host-sensor control plane info collecting
2022-10-23 11:12:07 +03:00
shm12
1f47223918 Fixed host-sensor control plane info collecting 2022-10-23 10:46:44 +03:00
Sanskriti
eb646696a3 Update README.md
This is a video tutorial by Kunal Kushwaha on how to scan a container image registry.
2022-10-21 10:42:02 +05:30
Moshe Rappaport
7cfe5160d5 Merge pull request #871 from Moshe-Rappaport-CA/dev
Support for getting backend URLs from ENV
2022-10-20 10:44:32 +03:00
Moshe-Rappaport-CA
95135c4379 following review 2022-10-19 18:52:32 +03:00
Moshe-Rappaport-CA
7e604d6a5b Merge remote-tracking branch 'armo/dev' into dev 2022-10-19 13:29:09 +03:00
Moshe-Rappaport-CA
64ac2666f9 Support for getting backend URLs from ENV 2022-10-19 13:28:44 +03:00
Avinesh Tripathi
05b3459342 Merge branch 'dev' into feature 2022-10-19 13:44:27 +05:30
shm12
92ad5f2407 Merge pull request #868 from shm12/dev
New host sensor endpoint `controlPlaneInfo` and OPA upgrade
2022-10-18 16:14:36 +03:00
shm12
e3c60e3202 APIServerInfo resource added to the scan 2022-10-12 22:52:42 +03:00
shm12
7b5bcb05b1 Upgrade opa dependency 2022-10-12 20:51:50 +03:00
shm12
154f94a0af Added controlPlaneInfo host-sensor endpoint 2022-10-12 20:51:06 +03:00
David Wertenteil
063d3ee313 Adopt custom cluster name (#862)
* change user to ks

* update logs

* host scanner with fixed version

* testing gh actions

* testing gh actions

* Workflow call testing (#7)

* testing gh actions

* testing gh actions

* remove deps

* build multi platforms

* workflow_call testing

* adding workflow-call-testing branch name

* specify URL

* adding org name

* adding workflows

* cleaning from unused branches

* Adopt custom cluster name
2022-10-06 11:04:05 +03:00
David Wertenteil
79859d05c0 Merge remote-tracking branch 'armosec/dev' 2022-10-06 08:16:27 +03:00
David Wertenteil
acd3a94c46 Adopt custom cluster name 2022-10-06 08:15:58 +03:00
Alessio Greggi
13f09315e7 feat: add --kubeconfig flag (#858)
* feat: add --kubeconfig flag

* docs: add kubeconfig flag example
2022-10-06 08:11:42 +03:00
Manuel Rüger
890528bf14 Update go.mod 2022-10-03 21:21:31 +02:00
Manuel Rüger
e4aafcf81e Migrate to yaml.v3 2022-10-03 21:20:15 +02:00
David Wertenteil
81c3c34ab8 Use workflow_call (#859)
* change user to ks

* update logs

* host scanner with fixed version

* testing gh actions

* testing gh actions

* Workflow call testing (#7)

* testing gh actions

* testing gh actions

* remove deps

* build multi platforms

* workflow_call testing

* adding workflow-call-testing branch name

* specify URL

* adding org name

* adding workflows

* cleaning from unused branches
2022-10-03 14:36:44 +03:00
David Wertenteil
b7b83b26b5 cleaning from unused branches 2022-10-03 14:35:07 +03:00
David Wertenteil
639cd3dfae Merge branch 'master' of github.com:dwertent/kubescape 2022-10-03 13:52:44 +03:00
David Wertenteil
7cf1302e8a Workflow call testing (#7)
* testing gh actions

* testing gh actions

* remove deps

* build multi platforms

* workflow_call testing

* adding workflow-call-testing branch name

* specify URL

* adding org name

* adding workflows
2022-10-03 13:48:50 +03:00
David Wertenteil
dd5dd53a38 testing gh actions 2022-10-03 09:10:08 +03:00
David Wertenteil
7275b8eac7 testing gh actions 2022-10-03 09:09:50 +03:00
David Wertenteil
408c6fc998 Merge branch 'master' of github.com:dwertent/kubescape 2022-10-03 09:02:19 +03:00
David Wertenteil
5ce638572f update path env var 2022-10-02 23:09:24 +03:00
Cheithanya
4b98490ff9 update cmd functionality added 2022-10-02 23:29:21 +05:30
Cheithanya
6ea18ec75b update cmd functionality added 2022-10-02 23:27:39 +05:30
Ben Hirschberg
56e2ffec5c Add arch diagrams (#857)
* Publishing community meetings

* Adding architecture diagrams

* fix type

* resize diagrams

* Changing the operator diagram

Co-authored-by: Benyamin Hirschberg <59160382+BenHirschbergCa@users.noreply.github.com>
2022-10-02 20:36:59 +03:00
David Wertenteil
fa204a208a Merge branch 'dev' 2022-10-02 20:32:09 +03:00
David Wertenteil
9ab0fc593f Update python build file 2022-10-02 20:27:38 +03:00
Moshe-Rappaport-CA
3b9c454245 Merge remote-tracking branch 'armo/dev' into dev 2022-09-29 18:07:29 +03:00
David Wertenteil
a6fc7a0da0 Update logs (#854)
* change user to ks

* update logs

* host scanner with fixed version
2022-09-29 12:18:05 +03:00
David Wertenteil
53ae57e478 host scanner with fixed version 2022-09-29 11:19:24 +03:00
David Wertenteil
1d3401e3b4 Merge remote-tracking branch 'armosec/dev' 2022-09-29 10:47:27 +03:00
Dipankar Das
634198df06 Addition of Cosign for image signing (#826)
- github action (uncommented)

Signed-off-by: Dipankar Das <dipankardas0115@gmail.com>

Signed-off-by: Dipankar Das <dipankardas0115@gmail.com>
2022-09-29 10:46:55 +03:00
David Wertenteil
cffc3953ea Merge remote-tracking branch 'armosec/dev' 2022-09-29 10:37:09 +03:00
David Wertenteil
ea768602fb update logs 2022-09-29 10:36:27 +03:00
David Wertenteil
b4fc6dddd3 change user to ks 2022-09-29 10:35:54 +03:00
Asutosh Panda
96d90c217e Fix typos, linting errors of markdown file, modify the content to make it more concise (#820) 2022-09-29 09:12:43 +03:00
Zoomhawk
a2f1722455 Correcting Punctuation (#849)
* Correcting Punctuation

* Recorrect
2022-09-29 08:57:53 +03:00
Dipankar Das
400b51df1c Refactoring of Code Base (#853)
* Refactoring of switch to if statement

* Edited the core/pkg/score/score.go

Signed-off-by: Dipankar Das <dipankardas0115@gmail.com>

* Changes to function comments
* core/pkg/registryadaptors/registryvulnerabilities/interfaces.go

Signed-off-by: Dipankar Das <dipankardas0115@gmail.com>

Signed-off-by: Dipankar Das <dipankardas0115@gmail.com>
2022-09-29 08:57:12 +03:00
David Wertenteil
0f3ce6917e Release (#844)
* Fix issue for scanning list obj

* Fix go mod in httphandler pkg

* Broken links fix in roadmap.md 

Planning, backlog, and wishlist links were not taking to the required section.

* override infoMap only if it's not nil

* improved icon of kubescape in readme

* Support scanning several files

* gramatical improvements

* docs(readme): Star → star

* Fix issues according to review

* Handle with issues  caused by updating opa-utils

* Fix scanning ListObj following reviews

* Update core/pkg/resourcehandler/filesloader.go

Co-authored-by: Vlad Klokun <vladklokun@users.noreply.github.com>

* Update completion.go

* Added fixed control input

* update go.mod

* Print chart name log when fail to generate

* Change formatting to %s

* Added resource prioritization information, raw resource will be sent on the result object

* Merging typo fixes from master (#772)

* greetings

* Update aws.sh

simplified the comment

* typo: In the title and h1 element

Their was a typo in index.html file.

* punctuation changes

* docs : added gitpod badge in readme.md

* fixed typos

* ƒ some grammar mistake is corrected inPULL_REQUEST_TEMPLATE.md file

* Updated README.md file

Added link to CONTRIBUTING.md file in a line in README.

* Added link to code of conduct file

I have added link to the code of conduct file and fixed some problems in the Readme file.

* Fixed readme

* Added alpine tag

Adding alpine tag instead of latest and removing repeating commands

* roadmap.md file is modified

* Automatically Close "Typo" labelled Issue

* build.py is modified

* modified PR template

* Fixed some typos in feature_request.md

"." at the end of the headings were missing and all the text were in same line.
Now this gives a clear and concise view of the texts.

* fixed the typo in docs/index.html

Found and fixed typo in the 'alt' attribute of img tag

* Update PULL_REQUEST_TEMPLATE.md

Co-authored-by: Krishna Agarwal <dmkrishna.agarwal@gmail.com>
Co-authored-by: Saswata Senapati <74651639+saswat16@users.noreply.github.com>
Co-authored-by: Rahul Singh <110548934+rahuldhirendersingh@users.noreply.github.com>
Co-authored-by: deepuyadav004 <deepuyadavze@gmail.com>
Co-authored-by: kartik <97971066+kartikgajjar7@users.noreply.github.com>
Co-authored-by: Rounak-28 <95576871+Rounak-28@users.noreply.github.com>
Co-authored-by: pwnb0y <vickykr07@yahoo.com>
Co-authored-by: Ben Hirschberg <59160382+slashben@users.noreply.github.com>
Co-authored-by: Saptarshi Sarkar <saptarshi.programmer@gmail.com>
Co-authored-by: Rahul Surwade <93492791+RahulSurwade08@users.noreply.github.com>
Co-authored-by: Suhas Gumma <43647369+suhasgumma@users.noreply.github.com>
Co-authored-by: Kamal Nayan <95926324+legendarykamal@users.noreply.github.com>
Co-authored-by: TarangVerma <90996971+TarangVerma@users.noreply.github.com>
Co-authored-by: avikittu <65793296+avikittu@users.noreply.github.com>

* update logger version

* update logger version (#773)

* Fixed: Kubescape fails to authenticate remote private Github repo (#721)

* grammar error fixer in CONTRIBUTING.md

* scanning private git repository is available

* giturl to gitapi

* NO TOKEN error functionality added

* Used GetToken method of giturl.IGitAPPI for auth

Co-authored-by: satyam kale <satyamkale271@gmail.com>
Co-authored-by: Ben Hirschberg <59160382+slashben@users.noreply.github.com>

* bump opa-utils to 181

* Option to force enable color output (closes #560) (#767)

* Option to force enable color output (closes #560)

(cherry picked from commit 4f951781ee8dd6bb451ac7d159787f47e4b07379)

* Update go.mod

* update scanner image

* Update host scanner image  (#774)

* update logger version

* update scanner image

* remove windows exe extension

* Remove windows extension build (#775)

* update logger version

* update scanner image

* remove windows exe extension

* commened out prioritization logic

* Edit Junit output (#802)

* Edit Junit output

* Update go sum

* Following review

* update AdoptClusterName

* Print line separator only if some controls failed (#813)

* removed the extra 'download' word from the example (#810)

it was confusing to understand the download command because there was an extra 'download' mentioned

* Prioritization (#815)

* removed commented out code

* Added attack tracks information to prioritization algorithm

* bump opa-utils

* go mod tidy

* go mod tidy

* CR changes

* Issue 613 cluster name (#783)

* added --clusterName flag (#613)

Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>

* update flag name to --cluster-name

Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>

Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>

* Per 307 fail on severity counters (#831)

* feat: fail on exceeding severity thresholds (#830)

- Add support for severity counters
- Add support for CLI flags that set severity thresholds
- Terminate Kubescape with an exit code 1 if scan results exceed the
  severity thresholds

* Update opa-utils pkg version

Co-authored-by: Vlad Klokun <vladklokun@users.noreply.github.com>

* Fix merge conflict

* typo in .gitignore file (#833)

* remove unsupported installation method

* fixed welcome message

* fixed merge

* fixed attack tracks loading logic

* add flag validation for --account-id (#605) (#793)

* add flag validation for --account-id (#605)

Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>

* add flag validation for --client-id & --secret-key

Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>

* Validation method should be a member function

* Adding unit tests for credentials validate

Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>
Co-authored-by: David Wertenteil <dwertent@armosec.io>

* Scan Kustomize Directory (#795)

* Scan Kustomize Files

* update 'scam Kustomize Directory' documentation in  Readme.md

* go get

* go get inside httphandler

* SourceTypeKustomizeDirectory

* Added Scan for Kustomization File

Co-authored-by: David Wertenteil <dwertent@armosec.io>

* feat: unify severity threshold into one CLI flag (#838)

* feat: unify severity threshold into one CLI flag

Before this commit, severity threshold flags were separated by severity.
This commit unifies these thresholds into one flag that forces Kubescape
to terminate with an exit code 1 if there was at least one failed
control at the specified severity threshold or above.

* chore: update opa utils version

* chore: update opa-utils in httphandler

* feat: dont enforce severity by default

Previous iteration of supporting the severity threshold enforced it even
if the severity threshold was not explicitly specified.
This change enforces the severity threshold only if it has been
explicitly set.

* refactor: clarify flagValidationFramework func name

This change clarifies the meaning of the function that validates the
scan info for the `scan framework` command.
It achieves this by renaming the `flagValidationFramework` function to
`validateFrameworkScanInfo`.

* Merge branch 'master' into dev

Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>
Co-authored-by: Moshe-Rappaport-CA <moshep@armosec.io>
Co-authored-by: Moshe Rappaport <89577611+Moshe-Rappaport-CA@users.noreply.github.com>
Co-authored-by: Om Raut <33827410+om2137@users.noreply.github.com>
Co-authored-by: Kamal Nayan <95926324+legendarykamal@users.noreply.github.com>
Co-authored-by: Vlad Klokun <vladklokun@users.noreply.github.com>
Co-authored-by: Chirag Arora <84070677+Chirag8023@users.noreply.github.com>
Co-authored-by: shm12 <shmuelb@armosec.io>
Co-authored-by: Amir Malka <amirm@armosec.io>
Co-authored-by: Krishna Agarwal <dmkrishna.agarwal@gmail.com>
Co-authored-by: Saswata Senapati <74651639+saswat16@users.noreply.github.com>
Co-authored-by: Rahul Singh <110548934+rahuldhirendersingh@users.noreply.github.com>
Co-authored-by: deepuyadav004 <deepuyadavze@gmail.com>
Co-authored-by: kartik <97971066+kartikgajjar7@users.noreply.github.com>
Co-authored-by: Rounak-28 <95576871+Rounak-28@users.noreply.github.com>
Co-authored-by: pwnb0y <vickykr07@yahoo.com>
Co-authored-by: Ben Hirschberg <59160382+slashben@users.noreply.github.com>
Co-authored-by: Saptarshi Sarkar <saptarshi.programmer@gmail.com>
Co-authored-by: Rahul Surwade <93492791+RahulSurwade08@users.noreply.github.com>
Co-authored-by: Suhas Gumma <43647369+suhasgumma@users.noreply.github.com>
Co-authored-by: TarangVerma <90996971+TarangVerma@users.noreply.github.com>
Co-authored-by: avikittu <65793296+avikittu@users.noreply.github.com>
Co-authored-by: satyam kale <satyamkale271@gmail.com>
Co-authored-by: Aditya Pratap Singh <adityapratapsingh51@gmail.com>
Co-authored-by: Ashray Shetty <ashrayshetty1999@gmail.com>
Co-authored-by: Anubhav Gupta <mail.anubhav06@gmail.com>
Co-authored-by: Meyazhagan <meyazhagan.ofcl@gmail.com>
2022-09-29 08:48:09 +03:00
David Wertenteil
11e57fe7ad Merge branch 'master' into dev 2022-09-22 17:34:52 +03:00
David Wertenteil
2ddce8723d Merge branch 'master' into dev 2022-09-22 17:06:44 +03:00
Suhas Gumma
291668647c build killercoda playground added (#843) 2022-09-22 15:59:08 +03:00
Vlad Klokun
d3c41f2492 feat: unify severity threshold into one CLI flag (#838)
* feat: unify severity threshold into one CLI flag

Before this commit, severity threshold flags were separated by severity.
This commit unifies these thresholds into one flag that forces Kubescape
to terminate with an exit code 1 if there was at least one failed
control at the specified severity threshold or above.

* chore: update opa utils version

* chore: update opa-utils in httphandler

* feat: dont enforce severity by default

Previous iteration of supporting the severity threshold enforced it even
if the severity threshold was not explicitly specified.
This change enforces the severity threshold only if it has been
explicitly set.

* refactor: clarify flagValidationFramework func name

This change clarifies the meaning of the function that validates the
scan info for the `scan framework` command.
It achieves this by renaming the `flagValidationFramework` function to
`validateFrameworkScanInfo`.
2022-09-22 15:56:30 +03:00
Avinesh Tripathi
10fa3cb27d Merge branch 'dev' into feature 2022-09-21 23:37:55 +05:30
Avinesh Tripathi
d8f95edff5 removed getLastScanId function 2022-09-21 23:37:08 +05:30
Avinesh Tripathi
37ffe86d8b changed name and added readme 2022-09-21 23:32:16 +05:30
Avinesh Tripathi
87fdbfdcc5 fixed 2022-09-20 18:50:19 +05:30
Cheithanya
424a218860 Added the required changes 2022-09-20 17:09:02 +05:30
Suhas Gumma
faf0ae6bdc Scan Kustomize Directory (#795)
* Scan Kustomize Files

* update 'scam Kustomize Directory' documentation in  Readme.md

* go get

* go get inside httphandler

* SourceTypeKustomizeDirectory

* Added Scan for Kustomization File

Co-authored-by: David Wertenteil <dwertent@armosec.io>
2022-09-20 10:28:50 +03:00
Anubhav Gupta
e46c42554b add flag validation for --account-id (#605) (#793)
* add flag validation for --account-id (#605)

Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>

* add flag validation for --client-id & --secret-key

Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>

* Validation method should be a member function

* Adding unit tests for credentials validate

Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>
Co-authored-by: David Wertenteil <dwertent@armosec.io>
2022-09-20 10:22:35 +03:00
David Wertenteil
eb16440ba6 Enhancing CLI flags (#835)
* Fix issue for scanning list obj

* Fix go mod in httphandler pkg

* Broken links fix in roadmap.md 

Planning, backlog, and wishlist links were not taking to the required section.

* override infoMap only if it's not nil

* improved icon of kubescape in readme

* Support scanning several files

* gramatical improvements

* docs(readme): Star → star

* Fix issues according to review

* Handle with issues  caused by updating opa-utils

* Fix scanning ListObj following reviews

* Update core/pkg/resourcehandler/filesloader.go

Co-authored-by: Vlad Klokun <vladklokun@users.noreply.github.com>

* Update completion.go

* Added fixed control input

* update go.mod

* Print chart name log when fail to generate

* Change formatting to %s

* Added resource prioritization information, raw resource will be sent on the result object

* Merging typo fixes from master (#772)

* greetings

* Update aws.sh

simplified the comment

* typo: In the title and h1 element

Their was a typo in index.html file.

* punctuation changes

* docs : added gitpod badge in readme.md

* fixed typos

* ƒ some grammar mistake is corrected inPULL_REQUEST_TEMPLATE.md file

* Updated README.md file

Added link to CONTRIBUTING.md file in a line in README.

* Added link to code of conduct file

I have added link to the code of conduct file and fixed some problems in the Readme file.

* Fixed readme

* Added alpine tag

Adding alpine tag instead of latest and removing repeating commands

* roadmap.md file is modified

* Automatically Close "Typo" labelled Issue

* build.py is modified

* modified PR template

* Fixed some typos in feature_request.md

"." at the end of the headings were missing and all the text were in same line.
Now this gives a clear and concise view of the texts.

* fixed the typo in docs/index.html

Found and fixed typo in the 'alt' attribute of img tag

* Update PULL_REQUEST_TEMPLATE.md

Co-authored-by: Krishna Agarwal <dmkrishna.agarwal@gmail.com>
Co-authored-by: Saswata Senapati <74651639+saswat16@users.noreply.github.com>
Co-authored-by: Rahul Singh <110548934+rahuldhirendersingh@users.noreply.github.com>
Co-authored-by: deepuyadav004 <deepuyadavze@gmail.com>
Co-authored-by: kartik <97971066+kartikgajjar7@users.noreply.github.com>
Co-authored-by: Rounak-28 <95576871+Rounak-28@users.noreply.github.com>
Co-authored-by: pwnb0y <vickykr07@yahoo.com>
Co-authored-by: Ben Hirschberg <59160382+slashben@users.noreply.github.com>
Co-authored-by: Saptarshi Sarkar <saptarshi.programmer@gmail.com>
Co-authored-by: Rahul Surwade <93492791+RahulSurwade08@users.noreply.github.com>
Co-authored-by: Suhas Gumma <43647369+suhasgumma@users.noreply.github.com>
Co-authored-by: Kamal Nayan <95926324+legendarykamal@users.noreply.github.com>
Co-authored-by: TarangVerma <90996971+TarangVerma@users.noreply.github.com>
Co-authored-by: avikittu <65793296+avikittu@users.noreply.github.com>

* update logger version

* update logger version (#773)

* Fixed: Kubescape fails to authenticate remote private Github repo (#721)

* grammar error fixer in CONTRIBUTING.md

* scanning private git repository is available

* giturl to gitapi

* NO TOKEN error functionality added

* Used GetToken method of giturl.IGitAPPI for auth

Co-authored-by: satyam kale <satyamkale271@gmail.com>
Co-authored-by: Ben Hirschberg <59160382+slashben@users.noreply.github.com>

* bump opa-utils to 181

* Option to force enable color output (closes #560) (#767)

* Option to force enable color output (closes #560)

(cherry picked from commit 4f951781ee8dd6bb451ac7d159787f47e4b07379)

* Update go.mod

* update scanner image

* Update host scanner image  (#774)

* update logger version

* update scanner image

* remove windows exe extension

* Remove windows extension build (#775)

* update logger version

* update scanner image

* remove windows exe extension

* commened out prioritization logic

* Edit Junit output (#802)

* Edit Junit output

* Update go sum

* Following review

* update AdoptClusterName

* Print line separator only if some controls failed (#813)

* removed the extra 'download' word from the example (#810)

it was confusing to understand the download command because there was an extra 'download' mentioned

* Prioritization (#815)

* removed commented out code

* Added attack tracks information to prioritization algorithm

* bump opa-utils

* go mod tidy

* go mod tidy

* CR changes

* Issue 613 cluster name (#783)

* added --clusterName flag (#613)

Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>

* update flag name to --cluster-name

Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>

Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>

* Per 307 fail on severity counters (#831)

* feat: fail on exceeding severity thresholds (#830)

- Add support for severity counters
- Add support for CLI flags that set severity thresholds
- Terminate Kubescape with an exit code 1 if scan results exceed the
  severity thresholds

* Update opa-utils pkg version

Co-authored-by: Vlad Klokun <vladklokun@users.noreply.github.com>

* Fix merge conflict

* typo in .gitignore file (#833)

* remove unsupported installation method

* fixed welcome message

* fixed merge

* fixed attack tracks loading logic

Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>
Co-authored-by: Moshe-Rappaport-CA <moshep@armosec.io>
Co-authored-by: Moshe Rappaport <89577611+Moshe-Rappaport-CA@users.noreply.github.com>
Co-authored-by: Om Raut <33827410+om2137@users.noreply.github.com>
Co-authored-by: Kamal Nayan <95926324+legendarykamal@users.noreply.github.com>
Co-authored-by: Vlad Klokun <vladklokun@users.noreply.github.com>
Co-authored-by: Chirag Arora <84070677+Chirag8023@users.noreply.github.com>
Co-authored-by: shm12 <shmuelb@armosec.io>
Co-authored-by: Amir Malka <amirm@armosec.io>
Co-authored-by: Krishna Agarwal <dmkrishna.agarwal@gmail.com>
Co-authored-by: Saswata Senapati <74651639+saswat16@users.noreply.github.com>
Co-authored-by: Rahul Singh <110548934+rahuldhirendersingh@users.noreply.github.com>
Co-authored-by: deepuyadav004 <deepuyadavze@gmail.com>
Co-authored-by: kartik <97971066+kartikgajjar7@users.noreply.github.com>
Co-authored-by: Rounak-28 <95576871+Rounak-28@users.noreply.github.com>
Co-authored-by: pwnb0y <vickykr07@yahoo.com>
Co-authored-by: Ben Hirschberg <59160382+slashben@users.noreply.github.com>
Co-authored-by: Saptarshi Sarkar <saptarshi.programmer@gmail.com>
Co-authored-by: Rahul Surwade <93492791+RahulSurwade08@users.noreply.github.com>
Co-authored-by: Suhas Gumma <43647369+suhasgumma@users.noreply.github.com>
Co-authored-by: TarangVerma <90996971+TarangVerma@users.noreply.github.com>
Co-authored-by: avikittu <65793296+avikittu@users.noreply.github.com>
Co-authored-by: satyam kale <satyamkale271@gmail.com>
Co-authored-by: Aditya Pratap Singh <adityapratapsingh51@gmail.com>
Co-authored-by: Ashray Shetty <ashrayshetty1999@gmail.com>
Co-authored-by: Anubhav Gupta <mail.anubhav06@gmail.com>
Co-authored-by: Meyazhagan <meyazhagan.ofcl@gmail.com>
2022-09-19 08:41:40 +03:00
Cheithanya
12f81353e0 Made the required changes 2022-09-19 00:30:26 +05:30
Amir Malka
fd33a8acd1 fixed attack tracks loading logic 2022-09-18 17:48:13 +03:00
David Wertenteil
374e268a4f fixed merge 2022-09-18 17:26:29 +03:00
David Wertenteil
405bfbf9ba Merge branch 'dev' of https://github.com/kubescape/kubescape into dev 2022-09-18 16:59:22 +03:00
David Wertenteil
304227200f fixed welcome message 2022-09-18 16:59:08 +03:00
David Wertenteil
dc10125380 Merge branch 'master' into dev 2022-09-18 15:48:48 +03:00
David Wertenteil
51c417ebc3 Merge branch 'master' of https://github.com/kubescape/kubescape 2022-09-18 15:37:52 +03:00
David Wertenteil
862230f58a Merge branch 'dev' of https://github.com/kubescape/kubescape into dev 2022-09-18 13:44:52 +03:00
David Wertenteil
6416fc56d7 remove unsupported installation method 2022-09-18 13:44:45 +03:00
Rutvikk :D
a8ad8e5f5a Update README.md (#827) 2022-09-18 13:33:07 +03:00
Meyazhagan
d5edf29554 typo in .gitignore file (#833) 2022-09-18 13:32:17 +03:00
David Wertenteil
4351099e79 Fix merge conflict 2022-09-18 11:41:28 +03:00
David Wertenteil
196d07edc6 Per 307 fail on severity counters (#831)
* feat: fail on exceeding severity thresholds (#830)

- Add support for severity counters
- Add support for CLI flags that set severity thresholds
- Terminate Kubescape with an exit code 1 if scan results exceed the
  severity thresholds

* Update opa-utils pkg version

Co-authored-by: Vlad Klokun <vladklokun@users.noreply.github.com>
2022-09-18 10:34:34 +03:00
Anubhav Gupta
f4bb03039a Issue 613 cluster name (#783)
* added --clusterName flag (#613)

Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>

* update flag name to --cluster-name

Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>

Signed-off-by: Anubhav Gupta <mail.anubhav06@gmail.com>
2022-09-18 10:27:52 +03:00
Avinesh Tripathi
d6427f0fc8 Merge branch 'dev' into feature 2022-09-16 10:34:51 +05:30
Avinesh Tripathi
f33a6d7634 added adaptor 2022-09-16 10:20:44 +05:30
Amir Malka
2b931fb3f0 Prioritization (#815)
* removed commented out code

* Added attack tracks information to prioritization algorithm

* bump opa-utils

* go mod tidy

* go mod tidy

* CR changes
2022-09-15 16:16:54 +03:00
Arvindh
bb586892ba Fixed typos on multiple files (#808) 2022-09-15 15:53:32 +03:00
Ashray Shetty
cb18f60f82 removed the extra 'download' word from the example (#810)
it was confusing to understand the download command because there was an extra 'download' mentioned
2022-09-15 15:48:11 +03:00
Moshe Rappaport
e5023943e5 Print line separator only if some controls failed (#813) 2022-09-15 15:47:39 +03:00
Amir Malka
c565dc5af7 Merge pull request #816 from dwertent/master
Fix cluster name adoption
2022-09-15 15:40:14 +03:00
David Wertenteil
5634903aa0 update AdoptClusterName 2022-09-15 14:53:55 +03:00
David Wertenteil
ce81a9cb22 Merge remote-tracking branch 'armosec/dev' 2022-09-15 14:14:27 +03:00
Moshe-Rappaport-CA
5a01a1a30a Print line separator only if some controls failed 2022-09-14 19:22:08 +03:00
Moshe Rappaport
cb704cb1e7 Edit Junit output (#802)
* Edit Junit output

* Update go sum

* Following review
2022-09-14 08:59:39 +03:00
Jatin Agarwal
6e2dda7993 Fixed typos in roadmap.md (#800) 2022-09-13 10:39:28 +03:00
itsCheithanya
15e1d6d1a2 Update README.md (#804)
* Update README.md

* Create kubeconfig

* Delete kubeconfig

* Create kubeconfig

* Rename kubeconfig to kubeconfig.go

* Delete kubeconfig.go

* Update root.go

* Update rootinfo.go

* Added the update command for kubescape

* auto pick the OS of the usersystem

* added the html format desc

* a branch without update command added

* rolled back to prev

* added the html format documentation
2022-09-13 10:23:04 +03:00
Cheithanya
ba588b9eef Added the comments for update command 2022-09-12 21:28:55 +05:30
Cheithanya
f48b848eb6 auto pick the OS of the usersystem 2022-09-11 23:48:14 +05:30
Cheithanya
f81fd74aa3 Added the update command for kubescape 2022-09-11 18:22:18 +05:30
itsCheithanya
ad608b08e0 Merge branch 'kubescape:master' into master 2022-09-07 14:24:55 +05:30
itsCheithanya
f9e80b709a Update rootinfo.go 2022-09-07 13:42:35 +05:30
itsCheithanya
f75b62e62c Update root.go 2022-09-07 13:39:29 +05:30
itsCheithanya
1c24a55d4b Delete kubeconfig.go 2022-09-07 13:37:32 +05:30
Vicky Aryan
43dbb55d50 fixed some typo mistakes (#786) 2022-09-07 11:01:21 +03:00
itsCheithanya
03418299b8 Rename kubeconfig to kubeconfig.go 2022-09-06 18:32:27 +05:30
itsCheithanya
f5bd86593c Create kubeconfig 2022-09-06 18:32:00 +05:30
itsCheithanya
2af78eaab2 Delete kubeconfig 2022-09-06 18:30:16 +05:30
itsCheithanya
67cd003afe Create kubeconfig 2022-09-06 18:18:08 +05:30
Krishna Agarwal
f7f11abfc2 fixed typos (#777)
* fixed typos

* Update container-image-vulnerability-adaptor.md
2022-09-06 09:41:18 +03:00
TarangVerma
52aa5f02e2 Fixed typos in contribute.md (#779)
Fixed some spellings as well as grammar so that reader gets clear and concise view of the text.
2022-09-06 09:40:47 +03:00
itsCheithanya
ce8175be61 Update README.md (#782) 2022-09-06 09:39:25 +03:00
itsCheithanya
0bc542f851 Update README.md 2022-09-05 22:23:40 +05:30
Ben Hirschberg
8e4f88ce5b Publishing community meetings (#781) 2022-09-05 19:41:19 +03:00
David Wertenteil
362ea83549 Merge branch 'dev' of https://github.com/kubescape/kubescape into dev 2022-09-05 11:50:40 +03:00
David Wertenteil
4cb7b999ad Merge branch 'master' into dev 2022-09-05 11:49:05 +03:00
Amir Malka
81482b7421 commened out prioritization logic 2022-09-05 11:46:59 +03:00
David Wertenteil
2b7807f300 Merge branch 'master' of https://github.com/kubescape/kubescape 2022-09-05 11:46:21 +03:00
David Wertenteil
ef23d022ee Merge remote-tracking branch 'armosec/dev' 2022-09-05 11:44:53 +03:00
David Wertenteil
02d7fdc4f9 Remove windows extension build (#775)
* update logger version

* update scanner image

* remove windows exe extension
2022-09-05 11:37:56 +03:00
David Wertenteil
ccb3351607 remove windows exe extension 2022-09-05 11:35:08 +03:00
David Wertenteil
bba70b4c46 Update host scanner image (#774)
* update logger version

* update scanner image
2022-09-05 10:15:56 +03:00
David Wertenteil
46073e0a6c update scanner image 2022-09-05 10:12:28 +03:00
David Wertenteil
93a44f494d Merge remote-tracking branch 'armosec/dev' 2022-09-05 09:33:59 +03:00
Aditya Pratap Singh
5c96f877ed Option to force enable color output (closes #560) (#767)
* Option to force enable color output (closes #560)

(cherry picked from commit 4f951781ee8dd6bb451ac7d159787f47e4b07379)

* Update go.mod
2022-09-05 09:03:38 +03:00
Amir Malka
23ea7e0511 bump opa-utils to 181 2022-09-04 15:32:42 +03:00
Suhas Gumma
137b3d7b5d Fixed: Kubescape fails to authenticate remote private Github repo (#721)
* grammar error fixer in CONTRIBUTING.md

* scanning private git repository is available

* giturl to gitapi

* NO TOKEN error functionality added

* Used GetToken method of giturl.IGitAPPI for auth

Co-authored-by: satyam kale <satyamkale271@gmail.com>
Co-authored-by: Ben Hirschberg <59160382+slashben@users.noreply.github.com>
2022-09-04 15:17:15 +03:00
David Wertenteil
13ffd92210 update logger version (#773) 2022-09-04 15:13:16 +03:00
David Wertenteil
4725f8b3ca update logger version 2022-09-04 15:02:04 +03:00
David Wertenteil
6d65a90de9 Merging typo fixes from master (#772)
* greetings

* Update aws.sh

simplified the comment

* typo: In the title and h1 element

Their was a typo in index.html file.

* punctuation changes

* docs : added gitpod badge in readme.md

* fixed typos

* ƒ some grammar mistake is corrected inPULL_REQUEST_TEMPLATE.md file

* Updated README.md file

Added link to CONTRIBUTING.md file in a line in README.

* Added link to code of conduct file

I have added link to the code of conduct file and fixed some problems in the Readme file.

* Fixed readme

* Added alpine tag

Adding alpine tag instead of latest and removing repeating commands

* roadmap.md file is modified

* Automatically Close "Typo" labelled Issue

* build.py is modified

* modified PR template

* Fixed some typos in feature_request.md

"." at the end of the headings were missing and all the text were in same line.
Now this gives a clear and concise view of the texts.

* fixed the typo in docs/index.html

Found and fixed typo in the 'alt' attribute of img tag

* Update PULL_REQUEST_TEMPLATE.md

Co-authored-by: Krishna Agarwal <dmkrishna.agarwal@gmail.com>
Co-authored-by: Saswata Senapati <74651639+saswat16@users.noreply.github.com>
Co-authored-by: Rahul Singh <110548934+rahuldhirendersingh@users.noreply.github.com>
Co-authored-by: deepuyadav004 <deepuyadavze@gmail.com>
Co-authored-by: kartik <97971066+kartikgajjar7@users.noreply.github.com>
Co-authored-by: Rounak-28 <95576871+Rounak-28@users.noreply.github.com>
Co-authored-by: pwnb0y <vickykr07@yahoo.com>
Co-authored-by: Ben Hirschberg <59160382+slashben@users.noreply.github.com>
Co-authored-by: Saptarshi Sarkar <saptarshi.programmer@gmail.com>
Co-authored-by: Rahul Surwade <93492791+RahulSurwade08@users.noreply.github.com>
Co-authored-by: Suhas Gumma <43647369+suhasgumma@users.noreply.github.com>
Co-authored-by: Kamal Nayan <95926324+legendarykamal@users.noreply.github.com>
Co-authored-by: TarangVerma <90996971+TarangVerma@users.noreply.github.com>
Co-authored-by: avikittu <65793296+avikittu@users.noreply.github.com>
2022-09-04 14:50:22 +03:00
David Wertenteil
faf928527d Merge pull request #770 from amirmalka/dev
Resources Prioritization
2022-09-04 14:17:19 +03:00
Amir Malka
18c6e80c3c Added resource prioritization information, raw resource will be sent on the result object 2022-09-04 13:38:48 +03:00
David Wertenteil
b103e817ed Merge pull request #745 from Moshe-Rappaport-CA/dev
Print chart name log when fail to generate
2022-09-04 12:59:30 +03:00
Moshe Rappaport
2db2f55d16 Change formatting to %s 2022-09-01 14:28:18 +03:00
David Wertenteil
b38ce5e812 Merge pull request #734 from Chirag8023/patch-3
Patch 3
2022-09-01 08:44:26 +03:00
David Wertenteil
d03806aea2 Merge pull request #738 from shm12/dev
Adding fixed control input
2022-09-01 08:32:26 +03:00
Moshe-Rappaport-CA
fc3912ca7d Print chart name log when fail to generate 2022-08-31 15:26:24 +03:00
shm12
a76228c1e1 update go.mod 2022-08-30 11:39:54 +03:00
shm12
9447f2933a Added fixed control input 2022-08-30 11:33:19 +03:00
David Wertenteil
26d4664cc5 Merge pull request #694 from Moshe-Rappaport-CA/support-scanning-several-files
Support scanning several files
2022-08-30 10:01:44 +03:00
Chirag Arora
acdad028a3 Update completion.go 2022-08-30 00:59:00 +05:30
Moshe Rappaport
de78615038 Update core/pkg/resourcehandler/filesloader.go
Co-authored-by: Vlad Klokun <vladklokun@users.noreply.github.com>
2022-08-29 18:10:21 +03:00
Moshe-Rappaport-CA
9c764c90e3 Fix scanning ListObj following reviews 2022-08-29 10:29:25 +03:00
Moshe-Rappaport-CA
95a4c19dc6 Handle with issues caused by updating opa-utils 2022-08-29 10:17:31 +03:00
Moshe-Rappaport-CA
e3352f90e1 Merge remote-tracking branch 'armo/dev' into support-scanning-several-files 2022-08-28 12:03:09 +03:00
Moshe-Rappaport-CA
677a9da80a Fix issues according to review 2022-08-28 12:02:21 +03:00
Vlad Klokun
9a7eb4b9a5 Merge pull request #690 from legendarykamal/master
docs: center the icon, reword starring call to action
2022-08-26 14:47:24 +03:00
Vlad Klokun
903b5f39df docs(readme): Star → star 2022-08-25 23:00:10 +03:00
Kamal Nayan
55f0ca3e9e gramatical improvements 2022-08-26 00:57:56 +05:30
David Wertenteil
3387e677ba Merge pull request #636 from Moshe-Rappaport-CA/dev
Fix scanning list obj
2022-08-25 17:33:38 +03:00
Moshe-Rappaport-CA
5774acfc81 Merge remote-tracking branch 'armo/dev' into support-scanning-several-files 2022-08-25 15:25:58 +03:00
Moshe-Rappaport-CA
0eee2d1d0a Support scanning several files 2022-08-25 15:20:26 +03:00
Kamal Nayan
aade1008c4 improved icon of kubescape in readme 2022-08-25 14:54:03 +05:30
David Wertenteil
80adf03926 Merge branch 'master' into dev 2022-08-24 17:23:28 +03:00
David Wertenteil
4b9c35d53b Merge branch 'master' of https://github.com/kubescape/kubescape 2022-08-24 17:23:10 +03:00
David Wertenteil
b936c3f857 Merge branch 'master' into dev 2022-08-24 17:11:38 +03:00
David Wertenteil
600b9a6fb0 Merge branch 'master' of https://github.com/kubescape/kubescape 2022-08-24 17:11:13 +03:00
David Wertenteil
3bec2ef0b7 override infoMap only if it's not nil 2022-08-24 17:10:18 +03:00
David Wertenteil
609cbff2da Merge pull request #655 from om2137/om2
Broken links fix in roadmap.md
2022-08-24 15:29:50 +03:00
Om Raut
a03b0c94c4 Broken links fix in roadmap.md
Planning, backlog, and wishlist links were not taking to the required section.
2022-08-24 02:18:56 +05:30
Moshe-Rappaport-CA
589d0545cb Merge branch 'dev' of github.com:Moshe-Rappaport-CA/kubescape into dev 2022-08-23 17:41:01 +03:00
Moshe-Rappaport-CA
32b74608bf Merge remote-tracking branch 'armo/dev' into dev 2022-08-23 17:02:15 +03:00
Moshe-Rappaport-CA
9454924b9f Merge remote-tracking branch 'armo/dev' into dev 2022-08-23 16:11:07 +03:00
Moshe Rappaport
7233f00c32 Merge branch 'dev' into dev 2022-08-23 15:51:57 +03:00
Moshe-Rappaport-CA
905db42625 Fix go mod in httphandler pkg 2022-08-23 15:49:06 +03:00
Moshe-Rappaport-CA
05d5ad47f2 Merge remote-tracking branch 'armo/dev' into dev 2022-08-23 14:42:38 +03:00
Moshe-Rappaport-CA
5ccb858d7f Fix issue for scanning list obj 2022-08-23 14:42:12 +03:00
167 changed files with 5704 additions and 3021 deletions

54
.github/workflows/01-golang-lint.yaml vendored Normal file
View File

@@ -0,0 +1,54 @@
name: golangci-lint
on:
push:
branches:
- dev
pull_request:
types: [ edited, opened, synchronize, reopened ]
branches: [ master, dev ]
paths-ignore:
- '**.yaml'
- '**.md'
permissions:
contents: read
# Optional: allow read access to pull request. Use with `only-new-issues` option.
pull-requests: read
jobs:
golangci:
name: lint
runs-on: ubuntu-20.04
steps:
- uses: actions/setup-go@v3
with:
go-version: 1.18
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Install libgit2
run: make libgit2
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
version: latest
# Optional: working directory, useful for monorepos
# working-directory: somedir
# Optional: golangci-lint command line arguments.
# args: --issues-exit-code=0
args: --timeout 10m --build-tags=static
#--new-from-rev dev
# Optional: show only new issues if it's a pull request. The default value is `false`.
only-new-issues: true
# Optional: if set to true then the all caching functionality will be complete disabled,
# takes precedence over all other caching options.
# skip-cache: true
# Optional: if set to true then the action don't cache or restore ~/go/pkg.
# skip-pkg-cache: true
# Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
# skip-build-cache: true

89
.github/workflows/build-image.yaml vendored Normal file
View File

@@ -0,0 +1,89 @@
name: build
on:
workflow_call:
inputs:
client:
description: 'client name'
required: true
type: string
image_tag:
description: 'image tag'
required: true
type: string
image_name:
description: 'image registry and name'
required: true
type: string
cosign:
required: false
default: false
type: boolean
description: 'run cosign on released image'
support_platforms:
required: false
default: true
type: boolean
description: 'support amd64/arm64'
jobs:
check-secret:
name: check if QUAYIO_REGISTRY_USERNAME & QUAYIO_REGISTRY_PASSWORD is set in github secrets
runs-on: ubuntu-latest
outputs:
is-secret-set: ${{ steps.check-secret-set.outputs.is-secret-set }}
steps:
- name: Check whether unity activation requests should be done
id: check-secret-set
env:
QUAYIO_REGISTRY_USERNAME: ${{ secrets.QUAYIO_REGISTRY_USERNAME }}
QUAYIO_REGISTRY_PASSWORD: ${{ secrets.QUAYIO_REGISTRY_PASSWORD }}
run: |
echo "is-secret-set=${{ env.QUAYIO_REGISTRY_USERNAME != '' && env.QUAYIO_REGISTRY_PASSWORD != '' }}" >> $GITHUB_OUTPUT
build-image:
needs: [check-secret]
if: needs.check-secret.outputs.is-secret-set == 'true'
name: Build image and upload to registry
runs-on: ubuntu-latest
permissions:
id-token: write
packages: write
contents: read
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Quay.io
env:
QUAY_PASSWORD: ${{ secrets.QUAYIO_REGISTRY_PASSWORD }}
QUAY_USERNAME: ${{ secrets.QUAYIO_REGISTRY_USERNAME }}
run: docker login -u="${QUAY_USERNAME}" -p="${QUAY_PASSWORD}" quay.io
- name: Build and push image
if: ${{ inputs.support_platforms }}
run: docker buildx build . --file build/Dockerfile --tag ${{ inputs.image_name }}:${{ inputs.image_tag }} --tag ${{ inputs.image_name }}:latest --build-arg image_version=${{ inputs.image_tag }} --build-arg client=${{ inputs.client }} --push --platform linux/amd64,linux/arm64
- name: Build and push image without amd64/arm64 support
if: ${{ !inputs.support_platforms }}
run: docker buildx build . --file build/Dockerfile --tag ${{ inputs.image_name }}:${{ inputs.image_tag }} --tag ${{ inputs.image_name }}:latest --build-arg image_version=${{ inputs.image_tag }} --build-arg client=${{ inputs.client }} --push
- name: Install cosign
uses: sigstore/cosign-installer@main
with:
cosign-release: 'v1.12.0'
- name: sign kubescape container image
if: ${{ inputs.cosign }}
env:
COSIGN_EXPERIMENTAL: "true"
run: |
cosign sign --force ${{ inputs.image_name }}

View File

@@ -4,76 +4,40 @@ on:
push:
branches: [ master ]
paths-ignore:
# Do not run the pipeline if only Markdown files changed
- '**.md'
jobs:
once:
name: Create release
runs-on: ubuntu-latest
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
- name: Create a release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v2.0.${{ github.run_number }}
release_name: Release v2.0.${{ github.run_number }}
draft: false
prerelease: false
build:
name: Create cross-platform release build, tag and upload binaries
needs: once
test:
uses: ./.github/workflows/test.yaml
with:
release: "v2.0.${{ github.run_number }}"
client: test
create-release:
uses: ./.github/workflows/release.yaml
needs: test
with:
release_name: "Release v2.0.${{ github.run_number }}"
tag_name: "v2.0.${{ github.run_number }}"
secrets: inherit
publish-artifacts:
name: Build and publish artifacts
needs: create-release
runs-on: ${{ matrix.os }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
os: [ubuntu-20.04, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Cache Go modules (Linux)
if: matrix.os == 'ubuntu-latest'
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (macOS)
if: matrix.os == 'macos-latest'
uses: actions/cache@v3
with:
path: |
~/Library/Caches/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (Windows)
if: matrix.os == 'windows-latest'
uses: actions/cache@v3
with:
path: |
~\AppData\Local\go-build
~\go\pkg\mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18
# - name: Test cmd pkg
# run: cd cmd && go test -v ./...
go-version: 1.19
- name: Install MSYS2 & libgit2 (Windows)
shell: cmd
@@ -84,102 +48,68 @@ jobs:
run: make libgit2
if: matrix.os != 'windows-latest'
- name: Test core pkg
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: go test -tags=static -v ./...
- name: Test httphandler pkg
run: cd httphandler && go test -tags=static -v ./...
- name: Build
env:
RELEASE: v2.0.${{ github.run_number }}
RELEASE: v2.0.${{ github.run_number }}
CLIENT: release
CGO_ENABLED: 1
run: python3 --version && python3 build.py
- name: Smoke Testing
env:
RELEASE: v2.0.${{ github.run_number }}
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape
- name: Upload release binaries
id: upload-release-asset
- name: Upload release binaries (Windows / MacOS)
id: upload-release-asset-win-macos
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.once.outputs.upload_url }}
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: build/${{ matrix.os }}/kubescape
asset_name: kubescape-${{ matrix.os }}
asset_content_type: application/octet-stream
if: matrix.os != 'ubuntu-20.04'
- name: Upload release hash
id: upload-release-hash
- name: Upload release binaries (Linux)
id: upload-release-asset-linux
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.once.outputs.upload_url }}
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: build/ubuntu-latest/kubescape
asset_name: kubescape-ubuntu-latest
asset_content_type: application/octet-stream
if: matrix.os == 'ubuntu-20.04'
- name: Upload release hash (Windows / MacOS)
id: upload-release-hash-win-macos
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
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
runs-on: ubuntu-latest
if: ${{ github.repository == 'kubescape/kubescape' }} # TODO
permissions:
id-token: write
packages: write
contents: read
if: matrix.os != 'ubuntu-20.04'
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Set image version
id: image-version
run: echo '::set-output name=IMAGE_VERSION::v2.0.${{ github.run_number }}'
- name: Set image name
id: image-name
run: echo '::set-output name=IMAGE_NAME::quay.io/${{ github.repository_owner }}/kubescape'
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Quay.io
- name: Upload release hash (Linux)
id: upload-release-hash-linux
uses: actions/upload-release-asset@v1
env:
QUAY_PASSWORD: ${{ secrets.QUAYIO_REGISTRY_PASSWORD }}
QUAY_USERNAME: ${{ secrets.QUAYIO_REGISTRY_USERNAME }}
run: docker login -u="${QUAY_USERNAME}" -p="${QUAY_PASSWORD}" quay.io
- name: Build the Docker image
run: docker buildx build . --file build/Dockerfile --tag ${{ steps.image-name.outputs.IMAGE_NAME }}:${{ steps.image-version.outputs.IMAGE_VERSION }} --tag ${{ steps.image-name.outputs.IMAGE_NAME }}:latest --build-arg image_version=${{ steps.image-version.outputs.IMAGE_VERSION }} --build-arg client=image-release --push --platform linux/amd64,linux/arm64
# - name: Login to GitHub Container Registry
# uses: docker/login-action@v1
# with:
# registry: ghcr.io
# username: ${{ github.actor }}
# password: ${{ secrets.GITHUB_TOKEN }}
# TODO - Wait for casign to support fixed tags -> https://github.com/sigstore/cosign/issues/1424
# - name: Install cosign
# uses: sigstore/cosign-installer@main
# with:
# cosign-release: 'v1.5.1' # optional
# - name: sign kubescape container image
# env:
# COSIGN_EXPERIMENTAL: "true"
# run: |
# cosign sign --force ${{ steps.image-name.outputs.IMAGE_NAME }}:latest
# cosign sign --force ${{ steps.image-name.outputs.IMAGE_NAME }}:${{ steps.image-version.outputs.IMAGE_VERSION }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.create-release.outputs.upload_url }}
asset_path: build/ubuntu-latest/kubescape.sha256
asset_name: kubescape-ubuntu-latest-sha256
asset_content_type: application/octet-stream
if: matrix.os == 'ubuntu-20.04'
publish-image:
uses: ./.github/workflows/build-image.yaml
needs: create-release
with:
client: "image-release"
image_name: "quay.io/${{ github.repository_owner }}/kubescape"
image_tag: "v2.0.${{ github.run_number }}"
support_platforms: true
cosign: true
secrets: inherit

View File

@@ -7,136 +7,19 @@ on:
# Do not run the pipeline if only Markdown files changed
- '**.md'
jobs:
build:
name: Create cross-platform dev build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Cache Go modules (Linux)
if: matrix.os == 'ubuntu-latest'
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (macOS)
if: matrix.os == 'macos-latest'
uses: actions/cache@v3
with:
path: |
~/Library/Caches/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (Windows)
if: matrix.os == 'windows-latest'
uses: actions/cache@v3
with:
path: |
~\AppData\Local\go-build
~\go\pkg\mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18
# - 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: Install MSYS2 & libgit2 (Windows)
shell: cmd
run: .\build.bat all
if: matrix.os == 'windows-latest'
- name: Install libgit2 (Linux/macOS)
run: make libgit2
if: matrix.os != 'windows-latest'
- name: Test core pkg
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: go test -tags=static -v ./...
- name: Test httphandler pkg
run: cd httphandler && go test -tags=static -v ./...
- name: Build
env:
RELEASE: v2.0.${{ github.run_number }}
CLIENT: release-dev
CGO_ENABLED: 1
run: python3 --version && python3 build.py
- name: Smoke Testing
env:
RELEASE: v2.0.${{ github.run_number }}
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape
- name: Upload build artifacts
uses: actions/upload-artifact@v2
with:
name: kubescape-${{ matrix.os }}
path: build/${{ matrix.os }}/kubescape
build-docker:
name: Build docker container, tag and upload to registry
needs: build
if: ${{ github.repository == 'kubescape/kubescape' }} # TODO
runs-on: ubuntu-latest
permissions:
id-token: write
packages: write
contents: read
steps:
- uses: actions/checkout@v2
with:
submodules: recursive
- name: Set image version
id: image-version
run: echo '::set-output name=IMAGE_VERSION::dev-v2.0.${{ github.run_number }}'
- name: Set image name
id: image-name
run: echo '::set-output name=IMAGE_NAME::quay.io/${{ github.repository_owner }}/kubescape'
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Quay.io
env:
QUAY_PASSWORD: ${{ secrets.QUAYIO_REGISTRY_PASSWORD }}
QUAY_USERNAME: ${{ secrets.QUAYIO_REGISTRY_USERNAME }}
run: docker login -u="${QUAY_USERNAME}" -p="${QUAY_PASSWORD}" quay.io
- name: Build the Docker image
run: docker buildx build . --file build/Dockerfile --tag ${{ steps.image-name.outputs.IMAGE_NAME }}:${{ steps.image-version.outputs.IMAGE_VERSION }} --build-arg image_version=${{ steps.image-version.outputs.IMAGE_VERSION }} --build-arg client=image-dev --push --platform linux/amd64,linux/arm64
test:
uses: ./.github/workflows/test.yaml
with:
release: "v2.0.${{ github.run_number }}"
client: test
# publish-dev-image:
# uses: ./.github/workflows/build-image.yaml
# needs: test
# with:
# client: "image-dev"
# image_name: "quay.io/${{ github.repository_owner }}/kubescape"
# image_tag: "dev-v2.0.${{ github.run_number }}"
# support_platforms: true
# cosign: true
# secrets: inherit

View File

@@ -17,6 +17,6 @@ jobs:
- uses: EddieHubCommunity/gh-action-community/src/welcome@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
issue-message: '<h3>Hey, Welcome to this repo, Congratulations on opening your issue. Keep Contributing to Kubescape</h3>'
pr-message: '<h3>Hey, Welcome to this repo, Congratulations on opening your Pull Request. Keep Contributing to Kubescape</h3>'
footer: '<h4>We''ll try to review and add you work as soon as possible and a maintainer will get back to you soon!</h4>'
issue-message: '<h3>Hi! Welcome to Kubescape. Thank you for taking the time and reporting an issue</h3>'
pr-message: '<h3>Hi! Welcome to Kubescape. Thank you for taking the time and contributing to the open source community</h3>'
footer: '<h4>We will try to review as soon as possible!</h4>'

View File

@@ -6,87 +6,11 @@ on:
types: [ edited, opened, synchronize, reopened ]
paths-ignore:
# Do not run the pipeline if only Markdown files changed
- '**.yaml'
- '**.md'
jobs:
build:
name: Create cross-platform build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Cache Go modules (Linux)
if: matrix.os == 'ubuntu-latest'
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (macOS)
if: matrix.os == 'macos-latest'
uses: actions/cache@v3
with:
path: |
~/Library/Caches/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (Windows)
if: matrix.os == 'windows-latest'
uses: actions/cache@v3
with:
path: |
~\AppData\Local\go-build
~\go\pkg\mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18
- name: Install MSYS2 & libgit2 (Windows)
shell: cmd
run: .\build.bat all
if: matrix.os == 'windows-latest'
- name: Install libgit2 (Linux/macOS)
run: make libgit2
if: matrix.os != 'windows-latest'
# - name: Test cmd pkg
# run: cd cmd && go test -v ./...
- name: Test core pkg
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: go test -tags=static -v ./...
- name: Test httphandler pkg
run: cd httphandler && go test -tags=static -v ./...
- name: Build
env:
RELEASE: v2.0.${{ github.run_number }}
CLIENT: test
CGO_ENABLED: 1
run: python3 --version && python3 build.py
- name: Smoke Testing
env:
RELEASE: v2.0.${{ github.run_number }}
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape
test:
uses: ./.github/workflows/test.yaml
with:
release: "v2.0.${{ github.run_number }}"
client: test

41
.github/workflows/release.yaml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: build
on:
workflow_call:
inputs:
release_name:
description: 'release'
required: true
type: string
tag_name:
description: 'tag'
required: true
type: string
draft:
description: 'create draft release'
required: false
type: boolean
default: false
outputs:
upload_url:
description: "The first output string"
value: ${{ jobs.release.outputs.upload_url }}
jobs:
release:
name: Create release
runs-on: ubuntu-latest
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
- name: Create a release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ inputs.tag_name }}
release_name: ${{ inputs.release_name }}
draft: ${{ inputs.draft }}
prerelease: false

100
.github/workflows/test.yaml vendored Normal file
View File

@@ -0,0 +1,100 @@
name: test
on:
workflow_call:
inputs:
release:
description: 'release'
required: true
type: string
client:
description: 'Client name'
required: true
type: string
jobs:
build:
name: Create cross-platform build
runs-on: ${{ matrix.os }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
strategy:
matrix:
os: [ubuntu-20.04, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Cache Go modules (Linux)
if: matrix.os == 'ubuntu-20.04'
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (macOS)
if: matrix.os == 'macos-latest'
uses: actions/cache@v3
with:
path: |
~/Library/Caches/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (Windows)
if: matrix.os == 'windows-latest'
uses: actions/cache@v3
with:
path: |
~\AppData\Local\go-build
~\go\pkg\mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19
- name: Install MSYS2 & libgit2 (Windows)
shell: cmd
run: .\build.bat all
if: matrix.os == 'windows-latest'
- name: Install libgit2 (Linux/macOS)
run: make libgit2
if: matrix.os != 'windows-latest'
- name: Test core pkg
run: go test -tags=static -v ./...
- name: Test httphandler pkg
run: cd httphandler && go test -tags=static -v ./...
- name: Build
env:
RELEASE: ${{ inputs.release }}
CLIENT: test
CGO_ENABLED: 1
run: python3 --version && python3 build.py
- name: Smoke Testing (Windows / MacOS)
env:
RELEASE: ${{ inputs.release }}
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape
if: matrix.os != 'ubuntu-20.04'
- name: Smoke Testing (Linux)
env:
RELEASE: ${{ inputs.release }}
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
run: python3 smoke_testing/init.py ${PWD}/build/ubuntu-latest/kubescape
if: matrix.os == 'ubuntu-20.04'

5
.gitignore vendored
View File

@@ -1,8 +1,9 @@
*.vs*
*kubescape*
*debug*
*vender*
*vendor*
*.pyc*
.idea
.history
ca.srl
ca.srl
*.out

58
.golangci.yml Normal file
View File

@@ -0,0 +1,58 @@
linters-settings:
govet:
check-shadowing: true
dupl:
threshold: 200
goconst:
min-len: 3
min-occurrences: 2
gocognit:
min-complexity: 65
linters:
enable:
- gosec
- staticcheck
- nolintlint
disable:
# temporarily disabled
- varcheck
- ineffassign
- unused
- typecheck
- errcheck
- govet
- gosimple
- deadcode
- gofmt
- goimports
- bodyclose
- dupl
- gocognit
- gocritic
- goimports
- nakedret
- revive
- stylecheck
- unconvert
- unparam
#- forbidigo # <- see later
# should remain disabled
- maligned
- lll
- gochecknoinits
- gochecknoglobals
issues:
exclude-rules:
- linters:
- revive
text: "var-naming"
- linters:
- revive
text: "type name will be used as (.+?) by other packages, and that stutters"
- linters:
- stylecheck
text: "ST1003"
run:
skip-dirs:
- git2go

View File

@@ -1,127 +1,3 @@
# Contributor Covenant Code of Conduct
## Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement [here](mailto:ben@armosec.io).
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
The Kubescape project follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).

View File

@@ -1,10 +1,11 @@
# Maintainers
The following table lists Kubescape project maintainers
The following table lists the Kubescape project maintainers:
| Name | GitHub | Email | Organization | Role | Added/Renewed On |
| --- | --- | --- | --- | --- | --- |
| [Ben Hirschberg](https://www.linkedin.com/in/benyamin-ben-hirschberg-66141890) | [@slashben](https://github.com/slashben) | ben@armosec.io | [ARMO](https://www.armosec.io/) | VP R&D | 2021-09-01 |
| [Rotem Refael](https://www.linkedin.com/in/rotem-refael) | [@rotemamsa](https://github.com/rotemamsa) | rrefael@armosec.io | [ARMO](https://www.armosec.io/) | Team Leader | 2021-10-11 |
| [David Wertenteil](https://www.linkedin.com/in/david-wertenteil-0ba277b9) | [@dwertent](https://github.com/dwertent) | dwertent@armosec.io | [ARMO](https://www.armosec.io/) | Kubescape CLI Developer | 2021-09-01 |
| [Bezalel Brandwine](https://www.linkedin.com/in/bezalel-brandwine) | [@Bezbran](https://github.com/Bezbran) | bbrandwine@armosec.io | [ARMO](https://www.armosec.io/) | Kubescape SaaS Developer | 2021-09-01 |
| Name | GitHub | Organization | Added/Renewed On |
| --- | --- | --- | --- |
| [Ben Hirschberg](https://www.linkedin.com/in/benyamin-ben-hirschberg-66141890) | [@slashben](https://github.com/slashben) | [ARMO](https://www.armosec.io/) | 2021-09-01 |
| [Rotem Refael](https://www.linkedin.com/in/rotem-refael) | [@rotemamsa](https://github.com/rotemamsa) | [ARMO](https://www.armosec.io/) | 2021-10-11 |
| [David Wertenteil](https://www.linkedin.com/in/david-wertenteil-0ba277b9) | [@dwertent](https://github.com/dwertent) | [ARMO](https://www.armosec.io/) | 2021-09-01 |
| [Bezalel Brandwine](https://www.linkedin.com/in/bezalel-brandwine) | [@Bezbran](https://github.com/Bezbran) | [ARMO](https://www.armosec.io/) | 2021-09-01 |
| [Craig Box](https://www.linkedin.com/in/crbnz/) | [@craigbox](https://github.com/craigbox) | [ARMO](https://www.armosec.io/) | 2022-10-31 |

117
README.md
View File

@@ -11,11 +11,11 @@
:sunglasses: [Want to contribute?](#being-a-part-of-the-team) :innocent:
Kubescape is a K8s open-source tool providing a Kubernetes single pane of glass, including risk analysis, security compliance, RBAC visualizer, and image vulnerabilities scanning.
Kubescape scans K8s clusters, YAML files, and HELM charts, detecting misconfigurations according to multiple frameworks (such as the [NSA-CISA](https://www.armosec.io/blog/kubernetes-hardening-guidance-summary-by-armo/?utm_source=github&utm_medium=repository), [MITRE ATT&CK®](https://www.microsoft.com/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/)), software vulnerabilities, and RBAC (role-based-access-control) violations at early stages of the CI/CD pipeline, calculates risk score instantly and shows risk trends over time.
Kubescape is an open-source Kubernetes security platform. A single pane of glass access to view risk analysis, security compliance, RBAC visualization, and image vulnerability scanning.
Kubescape scans Kubernetes clusters, YAML files, and Helm charts. It detects misconfigurations according to multiple frameworks (such as [NSA-CISA](https://www.armosec.io/blog/kubernetes-hardening-guidance-summary-by-armo/?utm_source=github&utm_medium=repository), [MITRE ATT&CK®](https://www.microsoft.com/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/) and [CIS Benchmark](https://www.armosec.io/blog/cis-kubernetes-benchmark-framework-scanning-tools-comparison/?utm_source=github&utm_medium=repository)). Kubescape also helps you find software vulnerabilities, and RBAC (role-based-access-control) violations at early stages of the CI/CD pipeline. It calculates your risk score instantly and shows risk trends over time.
It has become one of the fastest-growing Kubernetes tools among developers due to its easy-to-use CLI interface, flexible output formats, and automated scanning capabilities, saving Kubernetes users and admins precious time, effort, and resources.
Kubescape integrates natively with other DevOps tools, including Jenkins, CircleCI, Github workflows, Prometheus, and Slack, and supports multi-cloud K8s deployments like EKS, GKE, and AKS.
Kubescape is one of the fastest-growing Kubernetes security tools among developers. It saves Kubernetes users and admins precious time, effort, and resources with its easy-to-use CLI interface, flexible output formats, and automated scanning capabilities.
Kubescape integrates natively with other DevOps tools, including Jenkins, CircleCI, Github workflows, Prometheus, and Slack. It supports multi-cloud Kubernetes deployments like EKS, GKE, and AKS.
</br>
@@ -30,17 +30,17 @@ Kubescape integrates natively with other DevOps tools, including Jenkins, Circle
curl -s https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh | /bin/bash
```
*OR:*
[Install on windows](#install-on-windows)
[Install on macOS](#install-on-macos)
[Install on NixOS or Linux/macOS via nix](#install-on-nixos-or-with-nix-community)
[Install using Go](#install-using-go)
## Run:
```sh
kubescape scan --submit --enable-host-scan --verbose
kubescape scan --enable-host-scan --verbose
```
<img src="docs/summary.png">
@@ -51,15 +51,35 @@ kubescape scan --submit --enable-host-scan --verbose
</br>
## Architecture in short
[Component architecture](docs/architecture.drawio.svg)
### [CLI](#kubescape-cli)
<div align="center">
<img src="docs/ks-cli-arch.png" width="300" alt="cli-diagram">
</div>
### [Operator](https://github.com/kubescape/helm-charts#readme)
<div align="center">
<img src="docs/ks-operator-arch.png" width="300" alt="operator-diagram">
</div>
### Please [star ⭐](https://github.com/kubescape/kubescape/stargazers) the repo if you want us to continue developing and improving Kubescape 😀
</br>
# Being a part of the team
We invite you to our team! We are excited about this project and want to return the love we get.
## Community
You are in vited to our community! We are excited about this project and want to return the love we get.
[Want to contribute?](https://github.com/kubescape/kubescape/blob/master/CONTRIBUTING.md) Want to discuss something? Have an issue? Please make sure that you follow our [Code Of Conduct](https://github.com/kubescape/kubescape/blob/master/CODE_OF_CONDUCT.md) .
We hold community meetings on [Zoom](https://us02web.zoom.us/j/84020231442) on the first Tuesday of every month at 14:00 GMT! :sunglasses:
Please make sure that you follow our [Code Of Conduct](https://github.com/kubescape/kubescape/blob/master/CODE_OF_CONDUCT.md).
## Contributions
Want to discuss something? Have an issue? [Want to contribute?](https://github.com/kubescape/kubescape/blob/master/CONTRIBUTING.md)
* Feel free to pick a task from the [issues](https://github.com/kubescape/kubescape/issues?q=is%3Aissue+is%3Aopen+label%3A%22open+for+contribution%22), [roadmap](docs/roadmap.md) or suggest a feature of your own. [Contact us](MAINTAINERS.md) directly for more information :)
* [Open an issue](https://github.com/kubescape/kubescape/issues/new/choose) , we are trying to respond within 48 hours
@@ -81,6 +101,7 @@ We invite you to our team! We are excited about this project and want to return
* [Overview](https://youtu.be/wdBkt_0Qhbg)
* [How To Secure Kubernetes Clusters With Kubescape And Armo](https://youtu.be/ZATGiDIDBQk)
* [Scan Kubernetes YAML files](https://youtu.be/Ox6DaR7_4ZI)
* [Scan container image registry](https://youtu.be/iQ_k8EnK-3s)
* [Scan Kubescape on an air-gapped environment (offline support)](https://youtu.be/IGXL9s37smM)
* [Managing exceptions in the Kubescape SaaS version](https://youtu.be/OzpvxGmCR80)
* [Configure and run customized frameworks](https://youtu.be/12Sanq_rEhs)
@@ -159,22 +180,22 @@ Or to your profile (not preferred): `nix-env --install -A nixpkgs.kubescape`
### Examples
#### Scan a running Kubernetes cluster and submit results to the [Kubescape SaaS version](https://cloud.armosec.io?utm_source=github&utm_medium=repository)
#### Scan a running Kubernetes cluster
```
kubescape scan --submit --enable-host-scan --verbose
kubescape scan --enable-host-scan --verbose
```
> Read [here](https://hub.armosec.io/docs/host-sensor?utm_source=github&utm_medium=repository) more about the `enable-host-scan` flag
#### Scan a running Kubernetes cluster with [`nsa`](https://www.nsa.gov/Press-Room/News-Highlights/Article/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/) framework and submit results to the [Kubescape SaaS version](https://cloud.armosec.io?utm_source=github&utm_medium=repository)
#### Scan a running Kubernetes cluster with [`nsa`](https://www.nsa.gov/Press-Room/News-Highlights/Article/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/) framework
```
kubescape scan framework nsa --submit
kubescape scan framework nsa
```
#### Scan a running Kubernetes cluster with [`MITRE ATT&CK®`](https://www.microsoft.com/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/) framework and submit results to the [Kubescape SaaS version](https://cloud.armosec.io?utm_source=github&utm_medium=repository)
#### Scan a running Kubernetes cluster with [`MITRE ATT&CK®`](https://www.microsoft.com/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/) framework
```
kubescape scan framework mitre --submit
kubescape scan framework mitre
```
@@ -183,6 +204,11 @@ kubescape scan framework mitre --submit
kubescape scan control "Privileged container"
```
#### Scan using an alternative kubeconfig file
```
kubescape scan --kubeconfig cluster.conf
```
#### Scan specific namespaces
```
kubescape scan --include-namespaces development,staging,production
@@ -193,14 +219,15 @@ kubescape scan --include-namespaces development,staging,production
kubescape scan --exclude-namespaces kube-system,kube-public
```
#### Scan local `yaml`/`json` files before deploying. [Take a look at the demonstration](https://youtu.be/Ox6DaR7_4ZI). Submit the results in case the directory is a git repo. [docs](https://hub.armosec.io/docs/repository-scanning?utm_source=github&utm_medium=repository)
#### Scan local `yaml`/`json` files before deploying. [Take a look at the demonstration](https://youtu.be/Ox6DaR7_4ZI).
```
kubescape scan *.yaml --submit
kubescape scan *.yaml
```
#### Scan Kubernetes manifest files from a git repository [and submit the results](https://hub.armosec.io/docs/repository-scanning?utm_source=github&utm_medium=repository)
#### Scan Kubernetes manifest files from a git repository
```
kubescape scan https://github.com/kubescape/kubescape --submit
kubescape scan https://github.com/kubescape/kubescape
```
#### Display all scanned resources (including the resources which passed)
@@ -233,7 +260,13 @@ kubescape scan --format pdf --output results.pdf
kubescape scan --format prometheus
```
#### Scan with exceptions, objects with exceptions will be presented as `exclude` and not `fail`
#### Output in `html` format
```
kubescape scan --format html --output results.html
```
#### Scan with exceptions. Objects with exceptions will be presented as `exclude` and not `fail`
[Full documentation](examples/exceptions/README.md)
```
kubescape scan --exceptions examples/exceptions/exclude-kube-namespaces.json
@@ -241,11 +274,17 @@ kubescape scan --exceptions examples/exceptions/exclude-kube-namespaces.json
#### Scan Helm charts
```
kubescape scan </path/to/directory> --submit
kubescape scan </path/to/directory>
```
> Kubescape will load the default value file
### Offline/Air-gaped Environment Support
#### Scan a Kustomize Directory
```
kubescape scan </path/to/directory>
```
> Kubescape will generate Kubernetes YAML objects using a 'Kustomize' file and scan them for security.
### Offline/Air-gapped Environment Support
[Video tutorial](https://youtu.be/IGXL9s37smM)
@@ -289,7 +328,7 @@ kubescape scan framework nsa --use-from /path/nsa.json
![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)
Scan the YAML files while writing them using the [VS Code extension](https://github.com/armosec/vscode-kubescape/blob/master/README.md)
## Lens Extension
@@ -355,9 +394,31 @@ View Kubescape scan results directly in [Lens IDE](https://k8slens.dev/) using k
</details>
## VS code configuration samples
## Build on pre-configured killercoda's ubuntu playground
You can use the sample files below to setup your VS code environment for building and debugging purposes.
* [Pre-configured Killercoda's Ubuntu Playground](https://killercoda.com/suhas-gumma/scenario/kubescape-build-for-development)
<details><summary> Pre-programmed actions executed by the playground </summary>
* Clone the official GitHub repository of `Kubescape`.
* [Automate the build process on Linux](https://github.com/kubescape/kubescape#build-on-linuxmacos)
* The entire process involves executing multiple commands in order and it takes around 5-6 minutes to execute them all.
</details>
<details>
<summary>Instructions to use the playground</summary>
* Apply changes you wish to make to the Kubescape directory using text editors like `Vim`.
* [Build on Linux](https://github.com/kubescape/kubescape#build-on-linuxmacos)
* Now, you can use Kubescape like a regular user. Instead of using `kubescape`, use `./kubescape`. Make sure you are in the Kubescape directory because the command will execute the binary named `kubescape` in `kubescape directory`)
</details>
## VS Code configuration samples
You can use the sample files below to setup your VS Code environment for building and debugging purposes.
<details><summary>.vscode/settings.json</summary>
@@ -404,11 +465,11 @@ You can use the sample files below to setup your VS code environment for buildin
## Technology
Kubescape is based on the [OPA engine](https://github.com/open-policy-agent/opa) and ARMO's posture controls.
The tools retrieve Kubernetes objects from the API server and run a set of [rego's snippets](https://www.openpolicyagent.org/docs/latest/policy-language/) developed by [ARMO](https://www.armosec.io?utm_source=github&utm_medium=repository).
The tools retrieve Kubernetes objects from the API server and runs a set of [Rego snippets](https://www.openpolicyagent.org/docs/latest/policy-language/) developed by [ARMO](https://www.armosec.io?utm_source=github&utm_medium=repository).
The results by default are printed in a pretty "console friendly" manner, but they can be retrieved in JSON format for further processing.
The results by default are printed in a "console friendly" manner, but they can be retrieved in JSON format for further processing.
Kubescape is an open source project, we welcome your feedback and ideas for improvement. Were also aiming to collaborate with the Kubernetes community to help make the tests more robust and complete as Kubernetes develops.
Kubescape is an open source project, we welcome your feedback and ideas for improvement. We are part of the Kubernetes community and aim to make the tests more robust and complete as Kubernetes develops.
## Thanks to all the contributors ❤️
<a href = "https://github.com/kubescape/kubescape/graphs/contributors">

View File

@@ -14,14 +14,15 @@ def check_status(status, msg):
def get_build_dir():
current_platform = platform.system()
build_dir = "./build/"
build_dir = ""
if current_platform == "Windows": build_dir += "windows-latest"
elif current_platform == "Linux": build_dir += "ubuntu-latest"
elif current_platform == "Darwin": build_dir += "macos-latest"
if current_platform == "Windows": build_dir = "windows-latest"
elif current_platform == "Linux": build_dir = "ubuntu-latest"
elif current_platform == "Darwin": build_dir = "macos-latest"
else: raise OSError("Platform %s is not supported!" % (current_platform))
return build_dir
return os.path.join("build", build_dir)
def get_package_name():
package_name = "kubescape"
@@ -56,7 +57,7 @@ def main():
if client_name:
ldflags += " -X {}={}".format(client_var, client_name)
build_command = ["go", "build", "-tags=static", "-o", ks_file, "-ldflags" ,ldflags]
build_command = ["go", "build", "-buildmode=pie", "-tags=static", "-o", ks_file, "-ldflags" ,ldflags]
print("Building kubescape and saving here: {}".format(ks_file))
print("Build command: {}".format(" ".join(build_command)))

View File

@@ -1,4 +1,4 @@
FROM golang:1.18-alpine as builder
FROM golang:1.19-alpine as builder
ARG image_version
ARG client
@@ -12,7 +12,7 @@ ENV CGO_ENABLED=1
# Install required python/pip
ENV PYTHONUNBUFFERED=1
RUN apk add --update --no-cache python3 git openssl-dev musl-dev gcc make cmake pkgconfig && ln -sf python3 /usr/bin/python
RUN apk add --update --no-cache python3 gcc make git libc-dev binutils-gold cmake pkgconfig && ln -sf python3 /usr/bin/python
RUN python3 -m ensurepip
RUN pip3 install --no-cache --upgrade pip setuptools
@@ -35,15 +35,15 @@ RUN /work/build/ubuntu-latest/kubescape download artifacts -o /work/artifacts
FROM alpine:3.16.2
RUN addgroup -S armo && adduser -S armo -G armo
RUN addgroup -S ks && adduser -S ks -G ks
COPY --from=builder /work/artifacts/ /home/armo/.kubescape
COPY --from=builder /work/artifacts/ /home/ks/.kubescape
RUN chown -R armo:armo /home/armo/.kubescape
RUN chown -R ks:ks /home/ks/.kubescape
USER armo
USER ks
WORKDIR /home/armo
WORKDIR /home/ks
COPY --from=builder /work/httphandler/build/ubuntu-latest/kubescape /usr/bin/ksserver
COPY --from=builder /work/build/ubuntu-latest/kubescape /usr/bin/kubescape

View File

@@ -9,11 +9,11 @@ import (
var completionCmdExamples = `
# Enable BASH shell autocompletion
$ source <(kubescape completion bash)
# Enable BASH shell autocompletion
$ source <(kubescape completion bash)
$ echo 'source <(kubescape completion bash)' >> ~/.bashrc
# Enable ZSH shell autocompletion
# Enable ZSH shell autocompletion
$ source <(kubectl completion zsh)
$ echo 'source <(kubectl completion zsh)' >> "${fpath[1]}/_kubectl"
@@ -27,7 +27,7 @@ func GetCompletionCmd() *cobra.Command {
Example: completionCmdExamples,
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.ExactValidArgs(1),
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
Run: func(cmd *cobra.Command, args []string) {
switch strings.ToLower(args[0]) {
case "bash":

View File

@@ -25,6 +25,9 @@ var (
# Set access key
kubescape config set secretKey <access key>
# Set cloudAPIURL
kubescape config set cloudAPIURL <cloud API URL>
`
)

View File

@@ -33,9 +33,13 @@ func getSetCmd(ks meta.IKubescape) *cobra.Command {
}
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 },
"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 },
"cloudAPIURL": func(s *metav1.SetConfig, cloudAPIURL string) { s.CloudAPIURL = cloudAPIURL },
"cloudAuthURL": func(s *metav1.SetConfig, cloudAuthURL string) { s.CloudAuthURL = cloudAuthURL },
"cloudReportURL": func(s *metav1.SetConfig, cloudReportURL string) { s.CloudReportURL = cloudReportURL },
"cloudUIURL": func(s *metav1.SetConfig, cloudUIURL string) { s.CloudUIURL = cloudUIURL },
}
func stringKeysToSlice(m map[string]func(*metav1.SetConfig, string)) []string {

View File

@@ -22,6 +22,11 @@ func getExceptionsCmd(ks meta.IKubescape, deleteInfo *v1.Delete) *cobra.Command
return nil
},
Run: func(cmd *cobra.Command, args []string) {
if err := flagValidationDelete(deleteInfo); err != nil {
logger.L().Fatal(err.Error())
}
exceptionsNames := strings.Split(args[0], ";")
if len(exceptionsNames) == 0 {
logger.L().Fatal("missing exceptions names")
@@ -32,3 +37,10 @@ func getExceptionsCmd(ks meta.IKubescape, deleteInfo *v1.Delete) *cobra.Command
},
}
}
// Check if the flag entered are valid
func flagValidationDelete(deleteInfo *v1.Delete) error {
// Validate the user's credentials
return deleteInfo.Credentials.Validate()
}

View File

@@ -17,15 +17,15 @@ 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 "C-0001" control. Run 'kubescape list controls --id' for all controls ids
kubescape download control C-0001
@@ -36,6 +36,8 @@ var (
# Download the configured controls-inputs
kubescape download controls-inputs
# Download the attack tracks
kubescape download attack-tracks
`
)
@@ -59,12 +61,18 @@ func GeDownloadCmd(ks meta.IKubescape) *cobra.Command {
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := flagValidationDownload(&downloadInfo); err != nil {
return err
}
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]
downloadInfo.Identifier = args[1]
}
if err := ks.Download(&downloadInfo); err != nil {
logger.L().Fatal(err.Error())
@@ -80,3 +88,10 @@ func GeDownloadCmd(ks meta.IKubescape) *cobra.Command {
return downloadCmd
}
// Check if the flag entered are valid
func flagValidationDownload(downloadInfo *v1.DownloadInfo) error {
// Validate the user's credentials
return downloadInfo.Credentials.Validate()
}

View File

@@ -20,11 +20,8 @@ var (
# List all supported frameworks names
kubescape list frameworks --account <account id>
# List all supported controls names
# List all supported controls names with ids
kubescape list controls
# List all supported controls ids
kubescape list controls --id
Control documentation:
https://hub.armosec.io/docs/controls
@@ -51,6 +48,11 @@ func GetListCmd(ks meta.IKubescape) *cobra.Command {
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := flagValidationList(&listPolicies); err != nil {
return err
}
listPolicies.Target = args[0]
if err := ks.List(&listPolicies); err != nil {
@@ -62,8 +64,15 @@ func GetListCmd(ks meta.IKubescape) *cobra.Command {
listCmd.PersistentFlags().StringVarP(&listPolicies.Credentials.Account, "account", "", "", "Kubescape SaaS account ID. Default will load account ID from cache")
listCmd.PersistentFlags().StringVarP(&listPolicies.Credentials.ClientID, "client-id", "", "", "Kubescape SaaS client ID. Default will load client ID from cache, read more - https://hub.armosec.io/docs/authentication")
listCmd.PersistentFlags().StringVarP(&listPolicies.Credentials.SecretKey, "secret-key", "", "", "Kubescape SaaS secret key. Default will load secret key from cache, read more - https://hub.armosec.io/docs/authentication")
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")
listCmd.PersistentFlags().StringVar(&listPolicies.Format, "format", "pretty-print", "output format. supported: 'pretty-print'/'json'")
listCmd.PersistentFlags().MarkDeprecated("id", "Control ID's are included in list outpus")
return listCmd
}
// Check if the flag entered are valid
func flagValidationList(listPolicies *v1.ListPolicies) error {
// Validate the user's credentials
return listPolicies.Credentials.Validate()
}

View File

@@ -13,6 +13,7 @@ import (
"github.com/kubescape/kubescape/v2/cmd/list"
"github.com/kubescape/kubescape/v2/cmd/scan"
"github.com/kubescape/kubescape/v2/cmd/submit"
"github.com/kubescape/kubescape/v2/cmd/update"
"github.com/kubescape/kubescape/v2/cmd/version"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/cautils/getter"
@@ -26,7 +27,7 @@ var rootInfo cautils.RootInfo
var ksExamples = `
# Scan command
kubescape scan --submit
kubescape scan
# List supported frameworks
kubescape list frameworks
@@ -76,6 +77,7 @@ func getRootCmd(ks meta.IKubescape) *cobra.Command {
rootCmd.AddCommand(completion.GetCompletionCmd())
rootCmd.AddCommand(version.GetVersionCmd())
rootCmd.AddCommand(config.GetConfigCmd(ks))
rootCmd.AddCommand(update.GetUpdateCmd())
return rootCmd
}

View File

@@ -23,7 +23,7 @@ var (
kubescape scan control "privileged container"
# Scan list of controls separated with a comma
kubescape scan control "privileged container","allowed hostpath"
kubescape scan control "privileged container","HostPath mount"
# Scan list of controls using the control ID separated with a comma
kubescape scan control C-0058,C-0057
@@ -58,6 +58,10 @@ func getControlCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comman
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := validateFrameworkScanInfo(scanInfo); err != nil {
return err
}
// flagValidationControl(scanInfo)
scanInfo.PolicyIdentifier = []cautils.PolicyIdentifier{}
@@ -88,6 +92,10 @@ func getControlCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comman
scanInfo.FrameworkScan = false
if err := validateControlScanInfo(scanInfo); err != nil {
return err
}
results, err := ks.Scan(scanInfo)
if err != nil {
logger.L().Fatal(err.Error())
@@ -101,7 +109,23 @@ func getControlCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comman
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)))
}
enforceSeverityThresholds(results.GetResults().SummaryDetails.GetResourcesSeverityCounters(), scanInfo, terminateOnExceedingSeverity)
return nil
},
}
}
// validateControlScanInfo validates the ScanInfo struct for the `control` command
func validateControlScanInfo(scanInfo *cautils.ScanInfo) error {
severity := scanInfo.FailThresholdSeverity
if scanInfo.Submit && scanInfo.OmitRawResources {
return fmt.Errorf("you can use `omit-raw-resources` or `submit`, but not both")
}
if err := validateSeverity(severity); severity != "" && err != nil {
return err
}
return nil
}

View File

@@ -1,26 +1,28 @@
package scan
import (
"errors"
"fmt"
"io"
"os"
"strings"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
reporthandlingapis "github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/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 all frameworks
kubescape scan framework all
# Scan the NSA framework
kubescape scan framework nsa
@@ -32,10 +34,12 @@ var (
kubescape scan framework all
# Scan kubernetes YAML manifest files (single file or glob)
kubescape scan framework nsa *.yaml
kubescape scan framework nsa .
Run 'kubescape list frameworks' for the list of supported frameworks
`
ErrUnknownSeverity = errors.New("unknown severity")
)
func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Command {
@@ -62,7 +66,7 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
},
RunE: func(cmd *cobra.Command, args []string) error {
if err := flagValidationFramework(scanInfo); err != nil {
if err := validateFrameworkScanInfo(scanInfo); err != nil {
return err
}
scanInfo.FrameworkScan = true
@@ -108,22 +112,102 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
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)
cautils.SimpleDisplay(os.Stderr, "Run with '--verbose'/'-v' flag for detailed resources view\n\n")
}
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)))
}
enforceSeverityThresholds(results.GetData().Report.SummaryDetails.GetResourcesSeverityCounters(), scanInfo, terminateOnExceedingSeverity)
return nil
},
}
}
func flagValidationFramework(scanInfo *cautils.ScanInfo) error {
// countersExceedSeverityThreshold returns true if severity of failed controls exceed the set severity threshold, else returns false
func countersExceedSeverityThreshold(severityCounters reportsummary.ISeverityCounters, scanInfo *cautils.ScanInfo) (bool, error) {
targetSeverity := scanInfo.FailThresholdSeverity
if err := validateSeverity(targetSeverity); err != nil {
return false, err
}
getFailedResourcesFuncsBySeverity := []struct {
SeverityName string
GetFailedResources func() int
}{
{reporthandlingapis.SeverityLowString, severityCounters.NumberOfLowSeverity},
{reporthandlingapis.SeverityMediumString, severityCounters.NumberOfMediumSeverity},
{reporthandlingapis.SeverityHighString, severityCounters.NumberOfHighSeverity},
{reporthandlingapis.SeverityCriticalString, severityCounters.NumberOfCriticalSeverity},
}
targetSeverityIdx := 0
for idx, description := range getFailedResourcesFuncsBySeverity {
if strings.EqualFold(description.SeverityName, targetSeverity) {
targetSeverityIdx = idx
break
}
}
for _, description := range getFailedResourcesFuncsBySeverity[targetSeverityIdx:] {
failedResourcesCount := description.GetFailedResources()
if failedResourcesCount > 0 {
return true, nil
}
}
return false, nil
}
// terminateOnExceedingSeverity terminates the application on exceeding severity
func terminateOnExceedingSeverity(scanInfo *cautils.ScanInfo, l logger.ILogger) {
l.Fatal("result exceeds severity threshold", helpers.String("set severity threshold", scanInfo.FailThresholdSeverity))
}
// enforceSeverityThresholds ensures that the scan results are below the defined severity threshold
//
// The function forces the application to terminate with an exit code 1 if at least one control failed control that exceeds the set severity threshold
func enforceSeverityThresholds(severityCounters reportsummary.ISeverityCounters, scanInfo *cautils.ScanInfo, onExceed func(*cautils.ScanInfo, logger.ILogger)) {
// If a severity threshold is not set, we dont need to enforce it
if scanInfo.FailThresholdSeverity == "" {
return
}
if val, err := countersExceedSeverityThreshold(severityCounters, scanInfo); val && err == nil {
onExceed(scanInfo, logger.L())
} else if err != nil {
logger.L().Fatal(err.Error())
}
}
// validateSeverity returns an error if a given severity is not known, nil otherwise
func validateSeverity(severity string) error {
for _, val := range reporthandlingapis.GetSupportedSeverities() {
if strings.EqualFold(severity, val) {
return nil
}
}
return ErrUnknownSeverity
}
// validateFrameworkScanInfo validates the scan info struct for the `scan framework` command
func validateFrameworkScanInfo(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
if scanInfo.Submit && scanInfo.OmitRawResources {
return fmt.Errorf("you can use `omit-raw-resources` or `submit`, but not both")
}
severity := scanInfo.FailThresholdSeverity
if err := validateSeverity(severity); severity != "" && err != nil {
return err
}
// Validate the user's credentials
return scanInfo.Credentials.Validate()
}

View File

@@ -1,6 +1,7 @@
package scan
import (
"flag"
"fmt"
"github.com/kubescape/k8s-interface/k8sinterface"
@@ -10,16 +11,16 @@ import (
)
var scanCmdExamples = `
Scan command is for scanning an existing cluster or kubernetes manifest files based on pre-defind frameworks
Scan command is for scanning an existing cluster or kubernetes manifest files based on pre-defined frameworks
# Scan current cluster with all frameworks
kubescape scan --submit --enable-host-scan --verbose
kubescape scan --enable-host-scan --verbose
# Scan kubernetes YAML manifest files
kubescape scan *.yaml
kubescape scan .
# Scan and save the results in the JSON format
kubescape scan --format json --output results.json
kubescape scan --format json --output results.json --format-version=v2
# Display all resources
kubescape scan --verbose
@@ -57,6 +58,7 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
},
PreRun: func(cmd *cobra.Command, args []string) {
k8sinterface.SetClusterContextName(scanInfo.KubeContext)
},
PostRun: func(cmd *cobra.Command, args []string) {
// TODO - revert context
@@ -64,36 +66,44 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
}
scanCmd.PersistentFlags().StringVarP(&scanInfo.Credentials.Account, "account", "", "", "Kubescape SaaS account ID. Default will load account ID from cache")
// scanCmd.PersistentFlags().BoolVar(&scanInfo.CreateAccount, "create-account", false, "Create a Kubescape SaaS account ID account ID is not found in cache. After creating the account, the account ID will be saved in cache. In addition, the scanning results will be uploaded to the Kubescape SaaS")
scanCmd.PersistentFlags().StringVarP(&scanInfo.Credentials.ClientID, "client-id", "", "", "Kubescape SaaS client ID. Default will load client ID from cache, read more - https://hub.armosec.io/docs/authentication")
scanCmd.PersistentFlags().StringVarP(&scanInfo.Credentials.SecretKey, "secret-key", "", "", "Kubescape SaaS secret key. Default will load secret key from cache, read more - https://hub.armosec.io/docs/authentication")
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")
scanCmd.PersistentFlags().StringVar(&scanInfo.UseArtifactsFrom, "use-artifacts-from", "", "Load artifacts from local directory. If not used will download them")
scanCmd.PersistentFlags().StringVarP(&scanInfo.ExcludedNamespaces, "exclude-namespaces", "e", "", "Namespaces to exclude from scanning. Recommended: kube-system,kube-public")
scanCmd.PersistentFlags().StringVarP(&scanInfo.ExcludedNamespaces, "exclude-namespaces", "e", "", "Namespaces to exclude from scanning. Notice, when running with `exclude-namespace` kubescape does not scan cluster-scoped objects.")
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", "html"`)
scanCmd.PersistentFlags().StringVar(&scanInfo.FailThresholdSeverity, "severity-threshold", "", "Severity threshold is the severity of failed controls at which the command fails and returns exit code 1")
scanCmd.PersistentFlags().StringVarP(&scanInfo.Format, "format", "f", "", `Output file format. Supported formats: "pretty-printer", "json", "junit", "prometheus", "pdf", "html", "sarif"`)
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 configured backend.")
scanCmd.PersistentFlags().StringVarP(&scanInfo.Output, "output", "o", "", "Output file. Print output to file and not stdout")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.VerboseMode, "verbose", "v", false, "Display all of the input resources and not only failed resources")
scanCmd.PersistentFlags().StringVar(&scanInfo.View, "view", string(cautils.ResourceViewType), fmt.Sprintf("View results based on the %s/%s. default is --view=%s", cautils.ResourceViewType, cautils.ControlViewType, cautils.ResourceViewType))
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.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'")
scanCmd.PersistentFlags().StringVar(&scanInfo.FormatVersion, "format-version", "v1", "Output object can be different between versions, this is for maintaining backward and forward compatibility. Supported:'v1'/'v2'")
scanCmd.PersistentFlags().StringVar(&scanInfo.CustomClusterName, "cluster-name", "", "Set the custom name of the cluster. Not same as the kube-context flag")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Submit, "submit", "", false, "Submit the scan results to Kubescape SaaS 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().BoolVarP(&scanInfo.OmitRawResources, "omit-raw-resources", "", false, "Omit raw resources from the output. By default the raw resources are included in the output")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.PrintAttackTree, "print-attack-tree", "", false, "Print attack tree")
// 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 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
scanCmd.PersistentFlags().MarkHidden("omit-raw-resources")
scanCmd.PersistentFlags().MarkHidden("print-attack-tree")
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://github.com/kubescape/kubescape/blob/master/core/pkg/hostsensorutils/hostsensor.yaml")
// Retrieve --kubeconfig flag from https://github.com/kubernetes/kubectl/blob/master/pkg/cmd/cmd.go
scanCmd.PersistentFlags().AddGoFlag(flag.Lookup("kubeconfig"))
hostF := scanCmd.PersistentFlags().VarPF(&scanInfo.HostSensorEnabled, "enable-host-scan", "", "Deploy Kubescape 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://github.com/kubescape/kubescape/blob/master/core/pkg/hostsensorutils/hostsensor.yaml")
hostF.NoOptDefVal = "true"
hostF.DefValue = "false, for no TTY in stdin"

254
cmd/scan/scan_test.go Normal file
View File

@@ -0,0 +1,254 @@
package scan
import (
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"os"
"reflect"
"testing"
)
func TestExceedsSeverity(t *testing.T) {
testCases := []struct {
Description string
ScanInfo *cautils.ScanInfo
SeverityCounters reportsummary.ISeverityCounters
Want bool
Error error
}{
{
Description: "Critical failed resource should exceed Critical threshold",
ScanInfo: &cautils.ScanInfo{FailThresholdSeverity: "critical"},
SeverityCounters: &reportsummary.SeverityCounters{CriticalSeverityCounter: 1},
Want: true,
},
{
Description: "Critical failed resource should exceed Critical threshold set as constant",
ScanInfo: &cautils.ScanInfo{FailThresholdSeverity: apis.SeverityCriticalString},
SeverityCounters: &reportsummary.SeverityCounters{CriticalSeverityCounter: 1},
Want: true,
},
{
Description: "High failed resource should not exceed Critical threshold",
ScanInfo: &cautils.ScanInfo{FailThresholdSeverity: "critical"},
SeverityCounters: &reportsummary.SeverityCounters{HighSeverityCounter: 1},
Want: false,
},
{
Description: "Critical failed resource exceeds High threshold",
ScanInfo: &cautils.ScanInfo{FailThresholdSeverity: "high"},
SeverityCounters: &reportsummary.SeverityCounters{CriticalSeverityCounter: 1},
Want: true,
},
{
Description: "High failed resource exceeds High threshold",
ScanInfo: &cautils.ScanInfo{FailThresholdSeverity: "high"},
SeverityCounters: &reportsummary.SeverityCounters{HighSeverityCounter: 1},
Want: true,
},
{
Description: "Medium failed resource does not exceed High threshold",
ScanInfo: &cautils.ScanInfo{FailThresholdSeverity: "high"},
SeverityCounters: &reportsummary.SeverityCounters{MediumSeverityCounter: 1},
Want: false,
},
{
Description: "Critical failed resource exceeds Medium threshold",
ScanInfo: &cautils.ScanInfo{FailThresholdSeverity: "medium"},
SeverityCounters: &reportsummary.SeverityCounters{CriticalSeverityCounter: 1},
Want: true,
},
{
Description: "High failed resource exceeds Medium threshold",
ScanInfo: &cautils.ScanInfo{FailThresholdSeverity: "medium"},
SeverityCounters: &reportsummary.SeverityCounters{HighSeverityCounter: 1},
Want: true,
},
{
Description: "Medium failed resource exceeds Medium threshold",
ScanInfo: &cautils.ScanInfo{FailThresholdSeverity: "medium"},
SeverityCounters: &reportsummary.SeverityCounters{MediumSeverityCounter: 1},
Want: true,
},
{
Description: "Low failed resource does not exceed Medium threshold",
ScanInfo: &cautils.ScanInfo{FailThresholdSeverity: "medium"},
SeverityCounters: &reportsummary.SeverityCounters{LowSeverityCounter: 1},
Want: false,
},
{
Description: "Critical failed resource exceeds Low threshold",
ScanInfo: &cautils.ScanInfo{FailThresholdSeverity: "low"},
SeverityCounters: &reportsummary.SeverityCounters{CriticalSeverityCounter: 1},
Want: true,
},
{
Description: "High failed resource exceeds Low threshold",
ScanInfo: &cautils.ScanInfo{FailThresholdSeverity: "low"},
SeverityCounters: &reportsummary.SeverityCounters{HighSeverityCounter: 1},
Want: true,
},
{
Description: "Medium failed resource exceeds Low threshold",
ScanInfo: &cautils.ScanInfo{FailThresholdSeverity: "low"},
SeverityCounters: &reportsummary.SeverityCounters{MediumSeverityCounter: 1},
Want: true,
},
{
Description: "Low failed resource exceeds Low threshold",
ScanInfo: &cautils.ScanInfo{FailThresholdSeverity: "low"},
SeverityCounters: &reportsummary.SeverityCounters{LowSeverityCounter: 1},
Want: true,
},
{
Description: "Unknown severity returns an error",
ScanInfo: &cautils.ScanInfo{FailThresholdSeverity: "unknown"},
SeverityCounters: &reportsummary.SeverityCounters{LowSeverityCounter: 1},
Want: false,
Error: ErrUnknownSeverity,
},
}
for _, testCase := range testCases {
t.Run(testCase.Description, func(t *testing.T) {
got, err := countersExceedSeverityThreshold(testCase.SeverityCounters, testCase.ScanInfo)
want := testCase.Want
if got != want {
t.Errorf("got: %v, want: %v", got, want)
}
if err != testCase.Error {
t.Errorf(`got error "%v", want "%v"`, err, testCase.Error)
}
})
}
}
func Test_enforceSeverityThresholds(t *testing.T) {
testCases := []struct {
Description string
SeverityCounters *reportsummary.SeverityCounters
ScanInfo *cautils.ScanInfo
Want bool
}{
{
"Exceeding Critical severity counter should call the terminating function",
&reportsummary.SeverityCounters{CriticalSeverityCounter: 1},
&cautils.ScanInfo{FailThresholdSeverity: apis.SeverityCriticalString},
true,
},
{
"Non-exceeding severity counter should call not the terminating function",
&reportsummary.SeverityCounters{},
&cautils.ScanInfo{FailThresholdSeverity: apis.SeverityCriticalString},
false,
},
}
for _, tc := range testCases {
t.Run(
tc.Description,
func(t *testing.T) {
severityCounters := tc.SeverityCounters
scanInfo := tc.ScanInfo
want := tc.Want
got := false
onExceed := func(*cautils.ScanInfo, logger.ILogger) {
got = true
}
enforceSeverityThresholds(severityCounters, scanInfo, onExceed)
if got != want {
t.Errorf("got: %v, want %v", got, want)
}
},
)
}
}
type spyLogMessage struct {
Message string
Details map[string]string
}
type spyLogger struct {
setItems []spyLogMessage
}
func (l *spyLogger) Error(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Success(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Warning(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Info(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) Debug(msg string, details ...helpers.IDetails) {}
func (l *spyLogger) SetLevel(level string) error { return nil }
func (l *spyLogger) GetLevel() string { return "" }
func (l *spyLogger) SetWriter(w *os.File) {}
func (l *spyLogger) GetWriter() *os.File { return &os.File{} }
func (l *spyLogger) LoggerName() string { return "" }
func (l *spyLogger) Fatal(msg string, details ...helpers.IDetails) {
firstDetail := details[0]
detailsMap := map[string]string{firstDetail.Key(): firstDetail.Value().(string)}
newMsg := spyLogMessage{msg, detailsMap}
l.setItems = append(l.setItems, newMsg)
}
func (l *spyLogger) GetSpiedItems() []spyLogMessage {
return l.setItems
}
func Test_terminateOnExceedingSeverity(t *testing.T) {
expectedMessage := "result exceeds severity threshold"
expectedKey := "set severity threshold"
testCases := []struct {
Description string
ExpectedMessage string
ExpectedKey string
ExpectedValue string
Logger *spyLogger
}{
{
"Should log the Critical threshold that was set in scan info",
expectedMessage,
expectedKey,
apis.SeverityCriticalString,
&spyLogger{},
},
{
"Should log the High threshold that was set in scan info",
expectedMessage,
expectedKey,
apis.SeverityHighString,
&spyLogger{},
},
}
for _, tc := range testCases {
t.Run(
tc.Description,
func(t *testing.T) {
want := []spyLogMessage{
{tc.ExpectedMessage, map[string]string{tc.ExpectedKey: tc.ExpectedValue}},
}
scanInfo := &cautils.ScanInfo{FailThresholdSeverity: tc.ExpectedValue}
terminateOnExceedingSeverity(scanInfo, tc.Logger)
got := tc.Logger.GetSpiedItems()
if !reflect.DeepEqual(got, want) {
t.Errorf("got: %v, want: %v", got, want)
}
},
)
}
}

115
cmd/scan/validators_test.go Normal file
View File

@@ -0,0 +1,115 @@
package scan
import (
"github.com/kubescape/kubescape/v2/core/cautils"
"testing"
)
// Test_validateControlScanInfo tests how scan info is validated for the `scan control` command
func Test_validateControlScanInfo(t *testing.T) {
testCases := []struct {
Description string
ScanInfo *cautils.ScanInfo
Want error
}{
{
"Empty severity should be valid for scan info",
&cautils.ScanInfo{FailThresholdSeverity: ""},
nil,
},
{
"High severity should be valid for scan info",
&cautils.ScanInfo{FailThresholdSeverity: "High"},
nil,
},
{
"Unknown severity should be invalid for scan info",
&cautils.ScanInfo{FailThresholdSeverity: "Unknown"},
ErrUnknownSeverity,
},
}
for _, tc := range testCases {
t.Run(
tc.Description,
func(t *testing.T) {
var want error = tc.Want
got := validateControlScanInfo(tc.ScanInfo)
if got != want {
t.Errorf("got: %v, want: %v", got, want)
}
},
)
}
}
// Test_validateFrameworkScanInfo tests how scan info is validated for the `scan framework` command
func Test_validateFrameworkScanInfo(t *testing.T) {
testCases := []struct {
Description string
ScanInfo *cautils.ScanInfo
Want error
}{
{
"Empty severity should be valid for scan info",
&cautils.ScanInfo{FailThresholdSeverity: ""},
nil,
},
{
"High severity should be valid for scan info",
&cautils.ScanInfo{FailThresholdSeverity: "High"},
nil,
},
{
"Unknown severity should be invalid for scan info",
&cautils.ScanInfo{FailThresholdSeverity: "Unknown"},
ErrUnknownSeverity,
},
}
for _, tc := range testCases {
t.Run(
tc.Description,
func(t *testing.T) {
var want error = tc.Want
got := validateFrameworkScanInfo(tc.ScanInfo)
if got != want {
t.Errorf("got: %v, want: %v", got, want)
}
},
)
}
}
func Test_validateSeverity(t *testing.T) {
testCases := []struct {
Description string
Input string
Want error
}{
{"low should be a valid severity", "low", nil},
{"Low should be a valid severity", "Low", nil},
{"medium should be a valid severity", "medium", nil},
{"Medium should be a valid severity", "Medium", nil},
{"high should be a valid severity", "high", nil},
{"Critical should be a valid severity", "Critical", nil},
{"critical should be a valid severity", "critical", nil},
{"Unknown should be an invalid severity", "Unknown", ErrUnknownSeverity},
}
for _, testCase := range testCases {
t.Run(testCase.Description, func(t *testing.T) {
input := testCase.Input
want := testCase.Want
got := validateSeverity(input)
if got != want {
t.Errorf("got: %v, want: %v", got, want)
}
})
}
}

View File

@@ -21,6 +21,11 @@ func getExceptionsCmd(ks meta.IKubescape, submitInfo *metav1.Submit) *cobra.Comm
return nil
},
Run: func(cmd *cobra.Command, args []string) {
if err := flagValidationSubmit(submitInfo); err != nil {
logger.L().Fatal(err.Error())
}
if err := ks.SubmitExceptions(&submitInfo.Credentials, args[0]); err != nil {
logger.L().Fatal(err.Error())
}

View File

@@ -31,16 +31,21 @@ var (
// getRBACCmd represents the RBAC command
func getRBACCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
return &cobra.Command{
Use: "rbac",
Example: rbacExamples,
Short: "Submit cluster's Role-Based Access Control(RBAC)",
Long: ``,
Use: "rbac",
Deprecated: "This command is deprecated and will not be supported after 1/Jan/2023. Please use the 'scan' command instead.",
Example: rbacExamples,
Short: "Submit cluster's Role-Based Access Control(RBAC)",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
if err := flagValidationSubmit(submitInfo); err != nil {
return err
}
k8s := k8sinterface.NewKubernetesApi()
// get config
clusterConfig := getTenantConfig(&submitInfo.Credentials, "", k8s)
clusterConfig := getTenantConfig(&submitInfo.Credentials, "", "", k8s)
if err := clusterConfig.SetTenant(); err != nil {
logger.L().Error("failed setting account ID", helpers.Error(err))
}
@@ -77,9 +82,16 @@ func getKubernetesApi() *k8sinterface.KubernetesApi {
}
return k8sinterface.NewKubernetesApi()
}
func getTenantConfig(credentials *cautils.Credentials, clusterName string, k8s *k8sinterface.KubernetesApi) cautils.ITenantConfig {
func getTenantConfig(credentials *cautils.Credentials, clusterName string, customClusterName string, k8s *k8sinterface.KubernetesApi) cautils.ITenantConfig {
if !k8sinterface.IsConnectedToCluster() || k8s == nil {
return cautils.NewLocalConfig(getter.GetKSCloudAPIConnector(), credentials, clusterName)
return cautils.NewLocalConfig(getter.GetKSCloudAPIConnector(), credentials, clusterName, customClusterName)
}
return cautils.NewClusterConfig(k8s, getter.GetKSCloudAPIConnector(), credentials, clusterName)
return cautils.NewClusterConfig(k8s, getter.GetKSCloudAPIConnector(), credentials, clusterName, customClusterName)
}
// Check if the flag entered are valid
func flagValidationSubmit(submitInfo *v1.Submit) error {
// Validate the user's credentials
return submitInfo.Credentials.Validate()
}

View File

@@ -54,6 +54,11 @@ func getResultsCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
Short: "Submit a pre scanned results file. The file must be in json format",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
if err := flagValidationSubmit(submitInfo); err != nil {
return err
}
if len(args) == 0 {
return fmt.Errorf("missing results file")
}
@@ -61,7 +66,7 @@ func getResultsCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
k8s := getKubernetesApi()
// get config
clusterConfig := getTenantConfig(&submitInfo.Credentials, "", k8s)
clusterConfig := getTenantConfig(&submitInfo.Credentials, "", "", k8s)
if err := clusterConfig.SetTenant(); err != nil {
logger.L().Error("failed setting account ID", helpers.Error(err))
}

View File

@@ -7,16 +7,21 @@ import (
)
var submitCmdExamples = `
# Submit Kubescape scan results file
kubescape submit results
# Submit exceptions file to Kubescape SaaS
kubescape submit exceptions
`
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: ``,
Use: "submit <command>",
Short: "Submit an object to the Kubescape SaaS version",
Long: ``,
Example: submitCmdExamples,
Run: func(cmd *cobra.Command, args []string) {
},
}

59
cmd/update/update.go Normal file
View File

@@ -0,0 +1,59 @@
package update
//This update command updates to the latest kubescape release.
//Example:-
// kubescape update
import (
"os/exec"
"runtime"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/spf13/cobra"
)
func GetUpdateCmd() *cobra.Command {
updateCmd := &cobra.Command{
Use: "update",
Short: "Update your version",
Long: ``,
RunE: func(_ *cobra.Command, args []string) error {
//Checking the user's version of kubescape to the latest release
if cautils.BuildNumber == cautils.LatestReleaseVersion {
//your version == latest version
logger.L().Info(("You are in the latest version"))
} else {
const OSTYPE string = runtime.GOOS
var ShellToUse string
switch OSTYPE {
case "windows":
cautils.StartSpinner()
//run the installation command for windows
ShellToUse = "powershell"
_, err := exec.Command(ShellToUse, "-c", "iwr -useb https://raw.githubusercontent.com/kubescape/kubescape/master/install.ps1 | iex").Output()
if err != nil {
logger.L().Fatal(err.Error())
}
cautils.StopSpinner()
default:
ShellToUse = "bash"
cautils.StartSpinner()
//run the installation command for linux and macOS
_, err := exec.Command(ShellToUse, "-c", "curl -s https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh | /bin/bash").Output()
if err != nil {
logger.L().Fatal(err.Error())
}
cautils.StopSpinner()
}
}
return nil
},
}
return updateCmd
}

View File

@@ -0,0 +1,12 @@
package cautils
import (
"fmt"
"strings"
)
func GetControlLink(controlID string) string {
// For CIS Controls, cis-1.1.3 will be transformed to cis-1-1-3 in documentation link.
docLinkID := strings.ReplaceAll(controlID, ".", "-")
return fmt.Sprintf("https://hub.armosec.io/docs/%s", strings.ToLower(docLinkID))
}

View File

@@ -5,11 +5,13 @@ import (
"encoding/json"
"fmt"
"os"
"regexp"
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/kubescape/v2/core/cautils/getter"
corev1 "k8s.io/api/core/v1"
@@ -31,6 +33,10 @@ type ConfigObj struct {
Token string `json:"invitationParam,omitempty"`
CustomerAdminEMail string `json:"adminMail,omitempty"`
ClusterName string `json:"clusterName,omitempty"`
CloudReportURL string `json:"cloudReportURL,omitempty"`
CloudAPIURL string `json:"cloudAPIURL,omitempty"`
CloudUIURL string `json:"cloudUIURL,omitempty"`
CloudAuthURL string `json:"cloudAuthURL,omitempty"`
}
// Config - convert ConfigObj to config file
@@ -74,6 +80,10 @@ type ITenantConfig interface {
GetClientID() string
GetSecretKey() string
GetConfigObj() *ConfigObj
GetCloudReportURL() string
GetCloudAPIURL() string
GetCloudUIURL() string
GetCloudAuthURL() string
// GetBackendAPI() getter.IBackend
// GenerateURL()
@@ -90,7 +100,7 @@ type LocalConfig struct {
}
func NewLocalConfig(
backendAPI getter.IBackend, credentials *Credentials, clusterName string) *LocalConfig {
backendAPI getter.IBackend, credentials *Credentials, clusterName string, customClusterName string) *LocalConfig {
lc := &LocalConfig{
backendAPI: backendAPI,
@@ -102,26 +112,55 @@ func NewLocalConfig(
}
updateCredentials(lc.configObj, credentials)
updateCloudURLs(lc.configObj)
if clusterName != "" {
// If a custom cluster name is provided then set that name, else use the cluster's original name
if customClusterName != "" {
lc.configObj.ClusterName = AdoptClusterName(customClusterName)
} else if clusterName != "" {
lc.configObj.ClusterName = AdoptClusterName(clusterName) // override config clusterName
}
lc.backendAPI.SetAccountID(lc.configObj.AccountID)
lc.backendAPI.SetClientID(lc.configObj.ClientID)
lc.backendAPI.SetSecretKey(lc.configObj.SecretKey)
if lc.configObj.CloudAPIURL != "" {
lc.backendAPI.SetCloudAPIURL(lc.configObj.CloudAPIURL)
} else {
lc.configObj.CloudAPIURL = lc.backendAPI.GetCloudAPIURL()
}
if lc.configObj.CloudAuthURL != "" {
lc.backendAPI.SetCloudAuthURL(lc.configObj.CloudAuthURL)
} else {
lc.configObj.CloudAuthURL = lc.backendAPI.GetCloudAuthURL()
}
if lc.configObj.CloudReportURL != "" {
lc.backendAPI.SetCloudReportURL(lc.configObj.CloudReportURL)
} else {
lc.configObj.CloudReportURL = lc.backendAPI.GetCloudReportURL()
}
if lc.configObj.CloudUIURL != "" {
lc.backendAPI.SetCloudUIURL(lc.configObj.CloudUIURL)
} else {
lc.configObj.CloudUIURL = lc.backendAPI.GetCloudUIURL()
}
logger.L().Debug("Kubescape Cloud URLs", helpers.String("api", lc.backendAPI.GetCloudAPIURL()), helpers.String("auth", lc.backendAPI.GetCloudAuthURL()), helpers.String("report", lc.backendAPI.GetCloudReportURL()), helpers.String("UI", lc.backendAPI.GetCloudUIURL()))
return lc
}
func (lc *LocalConfig) GetConfigObj() *ConfigObj { return lc.configObj }
func (lc *LocalConfig) GetTenantEmail() string { return lc.configObj.CustomerAdminEMail }
func (lc *LocalConfig) GetAccountID() string { return lc.configObj.AccountID }
func (lc *LocalConfig) GetClientID() string { return lc.configObj.ClientID }
func (lc *LocalConfig) GetSecretKey() string { return lc.configObj.SecretKey }
func (lc *LocalConfig) GetContextName() string { return lc.configObj.ClusterName }
func (lc *LocalConfig) GetToken() string { return lc.configObj.Token }
func (lc *LocalConfig) IsConfigFound() bool { return existsConfigFile() }
func (lc *LocalConfig) GetConfigObj() *ConfigObj { return lc.configObj }
func (lc *LocalConfig) GetTenantEmail() string { return lc.configObj.CustomerAdminEMail }
func (lc *LocalConfig) GetAccountID() string { return lc.configObj.AccountID }
func (lc *LocalConfig) GetClientID() string { return lc.configObj.ClientID }
func (lc *LocalConfig) GetSecretKey() string { return lc.configObj.SecretKey }
func (lc *LocalConfig) GetContextName() string { return lc.configObj.ClusterName }
func (lc *LocalConfig) GetToken() string { return lc.configObj.Token }
func (lc *LocalConfig) GetCloudReportURL() string { return lc.configObj.CloudReportURL }
func (lc *LocalConfig) GetCloudAPIURL() string { return lc.configObj.CloudAPIURL }
func (lc *LocalConfig) GetCloudUIURL() string { return lc.configObj.CloudUIURL }
func (lc *LocalConfig) GetCloudAuthURL() string { return lc.configObj.CloudAuthURL }
func (lc *LocalConfig) IsConfigFound() bool { return existsConfigFile() }
func (lc *LocalConfig) SetTenant() error {
// Kubescape Cloud tenant GUID
@@ -178,7 +217,7 @@ KS_ACCOUNT_ID
KS_CLIENT_ID
KS_SECRET_KEY
TODO - supprot:
TODO - support:
KS_CACHE // path to cached files
*/
type ClusterConfig struct {
@@ -189,7 +228,7 @@ type ClusterConfig struct {
configMapNamespace string
}
func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBackend, credentials *Credentials, clusterName string) *ClusterConfig {
func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBackend, credentials *Credentials, clusterName string, customClusterName string) *ClusterConfig {
// var configObj *ConfigObj
c := &ClusterConfig{
k8s: k8s,
@@ -209,8 +248,12 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken
loadConfigFromFile(c.configObj)
}
updateCredentials(c.configObj, credentials)
updateCloudURLs(c.configObj)
if clusterName != "" {
// If a custom cluster name is provided then set that name, else use the cluster's original name
if customClusterName != "" {
c.configObj.ClusterName = AdoptClusterName(customClusterName)
} else if clusterName != "" {
c.configObj.ClusterName = AdoptClusterName(clusterName) // override config clusterName
}
@@ -223,18 +266,44 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken
c.backendAPI.SetAccountID(c.configObj.AccountID)
c.backendAPI.SetClientID(c.configObj.ClientID)
c.backendAPI.SetSecretKey(c.configObj.SecretKey)
if c.configObj.CloudAPIURL != "" {
c.backendAPI.SetCloudAPIURL(c.configObj.CloudAPIURL)
} else {
c.configObj.CloudAPIURL = c.backendAPI.GetCloudAPIURL()
}
if c.configObj.CloudAuthURL != "" {
c.backendAPI.SetCloudAuthURL(c.configObj.CloudAuthURL)
} else {
c.configObj.CloudAuthURL = c.backendAPI.GetCloudAuthURL()
}
if c.configObj.CloudReportURL != "" {
c.backendAPI.SetCloudReportURL(c.configObj.CloudReportURL)
} else {
c.configObj.CloudReportURL = c.backendAPI.GetCloudReportURL()
}
if c.configObj.CloudUIURL != "" {
c.backendAPI.SetCloudUIURL(c.configObj.CloudUIURL)
} else {
c.configObj.CloudUIURL = c.backendAPI.GetCloudUIURL()
}
logger.L().Debug("Kubescape Cloud URLs", helpers.String("api", c.backendAPI.GetCloudAPIURL()), helpers.String("auth", c.backendAPI.GetCloudAuthURL()), helpers.String("report", c.backendAPI.GetCloudReportURL()), helpers.String("UI", c.backendAPI.GetCloudUIURL()))
return c
}
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) GetClientID() string { return c.configObj.ClientID }
func (c *ClusterConfig) GetSecretKey() string { return c.configObj.SecretKey }
func (c *ClusterConfig) GetTenantEmail() string { return c.configObj.CustomerAdminEMail }
func (c *ClusterConfig) GetToken() string { return c.configObj.Token }
func (c *ClusterConfig) IsConfigFound() bool { return existsConfigFile() || c.existsConfigMap() }
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) GetClientID() string { return c.configObj.ClientID }
func (c *ClusterConfig) GetSecretKey() string { return c.configObj.SecretKey }
func (c *ClusterConfig) GetTenantEmail() string { return c.configObj.CustomerAdminEMail }
func (c *ClusterConfig) GetToken() string { return c.configObj.Token }
func (c *ClusterConfig) GetCloudReportURL() string { return c.configObj.CloudReportURL }
func (c *ClusterConfig) GetCloudAPIURL() string { return c.configObj.CloudAPIURL }
func (c *ClusterConfig) GetCloudUIURL() string { return c.configObj.CloudUIURL }
func (c *ClusterConfig) GetCloudAuthURL() string { return c.configObj.CloudAuthURL }
func (c *ClusterConfig) IsConfigFound() bool { return existsConfigFile() || c.existsConfigMap() }
func (c *ClusterConfig) SetTenant() error {
@@ -401,10 +470,7 @@ func (c *ClusterConfig) updateConfigMap() error {
}
func updateConfigFile(configObj *ConfigObj) error {
if err := os.WriteFile(ConfigFileFullPath(), configObj.Config(), 0664); err != nil {
return err
}
return nil
return os.WriteFile(ConfigFileFullPath(), configObj.Config(), 0664) //nolint:gosec
}
func (c *ClusterConfig) updateConfigData(configMap *corev1.ConfigMap) {
@@ -468,7 +534,11 @@ func DeleteConfigFile() error {
}
func AdoptClusterName(clusterName string) string {
return strings.ReplaceAll(clusterName, "/", "-")
re, err := regexp.Compile(`[^\w]+`)
if err != nil {
return clusterName
}
return re.ReplaceAllString(clusterName, "-")
}
func getConfigMapName() string {
@@ -516,3 +586,39 @@ func updateCredentials(configObj *ConfigObj, credentials *Credentials) {
}
}
func getCloudURLsFromEnv(cloudURLs *CloudURLs) {
// load from env
if cloudAPIURL := os.Getenv("KS_CLOUD_API_URL"); cloudAPIURL != "" {
cloudURLs.CloudAPIURL = cloudAPIURL
}
if cloudAuthURL := os.Getenv("KS_CLOUD_AUTH_URL"); cloudAuthURL != "" {
cloudURLs.CloudAuthURL = cloudAuthURL
}
if cloudReportURL := os.Getenv("KS_CLOUD_REPORT_URL"); cloudReportURL != "" {
cloudURLs.CloudReportURL = cloudReportURL
}
if cloudUIURL := os.Getenv("KS_CLOUD_UI_URL"); cloudUIURL != "" {
cloudURLs.CloudUIURL = cloudUIURL
}
}
func updateCloudURLs(configObj *ConfigObj) {
cloudURLs := &CloudURLs{}
getCloudURLsFromEnv(cloudURLs)
if cloudURLs.CloudAPIURL != "" {
configObj.CloudAPIURL = cloudURLs.CloudAPIURL // override config CloudAPIURL
}
if cloudURLs.CloudAuthURL != "" {
configObj.CloudAuthURL = cloudURLs.CloudAuthURL // override config CloudAuthURL
}
if cloudURLs.CloudReportURL != "" {
configObj.CloudReportURL = cloudURLs.CloudReportURL // override config CloudReportURL
}
if cloudURLs.CloudUIURL != "" {
configObj.CloudUIURL = cloudURLs.CloudUIURL // override config CloudUIURL
}
}

View File

@@ -2,6 +2,7 @@ package cautils
import (
"encoding/json"
"os"
"testing"
"github.com/stretchr/testify/assert"
@@ -16,6 +17,10 @@ func mockConfigObj() *ConfigObj {
ClusterName: "ddd",
CustomerAdminEMail: "ab@cd",
Token: "eee",
CloudReportURL: "report.armo.cloud",
CloudAPIURL: "api.armosec.io",
CloudUIURL: "cloud.armosec.io",
CloudAuthURL: "auth.armosec.io",
}
}
func mockLocalConfig() *LocalConfig {
@@ -39,6 +44,10 @@ func TestConfig(t *testing.T) {
assert.Equal(t, co.AccountID, cop.AccountID)
assert.Equal(t, co.ClientID, cop.ClientID)
assert.Equal(t, co.SecretKey, cop.SecretKey)
assert.Equal(t, co.CloudReportURL, cop.CloudReportURL)
assert.Equal(t, co.CloudAPIURL, cop.CloudAPIURL)
assert.Equal(t, co.CloudUIURL, cop.CloudUIURL)
assert.Equal(t, co.CloudAuthURL, cop.CloudAuthURL)
assert.Equal(t, "", cop.ClusterName) // Not copied to bytes
assert.Equal(t, "", cop.CustomerAdminEMail) // Not copied to bytes
assert.Equal(t, "", cop.Token) // Not copied to bytes
@@ -60,6 +69,10 @@ func TestITenantConfig(t *testing.T) {
assert.Equal(t, co.ClusterName, lc.GetContextName())
assert.Equal(t, co.CustomerAdminEMail, lc.GetTenantEmail())
assert.Equal(t, co.Token, lc.GetToken())
assert.Equal(t, co.CloudReportURL, lc.GetCloudReportURL())
assert.Equal(t, co.CloudAPIURL, lc.GetCloudAPIURL())
assert.Equal(t, co.CloudUIURL, lc.GetCloudUIURL())
assert.Equal(t, co.CloudAuthURL, lc.GetCloudAuthURL())
// test ClusterConfig methods
assert.Equal(t, co.AccountID, c.GetAccountID())
@@ -68,6 +81,10 @@ func TestITenantConfig(t *testing.T) {
assert.Equal(t, co.ClusterName, c.GetContextName())
assert.Equal(t, co.CustomerAdminEMail, c.GetTenantEmail())
assert.Equal(t, co.Token, c.GetToken())
assert.Equal(t, co.CloudReportURL, c.GetCloudReportURL())
assert.Equal(t, co.CloudAPIURL, c.GetCloudAPIURL())
assert.Equal(t, co.CloudUIURL, c.GetCloudUIURL())
assert.Equal(t, co.CloudAuthURL, c.GetCloudAuthURL())
}
func TestUpdateConfigData(t *testing.T) {
@@ -80,6 +97,10 @@ func TestUpdateConfigData(t *testing.T) {
assert.Equal(t, c.GetAccountID(), configMap.Data["accountID"])
assert.Equal(t, c.GetClientID(), configMap.Data["clientID"])
assert.Equal(t, c.GetSecretKey(), configMap.Data["secretKey"])
assert.Equal(t, c.GetCloudReportURL(), configMap.Data["cloudReportURL"])
assert.Equal(t, c.GetCloudAPIURL(), configMap.Data["cloudAPIURL"])
assert.Equal(t, c.GetCloudUIURL(), configMap.Data["cloudUIURL"])
assert.Equal(t, c.GetCloudAuthURL(), configMap.Data["cloudAuthURL"])
}
func TestReadConfig(t *testing.T) {
@@ -97,6 +118,10 @@ func TestReadConfig(t *testing.T) {
assert.Equal(t, com.ClusterName, co.ClusterName)
assert.Equal(t, com.CustomerAdminEMail, co.CustomerAdminEMail)
assert.Equal(t, com.Token, co.Token)
assert.Equal(t, com.CloudReportURL, co.CloudReportURL)
assert.Equal(t, com.CloudAPIURL, co.CloudAPIURL)
assert.Equal(t, com.CloudUIURL, co.CloudUIURL)
assert.Equal(t, com.CloudAuthURL, co.CloudAuthURL)
}
func TestLoadConfigFromData(t *testing.T) {
@@ -120,6 +145,10 @@ func TestLoadConfigFromData(t *testing.T) {
assert.Equal(t, c.GetContextName(), co.ClusterName)
assert.Equal(t, c.GetTenantEmail(), co.CustomerAdminEMail)
assert.Equal(t, c.GetToken(), co.Token)
assert.Equal(t, c.GetCloudReportURL(), co.CloudReportURL)
assert.Equal(t, c.GetCloudAPIURL(), co.CloudAPIURL)
assert.Equal(t, c.GetCloudUIURL(), co.CloudUIURL)
assert.Equal(t, c.GetCloudAuthURL(), co.CloudAuthURL)
}
// use case: all data is in config.json
@@ -139,6 +168,10 @@ func TestLoadConfigFromData(t *testing.T) {
assert.Equal(t, c.GetAccountID(), co.AccountID)
assert.Equal(t, c.GetClientID(), co.ClientID)
assert.Equal(t, c.GetSecretKey(), co.SecretKey)
assert.Equal(t, c.GetCloudReportURL(), co.CloudReportURL)
assert.Equal(t, c.GetCloudAPIURL(), co.CloudAPIURL)
assert.Equal(t, c.GetCloudUIURL(), co.CloudUIURL)
assert.Equal(t, c.GetCloudAuthURL(), co.CloudAuthURL)
}
// use case: some data is in config.json
@@ -151,10 +184,12 @@ func TestLoadConfigFromData(t *testing.T) {
// add to map
configMap.Data["clientID"] = c.configObj.ClientID
configMap.Data["secretKey"] = c.configObj.SecretKey
configMap.Data["cloudReportURL"] = c.configObj.CloudReportURL
// delete the content
c.configObj.ClientID = ""
c.configObj.SecretKey = ""
c.configObj.CloudReportURL = ""
configMap.Data["config.json"] = string(c.GetConfigObj().Config())
loadConfigFromData(c.configObj, configMap.Data)
@@ -162,6 +197,7 @@ func TestLoadConfigFromData(t *testing.T) {
assert.NotEmpty(t, c.GetAccountID())
assert.NotEmpty(t, c.GetClientID())
assert.NotEmpty(t, c.GetSecretKey())
assert.NotEmpty(t, c.GetCloudReportURL())
}
// use case: some data is in config.json
@@ -191,3 +227,44 @@ func TestLoadConfigFromData(t *testing.T) {
}
}
func TestAdoptClusterName(t *testing.T) {
tests := []struct {
name string
clusterName string
want string
}{
{
name: "replace 1",
clusterName: "my-name__is--ks",
want: "my-name__is-ks",
},
{
name: "replace 2",
clusterName: "my-name1",
want: "my-name1",
},
{
name: "replace 3",
clusterName: "my:name",
want: "my-name",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := AdoptClusterName(tt.clusterName); got != tt.want {
t.Errorf("AdoptClusterName() = %v, want %v", got, tt.want)
}
})
}
}
func TestUpdateCloudURLs(t *testing.T) {
co := mockConfigObj()
mockCloudAPIURL := "1-2-3-4.com"
os.Setenv("KS_CLOUD_API_URL", mockCloudAPIURL)
assert.NotEqual(t, co.CloudAPIURL, mockCloudAPIURL)
updateCloudURLs(co)
assert.Equal(t, co.CloudAPIURL, mockCloudAPIURL)
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/opa-utils/reporthandling"
apis "github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/attacktrack/v1alpha1"
"github.com/kubescape/opa-utils/reporthandling/results/v1/prioritization"
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
@@ -17,18 +18,22 @@ type KSResources map[string][]string
type OPASessionObj struct {
K8SResources *K8SResources // input k8s objects
ArmoResource *KSResources // input ARMO objects
Policies []reporthandling.Framework // list of frameworks to scan
AllPolicies *Policies // list of all frameworks
AllResources map[string]workloadinterface.IMetadata // all scanned resources, map[<resource ID>]<resource>
ResourcesResult map[string]resourcesresults.Result // resources scan results, map[<resource ID>]<resource result>
ResourceSource map[string]reporthandling.Source // resources sources, map[<resource ID>]<resource result>
ResourcesPrioritized map[string]prioritization.PrioritizedResource // resources prioritization information, map[<resource ID>]<prioritized resource>
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>]
ResourceAttackTracks map[string]v1alpha1.IAttackTrack // resources attack tracks, map[<resource ID>]<attack track>
AttackTracks map[string]v1alpha1.IAttackTrack
Report *reporthandlingv2.PostureReport // scan results v2 - Remove
RegoInputData RegoInputData // input passed to rego 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
InfoMap map[string]apis.StatusInfo // Map errors of resources to StatusInfo
ResourceToControlsMap map[string][]string // map[<apigroup/apiversion/resource>] = [<control_IDs>]
SessionID string // SessionID
Policies []reporthandling.Framework // list of frameworks to scan
Exceptions []armotypes.PostureExceptionPolicy // list of exceptions to apply on scan results
OmitRawResources bool // omit raw resources from output
}
func NewOPASessionObj(frameworks []reporthandling.Framework, k8sResources *K8SResources, scanInfo *ScanInfo) *OPASessionObj {
@@ -44,6 +49,7 @@ func NewOPASessionObj(frameworks []reporthandling.Framework, k8sResources *K8SRe
ResourceSource: make(map[string]reporthandling.Source),
SessionID: scanInfo.ScanID,
Metadata: scanInfoToScanMetadata(scanInfo),
OmitRawResources: scanInfo.OmitRawResources,
}
}
@@ -93,6 +99,7 @@ type Exception struct {
type RegoInputData struct {
PostureControlInputs map[string][]string `json:"postureControlInputs"`
DataControlInputs map[string]string `json:"dataControlInputs"`
// ClusterName string `json:"clusterName"`
// K8sConfig RegoK8sConfig `json:"k8sconfig"`
}

View File

@@ -15,7 +15,7 @@ import (
"github.com/kubescape/opa-utils/objectsenvelopes"
"github.com/kubescape/opa-utils/objectsenvelopes/localworkload"
"gopkg.in/yaml.v2"
"gopkg.in/yaml.v3"
)
var (
@@ -30,7 +30,7 @@ const (
JSON_FILE_FORMAT FileFormat = "json"
)
// LoadResourcesFromHelmCharts scans a given path (recuresively) for helm charts, renders the templates and returns a map of workloads and a map of chart names
// LoadResourcesFromHelmCharts scans a given path (recursively) for helm charts, renders the templates and returns a map of workloads and a map of chart names
func LoadResourcesFromHelmCharts(basePath string) (map[string][]workloadinterface.IMetadata, map[string]string) {
directories, _ := listDirs(basePath)
helmDirectories := make([]string, 0)
@@ -61,6 +61,41 @@ func LoadResourcesFromHelmCharts(basePath string) (map[string][]workloadinterfac
return sourceToWorkloads, sourceToChartName
}
// If the contents at given path is a Kustomize Directory, LoadResourcesFromKustomizeDirectory will
// generate yaml files using "Kustomize" & renders a map of workloads from those yaml files
func LoadResourcesFromKustomizeDirectory(basePath string) (map[string][]workloadinterface.IMetadata, string) {
isKustomizeDirectory := IsKustomizeDirectory(basePath)
isKustomizeFile := IsKustomizeFile(basePath)
if ok := isKustomizeDirectory || isKustomizeFile; !ok {
return nil, ""
}
sourceToWorkloads := map[string][]workloadinterface.IMetadata{}
kustomizeDirectory := NewKustomizeDirectory(basePath)
var newBasePath string
if isKustomizeFile {
newBasePath = filepath.Dir(basePath)
logger.L().Info("Kustomize File Detected, Scanning the rendered Kubernetes Objects...")
} else {
newBasePath = basePath
logger.L().Info("Kustomize Directory Detected, Scanning the rendered Kubernetes Objects...")
}
wls, errs := kustomizeDirectory.GetWorkloads(newBasePath)
kustomizeDirectoryName := GetKustomizeDirectoryName(newBasePath)
if len(errs) > 0 {
logger.L().Error(fmt.Sprintf("Rendering yaml from Kustomize failed: %v", errs))
}
for k, v := range wls {
sourceToWorkloads[k] = v
}
return sourceToWorkloads, kustomizeDirectoryName
}
func LoadResourcesFromFiles(input, rootPath string) map[string][]workloadinterface.IMetadata {
files, errs := listFiles(input)
if len(errs) > 0 {
@@ -104,9 +139,9 @@ func loadFiles(rootPath string, filePaths []string) (map[string][]workloadinterf
for j := range w {
lw := localworkload.NewLocalWorkload(w[j].GetObject())
if relPath, err := filepath.Rel(rootPath, path); err == nil {
lw.SetPath(relPath)
lw.SetPath(fmt.Sprintf("%s:%d", relPath, j))
} else {
lw.SetPath(path)
lw.SetPath(fmt.Sprintf("%s:%d", path, j))
}
wSlice = append(wSlice, lw)
}
@@ -262,7 +297,7 @@ func glob(root, pattern string, onlyDirectories bool) ([]string, error) {
return err
}
// listing only directotries
// listing only directories
if onlyDirectories {
if info.IsDir() {
if matched, err := filepath.Match(pattern, filepath.Base(path)); err != nil {

View File

@@ -1,10 +1,13 @@
package getter
import (
"fmt"
"strings"
"github.com/armosec/armoapi-go/armotypes"
"github.com/kubescape/opa-utils/gitregostore"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/attacktrack/v1alpha1"
)
// =======================================================================================================================
@@ -22,11 +25,11 @@ func NewDownloadReleasedPolicy() *DownloadReleasedPolicy {
}
}
func (drp *DownloadReleasedPolicy) GetControl(policyName string) (*reporthandling.Control, error) {
func (drp *DownloadReleasedPolicy) GetControl(ID string) (*reporthandling.Control, error) {
var control *reporthandling.Control
var err error
control, err = drp.gs.GetOPAControl(policyName)
control, err = drp.gs.GetOPAControlByID(ID)
if err != nil {
return nil, err
}
@@ -53,13 +56,29 @@ func (drp *DownloadReleasedPolicy) ListFrameworks() ([]string, error) {
return drp.gs.GetOPAFrameworksNamesList()
}
func (drp *DownloadReleasedPolicy) ListControls(listType ListType) ([]string, error) {
switch listType {
case ListID:
return drp.gs.GetOPAControlsIDsList()
default:
return drp.gs.GetOPAControlsNamesList()
func (drp *DownloadReleasedPolicy) ListControls() ([]string, error) {
controlsIDsList, err := drp.gs.GetOPAControlsIDsList()
if err != nil {
return []string{}, err
}
controlsNamesList, err := drp.gs.GetOPAControlsNamesList()
if err != nil {
return []string{}, err
}
controls, err := drp.gs.GetOPAControls()
if err != nil {
return []string{}, err
}
var controlsFrameworksList [][]string
for _, control := range controls {
controlsFrameworksList = append(controlsFrameworksList, control.FrameworkNames)
}
controlsNamesWithIDsandFrameworksList := make([]string, len(controlsIDsList))
// by design all slices have the same lengt
for i := range controlsIDsList {
controlsNamesWithIDsandFrameworksList[i] = fmt.Sprintf("%v|%v|%v", controlsIDsList[i], controlsNamesList[i], strings.Join(controlsFrameworksList[i], ","))
}
return controlsNamesWithIDsandFrameworksList, nil
}
func (drp *DownloadReleasedPolicy) GetControlsInputs(clusterName string) (map[string][]string, error) {
@@ -70,6 +89,14 @@ func (drp *DownloadReleasedPolicy) GetControlsInputs(clusterName string) (map[st
return defaultConfigInputs.Settings.PostureControlInputs, err
}
func (drp *DownloadReleasedPolicy) GetAttackTracks() ([]v1alpha1.AttackTrack, error) {
attackTracks, err := drp.gs.GetAttackTracks()
if err != nil {
return nil, err
}
return attackTracks, err
}
func (drp *DownloadReleasedPolicy) SetRegoObjects() error {
fwNames, err := drp.gs.GetOPAFrameworksNamesList()
if len(fwNames) != 0 && err == nil {
@@ -90,3 +117,11 @@ func contains(s []string, str string) bool {
}
return false
}
func (drp *DownloadReleasedPolicy) GetExceptions(clusterName string) ([]armotypes.PostureExceptionPolicy, error) {
exceptions, err := drp.gs.GetSystemPostureExceptionPolicies()
if err != nil {
return nil, err
}
return exceptions, nil
}

View File

@@ -0,0 +1,42 @@
package getter
import (
"context"
"os"
containeranalysis "cloud.google.com/go/containeranalysis/apiv1"
)
type GCPCloudAPI struct {
credentialsPath string
context context.Context
client *containeranalysis.Client
projectID string
credentialsCheck bool
}
func GetGlobalGCPCloudAPIConnector() *GCPCloudAPI {
if os.Getenv("KS_GCP_CREDENTIALS_PATH") == "" || os.Getenv("KS_GCP_PROJECT_ID") == "" {
return &GCPCloudAPI{
credentialsCheck: false,
}
} else {
return &GCPCloudAPI{
context: context.Background(),
credentialsPath: os.Getenv("KS_GCP_CREDENTIALS_PATH"),
projectID: os.Getenv("KS_GCP_PROJECT_ID"),
credentialsCheck: true,
}
}
}
func (api *GCPCloudAPI) SetClient(client *containeranalysis.Client) {
api.client = client
}
func (api *GCPCloudAPI) GetCredentialsPath() string { return api.credentialsPath }
func (api *GCPCloudAPI) GetClient() *containeranalysis.Client { return api.client }
func (api *GCPCloudAPI) GetProjectID() string { return api.projectID }
func (api *GCPCloudAPI) GetCredentialsCheck() bool { return api.credentialsCheck }
func (api *GCPCloudAPI) GetContext() context.Context { return api.context }

View File

@@ -3,21 +3,16 @@ package getter
import (
"github.com/armosec/armoapi-go/armotypes"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/attacktrack/v1alpha1"
)
// supported listing
type ListType string
const ListID ListType = "id"
const ListName ListType = "name"
type IPolicyGetter interface {
GetFramework(name string) (*reporthandling.Framework, error)
GetFrameworks() ([]reporthandling.Framework, error)
GetControl(name string) (*reporthandling.Control, error)
GetControl(ID string) (*reporthandling.Control, error)
ListFrameworks() ([]string, error)
ListControls(ListType) ([]string, error)
ListControls() ([]string, error)
}
type IExceptionsGetter interface {
@@ -27,10 +22,18 @@ type IBackend interface {
GetAccountID() string
GetClientID() string
GetSecretKey() string
GetCloudReportURL() string
GetCloudAPIURL() string
GetCloudUIURL() string
GetCloudAuthURL() string
SetAccountID(accountID string)
SetClientID(clientID string)
SetSecretKey(secretKey string)
SetCloudReportURL(cloudReportURL string)
SetCloudAPIURL(cloudAPIURL string)
SetCloudUIURL(cloudUIURL string)
SetCloudAuthURL(cloudAuthURL string)
GetTenant() (*TenantResponse, error)
}
@@ -38,3 +41,7 @@ type IBackend interface {
type IControlsInputsGetter interface {
GetControlsInputs(clusterName string) (map[string][]string, error)
}
type IAttackTracksGetter interface {
GetAttackTracks() ([]v1alpha1.AttackTrack, error)
}

View File

@@ -21,18 +21,19 @@ func SaveInFile(policy interface{}, pathStr string) error {
if err != nil {
return err
}
err = os.WriteFile(pathStr, []byte(fmt.Sprintf("%v", string(encodedData))), 0644)
err = os.WriteFile(pathStr, encodedData, 0644) //nolint:gosec
if err != nil {
if os.IsNotExist(err) {
pathDir := path.Dir(pathStr)
if err := os.Mkdir(pathDir, 0744); err != nil {
// pathDir could contain subdirectories
if err := os.MkdirAll(pathDir, 0755); err != nil {
return err
}
} else {
return err
}
err = os.WriteFile(pathStr, []byte(fmt.Sprintf("%v", string(encodedData))), 0644)
err = os.WriteFile(pathStr, encodedData, 0644) //nolint:gosec
if err != nil {
return err
}

View File

@@ -4,15 +4,14 @@ import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"io"
"net/http"
"strings"
"time"
"github.com/armosec/armoapi-go/armotypes"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/attacktrack/v1alpha1"
)
var (
@@ -34,23 +33,22 @@ var (
// KSCloudAPI allows accessing the API of the Kubescape Cloud offering
type KSCloudAPI struct {
httpClient *http.Client
apiURL string
authURL string
erURL string
feURL string
accountID string
clientID string
secretKey string
authCookie string
feToken FeLoginResponse
loggedIn bool
httpClient *http.Client
cloudAPIURL string
cloudAuthURL string
cloudReportURL string
cloudUIURL string
accountID string
clientID string
secretKey string
authCookie string
feToken FeLoginResponse
loggedIn bool
}
var globalKSCloudAPIConnector *KSCloudAPI
func SetKSCloudAPIConnector(ksCloudAPI *KSCloudAPI) {
logger.L().Debug("Kubescape Cloud URLs", helpers.String("api", ksCloudAPI.apiURL), helpers.String("auth", ksCloudAPI.authURL), helpers.String("report", ksCloudAPI.erURL), helpers.String("UI", ksCloudAPI.feURL))
globalKSCloudAPIConnector = ksCloudAPI
}
@@ -64,10 +62,10 @@ func GetKSCloudAPIConnector() *KSCloudAPI {
func NewKSCloudAPIDev() *KSCloudAPI {
apiObj := newKSCloudAPI()
apiObj.apiURL = ksCloudDevBEURL
apiObj.authURL = ksCloudDevAUTHURL
apiObj.erURL = ksCloudDevERURL
apiObj.feURL = ksCloudDevFEURL
apiObj.cloudAPIURL = ksCloudDevBEURL
apiObj.cloudAuthURL = ksCloudDevAUTHURL
apiObj.cloudReportURL = ksCloudDevERURL
apiObj.cloudUIURL = ksCloudDevFEURL
return apiObj
}
@@ -75,10 +73,10 @@ func NewKSCloudAPIDev() *KSCloudAPI {
func NewKSCloudAPIProd() *KSCloudAPI {
apiObj := newKSCloudAPI()
apiObj.apiURL = ksCloudBEURL
apiObj.erURL = ksCloudERURL
apiObj.feURL = ksCloudFEURL
apiObj.authURL = ksCloudAUTHURL
apiObj.cloudAPIURL = ksCloudBEURL
apiObj.cloudReportURL = ksCloudERURL
apiObj.cloudUIURL = ksCloudFEURL
apiObj.cloudAuthURL = ksCloudAUTHURL
return apiObj
}
@@ -86,10 +84,10 @@ func NewKSCloudAPIProd() *KSCloudAPI {
func NewKSCloudAPIStaging() *KSCloudAPI {
apiObj := newKSCloudAPI()
apiObj.apiURL = ksCloudStageBEURL
apiObj.erURL = ksCloudStageERURL
apiObj.feURL = ksCloudStageFEURL
apiObj.authURL = ksCloudStageAUTHURL
apiObj.cloudAPIURL = ksCloudStageBEURL
apiObj.cloudReportURL = ksCloudStageERURL
apiObj.cloudUIURL = ksCloudStageFEURL
apiObj.cloudAuthURL = ksCloudStageAUTHURL
return apiObj
}
@@ -97,10 +95,10 @@ func NewKSCloudAPIStaging() *KSCloudAPI {
func NewKSCloudAPICustomized(ksCloudERURL, ksCloudBEURL, ksCloudFEURL, ksCloudAUTHURL string) *KSCloudAPI {
apiObj := newKSCloudAPI()
apiObj.erURL = ksCloudERURL
apiObj.apiURL = ksCloudBEURL
apiObj.feURL = ksCloudFEURL
apiObj.authURL = ksCloudAUTHURL
apiObj.cloudReportURL = ksCloudERURL
apiObj.cloudAPIURL = ksCloudBEURL
apiObj.cloudUIURL = ksCloudFEURL
apiObj.cloudAuthURL = ksCloudAUTHURL
return apiObj
}
@@ -135,17 +133,36 @@ func (api *KSCloudAPI) Get(fullURL string, headers map[string]string) (string, e
return HttpGetter(api.httpClient, fullURL, headers)
}
func (api *KSCloudAPI) GetAccountID() string { return api.accountID }
func (api *KSCloudAPI) IsLoggedIn() bool { return api.loggedIn }
func (api *KSCloudAPI) GetClientID() string { return api.clientID }
func (api *KSCloudAPI) GetSecretKey() string { return api.secretKey }
func (api *KSCloudAPI) GetFrontendURL() string { return api.feURL }
func (api *KSCloudAPI) GetApiURL() string { return api.apiURL }
func (api *KSCloudAPI) GetAuthURL() string { return api.authURL }
func (api *KSCloudAPI) GetReportReceiverURL() string { return api.erURL }
func (api *KSCloudAPI) SetAccountID(accountID string) { api.accountID = accountID }
func (api *KSCloudAPI) SetClientID(clientID string) { api.clientID = clientID }
func (api *KSCloudAPI) SetSecretKey(secretKey string) { api.secretKey = secretKey }
func (api *KSCloudAPI) GetAccountID() string { return api.accountID }
func (api *KSCloudAPI) IsLoggedIn() bool { return api.loggedIn }
func (api *KSCloudAPI) GetClientID() string { return api.clientID }
func (api *KSCloudAPI) GetSecretKey() string { return api.secretKey }
func (api *KSCloudAPI) GetCloudReportURL() string { return api.cloudReportURL }
func (api *KSCloudAPI) GetCloudAPIURL() string { return api.cloudAPIURL }
func (api *KSCloudAPI) GetCloudUIURL() string { return api.cloudUIURL }
func (api *KSCloudAPI) GetCloudAuthURL() string { return api.cloudAuthURL }
func (api *KSCloudAPI) SetAccountID(accountID string) { api.accountID = accountID }
func (api *KSCloudAPI) SetClientID(clientID string) { api.clientID = clientID }
func (api *KSCloudAPI) SetSecretKey(secretKey string) { api.secretKey = secretKey }
func (api *KSCloudAPI) SetCloudReportURL(cloudReportURL string) { api.cloudReportURL = cloudReportURL }
func (api *KSCloudAPI) SetCloudAPIURL(cloudAPIURL string) { api.cloudAPIURL = cloudAPIURL }
func (api *KSCloudAPI) SetCloudUIURL(cloudUIURL string) { api.cloudUIURL = cloudUIURL }
func (api *KSCloudAPI) SetCloudAuthURL(cloudAuthURL string) { api.cloudAuthURL = cloudAuthURL }
func (api *KSCloudAPI) GetAttackTracks() ([]v1alpha1.AttackTrack, error) {
respStr, err := api.Get(api.getAttackTracksURL(), nil)
if err != nil {
return nil, nil
}
attackTracks := []v1alpha1.AttackTrack{}
if err = JSONDecoder(respStr).Decode(&attackTracks); err != nil {
return nil, err
}
return attackTracks, err
}
func (api *KSCloudAPI) GetFramework(name string) (*reporthandling.Framework, error) {
respStr, err := api.Get(api.getFrameworkURL(name), nil)
@@ -175,7 +192,7 @@ func (api *KSCloudAPI) GetFrameworks() ([]reporthandling.Framework, error) {
return frameworks, err
}
func (api *KSCloudAPI) GetControl(policyName string) (*reporthandling.Control, error) {
func (api *KSCloudAPI) GetControl(ID string) (*reporthandling.Control, error) {
return nil, fmt.Errorf("control api is not public")
}
@@ -289,7 +306,7 @@ func (api *KSCloudAPI) ListFrameworks() ([]string, error) {
return frameworkList, nil
}
func (api *KSCloudAPI) ListControls(l ListType) ([]string, error) {
func (api *KSCloudAPI) ListControls() ([]string, error) {
return nil, fmt.Errorf("control api is not public")
}
@@ -341,7 +358,7 @@ func (api *KSCloudAPI) Login() error {
return fmt.Errorf("error authenticating: %d", resp.StatusCode)
}
responseBody, err := ioutil.ReadAll(resp.Body)
responseBody, err := io.ReadAll(resp.Body)
if err != nil {
return err
}

View File

@@ -13,7 +13,7 @@ var NativeFrameworks = []string{"nsa", "mitre", "armobest", "devopsbest"}
func (api *KSCloudAPI) getFrameworkURL(frameworkName string) string {
u := url.URL{}
u.Scheme, u.Host = parseHost(api.GetApiURL())
u.Scheme, u.Host = parseHost(api.GetCloudAPIURL())
u.Path = "api/v1/armoFrameworks"
q := u.Query()
q.Add("customerGUID", api.getCustomerGUIDFallBack())
@@ -28,9 +28,20 @@ func (api *KSCloudAPI) getFrameworkURL(frameworkName string) string {
return u.String()
}
func (api *KSCloudAPI) getAttackTracksURL() string {
u := url.URL{}
u.Scheme, u.Host = parseHost(api.GetCloudAPIURL())
u.Path = "api/v1/attackTracks"
q := u.Query()
q.Add("customerGUID", api.getCustomerGUIDFallBack())
u.RawQuery = q.Encode()
return u.String()
}
func (api *KSCloudAPI) getListFrameworkURL() string {
u := url.URL{}
u.Scheme, u.Host = parseHost(api.GetApiURL())
u.Scheme, u.Host = parseHost(api.GetCloudAPIURL())
u.Path = "api/v1/armoFrameworks"
q := u.Query()
q.Add("customerGUID", api.getCustomerGUIDFallBack())
@@ -40,7 +51,7 @@ func (api *KSCloudAPI) getListFrameworkURL() string {
}
func (api *KSCloudAPI) getExceptionsURL(clusterName string) string {
u := url.URL{}
u.Scheme, u.Host = parseHost(api.GetApiURL())
u.Scheme, u.Host = parseHost(api.GetCloudAPIURL())
u.Path = "api/v1/armoPostureExceptions"
q := u.Query()
@@ -55,7 +66,7 @@ func (api *KSCloudAPI) getExceptionsURL(clusterName string) string {
func (api *KSCloudAPI) exceptionsURL(exceptionsPolicyName string) string {
u := url.URL{}
u.Scheme, u.Host = parseHost(api.GetApiURL())
u.Scheme, u.Host = parseHost(api.GetCloudAPIURL())
u.Path = "api/v1/postureExceptionPolicy"
q := u.Query()
@@ -77,7 +88,7 @@ func (api *KSCloudAPI) getAccountConfigDefault(clusterName string) string {
func (api *KSCloudAPI) getAccountConfig(clusterName string) string {
u := url.URL{}
u.Scheme, u.Host = parseHost(api.GetApiURL())
u.Scheme, u.Host = parseHost(api.GetCloudAPIURL())
u.Path = "api/v1/armoCustomerConfiguration"
q := u.Query()
@@ -92,21 +103,21 @@ func (api *KSCloudAPI) getAccountConfig(clusterName string) string {
func (api *KSCloudAPI) getAccountURL() string {
u := url.URL{}
u.Scheme, u.Host = parseHost(api.GetApiURL())
u.Scheme, u.Host = parseHost(api.GetCloudAPIURL())
u.Path = "api/v1/createTenant"
return u.String()
}
func (api *KSCloudAPI) getApiToken() string {
u := url.URL{}
u.Scheme, u.Host = parseHost(api.GetAuthURL())
u.Scheme, u.Host = parseHost(api.GetCloudAuthURL())
u.Path = "identity/resources/auth/v1/api-token"
return u.String()
}
func (api *KSCloudAPI) getOpenidCustomers() string {
u := url.URL{}
u.Scheme, u.Host = parseHost(api.GetApiURL())
u.Scheme, u.Host = parseHost(api.GetCloudAPIURL())
u.Path = "api/v1/openid_customers"
return u.String()
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/armosec/armoapi-go/armotypes"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/attacktrack/v1alpha1"
)
// =======================================================================================================================
@@ -35,11 +36,11 @@ func NewLoadPolicy(filePaths []string) *LoadPolicy {
}
}
// Return control from file
func (lp *LoadPolicy) GetControl(controlName string) (*reporthandling.Control, error) {
// GetControl returns a control from the policy file.
func (lp *LoadPolicy) GetControl(controlID string) (*reporthandling.Control, error) {
control := &reporthandling.Control{}
filePath := lp.filePath()
f, err := os.ReadFile(filePath)
if err != nil {
return nil, err
@@ -48,33 +49,39 @@ func (lp *LoadPolicy) GetControl(controlName string) (*reporthandling.Control, e
if err = json.Unmarshal(f, control); err != nil {
return control, err
}
if controlName != "" && !strings.EqualFold(controlName, control.Name) && !strings.EqualFold(controlName, control.ControlID) {
framework, err := lp.GetFramework(control.Name)
if err != nil {
return nil, fmt.Errorf("control from file not matching")
} else {
for _, ctrl := range framework.Controls {
if strings.EqualFold(ctrl.Name, controlName) || strings.EqualFold(ctrl.ControlID, controlName) {
control = &ctrl
break
}
}
if controlID == "" || strings.EqualFold(controlID, control.ControlID) {
return control, nil
}
framework, err := lp.GetFramework(control.Name)
if err != nil {
return nil, fmt.Errorf("control from file not matching")
}
for _, toPin := range framework.Controls {
ctrl := toPin
if strings.EqualFold(ctrl.ControlID, controlID) {
control = &ctrl
break
}
}
return control, err
return control, nil
}
func (lp *LoadPolicy) GetFramework(frameworkName string) (*reporthandling.Framework, error) {
framework := &reporthandling.Framework{}
var framework reporthandling.Framework
var err error
for _, filePath := range lp.filePaths {
framework = reporthandling.Framework{}
f, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
if err = json.Unmarshal(f, framework); err != nil {
return framework, err
if err = json.Unmarshal(f, &framework); err != nil {
return nil, err
}
if strings.EqualFold(frameworkName, framework.Name) {
break
@@ -84,7 +91,7 @@ func (lp *LoadPolicy) GetFramework(frameworkName string) (*reporthandling.Framew
return nil, fmt.Errorf("framework from file not matching")
}
return framework, err
return &framework, err
}
func (lp *LoadPolicy) GetFrameworks() ([]reporthandling.Framework, error) {
@@ -109,7 +116,7 @@ func (lp *LoadPolicy) ListFrameworks() ([]string, error) {
return fwNames, nil
}
func (lp *LoadPolicy) ListControls(listType ListType) ([]string, error) {
func (lp *LoadPolicy) ListControls() ([]string, error) {
// TODO - Support
return []string{}, fmt.Errorf("loading controls list from file is not supported")
}
@@ -130,14 +137,19 @@ func (lp *LoadPolicy) GetControlsInputs(clusterName string) (map[string][]string
filePath := lp.filePath()
accountConfig := &armotypes.CustomerConfig{}
f, err := os.ReadFile(filePath)
fileName := filepath.Base(filePath)
if err != nil {
return nil, err
formattedError := fmt.Errorf("Error opening %s file, \"controls-config\" will be downloaded from ARMO management portal", fileName)
return nil, formattedError
}
if err = json.Unmarshal(f, &accountConfig.Settings.PostureControlInputs); err == nil {
return accountConfig.Settings.PostureControlInputs, nil
}
return nil, err
formattedError := fmt.Errorf("Error reading %s file, %s, \"controls-config\" will be downloaded from ARMO management portal", fileName, err.Error())
return nil, formattedError
}
// temporary support for a list of files
@@ -147,3 +159,18 @@ func (lp *LoadPolicy) filePath() string {
}
return ""
}
func (lp *LoadPolicy) GetAttackTracks() ([]v1alpha1.AttackTrack, error) {
attackTracks := []v1alpha1.AttackTrack{}
f, err := os.ReadFile(lp.filePath())
if err != nil {
return nil, err
}
if err := json.Unmarshal(f, &attackTracks); err != nil {
return nil, err
}
return attackTracks, nil
}

View File

@@ -0,0 +1,16 @@
package cautils
import (
"testing"
giturl "github.com/kubescape/go-git-url"
"github.com/stretchr/testify/require"
)
func TestEnsureRemoteParsed(t *testing.T) {
const remote = "git@gitlab.com:foobar/gitlab-tests/sample-project.git"
require.NotPanics(t, func() {
_, _ = giturl.NewGitURL(remote)
})
}

View File

@@ -3,7 +3,6 @@ package cautils
import (
_ "embed"
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"strings"
@@ -39,7 +38,7 @@ func (s *HelmChartTestSuite) SetupSuite() {
}
var obj interface{}
file, _ := ioutil.ReadFile(filepath.Join("testdata", "helm_expected_default_values.json"))
file, _ := os.ReadFile(filepath.Join("testdata", "helm_expected_default_values.json"))
_ = json.Unmarshal([]byte(file), &obj)
s.expectedDefaultValues = obj.(map[string]interface{})
}

View File

@@ -0,0 +1,115 @@
package cautils
import (
"os"
"path/filepath"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/opa-utils/objectsenvelopes/localworkload"
"sigs.k8s.io/kustomize/api/krusty"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
type KustomizeDirectory struct {
path string
}
// Used for checking if there is "Kustomization" file in the given Directory
var kustomizationFileMatchers = [3]string{"kustomization.yml", "kustomization.yaml", "Kustomization"}
func IsKustomizeDirectory(path string) bool {
if isDir := IsDir(path); !isDir {
return false
}
if lastChar := path[len(path)-1:]; lastChar != "/" {
path += "/"
}
matches := 0
for _, kustomizationFileMatcher := range kustomizationFileMatchers {
checkPath := path + kustomizationFileMatcher
if _, err := os.Stat(checkPath); err == nil {
matches++
}
}
switch matches {
case 0:
return false
case 1:
return true
default:
logger.L().Info("Multiple kustomize files found while checking Kustomize Directory")
return false
}
}
// Used for checking if the path is Kustomization file.
func IsKustomizeFile(path string) bool {
fileName := filepath.Base(path)
for _, kustomizationFileMatcher := range kustomizationFileMatchers {
if fileName == kustomizationFileMatcher {
return true
}
}
return false
}
func NewKustomizeDirectory(path string) *KustomizeDirectory {
return &KustomizeDirectory{
path: path,
}
}
func GetKustomizeDirectoryName(path string) string {
if isKustomizeDirectory := IsKustomizeDirectory(path); !isKustomizeDirectory {
return ""
}
return filepath.Dir(path)
}
// Get Workloads, creates the yaml files(K8s resources) using Kustomize and
// renders the workloads from the yaml files (k8s resources)
func (kd *KustomizeDirectory) GetWorkloads(kustomizeDirectoryPath string) (map[string][]workloadinterface.IMetadata, []error) {
fSys := filesys.MakeFsOnDisk()
kustomizer := krusty.MakeKustomizer(krusty.MakeDefaultOptions())
resmap, err := kustomizer.Run(fSys, kustomizeDirectoryPath)
if err != nil {
return nil, []error{err}
}
yml, err := resmap.AsYaml()
if err != nil {
return nil, []error{err}
}
workloads := make(map[string][]workloadinterface.IMetadata, 0)
errs := []error{}
wls, e := ReadFile(yml, YAML_FILE_FORMAT)
if e != nil {
logger.L().Debug("failed to read rendered yaml file", helpers.String("file", kustomizeDirectoryPath), helpers.Error(e))
}
if len(wls) != 0 {
workloads[kustomizeDirectoryPath] = []workloadinterface.IMetadata{}
for i := range wls {
lw := localworkload.NewLocalWorkload(wls[i].GetObject())
lw.SetPath(kustomizeDirectoryPath)
workloads[kustomizeDirectoryPath] = append(workloads[kustomizeDirectoryPath], lw)
}
}
return workloads, errs
}

View File

@@ -6,10 +6,10 @@ import (
"strings"
"time"
"github.com/armosec/go-git-url/apis"
gitv5 "github.com/go-git/go-git/v5"
configv5 "github.com/go-git/go-git/v5/config"
plumbingv5 "github.com/go-git/go-git/v5/plumbing"
"github.com/kubescape/go-git-url/apis"
git2go "github.com/libgit2/git2go/v33"
)

View File

@@ -27,7 +27,7 @@ func unzipFile(zipPath, destinationFolder string) (*zip.ReadCloser, error) {
return nil, err
}
for _, f := range archive.File {
filePath := filepath.Join(destinationFolder, f.Name)
filePath := filepath.Join(destinationFolder, f.Name) //nolint:gosec
if !strings.HasPrefix(filePath, filepath.Clean(destinationFolder)+string(os.PathSeparator)) {
return nil, fmt.Errorf("invalid file path")
}
@@ -50,7 +50,7 @@ func unzipFile(zipPath, destinationFolder string) (*zip.ReadCloser, error) {
return nil, err
}
if _, err := io.Copy(dstFile, fileInArchive); err != nil {
if _, err := io.Copy(dstFile, fileInArchive); err != nil { //nolint:gosec
return nil, err
}

View File

@@ -1,5 +1,11 @@
package cautils
import (
"fmt"
"github.com/google/uuid"
)
type RootInfo struct {
Logger string // logger level
LoggerName string // logger name ("pretty"/"zap"/"none")
@@ -9,7 +15,12 @@ type RootInfo struct {
KSCloudBEURLs string // Kubescape Cloud URL
KSCloudBEURLsDep string // Kubescape Cloud URL
}
type CloudURLs struct {
CloudReportURL string
CloudAPIURL string
CloudUIURL string
CloudAuthURL string
}
type Credentials struct {
@@ -17,3 +28,23 @@ type Credentials struct {
ClientID string
SecretKey string
}
// To check if the user's credentials: accountID / clientID / secretKey are valid.
func (credentials *Credentials) Validate() error {
// Check if the Account-ID is valid
if _, err := uuid.Parse(credentials.Account); credentials.Account != "" && err != nil {
return fmt.Errorf("bad argument: account must be a valid UUID")
}
// Check if the Client-ID is valid
if _, err := uuid.Parse(credentials.ClientID); credentials.ClientID != "" && err != nil {
return fmt.Errorf("bad argument: account must be a valid UUID")
}
// Check if the Secret-Key is valid
if _, err := uuid.Parse(credentials.SecretKey); credentials.SecretKey != "" && err != nil {
return fmt.Errorf("bad argument: account must be a valid UUID")
}
return nil
}

View File

@@ -0,0 +1,71 @@
package cautils
import "testing"
func TestCredentials_Validate(t *testing.T) {
type fields struct {
Account string
ClientID string
SecretKey string
}
tests := []struct {
name string
fields fields
wantErr bool
}{
{
name: "valid account ID",
fields: fields{
Account: "22019933-feac-4012-a8eb-e81461ba6655",
},
wantErr: false,
},
{
name: "invalid account ID",
fields: fields{
Account: "22019933-feac-4012-a8eb-e81461ba665",
},
wantErr: true,
},
{
name: "valid client ID",
fields: fields{
ClientID: "22019933-feac-4012-a8eb-e81461ba6655",
},
wantErr: false,
},
{
name: "invalid client ID",
fields: fields{
ClientID: "22019933-feac-4012-a8eb-e81461ba665",
},
wantErr: true,
},
{
name: "valid secret key",
fields: fields{
SecretKey: "22019933-feac-4012-a8eb-e81461ba6655",
},
wantErr: false,
},
{
name: "invalid secret key",
fields: fields{
SecretKey: "22019933-feac-4012-a8eb-e81461ba665",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
credentials := &Credentials{
Account: tt.fields.Account,
ClientID: tt.fields.ClientID,
SecretKey: tt.fields.SecretKey,
}
if err := credentials.Validate(); (err != nil) != tt.wantErr {
t.Errorf("Credentials.Validate() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

View File

@@ -3,7 +3,6 @@ package cautils
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
@@ -11,7 +10,7 @@ import (
"github.com/armosec/armoapi-go/armotypes"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
giturl "github.com/armosec/go-git-url"
giturl "github.com/kubescape/go-git-url"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/k8sinterface"
@@ -40,7 +39,8 @@ const (
// ScanCluster string = "cluster"
// ScanLocalFiles string = "yaml"
localControlInputsFilename string = "controls-inputs.json"
localExceptionsFilename string = "exceptions.json"
LocalExceptionsFilename string = "exceptions.json"
LocalAttackTracksFilename string = "attack-tracks.json"
)
type BoolPtrFlag struct {
@@ -94,49 +94,54 @@ const (
)
type PolicyIdentifier struct {
Name string // policy name e.g. nsa,mitre,c-0012
Identifier string // policy Identifier e.g. c-0012 for control, nsa,mitre for frameworks
Kind apisv1.NotificationPolicyKind // policy kind e.g. Framework,Control,Rule
Designators armotypes.PortalDesignator
}
type ScanInfo struct {
Getters // TODO - remove from object
PolicyIdentifier []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
View string // 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 //
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 Kubescape Cloud BE
ScanID string // Report id of the current scan
HostSensorEnabled BoolPtrFlag // Deploy Kubescape K8s host scanner to collect data from certain controls
HostSensorYamlPath string // Path to hostsensor file
Local bool // Do not submit results
Credentials Credentials // account ID
KubeContext string // context name
FrameworkScan bool // false if scanning control
ScanAll bool // true if scan all frameworks
Getters // TODO - remove from object
PolicyIdentifier []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
View string // 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
CustomClusterName string // Set the custom name of the cluster
ExcludedNamespaces string // used for host scanner namespace
IncludeNamespaces string //
InputPatterns []string // Yaml files input patterns
Silent bool // Silent mode - Do not print progress logs
FailThreshold float32 // Failure score threshold
FailThresholdSeverity string // Severity at and above which the command should fail
Submit bool // Submit results to Kubescape Cloud BE
CreateAccount bool // Create account in Kubescape Cloud BE if no account found in local cache
ScanID string // Report id of the current scan
HostSensorEnabled BoolPtrFlag // Deploy Kubescape K8s host scanner to collect data from certain controls
HostSensorYamlPath string // Path to hostsensor file
Local bool // Do not submit results
Credentials Credentials // account ID
KubeContext string // context name
FrameworkScan bool // false if scanning control
ScanAll bool // true if scan all frameworks
OmitRawResources bool // true if omit raw resources from the output
PrintAttackTree bool // true if print attack tree
}
type Getters struct {
ExceptionsGetter getter.IExceptionsGetter
ControlsInputsGetter getter.IControlsInputsGetter
PolicyGetter getter.IPolicyGetter
AttackTracksGetter getter.IAttackTracksGetter
}
func (scanInfo *ScanInfo) Init() {
scanInfo.setUseFrom()
scanInfo.setOutputFile()
scanInfo.setUseArtifactsFrom()
if scanInfo.ScanID == "" {
scanInfo.ScanID = uuid.NewString()
@@ -156,7 +161,7 @@ func (scanInfo *ScanInfo) setUseArtifactsFrom() {
scanInfo.UseArtifactsFrom = dir
}
// set frameworks files
files, err := ioutil.ReadDir(scanInfo.UseArtifactsFrom)
files, err := os.ReadDir(scanInfo.UseArtifactsFrom)
if err != nil {
logger.L().Fatal("failed to read files from directory", helpers.String("dir", scanInfo.UseArtifactsFrom), helpers.Error(err))
}
@@ -173,51 +178,33 @@ func (scanInfo *ScanInfo) setUseArtifactsFrom() {
// set config-inputs file
scanInfo.ControlsInputs = filepath.Join(scanInfo.UseArtifactsFrom, localControlInputsFilename)
// set exceptions
scanInfo.UseExceptions = filepath.Join(scanInfo.UseArtifactsFrom, localExceptionsFilename)
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"))
scanInfo.UseFrom = append(scanInfo.UseFrom, getter.GetDefaultPath(policy.Identifier+".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"
}
// Formats returns a slice of output formats that have been requested for a given scan
func (scanInfo *ScanInfo) Formats() []string {
formatString := scanInfo.Format
if formatString != "" {
return strings.Split(scanInfo.Format, ",")
} else {
return []string{}
}
}
// 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 := PolicyIdentifier{}
newPolicy.Kind = kind
newPolicy.Name = policy
newPolicy.Identifier = policy
scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy)
}
}
@@ -225,7 +212,7 @@ func (scanInfo *ScanInfo) SetPolicyIdentifiers(policies []string, kind apisv1.No
func (scanInfo *ScanInfo) contains(policyName string) bool {
for _, policy := range scanInfo.PolicyIdentifier {
if policy.Name == policyName {
if policy.Identifier == policyName {
return true
}
}
@@ -253,7 +240,7 @@ func scanInfoToScanMetadata(scanInfo *ScanInfo) *reporthandlingv2.Metadata {
}
// append frameworks
for _, policy := range scanInfo.PolicyIdentifier {
metadata.ScanMetadata.TargetNames = append(metadata.ScanMetadata.TargetNames, policy.Name)
metadata.ScanMetadata.TargetNames = append(metadata.ScanMetadata.TargetNames, policy.Identifier)
}
metadata.ScanMetadata.KubescapeVersion = BuildNumber
@@ -423,6 +410,7 @@ func metadataGitLocal(input string) (*reporthandlingv2.RepoContextMetadata, erro
Date: commit.Committer.Date,
CommitterName: commit.Committer.Name,
}
context.LocalRootPath, _ = gitParser.GetRootDir()
return context, nil
}

View File

@@ -18,7 +18,8 @@ func TestSetContextMetadata(t *testing.T) {
assert.Nil(t, ctx.HelmContextMetadata)
assert.Nil(t, ctx.RepoContextMetadata)
}
{
// TODO: tests were commented out due to actual http calls ; http calls should be mocked.
/*{
ctx := reporthandlingv2.ContextMetadata{}
setContextMetadata(&ctx, "https://github.com/kubescape/kubescape")
@@ -31,7 +32,7 @@ func TestSetContextMetadata(t *testing.T) {
assert.Equal(t, "kubescape", ctx.RepoContextMetadata.Repo)
assert.Equal(t, "kubescape", ctx.RepoContextMetadata.Owner)
assert.Equal(t, "master", ctx.RepoContextMetadata.Branch)
}
}*/
}
func TestGetHostname(t *testing.T) {
@@ -42,3 +43,30 @@ func TestGetScanningContext(t *testing.T) {
assert.Equal(t, ContextCluster, GetScanningContext(""))
assert.Equal(t, ContextGitURL, GetScanningContext("https://github.com/kubescape/kubescape"))
}
func TestScanInfoFormats(t *testing.T) {
testCases := []struct {
Input string
Want []string
}{
{"", []string{}},
{"json", []string{"json"}},
{"pdf", []string{"pdf"}},
{"html", []string{"html"}},
{"sarif", []string{"sarif"}},
{"html,pdf,sarif", []string{"html", "pdf", "sarif"}},
{"pretty-printer,pdf,sarif", []string{"pretty-printer", "pdf", "sarif"}},
}
for _, tc := range testCases {
t.Run(tc.Input, func(t *testing.T) {
input := tc.Input
want := tc.Want
scanInfo := &ScanInfo{Format: input}
got := scanInfo.Formats()
assert.Equal(t, want, got)
})
}
}

View File

@@ -14,11 +14,13 @@ import (
"golang.org/x/mod/semver"
)
const SKIP_VERSION_CHECK_DEPRECATED = "KUBESCAPE_SKIP_UPDATE_CHECK"
const SKIP_VERSION_CHECK = "KS_SKIP_UPDATE_CHECK"
const SKIP_VERSION_CHECK_DEPRECATED_ENV = "KUBESCAPE_SKIP_UPDATE_CHECK"
const SKIP_VERSION_CHECK_ENV = "KS_SKIP_UPDATE_CHECK"
const CLIENT_ENV = "KS_CLIENT"
var BuildNumber string
var Client string
var LatestReleaseVersion string
const UnknownBuildNumber = "unknown"
@@ -30,9 +32,14 @@ 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 && boolutils.StringToBool(v) {
if v, ok := os.LookupEnv(CLIENT_ENV); ok && v != "" {
Client = v
}
if v, ok := os.LookupEnv(SKIP_VERSION_CHECK_ENV); ok && boolutils.StringToBool(v) {
return NewVersionCheckHandlerMock()
} else if v, ok := os.LookupEnv(SKIP_VERSION_CHECK_DEPRECATED); ok && boolutils.StringToBool(v) {
} else if v, ok := os.LookupEnv(SKIP_VERSION_CHECK_DEPRECATED_ENV); ok && boolutils.StringToBool(v) {
return NewVersionCheckHandlerMock()
}
return NewVersionCheckHandler()
@@ -108,9 +115,11 @@ func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckReques
return fmt.Errorf("failed to get latest version")
}
LatestReleaseVersion := latestVersion.ClientUpdate
if latestVersion.ClientUpdate != "" {
if BuildNumber != "" && semver.Compare(BuildNumber, latestVersion.ClientUpdate) == -1 {
logger.L().Warning(warningMessage(latestVersion.ClientUpdate))
if BuildNumber != "" && semver.Compare(BuildNumber, LatestReleaseVersion) == -1 {
logger.L().Warning(warningMessage(LatestReleaseVersion))
}
}

View File

@@ -3,6 +3,7 @@ package cautils
import (
"strings"
"github.com/kubescape/k8s-interface/cloudsupport"
"github.com/kubescape/opa-utils/reporthandling/apis"
)
@@ -17,8 +18,13 @@ var (
"LinuxKernelVariables",
"KubeletInfo",
"KubeProxyInfo",
"ControlPlaneInfo",
"CloudProviderInfo",
}
CloudResources = []string{
"ClusterDescribe",
string(cloudsupport.TypeApiServerInfo),
}
CloudResources = []string{"ClusterDescribe"}
)
func MapKSResource(ksResourceMap *KSResources, resources []string) []string {

View File

@@ -8,7 +8,7 @@ import (
func (ks *Kubescape) SetCachedConfig(setConfig *metav1.SetConfig) error {
tenant := getTenantConfig(nil, "", getKubernetesApi())
tenant := getTenantConfig(nil, "", "", getKubernetesApi())
if setConfig.Account != "" {
tenant.GetConfigObj().AccountID = setConfig.Account
@@ -19,19 +19,31 @@ func (ks *Kubescape) SetCachedConfig(setConfig *metav1.SetConfig) error {
if setConfig.ClientID != "" {
tenant.GetConfigObj().ClientID = setConfig.ClientID
}
if setConfig.CloudAPIURL != "" {
tenant.GetConfigObj().CloudAPIURL = setConfig.CloudAPIURL
}
if setConfig.CloudAuthURL != "" {
tenant.GetConfigObj().CloudAuthURL = setConfig.CloudAuthURL
}
if setConfig.CloudReportURL != "" {
tenant.GetConfigObj().CloudReportURL = setConfig.CloudReportURL
}
if setConfig.CloudUIURL != "" {
tenant.GetConfigObj().CloudUIURL = setConfig.CloudUIURL
}
return tenant.UpdateCachedConfig()
}
// View cached configurations
func (ks *Kubescape) ViewCachedConfig(viewConfig *metav1.ViewConfig) error {
tenant := getTenantConfig(nil, "", getKubernetesApi()) // change k8sinterface
tenant := getTenantConfig(nil, "", "", getKubernetesApi()) // change k8sinterface
fmt.Fprintf(viewConfig.Writer, "%s\n", tenant.GetConfigObj().Config())
return nil
}
func (ks *Kubescape) DeleteCachedConfig(deleteConfig *metav1.DeleteConfig) error {
tenant := getTenantConfig(nil, "", getKubernetesApi()) // change k8sinterface
tenant := getTenantConfig(nil, "", "", getKubernetesApi()) // change k8sinterface
return tenant.DeleteCachedConfig()
}

View File

@@ -12,7 +12,7 @@ import (
func (ks *Kubescape) DeleteExceptions(delExceptions *v1.DeleteExceptions) error {
// load cached config
getTenantConfig(&delExceptions.Credentials, "", getKubernetesApi())
getTenantConfig(&delExceptions.Credentials, "", "", getKubernetesApi())
// login kubescape SaaS
ksCloudAPI := getter.GetKSCloudAPIConnector()

View File

@@ -6,19 +6,28 @@ import (
"path/filepath"
"strings"
"github.com/armosec/armoapi-go/armotypes"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v2/core/cautils/getter"
metav1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
)
const (
TargetControlsInputs = "controls-inputs"
TargetExceptions = "exceptions"
TargetControl = "control"
TargetFramework = "framework"
TargetArtifacts = "artifacts"
TargetAttackTracks = "attack-tracks"
)
var downloadFunc = map[string]func(*metav1.DownloadInfo) error{
"controls-inputs": downloadConfigInputs,
"exceptions": downloadExceptions,
"control": downloadControl,
"framework": downloadFramework,
"artifacts": downloadArtifacts,
TargetControlsInputs: downloadConfigInputs,
TargetExceptions: downloadExceptions,
TargetControl: downloadControl,
TargetFramework: downloadFramework,
TargetArtifacts: downloadArtifacts,
TargetAttackTracks: downloadAttackTracks,
}
func DownloadSupportCommands() []string {
@@ -70,6 +79,7 @@ func downloadArtifacts(downloadInfo *metav1.DownloadInfo) error {
"controls-inputs": downloadConfigInputs,
"exceptions": downloadExceptions,
"framework": downloadFramework,
"attack-tracks": downloadAttackTracks,
}
for artifact := range artifacts {
if err := downloadArtifact(&metav1.DownloadInfo{Target: artifact, Path: downloadInfo.Path, FileName: fmt.Sprintf("%s.json", artifact)}, artifacts); err != nil {
@@ -80,9 +90,9 @@ func downloadArtifacts(downloadInfo *metav1.DownloadInfo) error {
}
func downloadConfigInputs(downloadInfo *metav1.DownloadInfo) error {
tenant := getTenantConfig(&downloadInfo.Credentials, "", getKubernetesApi())
tenant := getTenantConfig(&downloadInfo.Credentials, "", "", getKubernetesApi())
controlsInputsGetter := getConfigInputsGetter(downloadInfo.Name, tenant.GetAccountID(), nil)
controlsInputsGetter := getConfigInputsGetter(downloadInfo.Identifier, tenant.GetAccountID(), nil)
controlInputs, err := controlsInputsGetter.GetControlsInputs(tenant.GetContextName())
if err != nil {
return err
@@ -103,17 +113,14 @@ func downloadConfigInputs(downloadInfo *metav1.DownloadInfo) error {
}
func downloadExceptions(downloadInfo *metav1.DownloadInfo) error {
var err error
tenant := getTenantConfig(&downloadInfo.Credentials, "", getKubernetesApi())
tenant := getTenantConfig(&downloadInfo.Credentials, "", "", getKubernetesApi())
exceptionsGetter := getExceptionsGetter("", tenant.GetAccountID(), nil)
exceptionsGetter := getExceptionsGetter("")
exceptions := []armotypes.PostureExceptionPolicy{}
if tenant.GetAccountID() != "" {
exceptions, err = exceptionsGetter.GetExceptions(tenant.GetContextName())
if err != nil {
return err
}
exceptions, err := exceptionsGetter.GetExceptions(tenant.GetContextName())
if err != nil {
return err
}
if downloadInfo.FileName == "" {
downloadInfo.FileName = fmt.Sprintf("%s.json", downloadInfo.Target)
}
@@ -126,13 +133,37 @@ func downloadExceptions(downloadInfo *metav1.DownloadInfo) error {
return nil
}
func downloadAttackTracks(downloadInfo *metav1.DownloadInfo) error {
var err error
tenant := getTenantConfig(&downloadInfo.Credentials, "", "", getKubernetesApi())
attackTracksGetter := getAttackTracksGetter(tenant.GetAccountID(), nil)
attackTracks, err := attackTracksGetter.GetAttackTracks()
if err != nil {
return err
}
if downloadInfo.FileName == "" {
downloadInfo.FileName = fmt.Sprintf("%s.json", downloadInfo.Target)
}
// save in file
err = getter.SaveInFile(attackTracks, filepath.Join(downloadInfo.Path, downloadInfo.FileName))
if err != nil {
return err
}
logger.L().Success("Downloaded", helpers.String("attack tracks", downloadInfo.Target), helpers.String("path", filepath.Join(downloadInfo.Path, downloadInfo.FileName)))
return nil
}
func downloadFramework(downloadInfo *metav1.DownloadInfo) error {
tenant := getTenantConfig(&downloadInfo.Credentials, "", getKubernetesApi())
tenant := getTenantConfig(&downloadInfo.Credentials, "", "", getKubernetesApi())
g := getPolicyGetter(nil, tenant.GetTenantEmail(), true, nil)
if downloadInfo.Name == "" {
if downloadInfo.Identifier == "" {
// if framework name not specified - download all frameworks
frameworks, err := g.GetFrameworks()
if err != nil {
@@ -149,9 +180,9 @@ func downloadFramework(downloadInfo *metav1.DownloadInfo) error {
// return fmt.Errorf("missing framework name")
} else {
if downloadInfo.FileName == "" {
downloadInfo.FileName = fmt.Sprintf("%s.json", downloadInfo.Name)
downloadInfo.FileName = fmt.Sprintf("%s.json", downloadInfo.Identifier)
}
framework, err := g.GetFramework(downloadInfo.Name)
framework, err := g.GetFramework(downloadInfo.Identifier)
if err != nil {
return err
}
@@ -170,29 +201,29 @@ func downloadFramework(downloadInfo *metav1.DownloadInfo) error {
func downloadControl(downloadInfo *metav1.DownloadInfo) error {
tenant := getTenantConfig(&downloadInfo.Credentials, "", getKubernetesApi())
tenant := getTenantConfig(&downloadInfo.Credentials, "", "", getKubernetesApi())
g := getPolicyGetter(nil, tenant.GetTenantEmail(), false, nil)
if downloadInfo.Name == "" {
if downloadInfo.Identifier == "" {
// TODO - support
return fmt.Errorf("missing control name")
return fmt.Errorf("missing control ID")
}
if downloadInfo.FileName == "" {
downloadInfo.FileName = fmt.Sprintf("%s.json", downloadInfo.Name)
downloadInfo.FileName = fmt.Sprintf("%s.json", downloadInfo.Identifier)
}
controls, err := g.GetControl(downloadInfo.Name)
controls, err := g.GetControl(downloadInfo.Identifier)
if err != nil {
return err
return fmt.Errorf("failed to download control id '%s', %s", downloadInfo.Identifier, err.Error())
}
if controls == nil {
return fmt.Errorf("failed to download control - received an empty objects")
return fmt.Errorf("failed to download control id '%s' - received an empty objects", downloadInfo.Identifier)
}
downloadTo := filepath.Join(downloadInfo.Path, downloadInfo.FileName)
err = getter.SaveInFile(controls, downloadTo)
if err != nil {
return err
}
logger.L().Success("Downloaded", helpers.String("artifact", downloadInfo.Target), helpers.String("name", downloadInfo.Name), helpers.String("path", downloadTo))
logger.L().Success("Downloaded", helpers.String("artifact", downloadInfo.Target), helpers.String("ID", downloadInfo.Identifier), helpers.String("path", downloadTo))
return nil
}

View File

@@ -2,6 +2,7 @@ package core
import (
"fmt"
"os"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
@@ -10,6 +11,8 @@ import (
"github.com/kubescape/kubescape/v2/core/cautils/getter"
"github.com/kubescape/kubescape/v2/core/pkg/hostsensorutils"
"github.com/kubescape/kubescape/v2/core/pkg/resourcehandler"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer"
printerv2 "github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/reporter"
reporterv2 "github.com/kubescape/kubescape/v2/core/pkg/resultshandling/reporter/v2"
@@ -25,20 +28,32 @@ func getKubernetesApi() *k8sinterface.KubernetesApi {
}
return k8sinterface.NewKubernetesApi()
}
func getTenantConfig(credentials *cautils.Credentials, clusterName string, k8s *k8sinterface.KubernetesApi) cautils.ITenantConfig {
func getTenantConfig(credentials *cautils.Credentials, clusterName string, customClusterName string, k8s *k8sinterface.KubernetesApi) cautils.ITenantConfig {
if !k8sinterface.IsConnectedToCluster() || k8s == nil {
return cautils.NewLocalConfig(getter.GetKSCloudAPIConnector(), credentials, clusterName)
return cautils.NewLocalConfig(getter.GetKSCloudAPIConnector(), credentials, clusterName, customClusterName)
}
return cautils.NewClusterConfig(k8s, getter.GetKSCloudAPIConnector(), credentials, clusterName)
return cautils.NewClusterConfig(k8s, getter.GetKSCloudAPIConnector(), credentials, clusterName, customClusterName)
}
func getExceptionsGetter(useExceptions string) getter.IExceptionsGetter {
func getExceptionsGetter(useExceptions string, accountID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IExceptionsGetter {
if useExceptions != "" {
// load exceptions from file
return getter.NewLoadPolicy([]string{useExceptions})
} else {
}
if accountID != "" {
// download exceptions from Kubescape Cloud backend
return getter.GetKSCloudAPIConnector()
}
// download exceptions from GitHub
if downloadReleasedPolicy == nil {
downloadReleasedPolicy = getter.NewDownloadReleasedPolicy()
}
if err := downloadReleasedPolicy.SetRegoObjects(); err != nil { // if failed to pull attack tracks, fallback to cache
logger.L().Warning("failed to get exceptions from github release, loading attack tracks from cache", helpers.Error(err))
return getter.NewLoadPolicy([]string{getter.GetDefaultPath(cautils.LocalExceptionsFilename)})
}
return downloadReleasedPolicy
}
func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, submit bool) *cautils.RBACObjects {
@@ -58,7 +73,7 @@ func getReporter(tenantConfig cautils.ITenantConfig, reportID string, submit, fw
}
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")
return reporterv2.NewReportMock("https://hub.armosec.io/docs/installing-kubescape", "run kubescape with the '--account' flag")
}
var message string
if !fwScan {
@@ -87,7 +102,7 @@ func getHostSensorHandler(scanInfo *cautils.ScanInfo, k8s *k8sinterface.Kubernet
// we need to determined which controls needs host scanner
if scanInfo.HostSensorEnabled.Get() == nil && hasHostSensorControls {
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")
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)
@@ -110,34 +125,39 @@ func getFieldSelector(scanInfo *cautils.ScanInfo) resourcehandler.IFieldSelector
return &resourcehandler.EmptySelector{}
}
func policyIdentifierNames(pi []cautils.PolicyIdentifier) string {
policiesNames := ""
func policyIdentifierIdentities(pi []cautils.PolicyIdentifier) string {
policiesIdentities := ""
for i := range pi {
policiesNames += pi[i].Name
policiesIdentities += pi[i].Identifier
if i+1 < len(pi) {
policiesNames += ","
policiesIdentities += ","
}
}
if policiesNames == "" {
policiesNames = "all"
if policiesIdentities == "" {
policiesIdentities = "all"
}
return policiesNames
return policiesIdentities
}
// setSubmitBehavior - Setup the desired cluster behavior regarding submitting to the Kubescape Cloud BE
func setSubmitBehavior(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig) {
/*
If "First run (local config not found)" -
Default/keep-local - Do not send report
Submit - Create tenant & Submit report
If CloudReportURL not set - Do not send report
If "Submitted" -
If There is no account - Do not send report
If There is account -
keep-local - Do not send report
Default/Submit - Submit report
Default - Submit report
*/
if getter.GetKSCloudAPIConnector().GetCloudAPIURL() == "" {
scanInfo.Submit = false
return
}
// do not submit control scanning
if !scanInfo.FrameworkScan {
scanInfo.Submit = false
@@ -150,27 +170,30 @@ func setSubmitBehavior(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantC
return
}
if tenantConfig.IsConfigFound() { // config found in cache (submitted)
if !scanInfo.Local {
if tenantConfig.GetAccountID() != "" {
if _, err := uuid.Parse(tenantConfig.GetAccountID()); err != nil {
scanInfo.Submit = false
return
}
}
// Submit report
scanInfo.Submit = true
}
if scanInfo.Local {
scanInfo.Submit = false
return
}
// If There is no account, or if the account is not legal, do not submit
if _, err := uuid.Parse(tenantConfig.GetAccountID()); err != nil {
scanInfo.Submit = false
} else {
scanInfo.Submit = true
}
if scanInfo.CreateAccount {
scanInfo.Submit = true
}
}
// setPolicyGetter set the policy getter - local file/github release/Kubescape Cloud API
func getPolicyGetter(loadPoliciesFromFile []string, tennatEmail string, frameworkScope bool, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IPolicyGetter {
func getPolicyGetter(loadPoliciesFromFile []string, tenantEmail string, frameworkScope bool, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IPolicyGetter {
if len(loadPoliciesFromFile) > 0 {
return getter.NewLoadPolicy(loadPoliciesFromFile)
}
if tennatEmail != "" && frameworkScope {
if tenantEmail != "" && getter.GetKSCloudAPIConnector().GetCloudAPIURL() != "" && frameworkScope {
g := getter.GetKSCloudAPIConnector() // download policy from Kubescape Cloud backend
return g
}
@@ -223,3 +246,28 @@ func listFrameworksNames(policyGetter getter.IPolicyGetter) []string {
}
return getter.NativeFrameworks
}
func getAttackTracksGetter(accountID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IAttackTracksGetter {
if accountID != "" {
g := getter.GetKSCloudAPIConnector() // download attack tracks from Kubescape Cloud backend
return g
}
if downloadReleasedPolicy == nil {
downloadReleasedPolicy = getter.NewDownloadReleasedPolicy()
}
if err := downloadReleasedPolicy.SetRegoObjects(); err != nil { // if failed to pull attack tracks, fallback to cache
logger.L().Warning("failed to get attack tracks from github release, loading attack tracks from cache", helpers.Error(err))
return getter.NewLoadPolicy([]string{getter.GetDefaultPath(cautils.LocalAttackTracksFilename)})
}
return downloadReleasedPolicy
}
// getUIPrinter returns a printer that will be used to print to the programs UI (terminal)
func getUIPrinter(verboseMode bool, formatVersion string, attackTree bool, viewType cautils.ViewTypes) printer.IPrinter {
p := printerv2.NewPrettyPrinter(verboseMode, formatVersion, attackTree, viewType)
// Since the UI of the program is a CLI (Stdout), it means that it should always print to Stdout
p.SetWriter(os.Stdout.Name())
return p
}

View File

@@ -0,0 +1,39 @@
package core
import (
"reflect"
"testing"
"github.com/kubescape/kubescape/v2/core/cautils"
)
func Test_getUIPrinter(t *testing.T) {
scanInfo := &cautils.ScanInfo{
FormatVersion: "v2",
VerboseMode: true,
View: "control",
}
wantFormatVersion := scanInfo.FormatVersion
wantVerboseMode := scanInfo.VerboseMode
wantViewType := cautils.ViewTypes(scanInfo.View)
got := getUIPrinter(scanInfo.VerboseMode, scanInfo.FormatVersion, scanInfo.PrintAttackTree, cautils.ViewTypes(scanInfo.View))
gotValue := reflect.ValueOf(got).Elem()
gotFormatVersion := gotValue.FieldByName("formatVersion").String()
gotVerboseMode := gotValue.FieldByName("verboseMode").Bool()
gotViewType := cautils.ViewTypes(gotValue.FieldByName("viewType").String())
if gotFormatVersion != wantFormatVersion {
t.Errorf("Got: %s, want: %s", gotFormatVersion, wantFormatVersion)
}
if gotVerboseMode != wantVerboseMode {
t.Errorf("Got: %t, want: %t", gotVerboseMode, wantVerboseMode)
}
if gotViewType != wantViewType {
t.Errorf("Got: %v, want: %v", gotViewType, wantViewType)
}
}

View File

@@ -6,8 +6,11 @@ import (
"sort"
"strings"
"github.com/kubescape/kubescape/v2/core/cautils/getter"
"github.com/kubescape/kubescape/v2/core/cautils"
metav1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer"
v2 "github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2"
"github.com/olekukonko/tablewriter"
)
var listFunc = map[string]func(*metav1.ListPolicies) ([]string, error){
@@ -16,7 +19,7 @@ var listFunc = map[string]func(*metav1.ListPolicies) ([]string, error){
"exceptions": listExceptions,
}
var listFormatFunc = map[string]func(*metav1.ListPolicies, []string){
var listFormatFunc = map[string]func(string, []string){
"pretty-print": prettyPrintListFormat,
"json": jsonListFormat,
}
@@ -29,14 +32,18 @@ func ListSupportActions() []string {
return commands
}
func (ks *Kubescape) List(listPolicies *metav1.ListPolicies) error {
if f, ok := listFunc[listPolicies.Target]; ok {
policies, err := f(listPolicies)
if policyListerFunc, ok := listFunc[listPolicies.Target]; ok {
policies, err := policyListerFunc(listPolicies)
if err != nil {
return err
}
sort.Strings(policies)
listFormatFunc[listPolicies.Format](listPolicies, policies)
if listFormatFunction, ok := listFormatFunc[listPolicies.Format]; ok {
listFormatFunction(listPolicies.Target, policies)
} else {
return fmt.Errorf("Invalid format \"%s\", Supported formats: 'pretty-print'/'json' ", listPolicies.Format)
}
return nil
}
@@ -44,29 +51,25 @@ func (ks *Kubescape) List(listPolicies *metav1.ListPolicies) error {
}
func listFrameworks(listPolicies *metav1.ListPolicies) ([]string, error) {
tenant := getTenantConfig(&listPolicies.Credentials, "", getKubernetesApi()) // change k8sinterface
g := getPolicyGetter(nil, tenant.GetTenantEmail(), true, nil)
tenant := getTenantConfig(&listPolicies.Credentials, "", "", getKubernetesApi()) // change k8sinterface
policyGetter := getPolicyGetter(nil, tenant.GetTenantEmail(), true, nil)
return listFrameworksNames(g), nil
return listFrameworksNames(policyGetter), nil
}
func listControls(listPolicies *metav1.ListPolicies) ([]string, error) {
tenant := getTenantConfig(&listPolicies.Credentials, "", getKubernetesApi()) // change k8sinterface
tenant := getTenantConfig(&listPolicies.Credentials, "", "", getKubernetesApi()) // change k8sinterface
g := getPolicyGetter(nil, tenant.GetTenantEmail(), false, nil)
l := getter.ListName
if listPolicies.ListIDs {
l = getter.ListID
}
return g.ListControls(l)
policyGetter := getPolicyGetter(nil, tenant.GetTenantEmail(), false, nil)
return policyGetter.ListControls()
}
func listExceptions(listPolicies *metav1.ListPolicies) ([]string, error) {
// load tenant metav1
getTenantConfig(&listPolicies.Credentials, "", getKubernetesApi())
tenant := getTenantConfig(&listPolicies.Credentials, "", "", getKubernetesApi())
var exceptionsNames []string
ksCloudAPI := getExceptionsGetter("")
ksCloudAPI := getExceptionsGetter("", tenant.GetAccountID(), nil)
exceptions, err := ksCloudAPI.GetExceptions("")
if err != nil {
return exceptionsNames, err
@@ -77,12 +80,73 @@ func listExceptions(listPolicies *metav1.ListPolicies) ([]string, error) {
return exceptionsNames, nil
}
func prettyPrintListFormat(listPolicies *metav1.ListPolicies, policies []string) {
sep := "\n * "
fmt.Printf("Supported %s:%s%s\n", listPolicies.Target, sep, strings.Join(policies, sep))
func prettyPrintListFormat(targetPolicy string, policies []string) {
if targetPolicy == "controls" {
prettyPrintControls(policies)
return
}
header := fmt.Sprintf("Supported %s", targetPolicy)
policyTable := tablewriter.NewWriter(printer.GetWriter(""))
policyTable.SetAutoWrapText(true)
policyTable.SetHeader([]string{header})
policyTable.SetHeaderLine(true)
policyTable.SetRowLine(true)
data := v2.Matrix{}
controlRows := generatePolicyRows(policies)
data = append(data, controlRows...)
policyTable.SetAlignment(tablewriter.ALIGN_CENTER)
policyTable.AppendBulk(data)
policyTable.Render()
}
func jsonListFormat(listPolicies *metav1.ListPolicies, policies []string) {
func jsonListFormat(targetPolicy string, policies []string) {
j, _ := json.MarshalIndent(policies, "", " ")
fmt.Printf("%s\n", j)
}
func prettyPrintControls(policies []string) {
controlsTable := tablewriter.NewWriter(printer.GetWriter(""))
controlsTable.SetAutoWrapText(true)
controlsTable.SetHeader([]string{"Control ID", "Control Name", "Docs", "Frameworks"})
controlsTable.SetHeaderLine(true)
controlsTable.SetRowLine(true)
data := v2.Matrix{}
controlRows := generateControlRows(policies)
data = append(data, controlRows...)
controlsTable.AppendBulk(data)
controlsTable.Render()
}
func generateControlRows(policies []string) [][]string {
rows := [][]string{}
for _, control := range policies {
idAndControlAndFrameworks := strings.Split(control, "|")
id, control, framework := idAndControlAndFrameworks[0], idAndControlAndFrameworks[1], idAndControlAndFrameworks[2]
docs := cautils.GetControlLink(id)
currentRow := []string{id, control, docs, framework}
rows = append(rows, currentRow)
}
return rows
}
func generatePolicyRows(policies []string) [][]string {
rows := [][]string{}
for _, policy := range policies {
currentRow := []string{policy}
rows = append(rows, currentRow)
}
return rows
}

View File

@@ -15,6 +15,7 @@ import (
"github.com/kubescape/kubescape/v2/core/pkg/opaprocessor"
"github.com/kubescape/kubescape/v2/core/pkg/policyhandler"
"github.com/kubescape/kubescape/v2/core/pkg/resourcehandler"
"github.com/kubescape/kubescape/v2/core/pkg/resourcesprioritization"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer"
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/reporter"
@@ -26,7 +27,8 @@ type componentInterfaces struct {
tenantConfig cautils.ITenantConfig
resourceHandler resourcehandler.IResourceHandler
report reporter.IReport
printerHandler printer.IPrinter
outputPrinters []printer.IPrinter
uiPrinter printer.IPrinter
hostSensorHandler hostsensorutils.IHostSensor
}
@@ -43,7 +45,7 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
// ================== setup tenant object ======================================
tenantConfig := getTenantConfig(&scanInfo.Credentials, scanInfo.KubeContext, k8s)
tenantConfig := getTenantConfig(&scanInfo.Credentials, scanInfo.KubeContext, scanInfo.CustomClusterName, k8s)
// Set submit behavior AFTER loading tenant config
setSubmitBehavior(scanInfo, tenantConfig)
@@ -53,12 +55,16 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
if err := tenantConfig.SetTenant(); err != nil {
logger.L().Error(err.Error())
}
if scanInfo.OmitRawResources {
logger.L().Warning("omit-raw-resources flag will be ignored in submit mode")
}
}
// ================== version testing ======================================
v := cautils.NewIVersionCheckHandler()
v.CheckLatestVersion(cautils.NewVersionCheckRequest(cautils.BuildNumber, policyIdentifierNames(scanInfo.PolicyIdentifier), "", cautils.ScanningContextToScanningScope(scanInfo.GetScanningContext())))
v.CheckLatestVersion(cautils.NewVersionCheckRequest(cautils.BuildNumber, policyIdentifierIdentities(scanInfo.PolicyIdentifier), "", cautils.ScanningContextToScanningScope(scanInfo.GetScanningContext())))
// ================== setup host scanner object ======================================
@@ -88,9 +94,17 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
// reporting behavior - setup reporter
reportHandler := getReporter(tenantConfig, scanInfo.ScanID, scanInfo.Submit, scanInfo.FrameworkScan, scanInfo.GetScanningContext())
// setup printer
printerHandler := resultshandling.NewPrinter(scanInfo.Format, scanInfo.FormatVersion, scanInfo.VerboseMode, cautils.ViewTypes(scanInfo.View))
printerHandler.SetWriter(scanInfo.Output)
// setup printers
formats := scanInfo.Formats()
outputPrinters := make([]printer.IPrinter, 0)
for _, format := range formats {
printerHandler := resultshandling.NewPrinter(format, scanInfo.FormatVersion, scanInfo.PrintAttackTree, scanInfo.VerboseMode, cautils.ViewTypes(scanInfo.View))
printerHandler.SetWriter(scanInfo.Output)
outputPrinters = append(outputPrinters, printerHandler)
}
uiPrinter := getUIPrinter(scanInfo.VerboseMode, scanInfo.FormatVersion, scanInfo.PrintAttackTree, cautils.ViewTypes(scanInfo.View))
// ================== return interface ======================================
@@ -98,7 +112,8 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
tenantConfig: tenantConfig,
resourceHandler: resourceHandler,
report: reportHandler,
printerHandler: printerHandler,
outputPrinters: outputPrinters,
uiPrinter: uiPrinter,
hostSensorHandler: hostSensorHandler,
}
}
@@ -121,7 +136,8 @@ func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsH
// set policy getter only after setting the customerGUID
scanInfo.Getters.PolicyGetter = getPolicyGetter(scanInfo.UseFrom, interfaces.tenantConfig.GetTenantEmail(), scanInfo.FrameworkScan, downloadReleasedPolicy)
scanInfo.Getters.ControlsInputsGetter = getConfigInputsGetter(scanInfo.ControlsInputs, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
scanInfo.Getters.ExceptionsGetter = getExceptionsGetter(scanInfo.UseExceptions)
scanInfo.Getters.ExceptionsGetter = getExceptionsGetter(scanInfo.UseExceptions, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
scanInfo.Getters.AttackTracksGetter = getAttackTracksGetter(interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
// TODO - list supported frameworks/controls
if scanInfo.ScanAll {
@@ -135,7 +151,7 @@ func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsH
}
}()
resultsHandling := resultshandling.NewResultsHandler(interfaces.report, interfaces.printerHandler)
resultsHandling := resultshandling.NewResultsHandler(interfaces.report, interfaces.outputPrinters, interfaces.uiPrinter)
// ===================== policies & resources =====================
policyHandler := policyhandler.NewPolicyHandler(interfaces.resourceHandler)
@@ -152,15 +168,13 @@ func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsH
return resultsHandling, fmt.Errorf("%w", err)
}
/*
// ======================== prioritization ===================
// ======================== prioritization ===================
priotizationHandler := resourcesprioritization.NewResourcesPrioritizationHandler(true)
if err := priotizationHandler.PrioritizeResources(scanData); err != nil {
return resultsHandling, fmt.Errorf("%w", err)
}
*/
if priotizationHandler, err := resourcesprioritization.NewResourcesPrioritizationHandler(scanInfo.Getters.AttackTracksGetter, scanInfo.PrintAttackTree); err != nil {
logger.L().Warning("failed to get attack tracks, this may affect the scanning results", helpers.Error(err))
} else if err := priotizationHandler.PrioritizeResources(scanData); err != nil {
return resultsHandling, fmt.Errorf("%w", err)
}
// ========================= results handling =====================
resultsHandling.SetData(scanData)

View File

@@ -39,7 +39,7 @@ func (ks *Kubescape) SubmitExceptions(credentials *cautils.Credentials, excPath
logger.L().Info("submitting exceptions", helpers.String("path", excPath))
// load cached config
tenantConfig := getTenantConfig(credentials, "", getKubernetesApi())
tenantConfig := getTenantConfig(credentials, "", "", getKubernetesApi())
if err := tenantConfig.SetTenant(); err != nil {
logger.L().Error("failed setting account ID", helpers.Error(err))
}

View File

@@ -3,9 +3,13 @@ package v1
import "io"
type SetConfig struct {
Account string
ClientID string
SecretKey string
Account string
ClientID string
SecretKey string
CloudReportURL string
CloudAPIURL string
CloudUIURL string
CloudAuthURL string
}
type ViewConfig struct {

View File

@@ -6,6 +6,6 @@ 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
Identifier string // identifier of artifact to download
Credentials cautils.Credentials
}

View File

@@ -4,7 +4,6 @@ import "github.com/kubescape/kubescape/v2/core/cautils"
type ListPolicies struct {
Target string
ListIDs bool
Format string
Credentials cautils.Credentials
}

View File

@@ -8,7 +8,7 @@ import (
"github.com/kubescape/opa-utils/reporthandling"
)
var mockControl_0006 = `{"guid":"","name":"Allowed hostPath","attributes":{"armoBuiltin":true},"id":"C-0006","controlID":"C-0006","creationTime":"","description":"Mounting host directory to the container can be abused to get access to sensitive data and gain persistence on the host machine.","remediation":"Refrain from using host path mount.","rules":[{"guid":"","name":"alert-rw-hostpath","attributes":{"armoBuiltin":true,"m$K8sThreatMatrix":"Persistence::Writable hostPath mount, Lateral Movement::Writable volume mounts on the host"},"creationTime":"","rule":"package armo_builtins\n\n# input: pod\n# apiversion: v1\n# does: returns hostPath volumes\n\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n volumes := pod.spec.volumes\n volume := volumes[_]\n volume.hostPath\n\tcontainer := pod.spec.containers[i]\n\tvolumeMount := container.volumeMounts[k]\n\tvolumeMount.name == volume.name\n\tbegginingOfPath := \"spec.\"\n\tresult := isRWMount(volumeMount, begginingOfPath, i, k)\n\n podname := pod.metadata.name\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"pod: %v has: %v as hostPath volume\", [podname, volume.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": [result],\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n#handles majority of workload resources\ndeny[msga] {\n\twl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n volumes := wl.spec.template.spec.volumes\n volume := volumes[_]\n volume.hostPath\n\tcontainer := wl.spec.template.spec.containers[i]\n\tvolumeMount := container.volumeMounts[k]\n\tvolumeMount.name == volume.name\n\tbegginingOfPath := \"spec.template.spec.\"\n\tresult := isRWMount(volumeMount, begginingOfPath, i, k)\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"%v: %v has: %v as hostPath volume\", [wl.kind, wl.metadata.name, volume.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": [result],\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t\n\t}\n}\n\n#handles CronJobs\ndeny[msga] {\n\twl := input[_]\n\twl.kind == \"CronJob\"\n volumes := wl.spec.jobTemplate.spec.template.spec.volumes\n volume := volumes[_]\n volume.hostPath\n\n\tcontainer = wl.spec.jobTemplate.spec.template.spec.containers[i]\n\tvolumeMount := container.volumeMounts[k]\n\tvolumeMount.name == volume.name\n\tbegginingOfPath := \"spec.jobTemplate.spec.template.spec.\"\n\tresult := isRWMount(volumeMount, begginingOfPath, i, k)\n\n\tmsga := {\n\t\"alertMessage\": sprintf(\"%v: %v has: %v as hostPath volume\", [wl.kind, wl.metadata.name, volume.name]),\n\t\"packagename\": \"armo_builtins\",\n\t\"alertScore\": 7,\n\t\"failedPaths\": [result],\n\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\nisRWMount(mount, begginingOfPath, i, k) = path {\n not mount.readOnly == true\n not mount.readOnly == false\n path = \"\"\n}\nisRWMount(mount, begginingOfPath, i, k) = path {\n mount.readOnly == false\n path = sprintf(\"%vcontainers[%v].volumeMounts[%v].readOnly\", [begginingOfPath, format_int(i, 10), format_int(k, 10)])\n} ","resourceEnumerator":"","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","CronJob","Pod"]}],"ruleDependencies":[{"packageName":"cautils"},{"packageName":"kubernetes.api.client"}],"configInputs":null,"controlConfigInputs":null,"description":"determines if any workload contains a hostPath volume with rw permissions","remediation":"Set the readOnly field of the mount to true","ruleQuery":""}],"rulesIDs":[""],"baseScore":6}`
var mockControl_0006 = `{"guid":"","name":"HostPath mount","attributes":{"armoBuiltin":true},"id":"C-0048","controlID":"C-0048","creationTime":"","description":"Mounting host directory to the container can be abused to get access to sensitive data and gain persistence on the host machine.","remediation":"Refrain from using host path mount.","rules":[{"guid":"","name":"alert-rw-hostpath","attributes":{"armoBuiltin":true,"m$K8sThreatMatrix":"Persistence::Writable hostPath mount, Lateral Movement::Writable volume mounts on the host"},"creationTime":"","rule":"package armo_builtins\n\n# input: pod\n# apiversion: v1\n# does: returns hostPath volumes\n\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n volumes := pod.spec.volumes\n volume := volumes[_]\n volume.hostPath\n\tcontainer := pod.spec.containers[i]\n\tvolumeMount := container.volumeMounts[k]\n\tvolumeMount.name == volume.name\n\tbegginingOfPath := \"spec.\"\n\tresult := isRWMount(volumeMount, begginingOfPath, i, k)\n\n podname := pod.metadata.name\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"pod: %v has: %v as hostPath volume\", [podname, volume.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": [result],\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n#handles majority of workload resources\ndeny[msga] {\n\twl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n volumes := wl.spec.template.spec.volumes\n volume := volumes[_]\n volume.hostPath\n\tcontainer := wl.spec.template.spec.containers[i]\n\tvolumeMount := container.volumeMounts[k]\n\tvolumeMount.name == volume.name\n\tbegginingOfPath := \"spec.template.spec.\"\n\tresult := isRWMount(volumeMount, begginingOfPath, i, k)\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"%v: %v has: %v as hostPath volume\", [wl.kind, wl.metadata.name, volume.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": [result],\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t\n\t}\n}\n\n#handles CronJobs\ndeny[msga] {\n\twl := input[_]\n\twl.kind == \"CronJob\"\n volumes := wl.spec.jobTemplate.spec.template.spec.volumes\n volume := volumes[_]\n volume.hostPath\n\n\tcontainer = wl.spec.jobTemplate.spec.template.spec.containers[i]\n\tvolumeMount := container.volumeMounts[k]\n\tvolumeMount.name == volume.name\n\tbegginingOfPath := \"spec.jobTemplate.spec.template.spec.\"\n\tresult := isRWMount(volumeMount, begginingOfPath, i, k)\n\n\tmsga := {\n\t\"alertMessage\": sprintf(\"%v: %v has: %v as hostPath volume\", [wl.kind, wl.metadata.name, volume.name]),\n\t\"packagename\": \"armo_builtins\",\n\t\"alertScore\": 7,\n\t\"failedPaths\": [result],\n\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\nisRWMount(mount, begginingOfPath, i, k) = path {\n not mount.readOnly == true\n not mount.readOnly == false\n path = \"\"\n}\nisRWMount(mount, begginingOfPath, i, k) = path {\n mount.readOnly == false\n path = sprintf(\"%vcontainers[%v].volumeMounts[%v].readOnly\", [begginingOfPath, format_int(i, 10), format_int(k, 10)])\n} ","resourceEnumerator":"","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","CronJob","Pod"]}],"ruleDependencies":[{"packageName":"cautils"},{"packageName":"kubernetes.api.client"}],"configInputs":null,"controlConfigInputs":null,"description":"determines if any workload contains a hostPath volume with rw permissions","remediation":"Set the readOnly field of the mount to true","ruleQuery":""}],"rulesIDs":[""],"baseScore":6}`
var mockControl_0044 = `{"guid":"","name":"Container hostPort","attributes":{"armoBuiltin":true},"id":"C-0044","controlID":"C-0044","creationTime":"","description":"Configuring hostPort limits you to a particular port, and if any two workloads that specify the same HostPort they cannot be deployed to the same node. Therefore, if the number of replica of such workload is higher than the number of nodes, the deployment will fail.","remediation":"Avoid usage of hostPort unless it is absolutely necessary. Use NodePort / ClusterIP instead.","rules":[{"guid":"","name":"container-hostPort","attributes":{"armoBuiltin":true},"creationTime":"","rule":"package armo_builtins\n\n\n# Fails if pod has container with hostPort\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n container := pod.spec.containers[i]\n\tbegginingOfPath := \"spec.\"\n\tpath := isHostPort(container, i, begginingOfPath)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Container: %v has Host-port\", [ container.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": path,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n# Fails if workload has container with hostPort\ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n container := wl.spec.template.spec.containers[i]\n\tbegginingOfPath := \"spec.template.spec.\"\n path := isHostPort(container, i, begginingOfPath)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Container: %v in %v: %v has Host-port\", [ container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": path,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n# Fails if cronjob has container with hostPort\ndeny[msga] {\n \twl := input[_]\n\twl.kind == \"CronJob\"\n\tcontainer = wl.spec.jobTemplate.spec.template.spec.containers[i]\n\tbegginingOfPath := \"spec.jobTemplate.spec.template.spec.\"\n path := isHostPort(container, i, begginingOfPath)\n msga := {\n\t\t\"alertMessage\": sprintf(\"Container: %v in %v: %v has Host-port\", [ container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": path,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\n\nisHostPort(container, i, begginingOfPath) = path {\n\tpath = [sprintf(\"%vcontainers[%v].ports[%v].hostPort\", [begginingOfPath, format_int(i, 10), format_int(j, 10)]) | port = container.ports[j]; port.hostPort]\n\tcount(path) > 0\n}\n","resourceEnumerator":"","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob"]}],"ruleDependencies":[],"configInputs":null,"controlConfigInputs":null,"description":"fails if container has hostPort","remediation":"Make sure you do not configure hostPort for the container, if necessary use NodePort / ClusterIP","ruleQuery":"armo_builtins"}],"rulesIDs":[""],"baseScore":4}`
@@ -31,7 +31,7 @@ func MockFramework_0013() *reporthandling.Framework {
return fw
}
// MockFramework_0006_0013 mock control 0013 and control 0006 - "Non-root containers" and "Allowed hostPath"
// MockFramework_0006_0013 mock control 0013 and control 0006 - "Non-root containers" and "HostPath mount"
func MockFramework_0006_0013() *reporthandling.Framework {
fw := &reporthandling.Framework{
PortalBase: armotypes.PortalBase{

View File

@@ -50,7 +50,7 @@ func randSeq(n int, bank []rune) string {
b := make([]rune, n)
for i := range b {
b[i] = bank[rand.Intn(len(bank))]
b[i] = bank[rand.Intn(len(bank))] //nolint:gosec
}
return string(b)
}
@@ -60,7 +60,7 @@ func GenerateContainerScanLayer(layer *ScanResultLayer) {
layer.LayerHash = randSeq(32, hash)
layer.Vulnerabilities = make(VulnerabilitiesList, 0)
layer.Packages = make(LinuxPkgs, 0)
vuls := rand.Intn(10) + 1
vuls := rand.Intn(10) + 1 //nolint:gosec
for i := 0; i < vuls; i++ {
v := Vulnerability{}

View File

@@ -12,7 +12,7 @@ type ElasticContainerVulnerabilityResult struct {
Timestamp int64 `json:"timestamp"`
IsFixed int `json:"isFixed"`
IntroducedInLayer string `json:"layerHash"`
RelevantLinks []string `json:"links"` // shitty SE practice
RelevantLinks []string `json:"links"` // Bad SE practice
Vulnerability `json:",inline"`
}

View File

@@ -28,13 +28,17 @@ spec:
tolerations:
# this toleration is to have the DaemonDet runnable on master nodes
# remove it if your masters can't run pods
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
containers:
- name: host-sensor
image: quay.io/kubescape/host-scanner:latest
image: quay.io/kubescape/host-scanner:v1.0.39
securityContext:
allowPrivilegeEscalation: true
privileged: true
readOnlyRootFilesystem: true
procMount: Unmasked
@@ -69,4 +73,4 @@ spec:
name: host-filesystem
hostNetwork: true
hostPID: true
hostIPC: true
hostIPC: true

View File

@@ -3,6 +3,7 @@ package hostsensorutils
import (
"encoding/json"
"fmt"
"strings"
"sync"
logger "github.com/kubescape/go-logger"
@@ -99,6 +100,30 @@ func (hsh *HostSensorHandler) sendAllPodsHTTPGETRequest(path, requestKind string
return res, nil
}
// return host-scanner version
func (hsh *HostSensorHandler) GetVersion() (string, error) {
// loop over pods and port-forward it to each of them
podList, err := hsh.getPodList()
if err != nil {
return "", fmt.Errorf("failed to sendAllPodsHTTPGETRequest: %v", err)
}
// initialization of the channels
hsh.workerPool.init(len(podList))
hsh.workerPool.hostSensorApplyJobs(podList, "/version", "version")
for job := range hsh.workerPool.jobs {
resBytes, err := hsh.HTTPGetToPod(job.podName, job.path)
if err != nil {
return "", err
} else {
version := strings.ReplaceAll(string(resBytes), "\"", "")
version = strings.ReplaceAll(version, "\n", "")
return version, nil
}
}
return "", nil
}
// return list of LinuxKernelVariables
func (hsh *HostSensorHandler) GetKernelVariables() ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
@@ -129,6 +154,18 @@ func (hsh *HostSensorHandler) GetKubeProxyInfo() ([]hostsensor.HostSensorDataEnv
return hsh.sendAllPodsHTTPGETRequest("/kubeProxyInfo", "KubeProxyInfo")
}
// return list of KubeProxyInfo
func (hsh *HostSensorHandler) GetControlPlaneInfo() ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
return hsh.sendAllPodsHTTPGETRequest("/controlPlaneInfo", ControlPlaneInfo)
}
// return list of KubeProxyInfo
func (hsh *HostSensorHandler) GetCloudProviderInfo() ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
return hsh.sendAllPodsHTTPGETRequest("/cloudProviderInfo", CloudProviderInfo)
}
// return list of KubeletCommandLine
func (hsh *HostSensorHandler) GetKubeletCommandLine() ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
@@ -186,6 +223,16 @@ func (hsh *HostSensorHandler) CollectResources() ([]hostsensor.HostSensorDataEnv
var kcData []hostsensor.HostSensorDataEnvelope
var err error
logger.L().Debug("Accessing host scanner")
version, err := hsh.GetVersion()
if err != nil {
logger.L().Warning(err.Error())
}
if len(version) > 0 {
logger.L().Info("Host scanner version : " + version)
} else {
logger.L().Info("Unknown host scanner version")
}
//
kcData, err = hsh.GetKubeletConfigurations()
if err != nil {
addInfoToMap(KubeletConfiguration, infoMap, err)
@@ -269,6 +316,26 @@ func (hsh *HostSensorHandler) CollectResources() ([]hostsensor.HostSensorDataEnv
res = append(res, kcData...)
}
// GetControlPlaneInfo
kcData, err = hsh.GetControlPlaneInfo()
if err != nil {
addInfoToMap(ControlPlaneInfo, infoMap, err)
logger.L().Warning(err.Error())
}
if len(kcData) > 0 {
res = append(res, kcData...)
}
// GetCloudProviderInfo
kcData, err = hsh.GetCloudProviderInfo()
if err != nil {
addInfoToMap(CloudProviderInfo, infoMap, err)
logger.L().Warning(err.Error())
}
if len(kcData) > 0 {
res = append(res, kcData...)
}
logger.L().Debug("Done reading information from host scanner")
return res, infoMap, nil
}

View File

@@ -15,6 +15,8 @@ var (
KubeletCommandLine = "KubeletCommandLine"
KubeletInfo = "KubeletInfo"
KubeProxyInfo = "KubeProxyInfo"
ControlPlaneInfo = "ControlPlaneInfo"
CloudProviderInfo = "CloudProviderInfo"
MapHostSensorResourceToApiGroup = map[string]string{
KubeletConfiguration: "hostdata.kubescape.cloud/v1beta0",
@@ -26,6 +28,8 @@ var (
LinuxKernelVariables: "hostdata.kubescape.cloud/v1beta0",
KubeletInfo: "hostdata.kubescape.cloud/v1beta0",
KubeProxyInfo: "hostdata.kubescape.cloud/v1beta0",
ControlPlaneInfo: "hostdata.kubescape.cloud/v1beta0",
CloudProviderInfo: "hostdata.kubescape.cloud/v1beta0",
}
)

View File

@@ -35,6 +35,7 @@ type OPAProcessor struct {
func NewOPAProcessor(sessionObj *cautils.OPASessionObj, regoDependenciesData *resources.RegoDependenciesData) *OPAProcessor {
if regoDependenciesData != nil && sessionObj != nil {
regoDependenciesData.PostureControlInputs = sessionObj.RegoInputData.PostureControlInputs
regoDependenciesData.DataControlInputs = sessionObj.RegoInputData.DataControlInputs
}
return &OPAProcessor{
OPASessionObj: sessionObj,
@@ -43,12 +44,12 @@ func NewOPAProcessor(sessionObj *cautils.OPASessionObj, regoDependenciesData *re
}
func (opap *OPAProcessor) ProcessRulesListenner() error {
policies := ConvertFrameworksToPolicies(opap.Policies, cautils.BuildNumber)
opap.OPASessionObj.AllPolicies = ConvertFrameworksToPolicies(opap.Policies, cautils.BuildNumber)
ConvertFrameworksToSummaryDetails(&opap.Report.SummaryDetails, opap.Policies, policies)
ConvertFrameworksToSummaryDetails(&opap.Report.SummaryDetails, opap.Policies, opap.OPASessionObj.AllPolicies)
// process
if err := opap.Process(policies); err != nil {
if err := opap.Process(opap.OPASessionObj.AllPolicies); err != nil {
logger.L().Error(err.Error())
// Return error?
}
@@ -68,23 +69,26 @@ func (opap *OPAProcessor) Process(policies *cautils.Policies) error {
cautils.StartSpinner()
var errs error
for _, control := range policies.Controls {
for _, toPin := range policies.Controls {
control := toPin
resourcesAssociatedControl, err := opap.processControl(&control)
if err != nil {
logger.L().Error(err.Error())
}
if len(resourcesAssociatedControl) == 0 {
continue
}
// update resources with latest results
if len(resourcesAssociatedControl) != 0 {
for resourceID, controlResult := range resourcesAssociatedControl {
if _, ok := opap.ResourcesResult[resourceID]; !ok {
opap.ResourcesResult[resourceID] = resourcesresults.Result{ResourceID: resourceID}
}
t := opap.ResourcesResult[resourceID]
t.AssociatedControls = append(t.AssociatedControls, controlResult)
opap.ResourcesResult[resourceID] = t
for resourceID, controlResult := range resourcesAssociatedControl {
if _, ok := opap.ResourcesResult[resourceID]; !ok {
opap.ResourcesResult[resourceID] = resourcesresults.Result{ResourceID: resourceID}
}
t := opap.ResourcesResult[resourceID]
t.AssociatedControls = append(t.AssociatedControls, controlResult)
opap.ResourcesResult[resourceID] = t
}
}
@@ -94,7 +98,7 @@ func (opap *OPAProcessor) Process(policies *cautils.Policies) error {
opap.loggerDoneScanning()
return errs
return nil
}
func (opap *OPAProcessor) loggerStartScanning() {
@@ -153,12 +157,16 @@ func (opap *OPAProcessor) processControl(control *reporthandling.Control) (map[s
func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule, fixedControlInputs map[string][]string) (map[string]*resourcesresults.ResourceAssociatedRule, error) {
postureControlInputs := opap.regoDependenciesData.GetFilteredPostureControlInputs(rule.ConfigInputs) // get store
dataControlInputs := map[string]string{"cloudProvider": opap.OPASessionObj.Report.ClusterCloudProvider}
// Merge configurable control input and fixed control input
for k, v := range fixedControlInputs {
postureControlInputs[k] = v
}
RuleRegoDependenciesData := resources.RegoDependenciesData{DataControlInputs: dataControlInputs,
PostureControlInputs: postureControlInputs}
inputResources, err := reporthandling.RegoResourcesAggregator(rule, getAllSupportedObjects(opap.K8SResources, opap.ArmoResource, opap.AllResources, rule))
if err != nil {
return nil, fmt.Errorf("error getting aggregated k8sObjects: %s", err.Error())
@@ -185,7 +193,7 @@ func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule, fixedCont
opap.AllResources[inputResources[i].GetID()] = inputResources[i]
}
ruleResponses, err := opap.runOPAOnSingleRule(rule, inputRawResources, ruleData, postureControlInputs)
ruleResponses, err := opap.runOPAOnSingleRule(rule, inputRawResources, ruleData, RuleRegoDependenciesData)
if err != nil {
// TODO - Handle error
logger.L().Error(err.Error())
@@ -217,16 +225,16 @@ func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule, fixedCont
return resources, err
}
func (opap *OPAProcessor) runOPAOnSingleRule(rule *reporthandling.PolicyRule, k8sObjects []map[string]interface{}, getRuleData func(*reporthandling.PolicyRule) string, postureControlInputs map[string][]string) ([]reporthandling.RuleResponse, error) {
func (opap *OPAProcessor) runOPAOnSingleRule(rule *reporthandling.PolicyRule, k8sObjects []map[string]interface{}, getRuleData func(*reporthandling.PolicyRule) string, ruleRegoDependenciesData resources.RegoDependenciesData) ([]reporthandling.RuleResponse, error) {
switch rule.RuleLanguage {
case reporthandling.RegoLanguage, reporthandling.RegoLanguage2:
return opap.runRegoOnK8s(rule, k8sObjects, getRuleData, postureControlInputs)
return opap.runRegoOnK8s(rule, k8sObjects, getRuleData, ruleRegoDependenciesData)
default:
return nil, fmt.Errorf("rule: '%s', language '%v' not supported", rule.Name, rule.RuleLanguage)
}
}
func (opap *OPAProcessor) runRegoOnK8s(rule *reporthandling.PolicyRule, k8sObjects []map[string]interface{}, getRuleData func(*reporthandling.PolicyRule) string, postureControlInputs map[string][]string) ([]reporthandling.RuleResponse, error) {
func (opap *OPAProcessor) runRegoOnK8s(rule *reporthandling.PolicyRule, k8sObjects []map[string]interface{}, getRuleData func(*reporthandling.PolicyRule) string, ruleRegoDependenciesData resources.RegoDependenciesData) ([]reporthandling.RuleResponse, error) {
// compile modules
modules, err := getRuleDependencies()
@@ -239,7 +247,7 @@ func (opap *OPAProcessor) runRegoOnK8s(rule *reporthandling.PolicyRule, k8sObjec
return nil, fmt.Errorf("in 'runRegoOnSingleRule', failed to compile rule, name: %s, reason: %s", rule.Name, err.Error())
}
store, err := resources.TOStorage(postureControlInputs)
store, err := ruleRegoDependenciesData.TOStorage()
if err != nil {
return nil, err
}
@@ -282,8 +290,12 @@ func (opap *OPAProcessor) enumerateData(rule *reporthandling.PolicyRule, k8sObje
return k8sObjects, nil
}
postureControlInputs := opap.regoDependenciesData.GetFilteredPostureControlInputs(rule.ConfigInputs)
dataControlInputs := map[string]string{"cloudProvider": opap.OPASessionObj.Report.ClusterCloudProvider}
ruleResponse, err := opap.runOPAOnSingleRule(rule, k8sObjects, ruleEnumeratorData, postureControlInputs)
RuleRegoDependenciesData := resources.RegoDependenciesData{DataControlInputs: dataControlInputs,
PostureControlInputs: postureControlInputs}
ruleResponse, err := opap.runOPAOnSingleRule(rule, k8sObjects, ruleEnumeratorData, RuleRegoDependenciesData)
if err != nil {
return nil, err
}

View File

@@ -12,12 +12,12 @@ import (
resources "github.com/kubescape/opa-utils/resources"
)
// updateResults update the results objects and report objects. This is a critical function - DO NOT CHANGE
/*
- remove sensible data
- adding exceptions
- summarize results
*/
// updateResults updates the results objects and report objects. This is a critical function - DO NOT CHANGE
//
// The function:
// - removes sensible data
// - adds exceptions
// - summarizes results
func (opap *OPAProcessor) updateResults() {
// remove data from all objects
@@ -49,16 +49,6 @@ func (opap *OPAProcessor) updateResults() {
// map control to error
controlToInfoMap := mapControlToInfo(opap.ResourceToControlsMap, opap.InfoMap, opap.Report.SummaryDetails.Controls)
opap.Report.SummaryDetails.InitResourcesSummary(controlToInfoMap)
// for f := range opap.PostureReport.FrameworkReports {
// // set exceptions
// exceptions.SetFrameworkExceptions(&opap.PostureReport.FrameworkReports[f], opap.Exceptions, cautils.ClusterName)
// // set counters
// reporthandling.SetUniqueResourcesCounter(&opap.PostureReport.FrameworkReports[f])
// // set default score
// // reporthandling.SetDefaultScore(&opap.PostureReport.FrameworkReports[f])
// }
}
func mapControlToInfo(mapResourceToControls map[string][]string, infoMap map[string]apis.StatusInfo, controlSummary reportsummary.ControlSummaries) map[string]apis.StatusInfo {
@@ -127,9 +117,11 @@ func getKubernetesObjects(k8sResources *cautils.K8SResources, allResources map[s
groupResources := k8sinterface.ResourceGroupToString(groups, version, resource)
for _, groupResource := range groupResources {
if k8sObj, ok := (*k8sResources)[groupResource]; ok {
if k8sObj == nil {
// logger.L().Debug("skipping", helpers.String("resource", groupResource))
}
/*
if k8sObj == nil {
// logger.L().Debug("skipping", helpers.String("resource", groupResource))
}
*/
for i := range k8sObj {
k8sObjects = append(k8sObjects, allResources[k8sObj[i]])
}

View File

@@ -2,10 +2,21 @@ package policyhandler
import (
"fmt"
"strings"
helpersv1 "github.com/kubescape/opa-utils/reporthandling/helpers/v1"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
cloudsupportv1 "github.com/kubescape/k8s-interface/cloudsupport/v1"
reportv2 "github.com/kubescape/opa-utils/reporthandling/v2"
"github.com/armosec/armoapi-go/armotypes"
"github.com/kubescape/k8s-interface/cloudsupport"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/pkg/resourcehandler"
"github.com/kubescape/opa-utils/reporthandling/apis"
)
// PolicyHandler -
@@ -49,6 +60,8 @@ func (policyHandler *PolicyHandler) CollectResources(policyIdentifier []cautils.
func (policyHandler *PolicyHandler) getResources(policyIdentifier []cautils.PolicyIdentifier, opaSessionObj *cautils.OPASessionObj, scanInfo *cautils.ScanInfo) error {
opaSessionObj.Report.ClusterAPIServerInfo = policyHandler.resourceHandler.GetClusterAPIServerInfo()
setCloudMetadata(opaSessionObj)
resourcesMap, allResources, ksResources, err := policyHandler.resourceHandler.GetResources(opaSessionObj, &policyIdentifier[0].Designators)
if err != nil {
return err
@@ -67,3 +80,70 @@ func getDesignator(policyIdentifier []cautils.PolicyIdentifier) *armotypes.Porta
}
return &armotypes.PortalDesignator{}
}
func setCloudMetadata(opaSessionObj *cautils.OPASessionObj) {
iCloudMetadata := getCloudMetadata(opaSessionObj, k8sinterface.GetConfig())
if iCloudMetadata == nil {
return
}
cloudMetadata := reportv2.NewCloudMetadata(iCloudMetadata)
opaSessionObj.Metadata.ContextMetadata.ClusterContextMetadata.CloudMetadata = cloudMetadata
opaSessionObj.Metadata.ClusterMetadata.CloudMetadata = cloudMetadata // deprecated - fallback
opaSessionObj.Report.ClusterCloudProvider = iCloudMetadata.Provider().ToString() // deprecated - fallback
}
// getCloudMetadata - get cloud metadata from kubeconfig or API server
// There are 3 options:
// 1. Get cloud provider from API server git version (EKS, GKE)
// 2. Get cloud provider from kubeconfig by parsing the cluster context (EKS, GKE)
// 3. Get cloud provider from kubeconfig by parsing the server URL (AKS)
func getCloudMetadata(opaSessionObj *cautils.OPASessionObj, config *clientcmdapi.Config) apis.ICloudParser {
if config == nil {
return nil
}
var provider string
// attempting to get cloud provider from API server git version
if opaSessionObj.Report.ClusterAPIServerInfo != nil {
provider = cloudsupport.GetCloudProvider(opaSessionObj.Report.ClusterAPIServerInfo.GitVersion)
}
if provider == cloudsupportv1.AKS || isAKS(config) {
return helpersv1.NewAKSMetadata(k8sinterface.GetContextName())
}
if provider == cloudsupportv1.EKS || isEKS(config) {
return helpersv1.NewEKSMetadata(k8sinterface.GetContextName())
}
if provider == cloudsupportv1.GKE || isGKE(config) {
return helpersv1.NewGKEMetadata(k8sinterface.GetContextName())
}
return nil
}
// check if the server is AKS. e.g. https://XXX.XX.XXX.azmk8s.io:443
func isAKS(config *clientcmdapi.Config) bool {
const serverIdentifierAKS = "azmk8s.io"
if cluster, ok := config.Clusters[config.CurrentContext]; ok {
return strings.Contains(cluster.Server, serverIdentifierAKS)
}
return false
}
// check if the server is EKS. e.g. arn:aws:eks:eu-west-1:xxx:cluster/xxxx
func isEKS(config *clientcmdapi.Config) bool {
if context, ok := config.Contexts[config.CurrentContext]; ok {
return strings.Contains(context.Cluster, cloudsupportv1.EKS)
}
return false
}
// check if the server is GKE. e.g. gke_xxx-xx-0000_us-central1-c_xxxx-1
func isGKE(config *clientcmdapi.Config) bool {
if context, ok := config.Contexts[config.CurrentContext]; ok {
return strings.Contains(context.Cluster, cloudsupportv1.GKE)
}
return false
}

View File

@@ -0,0 +1,201 @@
package policyhandler
import (
_ "embed"
"encoding/json"
"testing"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/opa-utils/reporthandling/apis"
helpersv1 "github.com/kubescape/opa-utils/reporthandling/helpers/v1"
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
"k8s.io/apimachinery/pkg/version"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)
var (
//go:embed kubeconfig_mock.json
kubeConfigMock string
)
func getKubeConfigMock() *clientcmdapi.Config {
kubeConfig := clientcmdapi.Config{}
if err := json.Unmarshal([]byte(kubeConfigMock), &kubeConfig); err != nil {
panic(err)
}
return &kubeConfig
}
func Test_getCloudMetadata(t *testing.T) {
type args struct {
context string
opaSessionObj *cautils.OPASessionObj
kubeConfig *clientcmdapi.Config
}
kubeConfig := getKubeConfigMock()
tests := []struct {
want apis.ICloudParser
args args
name string
}{
{
name: "Test_getCloudMetadata - GitVersion: GKE",
args: args{
opaSessionObj: &cautils.OPASessionObj{
Report: &reporthandlingv2.PostureReport{
ClusterAPIServerInfo: &version.Info{
GitVersion: "v1.25.4-gke.1600",
},
},
},
context: "",
kubeConfig: kubeConfig,
},
want: helpersv1.NewGKEMetadata(""),
},
{
name: "Test_getCloudMetadata_context_GKE",
args: args{
opaSessionObj: &cautils.OPASessionObj{
Report: &reporthandlingv2.PostureReport{
ClusterAPIServerInfo: nil,
},
},
kubeConfig: kubeConfig,
context: "gke_xxx-xx-0000_us-central1-c_xxxx-1",
},
want: helpersv1.NewGKEMetadata(""),
},
{
name: "Test_getCloudMetadata_context_EKS",
args: args{
opaSessionObj: &cautils.OPASessionObj{
Report: &reporthandlingv2.PostureReport{
ClusterAPIServerInfo: nil,
},
},
kubeConfig: kubeConfig,
context: "arn:aws:eks:eu-west-1:xxx:cluster/xxxx",
},
want: helpersv1.NewEKSMetadata(""),
},
{
name: "Test_getCloudMetadata_context_AKS",
args: args{
opaSessionObj: &cautils.OPASessionObj{
Report: &reporthandlingv2.PostureReport{
ClusterAPIServerInfo: nil,
},
},
kubeConfig: kubeConfig,
context: "xxxx-2",
},
want: helpersv1.NewAKSMetadata(""),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.args.kubeConfig.CurrentContext = tt.args.context
got := getCloudMetadata(tt.args.opaSessionObj, tt.args.kubeConfig)
if got == nil {
t.Errorf("getCloudMetadata() = %v, want %v", got, tt.want.Provider())
return
}
if got.Provider() != tt.want.Provider() {
t.Errorf("getCloudMetadata() = %v, want %v", got, tt.want)
}
})
}
}
func Test_isGKE(t *testing.T) {
type args struct {
config *clientcmdapi.Config
context string
}
tests := []struct {
name string
args args
want bool
}{
{
name: "Test_isGKE",
args: args{
config: getKubeConfigMock(),
context: "gke_xxx-xx-0000_us-central1-c_xxxx-1",
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// set context
tt.args.config.CurrentContext = tt.args.context
if got := isGKE(tt.args.config); got != tt.want {
t.Errorf("isGKE() = %v, want %v", got, tt.want)
}
})
}
}
func Test_isEKS(t *testing.T) {
type args struct {
config *clientcmdapi.Config
context string
}
tests := []struct {
name string
args args
want bool
}{
{
name: "Test_isEKS",
args: args{
config: getKubeConfigMock(),
context: "arn:aws:eks:eu-west-1:xxx:cluster/xxxx",
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// set context
tt.args.config.CurrentContext = tt.args.context
if got := isEKS(tt.args.config); got != tt.want {
t.Errorf("isEKS() = %v, want %v", got, tt.want)
}
})
}
}
func Test_isAKS(t *testing.T) {
type args struct {
config *clientcmdapi.Config
context string
}
tests := []struct {
name string
args args
want bool
}{
{
name: "Test_isAKS",
args: args{
config: getKubeConfigMock(),
context: "xxxx-2",
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// set context
tt.args.config.CurrentContext = tt.args.context
if got := isAKS(tt.args.config); got != tt.want {
t.Errorf("isAKS() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -41,6 +41,8 @@ func (policyHandler *PolicyHandler) getPolicies(policyIdentifier []cautils.Polic
controlsInputs, err := policyHandler.getters.ControlsInputsGetter.GetControlsInputs(cautils.ClusterName)
if err == nil {
policiesAndResources.RegoInputData.PostureControlInputs = controlsInputs
} else {
logger.L().Error(err.Error())
}
cautils.StopSpinner()
@@ -54,14 +56,16 @@ func (policyHandler *PolicyHandler) getScanPolicies(policyIdentifier []cautils.P
switch getScanKind(policyIdentifier) {
case apisv1.KindFramework: // Download frameworks
for _, rule := range policyIdentifier {
receivedFramework, err := policyHandler.getters.PolicyGetter.GetFramework(rule.Name)
receivedFramework, err := policyHandler.getters.PolicyGetter.GetFramework(rule.Identifier)
if err != nil {
return frameworks, policyDownloadError(err)
}
if err := validateFramework(receivedFramework); err != nil {
return frameworks, err
}
if receivedFramework != nil {
frameworks = append(frameworks, *receivedFramework)
cache := getter.GetDefaultPath(rule.Name + ".json")
cache := getter.GetDefaultPath(rule.Identifier + ".json")
if err := getter.SaveInFile(receivedFramework, cache); err != nil {
logger.L().Warning("failed to cache file", helpers.String("file", cache), helpers.Error(err))
}
@@ -71,15 +75,15 @@ func (policyHandler *PolicyHandler) getScanPolicies(policyIdentifier []cautils.P
f := reporthandling.Framework{}
var receivedControl *reporthandling.Control
var err error
for _, rule := range policyIdentifier {
receivedControl, err = policyHandler.getters.PolicyGetter.GetControl(rule.Name)
for _, policy := range policyIdentifier {
receivedControl, err = policyHandler.getters.PolicyGetter.GetControl(policy.Identifier)
if err != nil {
return frameworks, policyDownloadError(err)
}
if receivedControl != nil {
f.Controls = append(f.Controls, *receivedControl)
cache := getter.GetDefaultPath(rule.Name + ".json")
cache := getter.GetDefaultPath(policy.Identifier + ".json")
if err := getter.SaveInFile(receivedControl, cache); err != nil {
logger.L().Warning("failed to cache file", helpers.String("file", cache), helpers.Error(err))
}
@@ -96,7 +100,7 @@ func (policyHandler *PolicyHandler) getScanPolicies(policyIdentifier []cautils.P
func policyIdentifierToSlice(rules []cautils.PolicyIdentifier) []string {
s := []string{}
for i := range rules {
s = append(s, fmt.Sprintf("%s: %s", rules[i].Kind, rules[i].Name))
s = append(s, fmt.Sprintf("%s: %s", rules[i].Kind, rules[i].Identifier))
}
return s
}

View File

@@ -1,23 +0,0 @@
package policyhandler
// func TestGetPoliciesFromBackend(t *testing.T) {
// notification := reporthandling.PolicyNotification{
// Rules: []reporthandling.PolicyIdentifier{
// {
// Kind: reporthandling.KindFramework,
// Name: "mitretest",
// },
// },
// }
// // os.Setenv(cacli., "")
// ph := PolicyHandler{
// cacli: &cacli.Cacli{},
// }
// f, err := ph.GetPoliciesFromBackend(&notification)
// if err != nil {
// t.Error(err)
// }
// if len(f) == 0 {
// t.Errorf("empty")
// }
// }

View File

@@ -5,6 +5,7 @@ import (
"strings"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/kubescape/v2/core/cautils"
)
@@ -21,3 +22,16 @@ func policyDownloadError(err error) error {
}
return err
}
// validate the framework
func validateFramework(framework *reporthandling.Framework) error {
if framework == nil {
return fmt.Errorf("received empty framework")
}
// validate the controls are not empty
if len(framework.Controls) == 0 {
return fmt.Errorf("failed to load controls for framework: %s: empty list of controls", framework.Name)
}
return nil
}

View File

@@ -0,0 +1,48 @@
package policyhandler
import (
"testing"
"github.com/kubescape/opa-utils/reporthandling"
)
func Test_validateFramework(t *testing.T) {
type args struct {
framework *reporthandling.Framework
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "empty framework",
args: args{
framework: &reporthandling.Framework{
Controls: []reporthandling.Control{},
},
},
wantErr: true,
},
{
name: "none empty framework",
args: args{
framework: &reporthandling.Framework{
Controls: []reporthandling.Control{
{
ControlID: "c-0001",
},
},
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := validateFramework(tt.args.framework); (err != nil) != tt.wantErr {
t.Errorf("validateControls() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

View File

@@ -0,0 +1,82 @@
{
"preferences": {},
"clusters": {
"xxxx-2": {
"server": "https://XXX.XX.XXX.azmk8s.io:443"
},
"arn:aws:eks:eu-west-1:xxx:cluster/xxxx": {
"server": "https://XX.XX.eu-west-1.eks.amazonaws.com"
},
"xxxx-0": {
"server": "https://kubernetes.docker.XXX:6443"
},
"xxxx-1": {
"server": "https://127.0.0.1:49498"
},
"gke_xxx-xx-0000_us-central1-c_xxxx-1": {
"server": "https://0.0.0.0"
},
"microk8s-cluster": {
"server": "https://0.0.0.0:16443"
}
},
"users": {
"arn:aws:eks:eu-west-1:xxx:cluster/xxxx": {
"exec": {
"command": "aws",
"args": [
"--region",
"eu-west-1",
"eks",
"get-token",
"--cluster-name",
"xxx"
],
"env": null,
"apiVersion": "client.authentication.k8s.io/v1beta1",
"provideClusterInfo": false,
"Config": null,
"InteractiveMode": "IfAvailable",
"StdinUnavailable": false,
"StdinUnavailableMessage": ""
}
},
"gke_elated-pottery-xxx_us-central1-c_xxxx-1": {
"auth-provider": {
"name": "gcp",
"config": {
"cmd-args": "config config-helper --format=json",
"cmd-path": "/opt/homebrew/Caskroom/google-cloud-sdk/latest/google-cloud-sdk/bin/gcloud"
}
}
}
},
"contexts": {
"xxxx-2": {
"cluster": "xxxx-2",
"user": "clusterUser_MyResourceGroup_xxxx-2"
},
"arn:aws:eks:eu-west-1:xxx:cluster/xxxx": {
"cluster": "arn:aws:eks:eu-west-1:xxx:cluster/xxxx",
"user": "arn:aws:eks:eu-west-1:xxx:cluster/xxxx"
},
"docker-desktop": {
"cluster": "docker-desktop",
"user": "docker-desktop"
},
"xxxx-0": {
"cluster": "xxxx-0",
"user": "xxxx-0",
"namespace": "default"
},
"gke_xxx-xx-0000_us-central1-c_xxxx-1": {
"cluster": "gke_xxx-xx-0000_us-central1-c_xxxx-1",
"user": "gke_xxx-xx-0000_us-central1-c_xxxx-1"
},
"microk8s": {
"cluster": "microk8s-cluster",
"user": "admin"
}
},
"current-context": "xxxx-0"
}

View File

@@ -25,14 +25,17 @@ func (ksCivAdaptor *KSCivAdaptor) Login() error {
}
func (ksCivAdaptor *KSCivAdaptor) GetImagesVulnerabilities(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageVulnerabilityReport, error) {
resultList := make([]registryvulnerabilities.ContainerImageVulnerabilityReport, 0)
for _, imageID := range imageIDs {
for _, toPin := range imageIDs {
imageID := toPin
result, err := ksCivAdaptor.GetImageVulnerability(&imageID)
if err == nil {
resultList = append(resultList, *result)
} else {
if err != nil {
logger.L().Debug("failed to get image vulnerabilities", helpers.String("image", imageID.Tag), helpers.Error(err))
continue
}
resultList = append(resultList, *result)
}
return resultList, nil
}
@@ -51,7 +54,7 @@ func (ksCivAdaptor *KSCivAdaptor) GetImageVulnerability(imageID *registryvulnera
pageNumber := 1
request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"}
requestBody, _ := json.Marshal(request)
requestUrl := fmt.Sprintf("https://%s/api/v1/vulnerability/scanResultsDetails?customerGUID=%s", ksCivAdaptor.ksCloudAPI.GetApiURL(), ksCivAdaptor.ksCloudAPI.GetAccountID())
requestUrl := fmt.Sprintf("https://%s/api/v1/vulnerability/scanResultsDetails?customerGUID=%s", ksCivAdaptor.ksCloudAPI.GetCloudAPIURL(), ksCivAdaptor.ksCloudAPI.GetAccountID())
resp, err := ksCivAdaptor.ksCloudAPI.Post(requestUrl, map[string]string{"Content-Type": "application/json"}, requestBody)
if err != nil {

View File

@@ -14,7 +14,7 @@ func (armoCivAdaptor *KSCivAdaptor) getImageLastScanId(imageID *registryvulnerab
pageNumber := 1
request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"}
requestBody, _ := json.Marshal(request)
requestUrl := fmt.Sprintf("https://%s/api/v1/vulnerability/scanResultsSumSummary?customerGUID=%s", armoCivAdaptor.ksCloudAPI.GetApiURL(), armoCivAdaptor.ksCloudAPI.GetAccountID())
requestUrl := fmt.Sprintf("https://%s/api/v1/vulnerability/scanResultsSumSummary?customerGUID=%s", armoCivAdaptor.ksCloudAPI.GetCloudAPIURL(), armoCivAdaptor.ksCloudAPI.GetAccountID())
resp, err := armoCivAdaptor.ksCloudAPI.Post(requestUrl, map[string]string{"Content-Type": "application/json"}, requestBody)
if err != nil {

View File

@@ -22,7 +22,7 @@ type V2ListRequest struct {
// How to order (sort) the list, field name + sort order (asc/desc), like https://www.w3schools.com/sql/sql_orderby.asp
// Example: "timestamp:asc,severity:desc"
OrderBy string `json:"orderBy,omitempty"`
// Cursor to the next page of former requset. Not supported yet
// Cursor to the next page of former request. Not supported yet
// Cursor cannot be used with another parameters of this struct
Cursor string `json:"cursor,omitempty"`
// FieldsList allow us to return only subset of the source document fields

View File

@@ -4,12 +4,12 @@
### Layers
* Controls and Rules: that actual control logic implementation, the "tests" themselves. Implemented in rego
* OPA engine: the [OPA](https://github.com/open-policy-agent/opa) rego interpreter
* Rules processor: Kubescape component, it enumerates and runs the controls while also preparing the all the input data that the controls need for running
* Data sources: set of different modules providing data to the Rules processor so it can run the controls with them. Examples: Kubernetes objects, cloud vendor API objects and adding in this proposal the vulnerability infomration
* Controls and Rules: that actual control logic implementation, the "tests" themselves. Implemented in rego.
* OPA engine: the [OPA](https://github.com/open-policy-agent/opa) rego interpreter.
* Rules processor: Kubescape component, it enumerates and runs the controls while preparing all of the input data that the controls need for running.
* Data sources: set of different modules providing data to the Rules processor so it can run the controls with them. Examples: Kubernetes objects, cloud vendor API objects and adding in this proposal the vulnerability information.
* Cloud Image Vulnerability adaption interface: the subject of this proposal, it gives a common interface for different registry/vulnerability vendors to adapt to.
* CIV adaptors: specific implementation of the CIV interface, example Harbor adaption
* CIV adaptors: specific implementation of the CIV interface, example Harbor adaption.
```
-----------------------
| Controls/Rules (rego) |
@@ -88,7 +88,7 @@ type ContainerImageInformation struct {
type IContainerImageVulnerabilityAdaptor interface {
// Credentials are coming from user input (CLI or configuration file) and they are abstracted at string to string map level
// so and example use would be like registry: "simpledockerregistry:80" and credentials like {"username":"joedoe","password":"abcd1234"}
// so an example use would be like registry: "simpledockerregistry:80" and credentials like {"username":"joedoe","password":"abcd1234"}
Login(registry string, credentials map[string]string) error
// For "help" purposes
@@ -161,4 +161,4 @@ The rego results will be a combination of the k8s artifact and the list of relev
}
]
}
```
```

View File

@@ -0,0 +1,33 @@
# GCP Adaptor
### How we add gcp adaptor
As there can be possiblities of use of multiple registries we check for each adaptor if we have required credentias. For every adaptor having credentials we append the adaptor to the adaptors slice.
Particularly for gcp, we frstly bring the `gcpCloudAPI` from the connector. We still haven't created a proper function that initiats the gcpCloudAPI with projectId, credentialsPath, credentialsCheck fields. We check for `credentialsCheck` bool which is set true when we have credentials(to be set when initializing the gcpCloudAPI)
### How we fetch vulnerabilities for images
Step 1:
Get container analysis client
For this we needs credentials of the service account. Out of few approaches here we are using [JSON key file](https://cloud.google.com/container-registry/docs/advanced-authentication#json-key) for credentials and path to this file should be stored in `credentialsPath`
Step 2:
Do ListOccurrenceRequest
For this we need the `projectID` and the `resourceUrl`. ProjectID should be provided by the users and resourceUrl is processed imageTag that we get from kubescape resources
Step 3:
Get Occurrence iterator
We use context and the request from the ListOccurenceRequest to get the iterators
### How we convert the response to Vulnerabilities
Response from the iterator has two type of kinds i.e. Discovery and Vulnerabilties and both has differnent struct
### How can this adaptor be used by the user
To know about GCR service accounts follow https://cloud.google.com/container-registry/docs/gcr-service-account
export variables
`export KS_GCP_CREDENTIALS_PATH=<path to service account credentials file>`
`export KS_GCP_PROJECT_ID=<your project ID>`

View File

@@ -0,0 +1,24 @@
package v1
import (
"github.com/kubescape/kubescape/v2/core/cautils/getter"
)
type GCPAdaptor struct {
GCPCloudAPI *getter.GCPCloudAPI
}
type Mock struct {
Name string
Notename string
CvssScore float32
CreatedTime int64
UpdatedTime int64
Type string
ShortDescription string
AffectedCPEURI string
AffectedPackage string
FixAvailable bool
AffectedVersion string
FixedVersion string
}

View File

@@ -0,0 +1,91 @@
package v1
import (
"fmt"
containeranalysis "cloud.google.com/go/containeranalysis/apiv1"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v2/core/cautils/getter"
"github.com/kubescape/kubescape/v2/core/pkg/registryadaptors/registryvulnerabilities"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
grafeaspb "google.golang.org/genproto/googleapis/grafeas/v1"
)
func NewGCPAdaptor(GCPCloudAPI *getter.GCPCloudAPI) *GCPAdaptor {
return &GCPAdaptor{
GCPCloudAPI: GCPCloudAPI,
}
}
func (GCPAdaptor *GCPAdaptor) Login() error {
client, err := containeranalysis.NewClient(GCPAdaptor.GCPCloudAPI.GetContext(), option.WithCredentialsFile(GCPAdaptor.GCPCloudAPI.GetCredentialsPath()))
if err != nil {
return err
}
GCPAdaptor.GCPCloudAPI.SetClient(client)
return nil
}
func (GCPAdaptor *GCPAdaptor) GetImagesVulnerabilities(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageVulnerabilityReport, error) {
resultList := make([]registryvulnerabilities.ContainerImageVulnerabilityReport, 0)
for _, toPin := range imageIDs {
imageID := toPin
result, err := GCPAdaptor.GetImageVulnerability(&imageID)
if err != nil {
logger.L().Debug("failed to get image vulnerabilities", helpers.String("image", imageID.Tag), helpers.Error(err))
continue
}
resultList = append(resultList, *result)
}
return resultList, nil
}
func (GCPAdaptor *GCPAdaptor) GetImageVulnerability(imageID *registryvulnerabilities.ContainerImageIdentifier) (*registryvulnerabilities.ContainerImageVulnerabilityReport, error) {
resourceUrl := fmt.Sprintf("https://%s", imageID.Tag)
req := &grafeaspb.ListOccurrencesRequest{
Parent: fmt.Sprintf("projects/%s", GCPAdaptor.GCPCloudAPI.GetProjectID()),
Filter: fmt.Sprintf(`resourceUrl=%q`, resourceUrl),
}
it := GCPAdaptor.GCPCloudAPI.GetClient().GetGrafeasClient().ListOccurrences(GCPAdaptor.GCPCloudAPI.GetContext(), req)
occs := []*grafeaspb.Occurrence{}
var count int
for {
occ, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
return nil, err
}
occs = append(occs, occ)
count++
}
vulnerabilities := responseObjectToVulnerabilities(occs, count)
resultImageVulnerabilityReport := registryvulnerabilities.ContainerImageVulnerabilityReport{
ImageID: *imageID,
Vulnerabilities: vulnerabilities,
}
return &resultImageVulnerabilityReport, nil
}
func (GCPAdaptor *GCPAdaptor) DescribeAdaptor() string {
return "GCP image vulnerabilities scanner, docs: https://cloud.google.com/container-analysis/docs/container-analysis"
}
func (GCPAdaptor *GCPAdaptor) GetImagesInformation(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageInformation, error) {
// TODO
return []registryvulnerabilities.ContainerImageInformation{}, nil
}
func (GCPAdaptor *GCPAdaptor) GetImagesScanStatus(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageScanStatus, error) {
// TODO
return []registryvulnerabilities.ContainerImageScanStatus{}, nil
}

View File

@@ -0,0 +1,31 @@
package v1
import (
"testing"
"github.com/kubescape/kubescape/v2/core/pkg/registryadaptors/registryvulnerabilities"
"github.com/stretchr/testify/assert"
)
func TestSum(t *testing.T) {
var err error
var adaptor registryvulnerabilities.IContainerImageVulnerabilityAdaptor
adaptor, err = NewGCPAdaptorMock()
assert.NoError(t, err)
assert.NoError(t, adaptor.Login())
imageVulnerabilityReports, err := adaptor.GetImagesVulnerabilities([]registryvulnerabilities.ContainerImageIdentifier{{Tag: "gcr.io/myproject/nginx@sha256:1XXXXX"}, {Tag: "gcr.io/myproject/nginx@sha256:2XXXXX"}})
assert.NoError(t, err)
for i := range imageVulnerabilityReports {
var length int
if i == 0 {
length = 5
} else if i == 1 {
length = 3
}
assert.Equal(t, length, len(imageVulnerabilityReports[i].Vulnerabilities))
}
}

View File

@@ -0,0 +1,186 @@
package v1
import (
"github.com/kubescape/kubescape/v2/core/pkg/registryadaptors/registryvulnerabilities"
grafeaspb "google.golang.org/genproto/googleapis/grafeas/v1"
"google.golang.org/protobuf/types/known/timestamppb"
)
type GCPAdaptorMock struct {
resultList []registryvulnerabilities.ContainerImageVulnerabilityReport
}
func NewGCPAdaptorMock() (*GCPAdaptorMock, error) {
return &GCPAdaptorMock{}, nil
}
func (GCPAdaptorMock *GCPAdaptorMock) Login() error {
return nil
}
func (GCPAdaptorMock *GCPAdaptorMock) GetImagesVulnerabilities(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageVulnerabilityReport, error) {
resultList := make([]registryvulnerabilities.ContainerImageVulnerabilityReport, 0)
for _, toPin := range imageIDs {
imageID := toPin
result, err := GCPAdaptorMock.GetImageVulnerability(&imageID)
if err != nil {
return nil, err
}
resultList = append(resultList, *result)
return resultList, nil //nolint:staticcheck // we return at once and shorten the mocked result
}
GCPAdaptorMock.resultList = resultList
return GCPAdaptorMock.resultList, nil
}
func (GCPAdaptorMock *GCPAdaptorMock) GetImageVulnerability(imageID *registryvulnerabilities.ContainerImageIdentifier) (*registryvulnerabilities.ContainerImageVulnerabilityReport, error) {
vulnerability := []*grafeaspb.Occurrence_Vulnerability{}
occurrence := []*grafeaspb.Occurrence{}
arr := GetMockData()
for i, _ := range arr {
if imageID.Tag == "gcr.io/myproject/nginx@sha256:2XXXXX" && i == 4 {
break
}
vulnerability = append(vulnerability, &grafeaspb.Occurrence_Vulnerability{
Vulnerability: &grafeaspb.VulnerabilityOccurrence{
Type: arr[i].Type,
CvssScore: arr[i].CvssScore,
ShortDescription: arr[i].ShortDescription,
PackageIssue: []*grafeaspb.VulnerabilityOccurrence_PackageIssue{
{
FixedVersion: &grafeaspb.Version{
FullName: arr[i].FixedVersion,
},
AffectedVersion: &grafeaspb.Version{
FullName: arr[i].AffectedVersion,
},
AffectedCpeUri: arr[i].AffectedCPEURI,
AffectedPackage: arr[i].AffectedPackage,
},
},
FixAvailable: arr[i].FixAvailable,
},
})
occurrence = append(occurrence, &grafeaspb.Occurrence{
Name: arr[i].Name,
Kind: grafeaspb.NoteKind_ATTESTATION,
NoteName: arr[i].Notename,
CreateTime: &timestamppb.Timestamp{
Seconds: arr[i].CreatedTime,
},
UpdateTime: &timestamppb.Timestamp{
Seconds: arr[i].UpdatedTime,
},
Details: vulnerability[i],
})
}
vulnerabilities := responseObjectToVulnerabilities(occurrence, 5)
resultImageVulnerabilityReport := registryvulnerabilities.ContainerImageVulnerabilityReport{
ImageID: *imageID,
Vulnerabilities: vulnerabilities,
}
return &resultImageVulnerabilityReport, nil
}
func (GCPAdaptorMock *GCPAdaptorMock) DescribeAdaptor() string {
// TODO
return ""
}
func (GCPAdaptorMock *GCPAdaptorMock) GetImagesInformation(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageInformation, error) {
// TODO
return []registryvulnerabilities.ContainerImageInformation{}, nil
}
func (GCPAdaptorMock *GCPAdaptorMock) GetImagesScanStatus(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageScanStatus, error) {
// TODO
return []registryvulnerabilities.ContainerImageScanStatus{}, nil
}
//==============================================================================================================================
//==============================================================================================================================
//==============================================================================================================================
func GetMockData() []Mock {
arr := []Mock{
{
Name: "projects/stable-furnace-356005/occurrences/41fd9fec-6fab-4531-a4ee-e7b97d518554",
Notename: "projects/goog-vulnz/notes/CVE-2009-4487",
CvssScore: 6.8,
CreatedTime: 1661061853,
UpdatedTime: 1661061853,
Type: "OS",
ShortDescription: "CVE-2009-4487",
AffectedCPEURI: "cpe:/o:debian:debian_linux:11",
AffectedPackage: "nginx",
FixAvailable: true,
AffectedVersion: "1.23.1-1~bullseye",
FixedVersion: "",
},
{
Name: "projects/stable-furnace-356005/occurrences/b28fa29f-5c2b-45c7-9727-2f1f02ed1957",
Notename: "projects/goog-vulnz/notes/CVE-2017-17740",
CvssScore: 2.3,
CreatedTime: 3237628,
UpdatedTime: 5989893,
Type: "OS",
ShortDescription: "CVE-2017-17740",
AffectedCPEURI: "cpe:/o:debian:debian_linux:11",
AffectedPackage: "openldap",
FixAvailable: false,
AffectedVersion: "1.3.5",
FixedVersion: "1.3.5",
},
{
Name: "projects/stable-furnace-356005/occurrences/b28fa29f-5c2b-45c7-9727-2f1f02ed1957",
Notename: "projects/goog-vulnz/notes/CVE-2017-17740",
CvssScore: 2.3,
CreatedTime: 3237628,
UpdatedTime: 5989893,
Type: "OS",
ShortDescription: "CVE-2017-17740",
AffectedCPEURI: "cpe:/o:debian:debian_linux:11",
AffectedPackage: "openldap",
FixAvailable: false,
AffectedVersion: "1.3.5",
FixedVersion: "1.3.5",
},
{
Name: "projects/stable-furnace-356005/occurrences/b28fa29f-5c2b-45c7-9727-2f1f02ed1957",
Notename: "projects/goog-vulnz/notes/CVE-2017-17740",
CvssScore: 2.3,
CreatedTime: 3237628,
UpdatedTime: 5989893,
Type: "OS",
ShortDescription: "CVE-2017-17740",
AffectedCPEURI: "cpe:/o:debian:debian_linux:11",
AffectedPackage: "openldap",
FixAvailable: false,
AffectedVersion: "1.3.5",
FixedVersion: "1.3.5",
},
{
Name: "projects/stable-furnace-356005/occurrences/b28fa29f-5c2b-45c7-9727-2f1f02ed1957",
Notename: "projects/goog-vulnz/notes/CVE-2017-17740",
CvssScore: 2.3,
CreatedTime: 3237628,
UpdatedTime: 5989893,
Type: "OS",
ShortDescription: "CVE-2017-17740",
AffectedCPEURI: "cpe:/o:debian:debian_linux:11",
AffectedPackage: "openldap",
FixAvailable: false,
AffectedVersion: "1.3.5",
FixedVersion: "1.3.5",
},
}
return arr
}

View File

@@ -0,0 +1,36 @@
package v1
import (
"github.com/kubescape/kubescape/v2/core/pkg/registryadaptors/registryvulnerabilities"
grafeaspb "google.golang.org/genproto/googleapis/grafeas/v1"
)
func responseObjectToVulnerabilities(vulnerabilityList []*grafeaspb.Occurrence, count int) []registryvulnerabilities.Vulnerability {
vulnerabilities := make([]registryvulnerabilities.Vulnerability, count)
for i, vulnerabilityEntry := range vulnerabilityList {
if vulnerabilityEntry.GetKind().String() != "DISCOVERY" {
vulnerabilities[i].Name = vulnerabilityEntry.Name
vulnerabilities[i].NoteName = vulnerabilityEntry.NoteName
vulnerabilities[i].CreateTime = vulnerabilityEntry.CreateTime.AsTime()
vulnerabilities[i].UpdateTime = vulnerabilityEntry.UpdateTime.AsTime()
vulnerabilities[i].CVSS = vulnerabilityEntry.GetVulnerability().CvssScore
vulnerabilities[i].AffectedCPEURI = vulnerabilityEntry.GetVulnerability().PackageIssue[0].AffectedCpeUri
vulnerabilities[i].AffectedPackage = vulnerabilityEntry.GetVulnerability().PackageIssue[0].AffectedPackage
vulnerabilities[i].AffectedVersion = vulnerabilityEntry.GetVulnerability().PackageIssue[0].AffectedVersion.FullName
vulnerabilities[i].FixedVersion = vulnerabilityEntry.GetVulnerability().PackageIssue[0].FixedVersion.FullName
vulnerabilities[i].FixedCPEURI = vulnerabilityEntry.GetVulnerability().PackageIssue[0].FixedCpeUri
vulnerabilities[i].FixedPackege = vulnerabilityEntry.GetVulnerability().PackageIssue[0].FixedPackage
vulnerabilities[i].FixAvailablePackage = vulnerabilityEntry.GetVulnerability().PackageIssue[0].GetFixAvailable()
vulnerabilities[i].PackageType = vulnerabilityEntry.GetVulnerability().PackageIssue[0].PackageType
vulnerabilities[i].EffectiveSeverityPackage = vulnerabilityEntry.GetVulnerability().PackageIssue[0].EffectiveSeverity.String()
vulnerabilities[i].AffectedPackage = vulnerabilityEntry.GetVulnerability().PackageIssue[0].AffectedPackage
vulnerabilities[i].Severity = vulnerabilityEntry.GetVulnerability().Severity.Enum().String()
vulnerabilities[i].ShortDescription = vulnerabilityEntry.GetVulnerability().ShortDescription
vulnerabilities[i].LongDescription = vulnerabilityEntry.GetVulnerability().LongDescription
} else {
vulnerabilities[i].Description = vulnerabilityEntry.GetDiscovery().String()
}
}
return vulnerabilities
}

View File

@@ -28,19 +28,36 @@ type Categories struct {
}
type Vulnerability struct {
Name string `json:"name"`
RelatedPackageName string `json:"packageName"`
PackageVersion string `json:"packageVersion"`
Link string `json:"link"`
Description string `json:"description"`
Severity string `json:"severity"`
Metadata interface{} `json:"metadata"`
Fixes []FixedIn `json:"fixedIn"`
Relevancy string `json:"relevant"` // use the related enum
UrgentCount int `json:"urgent"`
NeglectedCount int `json:"neglected"`
HealthStatus string `json:"healthStatus"`
Categories Categories `json:"categories"`
Name string `json:"name"`
RelatedPackageName string `json:"packageName"`
PackageVersion string `json:"packageVersion"`
Link string `json:"link"`
Description string `json:"description"`
Severity string `json:"severity"`
Metadata interface{} `json:"metadata"`
Fixes []FixedIn `json:"fixedIn"`
Relevancy string `json:"relevant"` // use the related enum
UrgentCount int `json:"urgent"`
NeglectedCount int `json:"neglected"`
HealthStatus string `json:"healthStatus"`
Categories Categories `json:"categories"`
NoteName string `json:",omitempty"`
CreateTime time.Time `json:",omitempty"`
UpdateTime time.Time `json:",omitempty"` // Vulnerablity started
CVSS float32 `json:",omitempty"` // other cvss versions are available
AffectedCPEURI string `json:",omitempty"` // Package issue
AffectedPackage string `json:",omitempty"`
AffectedVersion string `json:",omitempty"`
FixedVersion string `json:",omitempty"`
FixedCPEURI string `json:",omitempty"`
FixedPackege string `json:",omitempty"`
FixAvailablePackage bool `json:",omitempty"`
PackageType string `json:",omitempty"`
EffectiveSeverityPackage string `json:",omitempty"`
ShortDescription string `json:",omitempty"` // Package issue ends
LongDescription string `json:",omitempty"`
EffectiveSeverity string `json:",omitempty"`
FixAvailable bool `json:",omitempty"`
}
type ContainerImageVulnerabilityReport struct {

View File

@@ -1,11 +1,11 @@
package registryvulnerabilities
type IContainerImageVulnerabilityAdaptor interface {
// Credentials are coming from user input (CLI or configuration file) and they are abstracted at string to string map level
// Login Credentials are coming from user input (CLI or configuration file) and they are abstracted at string to string map level
// so and example use would be like registry: "simpledockerregistry:80" and credentials like {"username":"joedoe","password":"abcd1234"}
Login() error
// For "help" purposes
// DescribeAdaptor For "help" purposes
DescribeAdaptor() string
GetImagesScanStatus(imageIDs []ContainerImageIdentifier) ([]ContainerImageScanStatus, error)

View File

@@ -10,6 +10,7 @@ import (
type IFieldSelector interface {
GetNamespacesSelectors(*schema.GroupVersionResource) []string
GetClusterScope(*schema.GroupVersionResource) bool
}
type EmptySelector struct {
@@ -19,6 +20,10 @@ func (es *EmptySelector) GetNamespacesSelectors(resource *schema.GroupVersionRes
return []string{""} //
}
func (es *EmptySelector) GetClusterScope(*schema.GroupVersionResource) bool {
return true
}
type ExcludeSelector struct {
namespace string
}
@@ -27,6 +32,14 @@ func NewExcludeSelector(ns string) *ExcludeSelector {
return &ExcludeSelector{namespace: ns}
}
func (es *ExcludeSelector) GetClusterScope(resource *schema.GroupVersionResource) bool {
// for selector, 'namespace' is in Namespaced scope
if resource.Resource == "namespaces" {
return true
}
return false
}
type IncludeSelector struct {
namespace string
}
@@ -34,6 +47,15 @@ type IncludeSelector struct {
func NewIncludeSelector(ns string) *IncludeSelector {
return &IncludeSelector{namespace: ns}
}
func (is *IncludeSelector) GetClusterScope(resource *schema.GroupVersionResource) bool {
// for selector, 'namespace' is in Namespaced scope
if resource.Resource == "namespaces" {
return true
}
return false
}
func (es *ExcludeSelector) GetNamespacesSelectors(resource *schema.GroupVersionResource) []string {
fieldSelectors := ""
for _, n := range strings.Split(es.namespace, ",") {

View File

@@ -76,9 +76,10 @@ func (fileHandler *FileResourceHandler) GetResources(sessionObj *cautils.OPASess
}
if err := fileHandler.registryAdaptors.collectImagesVulnerabilities(k8sResources, allResources, ksResources); err != nil {
logger.L().Warning("failed to collect images vulnerabilities", helpers.Error(err))
}
// Should Kubescape scan image related controls when scanning local files?
// if err := fileHandler.registryAdaptors.collectImagesVulnerabilities(k8sResources, allResources, ksResources); err != nil {
// logger.L().Warning("failed to collect images vulnerabilities", helpers.Error(err))
// }
cautils.StopSpinner()
logger.L().Success("Done accessing local objects")
@@ -103,6 +104,8 @@ func getResourcesFromPath(path string) (map[string]reporthandling.Source, []work
gitRepo, err := cautils.NewLocalGitRepository(path)
if err == nil && gitRepo != nil {
repoRoot, _ = gitRepo.GetRootDir()
} else {
repoRoot, _ = filepath.Abs(path)
}
// load resource from local file system
@@ -141,7 +144,7 @@ func getResourcesFromPath(path string) (map[string]reporthandling.Source, []work
}
workloadSource := reporthandling.Source{
RelativePath: source,
RelativePath: relSource,
FileType: filetype,
LastCommit: lastCommit,
}
@@ -196,6 +199,44 @@ func getResourcesFromPath(path string) (map[string]reporthandling.Source, []work
logger.L().Debug("helm templates found in local storage", helpers.Int("helmTemplates", len(helmSourceToWorkloads)), helpers.Int("workloads", len(workloads)))
}
// Load resources from Kustomize directory
kustomizeSourceToWorkloads, kustomizeDirectoryName := cautils.LoadResourcesFromKustomizeDirectory(path)
// update workloads and workloadIDToSource with workloads from Kustomize Directory
for source, ws := range kustomizeSourceToWorkloads {
workloads = append(workloads, ws...)
relSource, err := filepath.Rel(repoRoot, source)
if err == nil {
source = relSource
}
var lastCommit reporthandling.LastCommit
if gitRepo != nil {
commitInfo, _ := gitRepo.GetFileLastCommit(source)
if commitInfo != nil {
lastCommit = reporthandling.LastCommit{
Hash: commitInfo.SHA,
Date: commitInfo.Author.Date,
CommitterName: commitInfo.Author.Name,
CommitterEmail: commitInfo.Author.Email,
Message: commitInfo.Message,
}
}
}
workloadSource := reporthandling.Source{
RelativePath: source,
FileType: reporthandling.SourceTypeKustomizeDirectory,
KustomizeDirectoryName: kustomizeDirectoryName,
LastCommit: lastCommit,
}
for i := range ws {
workloadIDToSource[ws[i].GetID()] = workloadSource
}
}
return workloadIDToSource, workloads, nil
}

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"path/filepath"
giturl "github.com/armosec/go-git-url"
giturl "github.com/kubescape/go-git-url"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/k8sinterface"

View File

@@ -79,22 +79,30 @@ func (k8sHandler *K8sResourceHandler) GetResources(sessionObj *cautils.OPASessio
sessionObj.SetNumberOfWorkerNodes(numberOfWorkerNodes)
}
cautils.StopSpinner()
logger.L().Success("Accessed to Kubernetes objects")
imgVulnResources := cautils.MapImageVulnResources(ksResourceMap)
// check that controls use image vulnerability resources
if len(imgVulnResources) > 0 {
logger.L().Info("Requesting images vulnerabilities results")
cautils.StartSpinner()
if err := k8sHandler.registryAdaptors.collectImagesVulnerabilities(k8sResourcesMap, allResources, ksResourceMap); err != nil {
logger.L().Warning("failed to collect image vulnerabilities", helpers.Error(err))
cautils.SetInfoMapForResources(fmt.Sprintf("failed to pull image scanning data: %s. for more information: https://hub.armosec.io/docs/configuration-of-image-vulnerabilities", err.Error()), imgVulnResources, sessionObj.InfoMap)
} else {
if isEmptyImgVulns(*ksResourceMap) {
cautils.SetInfoMapForResources("image scanning is not configured. for more information: https://hub.armosec.io/docs/configuration-of-image-vulnerabilities", imgVulnResources, sessionObj.InfoMap)
}
}
cautils.StopSpinner()
logger.L().Success("Requested images vulnerabilities results")
}
hostResources := cautils.MapHostResources(ksResourceMap)
// check that controls use host sensor resources
if len(hostResources) > 0 {
logger.L().Info("Requesting Host scanner data")
cautils.StartSpinner()
if sessionObj.Metadata.ScanMetadata.HostScanner {
infoMap, err := k8sHandler.collectHostResources(allResources, ksResourceMap)
if err != nil {
@@ -108,6 +116,8 @@ func (k8sHandler *K8sResourceHandler) GetResources(sessionObj *cautils.OPASessio
sessionObj.InfoMap = infoMap
}
}
cautils.StopSpinner()
logger.L().Success("Requested Host scanner data")
} else {
cautils.SetInfoMapForResources("enable-host-scan flag not used. For more information: https://hub.armosec.io/docs/host-sensor", hostResources, sessionObj.InfoMap)
}
@@ -133,14 +143,29 @@ func (k8sHandler *K8sResourceHandler) GetResources(sessionObj *cautils.OPASessio
sessionObj.Metadata.ContextMetadata.ClusterContextMetadata.CloudProvider = provider
}
}
// api server info resource
err = k8sHandler.collectAPIServerInfoResource(allResources, ksResourceMap)
if err != nil {
logger.L().Warning("failed to collect api server info resource", helpers.Error(err))
}
}
cautils.StopSpinner()
logger.L().Success("Accessed to Kubernetes objects")
return k8sResourcesMap, allResources, ksResourceMap, nil
}
func (k8sHandler *K8sResourceHandler) collectAPIServerInfoResource(allResources map[string]workloadinterface.IMetadata, ksResourceMap *cautils.KSResources) error {
clusterAPIServerInfo, err := k8sHandler.k8s.DiscoveryClient.ServerVersion()
if err != nil {
return err
}
resource := cloudsupport.NewApiServerVersionInfo(clusterAPIServerInfo)
allResources[resource.GetID()] = resource
(*ksResourceMap)[fmt.Sprintf("%s/%s", resource.GetApiVersion(), resource.GetKind())] = []string{resource.GetID()}
return nil
}
func (k8sHandler *K8sResourceHandler) GetClusterAPIServerInfo() *version.Info {
clusterAPIServerInfo, err := k8sHandler.k8s.DiscoveryClient.ServerVersion()
if err != nil {
@@ -218,10 +243,14 @@ func (k8sHandler *K8sResourceHandler) pullSingleResource(resource *schema.GroupV
// set dynamic object
var clientResource dynamic.ResourceInterface
if namespace != "" && k8sinterface.IsNamespaceScope(resource) {
clientResource = k8sHandler.k8s.DynamicClient.Resource(*resource).Namespace(namespace)
} else {
if namespace != "" {
clientResource = k8sHandler.k8s.DynamicClient.Resource(*resource)
} else if k8sinterface.IsNamespaceScope(resource) {
clientResource = k8sHandler.k8s.DynamicClient.Resource(*resource).Namespace(namespace)
} else if k8sHandler.fieldSelector.GetClusterScope(resource) {
clientResource = k8sHandler.k8s.DynamicClient.Resource(*resource)
} else {
continue
}
// list resources

View File

@@ -22,6 +22,8 @@ var (
ImageVulnerabilities = "ImageVulnerabilities"
KubeletInfo = "KubeletInfo"
KubeProxyInfo = "KubeProxyInfo"
ControlPlaneInfo = "ControlPlaneInfo"
CloudProviderInfo = "CloudProviderInfo"
MapResourceToApiGroup = map[string]string{
KubeletConfiguration: "hostdata.kubescape.cloud/v1beta0",
@@ -33,6 +35,8 @@ var (
LinuxKernelVariables: "hostdata.kubescape.cloud/v1beta0",
KubeletInfo: "hostdata.kubescape.cloud/v1beta0",
KubeProxyInfo: "hostdata.kubescape.cloud/v1beta0",
ControlPlaneInfo: "hostdata.kubescape.cloud/v1beta0",
CloudProviderInfo: "hostdata.kubescape.cloud/v1beta0",
}
MapResourceToApiGroupVuln = map[string][]string{
ImageVulnerabilities: {"armo.vuln.images/v1", "image.vulnscan.com/v1"}}

View File

@@ -9,6 +9,7 @@ import (
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/cautils/getter"
armosecadaptorv1 "github.com/kubescape/kubescape/v2/core/pkg/registryadaptors/armosec/v1"
gcpadaptorv1 "github.com/kubescape/kubescape/v2/core/pkg/registryadaptors/gcp/v1"
"github.com/kubescape/kubescape/v2/core/pkg/registryadaptors/registryvulnerabilities"
"github.com/kubescape/opa-utils/shared"
@@ -158,5 +159,12 @@ func listAdaptores() ([]registryvulnerabilities.IContainerImageVulnerabilityAdap
}
}
gcpCloudAPI := getter.GetGlobalGCPCloudAPIConnector()
if gcpCloudAPI != nil {
if gcpCloudAPI.GetCredentialsCheck() {
adaptors = append(adaptors, gcpadaptorv1.NewGCPAdaptor(getter.GetGlobalGCPCloudAPIConnector()))
}
}
return adaptors, nil
}

View File

@@ -6,21 +6,21 @@ import (
nethttp "net/http"
"os"
giturl "github.com/armosec/go-git-url"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/http"
giturl "github.com/kubescape/go-git-url"
)
// To Check if the given repository is Public(No Authentication needed), send a HTTP GET request to the URL
// If response code is 200, the repository is Public.
func isGitRepoPublic(URL string) bool {
resp, err := nethttp.Get(URL)
func isGitRepoPublic(u string) bool {
resp, err := nethttp.Get(u) //nolint:gosec
if err != nil {
return false
}
// if the status code is 200, our get request is successful.
// It only happens when the repository is public.
if resp.StatusCode == 200 {
@@ -38,6 +38,17 @@ func isGitTokenPresent(gitURL giturl.IGitAPI) bool {
return true
}
// Get the error message according to the provider
func getProviderError(gitURL giturl.IGitAPI) error {
switch gitURL.GetProvider(){
case "github":
return fmt.Errorf("%w", errors.New("GITHUB_TOKEN is not present"))
case "gitlab":
return fmt.Errorf("%w", errors.New("GITLAB_TOKEN is not present"))
}
return fmt.Errorf("%w", errors.New("unable to find the host name"))
}
// cloneRepo clones a repository to a local temporary directory and returns the directory
func cloneRepo(gitURL giturl.IGitAPI) (string, error) {
@@ -60,9 +71,9 @@ func cloneRepo(gitURL giturl.IGitAPI) (string, error) {
auth = nil
} else {
// Return Error if the GITHUB_TOKEN is not present
// Return Error if the AUTH_TOKEN is not present
if isGitTokenPresent := isGitTokenPresent(gitURL); !isGitTokenPresent {
return "", fmt.Errorf("%w", errors.New("GITHUB_TOKEN is not present"))
return "", getProviderError(gitURL)
}
auth = &http.BasicAuth{
Username: "anything Except Empty String",

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