Compare commits

...

79 Commits
dev ... v2.1.1

Author SHA1 Message Date
David Wertenteil
e8253d4193 Renaming the files (#1079)
* Renaming the files

Signed-off-by: David Wertenteil <dwertent@armosec.io>

* Removing un-used code

Signed-off-by: David Wertenteil <dwertent@armosec.io>

---------

Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 19:01:53 +02:00
David Wertenteil
8b8fe92072 Merge pull request #1078 from dwertent/master
Prepare for release
2023-02-06 16:31:54 +02:00
David Wertenteil
bcf9a10131 Remove host scanner warning message
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:13 +02:00
David Wertenteil
b6d21ffd01 removed comments
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:13 +02:00
David Wertenteil
086144c3da JSON version default v2
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:13 +02:00
David Wertenteil
a45ee8ed42 update compliance url
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:13 +02:00
David Wertenteil
129b0f3ee3 fix dockerbuild paths
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:13 +02:00
David Wertenteil
01a8a34637 list files for upload
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:13 +02:00
David Wertenteil
bcb6c06e73 update artifacts location
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:13 +02:00
David Wertenteil
da03022b94 Change artifacts dir
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:13 +02:00
David Wertenteil
17f313177c release with new tag
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:13 +02:00
David Wertenteil
a81353aa15 Leave release var empty for tests
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:12 +02:00
David Wertenteil
e0b82edd1e rename download dir
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:12 +02:00
David Wertenteil
b675d09fe2 rename build dir
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:12 +02:00
David Wertenteil
29b9448dc0 leaving release empty when testing PRs
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:12 +02:00
David Wertenteil
e1020dd1a6 saving file under build dir
Signed-off-by: David Wertenteil <dwertent@armosec.io>
2023-02-06 15:49:12 +02:00
David Wertenteil
9b734b1fa4 Merge pull request #1060 from kubescape/CIS-EKS-support
CIS EKS Support
2023-02-05 17:01:21 +02:00
YiscahLevySilas1
9f97f91f32 add context
Signed-off-by: YiscahLevySilas1 <yiscahls@armosec.io>
2023-02-05 12:03:10 +02:00
YiscahLevySilas1
c6eff8cbaa minor change 2023-02-05 11:54:21 +02:00
David Wertenteil
af9df548d6 Merge branch 'master' into CIS-EKS-support 2023-02-05 09:43:41 +02:00
David Wertenteil
786f3e6b41 Merge pull request #1074 from kubescape/add-otel-client
Add otel client
2023-02-05 09:35:54 +02:00
David Wertenteil
904751e117 Merge pull request #1075 from kubescape/revert-1073-new-wf-trigger
Revert "change wf trigger"
2023-02-02 16:40:09 +02:00
David Wertenteil
ce43661307 Revert "change wf trigger" 2023-02-02 16:39:16 +02:00
David Wertenteil
cd4b601557 Merge pull request #1044 from matthyx/otel
add otel with uptrace client
2023-02-02 16:37:18 +02:00
David Wertenteil
f34f1449db Merge pull request #1073 from kubescape/new-wf-trigger
change wf trigger
2023-02-01 21:16:40 +02:00
Matan Shkalim
16c74a228f change wf trigger
Signed-off-by: Matan Shkalim <shekel8@gmail.com>
2023-02-01 16:24:11 +00:00
David Wertenteil
ad01f01a6c Merge branch 'master' into otel 2023-01-31 23:09:02 +02:00
David Wertenteil
da0b9883ea Merge to master - PR number: 1070 2023-01-31 15:19:25 +02:00
Matan Shkalim
ac60dbed5e add new workflows + action
Signed-off-by: Matan Shkalim <shekel8@gmail.com>
2023-01-31 13:01:22 +00:00
Matthias Bertschy
3a90682c9e remove otel from CLI part
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2023-01-31 10:03:56 +01:00
Matthias Bertschy
160ac0db7c add otel with uptrace client
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2023-01-31 08:06:33 +01:00
David Wertenteil
7ec4fb75e3 Merge pull request #1067 from matthyx/fix-dockerfile
fix ksserver name in Dockerfile
2023-01-31 08:28:26 +02:00
Matthias Bertschy
7e88357940 fix ksserver name in Dockerfile
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2023-01-31 07:26:18 +01:00
YiscahLevySilas1
1ac808a935 Merge pull request #1063 from YiscahLevySilas1/dev
add unit test
2023-01-30 11:06:31 +02:00
yiscah
45fcc59b5f add unit test
Signed-off-by: yiscah <yiscahls@armosec.io>
2023-01-30 11:03:10 +02:00
YiscahLevySilas1
7875c14adf Merge pull request #1062 from YiscahLevySilas1/dev
update k8s-interface - fix in get region
2023-01-29 20:44:45 +02:00
yiscah
5cddba77aa update k8s-interface - fix in get region
Signed-off-by: yiscah <yiscahls@armosec.io>
2023-01-29 20:42:44 +02:00
YiscahLevySilas1
f3058bf168 Merge pull request #1061 from YiscahLevySilas1/dev
update k8s-interface - fix in get region
2023-01-29 20:34:46 +02:00
yiscah
0d1b92c2ee update k8s-interface - fix in get region
Signed-off-by: yiscah <yiscahls@armosec.io>
2023-01-29 20:32:12 +02:00
yiscah
8de308a5b1 go mod tidy 2023-01-29 09:44:28 +02:00
YiscahLevySilas1
a7f810f0d1 Merge pull request #1043 from YiscahLevySilas1/dev
Get cloud provider resources
2023-01-29 08:59:01 +02:00
Matthias Bertschy
e4e3071f5f Merge pull request #1057 from slashben/master
Progress bar in CLI
2023-01-27 17:52:08 +01:00
yiscah
9a7e61edd1 add cloud resource ListEntitiesForPolicies 2023-01-27 13:42:15 +02:00
Ben
5368330df9 updating httphandler
Signed-off-by: Ben <ben@armosec.io>
2023-01-26 09:40:36 +02:00
Ben
5e6a4cfb3f Checking for nil interface
Signed-off-by: Ben <ben@armosec.io>
2023-01-25 16:23:03 +02:00
Matthias Bertschy
052773b0dc Merge pull request #1022 from matthyx/krew
add kubectl plugin with krew
2023-01-25 08:44:28 +01:00
Matthias Bertschy
d462224b7a add kubectl plugin with krew
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
2023-01-25 08:07:31 +01:00
yiscah
de1d8a9d86 improve cloud resources getters 2023-01-24 17:18:39 +02:00
Ben
d346b05b76 Fixing system test after API change
Signed-off-by: Ben <ben@armosec.io>
2023-01-24 14:13:19 +02:00
Ben
a3a61d65e9 Limiting the size of the name of controls in the pretty print of the tabel
Signed-off-by: Ben <ben@armosec.io>
2023-01-24 11:55:08 +02:00
Ben
606b0e77ca fixing progress to work on stderr
Signed-off-by: Ben <ben@armosec.io>
2023-01-24 11:55:08 +02:00
Ben
2a82d6cd21 Implementing progress bar for control processing
Signed-off-by: Ben <ben@armosec.io>
2023-01-24 11:55:08 +02:00
yiscah
530ffde50d Merge branch 'master' of https://github.com/kubescape/kubescape into dev 2023-01-23 18:52:43 +02:00
yiscah
7cf23e9730 Merge branch 'dev' of https://github.com/kubescape/kubescape into dev 2023-01-23 18:35:27 +02:00
David Wertenteil
8d5a8f8e22 Merge pull request #1056 from amirmalka/master
fixed a bug in install.sh script
2023-01-23 13:21:51 +02:00
Amir Malka
b820ce1311 fixed a bug in install.sh script in which default install directory /usr/local/bin was missing and not created
Signed-off-by: Amir Malka <amirm@armosec.io>
2023-01-23 11:30:29 +02:00
kooomix
dae2458867 Merge pull request #1055 from kooomix/hostsensor
Support in CNIInfo hostscanner API
2023-01-23 11:02:08 +02:00
David Wertenteil
d45e636b52 Merge pull request #1050 from vladklokun/retarget-1040-empty-framework-name
chore: retarget a ListFrameworks fix, suggest targeting master in PRs
2023-01-23 10:43:05 +02:00
kooomix
8810631d5c Support in CNIInfo 2023-01-23 09:50:07 +02:00
yiscah
6cddce7399 minor improvements in getting cloud resources 2023-01-22 12:11:58 +02:00
Vlad Klokun
5d5c4f2c9f docs: suggest targeting master instead of dev
Signed-off-by: Vlad Klokun <vklokun@protonmail.ch>
2023-01-20 17:30:50 +02:00
Frédéric BIDON
e37049f68e fix ListFrameworks (could return an empty element)
Signed-off-by: Frédéric BIDON <fredbi@yahoo.com>
2023-01-20 17:26:27 +02:00
David Wertenteil
0622a474eb Merge pull request #1045 from kubescape/cosign-controls
Cosign controls
2023-01-19 18:45:04 +02:00
Daniel-GrunbergerCA
c357f12c82 add cosign functions for signature checking 2023-01-19 17:21:00 +01:00
yiscah
2cec58384a update go mod 2023-01-18 09:36:36 +02:00
yiscah
5e4bc5ddb8 get new cloud resource - DescribeRepositories 2023-01-18 09:25:50 +02:00
yiscah
f30752d9c3 Merge branch 'dev' of https://github.com/kubescape/kubescape into dev 2023-01-17 13:56:25 +02:00
David Wertenteil
a586549c57 Merge pull request #1039 from kubescape/release-on-new-tag
Update release flow
2023-01-15 14:50:54 +02:00
David Wertenteil
7c67a54230 Adding a screenshot 2023-01-15 11:20:56 +02:00
David Wertenteil
0006d7d8e7 Update release flow 2023-01-15 11:11:29 +02:00
David Wertenteil
63083ae48a Merge pull request #1037 from kubescape/dev
Release
2023-01-13 15:27:50 +02:00
yiscah
571a68fb58 Merge branch 'dev' of https://github.com/YiscahLevySilas1/kubescape into dev 2023-01-12 14:10:30 +02:00
YiscahLevySilas1
ef306ca0bf Merge branch 'kubescape:dev' into dev 2023-01-12 14:10:20 +02:00
yiscah
1a011f4968 Merge branch 'dev' of https://github.com/kubescape/kubescape into dev 2023-01-12 14:09:35 +02:00
YiscahLevySilas1
3cece6cf35 Merge branch 'kubescape:dev' into dev 2023-01-11 12:25:16 +02:00
yiscah
7fc10e8213 revert changes 2023-01-11 12:05:56 +02:00
yiscah
bb8f0e3c46 Revert "start developing port forward to host scanner (doesn't work yet)"
This reverts commit 87e2986024.
2023-01-11 12:02:23 +02:00
yiscah
cfd85eadab Merge branch 'dev' of https://github.com/YiscahLevySilas1/kubescape into dev 2023-01-11 11:59:16 +02:00
yiscah
87e2986024 start developing port forward to host scanner (doesn't work yet) 2022-12-15 19:03:44 +02:00
119 changed files with 4575 additions and 1312 deletions

View File

@@ -41,7 +41,4 @@ put an [x] in the box to get it checked
- [ ] If it is a core feature, I have added thorough tests.
- [ ] New and existing unit tests pass locally with my changes
**Please open the PR against the `dev` branch (Unless the PR contains only documentation changes)**
-->
-->

37
.github/actions/tag-action/action.yaml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: 'Tag validator and retag'
description: 'This action will check if the tag is rc and create a new tag for release'
inputs:
ORIGINAL_TAG: # id of input
description: 'Original tag'
required: true
default: ${{ github.ref_name }}
SUB_STRING:
description: 'Sub string for rc tag'
required: true
default: "-rc"
outputs:
NEW_TAG:
description: "The new tag for release"
value: ${{ steps.retag.outputs.NEW_TAG }}
runs:
using: "composite"
steps:
- run: |
SUB='-rc'
if [[ "${{ inputs.ORIGINAL_TAG }}" == *"${{ inputs.SUB_STRING }}"* ]]; then
echo "Release candidate tag found."
else
echo "Release candidate tag not found."
exit 1
fi
shell: bash
- id: retag
run: |
NEW_TAG=
echo "Original tag: ${{ inputs.ORIGINAL_TAG }}"
NEW_TAG=$(echo ${{ inputs.ORIGINAL_TAG }} | awk -F '-rc' '{print $1}')
echo "New tag: $NEW_TAG"
echo "NEW_TAG=$NEW_TAG" >> $GITHUB_OUTPUT
shell: bash

33
.github/workflows/00-pr-scanner.yaml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: 00-pr_scanner
on:
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
branches:
- 'master'
- 'main'
- 'dev'
paths-ignore:
- '**.yaml'
- '**.md'
- '**.sh'
- 'website/*'
- 'examples/*'
- 'docs/*'
- 'build/*'
- '.github/*'
concurrency:
group: ${{ github.head_ref }}
cancel-in-progress: true
jobs:
pr-scanner:
permissions:
pull-requests: write
uses: ./.github/workflows/a-pr-scanner.yaml
with:
RELEASE: ""
CLIENT: test
secrets: inherit

View File

@@ -0,0 +1,57 @@
name: 01-code_review_approved
on:
pull_request_review:
types: [submitted]
branches:
- 'master'
- 'main'
paths-ignore:
- '**.yaml'
- '**.md'
- '**.sh'
- 'website/*'
- 'examples/*'
- 'docs/*'
- 'build/*'
- '.github/*'
concurrency:
group: code-review-approved
cancel-in-progress: true
jobs:
binary-build:
if: ${{ github.event.review.state == 'approved' &&
contains( github.event.pull_request.labels.*.name, 'trigger-integration-test') &&
github.event.pull_request.base.ref == 'master' }} ## run only if labeled as "trigger-integration-test" and base branch is master
uses: ./.github/workflows/b-binary-build-and-e2e-tests.yaml
with:
COMPONENT_NAME: kubescape
CGO_ENABLED: 1
GO111MODULE: ""
GO_VERSION: "1.19"
RELEASE: ""
CLIENT: test
secrets: inherit
merge-to-master:
needs: binary-build
env:
GH_PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}
if: ${{ (github.event.review.state == 'approved' && github.event.pull_request.base.ref == 'master') &&
(always() && (contains(needs.*.result, 'success') || contains(needs.*.result, 'skipped')) && !(contains(needs.*.result, 'failure')) && !(contains(needs.*.result, 'cancelled'))) }}
runs-on: ubuntu-latest
steps:
- name: merge-to-master
if: ${{ env.GH_PERSONAL_ACCESS_TOKEN }}
uses: pascalgn/automerge-action@v0.15.5
env:
GITHUB_TOKEN: "${{ secrets.GH_PERSONAL_ACCESS_TOKEN }}"
MERGE_COMMIT_MESSAGE: "Merge to master - PR number: {pullRequest.number}"
MERGE_ERROR_FAIL: "true"
MERGE_METHOD: "merge"
MERGE_LABELS: ""
UPDATE_LABELS: ""

View File

@@ -1,64 +0,0 @@
name: golangci-lint
on:
push:
branches:
- dev
pull_request:
types: [ edited, opened, synchronize, reopened ]
branches:
- 'master'
- 'main'
- 'dev'
paths-ignore:
- '**.yaml'
- '**.md'
- '**.sh'
- 'website/*'
- 'examples/*'
- 'docs/*'
- 'build/*'
- '.github/*'
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.19
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Install libgit2
run: make libgit2
- name: golangci-lint
continue-on-error: true
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

69
.github/workflows/02-release.yaml vendored Normal file
View File

@@ -0,0 +1,69 @@
name: 02-create_release
on:
push:
tags:
- 'v*.*.*-rc.*'
jobs:
retag:
outputs:
NEW_TAG: ${{ steps.tag-calculator.outputs.NEW_TAG }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- id: tag-calculator
uses: ./.github/actions/tag-action
with:
SUB_STRING: "-rc"
binary-build:
needs: [retag]
uses: ./.github/workflows/b-binary-build-and-e2e-tests.yaml
with:
COMPONENT_NAME: kubescape
CGO_ENABLED: 1
GO111MODULE: ""
GO_VERSION: "1.19"
RELEASE: ${{ needs.retag.outputs.NEW_TAG }}
CLIENT: release
secrets: inherit
create-release:
permissions:
contents: write
needs: [retag, binary-build]
uses: ./.github/workflows/c-create-release.yaml
with:
RELEASE_NAME: "Release ${{ needs.retag.outputs.NEW_TAG }}"
TAG: ${{ needs.retag.outputs.NEW_TAG }}
DRAFT: false
secrets: inherit
# publish-krew-plugin:
# name: Publish Krew plugin
# runs-on: ubuntu-latest
# if: "${{ github.repository_owner }} == kubescape"
# needs: create-release
# steps:
# - uses: actions/checkout@v3
# with:
# submodules: recursive
# - name: Update new version in krew-index
# uses: rajatjindal/krew-release-bot@v0.0.43
publish-image:
permissions:
id-token: write
packages: write
contents: read
uses: ./.github/workflows/d-publish-image.yaml
needs: [ create-release, retag ]
with:
client: "image-release"
image_name: "quay.io/${{ github.repository_owner }}/kubescape"
image_tag: ${{ needs.retag.outputs.NEW_TAG }}
support_platforms: true
cosign: true
secrets: inherit

View File

@@ -1,14 +1,14 @@
name: create release digests
name: 03-create_release_digests
on:
release:
types: [ published]
types: [ published ]
branches:
- 'master'
- 'main'
jobs:
once:
create_release_digests:
name: Creating digests
runs-on: ubuntu-latest
steps:

52
.github/workflows/README.md vendored Normal file
View File

@@ -0,0 +1,52 @@
# Kubescape workflows
Tag terminology: `v<major>.<minor>.<patch>`
## Developing process
Kubescape's main branch is `main`, any PR will be opened against the main branch.
### Opening a PR
When a user opens a PR, this will trigger some basic tests (units, license, etc.)
### Reviewing a PR
The reviewer/maintainer of a PR will decide whether the PR introduces changes that require running the E2E system tests. If so, the reviewer will add the `trigger-integration-test` label.
### Approving a PR
Once a maintainer approves the PR, if the `trigger-integration-test` label was added to the PR, the GitHub actions will trigger the system test. The PR will be merged only after the system tests passed successfully. If the label was not added, the PR can be merged.
### Merging a PR
The code is merged, no other actions are needed
## Release process
Every two weeks, we will create a new tag by bumping the minor version, this will create the release and publish the artifacts.
If we are introducing breaking changes, we will update the `major` version instead.
When we wish to push a hot-fix/feature within the two weeks, we will bump the `patch`.
### Creating a new tag
Every two weeks or upon the decision of the maintainers, a maintainer can create a tag.
The tag should look as follows: `v<A>.<B>.<C>-rc.D` (release candidate).
When creating a tag, GitHub will trigger the following actions:
1. Basic tests - unit tests, license, etc.
2. System tests (integration tests). If the tests fail, the actions will stop here.
3. Create a new tag: `v<A>.<B>.<C>` (same tag just without the `rc` suffix)
4. Create a release
5. Publish artifacts
6. Build and publish the docker image (this is meanwhile until we separate the microservice code from the LCI codebase)
## Additional Information
The "callers" have the alphabetic prefix and the "executes" have the numeric prefix
## Screenshot
<img width="1469" alt="image" src="https://user-images.githubusercontent.com/64066841/212532727-e82ec9e7-263d-408b-b4b0-a8c943f0109a.png">

177
.github/workflows/a-pr-scanner.yaml vendored Normal file
View File

@@ -0,0 +1,177 @@
name: a-pr-scanner
on:
workflow_call:
inputs:
RELEASE:
description: 'release'
required: true
type: string
CLIENT:
description: 'Client name'
required: true
type: string
jobs:
scanners:
env:
GITGUARDIAN_API_KEY: ${{ secrets.GITGUARDIAN_API_KEY }}
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
name: PR Scanner
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
submodules: recursive
- uses: actions/setup-go@v3 # Install go because go-licenses use it
name: Installing go
with:
go-version: '1.19'
cache: true
- name: Scanning - Forbidden Licenses (go-licenses)
id: licenses-scan
continue-on-error: true
run: |
echo "## Installing go-licenses tool"
go install github.com/google/go-licenses@latest
echo "## Scanning for forbiden licenses ##"
go-licenses check .
- name: Scanning - Credentials (GitGuardian)
if: ${{ env.GITGUARDIAN_API_KEY }}
continue-on-error: true
id: credentials-scan
uses: GitGuardian/ggshield-action@master
with:
args: -v --all-policies
env:
GITHUB_PUSH_BEFORE_SHA: ${{ github.event.before }}
GITHUB_PUSH_BASE_SHA: ${{ github.event.base }}
GITHUB_PULL_BASE_SHA: ${{ github.event.pull_request.base.sha }}
GITHUB_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
GITGUARDIAN_API_KEY: ${{ secrets.GITGUARDIAN_API_KEY }}
- name: Scanning - Vulnerabilities (Snyk)
if: ${{ env.SNYK_TOKEN }}
id: vulnerabilities-scan
continue-on-error: true
uses: snyk/actions/golang@master
with:
command: test --all-projects
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
- name: Comment results to PR
continue-on-error: true # Warning: This might break opening PRs from forks
uses: peter-evans/create-or-update-comment@v2.1.0
with:
issue-number: ${{ github.event.pull_request.number }}
body: |
Scan results:
- License scan: ${{ steps.licenses-scan.outcome }}
- Credentials scan: ${{ steps.credentials-scan.outcome }}
- Vulnerabilities scan: ${{ steps.vulnerabilities-scan.outcome }}
reactions: 'eyes'
basic-tests:
needs: scanners
name: Create cross-platform build
runs-on: ${{ matrix.os }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE: ${{ inputs.RELEASE }}
CLIENT: ${{ inputs.CLIENT }}
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-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.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,gitenabled" -v ./...
- name: Test httphandler pkg
run: cd httphandler && go test "-tags=static,gitenabled" -v ./...
- name: Build
env:
RELEASE: ${{ inputs.RELEASE }}
CLIENT: ${{ inputs.CLIENT }}
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/kubescape-${{ matrix.os }}
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/kubescape-ubuntu-latest
if: matrix.os == 'ubuntu-20.04'
- name: golangci-lint
if: matrix.os == 'ubuntu-20.04'
continue-on-error: true
uses: golangci/golangci-lint-action@v3
with:
version: latest
args: --timeout 10m --build-tags=static
only-new-issues: true

View File

@@ -0,0 +1,275 @@
name: b-binary-build-and-e2e-tests
on:
workflow_call:
inputs:
COMPONENT_NAME:
required: true
type: string
RELEASE:
required: true
type: string
CLIENT:
required: true
type: string
GO_VERSION:
type: string
default: "1.19"
GO111MODULE:
required: true
type: string
CGO_ENABLED:
type: number
default: 1
BINARY_TESTS:
type: string
default: '[
"scan_nsa",
"scan_mitre",
"scan_with_exceptions",
"scan_repository",
"scan_local_file",
"scan_local_glob_files",
"scan_local_list_of_files",
"scan_nsa_and_submit_to_backend",
"scan_mitre_and_submit_to_backend",
"scan_local_repository_and_submit_to_backend",
"scan_repository_from_url_and_submit_to_backend",
"scan_with_exception_to_backend",
"scan_with_custom_framework",
"scan_customer_configuration",
"host_scanner"
]'
jobs:
check-secret:
name: secret-validator
runs-on: ubuntu-latest
outputs:
is-secret-set: ${{ steps.check-secret-set.outputs.is-secret-set }}
steps:
- name: check if the necessary secrets are set in github secrets
id: check-secret-set
env:
CUSTOMER: ${{ secrets.CUSTOMER }}
USERNAME: ${{ secrets.USERNAME }}
PASSWORD: ${{ secrets.PASSWORD }}
CLIENT_ID: ${{ secrets.CLIENT_ID_PROD }}
SECRET_KEY: ${{ secrets.SECRET_KEY_PROD }}
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
run: |
echo "is-secret-set=${{ env.CUSTOMER != '' &&
env.USERNAME != '' &&
env.PASSWORD != '' &&
env.CLIENT_ID != '' &&
env.SECRET_KEY != '' &&
env.REGISTRY_USERNAME != '' &&
env.REGISTRY_PASSWORD != ''
}}" >> $GITHUB_OUTPUT
binary-build:
name: Create cross-platform build
outputs:
TEST_NAMES: ${{ steps.export_tests_to_env.outputs.TEST_NAMES }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
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-
- uses: actions/setup-go@v3
name: Installing go
with:
go-version: ${{ inputs.GO_VERSION }}
cache: true
- 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,gitenabled" -v ./...
- name: Test httphandler pkg
run: cd httphandler && go test "-tags=static,gitenabled" -v ./...
- name: Build
env:
RELEASE: ${{ inputs.RELEASE }}
CLIENT: ${{ inputs.CLIENT }}
CGO_ENABLED: ${{ inputs.CGO_ENABLED }}
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/kubescape-${{ matrix.os }}
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/kubescape-ubuntu-latest
if: matrix.os == 'ubuntu-20.04'
- name: golangci-lint
if: matrix.os == 'ubuntu-20.04'
continue-on-error: true
uses: golangci/golangci-lint-action@v3
with:
version: latest
args: --timeout 10m --build-tags=static
only-new-issues: true
- id: export_tests_to_env
name: set test name
run: |
echo "TEST_NAMES=$input" >> $GITHUB_OUTPUT
env:
input: ${{ inputs.BINARY_TESTS }}
- uses: actions/upload-artifact@v3.1.1
name: Upload artifact (Linux)
if: matrix.os == 'ubuntu-20.04'
with:
name: kubescape-ubuntu-latest
path: build/
if-no-files-found: error
- uses: actions/upload-artifact@v3.1.1
name: Upload artifact (MacOS, Win)
if: matrix.os != 'ubuntu-20.04'
with:
name: kubescape-${{ matrix.os }}
path: build/
if-no-files-found: error
run-tests:
strategy:
fail-fast: false
matrix:
TEST: ${{ fromJson(needs.binary-build.outputs.TEST_NAMES) }}
needs: [check-secret, binary-build]
if: needs.check-secret.outputs.is-secret-set == 'true'
runs-on: ubuntu-latest # This cannot change
steps:
- uses: actions/download-artifact@v3.0.2
id: download-artifact
with:
name: kubescape-ubuntu-latest
path: "~"
- run: ls -laR
- name: chmod +x
run: chmod +x -R ${{steps.download-artifact.outputs.download-path}}/kubescape-ubuntu-latest
- name: Checkout systests repo
uses: actions/checkout@v3
with:
repository: armosec/system-tests
path: .
- uses: actions/setup-python@v4
with:
python-version: '3.8.13'
cache: 'pip'
- name: create env
run: ./create_env.sh
- name: Generate uuid
id: uuid
run: |
echo "RANDOM_UUID=$(uuidgen)" >> $GITHUB_OUTPUT
- name: Create k8s Kind Cluster
id: kind-cluster-install
uses: helm/kind-action@v1.3.0
with:
cluster_name: ${{ steps.uuid.outputs.RANDOM_UUID }}
- name: run-tests
env:
CUSTOMER: ${{ secrets.CUSTOMER }}
USERNAME: ${{ secrets.USERNAME }}
PASSWORD: ${{ secrets.PASSWORD }}
CLIENT_ID: ${{ secrets.CLIENT_ID_PROD }}
SECRET_KEY: ${{ secrets.SECRET_KEY_PROD }}
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
run: |
echo "Test history:"
echo " ${{ matrix.TEST }} " >/tmp/testhistory
cat /tmp/testhistory
source systests_python_env/bin/activate
python3 systest-cli.py \
-t ${{ matrix.TEST }} \
-b production \
-c CyberArmorTests \
--duration 3 \
--logger DEBUG \
--kwargs kubescape=${{steps.download-artifact.outputs.download-path}}/kubescape-ubuntu-latest
deactivate
- name: Test Report
uses: mikepenz/action-junit-report@v3.6.1
if: always() # always run even if the previous step fails
with:
report_paths: '**/results_xml_format/**.xml'
commit: ${{github.event.workflow_run.head_sha}}

View File

@@ -1,124 +0,0 @@
name: build
on:
push:
branches:
- 'master'
- 'main'
paths-ignore:
- '**.yaml'
- '**.md'
- '**.sh'
- 'website/*'
- 'examples/*'
- 'docs/*'
- 'build/*'
- '.github/*'
jobs:
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-20.04, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- 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: Build
env:
RELEASE: v2.0.${{ github.run_number }}
CLIENT: release
CGO_ENABLED: 1
run: python3 --version && python3 build.py
- 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.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 binaries (Linux)
id: upload-release-asset-linux
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
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
if: matrix.os != 'ubuntu-20.04'
- name: Upload release hash (Linux)
id: upload-release-hash-linux
uses: actions/upload-release-asset@v1
env:
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

@@ -1,31 +0,0 @@
name: build-dev
on:
push:
branches: [ dev ]
paths-ignore:
- '**.yaml'
- '**.md'
- '**.sh'
- 'website/*'
- 'examples/*'
- 'docs/*'
- 'build/*'
- '.github/*'
jobs:
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

57
.github/workflows/c-create-release.yaml vendored Normal file
View File

@@ -0,0 +1,57 @@
name: c-create_release
on:
workflow_call:
inputs:
RELEASE_NAME:
description: 'Release name'
required: true
type: string
TAG:
description: 'Tag name'
required: true
type: string
DRAFT:
description: 'Create draft release'
required: false
type: boolean
default: false
jobs:
create-release:
name: create-release
runs-on: ubuntu-latest
# permissions:
# contents: write
steps:
- uses: actions/download-artifact@v3.0.2
id: download-artifact
with:
path: .
- name: Release
uses: softprops/action-gh-release@v1
env:
MAC_OS: macos-latest
UBUNTU_OS: ubuntu-latest
WINDOWS_OS: windows-latest
with:
token: ${{ secrets.GITHUB_TOKEN }}
name: ${{ inputs.RELEASE_NAME }}
tag_name: ${{ inputs.TAG }}
body: ${{ github.event.pull_request.body }}
draft: ${{ inputs.DRAFT }}
fail_on_unmatched_files: true
prerelease: false
files: |
./kubescape-${{ env.MAC_OS }}/kubescape-${{ env.MAC_OS }}
./kubescape-${{ env.MAC_OS }}/kubescape-${{ env.MAC_OS }}.sha256
./kubescape-${{ env.MAC_OS }}/kubescape-${{ env.MAC_OS }}.tar.gz
./kubescape-${{ env.UBUNTU_OS }}/kubescape-${{ env.UBUNTU_OS }}
./kubescape-${{ env.UBUNTU_OS }}/kubescape-${{ env.UBUNTU_OS }}.sha256
./kubescape-${{ env.UBUNTU_OS }}/kubescape-${{ env.UBUNTU_OS }}.tar.gz
./kubescape-${{ env.WINDOWS_OS }}/kubescape-${{ env.WINDOWS_OS }}
./kubescape-${{ env.WINDOWS_OS }}/kubescape-${{ env.WINDOWS_OS }}.sha256
./kubescape-${{ env.WINDOWS_OS }}/kubescape-${{ env.WINDOWS_OS }}.tar.gz

View File

@@ -1,4 +1,4 @@
name: build
name: d-publish-image
on:
workflow_call:
@@ -33,7 +33,7 @@ jobs:
outputs:
is-secret-set: ${{ steps.check-secret-set.outputs.is-secret-set }}
steps:
- name: Check whether unity activation requests should be done
- name: check if QUAYIO_REGISTRY_USERNAME & QUAYIO_REGISTRY_PASSWORD is set in github secrets
id: check-secret-set
env:
QUAYIO_REGISTRY_USERNAME: ${{ secrets.QUAYIO_REGISTRY_USERNAME }}
@@ -46,10 +46,6 @@ jobs:
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

View File

@@ -1,24 +0,0 @@
name: pr-checks
on:
pull_request:
types: [ edited, opened, synchronize, reopened ]
branches:
- 'master'
- 'main'
- 'dev'
paths-ignore:
- '**.yaml'
- '**.md'
- '**.sh'
- 'website/*'
- 'examples/*'
- 'docs/*'
- 'build/*'
- '.github/*'
jobs:
test:
uses: ./.github/workflows/test.yaml
with:
release: "v2.0.${{ github.run_number }}"
client: test

View File

@@ -1,41 +0,0 @@
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

View File

@@ -1,100 +0,0 @@
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,gitenabled" -v ./...
- name: Test httphandler pkg
run: cd httphandler && go test "-tags=static,gitenabled" -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'

36
.krew.yaml Normal file
View File

@@ -0,0 +1,36 @@
apiVersion: krew.googlecontainertools.github.com/v1alpha2
kind: Plugin
metadata:
name: kubescape
spec:
homepage: https://kubescape.io/
shortDescription: An open-source Kubernetes security platform for your IDE, CI/CD pipelines, and clusters
version: {{ .TagName }}
description: |
Kubescape is an open-source Kubernetes security platform.
It includes risk analysis, security compliance, and misconfiguration scanning.
Targeted at the DevSecOps practitioner or platform engineer,
it offers an easy-to-use CLI interface, flexible output formats, and automated scanning capabilities.
It saves Kubernetes users and admins precious time, effort, and resources.
Kubescape was created by [ARMO](https://www.armosec.io/?utm_source=github&utm_medium=repository)
and is a [Cloud Native Computing Foundation (CNCF) sandbox project](https://www.cncf.io/sandbox-projects/).
platforms:
- selector:
matchLabels:
os: darwin
arch: amd64
{{ addURIAndSha "https://github.com/kubescape/kubescape/releases/download/{{ .TagName }}/kubescape-macos-latest" .TagName }}
bin: kubescape
- selector:
matchLabels:
os: linux
arch: amd64
{{ addURIAndSha "https://github.com/kubescape/kubescape/releases/download/{{ .TagName }}/kubescape-ubuntu-latest" .TagName }}
bin: kubescape
- selector:
matchLabels:
os: windows
arch: amd64
{{ addURIAndSha "https://github.com/kubescape/kubescape/releases/download/{{ .TagName }}/kubescape-windows-latest" .TagName }}
bin: kubescape

View File

@@ -21,7 +21,7 @@ Please follow our [code of conduct](CODE_OF_CONDUCT.md) in all of your interacti
build.
2. Update the README.md with details of changes to the interface, this includes new environment
variables, exposed ports, useful file locations and container parameters.
3. Open Pull Request to `dev` branch - we test the component before merging into the `master` branch
3. Open Pull Request to the `master` branch.
4. We will merge the Pull Request once you have the sign-off.
## Developer Certificate of Origin

View File

@@ -3,9 +3,16 @@ import sys
import hashlib
import platform
import subprocess
import tarfile
BASE_GETTER_CONST = "github.com/kubescape/kubescape/v2/core/cautils/getter"
platformSuffixes = {
"Windows": "windows-latest",
"Linux": "ubuntu-latest",
"Darwin": "macos-latest",
}
def check_status(status, msg):
if status != 0:
sys.stderr.write(msg)
@@ -13,21 +20,15 @@ def check_status(status, msg):
def get_build_dir():
current_platform = platform.system()
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"
else: raise OSError("Platform %s is not supported!" % (current_platform))
return os.path.join("build", build_dir)
return "build"
def get_package_name():
package_name = "kubescape"
current_platform = platform.system()
return package_name
if current_platform not in platformSuffixes: raise OSError("Platform %s is not supported!" % (current_platform))
return "kubescape-" + platformSuffixes[current_platform]
def main():
@@ -46,6 +47,7 @@ def main():
ks_file = os.path.join(build_dir, package_name)
hash_file = ks_file + ".sha256"
tar_file = ks_file + ".tar.gz"
if not os.path.isdir(build_dir):
os.makedirs(build_dir)
@@ -73,6 +75,9 @@ def main():
print("kubescape hash: {}, file: {}".format(hash, hash_file))
kube_sha.write(sha256.hexdigest())
with tarfile.open(tar_file, 'w:gz') as archive:
archive.add(ks_file, "kubescape")
print("Build Done")

View File

@@ -25,13 +25,13 @@ RUN rm -rf git2go && make libgit2
# build kubescape server
WORKDIR /work/httphandler
RUN python build.py
RUN ls -ltr build/ubuntu-latest
RUN ls -ltr build/
# build kubescape cmd
WORKDIR /work
RUN python build.py
RUN /work/build/ubuntu-latest/kubescape download artifacts -o /work/artifacts
RUN /work/build/kubescape-ubuntu-latest download artifacts -o /work/artifacts
FROM alpine:3.16.2
@@ -45,7 +45,7 @@ USER ks
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
COPY --from=builder /work/httphandler/build/kubescape-ubuntu-latest /usr/bin/ksserver
COPY --from=builder /work/build/kubescape-ubuntu-latest /usr/bin/kubescape
ENTRYPOINT ["ksserver"]

View File

@@ -1,23 +1,23 @@
package completion
import (
"fmt"
"os"
"strings"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/spf13/cobra"
)
var completionCmdExamples = `
var completionCmdExamples = fmt.Sprintf(`
# Enable BASH shell autocompletion
$ source <(kubescape completion bash)
$ echo 'source <(kubescape completion bash)' >> ~/.bashrc
$ source <(%[1]s completion bash)
$ echo 'source <(%[1]s completion bash)' >> ~/.bashrc
# Enable ZSH shell autocompletion
$ source <(kubectl completion zsh)
$ echo 'source <(kubectl completion zsh)' >> "${fpath[1]}/_kubectl"
`
`, cautils.ExecName())
func GetCompletionCmd() *cobra.Command {
completionCmd := &cobra.Command{

View File

@@ -1,34 +1,37 @@
package config
import (
"fmt"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/meta"
"github.com/spf13/cobra"
)
var (
configExample = `
configExample = fmt.Sprintf(`
# View cached configurations
kubescape config view
%[1]s config view
# Delete cached configurations
kubescape config delete
%[1]s config delete
# Set cached configurations
kubescape config set --help
`
setConfigExample = `
%[1]s config set --help
`, cautils.ExecName())
setConfigExample = fmt.Sprintf(`
# Set account id
kubescape config set accountID <account id>
%[1]s config set accountID <account id>
# Set client id
kubescape config set clientID <client id>
%[1]s config set clientID <client id>
# Set access key
kubescape config set secretKey <access key>
%[1]s config set secretKey <access key>
# Set cloudAPIURL
kubescape config set cloudAPIURL <cloud API URL>
`
%[1]s config set cloudAPIURL <cloud API URL>
`, cautils.ExecName())
)
func GetConfigCmd(ks meta.IKubescape) *cobra.Command {

View File

@@ -1,6 +1,8 @@
package config
import (
"context"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/kubescape/v2/core/meta"
v1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
@@ -13,7 +15,7 @@ func getDeleteCmd(ks meta.IKubescape) *cobra.Command {
Short: "Delete cached configurations",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
if err := ks.DeleteCachedConfig(&v1.DeleteConfig{}); err != nil {
if err := ks.DeleteCachedConfig(context.TODO(), &v1.DeleteConfig{}); err != nil {
logger.L().Fatal(err.Error())
}
},

View File

@@ -1,18 +1,21 @@
package delete
import (
"fmt"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/meta"
v1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
)
var deleteExceptionsExamples = `
var deleteExceptionsExamples = fmt.Sprintf(`
# Delete single exception
kubescape delete exceptions "exception name"
%[1]s delete exceptions "exception name"
# Delete multiple exceptions
kubescape delete exceptions "first exception;second exception;third exception"
`
%[1]s delete exceptions "first exception;second exception;third exception"
`, cautils.ExecName())
func GetDeleteCmd(ks meta.IKubescape) *cobra.Command {
var deleteInfo v1.Delete

View File

@@ -5,6 +5,7 @@ import (
"strings"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/meta"
v1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
@@ -13,7 +14,7 @@ import (
func getExceptionsCmd(ks meta.IKubescape, deleteInfo *v1.Delete) *cobra.Command {
return &cobra.Command{
Use: "exceptions <exception name>",
Short: "Delete exceptions from Kubescape SaaS version. Run 'kubescape list exceptions' for all exceptions names",
Short: fmt.Sprintf("Delete exceptions from Kubescape SaaS version. Run '%[1]s list exceptions' for all exceptions names", cautils.ExecName()),
Example: deleteExceptionsExamples,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {

View File

@@ -1,6 +1,7 @@
package download
import (
"context"
"fmt"
"path/filepath"
"strings"
@@ -14,34 +15,34 @@ import (
)
var (
downloadExample = `
downloadExample = fmt.Sprintf(`
# Download all artifacts and save them in the default path (~/.kubescape)
kubescape download artifacts
%[1]s download artifacts
# Download all artifacts and save them in /tmp path
kubescape download artifacts --output /tmp
%[1]s download artifacts --output /tmp
# Download the NSA framework. Run 'kubescape list frameworks' for all frameworks names
kubescape download framework nsa
# Download the NSA framework. Run '%[1]s list frameworks' for all frameworks names
%[1]s download framework nsa
# 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 '%[1]s list controls --id' for all controls ids
%[1]s download control "C-0001"
# 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 '%[1]s list controls --id' for all controls ids
%[1]s download control C-0001
# Download the configured exceptions
kubescape download exceptions
%[1]s download exceptions
# Download the configured controls-inputs
kubescape download controls-inputs
%[1]s download controls-inputs
# Download the attack tracks
kubescape download attack-tracks
`
%[1]s download attack-tracks
`, cautils.ExecName())
)
func GeDownloadCmd(ks meta.IKubescape) *cobra.Command {
func GetDownloadCmd(ks meta.IKubescape) *cobra.Command {
var downloadInfo = v1.DownloadInfo{}
downloadCmd := &cobra.Command{
@@ -74,7 +75,7 @@ func GeDownloadCmd(ks meta.IKubescape) *cobra.Command {
downloadInfo.Identifier = args[1]
}
if err := ks.Download(&downloadInfo); err != nil {
if err := ks.Download(context.TODO(), &downloadInfo); err != nil {
logger.L().Fatal(err.Error())
}
return nil

View File

@@ -1,23 +1,26 @@
package fix
import (
"context"
"errors"
"fmt"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/meta"
metav1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
)
var fixCmdExamples = `
var fixCmdExamples = fmt.Sprintf(`
Fix command is for fixing kubernetes manifest files based on a scan command output.
Use with caution, this command will change your files in-place.
# Fix kubernetes YAML manifest files based on a scan command output (output.json)
1) kubescape scan --format json --format-version v2 --output output.json
2) kubescape fix output.json
1) %[1]s scan --format json --format-version v2 --output output.json
2) %[1]s fix output.json
`
`, cautils.ExecName())
func GetFixCmd(ks meta.IKubescape) *cobra.Command {
var fixInfo metav1.FixInfo
@@ -33,7 +36,7 @@ func GetFixCmd(ks meta.IKubescape) *cobra.Command {
}
fixInfo.ReportFile = args[0]
return ks.Fix(&fixInfo)
return ks.Fix(context.TODO(), &fixInfo)
},
}

View File

@@ -1,6 +1,7 @@
package list
import (
"context"
"fmt"
"strings"
@@ -13,19 +14,19 @@ import (
)
var (
listExample = `
listExample = fmt.Sprintf(`
# List default supported frameworks names
kubescape list frameworks
%[1]s list frameworks
# List all supported frameworks names
kubescape list frameworks --account <account id>
%[1]s list frameworks --account <account id>
# List all supported controls names with ids
kubescape list controls
%[1]s list controls
Control documentation:
https://hub.armosec.io/docs/controls
`
`, cautils.ExecName())
)
func GetListCmd(ks meta.IKubescape) *cobra.Command {
@@ -55,7 +56,7 @@ func GetListCmd(ks meta.IKubescape) *cobra.Command {
listPolicies.Target = args[0]
if err := ks.List(&listPolicies); err != nil {
if err := ks.List(context.TODO(), &listPolicies); err != nil {
logger.L().Fatal(err.Error())
}
return nil
@@ -65,7 +66,7 @@ func GetListCmd(ks meta.IKubescape) *cobra.Command {
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-print'/'json'")
listCmd.PersistentFlags().MarkDeprecated("id", "Control ID's are included in list outpus")
listCmd.PersistentFlags().MarkDeprecated("id", "Control ID's are included in list outputs")
return listCmd
}

View File

@@ -26,19 +26,19 @@ import (
var rootInfo cautils.RootInfo
var ksExamples = `
var ksExamples = fmt.Sprintf(`
# Scan command
kubescape scan
%[1]s scan
# List supported frameworks
kubescape list frameworks
%[1]s list frameworks
# Download artifacts (air-gapped environment support)
kubescape download artifacts
%[1]s download artifacts
# View cached configurations
kubescape config view
`
%[1]s config view
`, cautils.ExecName())
func NewDefaultKubescapeCommand() *cobra.Command {
ks := core.NewKubescape()
@@ -53,6 +53,16 @@ func getRootCmd(ks meta.IKubescape) *cobra.Command {
Example: ksExamples,
}
if cautils.IsKrewPlugin() {
// Invoked as a kubectl plugin.
// Cobra doesn't have a way to specify a two word command (i.e. "kubectl kubescape"), so set a custom usage template
// with kubectl in it. Cobra will use this template for the root and all child commands.
oldUsageTemplate := rootCmd.UsageTemplate()
newUsageTemplate := strings.NewReplacer("{{.UseLine}}", "kubectl {{.UseLine}}", "{{.CommandPath}}", "kubectl {{.CommandPath}}").Replace(oldUsageTemplate)
rootCmd.SetUsageTemplate(newUsageTemplate)
}
rootCmd.PersistentFlags().StringVar(&rootInfo.KSCloudBEURLsDep, "environment", "", envFlagUsage)
rootCmd.PersistentFlags().StringVar(&rootInfo.KSCloudBEURLs, "env", "", envFlagUsage)
rootCmd.PersistentFlags().MarkDeprecated("environment", "use 'env' instead")
@@ -71,7 +81,7 @@ func getRootCmd(ks meta.IKubescape) *cobra.Command {
// Supported commands
rootCmd.AddCommand(scan.GetScanCommand(ks))
rootCmd.AddCommand(download.GeDownloadCmd(ks))
rootCmd.AddCommand(download.GetDownloadCmd(ks))
rootCmd.AddCommand(delete.GetDeleteCmd(ks))
rootCmd.AddCommand(list.GetListCmd(ks))
rootCmd.AddCommand(submit.GetSubmitCmd(ks))

View File

@@ -1,6 +1,7 @@
package scan
import (
"context"
"fmt"
"io"
"os"
@@ -18,28 +19,28 @@ import (
)
var (
controlExample = `
controlExample = fmt.Sprintf(`
# Scan the 'privileged container' control
kubescape scan control "privileged container"
%[1]s scan control "privileged container"
# Scan list of controls separated with a comma
kubescape scan control "privileged container","HostPath mount"
%[1]s 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
%[1]s scan control C-0058,C-0057
Run 'kubescape list controls' for the list of supported controls
Run '%[1]s list controls' for the list of supported controls
Control documentation:
https://hub.armosec.io/docs/controls
`
`, cautils.ExecName())
)
// controlCmd represents the control command
func getControlCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Command {
return &cobra.Command{
Use: "control <control names list>/<control ids list>",
Short: "The controls you wish to use. Run 'kubescape list controls' for the list of supported controls",
Short: fmt.Sprintf("The controls you wish to use. Run '%[1]s list controls' for the list of supported controls", cautils.ExecName()),
Example: controlExample,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
@@ -67,7 +68,7 @@ func getControlCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comman
if len(args) == 0 {
scanInfo.ScanAll = true
} else { // expected control or list of control sepparated by ","
} else { // expected control or list of control separated by ","
// Read controls from input args
scanInfo.SetPolicyIdentifiers(strings.Split(args[0], ","), apisv1.KindControl)
@@ -96,11 +97,12 @@ func getControlCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comman
return err
}
results, err := ks.Scan(scanInfo)
ctx := context.TODO()
results, err := ks.Scan(ctx, scanInfo)
if err != nil {
logger.L().Fatal(err.Error())
}
if err := results.HandleResults(); err != nil {
if err := results.HandleResults(ctx); err != nil {
logger.L().Fatal(err.Error())
}
if !scanInfo.VerboseMode {

View File

@@ -1,6 +1,7 @@
package scan
import (
"context"
"errors"
"fmt"
"io"
@@ -20,24 +21,24 @@ import (
)
var (
frameworkExample = `
frameworkExample = fmt.Sprintf(`
# Scan all frameworks
kubescape scan framework all
%[1]s scan framework all
# Scan the NSA framework
kubescape scan framework nsa
%[1]s scan framework nsa
# Scan the NSA and MITRE framework
kubescape scan framework nsa,mitre
%[1]s scan framework nsa,mitre
# Scan all frameworks
kubescape scan framework all
%[1]s scan framework all
# Scan kubernetes YAML manifest files (single file or glob)
kubescape scan framework nsa .
%[1]s scan framework nsa .
Run 'kubescape list frameworks' for the list of supported frameworks
`
Run '%[1]s list frameworks' for the list of supported frameworks
`, cautils.ExecName())
ErrUnknownSeverity = errors.New("unknown severity")
)
@@ -46,7 +47,7 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
return &cobra.Command{
Use: "framework <framework names list> [`<glob pattern>`/`-`] [flags]",
Short: "The framework you wish to use. Run 'kubescape list frameworks' for the list of supported frameworks",
Short: fmt.Sprintf("The framework you wish to use. Run '%[1]s list frameworks' for the list of supported frameworks", cautils.ExecName()),
Example: frameworkExample,
Long: "Execute a scan on a running Kubernetes cluster or `yaml`/`json` files (use glob) or `-` for stdin",
Args: func(cmd *cobra.Command, args []string) error {
@@ -103,12 +104,13 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
scanInfo.SetPolicyIdentifiers(frameworks, apisv1.KindFramework)
results, err := ks.Scan(scanInfo)
ctx := context.TODO()
results, err := ks.Scan(ctx, scanInfo)
if err != nil {
logger.L().Fatal(err.Error())
}
if err = results.HandleResults(); err != nil {
if err = results.HandleResults(ctx); err != nil {
logger.L().Fatal(err.Error())
}
if !scanInfo.VerboseMode {
@@ -161,14 +163,14 @@ func countersExceedSeverityThreshold(severityCounters reportsummary.ISeverityCou
}
// terminateOnExceedingSeverity terminates the application on exceeding severity
func terminateOnExceedingSeverity(scanInfo *cautils.ScanInfo, l logger.ILogger) {
func terminateOnExceedingSeverity(scanInfo *cautils.ScanInfo, l helpers.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)) {
func enforceSeverityThresholds(severityCounters reportsummary.ISeverityCounters, scanInfo *cautils.ScanInfo, onExceed func(*cautils.ScanInfo, helpers.ILogger)) {
// If a severity threshold is not set, we dont need to enforce it
if scanInfo.FailThresholdSeverity == "" {
return

View File

@@ -10,25 +10,24 @@ import (
"github.com/spf13/cobra"
)
var scanCmdExamples = `
var scanCmdExamples = fmt.Sprintf(`
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 --enable-host-scan --verbose
%[1]s scan --enable-host-scan --verbose
# Scan kubernetes YAML manifest files
kubescape scan .
%[1]s scan .
# Scan and save the results in the JSON format
kubescape scan --format json --output results.json --format-version=v2
%[1]s scan --format json --output results.json --format-version=v2
# Display all resources
kubescape scan --verbose
%[1]s scan --verbose
# Scan different clusters from the kubectl context
kubescape scan --kube-context <kubernetes context>
`
%[1]s scan --kube-context <kubernetes context>
`, cautils.ExecName())
func GetScanCommand(ks meta.IKubescape) *cobra.Command {
var scanInfo cautils.ScanInfo
@@ -87,7 +86,7 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
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().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 different between versions, this is for maintaining backward and forward compatibility. Supported:'v1'/'v2'")
scanCmd.PersistentFlags().StringVar(&scanInfo.FormatVersion, "format-version", "v2", "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")

View File

@@ -1,7 +1,8 @@
package scan
import (
logger "github.com/kubescape/go-logger"
"context"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v2/core/cautils"
@@ -160,7 +161,7 @@ func Test_enforceSeverityThresholds(t *testing.T) {
want := tc.Want
got := false
onExceed := func(*cautils.ScanInfo, logger.ILogger) {
onExceed := func(*cautils.ScanInfo, helpers.ILogger) {
got = true
}
@@ -193,6 +194,7 @@ 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) Ctx(_ context.Context) helpers.ILogger { return l }
func (l *spyLogger) Fatal(msg string, details ...helpers.IDetails) {
firstDetail := details[0]

View File

@@ -1,6 +1,7 @@
package submit
import (
"context"
"fmt"
logger "github.com/kubescape/go-logger"
@@ -26,7 +27,7 @@ func getExceptionsCmd(ks meta.IKubescape, submitInfo *metav1.Submit) *cobra.Comm
logger.L().Fatal(err.Error())
}
if err := ks.SubmitExceptions(&submitInfo.Credentials, args[0]); err != nil {
if err := ks.SubmitExceptions(context.TODO(), &submitInfo.Credentials, args[0]); err != nil {
logger.L().Fatal(err.Error())
}
},

View File

@@ -1,6 +1,7 @@
package submit
import (
"context"
"fmt"
"github.com/google/uuid"
@@ -19,13 +20,13 @@ import (
)
var (
rbacExamples = `
rbacExamples = fmt.Sprintf(`
# Submit cluster's Role-Based Access Control(RBAC)
kubescape submit rbac
%[1]s submit rbac
# Submit cluster's Role-Based Access Control(RBAC) with account ID
kubescape submit rbac --account <account-id>
`
%[1]s submit rbac --account <account-id>
`, cautils.ExecName())
)
// getRBACCmd represents the RBAC command
@@ -36,7 +37,7 @@ func getRBACCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
Example: rbacExamples,
Short: "Submit cluster's Role-Based Access Control(RBAC)",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
RunE: func(_ *cobra.Command, args []string) error {
if err := flagValidationSubmit(submitInfo); err != nil {
return err
@@ -51,7 +52,7 @@ func getRBACCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
}
if clusterConfig.GetAccountID() == "" {
return fmt.Errorf("account ID is not set, run 'kubescape submit rbac --account <account-id>'")
return fmt.Errorf("account ID is not set, run '%[1]s submit rbac --account <account-id>'", cautils.ExecName())
}
// list RBAC
@@ -66,7 +67,7 @@ func getRBACCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
Reporter: r,
}
if err := ks.Submit(submitInterfaces); err != nil {
if err := ks.Submit(context.TODO(), submitInterfaces); err != nil {
logger.L().Fatal(err.Error())
}
return nil

View File

@@ -1,11 +1,13 @@
package submit
import (
"context"
"encoding/json"
"fmt"
"os"
"github.com/google/uuid"
"github.com/kubescape/kubescape/v2/core/cautils"
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
logger "github.com/kubescape/go-logger"
@@ -50,7 +52,7 @@ func (resultsObject *ResultsObject) ListAllResources() (map[string]workloadinter
func getResultsCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
var resultsCmd = &cobra.Command{
Use: "results <json file>\nExample:\n$ kubescape submit results path/to/results.json --format-version v2",
Use: fmt.Sprintf("results <json file>\nExample:\n$ %[1]s submit results path/to/results.json --format-version v2", cautils.ExecName()),
Short: "Submit a pre scanned results file. The file must be in json format",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
@@ -81,13 +83,13 @@ func getResultsCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
Reporter: r,
}
if err := ks.Submit(submitInterfaces); err != nil {
if err := ks.Submit(context.TODO(), submitInterfaces); err != nil {
logger.L().Fatal(err.Error())
}
return nil
},
}
resultsCmd.PersistentFlags().StringVar(&formatVersion, "format-version", "v1", "Output object can be differnet between versions, this is for maintaining backward and forward compatibility. Supported:'v1'/'v2'")
resultsCmd.PersistentFlags().StringVar(&formatVersion, "format-version", "v2", "Output object can be different between versions, this is for maintaining backward and forward compatibility. Supported:'v1'/'v2'")
return resultsCmd
}

View File

@@ -1,18 +1,21 @@
package submit
import (
"fmt"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/meta"
metav1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
"github.com/spf13/cobra"
)
var submitCmdExamples = `
var submitCmdExamples = fmt.Sprintf(`
# Submit Kubescape scan results file
kubescape submit results
%[1]s submit results
# Submit exceptions file to Kubescape SaaS
kubescape submit exceptions
`
%[1]s submit exceptions
`, cautils.ExecName())
func GetSubmitCmd(ks meta.IKubescape) *cobra.Command {
var submitInfo metav1.Submit

View File

@@ -5,6 +5,7 @@ package update
// kubescape update
import (
"fmt"
"os/exec"
"runtime"
@@ -13,11 +14,17 @@ import (
"github.com/spf13/cobra"
)
var updateCmdExamples = fmt.Sprintf(`
# Update to the latest kubescape release
%[1]s update
`, cautils.ExecName())
func GetUpdateCmd() *cobra.Command {
updateCmd := &cobra.Command{
Use: "update",
Short: "Update your version",
Long: ``,
Use: "update",
Short: "Update your version",
Long: ``,
Example: updateCmdExamples,
RunE: func(_ *cobra.Command, args []string) error {
//Checking the user's version of kubescape to the latest release
if cautils.BuildNumber == cautils.LatestReleaseVersion {

View File

@@ -1,6 +1,7 @@
package version
import (
"context"
"fmt"
"os"
@@ -14,8 +15,9 @@ func GetVersionCmd() *cobra.Command {
Short: "Get current version",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
v := cautils.NewIVersionCheckHandler()
v.CheckLatestVersion(cautils.NewVersionCheckRequest(cautils.BuildNumber, "", "", "version"))
ctx := context.TODO()
v := cautils.NewIVersionCheckHandler(ctx)
v.CheckLatestVersion(ctx, cautils.NewVersionCheckRequest(cautils.BuildNumber, "", "", "version"))
fmt.Fprintf(os.Stdout,
"Your current version is: %s [git enabled in build: %t]\n",
cautils.BuildNumber,

View File

@@ -70,7 +70,7 @@ type ITenantConfig interface {
// set
SetTenant() error
UpdateCachedConfig() error
DeleteCachedConfig() error
DeleteCachedConfig(ctx context.Context) error
// getters
GetContextName() string
@@ -175,9 +175,9 @@ func (lc *LocalConfig) UpdateCachedConfig() error {
return updateConfigFile(lc.configObj)
}
func (lc *LocalConfig) DeleteCachedConfig() error {
func (lc *LocalConfig) DeleteCachedConfig(ctx context.Context) error {
if err := DeleteConfigFile(); err != nil {
logger.L().Warning(err.Error())
logger.L().Ctx(ctx).Warning(err.Error())
}
return nil
}
@@ -330,12 +330,12 @@ func (c *ClusterConfig) UpdateCachedConfig() error {
return updateConfigFile(c.configObj)
}
func (c *ClusterConfig) DeleteCachedConfig() error {
func (c *ClusterConfig) DeleteCachedConfig(ctx context.Context) error {
if err := c.deleteConfigMap(); err != nil {
logger.L().Warning(err.Error())
logger.L().Ctx(ctx).Warning(err.Error())
}
if err := DeleteConfigFile(); err != nil {
logger.L().Warning(err.Error())
logger.L().Ctx(ctx).Warning(err.Error())
}
return nil
}

View File

@@ -1,6 +1,8 @@
package cautils
import (
"context"
"github.com/armosec/armoapi-go/armotypes"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/opa-utils/reporthandling"
@@ -36,7 +38,7 @@ type OPASessionObj struct {
OmitRawResources bool // omit raw resources from output
}
func NewOPASessionObj(frameworks []reporthandling.Framework, k8sResources *K8SResources, scanInfo *ScanInfo) *OPASessionObj {
func NewOPASessionObj(ctx context.Context, frameworks []reporthandling.Framework, k8sResources *K8SResources, scanInfo *ScanInfo) *OPASessionObj {
return &OPASessionObj{
Report: &reporthandlingv2.PostureReport{},
Policies: frameworks,
@@ -48,7 +50,7 @@ func NewOPASessionObj(frameworks []reporthandling.Framework, k8sResources *K8SRe
ResourceToControlsMap: make(map[string][]string),
ResourceSource: make(map[string]reporthandling.Source),
SessionID: scanInfo.ScanID,
Metadata: scanInfoToScanMetadata(scanInfo),
Metadata: scanInfoToScanMetadata(ctx, scanInfo),
OmitRawResources: scanInfo.OmitRawResources,
}
}

View File

@@ -7,6 +7,7 @@ import (
spinnerpkg "github.com/briandowns/spinner"
"github.com/fatih/color"
"github.com/mattn/go-isatty"
"github.com/schollz/progressbar/v3"
)
var FailureDisplay = color.New(color.Bold, color.FgHiRed).FprintfFunc()
@@ -39,3 +40,28 @@ func StopSpinner() {
}
spinner.Stop()
}
type ProgressHandler struct {
title string
pb *progressbar.ProgressBar
}
func NewProgressHandler(title string) *ProgressHandler {
return &ProgressHandler{title: title}
}
func (p *ProgressHandler) Start(allSteps int) {
if isatty.IsTerminal(os.Stderr.Fd()) {
p.pb = progressbar.Default(int64(allSteps), p.title)
} else {
p.pb = progressbar.DefaultSilent(int64(allSteps), p.title)
}
}
func (p *ProgressHandler) ProgressJob(step int, message string) {
p.pb.Add(step)
p.pb.Describe(message)
}
func (p *ProgressHandler) Stop() {
}

View File

@@ -2,6 +2,7 @@ package cautils
import (
"bytes"
"context"
"encoding/json"
"fmt"
"os"
@@ -31,7 +32,7 @@ const (
)
// 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) {
func LoadResourcesFromHelmCharts(ctx context.Context, basePath string) (map[string][]workloadinterface.IMetadata, map[string]string) {
directories, _ := listDirs(basePath)
helmDirectories := make([]string, 0)
for _, dir := range directories {
@@ -47,7 +48,7 @@ func LoadResourcesFromHelmCharts(basePath string) (map[string][]workloadinterfac
if err == nil {
wls, errs := chart.GetWorkloadsWithDefaultValues()
if len(errs) > 0 {
logger.L().Error(fmt.Sprintf("Rendering of Helm chart template '%s', failed: %v", chart.GetName(), errs))
logger.L().Ctx(ctx).Error(fmt.Sprintf("Rendering of Helm chart template '%s', failed: %v", chart.GetName(), errs))
continue
}
@@ -63,7 +64,7 @@ func LoadResourcesFromHelmCharts(basePath string) (map[string][]workloadinterfac
// 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) {
func LoadResourcesFromKustomizeDirectory(ctx context.Context, basePath string) (map[string][]workloadinterface.IMetadata, string) {
isKustomizeDirectory := IsKustomizeDirectory(basePath)
isKustomizeFile := IsKustomizeFile(basePath)
if ok := isKustomizeDirectory || isKustomizeFile; !ok {
@@ -87,7 +88,7 @@ func LoadResourcesFromKustomizeDirectory(basePath string) (map[string][]workload
kustomizeDirectoryName := GetKustomizeDirectoryName(newBasePath)
if len(errs) > 0 {
logger.L().Error(fmt.Sprintf("Rendering yaml from Kustomize failed: %v", errs))
logger.L().Ctx(ctx).Error(fmt.Sprintf("Rendering yaml from Kustomize failed: %v", errs))
}
for k, v := range wls {
@@ -96,10 +97,10 @@ func LoadResourcesFromKustomizeDirectory(basePath string) (map[string][]workload
return sourceToWorkloads, kustomizeDirectoryName
}
func LoadResourcesFromFiles(input, rootPath string) map[string][]workloadinterface.IMetadata {
func LoadResourcesFromFiles(ctx context.Context, input, rootPath string) map[string][]workloadinterface.IMetadata {
files, errs := listFiles(input)
if len(errs) > 0 {
logger.L().Error(fmt.Sprintf("%v", errs))
logger.L().Ctx(ctx).Error(fmt.Sprintf("%v", errs))
}
if len(files) == 0 {
return nil
@@ -107,7 +108,7 @@ func LoadResourcesFromFiles(input, rootPath string) map[string][]workloadinterfa
workloads, errs := loadFiles(rootPath, files)
if len(errs) > 0 {
logger.L().Error(fmt.Sprintf("%v", errs))
logger.L().Ctx(ctx).Error(fmt.Sprintf("%v", errs))
}
return workloads

View File

@@ -1,6 +1,7 @@
package cautils
import (
"context"
"os"
"path/filepath"
"strings"
@@ -30,7 +31,7 @@ func TestListFiles(t *testing.T) {
}
func TestLoadResourcesFromFiles(t *testing.T) {
workloads := LoadResourcesFromFiles(onlineBoutiquePath(), "")
workloads := LoadResourcesFromFiles(context.TODO(), onlineBoutiquePath(), "")
assert.Equal(t, 12, len(workloads))
for i, w := range workloads {
@@ -44,7 +45,7 @@ func TestLoadResourcesFromFiles(t *testing.T) {
}
func TestLoadResourcesFromHelmCharts(t *testing.T) {
sourceToWorkloads, sourceToChartName := LoadResourcesFromHelmCharts(helmChartPath())
sourceToWorkloads, sourceToChartName := LoadResourcesFromHelmCharts(context.TODO(), helmChartPath())
assert.Equal(t, 6, len(sourceToWorkloads))
for file, workloads := range sourceToWorkloads {

20
core/cautils/krewutils.go Normal file
View File

@@ -0,0 +1,20 @@
package cautils
import (
"os"
"path/filepath"
"strings"
)
// ExecName returns the correct name to use in examples depending on how kubescape is invoked
func ExecName() string {
n := "kubescape"
if IsKrewPlugin() {
return "kubectl " + n
}
return n
}
func IsKrewPlugin() bool {
return strings.HasPrefix(filepath.Base(os.Args[0]), "kubectl-")
}

View File

@@ -1,6 +1,7 @@
package cautils
import (
"context"
"encoding/json"
"fmt"
"os"
@@ -8,13 +9,12 @@ import (
"strings"
"github.com/armosec/armoapi-go/armotypes"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
giturl "github.com/kubescape/go-git-url"
"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"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
"github.com/kubescape/opa-utils/reporthandling"
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
@@ -112,7 +112,7 @@ type ScanInfo struct {
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
FormatVersion string // Output object can be different 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 //
@@ -141,16 +141,16 @@ type Getters struct {
AttackTracksGetter getter.IAttackTracksGetter
}
func (scanInfo *ScanInfo) Init() {
func (scanInfo *ScanInfo) Init(ctx context.Context) {
scanInfo.setUseFrom()
scanInfo.setUseArtifactsFrom()
scanInfo.setUseArtifactsFrom(ctx)
if scanInfo.ScanID == "" {
scanInfo.ScanID = uuid.NewString()
}
}
func (scanInfo *ScanInfo) setUseArtifactsFrom() {
func (scanInfo *ScanInfo) setUseArtifactsFrom(ctx context.Context) {
if scanInfo.UseArtifactsFrom == "" {
return
}
@@ -164,7 +164,7 @@ func (scanInfo *ScanInfo) setUseArtifactsFrom() {
// set frameworks files
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))
logger.L().Ctx(ctx).Fatal("failed to read files from directory", helpers.String("dir", scanInfo.UseArtifactsFrom), helpers.Error(err))
}
framework := &reporthandling.Framework{}
for _, f := range files {
@@ -223,7 +223,7 @@ func (scanInfo *ScanInfo) contains(policyName string) bool {
return false
}
func scanInfoToScanMetadata(scanInfo *ScanInfo) *reporthandlingv2.Metadata {
func scanInfoToScanMetadata(ctx context.Context, scanInfo *ScanInfo) *reporthandlingv2.Metadata {
metadata := &reporthandlingv2.Metadata{}
metadata.ScanMetadata.Format = scanInfo.Format
@@ -277,7 +277,7 @@ func scanInfoToScanMetadata(scanInfo *ScanInfo) *reporthandlingv2.Metadata {
}
setContextMetadata(&metadata.ContextMetadata, inputFiles)
setContextMetadata(ctx, &metadata.ContextMetadata, inputFiles)
return metadata
}
@@ -321,7 +321,7 @@ func GetScanningContext(input string) ScanningContext {
// dir/glob
return ContextDir
}
func setContextMetadata(contextMetadata *reporthandlingv2.ContextMetadata, input string) {
func setContextMetadata(ctx context.Context, contextMetadata *reporthandlingv2.ContextMetadata, input string) {
switch GetScanningContext(input) {
case ContextCluster:
contextMetadata.ClusterContextMetadata = &reporthandlingv2.ClusterMetadata{
@@ -331,7 +331,7 @@ func setContextMetadata(contextMetadata *reporthandlingv2.ContextMetadata, input
// url
context, err := metadataGitURL(input)
if err != nil {
logger.L().Warning("in setContextMetadata", helpers.Interface("case", ContextGitURL), helpers.Error(err))
logger.L().Ctx(ctx).Warning("in setContextMetadata", helpers.Interface("case", ContextGitURL), helpers.Error(err))
}
contextMetadata.RepoContextMetadata = context
case ContextDir:
@@ -348,7 +348,7 @@ func setContextMetadata(contextMetadata *reporthandlingv2.ContextMetadata, input
// local
context, err := metadataGitLocal(input)
if err != nil {
logger.L().Warning("in setContextMetadata", helpers.Interface("case", ContextGitURL), helpers.Error(err))
logger.L().Ctx(ctx).Warning("in setContextMetadata", helpers.Interface("case", ContextGitURL), helpers.Error(err))
}
contextMetadata.RepoContextMetadata = context
}

View File

@@ -1,6 +1,7 @@
package cautils
import (
"context"
"testing"
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
@@ -10,7 +11,7 @@ import (
func TestSetContextMetadata(t *testing.T) {
{
ctx := reporthandlingv2.ContextMetadata{}
setContextMetadata(&ctx, "")
setContextMetadata(context.TODO(), &ctx, "")
assert.NotNil(t, ctx.ClusterContextMetadata)
assert.Nil(t, ctx.DirectoryContextMetadata)

View File

@@ -1,6 +1,7 @@
package cautils
import (
"context"
"encoding/json"
"fmt"
"net/http"
@@ -10,7 +11,7 @@ import (
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/kubescape/v2/core/cautils/getter"
"go.opentelemetry.io/otel"
"golang.org/x/mod/semver"
)
@@ -25,12 +26,12 @@ var LatestReleaseVersion string
const UnknownBuildNumber = "unknown"
type IVersionCheckHandler interface {
CheckLatestVersion(*VersionCheckRequest) error
CheckLatestVersion(context.Context, *VersionCheckRequest) error
}
func NewIVersionCheckHandler() IVersionCheckHandler {
func NewIVersionCheckHandler(ctx context.Context) IVersionCheckHandler {
if BuildNumber == "" {
logger.L().Warning("unknown build number, this might affect your scan results. Please make sure you are updated to latest version")
logger.L().Ctx(ctx).Warning("unknown build number, this might affect your scan results. Please make sure you are updated to latest version")
}
if v, ok := os.LookupEnv(CLIENT_ENV); ok && v != "" {
@@ -98,15 +99,17 @@ func NewVersionCheckRequest(buildNumber, frameworkName, frameworkVersion, scanni
}
}
func (v *VersionCheckHandlerMock) CheckLatestVersion(versionData *VersionCheckRequest) error {
func (v *VersionCheckHandlerMock) CheckLatestVersion(_ context.Context, _ *VersionCheckRequest) error {
logger.L().Info("Skipping version check")
return nil
}
func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckRequest) error {
func (v *VersionCheckHandler) CheckLatestVersion(ctx context.Context, versionData *VersionCheckRequest) error {
ctx, span := otel.Tracer("").Start(ctx, "versionCheckHandler.CheckLatestVersion")
defer span.End()
defer func() {
if err := recover(); err != nil {
logger.L().Warning("failed to get latest version", helpers.Interface("error", err))
logger.L().Ctx(ctx).Warning("failed to get latest version", helpers.Interface("error", err))
}
}()
@@ -119,7 +122,7 @@ func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckReques
if latestVersion.ClientUpdate != "" {
if BuildNumber != "" && semver.Compare(BuildNumber, LatestReleaseVersion) == -1 {
logger.L().Warning(warningMessage(LatestReleaseVersion))
logger.L().Ctx(ctx).Warning(warningMessage(LatestReleaseVersion))
}
}

View File

@@ -4,6 +4,7 @@ import (
"strings"
"github.com/kubescape/k8s-interface/cloudsupport"
cloudapis "github.com/kubescape/k8s-interface/cloudsupport/apis"
"github.com/kubescape/opa-utils/reporthandling/apis"
)
@@ -20,9 +21,12 @@ var (
"KubeProxyInfo",
"ControlPlaneInfo",
"CloudProviderInfo",
"CNIInfo",
}
CloudResources = []string{
"ClusterDescribe",
cloudapis.CloudProviderDescribeKind,
cloudapis.CloudProviderDescribeRepositoriesKind,
cloudapis.CloudProviderListEntitiesForPoliciesKind,
string(cloudsupport.TypeApiServerInfo),
}
)

View File

@@ -1,6 +1,7 @@
package core
import (
"context"
"fmt"
metav1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
@@ -42,8 +43,8 @@ func (ks *Kubescape) ViewCachedConfig(viewConfig *metav1.ViewConfig) error {
return nil
}
func (ks *Kubescape) DeleteCachedConfig(deleteConfig *metav1.DeleteConfig) error {
func (ks *Kubescape) DeleteCachedConfig(ctx context.Context, deleteConfig *metav1.DeleteConfig) error {
tenant := getTenantConfig(nil, "", "", getKubernetesApi()) // change k8sinterface
return tenant.DeleteCachedConfig()
return tenant.DeleteCachedConfig(ctx)
}

View File

@@ -1,6 +1,7 @@
package core
import (
"context"
"fmt"
"os"
"path/filepath"
@@ -21,7 +22,7 @@ const (
TargetAttackTracks = "attack-tracks"
)
var downloadFunc = map[string]func(*metav1.DownloadInfo) error{
var downloadFunc = map[string]func(context.Context, *metav1.DownloadInfo) error{
TargetControlsInputs: downloadConfigInputs,
TargetExceptions: downloadExceptions,
TargetControl: downloadControl,
@@ -38,20 +39,20 @@ func DownloadSupportCommands() []string {
return commands
}
func (ks *Kubescape) Download(downloadInfo *metav1.DownloadInfo) error {
func (ks *Kubescape) Download(ctx context.Context, downloadInfo *metav1.DownloadInfo) error {
setPathandFilename(downloadInfo)
if err := os.MkdirAll(downloadInfo.Path, os.ModePerm); err != nil {
return err
}
if err := downloadArtifact(downloadInfo, downloadFunc); err != nil {
if err := downloadArtifact(ctx, downloadInfo, downloadFunc); err != nil {
return err
}
return nil
}
func downloadArtifact(downloadInfo *metav1.DownloadInfo, downloadArtifactFunc map[string]func(*metav1.DownloadInfo) error) error {
func downloadArtifact(ctx context.Context, downloadInfo *metav1.DownloadInfo, downloadArtifactFunc map[string]func(context.Context, *metav1.DownloadInfo) error) error {
if f, ok := downloadArtifactFunc[downloadInfo.Target]; ok {
if err := f(downloadInfo); err != nil {
if err := f(ctx, downloadInfo); err != nil {
return err
}
return nil
@@ -73,26 +74,26 @@ func setPathandFilename(downloadInfo *metav1.DownloadInfo) {
}
}
func downloadArtifacts(downloadInfo *metav1.DownloadInfo) error {
func downloadArtifacts(ctx context.Context, downloadInfo *metav1.DownloadInfo) error {
downloadInfo.FileName = ""
var artifacts = map[string]func(*metav1.DownloadInfo) error{
var artifacts = map[string]func(context.Context, *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 {
logger.L().Error("error downloading", helpers.String("artifact", artifact), helpers.Error(err))
if err := downloadArtifact(ctx, &metav1.DownloadInfo{Target: artifact, Path: downloadInfo.Path, FileName: fmt.Sprintf("%s.json", artifact)}, artifacts); err != nil {
logger.L().Ctx(ctx).Error("error downloading", helpers.String("artifact", artifact), helpers.Error(err))
}
}
return nil
}
func downloadConfigInputs(downloadInfo *metav1.DownloadInfo) error {
func downloadConfigInputs(ctx context.Context, downloadInfo *metav1.DownloadInfo) error {
tenant := getTenantConfig(&downloadInfo.Credentials, "", "", getKubernetesApi())
controlsInputsGetter := getConfigInputsGetter(downloadInfo.Identifier, tenant.GetAccountID(), nil)
controlsInputsGetter := getConfigInputsGetter(ctx, downloadInfo.Identifier, tenant.GetAccountID(), nil)
controlInputs, err := controlsInputsGetter.GetControlsInputs(tenant.GetContextName())
if err != nil {
return err
@@ -112,9 +113,9 @@ func downloadConfigInputs(downloadInfo *metav1.DownloadInfo) error {
return nil
}
func downloadExceptions(downloadInfo *metav1.DownloadInfo) error {
func downloadExceptions(ctx context.Context, downloadInfo *metav1.DownloadInfo) error {
tenant := getTenantConfig(&downloadInfo.Credentials, "", "", getKubernetesApi())
exceptionsGetter := getExceptionsGetter("", tenant.GetAccountID(), nil)
exceptionsGetter := getExceptionsGetter(ctx, "", tenant.GetAccountID(), nil)
exceptions, err := exceptionsGetter.GetExceptions(tenant.GetContextName())
if err != nil {
@@ -129,15 +130,15 @@ func downloadExceptions(downloadInfo *metav1.DownloadInfo) error {
if err != nil {
return err
}
logger.L().Success("Downloaded", helpers.String("artifact", downloadInfo.Target), helpers.String("path", filepath.Join(downloadInfo.Path, downloadInfo.FileName)))
logger.L().Ctx(ctx).Success("Downloaded", helpers.String("artifact", downloadInfo.Target), helpers.String("path", filepath.Join(downloadInfo.Path, downloadInfo.FileName)))
return nil
}
func downloadAttackTracks(downloadInfo *metav1.DownloadInfo) error {
func downloadAttackTracks(ctx context.Context, downloadInfo *metav1.DownloadInfo) error {
var err error
tenant := getTenantConfig(&downloadInfo.Credentials, "", "", getKubernetesApi())
attackTracksGetter := getAttackTracksGetter("", tenant.GetAccountID(), nil)
attackTracksGetter := getAttackTracksGetter(ctx, "", tenant.GetAccountID(), nil)
attackTracks, err := attackTracksGetter.GetAttackTracks()
if err != nil {
@@ -157,11 +158,11 @@ func downloadAttackTracks(downloadInfo *metav1.DownloadInfo) error {
}
func downloadFramework(downloadInfo *metav1.DownloadInfo) error {
func downloadFramework(ctx context.Context, downloadInfo *metav1.DownloadInfo) error {
tenant := getTenantConfig(&downloadInfo.Credentials, "", "", getKubernetesApi())
g := getPolicyGetter(nil, tenant.GetTenantEmail(), true, nil)
g := getPolicyGetter(ctx, nil, tenant.GetTenantEmail(), true, nil)
if downloadInfo.Identifier == "" {
// if framework name not specified - download all frameworks
@@ -199,11 +200,11 @@ func downloadFramework(downloadInfo *metav1.DownloadInfo) error {
return nil
}
func downloadControl(downloadInfo *metav1.DownloadInfo) error {
func downloadControl(ctx context.Context, downloadInfo *metav1.DownloadInfo) error {
tenant := getTenantConfig(&downloadInfo.Credentials, "", "", getKubernetesApi())
g := getPolicyGetter(nil, tenant.GetTenantEmail(), false, nil)
g := getPolicyGetter(ctx, nil, tenant.GetTenantEmail(), false, nil)
if downloadInfo.Identifier == "" {
// TODO - support

View File

@@ -1,6 +1,7 @@
package core
import (
"context"
"fmt"
"strings"
@@ -14,14 +15,14 @@ const NoChangesApplied = "No changes were applied."
const NoResourcesToFix = "No issues to fix."
const ConfirmationQuestion = "Would you like to apply the changes to the files above? [y|n]: "
func (ks *Kubescape) Fix(fixInfo *metav1.FixInfo) error {
func (ks *Kubescape) Fix(ctx context.Context, fixInfo *metav1.FixInfo) error {
logger.L().Info("Reading report file...")
handler, err := fixhandler.NewFixHandler(fixInfo)
if err != nil {
return err
}
resourcesToFix := handler.PrepareResourcesToFix()
resourcesToFix := handler.PrepareResourcesToFix(ctx)
if len(resourcesToFix) == 0 {
logger.L().Info(NoResourcesToFix)
@@ -40,12 +41,12 @@ func (ks *Kubescape) Fix(fixInfo *metav1.FixInfo) error {
return nil
}
updatedFilesCount, errors := handler.ApplyChanges(resourcesToFix)
updatedFilesCount, errors := handler.ApplyChanges(ctx, resourcesToFix)
logger.L().Info(fmt.Sprintf("Fixed resources in %d files.", updatedFilesCount))
if len(errors) > 0 {
for _, err := range errors {
logger.L().Error(err.Error())
logger.L().Ctx(ctx).Error(err.Error())
}
return fmt.Errorf("Failed to fix some resources, check the logs for more details")
}

View File

@@ -1,6 +1,7 @@
package core
import (
"context"
"fmt"
"os"
@@ -15,6 +16,7 @@ import (
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"
"go.opentelemetry.io/otel"
"github.com/google/uuid"
@@ -35,7 +37,7 @@ func getTenantConfig(credentials *cautils.Credentials, clusterName string, custo
return cautils.NewClusterConfig(k8s, getter.GetKSCloudAPIConnector(), credentials, clusterName, customClusterName)
}
func getExceptionsGetter(useExceptions string, accountID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IExceptionsGetter {
func getExceptionsGetter(ctx context.Context, useExceptions string, accountID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IExceptionsGetter {
if useExceptions != "" {
// load exceptions from file
return getter.NewLoadPolicy([]string{useExceptions})
@@ -49,7 +51,7 @@ func getExceptionsGetter(useExceptions string, accountID string, downloadRelease
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))
logger.L().Ctx(ctx).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
@@ -63,7 +65,9 @@ func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.Kubern
return nil
}
func getReporter(tenantConfig cautils.ITenantConfig, reportID string, submit, fwScan bool, scanningContext cautils.ScanningContext) reporter.IReport {
func getReporter(ctx context.Context, tenantConfig cautils.ITenantConfig, reportID string, submit, fwScan bool, scanningContext cautils.ScanningContext) reporter.IReport {
ctx, span := otel.Tracer("").Start(ctx, "getReporter")
defer span.End()
if submit {
submitData := reporterv2.SubmitContextScan
if scanningContext != cautils.ContextCluster {
@@ -83,17 +87,19 @@ func getReporter(tenantConfig cautils.ITenantConfig, reportID string, submit, fw
return reporterv2.NewReportMock("", message)
}
func getResourceHandler(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, hostSensorHandler hostsensorutils.IHostSensor, registryAdaptors *resourcehandler.RegistryAdaptors) resourcehandler.IResourceHandler {
func getResourceHandler(ctx context.Context, scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, hostSensorHandler hostsensorutils.IHostSensor, registryAdaptors *resourcehandler.RegistryAdaptors) resourcehandler.IResourceHandler {
ctx, span := otel.Tracer("").Start(ctx, "getResourceHandler")
defer span.End()
if len(scanInfo.InputPatterns) > 0 || k8s == nil {
// scanInfo.HostSensor.SetBool(false)
return resourcehandler.NewFileResourceHandler(scanInfo.InputPatterns, registryAdaptors)
return resourcehandler.NewFileResourceHandler(ctx, scanInfo.InputPatterns, registryAdaptors)
}
getter.GetKSCloudAPIConnector()
rbacObjects := getRBACHandler(tenantConfig, k8s, scanInfo.Submit)
return resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo), hostSensorHandler, rbacObjects, registryAdaptors)
}
func getHostSensorHandler(scanInfo *cautils.ScanInfo, k8s *k8sinterface.KubernetesApi) hostsensorutils.IHostSensor {
func getHostSensorHandler(ctx context.Context, scanInfo *cautils.ScanInfo, k8s *k8sinterface.KubernetesApi) hostsensorutils.IHostSensor {
if !k8sinterface.IsConnectedToCluster() || k8s == nil {
return &hostsensorutils.HostSensorHandlerMock{}
}
@@ -102,12 +108,11 @@ 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")
}
if hostSensorVal := scanInfo.HostSensorEnabled.Get(); hostSensorVal != nil && *hostSensorVal {
hostSensorHandler, err := hostsensorutils.NewHostSensorHandler(k8s, scanInfo.HostSensorYamlPath)
if err != nil {
logger.L().Warning(fmt.Sprintf("failed to create host scanner: %s", err.Error()))
logger.L().Ctx(ctx).Warning(fmt.Sprintf("failed to create host scanner: %s", err.Error()))
return &hostsensorutils.HostSensorHandlerMock{}
}
return hostSensorHandler
@@ -189,7 +194,7 @@ func setSubmitBehavior(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantC
}
// setPolicyGetter set the policy getter - local file/github release/Kubescape Cloud API
func getPolicyGetter(loadPoliciesFromFile []string, tenantEmail string, frameworkScope bool, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IPolicyGetter {
func getPolicyGetter(ctx context.Context, loadPoliciesFromFile []string, tenantEmail string, frameworkScope bool, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IPolicyGetter {
if len(loadPoliciesFromFile) > 0 {
return getter.NewLoadPolicy(loadPoliciesFromFile)
}
@@ -200,12 +205,12 @@ func getPolicyGetter(loadPoliciesFromFile []string, tenantEmail string, framewor
if downloadReleasedPolicy == nil {
downloadReleasedPolicy = getter.NewDownloadReleasedPolicy()
}
return getDownloadReleasedPolicy(downloadReleasedPolicy)
return getDownloadReleasedPolicy(ctx, downloadReleasedPolicy)
}
// setConfigInputsGetter sets the config input getter - local file/github release/Kubescape Cloud API
func getConfigInputsGetter(ControlsInputs string, accountID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IControlsInputsGetter {
func getConfigInputsGetter(ctx context.Context, ControlsInputs string, accountID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IControlsInputsGetter {
if len(ControlsInputs) > 0 {
return getter.NewLoadPolicy([]string{ControlsInputs})
}
@@ -217,14 +222,14 @@ func getConfigInputsGetter(ControlsInputs string, accountID string, downloadRele
downloadReleasedPolicy = getter.NewDownloadReleasedPolicy()
}
if err := downloadReleasedPolicy.SetRegoObjects(); err != nil { // if failed to pull config inputs, fallback to BE
logger.L().Warning("failed to get config inputs from github release, this may affect the scanning results", helpers.Error(err))
logger.L().Ctx(ctx).Warning("failed to get config inputs from github release, this may affect the scanning results", helpers.Error(err))
}
return downloadReleasedPolicy
}
func getDownloadReleasedPolicy(downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IPolicyGetter {
func getDownloadReleasedPolicy(ctx context.Context, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IPolicyGetter {
if err := downloadReleasedPolicy.SetRegoObjects(); err != nil { // if failed to pull policy, fallback to cache
logger.L().Warning("failed to get policies from github release, loading policies from cache", helpers.Error(err))
logger.L().Ctx(ctx).Warning("failed to get policies from github release, loading policies from cache", helpers.Error(err))
return getter.NewLoadPolicy(getDefaultFrameworksPaths())
} else {
return downloadReleasedPolicy
@@ -247,7 +252,7 @@ func listFrameworksNames(policyGetter getter.IPolicyGetter) []string {
return getter.NativeFrameworks
}
func getAttackTracksGetter(attackTracks, accountID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IAttackTracksGetter {
func getAttackTracksGetter(ctx context.Context, attackTracks, accountID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IAttackTracksGetter {
if len(attackTracks) > 0 {
return getter.NewLoadPolicy([]string{attackTracks})
}
@@ -260,18 +265,18 @@ func getAttackTracksGetter(attackTracks, accountID string, downloadReleasedPolic
}
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))
logger.L().Ctx(ctx).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 {
func getUIPrinter(ctx context.Context, 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())
p.SetWriter(ctx, os.Stdout.Name())
return p
}

View File

@@ -1,6 +1,7 @@
package core
import (
"context"
"reflect"
"testing"
@@ -17,7 +18,7 @@ func Test_getUIPrinter(t *testing.T) {
wantVerboseMode := scanInfo.VerboseMode
wantViewType := cautils.ViewTypes(scanInfo.View)
got := getUIPrinter(scanInfo.VerboseMode, scanInfo.FormatVersion, scanInfo.PrintAttackTree, cautils.ViewTypes(scanInfo.View))
got := getUIPrinter(context.TODO(), scanInfo.VerboseMode, scanInfo.FormatVersion, scanInfo.PrintAttackTree, cautils.ViewTypes(scanInfo.View))
gotValue := reflect.ValueOf(got).Elem()
gotFormatVersion := gotValue.FieldByName("formatVersion").String()

View File

@@ -1,6 +1,7 @@
package core
import (
"context"
"encoding/json"
"fmt"
"sort"
@@ -13,13 +14,13 @@ import (
"github.com/olekukonko/tablewriter"
)
var listFunc = map[string]func(*metav1.ListPolicies) ([]string, error){
var listFunc = map[string]func(context.Context, *metav1.ListPolicies) ([]string, error){
"controls": listControls,
"frameworks": listFrameworks,
"exceptions": listExceptions,
}
var listFormatFunc = map[string]func(string, []string){
var listFormatFunc = map[string]func(context.Context, string, []string){
"pretty-print": prettyPrintListFormat,
"json": jsonListFormat,
}
@@ -31,16 +32,16 @@ func ListSupportActions() []string {
}
return commands
}
func (ks *Kubescape) List(listPolicies *metav1.ListPolicies) error {
func (ks *Kubescape) List(ctx context.Context, listPolicies *metav1.ListPolicies) error {
if policyListerFunc, ok := listFunc[listPolicies.Target]; ok {
policies, err := policyListerFunc(listPolicies)
policies, err := policyListerFunc(ctx, listPolicies)
if err != nil {
return err
}
sort.Strings(policies)
if listFormatFunction, ok := listFormatFunc[listPolicies.Format]; ok {
listFormatFunction(listPolicies.Target, policies)
listFormatFunction(ctx, listPolicies.Target, policies)
} else {
return fmt.Errorf("Invalid format \"%s\", Supported formats: 'pretty-print'/'json' ", listPolicies.Format)
}
@@ -50,26 +51,26 @@ func (ks *Kubescape) List(listPolicies *metav1.ListPolicies) error {
return fmt.Errorf("unknown command to download")
}
func listFrameworks(listPolicies *metav1.ListPolicies) ([]string, error) {
func listFrameworks(ctx context.Context, listPolicies *metav1.ListPolicies) ([]string, error) {
tenant := getTenantConfig(&listPolicies.Credentials, "", "", getKubernetesApi()) // change k8sinterface
policyGetter := getPolicyGetter(nil, tenant.GetTenantEmail(), true, nil)
policyGetter := getPolicyGetter(ctx, nil, tenant.GetTenantEmail(), true, nil)
return listFrameworksNames(policyGetter), nil
}
func listControls(listPolicies *metav1.ListPolicies) ([]string, error) {
func listControls(ctx context.Context, listPolicies *metav1.ListPolicies) ([]string, error) {
tenant := getTenantConfig(&listPolicies.Credentials, "", "", getKubernetesApi()) // change k8sinterface
policyGetter := getPolicyGetter(nil, tenant.GetTenantEmail(), false, nil)
policyGetter := getPolicyGetter(ctx, nil, tenant.GetTenantEmail(), false, nil)
return policyGetter.ListControls()
}
func listExceptions(listPolicies *metav1.ListPolicies) ([]string, error) {
func listExceptions(ctx context.Context, listPolicies *metav1.ListPolicies) ([]string, error) {
// load tenant metav1
tenant := getTenantConfig(&listPolicies.Credentials, "", "", getKubernetesApi())
var exceptionsNames []string
ksCloudAPI := getExceptionsGetter("", tenant.GetAccountID(), nil)
ksCloudAPI := getExceptionsGetter(ctx, "", tenant.GetAccountID(), nil)
exceptions, err := ksCloudAPI.GetExceptions("")
if err != nil {
return exceptionsNames, err
@@ -80,15 +81,15 @@ func listExceptions(listPolicies *metav1.ListPolicies) ([]string, error) {
return exceptionsNames, nil
}
func prettyPrintListFormat(targetPolicy string, policies []string) {
func prettyPrintListFormat(ctx context.Context, targetPolicy string, policies []string) {
if targetPolicy == "controls" {
prettyPrintControls(policies)
prettyPrintControls(ctx, policies)
return
}
header := fmt.Sprintf("Supported %s", targetPolicy)
policyTable := tablewriter.NewWriter(printer.GetWriter(""))
policyTable := tablewriter.NewWriter(printer.GetWriter(ctx, ""))
policyTable.SetAutoWrapText(true)
policyTable.SetHeader([]string{header})
policyTable.SetHeaderLine(true)
@@ -103,14 +104,14 @@ func prettyPrintListFormat(targetPolicy string, policies []string) {
policyTable.Render()
}
func jsonListFormat(targetPolicy string, policies []string) {
func jsonListFormat(_ context.Context, _ string, policies []string) {
j, _ := json.MarshalIndent(policies, "", " ")
fmt.Printf("%s\n", j)
}
func prettyPrintControls(policies []string) {
controlsTable := tablewriter.NewWriter(printer.GetWriter(""))
func prettyPrintControls(ctx context.Context, policies []string) {
controlsTable := tablewriter.NewWriter(printer.GetWriter(ctx, ""))
controlsTable.SetAutoWrapText(true)
controlsTable.SetHeader([]string{"Control ID", "Control Name", "Docs", "Frameworks"})
controlsTable.SetHeaderLine(true)

View File

@@ -1,14 +1,12 @@
package core
import (
"context"
"fmt"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/cautils/getter"
"github.com/kubescape/kubescape/v2/core/pkg/hostsensorutils"
@@ -19,6 +17,8 @@ import (
"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"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
"go.opentelemetry.io/otel"
"github.com/kubescape/opa-utils/resources"
)
@@ -32,19 +32,21 @@ type componentInterfaces struct {
hostSensorHandler hostsensorutils.IHostSensor
}
func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
func getInterfaces(ctx context.Context, scanInfo *cautils.ScanInfo) componentInterfaces {
ctx, span := otel.Tracer("").Start(ctx, "getInterfaces")
defer span.End()
// ================== setup k8s interface object ======================================
var k8s *k8sinterface.KubernetesApi
if scanInfo.GetScanningContext() == cautils.ContextCluster {
k8s = getKubernetesApi()
if k8s == nil {
logger.L().Fatal("failed connecting to Kubernetes cluster")
logger.L().Ctx(ctx).Fatal("failed connecting to Kubernetes cluster")
}
}
// ================== setup tenant object ======================================
ctxTenant, spanTenant := otel.Tracer("").Start(ctx, "setup tenant")
tenantConfig := getTenantConfig(&scanInfo.Credentials, scanInfo.KubeContext, scanInfo.CustomClusterName, k8s)
// Set submit behavior AFTER loading tenant config
@@ -53,58 +55,60 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
if scanInfo.Submit {
// submit - Create tenant & Submit report
if err := tenantConfig.SetTenant(); err != nil {
logger.L().Error(err.Error())
logger.L().Ctx(ctxTenant).Error(err.Error())
}
if scanInfo.OmitRawResources {
logger.L().Warning("omit-raw-resources flag will be ignored in submit mode")
logger.L().Ctx(ctx).Warning("omit-raw-resources flag will be ignored in submit mode")
}
}
spanTenant.End()
// ================== version testing ======================================
v := cautils.NewIVersionCheckHandler()
v.CheckLatestVersion(cautils.NewVersionCheckRequest(cautils.BuildNumber, policyIdentifierIdentities(scanInfo.PolicyIdentifier), "", cautils.ScanningContextToScanningScope(scanInfo.GetScanningContext())))
v := cautils.NewIVersionCheckHandler(ctx)
v.CheckLatestVersion(ctx, cautils.NewVersionCheckRequest(cautils.BuildNumber, policyIdentifierIdentities(scanInfo.PolicyIdentifier), "", cautils.ScanningContextToScanningScope(scanInfo.GetScanningContext())))
// ================== setup host scanner object ======================================
hostSensorHandler := getHostSensorHandler(scanInfo, k8s)
if err := hostSensorHandler.Init(); err != nil {
logger.L().Error("failed to init host scanner", helpers.Error(err))
ctxHostScanner, spanHostScanner := otel.Tracer("").Start(ctx, "setup host scanner")
hostSensorHandler := getHostSensorHandler(ctx, scanInfo, k8s)
if err := hostSensorHandler.Init(ctxHostScanner); err != nil {
logger.L().Ctx(ctxHostScanner).Error("failed to init host scanner", helpers.Error(err))
hostSensorHandler = &hostsensorutils.HostSensorHandlerMock{}
}
// excluding hostsensor namespace
if len(scanInfo.IncludeNamespaces) == 0 && hostSensorHandler.GetNamespace() != "" {
scanInfo.ExcludedNamespaces = fmt.Sprintf("%s,%s", scanInfo.ExcludedNamespaces, hostSensorHandler.GetNamespace())
}
spanHostScanner.End()
// ================== setup registry adaptors ======================================
registryAdaptors, err := resourcehandler.NewRegistryAdaptors()
if err != nil {
logger.L().Error("failed to initialize registry adaptors", helpers.Error(err))
logger.L().Ctx(ctx).Error("failed to initialize registry adaptors", helpers.Error(err))
}
// ================== setup resource collector object ======================================
resourceHandler := getResourceHandler(scanInfo, tenantConfig, k8s, hostSensorHandler, registryAdaptors)
resourceHandler := getResourceHandler(ctx, scanInfo, tenantConfig, k8s, hostSensorHandler, registryAdaptors)
// ================== setup reporter & printer objects ======================================
// reporting behavior - setup reporter
reportHandler := getReporter(tenantConfig, scanInfo.ScanID, scanInfo.Submit, scanInfo.FrameworkScan, scanInfo.GetScanningContext())
reportHandler := getReporter(ctx, tenantConfig, scanInfo.ScanID, scanInfo.Submit, scanInfo.FrameworkScan, scanInfo.GetScanningContext())
// 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)
printerHandler := resultshandling.NewPrinter(ctx, format, scanInfo.FormatVersion, scanInfo.PrintAttackTree, scanInfo.VerboseMode, cautils.ViewTypes(scanInfo.View))
printerHandler.SetWriter(ctx, scanInfo.Output)
outputPrinters = append(outputPrinters, printerHandler)
}
uiPrinter := getUIPrinter(scanInfo.VerboseMode, scanInfo.FormatVersion, scanInfo.PrintAttackTree, cautils.ViewTypes(scanInfo.View))
uiPrinter := getUIPrinter(ctx, scanInfo.VerboseMode, scanInfo.FormatVersion, scanInfo.PrintAttackTree, cautils.ViewTypes(scanInfo.View))
// ================== return interface ======================================
@@ -118,13 +122,16 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
}
}
func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsHandler, error) {
func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*resultshandling.ResultsHandler, error) {
ctx, spanScan := otel.Tracer("").Start(ctx, "kubescape.Scan")
defer spanScan.End()
logger.L().Info("Kubescape scanner starting")
// ===================== Initialization =====================
scanInfo.Init() // initialize scan info
ctxInit, spanInit := otel.Tracer("").Start(ctx, "initialization")
scanInfo.Init(ctxInit) // initialize scan info
interfaces := getInterfaces(scanInfo)
interfaces := getInterfaces(ctxInit, scanInfo)
cautils.ClusterName = interfaces.tenantConfig.GetContextName() // TODO - Deprecated
cautils.CustomerGUID = interfaces.tenantConfig.GetAccountID() // TODO - Deprecated
@@ -134,10 +141,10 @@ func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsH
downloadReleasedPolicy := getter.NewDownloadReleasedPolicy() // download config inputs from github release
// set policy getter only after setting the customerGUID
scanInfo.Getters.PolicyGetter = getPolicyGetter(scanInfo.UseFrom, interfaces.tenantConfig.GetTenantEmail(), scanInfo.FrameworkScan, downloadReleasedPolicy)
scanInfo.Getters.ControlsInputsGetter = getConfigInputsGetter(scanInfo.ControlsInputs, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
scanInfo.Getters.ExceptionsGetter = getExceptionsGetter(scanInfo.UseExceptions, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
scanInfo.Getters.AttackTracksGetter = getAttackTracksGetter(scanInfo.AttackTracks, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
scanInfo.Getters.PolicyGetter = getPolicyGetter(ctx, scanInfo.UseFrom, interfaces.tenantConfig.GetTenantEmail(), scanInfo.FrameworkScan, downloadReleasedPolicy)
scanInfo.Getters.ControlsInputsGetter = getConfigInputsGetter(ctx, scanInfo.ControlsInputs, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
scanInfo.Getters.ExceptionsGetter = getExceptionsGetter(ctx, scanInfo.UseExceptions, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
scanInfo.Getters.AttackTracksGetter = getAttackTracksGetter(ctx, scanInfo.AttackTracks, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
// TODO - list supported frameworks/controls
if scanInfo.ScanAll {
@@ -147,34 +154,40 @@ func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsH
// remove host scanner components
defer func() {
if err := interfaces.hostSensorHandler.TearDown(); err != nil {
logger.L().Error("failed to tear down host scanner", helpers.Error(err))
logger.L().Ctx(ctxInit).Error("failed to tear down host scanner", helpers.Error(err))
}
}()
resultsHandling := resultshandling.NewResultsHandler(interfaces.report, interfaces.outputPrinters, interfaces.uiPrinter)
spanInit.End()
// ===================== policies & resources =====================
ctxPolicies, spanPolicies := otel.Tracer("").Start(ctx, "policies & resources")
policyHandler := policyhandler.NewPolicyHandler(interfaces.resourceHandler)
scanData, err := policyHandler.CollectResources(scanInfo.PolicyIdentifier, scanInfo)
scanData, err := policyHandler.CollectResources(ctxPolicies, scanInfo.PolicyIdentifier, scanInfo)
if err != nil {
return resultsHandling, err
}
spanPolicies.End()
// ========================= opa testing =====================
ctxOpa, spanOpa := otel.Tracer("").Start(ctx, "opa testing")
deps := resources.NewRegoDependenciesData(k8sinterface.GetK8sConfig(), interfaces.tenantConfig.GetContextName())
reportResults := opaprocessor.NewOPAProcessor(scanData, deps)
if err := reportResults.ProcessRulesListenner(); err != nil {
if err := reportResults.ProcessRulesListenner(ctxOpa, cautils.NewProgressHandler("")); err != nil {
// TODO - do something
return resultsHandling, fmt.Errorf("%w", err)
}
spanOpa.End()
// ======================== prioritization ===================
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))
_, spanPrioritization := otel.Tracer("").Start(ctx, "prioritization")
if priotizationHandler, err := resourcesprioritization.NewResourcesPrioritizationHandler(ctx, scanInfo.Getters.AttackTracksGetter, scanInfo.PrintAttackTree); err != nil {
logger.L().Ctx(ctx).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)
}
spanPrioritization.End()
// ========================= results handling =====================
resultsHandling.SetData(scanData)

View File

@@ -1,6 +1,8 @@
package core
import (
"context"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/cautils/getter"
"github.com/kubescape/kubescape/v2/core/meta/cliinterfaces"
@@ -9,7 +11,7 @@ import (
"github.com/kubescape/go-logger/helpers"
)
func (ks *Kubescape) Submit(submitInterfaces cliinterfaces.SubmitInterfaces) error {
func (ks *Kubescape) Submit(ctx context.Context, submitInterfaces cliinterfaces.SubmitInterfaces) error {
// list resources
report, err := submitInterfaces.SubmitObjects.SetResourcesReport()
@@ -26,7 +28,7 @@ func (ks *Kubescape) Submit(submitInterfaces cliinterfaces.SubmitInterfaces) err
AllResources: allresources,
Metadata: &report.Metadata,
}
if err := submitInterfaces.Reporter.Submit(o); err != nil {
if err := submitInterfaces.Reporter.Submit(ctx, o); err != nil {
return err
}
logger.L().Success("Data has been submitted successfully")
@@ -35,13 +37,13 @@ func (ks *Kubescape) Submit(submitInterfaces cliinterfaces.SubmitInterfaces) err
return nil
}
func (ks *Kubescape) SubmitExceptions(credentials *cautils.Credentials, excPath string) error {
func (ks *Kubescape) SubmitExceptions(ctx context.Context, credentials *cautils.Credentials, excPath string) error {
logger.L().Info("submitting exceptions", helpers.String("path", excPath))
// load cached config
tenantConfig := getTenantConfig(credentials, "", "", getKubernetesApi())
if err := tenantConfig.SetTenant(); err != nil {
logger.L().Error("failed setting account ID", helpers.Error(err))
logger.L().Ctx(ctx).Error("failed setting account ID", helpers.Error(err))
}
// load exceptions from file

View File

@@ -1,6 +1,8 @@
package meta
import (
"context"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/kubescape/v2/core/meta/cliinterfaces"
metav1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
@@ -8,24 +10,24 @@ import (
)
type IKubescape interface {
Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsHandler, error) // TODO - use scanInfo from v1
Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*resultshandling.ResultsHandler, error) // TODO - use scanInfo from v1
// policies
List(listPolicies *metav1.ListPolicies) error // TODO - return list response
Download(downloadInfo *metav1.DownloadInfo) error // TODO - return downloaded policies
List(ctx context.Context, listPolicies *metav1.ListPolicies) error // TODO - return list response
Download(ctx context.Context, downloadInfo *metav1.DownloadInfo) error // TODO - return downloaded policies
// submit
Submit(submitInterfaces cliinterfaces.SubmitInterfaces) error // TODO - func should receive object
SubmitExceptions(credentials *cautils.Credentials, excPath string) error // TODO - remove
Submit(ctx context.Context, submitInterfaces cliinterfaces.SubmitInterfaces) error // TODO - func should receive object
SubmitExceptions(ctx context.Context, credentials *cautils.Credentials, excPath string) error // TODO - remove
// config
SetCachedConfig(setConfig *metav1.SetConfig) error
ViewCachedConfig(viewConfig *metav1.ViewConfig) error
DeleteCachedConfig(deleteConfig *metav1.DeleteConfig) error
DeleteCachedConfig(ctx context.Context, deleteConfig *metav1.DeleteConfig) error
// delete
DeleteExceptions(deleteexceptions *metav1.DeleteExceptions) error
// fix
Fix(fixInfo *metav1.FixInfo) error
Fix(ctx context.Context, fixInfo *metav1.FixInfo) error
}

View File

@@ -1,6 +1,7 @@
package fixhandler
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
@@ -119,7 +120,7 @@ func (h *FixHandler) getPathFromRawResource(obj map[string]interface{}) string {
return ""
}
func (h *FixHandler) PrepareResourcesToFix() []ResourceFixInfo {
func (h *FixHandler) PrepareResourcesToFix(ctx context.Context) []ResourceFixInfo {
resourceIdToResource := h.buildResourcesMap()
resourcesToFix := make([]ResourceFixInfo, 0)
@@ -141,13 +142,13 @@ func (h *FixHandler) PrepareResourcesToFix() []ResourceFixInfo {
relativePath, documentIndex, err := h.getFilePathAndIndex(resourcePath)
if err != nil {
logger.L().Error("Skipping invalid resource path: " + resourcePath)
logger.L().Ctx(ctx).Error("Skipping invalid resource path: " + resourcePath)
continue
}
absolutePath := path.Join(h.localBasePath, relativePath)
if _, err := os.Stat(absolutePath); err != nil {
logger.L().Error("Skipping missing file: " + absolutePath)
logger.L().Ctx(ctx).Error("Skipping missing file: " + absolutePath)
continue
}
@@ -193,7 +194,7 @@ func (h *FixHandler) PrintExpectedChanges(resourcesToFix []ResourceFixInfo) {
logger.L().Info(sb.String())
}
func (h *FixHandler) ApplyChanges(resourcesToFix []ResourceFixInfo) (int, []error) {
func (h *FixHandler) ApplyChanges(ctx context.Context, resourcesToFix []ResourceFixInfo) (int, []error) {
updatedFiles := make(map[string]bool)
errors := make([]error, 0)
@@ -207,7 +208,7 @@ func (h *FixHandler) ApplyChanges(resourcesToFix []ResourceFixInfo) (int, []erro
continue
}
fixedYamlString, err := h.ApplyFixToContent(fileAsString, yamlExpression)
fixedYamlString, err := h.ApplyFixToContent(ctx, fileAsString, yamlExpression)
if err != nil {
errors = append(errors, fmt.Errorf("Failed to fix file %s: %w ", filepath, err))
@@ -219,7 +220,7 @@ func (h *FixHandler) ApplyChanges(resourcesToFix []ResourceFixInfo) (int, []erro
err = writeFixesToFile(filepath, fixedYamlString)
if err != nil {
logger.L().Error(fmt.Sprintf("Failed to write fixes to file %s, %v", filepath, err.Error()))
logger.L().Ctx(ctx).Error(fmt.Sprintf("Failed to write fixes to file %s, %v", filepath, err.Error()))
errors = append(errors, err)
}
}
@@ -241,7 +242,7 @@ func (h *FixHandler) getFilePathAndIndex(filePathWithIndex string) (filePath str
}
}
func (h *FixHandler) ApplyFixToContent(yamlAsString, yamlExpression string) (fixedString string, err error) {
func (h *FixHandler) ApplyFixToContent(ctx context.Context, yamlAsString, yamlExpression string) (fixedString string, err error) {
newline := determineNewlineSeparator(yamlAsString)
yamlLines := strings.Split(yamlAsString, newline)
@@ -252,13 +253,13 @@ func (h *FixHandler) ApplyFixToContent(yamlAsString, yamlExpression string) (fix
return "", err
}
fixedRootNodes, err := getFixedNodes(yamlAsString, yamlExpression)
fixedRootNodes, err := getFixedNodes(ctx, yamlAsString, yamlExpression)
if err != nil {
return "", err
}
fileFixInfo := getFixInfo(originalRootNodes, fixedRootNodes)
fileFixInfo := getFixInfo(ctx, originalRootNodes, fixedRootNodes)
fixedYamlLines := getFixedYamlLines(yamlLines, fileFixInfo, newline)

View File

@@ -1,6 +1,7 @@
package fixhandler
import (
"context"
"os"
"path/filepath"
"testing"
@@ -188,7 +189,7 @@ func TestApplyFixKeepsFormatting(t *testing.T) {
h, _ := NewFixHandlerMock()
got, _ := h.ApplyFixToContent(string(input), expression)
got, _ := h.ApplyFixToContent(context.TODO(), string(input), expression)
assert.Equalf(
t, want, got,

View File

@@ -2,6 +2,7 @@ package fixhandler
import (
"container/list"
"context"
"errors"
"fmt"
"io"
@@ -36,7 +37,7 @@ func decodeDocumentRoots(yamlAsString string) ([]yaml.Node, error) {
return nodes, nil
}
func getFixedNodes(yamlAsString, yamlExpression string) ([]yaml.Node, error) {
func getFixedNodes(ctx context.Context, yamlAsString, yamlExpression string) ([]yaml.Node, error) {
preferences := yqlib.ConfiguredYamlPreferences
preferences.EvaluateTogether = true
decoder := yqlib.NewYamlDecoder(preferences)
@@ -44,7 +45,7 @@ func getFixedNodes(yamlAsString, yamlExpression string) ([]yaml.Node, error) {
var allDocuments = list.New()
reader := strings.NewReader(yamlAsString)
fileDocuments, err := readDocuments(reader, decoder)
fileDocuments, err := readDocuments(ctx, reader, decoder)
if err != nil {
return nil, err
}
@@ -87,14 +88,14 @@ func flattenWithDFSHelper(node *yaml.Node, parent *yaml.Node, dfsOrder *[]nodeIn
}
}
func getFixInfo(originalRootNodes, fixedRootNodes []yaml.Node) fileFixInfo {
func getFixInfo(ctx context.Context, originalRootNodes, fixedRootNodes []yaml.Node) fileFixInfo {
contentToAdd := make([]contentToAdd, 0)
linesToRemove := make([]linesToRemove, 0)
for idx := 0; idx < len(fixedRootNodes); idx++ {
originalList := flattenWithDFS(&originalRootNodes[idx])
fixedList := flattenWithDFS(&fixedRootNodes[idx])
nodeContentToAdd, nodeLinesToRemove := getFixInfoHelper(*originalList, *fixedList)
nodeContentToAdd, nodeLinesToRemove := getFixInfoHelper(ctx, *originalList, *fixedList)
contentToAdd = append(contentToAdd, nodeContentToAdd...)
linesToRemove = append(linesToRemove, nodeLinesToRemove...)
}
@@ -105,7 +106,7 @@ func getFixInfo(originalRootNodes, fixedRootNodes []yaml.Node) fileFixInfo {
}
}
func getFixInfoHelper(originalList, fixedList []nodeInfo) ([]contentToAdd, []linesToRemove) {
func getFixInfoHelper(ctx context.Context, originalList, fixedList []nodeInfo) ([]contentToAdd, []linesToRemove) {
// While obtaining fixedYamlNode, comments and empty lines at the top are ignored.
// This causes a difference in Line numbers across the tree structure. In order to
@@ -138,20 +139,20 @@ func getFixInfoHelper(originalList, fixedList []nodeInfo) ([]contentToAdd, []lin
fixedListTracker += 1
case removedNode:
originalListTracker, fixedListTracker = addLinesToRemove(fixInfoMetadata)
originalListTracker, fixedListTracker = addLinesToRemove(ctx, fixInfoMetadata)
case insertedNode:
originalListTracker, fixedListTracker = addLinesToInsert(fixInfoMetadata)
originalListTracker, fixedListTracker = addLinesToInsert(ctx, fixInfoMetadata)
case replacedNode:
originalListTracker, fixedListTracker = updateLinesToReplace(fixInfoMetadata)
originalListTracker, fixedListTracker = updateLinesToReplace(ctx, fixInfoMetadata)
}
}
// Some nodes are still not visited if they are removed at the end of the list
for originalListTracker < len(originalList) {
fixInfoMetadata.originalListTracker = originalListTracker
originalListTracker, _ = addLinesToRemove(fixInfoMetadata)
originalListTracker, _ = addLinesToRemove(ctx, fixInfoMetadata)
}
// Some nodes are still not visited if they are inserted at the end of the list
@@ -159,7 +160,7 @@ func getFixInfoHelper(originalList, fixedList []nodeInfo) ([]contentToAdd, []lin
// Use negative index of last node in original list as a placeholder to determine the last line number later
fixInfoMetadata.originalListTracker = -(len(originalList) - 1)
fixInfoMetadata.fixedListTracker = fixedListTracker
_, fixedListTracker = addLinesToInsert(fixInfoMetadata)
_, fixedListTracker = addLinesToInsert(ctx, fixInfoMetadata)
}
return contentToAdd, linesToRemove
@@ -167,13 +168,13 @@ func getFixInfoHelper(originalList, fixedList []nodeInfo) ([]contentToAdd, []lin
}
// Adds the lines to remove and returns the updated originalListTracker
func addLinesToRemove(fixInfoMetadata *fixInfoMetadata) (int, int) {
func addLinesToRemove(ctx context.Context, fixInfoMetadata *fixInfoMetadata) (int, int) {
isOneLine, line := isOneLineSequenceNode(fixInfoMetadata.originalList, fixInfoMetadata.originalListTracker)
if isOneLine {
// Remove the entire line and replace it with the sequence node in fixed info. This way,
// the original formatting is not lost.
return replaceSingleLineSequence(fixInfoMetadata, line)
return replaceSingleLineSequence(ctx, fixInfoMetadata, line)
}
currentDFSNode := (*fixInfoMetadata.originalList)[fixInfoMetadata.originalListTracker]
@@ -188,18 +189,18 @@ func addLinesToRemove(fixInfoMetadata *fixInfoMetadata) (int, int) {
}
// Adds the lines to insert and returns the updated fixedListTracker
func addLinesToInsert(fixInfoMetadata *fixInfoMetadata) (int, int) {
func addLinesToInsert(ctx context.Context, fixInfoMetadata *fixInfoMetadata) (int, int) {
isOneLine, line := isOneLineSequenceNode(fixInfoMetadata.fixedList, fixInfoMetadata.fixedListTracker)
if isOneLine {
return replaceSingleLineSequence(fixInfoMetadata, line)
return replaceSingleLineSequence(ctx, fixInfoMetadata, line)
}
currentDFSNode := (*fixInfoMetadata.fixedList)[fixInfoMetadata.fixedListTracker]
lineToInsert := getLineToInsert(fixInfoMetadata)
contentToInsert := getContent(currentDFSNode.parent, fixInfoMetadata.fixedList, fixInfoMetadata.fixedListTracker)
contentToInsert := getContent(ctx, currentDFSNode.parent, fixInfoMetadata.fixedList, fixInfoMetadata.fixedListTracker)
newFixedTracker := updateTracker(fixInfoMetadata.fixedList, fixInfoMetadata.fixedListTracker)
@@ -212,12 +213,12 @@ func addLinesToInsert(fixInfoMetadata *fixInfoMetadata) (int, int) {
}
// Adds the lines to remove and insert and updates the fixedListTracker and originalListTracker
func updateLinesToReplace(fixInfoMetadata *fixInfoMetadata) (int, int) {
func updateLinesToReplace(ctx context.Context, fixInfoMetadata *fixInfoMetadata) (int, int) {
isOneLine, line := isOneLineSequenceNode(fixInfoMetadata.fixedList, fixInfoMetadata.fixedListTracker)
if isOneLine {
return replaceSingleLineSequence(fixInfoMetadata, line)
return replaceSingleLineSequence(ctx, fixInfoMetadata, line)
}
currentDFSNode := (*fixInfoMetadata.fixedList)[fixInfoMetadata.fixedListTracker]
@@ -228,8 +229,8 @@ func updateLinesToReplace(fixInfoMetadata *fixInfoMetadata) (int, int) {
fixInfoMetadata.fixedListTracker -= 1
}
addLinesToRemove(fixInfoMetadata)
updatedOriginalTracker, updatedFixedTracker := addLinesToInsert(fixInfoMetadata)
addLinesToRemove(ctx, fixInfoMetadata)
updatedOriginalTracker, updatedFixedTracker := addLinesToInsert(ctx, fixInfoMetadata)
return updatedOriginalTracker, updatedFixedTracker
}

View File

@@ -4,6 +4,7 @@ import (
"bufio"
"bytes"
"container/list"
"context"
"errors"
"fmt"
"io"
@@ -111,10 +112,10 @@ func enocodeIntoYaml(parentNode *yaml.Node, nodeList *[]nodeInfo, tracker int) (
return fmt.Sprintf(`%v`, buf.String()), nil
}
func getContent(parentNode *yaml.Node, nodeList *[]nodeInfo, tracker int) string {
func getContent(ctx context.Context, parentNode *yaml.Node, nodeList *[]nodeInfo, tracker int) string {
content, err := enocodeIntoYaml(parentNode, nodeList, tracker)
if err != nil {
logger.L().Fatal("Cannot Encode into YAML")
logger.L().Ctx(ctx).Fatal("Cannot Encode into YAML")
}
indentationSpaces := parentNode.Column - 1
@@ -274,7 +275,7 @@ func isEmptyLineOrComment(lineContent string) bool {
return false
}
func readDocuments(reader io.Reader, decoder yqlib.Decoder) (*list.List, error) {
func readDocuments(ctx context.Context, reader io.Reader, decoder yqlib.Decoder) (*list.List, error) {
err := decoder.Init(reader)
if err != nil {
return nil, fmt.Errorf("Error Initializing the decoder, %w", err)
@@ -289,7 +290,7 @@ func readDocuments(reader io.Reader, decoder yqlib.Decoder) (*list.List, error)
if errors.Is(errorReading, io.EOF) {
switch reader := reader.(type) {
case *os.File:
safelyCloseFile(reader)
safelyCloseFile(ctx, reader)
}
return inputList, nil
} else if errorReading != nil {
@@ -305,21 +306,21 @@ func readDocuments(reader io.Reader, decoder yqlib.Decoder) (*list.List, error)
}
}
func safelyCloseFile(file *os.File) {
func safelyCloseFile(ctx context.Context, file *os.File) {
err := file.Close()
if err != nil {
logger.L().Error("Error Closing File")
logger.L().Ctx(ctx).Error("Error Closing File")
}
}
// Remove the entire line and replace it with the sequence node in fixed info. This way,
// the original formatting is lost.
func replaceSingleLineSequence(fixInfoMetadata *fixInfoMetadata, line int) (int, int) {
func replaceSingleLineSequence(ctx context.Context, fixInfoMetadata *fixInfoMetadata, line int) (int, int) {
originalListTracker := getFirstNodeInLine(fixInfoMetadata.originalList, line)
fixedListTracker := getFirstNodeInLine(fixInfoMetadata.fixedList, line)
currentDFSNode := (*fixInfoMetadata.fixedList)[fixedListTracker]
contentToInsert := getContent(currentDFSNode.parent, fixInfoMetadata.fixedList, fixedListTracker)
contentToInsert := getContent(ctx, currentDFSNode.parent, fixInfoMetadata.fixedList, fixedListTracker)
// Remove the Single line
*fixInfoMetadata.linesToRemove = append(*fixInfoMetadata.linesToRemove, linesToRemove{

View File

@@ -16,6 +16,7 @@ metadata:
labels:
app: host-scanner
k8s-app: kubescape-host-scanner
otel: enabled
spec:
selector:
matchLabels:
@@ -36,7 +37,10 @@ spec:
effect: NoSchedule
containers:
- name: host-sensor
image: quay.io/kubescape/host-scanner:v1.0.39
image: quay.io/kubescape/host-scanner:v1.0.45
env:
- name: OTEL_COLLECTOR_SVC
value: "otel-collector.kubescape:4317"
securityContext:
allowPrivilegeEscalation: true
privileged: true

View File

@@ -1,6 +1,7 @@
package hostsensorutils
import (
"context"
_ "embed"
"encoding/json"
"fmt"
@@ -69,7 +70,7 @@ func NewHostSensorHandler(k8sObj *k8sinterface.KubernetesApi, hostSensorYAMLFile
return hsh, nil
}
func (hsh *HostSensorHandler) Init() error {
func (hsh *HostSensorHandler) Init(ctx context.Context) error {
// deploy the YAML
// store namespace + port
// store pod names
@@ -79,19 +80,19 @@ func (hsh *HostSensorHandler) Init() error {
cautils.StartSpinner()
if err := hsh.applyYAML(); err != nil {
if err := hsh.applyYAML(ctx); err != nil {
cautils.StopSpinner()
return fmt.Errorf("failed to apply host scanner YAML, reason: %v", err)
}
hsh.populatePodNamesToNodeNames()
hsh.populatePodNamesToNodeNames(ctx)
if err := hsh.checkPodForEachNode(); err != nil {
logger.L().Error("failed to validate host-sensor pods status", helpers.Error(err))
logger.L().Ctx(ctx).Error("failed to validate host-sensor pods status", helpers.Error(err))
}
cautils.StopSpinner()
return nil
}
func (hsh *HostSensorHandler) applyYAML() error {
func (hsh *HostSensorHandler) applyYAML(ctx context.Context) error {
workloads, err := cautils.ReadFile([]byte(hostSensorYAML), cautils.YAML_FILE_FORMAT)
if err != nil {
return fmt.Errorf("failed to read YAML files, reason: %v", err)
@@ -121,7 +122,7 @@ func (hsh *HostSensorHandler) applyYAML() error {
containers, err := w.GetContainers()
if err != nil {
if erra := hsh.tearDownNamespace(namespaceName); erra != nil {
logger.L().Warning("failed to tear down namespace", helpers.Error(erra))
logger.L().Ctx(ctx).Warning("failed to tear down namespace", helpers.Error(erra))
}
return fmt.Errorf("container not found in DaemonSet: %v", err)
}
@@ -146,7 +147,7 @@ func (hsh *HostSensorHandler) applyYAML() error {
}
if e != nil {
if erra := hsh.tearDownNamespace(namespaceName); erra != nil {
logger.L().Warning("failed to tear down namespace", helpers.Error(erra))
logger.L().Ctx(ctx).Warning("failed to tear down namespace", helpers.Error(erra))
}
return fmt.Errorf("failed to create/update YAML, reason: %v", e)
}
@@ -156,14 +157,14 @@ func (hsh *HostSensorHandler) applyYAML() error {
b, err := json.Marshal(newWorkload.GetObject())
if err != nil {
if erra := hsh.tearDownNamespace(namespaceName); erra != nil {
logger.L().Warning("failed to tear down namespace", helpers.Error(erra))
logger.L().Ctx(ctx).Warning("failed to tear down namespace", helpers.Error(erra))
}
return fmt.Errorf("failed to Marshal YAML of DaemonSet, reason: %v", err)
}
var ds appsv1.DaemonSet
if err := json.Unmarshal(b, &ds); err != nil {
if erra := hsh.tearDownNamespace(namespaceName); erra != nil {
logger.L().Warning("failed to tear down namespace", helpers.Error(erra))
logger.L().Ctx(ctx).Warning("failed to tear down namespace", helpers.Error(erra))
}
return fmt.Errorf("failed to Unmarshal YAML of DaemonSet, reason: %v", err)
}
@@ -200,7 +201,7 @@ func (hsh *HostSensorHandler) checkPodForEachNode() error {
}
// initiating routine to keep pod list updated
func (hsh *HostSensorHandler) populatePodNamesToNodeNames() {
func (hsh *HostSensorHandler) populatePodNamesToNodeNames(ctx context.Context) {
go func() {
var watchRes watch.Interface
@@ -210,7 +211,7 @@ func (hsh *HostSensorHandler) populatePodNamesToNodeNames() {
LabelSelector: fmt.Sprintf("name=%s", hsh.DaemonSet.Spec.Template.Labels["name"]),
})
if err != nil {
logger.L().Error("failed to watch over daemonset pods - are we missing watch pods permissions?", helpers.Error(err))
logger.L().Ctx(ctx).Error("failed to watch over daemonset pods - are we missing watch pods permissions?", helpers.Error(err))
}
if watchRes == nil {
return
@@ -220,12 +221,12 @@ func (hsh *HostSensorHandler) populatePodNamesToNodeNames() {
if !ok {
continue
}
go hsh.updatePodInListAtomic(eve.Type, pod)
go hsh.updatePodInListAtomic(ctx, eve.Type, pod)
}
}()
}
func (hsh *HostSensorHandler) updatePodInListAtomic(eventType watch.EventType, podObj *corev1.Pod) {
func (hsh *HostSensorHandler) updatePodInListAtomic(ctx context.Context, eventType watch.EventType, podObj *corev1.Pod) {
hsh.podListLock.Lock()
defer hsh.podListLock.Unlock()
@@ -246,7 +247,7 @@ func (hsh *HostSensorHandler) updatePodInListAtomic(eventType watch.EventType, p
len(podObj.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchFields[0].Values) > 0 {
nodeName = podObj.Spec.Affinity.NodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms[0].MatchFields[0].Values[0]
}
logger.L().Warning("One host-sensor pod is unable to schedule on node. We will fail to collect the data from this node",
logger.L().Ctx(ctx).Warning("One host-sensor pod is unable to schedule on node. We will fail to collect the data from this node",
helpers.String("message", podObj.Status.Conditions[0].Message),
helpers.String("nodeName", nodeName),
helpers.String("podName", podObj.ObjectMeta.Name))

View File

@@ -1,6 +1,7 @@
package hostsensorutils
import (
"context"
"encoding/json"
"fmt"
"strings"
@@ -81,7 +82,7 @@ func (hsh *HostSensorHandler) ForwardToPod(podName, path string) ([]byte, error)
// The function produces a worker-pool with a fixed number of workers.
// For each node the request is pushed to the jobs channel, the worker sends the request and pushes the result to the result channel.
// When all workers have finished, the function returns a list of results
func (hsh *HostSensorHandler) sendAllPodsHTTPGETRequest(path, requestKind string) ([]hostsensor.HostSensorDataEnvelope, error) {
func (hsh *HostSensorHandler) sendAllPodsHTTPGETRequest(ctx context.Context, path, requestKind string) ([]hostsensor.HostSensorDataEnvelope, error) {
podList, err := hsh.getPodList()
if err != nil {
return nil, fmt.Errorf("failed to sendAllPodsHTTPGETRequest: %v", err)
@@ -94,7 +95,7 @@ func (hsh *HostSensorHandler) sendAllPodsHTTPGETRequest(path, requestKind string
hsh.workerPool.hostSensorApplyJobs(podList, path, requestKind)
hsh.workerPool.hostSensorGetResults(&res)
hsh.workerPool.createWorkerPool(hsh, &wg)
hsh.workerPool.createWorkerPool(ctx, hsh, &wg)
hsh.workerPool.waitForDone(&wg)
return res, nil
@@ -125,51 +126,51 @@ func (hsh *HostSensorHandler) GetVersion() (string, error) {
}
// return list of LinuxKernelVariables
func (hsh *HostSensorHandler) GetKernelVariables() ([]hostsensor.HostSensorDataEnvelope, error) {
func (hsh *HostSensorHandler) GetKernelVariables(ctx context.Context) ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
return hsh.sendAllPodsHTTPGETRequest("/LinuxKernelVariables", "LinuxKernelVariables")
return hsh.sendAllPodsHTTPGETRequest(ctx, "/LinuxKernelVariables", LinuxKernelVariables)
}
// return list of OpenPortsList
func (hsh *HostSensorHandler) GetOpenPortsList() ([]hostsensor.HostSensorDataEnvelope, error) {
func (hsh *HostSensorHandler) GetOpenPortsList(ctx context.Context) ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
return hsh.sendAllPodsHTTPGETRequest("/openedPorts", "OpenPortsList")
return hsh.sendAllPodsHTTPGETRequest(ctx, "/openedPorts", OpenPortsList)
}
// return list of LinuxSecurityHardeningStatus
func (hsh *HostSensorHandler) GetLinuxSecurityHardeningStatus() ([]hostsensor.HostSensorDataEnvelope, error) {
func (hsh *HostSensorHandler) GetLinuxSecurityHardeningStatus(ctx context.Context) ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
return hsh.sendAllPodsHTTPGETRequest("/linuxSecurityHardening", "LinuxSecurityHardeningStatus")
return hsh.sendAllPodsHTTPGETRequest(ctx, "/linuxSecurityHardening", LinuxSecurityHardeningStatus)
}
// return list of KubeletInfo
func (hsh *HostSensorHandler) GetKubeletInfo() ([]hostsensor.HostSensorDataEnvelope, error) {
func (hsh *HostSensorHandler) GetKubeletInfo(ctx context.Context) ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
return hsh.sendAllPodsHTTPGETRequest("/kubeletInfo", "KubeletInfo")
return hsh.sendAllPodsHTTPGETRequest(ctx, "/kubeletInfo", KubeletInfo)
}
// return list of KubeProxyInfo
func (hsh *HostSensorHandler) GetKubeProxyInfo() ([]hostsensor.HostSensorDataEnvelope, error) {
// return list of kubeProxyInfo
func (hsh *HostSensorHandler) GetKubeProxyInfo(ctx context.Context) ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
return hsh.sendAllPodsHTTPGETRequest("/kubeProxyInfo", "KubeProxyInfo")
return hsh.sendAllPodsHTTPGETRequest(ctx, "/kubeProxyInfo", KubeProxyInfo)
}
// return list of KubeProxyInfo
func (hsh *HostSensorHandler) GetControlPlaneInfo() ([]hostsensor.HostSensorDataEnvelope, error) {
// return list of controlPlaneInfo
func (hsh *HostSensorHandler) GetControlPlaneInfo(ctx context.Context) ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
return hsh.sendAllPodsHTTPGETRequest("/controlPlaneInfo", ControlPlaneInfo)
return hsh.sendAllPodsHTTPGETRequest(ctx, "/controlPlaneInfo", ControlPlaneInfo)
}
// return list of KubeProxyInfo
func (hsh *HostSensorHandler) GetCloudProviderInfo() ([]hostsensor.HostSensorDataEnvelope, error) {
// return list of cloudProviderInfo
func (hsh *HostSensorHandler) GetCloudProviderInfo(ctx context.Context) ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
return hsh.sendAllPodsHTTPGETRequest("/cloudProviderInfo", CloudProviderInfo)
return hsh.sendAllPodsHTTPGETRequest(ctx, "/cloudProviderInfo", CloudProviderInfo)
}
// return list of KubeletCommandLine
func (hsh *HostSensorHandler) GetKubeletCommandLine() ([]hostsensor.HostSensorDataEnvelope, error) {
func (hsh *HostSensorHandler) GetKubeletCommandLine(ctx context.Context) ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
resps, err := hsh.sendAllPodsHTTPGETRequest("/kubeletCommandLine", "KubeletCommandLine")
resps, err := hsh.sendAllPodsHTTPGETRequest(ctx, "/kubeletCommandLine", KubeletCommandLine)
if err != nil {
return resps, err
}
@@ -187,26 +188,32 @@ func (hsh *HostSensorHandler) GetKubeletCommandLine() ([]hostsensor.HostSensorDa
}
// return list of
func (hsh *HostSensorHandler) GetKernelVersion() ([]hostsensor.HostSensorDataEnvelope, error) {
// return list of CNIInfo
func (hsh *HostSensorHandler) GetCNIInfo(ctx context.Context) ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
return hsh.sendAllPodsHTTPGETRequest("/kernelVersion", "KernelVersion")
return hsh.sendAllPodsHTTPGETRequest(ctx, "/CNIInfo", CNIInfo)
}
// return list of
func (hsh *HostSensorHandler) GetOsReleaseFile() ([]hostsensor.HostSensorDataEnvelope, error) {
// return list of kernelVersion
func (hsh *HostSensorHandler) GetKernelVersion(ctx context.Context) ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
return hsh.sendAllPodsHTTPGETRequest("/osRelease", "OsReleaseFile")
return hsh.sendAllPodsHTTPGETRequest(ctx, "/kernelVersion", "KernelVersion")
}
// return list of
func (hsh *HostSensorHandler) GetKubeletConfigurations() ([]hostsensor.HostSensorDataEnvelope, error) {
// return list of osRelease
func (hsh *HostSensorHandler) GetOsReleaseFile(ctx context.Context) ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
res, err := hsh.sendAllPodsHTTPGETRequest("/kubeletConfigurations", "KubeletConfiguration") // empty kind, will be overridden
return hsh.sendAllPodsHTTPGETRequest(ctx, "/osRelease", "OsReleaseFile")
}
// return list of kubeletConfigurations
func (hsh *HostSensorHandler) GetKubeletConfigurations(ctx context.Context) ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
res, err := hsh.sendAllPodsHTTPGETRequest(ctx, "/kubeletConfigurations", "KubeletConfiguration") // empty kind, will be overridden
for resIdx := range res {
jsonBytes, ery := yaml.YAMLToJSON(res[resIdx].Data)
if ery != nil {
logger.L().Error("failed to convert kubelet configurations from yaml to json", helpers.Error(ery))
logger.L().Ctx(ctx).Error("failed to convert kubelet configurations from yaml to json", helpers.Error(ery))
continue
}
res[resIdx].SetData(jsonBytes)
@@ -214,7 +221,7 @@ func (hsh *HostSensorHandler) GetKubeletConfigurations() ([]hostsensor.HostSenso
return res, err
}
func (hsh *HostSensorHandler) CollectResources() ([]hostsensor.HostSensorDataEnvelope, map[string]apis.StatusInfo, error) {
func (hsh *HostSensorHandler) CollectResources(ctx context.Context) ([]hostsensor.HostSensorDataEnvelope, map[string]apis.StatusInfo, error) {
res := make([]hostsensor.HostSensorDataEnvelope, 0)
infoMap := make(map[string]apis.StatusInfo)
if hsh.DaemonSet == nil {
@@ -225,7 +232,7 @@ func (hsh *HostSensorHandler) CollectResources() ([]hostsensor.HostSensorDataEnv
logger.L().Debug("Accessing host scanner")
version, err := hsh.GetVersion()
if err != nil {
logger.L().Warning(err.Error())
logger.L().Ctx(ctx).Warning(err.Error())
}
if len(version) > 0 {
logger.L().Info("Host scanner version : " + version)
@@ -233,103 +240,113 @@ func (hsh *HostSensorHandler) CollectResources() ([]hostsensor.HostSensorDataEnv
logger.L().Info("Unknown host scanner version")
}
//
kcData, err = hsh.GetKubeletConfigurations()
kcData, err = hsh.GetKubeletConfigurations(ctx)
if err != nil {
addInfoToMap(KubeletConfiguration, infoMap, err)
logger.L().Warning(err.Error())
logger.L().Ctx(ctx).Warning(err.Error())
}
if len(kcData) > 0 {
res = append(res, kcData...)
}
//
kcData, err = hsh.GetKubeletCommandLine()
kcData, err = hsh.GetKubeletCommandLine(ctx)
if err != nil {
addInfoToMap(KubeletCommandLine, infoMap, err)
logger.L().Warning(err.Error())
logger.L().Ctx(ctx).Warning(err.Error())
}
if len(kcData) > 0 {
res = append(res, kcData...)
}
//
kcData, err = hsh.GetOsReleaseFile()
kcData, err = hsh.GetOsReleaseFile(ctx)
if err != nil {
addInfoToMap(OsReleaseFile, infoMap, err)
logger.L().Warning(err.Error())
logger.L().Ctx(ctx).Warning(err.Error())
}
if len(kcData) > 0 {
res = append(res, kcData...)
}
//
kcData, err = hsh.GetKernelVersion()
kcData, err = hsh.GetKernelVersion(ctx)
if err != nil {
addInfoToMap(KernelVersion, infoMap, err)
logger.L().Warning(err.Error())
logger.L().Ctx(ctx).Warning(err.Error())
}
if len(kcData) > 0 {
res = append(res, kcData...)
}
//
kcData, err = hsh.GetLinuxSecurityHardeningStatus()
kcData, err = hsh.GetLinuxSecurityHardeningStatus(ctx)
if err != nil {
addInfoToMap(LinuxSecurityHardeningStatus, infoMap, err)
logger.L().Warning(err.Error())
logger.L().Ctx(ctx).Warning(err.Error())
}
if len(kcData) > 0 {
res = append(res, kcData...)
}
//
kcData, err = hsh.GetOpenPortsList()
kcData, err = hsh.GetOpenPortsList(ctx)
if err != nil {
addInfoToMap(OpenPortsList, infoMap, err)
logger.L().Warning(err.Error())
logger.L().Ctx(ctx).Warning(err.Error())
}
if len(kcData) > 0 {
res = append(res, kcData...)
}
// GetKernelVariables
kcData, err = hsh.GetKernelVariables()
kcData, err = hsh.GetKernelVariables(ctx)
if err != nil {
addInfoToMap(LinuxKernelVariables, infoMap, err)
logger.L().Warning(err.Error())
logger.L().Ctx(ctx).Warning(err.Error())
}
if len(kcData) > 0 {
res = append(res, kcData...)
}
// GetKubeletInfo
kcData, err = hsh.GetKubeletInfo()
kcData, err = hsh.GetKubeletInfo(ctx)
if err != nil {
addInfoToMap(KubeletInfo, infoMap, err)
logger.L().Warning(err.Error())
logger.L().Ctx(ctx).Warning(err.Error())
}
if len(kcData) > 0 {
res = append(res, kcData...)
}
// GetKubeProxyInfo
kcData, err = hsh.GetKubeProxyInfo()
kcData, err = hsh.GetKubeProxyInfo(ctx)
if err != nil {
addInfoToMap(KubeProxyInfo, infoMap, err)
logger.L().Warning(err.Error())
logger.L().Ctx(ctx).Warning(err.Error())
}
if len(kcData) > 0 {
res = append(res, kcData...)
}
// GetControlPlaneInfo
kcData, err = hsh.GetControlPlaneInfo()
kcData, err = hsh.GetControlPlaneInfo(ctx)
if err != nil {
addInfoToMap(ControlPlaneInfo, infoMap, err)
logger.L().Warning(err.Error())
logger.L().Ctx(ctx).Warning(err.Error())
}
if len(kcData) > 0 {
res = append(res, kcData...)
}
// GetCloudProviderInfo
kcData, err = hsh.GetCloudProviderInfo()
kcData, err = hsh.GetCloudProviderInfo(ctx)
if err != nil {
addInfoToMap(CloudProviderInfo, infoMap, err)
logger.L().Ctx(ctx).Warning(err.Error())
}
if len(kcData) > 0 {
res = append(res, kcData...)
}
// GetCNIInfo
kcData, err = hsh.GetCNIInfo(ctx)
if err != nil {
addInfoToMap(CNIInfo, infoMap, err)
logger.L().Warning(err.Error())
}
if len(kcData) > 0 {

View File

@@ -1,13 +1,15 @@
package hostsensorutils
import (
"context"
"github.com/kubescape/opa-utils/objectsenvelopes/hostsensor"
"github.com/kubescape/opa-utils/reporthandling/apis"
)
type IHostSensor interface {
Init() error
Init(ctx context.Context) error
TearDown() error
CollectResources() ([]hostsensor.HostSensorDataEnvelope, map[string]apis.StatusInfo, error)
CollectResources(context.Context) ([]hostsensor.HostSensorDataEnvelope, map[string]apis.StatusInfo, error)
GetNamespace() string
}

View File

@@ -1,6 +1,8 @@
package hostsensorutils
import (
"context"
"github.com/kubescape/opa-utils/objectsenvelopes/hostsensor"
"github.com/kubescape/opa-utils/reporthandling/apis"
)
@@ -8,7 +10,7 @@ import (
type HostSensorHandlerMock struct {
}
func (hshm *HostSensorHandlerMock) Init() error {
func (hshm *HostSensorHandlerMock) Init(_ context.Context) error {
return nil
}
@@ -16,7 +18,7 @@ func (hshm *HostSensorHandlerMock) TearDown() error {
return nil
}
func (hshm *HostSensorHandlerMock) CollectResources() ([]hostsensor.HostSensorDataEnvelope, map[string]apis.StatusInfo, error) {
func (hshm *HostSensorHandlerMock) CollectResources(_ context.Context) ([]hostsensor.HostSensorDataEnvelope, map[string]apis.StatusInfo, error) {
return []hostsensor.HostSensorDataEnvelope{}, nil, nil
}

View File

@@ -1,6 +1,7 @@
package hostsensorutils
import (
"context"
"sync"
logger "github.com/kubescape/go-logger"
@@ -42,22 +43,22 @@ func (wp *workerPool) init(noOfPods ...int) {
}
// The worker takes a job out of the chan, executes the request, and pushes the result to the results chan
func (wp *workerPool) hostSensorWorker(hsh *HostSensorHandler, wg *sync.WaitGroup) {
func (wp *workerPool) hostSensorWorker(ctx context.Context, hsh *HostSensorHandler, wg *sync.WaitGroup) {
defer wg.Done()
for job := range wp.jobs {
hostSensorDataEnvelope, err := hsh.getResourcesFromPod(job.podName, job.nodeName, job.requestKind, job.path)
if err != nil {
logger.L().Error("failed to get data", helpers.String("path", job.path), helpers.String("podName", job.podName), helpers.Error(err))
logger.L().Ctx(ctx).Error("failed to get data", helpers.String("path", job.path), helpers.String("podName", job.podName), helpers.Error(err))
} else {
wp.results <- hostSensorDataEnvelope
}
}
}
func (wp *workerPool) createWorkerPool(hsh *HostSensorHandler, wg *sync.WaitGroup) {
func (wp *workerPool) createWorkerPool(ctx context.Context, hsh *HostSensorHandler, wg *sync.WaitGroup) {
for i := 0; i < noOfWorkers; i++ {
wg.Add(1)
go wp.hostSensorWorker(hsh, wg)
go wp.hostSensorWorker(ctx, hsh, wg)
}
}

View File

@@ -17,6 +17,7 @@ var (
KubeProxyInfo = "KubeProxyInfo"
ControlPlaneInfo = "ControlPlaneInfo"
CloudProviderInfo = "CloudProviderInfo"
CNIInfo = "CNIInfo"
MapHostSensorResourceToApiGroup = map[string]string{
KubeletConfiguration: "hostdata.kubescape.cloud/v1beta0",
@@ -30,6 +31,7 @@ var (
KubeProxyInfo: "hostdata.kubescape.cloud/v1beta0",
ControlPlaneInfo: "hostdata.kubescape.cloud/v1beta0",
CloudProviderInfo: "hostdata.kubescape.cloud/v1beta0",
CNIInfo: "hostdata.kubescape.cloud/v1beta0",
}
)

View File

@@ -0,0 +1,27 @@
package opaprocessor
import (
"context"
"github.com/google/go-containerregistry/pkg/name"
"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/sigstore/cosign/pkg/cosign"
)
func has_signature(img string) bool {
ref, err := name.ParseReference(img)
if err != nil {
logger.L().Error("parsing reference", helpers.Error(err))
return false
}
sins, err := cosign.FetchSignaturesForReference(context.Background(), ref)
if err != nil {
logger.L().Error("verifying signature", helpers.Error(err))
return false
}
return len(sins) > 0
}

View File

@@ -0,0 +1,27 @@
package opaprocessor
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_has_signature(t *testing.T) {
tests := []struct {
name string
img string
want bool
}{
{
name: "valid signature",
img: "quay.io/kubescape/gateway",
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, has_signature(tt.img), tt.name)
})
}
}

View File

@@ -0,0 +1,77 @@
package opaprocessor
import (
"context"
"crypto"
"fmt"
"github.com/google/go-containerregistry/pkg/name"
"github.com/sigstore/cosign/cmd/cosign/cli/options"
"github.com/sigstore/cosign/cmd/cosign/cli/sign"
"github.com/sigstore/cosign/pkg/cosign"
"github.com/sigstore/cosign/pkg/cosign/pkcs11key"
ociremote "github.com/sigstore/cosign/pkg/oci/remote"
sigs "github.com/sigstore/cosign/pkg/signature"
)
// VerifyCommand verifies a signature on a supplied container image
// nolint
type VerifyCommand struct {
options.RegistryOptions
CheckClaims bool
KeyRef string
CertRef string
CertEmail string
CertIdentity string
CertOidcIssuer string
CertGithubWorkflowTrigger string
CertGithubWorkflowSha string
CertGithubWorkflowName string
CertGithubWorkflowRepository string
CertGithubWorkflowRef string
CertChain string
CertOidcProvider string
EnforceSCT bool
Sk bool
Slot string
Output string
RekorURL string
Attachment string
Annotations sigs.AnnotationsMap
SignatureRef string
HashAlgorithm crypto.Hash
LocalImage bool
}
// Exec runs the verification command
func verify(img string, key string) (bool, error) {
co := &cosign.CheckOpts{}
var ociremoteOpts []ociremote.Option
attachment := ""
pubKey, err := sigs.LoadPublicKeyRaw([]byte(key), crypto.SHA256)
if err != nil {
return false, fmt.Errorf("loading public key: %w", err)
}
pkcs11Key, ok := pubKey.(*pkcs11key.Key)
if ok {
defer pkcs11Key.Close()
}
co.SigVerifier = pubKey
ref, err := name.ParseReference(img)
if err != nil {
return false, fmt.Errorf("parsing reference: %w", err)
}
ref, err = sign.GetAttachedImageRef(ref, attachment, ociremoteOpts...)
if err != nil {
return false, fmt.Errorf("resolving attachment type %s for image %s: %w", attachment, img, err)
}
_, _, err = cosign.VerifyImageSignatures(context.TODO(), ref, co)
if err != nil {
return false, fmt.Errorf("verifying signature: %w", err)
}
return true, nil
}

View File

@@ -0,0 +1,49 @@
package opaprocessor
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_verify(t *testing.T) {
type args struct {
img string
key string
}
tests := []struct {
name string
args args
want bool
wantErr assert.ErrorAssertionFunc
}{
{
"valid signature",
args{
img: "hisu/cosign-tests:signed",
key: "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGnMCUU0jGe6r4mPsPuyTXf61PE4e\nNwB/31SvUMmnoyd/1UxSqd+MRPXPU6pcub4k6E9G9SprVCuf6Sydcbyiqw==\n-----END PUBLIC KEY-----",
},
true,
assert.NoError,
},
{
"no signature",
args{
img: "hisu/cosign-tests:unsigned",
key: "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGnMCUU0jGe6r4mPsPuyTXf61PE4e\nNwB/31SvUMmnoyd/1UxSqd+MRPXPU6pcub4k6E9G9SprVCuf6Sydcbyiqw==\n-----END PUBLIC KEY-----",
},
false,
assert.Error,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := verify(tt.args.img, tt.args.key)
if !tt.wantErr(t, err, fmt.Sprintf("verify(%v, %v)", tt.args.img, tt.args.key)) {
return
}
assert.Equalf(t, tt.want, got, "verify(%v, %v)", tt.args.img, tt.args.key)
})
}
}

View File

@@ -14,8 +14,8 @@ import (
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
"github.com/open-policy-agent/opa/storage"
"go.opentelemetry.io/otel"
"github.com/kubescape/k8s-interface/workloadinterface"
@@ -27,6 +27,12 @@ import (
const ScoreConfigPath = "/resources/config"
type IJobProgressNotificationClient interface {
Start(allSteps int)
ProgressJob(step int, message string)
Stop()
}
type OPAProcessor struct {
regoDependenciesData *resources.RegoDependenciesData
*cautils.OPASessionObj
@@ -42,20 +48,20 @@ func NewOPAProcessor(sessionObj *cautils.OPASessionObj, regoDependenciesData *re
regoDependenciesData: regoDependenciesData,
}
}
func (opap *OPAProcessor) ProcessRulesListenner() error {
func (opap *OPAProcessor) ProcessRulesListenner(ctx context.Context, progressListener IJobProgressNotificationClient) error {
opap.OPASessionObj.AllPolicies = ConvertFrameworksToPolicies(opap.Policies, cautils.BuildNumber)
ConvertFrameworksToSummaryDetails(&opap.Report.SummaryDetails, opap.Policies, opap.OPASessionObj.AllPolicies)
// process
if err := opap.Process(opap.OPASessionObj.AllPolicies); err != nil {
logger.L().Error(err.Error())
if err := opap.Process(ctx, opap.OPASessionObj.AllPolicies, progressListener); err != nil {
logger.L().Ctx(ctx).Error(err.Error())
// Return error?
}
// edit results
opap.updateResults()
opap.updateResults(ctx)
//TODO: review this location
scorewrapper := score.NewScoreWrapper(opap.OPASessionObj)
@@ -64,17 +70,26 @@ func (opap *OPAProcessor) ProcessRulesListenner() error {
return nil
}
func (opap *OPAProcessor) Process(policies *cautils.Policies) error {
func (opap *OPAProcessor) Process(ctx context.Context, policies *cautils.Policies, progressListener IJobProgressNotificationClient) error {
ctx, span := otel.Tracer("").Start(ctx, "OPAProcessor.Process")
defer span.End()
opap.loggerStartScanning()
cautils.StartSpinner()
if progressListener != nil {
progressListener.Start(len(policies.Controls))
defer progressListener.Stop()
}
for _, toPin := range policies.Controls {
if progressListener != nil {
progressListener.ProgressJob(1, fmt.Sprintf("Control %s", toPin.ControlID))
}
control := toPin
resourcesAssociatedControl, err := opap.processControl(&control)
resourcesAssociatedControl, err := opap.processControl(ctx, &control)
if err != nil {
logger.L().Error(err.Error())
logger.L().Ctx(ctx).Error(err.Error())
}
if len(resourcesAssociatedControl) == 0 {
@@ -94,8 +109,6 @@ func (opap *OPAProcessor) Process(policies *cautils.Policies) error {
opap.Report.ReportGenerationTime = time.Now().UTC()
cautils.StopSpinner()
opap.loggerDoneScanning()
return nil
@@ -119,16 +132,16 @@ func (opap *OPAProcessor) loggerDoneScanning() {
}
}
func (opap *OPAProcessor) processControl(control *reporthandling.Control) (map[string]resourcesresults.ResourceAssociatedControl, error) {
func (opap *OPAProcessor) processControl(ctx context.Context, control *reporthandling.Control) (map[string]resourcesresults.ResourceAssociatedControl, error) {
var errs error
resourcesAssociatedControl := make(map[string]resourcesresults.ResourceAssociatedControl)
// ruleResults := make(map[string][]resourcesresults.ResourceAssociatedRule)
for i := range control.Rules {
resourceAssociatedRule, err := opap.processRule(&control.Rules[i], control.FixedInput)
resourceAssociatedRule, err := opap.processRule(ctx, &control.Rules[i], control.FixedInput)
if err != nil {
logger.L().Error(err.Error())
logger.L().Ctx(ctx).Error(err.Error())
continue
}
@@ -154,7 +167,7 @@ func (opap *OPAProcessor) processControl(control *reporthandling.Control) (map[s
return resourcesAssociatedControl, errs
}
func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule, fixedControlInputs map[string][]string) (map[string]*resourcesresults.ResourceAssociatedRule, error) {
func (opap *OPAProcessor) processRule(ctx context.Context, 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}
@@ -179,7 +192,7 @@ func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule, fixedCont
resources := map[string]*resourcesresults.ResourceAssociatedRule{}
// the failed resources are a subgroup of the enumeratedData, so we store the enumeratedData like it was the input data
enumeratedData, err := opap.enumerateData(rule, inputRawResources)
enumeratedData, err := opap.enumerateData(ctx, rule, inputRawResources)
if err != nil {
return nil, err
}
@@ -193,10 +206,10 @@ func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule, fixedCont
opap.AllResources[inputResources[i].GetID()] = inputResources[i]
}
ruleResponses, err := opap.runOPAOnSingleRule(rule, inputRawResources, ruleData, RuleRegoDependenciesData)
ruleResponses, err := opap.runOPAOnSingleRule(ctx, rule, inputRawResources, ruleData, RuleRegoDependenciesData)
if err != nil {
// TODO - Handle error
logger.L().Error(err.Error())
logger.L().Ctx(ctx).Error(err.Error())
} else {
// ruleResponse to ruleResult
for i := range ruleResponses {
@@ -225,24 +238,27 @@ 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, ruleRegoDependenciesData resources.RegoDependenciesData) ([]reporthandling.RuleResponse, error) {
func (opap *OPAProcessor) runOPAOnSingleRule(ctx context.Context, 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, ruleRegoDependenciesData)
return opap.runRegoOnK8s(ctx, 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, ruleRegoDependenciesData resources.RegoDependenciesData) ([]reporthandling.RuleResponse, error) {
func (opap *OPAProcessor) runRegoOnK8s(ctx context.Context, rule *reporthandling.PolicyRule, k8sObjects []map[string]interface{}, getRuleData func(*reporthandling.PolicyRule) string, ruleRegoDependenciesData resources.RegoDependenciesData) ([]reporthandling.RuleResponse, error) {
// compile modules
modules, err := getRuleDependencies()
modules, err := getRuleDependencies(ctx)
if err != nil {
return nil, fmt.Errorf("rule: '%s', %s", rule.Name, err.Error())
}
rego.RegisterBuiltin2(cosignVerifySignatureDeclaration, cosignVerifySignatureDefinition)
rego.RegisterBuiltin1(cosignHasSignatureDeclaration, cosignHasSignatureDefinition)
modules[rule.Name] = getRuleData(rule)
compiled, err := ast.CompileModules(modules)
if err != nil {
return nil, fmt.Errorf("in 'runRegoOnSingleRule', failed to compile rule, name: %s, reason: %s", rule.Name, err.Error())
}
@@ -255,7 +271,7 @@ func (opap *OPAProcessor) runRegoOnK8s(rule *reporthandling.PolicyRule, k8sObjec
// Eval
results, err := opap.regoEval(k8sObjects, compiled, &store)
if err != nil {
logger.L().Error(err.Error())
logger.L().Ctx(ctx).Error(err.Error())
}
return results, nil
@@ -284,7 +300,7 @@ func (opap *OPAProcessor) regoEval(inputObj []map[string]interface{}, compiledRe
return results, nil
}
func (opap *OPAProcessor) enumerateData(rule *reporthandling.PolicyRule, k8sObjects []map[string]interface{}) ([]map[string]interface{}, error) {
func (opap *OPAProcessor) enumerateData(ctx context.Context, rule *reporthandling.PolicyRule, k8sObjects []map[string]interface{}) ([]map[string]interface{}, error) {
if ruleEnumeratorData(rule) == "" {
return k8sObjects, nil
@@ -295,7 +311,7 @@ func (opap *OPAProcessor) enumerateData(rule *reporthandling.PolicyRule, k8sObje
RuleRegoDependenciesData := resources.RegoDependenciesData{DataControlInputs: dataControlInputs,
PostureControlInputs: postureControlInputs}
ruleResponse, err := opap.runOPAOnSingleRule(rule, k8sObjects, ruleEnumeratorData, RuleRegoDependenciesData)
ruleResponse, err := opap.runOPAOnSingleRule(ctx, rule, k8sObjects, ruleEnumeratorData, RuleRegoDependenciesData)
if err != nil {
return nil, err
}

View File

@@ -1,6 +1,7 @@
package opaprocessor
import (
"context"
"testing"
"github.com/armosec/armoapi-go/armotypes"
@@ -38,7 +39,7 @@ func TestProcessResourcesResult(t *testing.T) {
opaSessionObj.AllResources[deployment.GetID()] = deployment
opap := NewOPAProcessor(opaSessionObj, resources.NewRegoDependenciesDataMock())
opap.Process(policies)
opap.Process(context.TODO(), policies, nil)
assert.Equal(t, 1, len(opaSessionObj.ResourcesResult))
res := opaSessionObj.ResourcesResult[deployment.GetID()]
@@ -49,7 +50,7 @@ func TestProcessResourcesResult(t *testing.T) {
assert.False(t, res.GetStatus(nil).IsPassed())
assert.Equal(t, deployment.GetID(), opaSessionObj.ResourcesResult[deployment.GetID()].ResourceID)
opap.updateResults()
opap.updateResults(context.TODO())
res = opaSessionObj.ResourcesResult[deployment.GetID()]
assert.Equal(t, 2, res.ListControlsIDs(nil).All().Len())
assert.Equal(t, 2, res.ListControlsIDs(nil).All().Len())
@@ -80,7 +81,7 @@ func TestProcessResourcesResult(t *testing.T) {
assert.True(t, summaryDetails.GetStatus().IsFailed())
opaSessionObj.Exceptions = []armotypes.PostureExceptionPolicy{*mocks.MockExceptionAllKinds(&armotypes.PosturePolicy{FrameworkName: frameworks[0].Name})}
opap.updateResults()
opap.updateResults(context.TODO())
res = opaSessionObj.ResourcesResult[deployment.GetID()]
assert.Equal(t, 2, res.ListControlsIDs(nil).All().Len())

View File

@@ -1,15 +1,17 @@
package opaprocessor
import (
logger "github.com/kubescape/go-logger"
"github.com/kubescape/kubescape/v2/core/cautils"
"context"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
resources "github.com/kubescape/opa-utils/resources"
"go.opentelemetry.io/otel"
)
// updateResults updates the results objects and report objects. This is a critical function - DO NOT CHANGE
@@ -18,8 +20,9 @@ import (
// - removes sensible data
// - adds exceptions
// - summarizes results
func (opap *OPAProcessor) updateResults() {
func (opap *OPAProcessor) updateResults(ctx context.Context) {
ctx, span := otel.Tracer("").Start(ctx, "OPAProcessor.updateResults")
defer span.End()
// remove data from all objects
for i := range opap.AllResources {
removeData(opap.AllResources[i])
@@ -156,10 +159,10 @@ func filterOutChildResources(objects []workloadinterface.IMetadata, match []repo
}
return response
}
func getRuleDependencies() (map[string]string, error) {
func getRuleDependencies(ctx context.Context) (map[string]string, error) {
modules := resources.LoadRegoModules()
if len(modules) == 0 {
logger.L().Warning("failed to load rule dependencies")
logger.L().Ctx(ctx).Warning("failed to load rule dependencies")
}
return modules, nil
}

View File

@@ -1,9 +1,15 @@
package opaprocessor
import (
"fmt"
"github.com/kubescape/kubescape/v2/core/cautils"
"github.com/kubescape/opa-utils/reporthandling"
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/rego"
"github.com/open-policy-agent/opa/topdown/builtins"
"github.com/open-policy-agent/opa/types"
)
// ConvertFrameworksToPolicies convert list of frameworks to list of policies
@@ -43,3 +49,34 @@ func ConvertFrameworksToSummaryDetails(summaryDetails *reportsummary.SummaryDeta
}
}
var cosignVerifySignatureDeclaration = &rego.Function{
Name: "cosign.verify",
Decl: types.NewFunction(types.Args(types.S, types.A), types.B),
Memoize: true,
}
var cosignVerifySignatureDefinition = func(bctx rego.BuiltinContext, a, b *ast.Term) (*ast.Term, error) {
aStr, err := builtins.StringOperand(a.Value, 1)
if err != nil {
return nil, fmt.Errorf("invalid parameter type: %v", err)
}
bStr, err := builtins.StringOperand(b.Value, 1)
if err != nil {
return nil, fmt.Errorf("invalid parameter type: %v", err)
}
result, _ := verify(string(aStr), string(bStr))
return ast.BooleanTerm(result), nil
}
var cosignHasSignatureDeclaration = &rego.Function{
Name: "cosign.has_signature",
Decl: types.NewFunction(types.Args(types.S), types.B),
Memoize: true,
}
var cosignHasSignatureDefinition = func(bctx rego.BuiltinContext, a *ast.Term) (*ast.Term, error) {
aStr, err := builtins.StringOperand(a.Value, 1)
if err != nil {
return nil, fmt.Errorf("invalid parameter type: %v", err)
}
return ast.BooleanTerm(has_signature(string(aStr))), nil
}

View File

@@ -1,13 +1,14 @@
package policyhandler
import (
"context"
"fmt"
"strings"
logger "github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
helpersv1 "github.com/kubescape/opa-utils/reporthandling/helpers/v1"
"go.opentelemetry.io/otel"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
cloudsupportv1 "github.com/kubescape/k8s-interface/cloudsupport/v1"
@@ -23,7 +24,7 @@ import (
// PolicyHandler -
type PolicyHandler struct {
resourceHandler resourcehandler.IResourceHandler
// we are listening on this chan in opaprocessor/processorhandler.go/ProcessRulesListenner func
// we are listening on this chan in opaprocessor/processorhandler.go/ProcessRulesListener func
getters *cautils.Getters
}
@@ -34,19 +35,19 @@ func NewPolicyHandler(resourceHandler resourcehandler.IResourceHandler) *PolicyH
}
}
func (policyHandler *PolicyHandler) CollectResources(policyIdentifier []cautils.PolicyIdentifier, scanInfo *cautils.ScanInfo) (*cautils.OPASessionObj, error) {
opaSessionObj := cautils.NewOPASessionObj(nil, nil, scanInfo)
func (policyHandler *PolicyHandler) CollectResources(ctx context.Context, policyIdentifier []cautils.PolicyIdentifier, scanInfo *cautils.ScanInfo) (*cautils.OPASessionObj, error) {
opaSessionObj := cautils.NewOPASessionObj(ctx, nil, nil, scanInfo)
// validate notification
// TODO
policyHandler.getters = &scanInfo.Getters
// get policies
if err := policyHandler.getPolicies(policyIdentifier, opaSessionObj); err != nil {
if err := policyHandler.getPolicies(ctx, policyIdentifier, opaSessionObj); err != nil {
return opaSessionObj, err
}
err := policyHandler.getResources(policyIdentifier, opaSessionObj, scanInfo)
err := policyHandler.getResources(ctx, policyIdentifier, opaSessionObj)
if err != nil {
return opaSessionObj, err
}
@@ -58,15 +59,17 @@ func (policyHandler *PolicyHandler) CollectResources(policyIdentifier []cautils.
return opaSessionObj, nil
}
func (policyHandler *PolicyHandler) getResources(policyIdentifier []cautils.PolicyIdentifier, opaSessionObj *cautils.OPASessionObj, scanInfo *cautils.ScanInfo) error {
opaSessionObj.Report.ClusterAPIServerInfo = policyHandler.resourceHandler.GetClusterAPIServerInfo()
func (policyHandler *PolicyHandler) getResources(ctx context.Context, policyIdentifier []cautils.PolicyIdentifier, opaSessionObj *cautils.OPASessionObj) error {
ctx, span := otel.Tracer("").Start(ctx, "policyHandler.getResources")
defer span.End()
opaSessionObj.Report.ClusterAPIServerInfo = policyHandler.resourceHandler.GetClusterAPIServerInfo(ctx)
// set cloud metadata only when scanning a cluster
if opaSessionObj.Metadata.ScanMetadata.ScanningTarget == reportv2.Cluster {
setCloudMetadata(opaSessionObj)
}
resourcesMap, allResources, ksResources, err := policyHandler.resourceHandler.GetResources(opaSessionObj, &policyIdentifier[0].Designators)
resourcesMap, allResources, ksResources, err := policyHandler.resourceHandler.GetResources(ctx, opaSessionObj, &policyIdentifier[0].Designators)
if err != nil {
return err
}

View File

@@ -28,7 +28,6 @@ var (
kubeConfigMock string
)
func getKubeConfigMock() *clientcmdapi.Config {
kubeConfig := clientcmdapi.Config{}
if err := json.Unmarshal([]byte(kubeConfigMock), &kubeConfig); err != nil {
@@ -250,13 +249,12 @@ func Test_getResources(t *testing.T) {
policyIdentifier := []cautils.PolicyIdentifier{{}}
assert.NotPanics(t, func() {
policyHandler.getResources(policyIdentifier, objSession, &cautils.ScanInfo{})
policyHandler.getResources(context.TODO(), policyIdentifier, objSession)
}, "Cluster named .*eks.* without a cloud config panics on cluster scan !")
assert.NotPanics(t, func() {
objSession.Metadata.ScanMetadata.ScanningTarget = reportv2.File
policyHandler.getResources(policyIdentifier, objSession, &cautils.ScanInfo{})
policyHandler.getResources(context.TODO(), policyIdentifier, objSession)
}, "Cluster named .*eks.* without a cloud config panics on non-cluster scan !")
}

View File

@@ -1,25 +1,28 @@
package policyhandler
import (
"context"
"fmt"
"strings"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
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/cautils/getter"
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
"github.com/kubescape/opa-utils/reporthandling"
"go.opentelemetry.io/otel"
)
func (policyHandler *PolicyHandler) getPolicies(policyIdentifier []cautils.PolicyIdentifier, policiesAndResources *cautils.OPASessionObj) error {
func (policyHandler *PolicyHandler) getPolicies(ctx context.Context, policyIdentifier []cautils.PolicyIdentifier, policiesAndResources *cautils.OPASessionObj) error {
ctx, span := otel.Tracer("").Start(ctx, "policyHandler.getPolicies")
defer span.End()
logger.L().Info("Downloading/Loading policy definitions")
cautils.StartSpinner()
defer cautils.StopSpinner()
policies, err := policyHandler.getScanPolicies(policyIdentifier)
policies, err := policyHandler.getScanPolicies(ctx, policyIdentifier)
if err != nil {
return err
}
@@ -34,7 +37,7 @@ func (policyHandler *PolicyHandler) getPolicies(policyIdentifier []cautils.Polic
if err == nil {
policiesAndResources.Exceptions = exceptionPolicies
} else {
logger.L().Error("failed to load exceptions", helpers.Error(err))
logger.L().Ctx(ctx).Error("failed to load exceptions", helpers.Error(err))
}
// get account configuration
@@ -42,7 +45,7 @@ func (policyHandler *PolicyHandler) getPolicies(policyIdentifier []cautils.Polic
if err == nil {
policiesAndResources.RegoInputData.PostureControlInputs = controlsInputs
} else {
logger.L().Error(err.Error())
logger.L().Ctx(ctx).Error(err.Error())
}
cautils.StopSpinner()
@@ -50,7 +53,7 @@ func (policyHandler *PolicyHandler) getPolicies(policyIdentifier []cautils.Polic
return nil
}
func (policyHandler *PolicyHandler) getScanPolicies(policyIdentifier []cautils.PolicyIdentifier) ([]reporthandling.Framework, error) {
func (policyHandler *PolicyHandler) getScanPolicies(ctx context.Context, policyIdentifier []cautils.PolicyIdentifier) ([]reporthandling.Framework, error) {
frameworks := []reporthandling.Framework{}
switch getScanKind(policyIdentifier) {
@@ -67,7 +70,7 @@ func (policyHandler *PolicyHandler) getScanPolicies(policyIdentifier []cautils.P
frameworks = append(frameworks, *receivedFramework)
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))
logger.L().Ctx(ctx).Warning("failed to cache file", helpers.String("file", cache), helpers.Error(err))
}
}
}
@@ -85,7 +88,7 @@ func (policyHandler *PolicyHandler) getScanPolicies(policyIdentifier []cautils.P
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))
logger.L().Ctx(ctx).Warning("failed to cache file", helpers.String("file", cache), helpers.Error(err))
}
}
}

View File

@@ -1,6 +1,7 @@
package resourcehandler
import (
"context"
"fmt"
"os"
"path/filepath"
@@ -22,7 +23,7 @@ type FileResourceHandler struct {
registryAdaptors *RegistryAdaptors
}
func NewFileResourceHandler(inputPatterns []string, registryAdaptors *RegistryAdaptors) *FileResourceHandler {
func NewFileResourceHandler(_ context.Context, inputPatterns []string, registryAdaptors *RegistryAdaptors) *FileResourceHandler {
k8sinterface.InitializeMapResourcesMock() // initialize the resource map
return &FileResourceHandler{
inputPatterns: inputPatterns,
@@ -30,7 +31,7 @@ func NewFileResourceHandler(inputPatterns []string, registryAdaptors *RegistryAd
}
}
func (fileHandler *FileResourceHandler) GetResources(sessionObj *cautils.OPASessionObj, designator *armotypes.PortalDesignator) (*cautils.K8SResources, map[string]workloadinterface.IMetadata, *cautils.KSResources, error) {
func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessionObj *cautils.OPASessionObj, _ *armotypes.PortalDesignator) (*cautils.K8SResources, map[string]workloadinterface.IMetadata, *cautils.KSResources, error) {
//
// build resources map
@@ -47,7 +48,7 @@ func (fileHandler *FileResourceHandler) GetResources(sessionObj *cautils.OPASess
cautils.StartSpinner()
for path := range fileHandler.inputPatterns {
workloadIDToSource, workloads, err := getResourcesFromPath(fileHandler.inputPatterns[path])
workloadIDToSource, workloads, err := getResourcesFromPath(ctx, fileHandler.inputPatterns[path])
if err != nil {
return nil, allResources, nil, err
}
@@ -78,7 +79,7 @@ func (fileHandler *FileResourceHandler) GetResources(sessionObj *cautils.OPASess
// 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))
// logger.L().Ctx(ctx).Warning("failed to collect images vulnerabilities", helpers.Error(err))
// }
cautils.StopSpinner()
@@ -87,8 +88,7 @@ func (fileHandler *FileResourceHandler) GetResources(sessionObj *cautils.OPASess
return k8sResources, allResources, ksResources, nil
}
func getResourcesFromPath(path string) (map[string]reporthandling.Source, []workloadinterface.IMetadata, error) {
func getResourcesFromPath(ctx context.Context, path string) (map[string]reporthandling.Source, []workloadinterface.IMetadata, error) {
workloadIDToSource := make(map[string]reporthandling.Source, 0)
workloads := []workloadinterface.IMetadata{}
@@ -116,7 +116,7 @@ func getResourcesFromPath(path string) (map[string]reporthandling.Source, []work
}
// load resource from local file system
sourceToWorkloads := cautils.LoadResourcesFromFiles(path, repoRoot)
sourceToWorkloads := cautils.LoadResourcesFromFiles(ctx, path, repoRoot)
// update workloads and workloadIDToSource
var warnIssued bool
@@ -142,7 +142,7 @@ func getResourcesFromPath(path string) (map[string]reporthandling.Source, []work
if gitRepo != nil {
commitInfo, err := gitRepo.GetFileLastCommit(source)
if err != nil && !warnIssued {
logger.L().Warning("git scan skipped", helpers.Error(err))
logger.L().Ctx(ctx).Warning("git scan skipped", helpers.Error(err))
warnIssued = true // croak only once
}
@@ -173,7 +173,7 @@ func getResourcesFromPath(path string) (map[string]reporthandling.Source, []work
}
// load resources from helm charts
helmSourceToWorkloads, helmSourceToChartName := cautils.LoadResourcesFromHelmCharts(path)
helmSourceToWorkloads, helmSourceToChartName := cautils.LoadResourcesFromHelmCharts(ctx, path)
for source, ws := range helmSourceToWorkloads {
workloads = append(workloads, ws...)
helmChartName := helmSourceToChartName[source]
@@ -214,7 +214,7 @@ func getResourcesFromPath(path string) (map[string]reporthandling.Source, []work
}
// Load resources from Kustomize directory
kustomizeSourceToWorkloads, kustomizeDirectoryName := cautils.LoadResourcesFromKustomizeDirectory(path)
kustomizeSourceToWorkloads, kustomizeDirectoryName := cautils.LoadResourcesFromKustomizeDirectory(ctx, path)
// update workloads and workloadIDToSource with workloads from Kustomize Directory
for source, ws := range kustomizeSourceToWorkloads {
@@ -254,6 +254,6 @@ func getResourcesFromPath(path string) (map[string]reporthandling.Source, []work
return workloadIDToSource, workloads, nil
}
func (fileHandler *FileResourceHandler) GetClusterAPIServerInfo() *version.Info {
func (fileHandler *FileResourceHandler) GetClusterAPIServerInfo(_ context.Context) *version.Info {
return nil
}

View File

@@ -13,6 +13,8 @@ import (
"github.com/kubescape/opa-utils/reporthandling/apis"
"github.com/kubescape/k8s-interface/cloudsupport"
cloudapis "github.com/kubescape/k8s-interface/cloudsupport/apis"
cloudv1 "github.com/kubescape/k8s-interface/cloudsupport/v1"
"github.com/kubescape/k8s-interface/k8sinterface"
"github.com/kubescape/k8s-interface/workloadinterface"
@@ -27,6 +29,14 @@ import (
"k8s.io/client-go/dynamic"
)
type cloudResourceGetter func(string, string) (workloadinterface.IMetadata, error)
var cloudResourceGetterMapping = map[string]cloudResourceGetter{
cloudapis.CloudProviderDescribeKind: cloudsupport.GetDescriptiveInfoFromCloudProvider,
cloudapis.CloudProviderDescribeRepositoriesKind: cloudsupport.GetDescribeRepositoriesFromCloudProvider,
cloudapis.CloudProviderListEntitiesForPoliciesKind: cloudsupport.GetListEntitiesForPoliciesFromCloudProvider,
}
type K8sResourceHandler struct {
k8s *k8sinterface.KubernetesApi
hostSensorHandler hostsensorutils.IHostSensor
@@ -45,7 +55,7 @@ func NewK8sResourceHandler(k8s *k8sinterface.KubernetesApi, fieldSelector IField
}
}
func (k8sHandler *K8sResourceHandler) GetResources(sessionObj *cautils.OPASessionObj, designator *armotypes.PortalDesignator) (*cautils.K8SResources, map[string]workloadinterface.IMetadata, *cautils.KSResources, error) {
func (k8sHandler *K8sResourceHandler) GetResources(ctx context.Context, sessionObj *cautils.OPASessionObj, designator *armotypes.PortalDesignator) (*cautils.K8SResources, map[string]workloadinterface.IMetadata, *cautils.KSResources, error) {
allResources := map[string]workloadinterface.IMetadata{}
// get k8s resources
@@ -104,9 +114,9 @@ func (k8sHandler *K8sResourceHandler) GetResources(sessionObj *cautils.OPASessio
logger.L().Info("Requesting Host scanner data")
cautils.StartSpinner()
if sessionObj.Metadata.ScanMetadata.HostScanner {
infoMap, err := k8sHandler.collectHostResources(allResources, ksResourceMap)
infoMap, err := k8sHandler.collectHostResources(ctx, allResources, ksResourceMap)
if err != nil {
logger.L().Warning("failed to collect host scanner resources", helpers.Error(err))
logger.L().Ctx(ctx).Warning("failed to collect host scanner resources", helpers.Error(err))
cautils.SetInfoMapForResources(err.Error(), hostResources, sessionObj.InfoMap)
} else if k8sHandler.hostSensorHandler == nil {
// using hostSensor mock
@@ -124,36 +134,74 @@ func (k8sHandler *K8sResourceHandler) GetResources(sessionObj *cautils.OPASessio
}
if err := k8sHandler.collectRbacResources(allResources); err != nil {
logger.L().Warning("failed to collect rbac resources", helpers.Error(err))
logger.L().Ctx(ctx).Warning("failed to collect rbac resources", helpers.Error(err))
}
cloudResources := cautils.MapCloudResources(ksResourceMap)
setMapNamespaceToNumOfResources(allResources, sessionObj)
setMapNamespaceToNumOfResources(ctx, allResources, sessionObj)
// check that controls use cloud resources
if len(cloudResources) > 0 {
provider, err := getCloudProviderDescription(allResources, ksResourceMap)
err := k8sHandler.collectCloudResources(ctx, sessionObj, allResources, ksResourceMap, cloudResources)
if err != nil {
cautils.SetInfoMapForResources(err.Error(), cloudResources, sessionObj.InfoMap)
logger.L().Warning("failed to collect cloud data", helpers.Error(err))
}
if provider != "" {
if sessionObj.Metadata != nil && sessionObj.Metadata.ContextMetadata.ClusterContextMetadata != nil {
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))
logger.L().Ctx(ctx).Warning("failed to collect cloud data", helpers.Error(err))
}
}
return k8sResourcesMap, allResources, ksResourceMap, nil
}
func (k8sHandler *K8sResourceHandler) collectCloudResources(ctx context.Context, sessionObj *cautils.OPASessionObj, allResources map[string]workloadinterface.IMetadata, ksResourceMap *cautils.KSResources, cloudResources []string) error {
var err error
clusterName := cautils.ClusterName
provider := cloudsupport.GetCloudProvider(clusterName)
if provider == "" {
return fmt.Errorf("failed to get cloud provider, cluster: %s", clusterName)
}
if sessionObj.Metadata != nil && sessionObj.Metadata.ContextMetadata.ClusterContextMetadata != nil {
sessionObj.Metadata.ContextMetadata.ClusterContextMetadata.CloudProvider = provider
}
logger.L().Debug("cloud", helpers.String("cluster", clusterName), helpers.String("clusterName", clusterName), helpers.String("provider", provider))
for resourceKind, resourceGetter := range cloudResourceGetterMapping {
if cloudResourceRequired(cloudResources, resourceKind) {
logger.L().Debug("Collecting cloud data ", helpers.String("resourceKind", resourceKind))
wl, err := resourceGetter(clusterName, provider)
if err != nil {
if !strings.Contains(err.Error(), cloudv1.NotSupportedMsg) {
// Return error with useful info on how to configure credentials for getting cloud provider info
logger.L().Debug("failed to get cloud data", helpers.String("resourceKind", resourceKind), helpers.Error(err))
err = fmt.Errorf("failed to get %s descriptive information. Read more: https://hub.armosec.io/docs/kubescape-integration-with-cloud-providers", strings.ToUpper(provider))
cautils.SetInfoMapForResources(err.Error(), cloudResources, sessionObj.InfoMap)
}
} else {
allResources[wl.GetID()] = wl
(*ksResourceMap)[fmt.Sprintf("%s/%s", wl.GetApiVersion(), wl.GetKind())] = []string{wl.GetID()}
}
}
}
// get api server info resource
if cloudResourceRequired(cloudResources, string(cloudsupport.TypeApiServerInfo)) {
err = k8sHandler.collectAPIServerInfoResource(allResources, ksResourceMap)
if err != nil {
logger.L().Ctx(ctx).Warning("failed to collect api server info resource", helpers.Error(err))
}
}
return err
}
func cloudResourceRequired(cloudResources []string, resource string) bool {
for _, cresource := range cloudResources {
if strings.Contains(cresource, resource) {
return true
}
}
return false
}
func (k8sHandler *K8sResourceHandler) collectAPIServerInfoResource(allResources map[string]workloadinterface.IMetadata, ksResourceMap *cautils.KSResources) error {
clusterAPIServerInfo, err := k8sHandler.k8s.DiscoveryClient.ServerVersion()
if err != nil {
@@ -166,17 +214,17 @@ func (k8sHandler *K8sResourceHandler) collectAPIServerInfoResource(allResources
return nil
}
func (k8sHandler *K8sResourceHandler) GetClusterAPIServerInfo() *version.Info {
func (k8sHandler *K8sResourceHandler) GetClusterAPIServerInfo(ctx context.Context) *version.Info {
clusterAPIServerInfo, err := k8sHandler.k8s.DiscoveryClient.ServerVersion()
if err != nil {
logger.L().Error("failed to discover API server information", helpers.Error(err))
logger.L().Ctx(ctx).Error("failed to discover API server information", helpers.Error(err))
return nil
}
return clusterAPIServerInfo
}
// set namespaceToNumOfResources map in report
func setMapNamespaceToNumOfResources(allResources map[string]workloadinterface.IMetadata, sessionObj *cautils.OPASessionObj) {
func setMapNamespaceToNumOfResources(ctx context.Context, allResources map[string]workloadinterface.IMetadata, sessionObj *cautils.OPASessionObj) {
mapNamespaceToNumberOfResources := make(map[string]int)
for _, resource := range allResources {
@@ -192,7 +240,7 @@ func setMapNamespaceToNumOfResources(allResources map[string]workloadinterface.I
}
}
} else {
logger.L().Warning(fmt.Sprintf("failed to get owner references. Resource %s will not be counted", obj.GetName()), helpers.Error(err))
logger.L().Ctx(ctx).Warning(fmt.Sprintf("failed to get owner references. Resource %s will not be counted", obj.GetName()), helpers.Error(err))
}
}
}
@@ -276,9 +324,9 @@ func ConvertMapListToMeta(resourceMap []map[string]interface{}) []workloadinterf
return workloads
}
func (k8sHandler *K8sResourceHandler) collectHostResources(allResources map[string]workloadinterface.IMetadata, ksResourceMap *cautils.KSResources) (map[string]apis.StatusInfo, error) {
func (k8sHandler *K8sResourceHandler) collectHostResources(ctx context.Context, allResources map[string]workloadinterface.IMetadata, ksResourceMap *cautils.KSResources) (map[string]apis.StatusInfo, error) {
logger.L().Debug("Collecting host scanner resources")
hostResources, infoMap, err := k8sHandler.hostSensorHandler.CollectResources()
hostResources, infoMap, err := k8sHandler.hostSensorHandler.CollectResources(ctx)
if err != nil {
return nil, err
}
@@ -313,29 +361,6 @@ func (k8sHandler *K8sResourceHandler) collectRbacResources(allResources map[stri
return nil
}
func getCloudProviderDescription(allResources map[string]workloadinterface.IMetadata, ksResourceMap *cautils.KSResources) (string, error) {
logger.L().Debug("Collecting cloud data")
clusterName := cautils.ClusterName
provider := cloudsupport.GetCloudProvider(clusterName)
if provider != "" {
logger.L().Debug("cloud", helpers.String("cluster", clusterName), helpers.String("clusterName", clusterName), helpers.String("provider", provider))
wl, err := cloudsupport.GetDescriptiveInfoFromCloudProvider(clusterName, provider)
if err != nil {
// Return error with useful info on how to configure credentials for getting cloud provider info
logger.L().Debug("failed to get descriptive information", helpers.Error(err))
return provider, fmt.Errorf("failed to get %s descriptive information. Read more: https://hub.armosec.io/docs/kubescape-integration-with-cloud-providers", strings.ToUpper(provider))
}
allResources[wl.GetID()] = wl
(*ksResourceMap)[fmt.Sprintf("%s/%s", wl.GetApiVersion(), wl.GetKind())] = []string{wl.GetID()}
}
return provider, nil
}
func (k8sHandler *K8sResourceHandler) pullWorkerNodesNumber() (int, error) {
nodesList, err := k8sHandler.k8s.KubernetesClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
scheduableNodes := v1.NodeList{}

View File

@@ -1,6 +1,7 @@
package resourcehandler
import (
"context"
_ "embed"
"encoding/json"
"reflect"
@@ -555,7 +556,7 @@ func TestSetMapNamespaceToNumOfResources(t *testing.T) {
}
sessionObj := cautils.NewOPASessionObjMock()
setMapNamespaceToNumOfResources(allResources, sessionObj)
setMapNamespaceToNumOfResources(context.TODO(), allResources, sessionObj)
expected := map[string]int{
"kube-system": 1,
"armo-system": 3,
@@ -566,3 +567,13 @@ func TestSetMapNamespaceToNumOfResources(t *testing.T) {
assert.NotContains(t, sessionObj.Metadata.ContextMetadata.ClusterContextMetadata.MapNamespaceToNumberOfResources, "clusterrole")
assert.NotContains(t, sessionObj.Metadata.ContextMetadata.ClusterContextMetadata.MapNamespaceToNumberOfResources, "pod")
}
func TestCloudResourceRequired(t *testing.T) {
cloudResources := []string{"container.googleapis.com/v1/ClusterDescribe",
"eks.amazonaws.com/v1/DescribeRepositories",
"eks.amazonaws.com/v1/ListEntitiesForPolicies",
"eks.amazonaws.com/v1/ClusterDescribe"}
assert.True(t, cloudResourceRequired(cloudResources, ClusterDescribe))
assert.False(t, cloudResourceRequired(cloudResources, "ListRolePolicies"))
}

View File

@@ -12,6 +12,8 @@ import (
var (
ClusterDescribe = "ClusterDescribe"
DescribeRepositories = "DescribeRepositories"
ListEntitiesForPolicies = "ListEntitiesForPolicies"
KubeletConfiguration = "KubeletConfiguration"
OsReleaseFile = "OsReleaseFile"
KernelVersion = "KernelVersion"
@@ -24,6 +26,7 @@ var (
KubeProxyInfo = "KubeProxyInfo"
ControlPlaneInfo = "ControlPlaneInfo"
CloudProviderInfo = "CloudProviderInfo"
CNIInfo = "CNIInfo"
MapResourceToApiGroup = map[string]string{
KubeletConfiguration: "hostdata.kubescape.cloud/v1beta0",
@@ -37,11 +40,15 @@ var (
KubeProxyInfo: "hostdata.kubescape.cloud/v1beta0",
ControlPlaneInfo: "hostdata.kubescape.cloud/v1beta0",
CloudProviderInfo: "hostdata.kubescape.cloud/v1beta0",
CNIInfo: "hostdata.kubescape.cloud/v1beta0",
}
MapResourceToApiGroupVuln = map[string][]string{
ImageVulnerabilities: {"armo.vuln.images/v1", "image.vulnscan.com/v1"}}
MapResourceToApiGroupCloud = map[string][]string{
ClusterDescribe: {"container.googleapis.com/v1", "eks.amazonaws.com/v1", "management.azure.com/v1"}}
ClusterDescribe: {"container.googleapis.com/v1", "eks.amazonaws.com/v1", "management.azure.com/v1"},
DescribeRepositories: {"eks.amazonaws.com/v1"}, //TODO - add google and azure when they are supported
ListEntitiesForPolicies: {"eks.amazonaws.com/v1"}, //TODO - add google and azure when they are supported
}
)
func isEmptyImgVulns(ksResourcesMap cautils.KSResources) bool {

View File

@@ -28,7 +28,7 @@ type RegistryAdaptors struct {
func NewRegistryAdaptors() (*RegistryAdaptors, error) {
// list supported adaptors
registryAdaptors := &RegistryAdaptors{}
adaptors, err := listAdaptores()
adaptors, err := listAdaptors()
if err != nil {
return registryAdaptors, err
}
@@ -148,7 +148,7 @@ func imageTagsToContainerImageIdentifier(images []string) []registryvulnerabilit
}
return imagesIdentifiers
}
func listAdaptores() ([]registryvulnerabilities.IContainerImageVulnerabilityAdaptor, error) {
func listAdaptors() ([]registryvulnerabilities.IContainerImageVulnerabilityAdaptor, error) {
adaptors := []registryvulnerabilities.IContainerImageVulnerabilityAdaptor{}

View File

@@ -1,6 +1,8 @@
package resourcehandler
import (
"context"
"github.com/armosec/armoapi-go/armotypes"
"github.com/kubescape/k8s-interface/workloadinterface"
"github.com/kubescape/kubescape/v2/core/cautils"
@@ -8,6 +10,6 @@ import (
)
type IResourceHandler interface {
GetResources(*cautils.OPASessionObj, *armotypes.PortalDesignator) (*cautils.K8SResources, map[string]workloadinterface.IMetadata, *cautils.KSResources, error)
GetClusterAPIServerInfo() *version.Info
GetResources(context.Context, *cautils.OPASessionObj, *armotypes.PortalDesignator) (*cautils.K8SResources, map[string]workloadinterface.IMetadata, *cautils.KSResources, error)
GetClusterAPIServerInfo(ctx context.Context) *version.Info
}

View File

@@ -13,7 +13,7 @@ func loadResourcesFromUrl(inputPatterns []string) (map[string][]workloadinterfac
files, errs := g.DownloadFilesWithExtension(append(cautils.YAML_PREFIX, cautils.JSON_PREFIX...))
if len(errs) > 0 {
for i, j := range errs {
logger.L().Error(i, helpers.Error(j))
logger.L().Ctx(ctx).Error(i, helpers.Error(j))
}
}

View File

@@ -1,6 +1,7 @@
package resourcesprioritization
import (
"context"
"encoding/json"
"fmt"
@@ -19,7 +20,7 @@ type ResourcesPrioritizationHandler struct {
buildResourcesMap bool
}
func NewResourcesPrioritizationHandler(attackTracksGetter getter.IAttackTracksGetter, buildResourcesMap bool) (*ResourcesPrioritizationHandler, error) {
func NewResourcesPrioritizationHandler(ctx context.Context, attackTracksGetter getter.IAttackTracksGetter, buildResourcesMap bool) (*ResourcesPrioritizationHandler, error) {
handler := &ResourcesPrioritizationHandler{
attackTracks: make([]v1alpha1.IAttackTrack, 0),
resourceToAttackTracks: make(map[string]v1alpha1.IAttackTrack),
@@ -47,7 +48,7 @@ func NewResourcesPrioritizationHandler(attackTracksGetter getter.IAttackTracksGe
// Store attack tracks in cache
cache := getter.GetDefaultPath(cautils.LocalAttackTracksFilename)
if err := getter.SaveInFile(tracks, cache); err != nil {
logger.L().Warning("failed to cache file", helpers.String("file", cache), helpers.Error(err))
logger.L().Ctx(ctx).Warning("failed to cache file", helpers.String("file", cache), helpers.Error(err))
}
return handler, nil

View File

@@ -1,6 +1,7 @@
package resourcesprioritization
import (
"context"
"fmt"
"testing"
@@ -97,7 +98,7 @@ func ResourceAssociatedControlMock(controlID string, status apis.ScanningStatus)
}
func TestNewResourcesPrioritizationHandler(t *testing.T) {
handler, err := NewResourcesPrioritizationHandler(&AttackTracksGetterMock{}, false)
handler, err := NewResourcesPrioritizationHandler(context.TODO(), &AttackTracksGetterMock{}, false)
assert.NoError(t, err)
assert.Len(t, handler.attackTracks, 2)
assert.Equal(t, handler.attackTracks[0].GetName(), "TestAttackTrack")
@@ -182,7 +183,7 @@ func TestResourcesPrioritizationHandler_PrioritizeResources(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
handler, _ := NewResourcesPrioritizationHandler(&AttackTracksGetterMock{}, false)
handler, _ := NewResourcesPrioritizationHandler(context.TODO(), &AttackTracksGetterMock{}, false)
sessionObj := OPASessionObjMock(tt.allPoliciesControls, tt.results, tt.controls, tt.resources)
err := handler.PrioritizeResources(sessionObj)
assert.NoError(t, err, "expected to have no errors in PrioritizeResources()")

View File

@@ -1,6 +1,7 @@
package printer
import (
"context"
"fmt"
"os"
"path/filepath"
@@ -23,20 +24,20 @@ const (
)
type IPrinter interface {
ActionPrint(opaSessionObj *cautils.OPASessionObj)
SetWriter(outputFile string)
ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj)
SetWriter(ctx context.Context, outputFile string)
Score(score float32)
}
func GetWriter(outputFile string) *os.File {
func GetWriter(ctx context.Context, outputFile string) *os.File {
if outputFile != "" {
if err := os.MkdirAll(filepath.Dir(outputFile), os.ModePerm); err != nil {
logger.L().Error(fmt.Sprintf("failed to create directory, reason: %s", err.Error()))
logger.L().Ctx(ctx).Error(fmt.Sprintf("failed to create directory, reason: %s", err.Error()))
return os.Stdout
}
f, err := os.Create(outputFile)
if err != nil {
logger.L().Error(fmt.Sprintf("failed to open file for writing, reason: %s", err.Error()))
logger.L().Ctx(ctx).Error(fmt.Sprintf("failed to open file for writing, reason: %s", err.Error()))
return os.Stdout
}
return f

View File

@@ -1,6 +1,7 @@
package printer
import (
"context"
"encoding/json"
"fmt"
"os"
@@ -25,21 +26,21 @@ func NewJsonPrinter() *JsonPrinter {
return &JsonPrinter{}
}
func (jsonPrinter *JsonPrinter) SetWriter(outputFile string) {
func (jsonPrinter *JsonPrinter) SetWriter(ctx context.Context, outputFile string) {
if strings.TrimSpace(outputFile) == "" {
outputFile = jsonOutputFile
}
if filepath.Ext(strings.TrimSpace(outputFile)) != jsonOutputExt {
outputFile = outputFile + jsonOutputExt
}
jsonPrinter.writer = printer.GetWriter(outputFile)
jsonPrinter.writer = printer.GetWriter(ctx, outputFile)
}
func (jsonPrinter *JsonPrinter) Score(score float32) {
fmt.Fprintf(os.Stderr, "\nOverall risk-score (0- Excellent, 100- All failed): %d\n", cautils.Float32ToInt(score))
}
func (jsonPrinter *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
func (jsonPrinter *JsonPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj) {
report := cautils.ReportV2ToV1(opaSessionObj)
var postureReportStr []byte
@@ -52,13 +53,13 @@ func (jsonPrinter *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj
}
if err != nil {
logger.L().Fatal("failed to convert posture report object")
logger.L().Ctx(ctx).Fatal("failed to convert posture report object")
}
_, err = jsonPrinter.writer.Write(postureReportStr)
if err != nil {
logger.L().Fatal("failed to Write posture report object into JSON output")
logger.L().Ctx(ctx).Fatal("failed to Write posture report object into JSON output")
} else {
printer.LogOutputFile(jsonPrinter.writer.Name())
}

View File

@@ -1,6 +1,7 @@
package printer
import (
"context"
"fmt"
"os"
@@ -22,8 +23,8 @@ func NewPrometheusPrinter(verboseMode bool) *PrometheusPrinter {
}
}
func (p *PrometheusPrinter) SetWriter(outputFile string) {
p.writer = printer.GetWriter(outputFile)
func (p *PrometheusPrinter) SetWriter(ctx context.Context, outputFile string) {
p.writer = printer.GetWriter(ctx, outputFile)
}
func (p *PrometheusPrinter) Score(score float32) {
@@ -86,12 +87,12 @@ func (p *PrometheusPrinter) printReports(allResources map[string]workloadinterfa
return nil
}
func (p *PrometheusPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
func (p *PrometheusPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj) {
report := cautils.ReportV2ToV1(opaSessionObj)
err := p.printReports(opaSessionObj.AllResources, report.FrameworkReports)
if err != nil {
logger.L().Fatal(err.Error())
logger.L().Ctx(ctx).Fatal(err.Error())
} else {
printer.LogOutputFile(p.writer.Name())
}

View File

@@ -35,7 +35,11 @@ func generateRow(controlSummary reportsummary.IControlSummary, infoToPrintInfo [
}
row[columnSeverity] = getSeverityColumn(controlSummary)
row[columnName] = controlSummary.GetName()
if len(controlSummary.GetName()) > 50 {
row[columnName] = controlSummary.GetName()[:50] + "..."
} else {
row[columnName] = controlSummary.GetName()
}
row[columnCounterFailed] = fmt.Sprintf("%d", controlSummary.NumberOfResources().Failed())
row[columnCounterExclude] = fmt.Sprintf("%d", controlSummary.NumberOfResources().Excluded())
row[columnCounterAll] = fmt.Sprintf("%d", controlSummary.NumberOfResources().All())

View File

@@ -1,6 +1,7 @@
package printer
import (
"context"
_ "embed"
"html/template"
"os"
@@ -38,17 +39,17 @@ func NewHtmlPrinter() *HtmlPrinter {
return &HtmlPrinter{}
}
func (hp *HtmlPrinter) SetWriter(outputFile string) {
func (hp *HtmlPrinter) SetWriter(ctx context.Context, outputFile string) {
if strings.TrimSpace(outputFile) == "" {
outputFile = htmlOutputFile
}
if filepath.Ext(strings.TrimSpace(outputFile)) != htmlOutputExt {
outputFile = outputFile + htmlOutputExt
}
hp.writer = printer.GetWriter(outputFile)
hp.writer = printer.GetWriter(ctx, outputFile)
}
func (hp *HtmlPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
func (hp *HtmlPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj) {
tplFuncMap := template.FuncMap{
"sum": func(nums ...int) int {
total := 0
@@ -106,7 +107,7 @@ func (hp *HtmlPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
reportingCtx := HTMLReportingCtx{opaSessionObj, resourceTableView}
err := tpl.Execute(hp.writer, reportingCtx)
if err != nil {
logger.L().Error("failed to render template", helpers.Error(err))
logger.L().Ctx(ctx).Error("failed to render template", helpers.Error(err))
} else {
printer.LogOutputFile(hp.writer.Name())
}

View File

@@ -1,6 +1,7 @@
package printer
import (
"context"
"encoding/json"
"fmt"
"os"
@@ -26,28 +27,28 @@ func NewJsonPrinter() *JsonPrinter {
return &JsonPrinter{}
}
func (jp *JsonPrinter) SetWriter(outputFile string) {
func (jp *JsonPrinter) SetWriter(ctx context.Context, outputFile string) {
if strings.TrimSpace(outputFile) == "" {
outputFile = jsonOutputFile
}
if filepath.Ext(strings.TrimSpace(outputFile)) != jsonOutputExt {
outputFile = outputFile + jsonOutputExt
}
jp.writer = printer.GetWriter(outputFile)
jp.writer = printer.GetWriter(ctx, outputFile)
}
func (jp *JsonPrinter) Score(score float32) {
fmt.Fprintf(os.Stderr, "\nOverall risk-score (0- Excellent, 100- All failed): %d\n", cautils.Float32ToInt(score))
}
func (jp *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
func (jp *JsonPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj) {
r, err := json.Marshal(FinalizeResults(opaSessionObj))
if err != nil {
logger.L().Fatal("failed to Marshal posture report object")
logger.L().Ctx(ctx).Fatal("failed to Marshal posture report object")
}
if _, err := jp.writer.Write(r); err != nil {
logger.L().Error("failed to write results", helpers.Error(err))
logger.L().Ctx(ctx).Error("failed to write results", helpers.Error(err))
} else {
printer.LogOutputFile(jp.writer.Name())
}

View File

@@ -1,6 +1,7 @@
package printer
import (
"context"
"encoding/xml"
"fmt"
"os"
@@ -98,29 +99,29 @@ func NewJunitPrinter(verbose bool) *JunitPrinter {
}
}
func (jp *JunitPrinter) SetWriter(outputFile string) {
func (jp *JunitPrinter) SetWriter(ctx context.Context, outputFile string) {
if strings.TrimSpace(outputFile) == "" {
outputFile = junitOutputFile
}
if filepath.Ext(strings.TrimSpace(outputFile)) != junitOutputExt {
outputFile = outputFile + junitOutputExt
}
jp.writer = printer.GetWriter(outputFile)
jp.writer = printer.GetWriter(ctx, outputFile)
}
func (jp *JunitPrinter) Score(score float32) {
fmt.Fprintf(os.Stderr, "\nOverall risk-score (0- Excellent, 100- All failed): %d\n", cautils.Float32ToInt(score))
}
func (jp *JunitPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
func (jp *JunitPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj) {
junitResult := testsSuites(opaSessionObj)
postureReportStr, err := xml.Marshal(junitResult)
if err != nil {
logger.L().Fatal("failed to Marshal xml result object", helpers.Error(err))
logger.L().Ctx(ctx).Fatal("failed to Marshal xml result object", helpers.Error(err))
}
if _, err := jp.writer.Write(postureReportStr); err != nil {
logger.L().Error("failed to write results", helpers.Error(err))
logger.L().Ctx(ctx).Error("failed to write results", helpers.Error(err))
} else {
printer.LogOutputFile(jp.writer.Name())
}

View File

@@ -1,6 +1,7 @@
package printer
import (
"context"
_ "embed"
b64 "encoding/base64"
"fmt"
@@ -39,7 +40,7 @@ func NewPdfPrinter() *PdfPrinter {
return &PdfPrinter{}
}
func (pp *PdfPrinter) SetWriter(outputFile string) {
func (pp *PdfPrinter) SetWriter(ctx context.Context, outputFile string) {
// Ensure to have an available output file, otherwise create it.
if strings.TrimSpace(outputFile) == "" {
outputFile = pdfOutputFile
@@ -48,7 +49,7 @@ func (pp *PdfPrinter) SetWriter(outputFile string) {
if filepath.Ext(strings.TrimSpace(outputFile)) != pdfOutputExt {
outputFile = outputFile + pdfOutputExt
}
pp.writer = printer.GetWriter(outputFile)
pp.writer = printer.GetWriter(ctx, outputFile)
}
func (pp *PdfPrinter) Score(score float32) {
@@ -75,7 +76,7 @@ func (pp *PdfPrinter) printInfo(m pdf.Maroto, summaryDetails *reportsummary.Summ
}
func (pp *PdfPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
func (pp *PdfPrinter) ActionPrint(ctx context.Context, opaSessionObj *cautils.OPASessionObj) {
sortedControlIDs := getSortedControlsIDs(opaSessionObj.Report.SummaryDetails.Controls)
infoToPrintInfo := mapInfoToPrintInfo(opaSessionObj.Report.SummaryDetails.Controls)
@@ -89,12 +90,12 @@ func (pp *PdfPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
// Extrat output buffer.
outBuff, err := m.Output()
if err != nil {
logger.L().Error("failed to generate pdf format", helpers.Error(err))
logger.L().Ctx(ctx).Error("failed to generate pdf format", helpers.Error(err))
return
}
if _, err := pp.writer.Write(outBuff.Bytes()); err != nil {
logger.L().Error("failed to write results", helpers.Error(err))
logger.L().Ctx(ctx).Error("failed to write results", helpers.Error(err))
} else {
printer.LogOutputFile(pp.writer.Name())
}

View File

@@ -1,6 +1,7 @@
package printer
import (
"context"
"fmt"
"os"
"path/filepath"
@@ -40,7 +41,7 @@ func NewPrettyPrinter(verboseMode bool, formatVersion string, attackTree bool, v
}
}
func (pp *PrettyPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
func (pp *PrettyPrinter) ActionPrint(_ context.Context, opaSessionObj *cautils.OPASessionObj) {
fmt.Fprintf(pp.writer, "\n"+getSeparator("^")+"\n")
sortedControlIDs := getSortedControlsIDs(opaSessionObj.Report.SummaryDetails.Controls) // ListControls().All())
@@ -65,12 +66,12 @@ func (pp *PrettyPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
pp.printAttackTracks(opaSessionObj)
}
func (pp *PrettyPrinter) SetWriter(outputFile string) {
func (pp *PrettyPrinter) SetWriter(ctx context.Context, outputFile string) {
// PrettyPrinter should accept Stdout at least by its full name (path)
// and follow the common behavior of outputting to a default filename
// otherwise
if outputFile == os.Stdout.Name() {
pp.writer = printer.GetWriter("")
pp.writer = printer.GetWriter(ctx, "")
return
}
@@ -81,7 +82,7 @@ func (pp *PrettyPrinter) SetWriter(outputFile string) {
outputFile = outputFile + prettyPrinterOutputExt
}
pp.writer = printer.GetWriter(outputFile)
pp.writer = printer.GetWriter(ctx, outputFile)
}
func (pp *PrettyPrinter) Score(score float32) {

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