mirror of
https://github.com/kubescape/kubescape.git
synced 2026-03-03 02:00:27 +00:00
Compare commits
127 Commits
v3.0.9-rc.
...
v3.0.21-rc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9ac880356 | ||
|
|
761d4c6ff4 | ||
|
|
bbb2aafc7e | ||
|
|
7735087937 | ||
|
|
5b9c6491de | ||
|
|
b0e3744140 | ||
|
|
0451cdb345 | ||
|
|
4546465f4a | ||
|
|
52c564b2a4 | ||
|
|
0abc81003e | ||
|
|
817d4902ff | ||
|
|
5553a1adf0 | ||
|
|
e95352d31e | ||
|
|
5655051a95 | ||
|
|
90c359533f | ||
|
|
7013d83aa8 | ||
|
|
c4935671fe | ||
|
|
e67ef1c54d | ||
|
|
5d6b9a5e83 | ||
|
|
a6752a5a6d | ||
|
|
6f47f8dae0 | ||
|
|
d82e8daa25 | ||
|
|
1cad446fe6 | ||
|
|
12c6f34fe7 | ||
|
|
79c2dd9a2b | ||
|
|
f64d5a03af | ||
|
|
e27d1581f1 | ||
|
|
99985e3235 | ||
|
|
27782afeac | ||
|
|
0e846b2dc5 | ||
|
|
45fb0f207e | ||
|
|
b7c91c238d | ||
|
|
76635a0705 | ||
|
|
c91d69e7fd | ||
|
|
5fd7096d67 | ||
|
|
f4189cb5ec | ||
|
|
278ca5b8ae | ||
|
|
729efcb8c3 | ||
|
|
406031d4e9 | ||
|
|
9e1d0d2cd6 | ||
|
|
f3e78f9408 | ||
|
|
eea4cc0b49 | ||
|
|
1127f44c10 | ||
|
|
377509fab8 | ||
|
|
c21e2f3147 | ||
|
|
0b4c5db939 | ||
|
|
6d490fc501 | ||
|
|
dbb71ba066 | ||
|
|
d5b8532e40 | ||
|
|
db396b26f8 | ||
|
|
1242259331 | ||
|
|
ad0e50898a | ||
|
|
3cf45cffd8 | ||
|
|
ac0d982531 | ||
|
|
99e22efe7b | ||
|
|
aedeb8f9cb | ||
|
|
824e76200e | ||
|
|
8342f96a62 | ||
|
|
b824d52345 | ||
|
|
11b6567db4 | ||
|
|
c7d3105ca5 | ||
|
|
f1c15cd2b5 | ||
|
|
7507f58306 | ||
|
|
48ad56a2ef | ||
|
|
2fdec20b28 | ||
|
|
2d77ea7b62 | ||
|
|
eacd559c34 | ||
|
|
c56e5799d7 | ||
|
|
ae5744f54e | ||
|
|
c649cc66a5 | ||
|
|
7db735ade6 | ||
|
|
456145e240 | ||
|
|
382a2f03c8 | ||
|
|
44ebf59d76 | ||
|
|
0688e3620b | ||
|
|
ab534b0346 | ||
|
|
09420a41a7 | ||
|
|
e93eb942a8 | ||
|
|
12f87b2710 | ||
|
|
d6dc8f219c | ||
|
|
fb3376d305 | ||
|
|
ef2ded1933 | ||
|
|
e9f1d4085a | ||
|
|
51a9707d24 | ||
|
|
a4058eac62 | ||
|
|
f2b621134c | ||
|
|
58ce50e751 | ||
|
|
2bbedc99dd | ||
|
|
78794990d7 | ||
|
|
a7127c0b27 | ||
|
|
01505406a6 | ||
|
|
e1fe7cda50 | ||
|
|
f0bc2845cf | ||
|
|
c2c521b715 | ||
|
|
2d5ea3e789 | ||
|
|
137fe81701 | ||
|
|
f293606f81 | ||
|
|
d6d2315ad0 | ||
|
|
65aa28dd38 | ||
|
|
15e55e011c | ||
|
|
0ee98351c0 | ||
|
|
f52056a879 | ||
|
|
840162c865 | ||
|
|
160709eabf | ||
|
|
7f9f6d35f7 | ||
|
|
b2b37f6abc | ||
|
|
0863d845e1 | ||
|
|
da6faa3df0 | ||
|
|
3cbd2c458d | ||
|
|
629451dd33 | ||
|
|
29a313e708 | ||
|
|
38896ccd24 | ||
|
|
834623762d | ||
|
|
c937ed16f4 | ||
|
|
ea5f72af4e | ||
|
|
beb5a4d43e | ||
|
|
77e21d5e94 | ||
|
|
3fd7bf40cc | ||
|
|
18e0a227e1 | ||
|
|
060c17b480 | ||
|
|
e67a2e9d1c | ||
|
|
4c9cacecfe | ||
|
|
6ee6a78a75 | ||
|
|
e754ecff4f | ||
|
|
b9fd60b395 | ||
|
|
f0c3a568f0 | ||
|
|
a423b41e68 |
4
.github/workflows/00-pr-scanner.yaml
vendored
4
.github/workflows/00-pr-scanner.yaml
vendored
@@ -23,7 +23,6 @@ jobs:
|
||||
permissions:
|
||||
actions: read
|
||||
checks: read
|
||||
contents: read
|
||||
deployments: read
|
||||
id-token: write
|
||||
issues: read
|
||||
@@ -35,6 +34,7 @@ jobs:
|
||||
security-events: read
|
||||
statuses: read
|
||||
attestations: read
|
||||
contents: write
|
||||
uses: ./.github/workflows/a-pr-scanner.yaml
|
||||
with:
|
||||
RELEASE: ""
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
permissions:
|
||||
actions: read
|
||||
checks: read
|
||||
contents: read
|
||||
contents: write
|
||||
deployments: read
|
||||
discussions: read
|
||||
id-token: write
|
||||
|
||||
28
.github/workflows/02-release.yaml
vendored
28
.github/workflows/02-release.yaml
vendored
@@ -8,7 +8,7 @@ jobs:
|
||||
retag:
|
||||
outputs:
|
||||
NEW_TAG: ${{ steps.tag-calculator.outputs.NEW_TAG }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu22-core4-mem16-ssd150
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- id: tag-calculator
|
||||
@@ -19,7 +19,6 @@ jobs:
|
||||
permissions:
|
||||
actions: read
|
||||
checks: read
|
||||
contents: read
|
||||
deployments: read
|
||||
discussions: read
|
||||
id-token: write
|
||||
@@ -30,6 +29,7 @@ jobs:
|
||||
repository-projects: read
|
||||
security-events: read
|
||||
statuses: read
|
||||
contents: write
|
||||
attestations: write
|
||||
needs: [retag]
|
||||
uses: ./.github/workflows/b-binary-build-and-e2e-tests.yaml
|
||||
@@ -68,7 +68,6 @@ jobs:
|
||||
permissions:
|
||||
actions: read
|
||||
checks: read
|
||||
contents: read
|
||||
deployments: read
|
||||
discussions: read
|
||||
id-token: write
|
||||
@@ -80,6 +79,7 @@ jobs:
|
||||
security-events: read
|
||||
statuses: read
|
||||
attestations: read
|
||||
contents: write
|
||||
uses: ./.github/workflows/d-publish-image.yaml
|
||||
needs: [create-release, retag]
|
||||
with:
|
||||
@@ -89,4 +89,24 @@ jobs:
|
||||
support_platforms: true
|
||||
cosign: true
|
||||
secrets: inherit
|
||||
|
||||
post-release:
|
||||
permissions:
|
||||
actions: read
|
||||
checks: read
|
||||
deployments: read
|
||||
discussions: read
|
||||
id-token: write
|
||||
issues: read
|
||||
packages: write
|
||||
pages: read
|
||||
pull-requests: read
|
||||
repository-projects: read
|
||||
security-events: read
|
||||
statuses: read
|
||||
attestations: read
|
||||
contents: write
|
||||
uses: ./.github/workflows/e-post-release.yaml
|
||||
needs: [publish-image]
|
||||
with:
|
||||
TAG: ${{ needs.retag.outputs.NEW_TAG }}
|
||||
secrets: inherit
|
||||
|
||||
17
.github/workflows/04-publish-krew-plugin.yaml
vendored
17
.github/workflows/04-publish-krew-plugin.yaml
vendored
@@ -1,17 +0,0 @@
|
||||
name: 04-publish_krew_plugin
|
||||
permissions: read-all
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
jobs:
|
||||
publish_krew_plugin:
|
||||
name: Publish Krew plugin
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'kubescape'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Update new version in krew-index
|
||||
uses: rajatjindal/krew-release-bot@92da038bbf995803124a8e50ebd438b2f37bbbb0 # ratchet:rajatjindal/krew-release-bot@v0.0.43
|
||||
4
.github/workflows/a-pr-scanner.yaml
vendored
4
.github/workflows/a-pr-scanner.yaml
vendored
@@ -27,7 +27,7 @@ jobs:
|
||||
name: Create cross-platform build
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu22-core4-mem16-ssd150
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
GITGUARDIAN_API_KEY: ${{ secrets.GITGUARDIAN_API_KEY }}
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
name: PR Scanner
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu22-core4-mem16-ssd150
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
||||
@@ -18,7 +18,7 @@ on:
|
||||
GO_VERSION:
|
||||
required: false
|
||||
type: string
|
||||
default: "1.21"
|
||||
default: "1.23"
|
||||
GO111MODULE:
|
||||
required: false
|
||||
type: string
|
||||
@@ -30,7 +30,32 @@ on:
|
||||
BINARY_TESTS:
|
||||
type: string
|
||||
required: false
|
||||
default: '["ks_microservice_create_2_cronjob_mitre_and_nsa_proxy", "ks_microservice_triggering_with_cron_job", "ks_microservice_update_cronjob_schedule", "ks_microservice_delete_cronjob", "ks_microservice_create_2_cronjob_mitre_and_nsa", "ks_microservice_ns_creation", "ks_microservice_on_demand", "ks_microservice_mitre_framework_on_demand", "ks_microservice_nsa_and_mitre_framework_demand", "scan_nsa", "scan_mitre", "scan_with_exceptions", "scan_repository", "scan_local_file", "scan_local_glob_files", "scan_local_list_of_files", "scan_with_exception_to_backend", "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_custom_framework", "scan_customer_configuration", "host_scanner", "scan_compliance_score", "control_cluster_from_CLI_config_scan_exclude_namespaces", "control_cluster_from_CLI_config_scan_include_namespaces", "control_cluster_from_CLI_config_scan_host_scanner_enabled", "control_cluster_from_CLI_config_scan_MITRE_framework", "control_cluster_from_CLI_vulnerabilities_scan_default", "control_cluster_from_CLI_vulnerabilities_scan_include_namespaces"]'
|
||||
default: '[
|
||||
"ks_microservice_create_2_cronjob_mitre_and_nsa_proxy",
|
||||
"ks_microservice_triggering_with_cron_job",
|
||||
"ks_microservice_update_cronjob_schedule",
|
||||
"ks_microservice_delete_cronjob",
|
||||
"ks_microservice_create_2_cronjob_mitre_and_nsa",
|
||||
"ks_microservice_ns_creation",
|
||||
"ks_microservice_on_demand",
|
||||
"ks_microservice_mitre_framework_on_demand",
|
||||
"ks_microservice_nsa_and_mitre_framework_demand",
|
||||
"scan_nsa",
|
||||
"scan_mitre",
|
||||
"scan_with_exceptions",
|
||||
"scan_repository",
|
||||
"scan_local_file",
|
||||
"scan_local_glob_files",
|
||||
"scan_local_list_of_files",
|
||||
"scan_with_exception_to_backend",
|
||||
"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_custom_framework",
|
||||
"scan_customer_configuration",
|
||||
"scan_compliance_score"
|
||||
]'
|
||||
|
||||
workflow_call:
|
||||
inputs:
|
||||
@@ -45,7 +70,7 @@ on:
|
||||
type: string
|
||||
GO_VERSION:
|
||||
type: string
|
||||
default: "1.21"
|
||||
default: "1.23"
|
||||
GO111MODULE:
|
||||
required: true
|
||||
type: string
|
||||
@@ -54,7 +79,25 @@ on:
|
||||
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_custom_framework", "scan_customer_configuration", "host_scanner", "scan_compliance_score", "scan_custom_framework_scanning_file_scope_testing", "scan_custom_framework_scanning_cluster_scope_testing", "scan_custom_framework_scanning_cluster_and_file_scope_testing" ]'
|
||||
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_custom_framework",
|
||||
"scan_customer_configuration",
|
||||
"scan_compliance_score",
|
||||
"scan_custom_framework_scanning_file_scope_testing",
|
||||
"scan_custom_framework_scanning_cluster_scope_testing",
|
||||
"scan_custom_framework_scanning_cluster_and_file_scope_testing"
|
||||
]'
|
||||
|
||||
jobs:
|
||||
wf-preparation:
|
||||
@@ -103,31 +146,48 @@ jobs:
|
||||
needs: wf-preparation
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-large
|
||||
steps:
|
||||
- name: (debug) Step 1 - Check disk space before checkout
|
||||
run: df -h
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: (debug) Step 2 - Check disk space before installing Go
|
||||
run: df -h
|
||||
|
||||
- uses: actions/setup-go@v4
|
||||
name: Installing go
|
||||
with:
|
||||
go-version: ${{ inputs.GO_VERSION }}
|
||||
cache: true
|
||||
|
||||
- name: (debug) Step 3 - Check disk space before build
|
||||
run: df -h
|
||||
|
||||
- name: Test core pkg
|
||||
run: ${{ env.DOCKER_CMD }} go test -v ./...
|
||||
if: startsWith(github.ref, 'refs/tags')
|
||||
|
||||
- name: (debug) Step 4 - Check disk space before testing httphandler pkg
|
||||
run: df -h
|
||||
|
||||
- name: Test httphandler pkg
|
||||
run: ${{ env.DOCKER_CMD }} sh -c 'cd httphandler && go test -v ./...'
|
||||
if: startsWith(github.ref, 'refs/tags')
|
||||
|
||||
- name: (debug) Step 5 - Check disk space before setting up Syft
|
||||
run: df -h
|
||||
|
||||
- uses: anchore/sbom-action/download-syft@v0.15.2
|
||||
name: Setup Syft
|
||||
|
||||
- name: (debug) Step 6 - Check disk space before goreleaser
|
||||
run: df -h
|
||||
|
||||
- uses: goreleaser/goreleaser-action@v5
|
||||
name: Build
|
||||
with:
|
||||
@@ -139,12 +199,18 @@ jobs:
|
||||
CLIENT: ${{ inputs.CLIENT }}
|
||||
CGO_ENABLED: ${{ inputs.CGO_ENABLED }}
|
||||
|
||||
- name: (debug) Step 7 - Check disk space before smoke testing
|
||||
run: df -h
|
||||
|
||||
- name: Smoke Testing
|
||||
env:
|
||||
RELEASE: ${{ inputs.RELEASE }}
|
||||
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
|
||||
run: ${{ env.DOCKER_CMD }} python3 smoke_testing/init.py ${PWD}/dist/kubescape-ubuntu-latest
|
||||
|
||||
- name: (debug) Step 8 - Check disk space before golangci-lint
|
||||
run: df -h
|
||||
|
||||
- name: golangci-lint
|
||||
continue-on-error: true
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
@@ -155,6 +221,9 @@ jobs:
|
||||
skip-pkg-cache: true
|
||||
skip-build-cache: true
|
||||
|
||||
- name: (debug) Step 9 - Check disk space before uploading artifacts
|
||||
run: df -h
|
||||
|
||||
- uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # ratchet:actions/upload-artifact@v3.1.1
|
||||
name: Upload artifacts
|
||||
with:
|
||||
@@ -162,9 +231,12 @@ jobs:
|
||||
path: dist/kubescape*
|
||||
if-no-files-found: error
|
||||
|
||||
- name: (debug) Step 10 - Check disk space after uploading artifacts
|
||||
run: df -h
|
||||
|
||||
build-http-image:
|
||||
permissions:
|
||||
contents: read
|
||||
contents: write
|
||||
id-token: write
|
||||
packages: write
|
||||
pull-requests: read
|
||||
@@ -177,7 +249,7 @@ jobs:
|
||||
CGO_ENABLED: 0
|
||||
GO111MODULE: "on"
|
||||
BUILD_PLATFORM: linux/amd64,linux/arm64
|
||||
GO_VERSION: "1.21"
|
||||
GO_VERSION: "1.23"
|
||||
REQUIRED_TESTS: '[
|
||||
"ks_microservice_create_2_cronjob_mitre_and_nsa_proxy",
|
||||
"ks_microservice_triggering_with_cron_job",
|
||||
@@ -202,15 +274,8 @@ jobs:
|
||||
"scan_repository_from_url_and_submit_to_backend",
|
||||
"scan_with_custom_framework",
|
||||
"scan_customer_configuration",
|
||||
"host_scanner",
|
||||
"scan_compliance_score",
|
||||
"control_cluster_from_CLI_config_scan_exclude_namespaces",
|
||||
"control_cluster_from_CLI_config_scan_include_namespaces",
|
||||
"control_cluster_from_CLI_config_scan_host_scanner_enabled",
|
||||
"control_cluster_from_CLI_config_scan_MITRE_framework",
|
||||
"control_cluster_from_CLI_vulnerabilities_scan_default",
|
||||
"control_cluster_from_CLI_vulnerabilities_scan_include_namespaces"
|
||||
]'
|
||||
"scan_compliance_score"
|
||||
]'
|
||||
COSIGN: true
|
||||
HELM_E2E_TEST: true
|
||||
FORCE: true
|
||||
|
||||
2
.github/workflows/build-image.yaml
vendored
2
.github/workflows/build-image.yaml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
permissions:
|
||||
id-token: write
|
||||
packages: write
|
||||
contents: read
|
||||
contents: write
|
||||
pull-requests: read
|
||||
uses: kubescape/workflows/.github/workflows/incluster-comp-pr-merged.yaml@main
|
||||
with:
|
||||
|
||||
4
.github/workflows/c-create-release.yaml
vendored
4
.github/workflows/c-create-release.yaml
vendored
@@ -24,8 +24,8 @@ jobs:
|
||||
MAC_OS: macos-latest
|
||||
UBUNTU_OS: ubuntu-latest
|
||||
WINDOWS_OS: windows-latest
|
||||
# permissions:
|
||||
# contents: write
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # ratchet:actions/download-artifact@v3.0.2
|
||||
id: download-artifact
|
||||
|
||||
2
.github/workflows/d-publish-image.yaml
vendored
2
.github/workflows/d-publish-image.yaml
vendored
@@ -2,7 +2,7 @@ name: d-publish-image
|
||||
permissions:
|
||||
actions: read
|
||||
checks: read
|
||||
contents: read
|
||||
contents: write
|
||||
deployments: read
|
||||
discussions: read
|
||||
id-token: write
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
name: 03-post_release
|
||||
name: e-post_release
|
||||
permissions: read-all
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
branches:
|
||||
- 'master'
|
||||
- 'main'
|
||||
workflow_call:
|
||||
inputs:
|
||||
TAG:
|
||||
description: 'Tag name'
|
||||
required: true
|
||||
type: string
|
||||
jobs:
|
||||
post_release:
|
||||
name: Post release jobs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Digest
|
||||
uses: MCJack123/ghaction-generate-release-hashes@c03f3111b39432dde3edebe401c5a8d1ffbbf917 # ratchet:MCJack123/ghaction-generate-release-hashes@v1
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
hash-type: sha1
|
||||
file-name: kubescape-release-digests
|
||||
submodules: recursive
|
||||
- name: Update new version in krew-index
|
||||
uses: rajatjindal/krew-release-bot@v0.0.47
|
||||
if: github.repository_owner == 'kubescape'
|
||||
env:
|
||||
GITHUB_REF: ${{ inputs.TAG }}
|
||||
- name: Invoke workflow to update packaging
|
||||
uses: benc-uk/workflow-dispatch@v1
|
||||
if: github.repository_owner == 'kubescape'
|
||||
@@ -1,6 +1,6 @@
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: true
|
||||
shadow: true
|
||||
dupl:
|
||||
threshold: 200
|
||||
goconst:
|
||||
@@ -24,7 +24,6 @@ linters:
|
||||
- gosimple
|
||||
disable:
|
||||
# temporarily disabled
|
||||
- varcheck
|
||||
- errcheck
|
||||
- dupl
|
||||
- gocritic
|
||||
@@ -36,8 +35,6 @@ linters:
|
||||
- unparam
|
||||
#- forbidigo # <- see later
|
||||
# should remain disabled
|
||||
- deadcode # deprecated linter
|
||||
- maligned
|
||||
- lll
|
||||
- gochecknoinits
|
||||
- gochecknoglobals
|
||||
|
||||
@@ -3,14 +3,16 @@
|
||||
First, it is awesome that you are considering contributing to Kubescape! Contributing is important and fun and we welcome your efforts.
|
||||
|
||||
When contributing, we categorize contributions into two:
|
||||
* Small code changes or fixes, whose scope is limited to a single or two files
|
||||
* Small code changes or fixes, whose scope is limited to one or two files
|
||||
* Complex features and improvements, with potentially unlimited scope
|
||||
|
||||
If you have a small change, feel free to fire up a Pull Request.
|
||||
|
||||
When planning a bigger change, please first discuss the change you wish to make via an issue,
|
||||
When planning a bigger change, please first discuss the change you wish to make via an [issue](https://github.com/kubescape/kubescape/issues),
|
||||
so the maintainers are able to help guide you and let you know if you are going in the right direction.
|
||||
|
||||
[You can also find the maintainers on the CNCF Slack](https://kubescape.io/project/community/#slack) or [join our bi-weekly project meeting](https://kubescape.io/project/community/#community-meetings).
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Please follow our [code of conduct](CODE_OF_CONDUCT.md) in all of your interactions within the project.
|
||||
|
||||
@@ -4,26 +4,41 @@
|
||||
|
||||
The Kubescape project is an open-source initiative dedicated to improve security and best practices in Kubernetes environments. This document outlines the governance structure of the Kubescape project and provides guidance for its community contributors.
|
||||
|
||||
## Decision Making
|
||||
## Code of Conduct
|
||||
|
||||
### Maintainers
|
||||
The CNCF has defined its core values and norms in a [code of conduct](CODE_OF_CONDUCT.md). As a CNCF project, we require all participants in the Kubescape community to behave in line with the standards set out in that document.
|
||||
|
||||
- Maintainers are responsible for the smooth operation of the project.
|
||||
- They review and merge pull requests, manage releases, and ensure the quality and stability of the codebase.
|
||||
- Maintainers are chosen based on their ongoing contributions and their demonstrated commitment to the project.
|
||||
- Everyone who had at least 5 code contribution in the last 12 month can submit her/himself for joining the maintainer team
|
||||
- Maintainers who are not taken part in the project work (code, reviews, discussions) for 12 month are automaticaly removed from the maintainer team
|
||||
|
||||
|
||||
### Committers
|
||||
|
||||
- Committers are contributors who have made significant and consistent contributions to the project.
|
||||
- They have the ability to merge minor pull requests if assigned by maintainers.
|
||||
- A contributor can be proposed as a committer by any existing maintainer. The proposal will be reviewed and voted on by the existing maintainers.
|
||||
## Contributor roles
|
||||
|
||||
### Community Members
|
||||
|
||||
- Anyone can become a community member by contributing to the project. This can be in the form of code contributions, documentation, or any other form of project support.
|
||||
- Contribute to the project in any form.
|
||||
- Participate in discussions and provide feedback.
|
||||
|
||||
Anyone can become a community member by contributing to the project. This can be in the form of code contributions, documentation, or any other form of project support.
|
||||
|
||||
### Committers
|
||||
|
||||
Committers are community members who have made significant and consistent contributions to the project. They have the ability to merge minor pull requests if assigned by maintainers.
|
||||
|
||||
- Review and merge minor pull requests.
|
||||
- Assist maintainers in project tasks.
|
||||
- Promote best practices within the community.
|
||||
|
||||
A contributor can be proposed as a committer by any existing maintainer. The proposal will be reviewed and voted on by the existing maintainers.
|
||||
|
||||
### Maintainers
|
||||
|
||||
Maintainers are responsible for the smooth operation of the project. They review and merge pull requests, manage releases, and ensure the quality and stability of the codebase.
|
||||
|
||||
- Ensure the quality and stability of the project.
|
||||
- Resolve conflicts.
|
||||
- Provide direction and set priorities for the project.
|
||||
|
||||
Maintainers are chosen based on their ongoing contributions and their demonstrated commitment to the project.
|
||||
|
||||
- Any committer who had at least 5 code contribution in the past 12 month can submit themselves to join the maintainer team. The maintainers will appoint members by a majority vote.
|
||||
- Maintainers who have not taken part in project work (code, reviews, discussions) in the past 12 months will be considered inactive, and may be removed from the maintainer team.
|
||||
|
||||
## Processes
|
||||
|
||||
@@ -40,26 +55,6 @@ The Kubescape project is an open-source initiative dedicated to improve security
|
||||
2. If the conflict cannot be resolved, it will be escalated to the maintainers for resolution.
|
||||
3. Maintainers' decision will be final in case of unresolved conflicts.
|
||||
|
||||
## Roles and Responsibilities
|
||||
|
||||
### Maintainers
|
||||
|
||||
- Ensure the quality and stability of the project.
|
||||
- Resolve conflicts.
|
||||
- Provide direction and set priorities for the project.
|
||||
|
||||
### Committers
|
||||
|
||||
- Review and merge minor pull requests.
|
||||
- Assist maintainers in project tasks.
|
||||
- Promote best practices within the community.
|
||||
|
||||
### Community Members
|
||||
|
||||
- Contribute to the project in any form.
|
||||
- Participate in discussions and provide feedback.
|
||||
- Respect the code of conduct and governance of the project.
|
||||
|
||||
## Changes to the Governance Document
|
||||
|
||||
Proposed changes to this governance document should follow the same process as any other code change to the Kubescape project (see "Proposing Changes").
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
The following table lists the Kubescape project core maintainers:
|
||||
|
||||
| Name | GitHub | Organization | Added/Renewed On |
|
||||
| --- | --- | --- | --- |
|
||||
| [Matthias Bertschy](https://www.linkedin.com/in/matthias-bertschy-b427b815/) | [@matthyx](https://github.com/matthyx) | [ARMO](https://www.armosec.io/) | 2023-01-01 |
|
||||
| [Craig Box](https://www.linkedin.com/in/crbnz/) | [@craigbox](https://github.com/craigbox) | [Solo.io](https://www.solo.io/) | 2022-10-31 |
|
||||
| [Ben Hirschberg](https://www.linkedin.com/in/benyamin-ben-hirschberg-66141890) | [@slashben](https://github.com/slashben) | [ARMO](https://www.armosec.io/) | 2021-09-01 |
|
||||
| [Rotem Refael](https://www.linkedin.com/in/rotem-refael) | [@rotemamsa](https://github.com/rotemamsa) | [ARMO](https://www.armosec.io/) | 2021-10-11 |
|
||||
| [David Wertenteil](https://www.linkedin.com/in/david-wertenteil-0ba277b9) | [@dwertent](https://github.com/dwertent) | [ARMO](https://www.armosec.io/) | 2021-09-01 |
|
||||
| Name | GitHub | Organization
|
||||
| --- | --- | ---
|
||||
| [Matthias Bertschy](https://www.linkedin.com/in/matthias-bertschy-b427b815/) | [@matthyx](https://github.com/matthyx) | [ARMO](https://www.armosec.io/)
|
||||
| [Craig Box](https://www.linkedin.com/in/crbnz/) | [@craigbox](https://github.com/craigbox) | [Solo.io](https://www.solo.io/)
|
||||
| [Ben Hirschberg](https://www.linkedin.com/in/benyamin-ben-hirschberg-66141890) | [@slashben](https://github.com/slashben) | [ARMO](https://www.armosec.io/)
|
||||
| [Rotem Refael](https://www.linkedin.com/in/rotem-refael) | [@rotemamsa](https://github.com/rotemamsa) | [ARMO](https://www.armosec.io/)
|
||||
| [David Wertenteil](https://www.linkedin.com/in/david-wertenteil-0ba277b9) | [@dwertent](https://github.com/dwertent) | [Kaleido](https://kaleido.io/)
|
||||
|
||||
|
||||
74
README.md
74
README.md
@@ -20,21 +20,34 @@
|
||||
<img alt="Kubescape logo" align="right" src="https://raw.githubusercontent.com/cncf/artwork/master/projects/kubescape/stacked/color/kubescape-stacked-color.svg" width="150">
|
||||
</picture>
|
||||
|
||||
_An open-source Kubernetes security platform for your clusters, CI/CD pipelines, and IDE that seperates out the security signal from the scanner noise_
|
||||
_Comprehensive Kubernetes Security from Development to Runtime_
|
||||
|
||||
Kubescape is an open-source Kubernetes security platform, built for use in your day-to-day workflow, by fitting into your clusters, CI/CD pipelines and IDE. It serves as a one-stop-shop for Kuberenetes security and includes vulnerability and misconfiguration scanning. You can run scans via the CLI, or add the Kubescape Helm chart, which gives an in-depth view of what is going on in the cluster.
|
||||
Kubescape is an open-source Kubernetes security platform that provides comprehensive security coverage from left to right across the entire development and deployment lifecycle. It offers hardening, posture management, and runtime security capabilities to ensure robust protection for Kubernetes environments.
|
||||
|
||||
Kubescape includes misconfiguration and vulnerability scanning as well as risk analysis and security compliance indicators. All results are presented in context and users get many cues on what to do based on scan results.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.
|
||||
**Key features of Kubescape include**
|
||||
|
||||
Kubescape scans clusters, YAML files, and Helm charts. It detects misconfigurations according to multiple frameworks (including [NSA-CISA](https://www.armosec.io/blog/kubernetes-hardening-guidance-summary-by-armo/?utm_source=github&utm_medium=repository), [MITRE ATT&CK®](https://www.microsoft.com/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/) and the [CIS Benchmark](https://www.armosec.io/blog/cis-kubernetes-benchmark-framework-scanning-tools-comparison/?utm_source=github&utm_medium=repository)).
|
||||
* **Shift-left security**: Kubescape enables developers to scan for misconfigurations as early as the manifest file submission stage, promoting a proactive approach to security.
|
||||
* **IDE and CI/CD integration**: The tool integrates seamlessly with popular IDEs like VSCode and Lens, as well as CI/CD platforms such as GitHub and GitLab, allowing for security checks throughout the development process.
|
||||
* **Cluster scanning**: Kubescape can scan active Kubernetes clusters for vulnerabilities, misconfigurations, and security issues
|
||||
* **Multiple framework support**: Kubescape can test against various security frameworks, including NSA, MITRE, SOC2, and more.
|
||||
* **YAML and Helm chart validation**: The tool checks YAML files and Helm charts for correct configuration according to the frameworks above, without requiring an active cluster.
|
||||
* **Kubernetes hardening**: Kubescape ensures proactive identification and rapid remediation of misconfigurations and vulnerabilities through manual, recurring, or event-triggered scans.
|
||||
* **Runtime security**: Kubescape extends its protection to the runtime environment, providing continuous monitoring and threat detection for deployed applications.
|
||||
* **Compliance management**: The tool aids in maintaining compliance with recognized frameworks and standards, simplifying the process of meeting regulatory requirements.
|
||||
* **Multi-cloud support**: Kubescape offers frictionless security across various cloud providers and Kubernetes distributions.
|
||||
|
||||
By providing this comprehensive security coverage from development to production, Kubescape enables organizations to implement a robust security posture throughout their Kubernetes deployment, addressing potential vulnerabilities and threats at every stage of the application lifecycle.
|
||||
|
||||
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/).
|
||||
|
||||
## Demo
|
||||
<img src="docs/img/demo-v3.gif">
|
||||
|
||||
_Please [star ⭐](https://github.com/kubescape/kubescape/stargazers) the repo if you want us to continue developing and improving Kubescape! 😀_
|
||||
|
||||
## Demo
|
||||
|
||||
Kubescape has a command line tool that you can use to quickly get a report on the security posture of a Kubernetes cluster:
|
||||
|
||||
<img src="docs/img/demo-v3.gif">
|
||||
|
||||
## Getting started
|
||||
|
||||
Experimenting with Kubescape is as easy as:
|
||||
@@ -43,13 +56,13 @@ Experimenting with Kubescape is as easy as:
|
||||
curl -s https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh | /bin/bash
|
||||
```
|
||||
|
||||
This script will automatically download the latest Kubescape CLI release and scan the Kubernetes cluster in your current kubectl context.
|
||||
|
||||
Learn more about:
|
||||
|
||||
* [Installing Kubescape](docs/installation.md)
|
||||
* [Running your first scan](docs/getting-started.md#run-your-first-scan)
|
||||
* [Usage](docs/getting-started.md#examples)
|
||||
* [Architecture](docs/architecture.md)
|
||||
* [Building Kubescape from source](https://github.com/kubescape/kubescape/wiki/Building)
|
||||
* [Installing the Kubescape CLI](https://kubescape.io/docs/install-cli/)
|
||||
* [Running your first scan](https://kubescape.io/docs/scanning/)
|
||||
* [Accepting risk with exceptions](https://kubescape.io/docs/accepting-risk/)
|
||||
|
||||
_Did you know you can use Kubescape in all these places?_
|
||||
|
||||
@@ -57,45 +70,54 @@ _Did you know you can use Kubescape in all these places?_
|
||||
<img src="docs/img/ksfromcodetodeploy.png" alt="Places you can use Kubescape: in your IDE, CI, CD, or against a running cluster.">
|
||||
</div>
|
||||
|
||||
## Kubescape-operator Helm-Chart
|
||||
### Continuous security monitoring with the Kubescape Operator
|
||||
|
||||
Besides the CLI, the Kubescape operator can also be installed via a Helm chart. Installing the Helm chart is an excellent way to begin using Kubescape, as it provides extensive features such as continuous scanning, image vulnerability scanning, runtime analysis, network policy generation, and more. You can find the Helm chart in the [Kubescape-operator documentation](https://kubescape.io/docs/install-operator/).
|
||||
As well as a CLI, Kubescape provides an in-cluster mode, which is installed via a Helm chart. Kubescape in-cluster provides extensive features such as continuous scanning, image vulnerability scanning, runtime analysis, network policy generation, and more. [Learn more about the Kubescape operator](https://kubescape.io/docs/operator/).
|
||||
|
||||
## Kubescape GitHub Action
|
||||
### Using Kubescape as a GitHub Action
|
||||
|
||||
Kubescape can be used as a GitHub Action. This is a great way to integrate Kubescape into your CI/CD pipeline. You can find the Kubescape GitHub Action in the [GitHub Action marketplace](https://github.com/marketplace/actions/kubescape).
|
||||
|
||||
## Under the hood
|
||||
|
||||
Kubescape uses [Open Policy Agent](https://github.com/open-policy-agent/opa) to verify Kubernetes objects against [a library of posture controls](https://github.com/kubescape/regolibrary).
|
||||
Kubescape uses [Open Policy Agent](https://github.com/open-policy-agent/opa) to verify Kubernetes objects against [a library of posture controls](https://github.com/kubescape/regolibrary). Kubescape retrieves Kubernetes resources from the API server and runs a set of [Rego snippets](https://www.openpolicyagent.org/docs/latest/policy-language/) developed by [ARMO](https://www.armosec.io?utm_source=github&utm_medium=repository).
|
||||
|
||||
By default, the results are printed in a console-friendly manner, but they can be:
|
||||
Container image scanning is powered by [Grype](https://github.com/anchore/grype) and image patching uses [Copacetic](https://github.com/project-copacetic/copacetic).
|
||||
|
||||
* exported to JSON or junit XML
|
||||
By default, CLI scan results are printed in a console-friendly manner, but they can be:
|
||||
|
||||
* exported to JSON, junit XML or SARIF
|
||||
* rendered to HTML or PDF
|
||||
* submitted to a [cloud service](docs/providers.md)
|
||||
|
||||
It retrieves Kubernetes objects from the API server and runs a set of [Rego snippets](https://www.openpolicyagent.org/docs/latest/policy-language/) developed by [ARMO](https://www.armosec.io?utm_source=github&utm_medium=repository).
|
||||
### In-cluster architecture
|
||||
|
||||

|
||||
|
||||
## Community
|
||||
|
||||
Kubescape is an open source project, we welcome your feedback and ideas for improvement. We are part of the Kubernetes community and are building more tests and controls as the ecosystem develops.
|
||||
We welcome user feedback and ideas for improvement.
|
||||
|
||||
We hold [community meetings](https://zoom.us/j/95174063585) on Zoom, every second week on Tuesdays, at 15:00 CET. ([See that in your local time zone](https://time.is/compare/1500_in_CET)).
|
||||
Kubescape users and developers meet on the CNCF Slack. [Join it](https://slack.cncf.io/) and find us in [#kubescape](https://cloud-native.slack.com/archives/C04EY3ZF9GE) or [#kubescape-dev](https://cloud-native.slack.com/archives/C04GY6H082K).
|
||||
|
||||
We hold [community meetings](https://zoom.us/j/95174063585) on Zoom, every second Tuesday, at 15:00 CET. ([See that in your local time zone](https://time.is/compare/1500_in_CET).
|
||||
|
||||
* Meetings are announced in [#kubescape-dev](https://cloud-native.slack.com/archives/C04GY6H082K) on Slack (including any cancellations).
|
||||
* [The agenda and notes are in a public Google doc](https://docs.google.com/document/d/1X_eyhPzJvb4ascVQ2e0jN87LAvq7lTuXT5d4gQxi8us/edit?tab=t.0).
|
||||
* [Recordings are posted to YouTube](https://www.youtube.com/@kubescape).
|
||||
|
||||
The Kubescape project follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
|
||||
|
||||
### Adopters
|
||||
|
||||
See [here](ADOPTERS.md) a list of adopters.
|
||||
See [here](ADOPTERS.md) for a list of reference adopters.
|
||||
|
||||
## Contributions
|
||||
### Contributions
|
||||
|
||||
Thanks to all our contributors! Check out our [CONTRIBUTING](CONTRIBUTING.md) file to learn how to join them.
|
||||
|
||||
* Feel free to pick a task from the [issues](https://github.com/kubescape/kubescape/issues?q=is%3Aissue+is%3Aopen+label%3A%22open+for+contribution%22), [roadmap](docs/roadmap.md) or suggest a feature of your own.
|
||||
* [Open an issue](https://github.com/kubescape/kubescape/issues/new/choose): we aim to respond to all issues within 48 hours.
|
||||
* [Join the CNCF Slack](https://slack.cncf.io/) and then our [users](https://cloud-native.slack.com/archives/C04EY3ZF9GE) or [developers](https://cloud-native.slack.com/archives/C04GY6H082K) channel.
|
||||
|
||||
<br>
|
||||
|
||||
@@ -105,11 +127,11 @@ Thanks to all our contributors! Check out our [CONTRIBUTING](CONTRIBUTING.md) f
|
||||
|
||||
## Changelog
|
||||
|
||||
Kubescape changes are tracked on the [release](https://github.com/kubescape/kubescape/releases) page
|
||||
Kubescape changes are tracked on the [release](https://github.com/kubescape/kubescape/releases) page.
|
||||
|
||||
## License
|
||||
|
||||
Copyright 2021-2023, the Kubescape Authors. All rights reserved. Kubescape is released under the Apache 2.0 license. See the [LICENSE](LICENSE) file for details.
|
||||
Copyright 2021-2024, the Kubescape Authors. All rights reserved. Kubescape is released under the Apache 2.0 license. See the [LICENSE](LICENSE) file for details.
|
||||
|
||||
Kubescape is a [Cloud Native Computing Foundation (CNCF) sandbox project](https://www.cncf.io/sandbox-projects/) and was contributed by [ARMO](https://www.armosec.io/?utm_source=github&utm_medium=repository).
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM --platform=$BUILDPLATFORM golang:1.21-bullseye as builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.23-bookworm AS builder
|
||||
|
||||
ENV GO111MODULE=on CGO_ENABLED=0
|
||||
WORKDIR /work
|
||||
@@ -9,7 +9,7 @@ RUN --mount=target=. \
|
||||
--mount=type=cache,target=/go/pkg \
|
||||
cd httphandler && GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o /out/ksserver .
|
||||
|
||||
FROM gcr.io/distroless/static-debian11:nonroot
|
||||
FROM gcr.io/distroless/static-debian12:nonroot
|
||||
|
||||
USER nonroot
|
||||
WORKDIR /home/nonroot/
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM gcr.io/distroless/base-debian11:debug-nonroot
|
||||
FROM gcr.io/distroless/base-debian12:debug-nonroot
|
||||
|
||||
USER nonroot
|
||||
WORKDIR /home/nonroot/
|
||||
|
||||
@@ -3,7 +3,7 @@ package config
|
||||
import (
|
||||
"context"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/kubescape/v3/core/meta"
|
||||
v1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/kubescape/v3/core/meta"
|
||||
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -3,7 +3,7 @@ package config
|
||||
import (
|
||||
"os"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/kubescape/v3/core/meta"
|
||||
v1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/core"
|
||||
"github.com/kubescape/kubescape/v3/core/meta"
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/core"
|
||||
"github.com/kubescape/kubescape/v3/core/meta"
|
||||
|
||||
@@ -47,6 +47,7 @@ The patch command can be run in 2 ways:
|
||||
| -a, --addr | Address of the buildkitd service | No | unix:///run/buildkit/buildkitd.sock |
|
||||
| -t, --tag | Tag of the resultant patched image | No | image_name-patched |
|
||||
| --timeout | Timeout for the patching process | No | 5m |
|
||||
| --ignore-errors| Ignore errors during patching | No | false |
|
||||
| -u, --username | Username for the image registry login | No | |
|
||||
| -p, --password | Password for the image registry login | No | |
|
||||
| -f, --format | Output file format. | No | |
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/distribution/reference"
|
||||
"github.com/docker/distribution/reference"
|
||||
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/kubescape/v3/cmd/shared"
|
||||
@@ -69,6 +69,7 @@ func GetPatchCmd(ks meta.IKubescape) *cobra.Command {
|
||||
patchCmd.PersistentFlags().StringVarP(&patchInfo.PatchedImageTag, "tag", "t", "", "Tag for the patched image. Defaults to '<image-tag>-patched' ")
|
||||
patchCmd.PersistentFlags().StringVarP(&patchInfo.BuildkitAddress, "address", "a", "unix:///run/buildkit/buildkitd.sock", "Address of buildkitd service, defaults to local buildkitd.sock")
|
||||
patchCmd.PersistentFlags().DurationVar(&patchInfo.Timeout, "timeout", 5*time.Minute, "Timeout for the operation, defaults to '5m'")
|
||||
patchCmd.PersistentFlags().BoolVar(&patchInfo.IgnoreError, "ignore-errors", false, "Ignore errors and continue patching other images. Default to false")
|
||||
|
||||
patchCmd.PersistentFlags().StringVarP(&patchInfo.Username, "username", "u", "", "Username for registry login")
|
||||
patchCmd.PersistentFlags().StringVarP(&patchInfo.Password, "password", "p", "", "Password for registry login")
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/k8s-interface/k8sinterface"
|
||||
"github.com/kubescape/kubescape/v3/cmd/completion"
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/kubescape/kubescape/v3/cmd/patch"
|
||||
"github.com/kubescape/kubescape/v3/cmd/scan"
|
||||
"github.com/kubescape/kubescape/v3/cmd/update"
|
||||
"github.com/kubescape/kubescape/v3/cmd/vap"
|
||||
"github.com/kubescape/kubescape/v3/cmd/version"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils/getter"
|
||||
@@ -97,6 +98,7 @@ func getRootCmd(ks meta.IKubescape) *cobra.Command {
|
||||
rootCmd.AddCommand(update.GetUpdateCmd())
|
||||
rootCmd.AddCommand(fix.GetFixCmd(ks))
|
||||
rootCmd.AddCommand(patch.GetPatchCmd(ks))
|
||||
rootCmd.AddCommand(vap.GetVapHelperCmd())
|
||||
rootCmd.AddCommand(operator.GetOperatorCmd(ks))
|
||||
|
||||
// deprecated commands
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
v1 "github.com/kubescape/backend/pkg/client/v1"
|
||||
"github.com/kubescape/backend/pkg/servicediscovery"
|
||||
sdClientV2 "github.com/kubescape/backend/pkg/servicediscovery/v2"
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/go-logger/iconlogger"
|
||||
"github.com/kubescape/go-logger/zaplogger"
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
apisv1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/kubescape/v3/cmd/shared"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/kubescape/v3/cmd/shared"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/kubescape/v3/cmd/shared"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/meta"
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils/getter"
|
||||
"github.com/kubescape/kubescape/v3/core/meta"
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/meta"
|
||||
v1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/kubescape/backend/pkg/versioncheck"
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
236
cmd/vap/vap.go
Normal file
236
cmd/vap/vap.go
Normal file
@@ -0,0 +1,236 @@
|
||||
package vap
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
admissionv1 "k8s.io/api/admissionregistration/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
var vapHelperCmdExamples = fmt.Sprintf(`
|
||||
vap command can be used for managing Validating Admission Policies in a Kubernetes cluster.
|
||||
This is an experimental feature and it might change.
|
||||
|
||||
Examples:
|
||||
|
||||
# Install Kubescape CEL admission policy library
|
||||
%[1]s vap deploy-library | kubectl apply -f -
|
||||
# Create a policy binding
|
||||
%[1]s vap create-policy-binding --name my-policy-binding --policy c-0016 --namespace=my-namespace | kubectl apply -f -
|
||||
`, cautils.ExecName())
|
||||
|
||||
func GetVapHelperCmd() *cobra.Command {
|
||||
|
||||
vapHelperCmd := &cobra.Command{
|
||||
Use: "vap",
|
||||
Short: "Helper commands for managing Validating Admission Policies in a Kubernetes cluster",
|
||||
Long: ``,
|
||||
Example: vapHelperCmdExamples,
|
||||
}
|
||||
|
||||
// Create subcommands
|
||||
vapHelperCmd.AddCommand(getDeployLibraryCmd())
|
||||
vapHelperCmd.AddCommand(getCreatePolicyBindingCmd())
|
||||
|
||||
return vapHelperCmd
|
||||
}
|
||||
|
||||
func getDeployLibraryCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "deploy-library",
|
||||
Short: "Install Kubescape CEL admission policy library",
|
||||
Long: ``,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return deployLibrary()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getCreatePolicyBindingCmd() *cobra.Command {
|
||||
var policyBindingName string
|
||||
var policyName string
|
||||
var namespaceArr []string
|
||||
var labelArr []string
|
||||
var action string
|
||||
var parameterReference string
|
||||
|
||||
createPolicyBindingCmd := &cobra.Command{
|
||||
Use: "create-policy-binding",
|
||||
Short: "Create a policy binding",
|
||||
Long: ``,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
// Validate the inputs
|
||||
if err := isValidK8sObjectName(policyBindingName); err != nil {
|
||||
return fmt.Errorf("invalid policy binding name %s: %w", policyBindingName, err)
|
||||
}
|
||||
if err := isValidK8sObjectName(policyName); err != nil {
|
||||
return fmt.Errorf("invalid policy name %s: %w", policyName, err)
|
||||
}
|
||||
for _, namespace := range namespaceArr {
|
||||
if err := isValidK8sObjectName(namespace); err != nil {
|
||||
return fmt.Errorf("invalid namespace %s: %w", namespace, err)
|
||||
}
|
||||
}
|
||||
for _, label := range labelArr {
|
||||
// Label selector must be in the format key=value
|
||||
if !regexp.MustCompile(`^[a-zA-Z0-9]+=[a-zA-Z0-9]+$`).MatchString(label) {
|
||||
return fmt.Errorf("invalid label selector: %s", label)
|
||||
}
|
||||
}
|
||||
if action != "Deny" && action != "Audit" && action != "Warn" {
|
||||
return fmt.Errorf("invalid action: %s", action)
|
||||
}
|
||||
if parameterReference != "" {
|
||||
if err := isValidK8sObjectName(parameterReference); err != nil {
|
||||
return fmt.Errorf("invalid parameter reference %s: %w", parameterReference, err)
|
||||
}
|
||||
}
|
||||
|
||||
return createPolicyBinding(policyBindingName, policyName, action, parameterReference, namespaceArr, labelArr)
|
||||
},
|
||||
}
|
||||
// Must specify the name of the policy binding
|
||||
createPolicyBindingCmd.Flags().StringVarP(&policyBindingName, "name", "n", "", "Name of the policy binding")
|
||||
createPolicyBindingCmd.MarkFlagRequired("name")
|
||||
createPolicyBindingCmd.Flags().StringVarP(&policyName, "policy", "p", "", "Name of the policy to bind the resources to")
|
||||
createPolicyBindingCmd.MarkFlagRequired("policy")
|
||||
createPolicyBindingCmd.Flags().StringSliceVar(&namespaceArr, "namespace", []string{}, "Resource namespace selector")
|
||||
createPolicyBindingCmd.Flags().StringSliceVar(&labelArr, "label", []string{}, "Resource label selector")
|
||||
createPolicyBindingCmd.Flags().StringVarP(&action, "action", "a", "Deny", "Action to take when policy fails")
|
||||
createPolicyBindingCmd.Flags().StringVarP(¶meterReference, "parameter-reference", "r", "", "Parameter reference object name")
|
||||
|
||||
return createPolicyBindingCmd
|
||||
}
|
||||
|
||||
// Implementation of the VAP helper commands
|
||||
// deploy-library
|
||||
func deployLibrary() error {
|
||||
logger.L().Info("Downloading the Kubescape CEL admission policy library")
|
||||
// Download the policy-configuration-definition.yaml from the latest release URL
|
||||
policyConfigurationDefinitionURL := "https://github.com/kubescape/cel-admission-library/releases/latest/download/policy-configuration-definition.yaml"
|
||||
policyConfigurationDefinition, err := downloadFileToString(policyConfigurationDefinitionURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Download the basic-control-configuration.yaml from the latest release URL
|
||||
basicControlConfigurationURL := "https://github.com/kubescape/cel-admission-library/releases/latest/download/basic-control-configuration.yaml"
|
||||
basicControlConfiguration, err := downloadFileToString(basicControlConfigurationURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Download the kubescape-validating-admission-policies.yaml from the latest release URL
|
||||
kubescapeValidatingAdmissionPoliciesURL := "https://github.com/kubescape/cel-admission-library/releases/latest/download/kubescape-validating-admission-policies.yaml"
|
||||
kubescapeValidatingAdmissionPolicies, err := downloadFileToString(kubescapeValidatingAdmissionPoliciesURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.L().Info("Successfully downloaded admission policy library")
|
||||
|
||||
// Print the downloaded files to the STDOUT for the user to apply connecting them to a single YAML with ---
|
||||
fmt.Println(policyConfigurationDefinition)
|
||||
fmt.Println("---")
|
||||
fmt.Println(basicControlConfiguration)
|
||||
fmt.Println("---")
|
||||
fmt.Println(kubescapeValidatingAdmissionPolicies)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func downloadFileToString(url string) (string, error) {
|
||||
// Send an HTTP GET request to the URL
|
||||
response, err := http.Get(url) //nolint:gosec
|
||||
if err != nil {
|
||||
return "", err // Return an empty string and the error if the request fails
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
// Check for a successful response (HTTP 200 OK)
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("failed to download file: %s", response.Status)
|
||||
}
|
||||
|
||||
// Read the response body
|
||||
bodyBytes, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
return "", err // Return an empty string and the error if reading fails
|
||||
}
|
||||
|
||||
// Convert the byte slice to a string
|
||||
bodyString := string(bodyBytes)
|
||||
return bodyString, nil
|
||||
}
|
||||
|
||||
func isValidK8sObjectName(name string) error {
|
||||
// Kubernetes object names must consist of lower case alphanumeric characters, '-' or '.',
|
||||
// and must start and end with an alphanumeric character (e.g., 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')
|
||||
// Max length of 63 characters.
|
||||
if len(name) > 63 {
|
||||
return errors.New("name should be less than 63 characters")
|
||||
}
|
||||
|
||||
regex := regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`)
|
||||
if !regex.MatchString(name) {
|
||||
return errors.New("name should consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create a policy binding
|
||||
func createPolicyBinding(bindingName string, policyName string, action string, paramRefName string, namespaceArr []string, labelMatch []string) error {
|
||||
// Create a policy binding struct
|
||||
policyBinding := &admissionv1.ValidatingAdmissionPolicyBinding{}
|
||||
// Print the policy binding after marshalling it to YAML to the STDOUT
|
||||
// The user can apply the output to the cluster
|
||||
policyBinding.APIVersion = "admissionregistration.k8s.io/v1"
|
||||
policyBinding.Name = bindingName
|
||||
policyBinding.Kind = "ValidatingAdmissionPolicyBinding"
|
||||
policyBinding.Spec.PolicyName = policyName
|
||||
policyBinding.Spec.MatchResources = &admissionv1.MatchResources{}
|
||||
if len(namespaceArr) > 0 {
|
||||
policyBinding.Spec.MatchResources.NamespaceSelector = &metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: "kubernetes.io/metadata.name",
|
||||
Operator: metav1.LabelSelectorOpIn,
|
||||
Values: namespaceArr,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if len(labelMatch) > 0 {
|
||||
policyBinding.Spec.MatchResources.ObjectSelector = &metav1.LabelSelector{}
|
||||
policyBinding.Spec.MatchResources.ObjectSelector.MatchLabels = make(map[string]string)
|
||||
for _, label := range labelMatch {
|
||||
labelParts := regexp.MustCompile(`=`).Split(label, 2)
|
||||
policyBinding.Spec.MatchResources.ObjectSelector.MatchLabels[labelParts[0]] = labelParts[1]
|
||||
}
|
||||
}
|
||||
|
||||
policyBinding.Spec.ValidationActions = []admissionv1.ValidationAction{admissionv1.ValidationAction(action)}
|
||||
if paramRefName != "" {
|
||||
policyBinding.Spec.ParamRef = &admissionv1.ParamRef{
|
||||
Name: paramRefName,
|
||||
}
|
||||
}
|
||||
// Marshal the policy binding to YAML
|
||||
out, err := yaml.Marshal(policyBinding)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(out))
|
||||
return nil
|
||||
}
|
||||
10
cmd/vap/vap_test.go
Normal file
10
cmd/vap/vap_test.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package vap
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetVapHelperCmd(t *testing.T) {
|
||||
// Call the GetFixCmd function
|
||||
_ = GetVapHelperCmd()
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/kubescape/backend/pkg/servicediscovery"
|
||||
servicediscoveryv1 "github.com/kubescape/backend/pkg/servicediscovery/v1"
|
||||
servicediscoveryv2 "github.com/kubescape/backend/pkg/servicediscovery/v2"
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/k8s-interface/k8sinterface"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils/getter"
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
spinnerpkg "github.com/briandowns/spinner"
|
||||
"github.com/jwalton/gchalk"
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/mattn/go-isatty"
|
||||
"github.com/schollz/progressbar/v3"
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/kubescape/k8s-interface/workloadinterface"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/opa-utils/objectsenvelopes"
|
||||
"github.com/kubescape/opa-utils/objectsenvelopes/localworkload"
|
||||
|
||||
@@ -38,7 +38,7 @@ type Chart struct {
|
||||
}
|
||||
|
||||
// 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(ctx context.Context, basePath string) (map[string][]workloadinterface.IMetadata, map[string]Chart, map[string]MappingNodes) {
|
||||
func LoadResourcesFromHelmCharts(ctx context.Context, basePath string) (map[string][]workloadinterface.IMetadata, map[string]Chart) {
|
||||
directories, _ := listDirs(basePath)
|
||||
helmDirectories := make([]string, 0)
|
||||
for _, dir := range directories {
|
||||
@@ -49,19 +49,14 @@ func LoadResourcesFromHelmCharts(ctx context.Context, basePath string) (map[stri
|
||||
|
||||
sourceToWorkloads := map[string][]workloadinterface.IMetadata{}
|
||||
sourceToChart := make(map[string]Chart, 0)
|
||||
sourceToNodes := map[string]MappingNodes{}
|
||||
for _, helmDir := range helmDirectories {
|
||||
chart, err := NewHelmChart(helmDir)
|
||||
if err == nil {
|
||||
wls, templateToNodes, errs := chart.GetWorkloadsWithDefaultValues()
|
||||
wls, errs := chart.GetWorkloadsWithDefaultValues()
|
||||
if len(errs) > 0 {
|
||||
logger.L().Ctx(ctx).Warning(fmt.Sprintf("Rendering of Helm chart template '%s', failed: %v", chart.GetName(), errs))
|
||||
continue
|
||||
}
|
||||
for k, v := range templateToNodes {
|
||||
sourceToNodes[k] = v
|
||||
}
|
||||
|
||||
chartName := chart.GetName()
|
||||
for k, v := range wls {
|
||||
sourceToWorkloads[k] = v
|
||||
@@ -72,7 +67,7 @@ func LoadResourcesFromHelmCharts(ctx context.Context, basePath string) (map[stri
|
||||
}
|
||||
}
|
||||
}
|
||||
return sourceToWorkloads, sourceToChart, sourceToNodes
|
||||
return sourceToWorkloads, sourceToChart
|
||||
}
|
||||
|
||||
// If the contents at given path is a Kustomize Directory, LoadResourcesFromKustomizeDirectory will
|
||||
|
||||
@@ -45,7 +45,7 @@ func TestLoadResourcesFromFiles(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLoadResourcesFromHelmCharts(t *testing.T) {
|
||||
sourceToWorkloads, sourceToChartName, _ := LoadResourcesFromHelmCharts(context.TODO(), helmChartPath())
|
||||
sourceToWorkloads, sourceToChartName := LoadResourcesFromHelmCharts(context.TODO(), helmChartPath())
|
||||
assert.Equal(t, 6, len(sourceToWorkloads))
|
||||
|
||||
for file, workloads := range sourceToWorkloads {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/k8s-interface/workloadinterface"
|
||||
"github.com/kubescape/opa-utils/objectsenvelopes/localworkload"
|
||||
@@ -45,35 +45,24 @@ func (hc *HelmChart) GetDefaultValues() map[string]interface{} {
|
||||
return hc.chart.Values
|
||||
}
|
||||
|
||||
// GetWorkloads renders chart template using the default values and returns a map of source file to its workloads
|
||||
func (hc *HelmChart) GetWorkloadsWithDefaultValues() (map[string][]workloadinterface.IMetadata, map[string]MappingNodes, []error) {
|
||||
// GetWorkloadsWithDefaultValues renders chart template using the default values and returns a map of source file to its workloads
|
||||
func (hc *HelmChart) GetWorkloadsWithDefaultValues() (map[string][]workloadinterface.IMetadata, []error) {
|
||||
return hc.GetWorkloads(hc.GetDefaultValues())
|
||||
}
|
||||
|
||||
// GetWorkloads renders chart template using the provided values and returns a map of source (absolute) file path to its workloads
|
||||
func (hc *HelmChart) GetWorkloads(values map[string]interface{}) (map[string][]workloadinterface.IMetadata, map[string]MappingNodes, []error) {
|
||||
func (hc *HelmChart) GetWorkloads(values map[string]interface{}) (map[string][]workloadinterface.IMetadata, []error) {
|
||||
vals, err := helmchartutil.ToRenderValues(hc.chart, values, helmchartutil.ReleaseOptions{}, nil)
|
||||
if err != nil {
|
||||
return nil, nil, []error{err}
|
||||
return nil, []error{err}
|
||||
}
|
||||
|
||||
// change the chart to template with comment, only is template(.yaml added otherwise no)
|
||||
hc.AddCommentToTemplate()
|
||||
|
||||
sourceToFile, err := helmengine.Render(hc.chart, vals)
|
||||
if err != nil {
|
||||
return nil, nil, []error{err}
|
||||
return nil, []error{err}
|
||||
}
|
||||
|
||||
// get the resouse and analysis and store it to the struct
|
||||
fileMapping := make(map[string]MappingNodes)
|
||||
GetTemplateMapping(sourceToFile, fileMapping)
|
||||
|
||||
// delete the comment from chart and from sourceToFile
|
||||
RemoveComment(sourceToFile)
|
||||
|
||||
workloads := make(map[string][]workloadinterface.IMetadata, 0)
|
||||
errs := []error{}
|
||||
workloads := make(map[string][]workloadinterface.IMetadata)
|
||||
var errs []error
|
||||
|
||||
for path, renderedYaml := range sourceToFile {
|
||||
if !IsYaml(strings.ToLower(path)) {
|
||||
@@ -87,13 +76,9 @@ func (hc *HelmChart) GetWorkloads(values map[string]interface{}) (map[string][]w
|
||||
if len(wls) == 0 {
|
||||
continue
|
||||
}
|
||||
if firstPathSeparatorIndex := strings.Index(path, string("/")); firstPathSeparatorIndex != -1 {
|
||||
if firstPathSeparatorIndex := strings.Index(path, "/"); firstPathSeparatorIndex != -1 {
|
||||
absPath := filepath.Join(hc.path, path[firstPathSeparatorIndex:])
|
||||
|
||||
if nodes, ok := fileMapping[path]; ok {
|
||||
fileMapping[absPath] = nodes
|
||||
delete(fileMapping, path)
|
||||
}
|
||||
workloads[absPath] = []workloadinterface.IMetadata{}
|
||||
for i := range wls {
|
||||
lw := localworkload.NewLocalWorkload(wls[i].GetObject())
|
||||
@@ -102,7 +87,7 @@ func (hc *HelmChart) GetWorkloads(values map[string]interface{}) (map[string][]w
|
||||
}
|
||||
}
|
||||
}
|
||||
return workloads, fileMapping, errs
|
||||
return workloads, errs
|
||||
}
|
||||
|
||||
func (hc *HelmChart) AddCommentToTemplate() {
|
||||
@@ -121,27 +106,3 @@ func (hc *HelmChart) AddCommentToTemplate() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func RemoveComment(sourceToFile map[string]string) {
|
||||
// commentRe := regexp.MustCompile(CommentFormat)
|
||||
for fileName, file := range sourceToFile {
|
||||
if !IsYaml(strings.ToLower((fileName))) {
|
||||
continue
|
||||
}
|
||||
sourceToFile[fileName] = commentRe.ReplaceAllLiteralString(file, "")
|
||||
}
|
||||
}
|
||||
|
||||
func GetTemplateMapping(sourceToFile map[string]string, fileMapping map[string]MappingNodes) {
|
||||
for fileName, fileContent := range sourceToFile {
|
||||
mappingNodes, err := GetMapping(fileName, fileContent)
|
||||
if err != nil {
|
||||
// if one file cannot get mapping nodes, generate error, then ignore it
|
||||
logger.L().Warning("Failed to get File Mapping nodes", helpers.String("file name", fileName), helpers.Error(err))
|
||||
continue
|
||||
}
|
||||
if len(mappingNodes.Nodes) != 0 {
|
||||
fileMapping[fileName] = *mappingNodes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func (s *HelmChartTestSuite) TestGetWorkloadsWithOverride() {
|
||||
// Override default value
|
||||
values["image"].(map[string]interface{})["pullPolicy"] = "Never"
|
||||
|
||||
fileToWorkloads, _, errs := chart.GetWorkloads(values)
|
||||
fileToWorkloads, errs := chart.GetWorkloads(values)
|
||||
s.Len(errs, 0)
|
||||
|
||||
s.Lenf(fileToWorkloads, len(s.expectedFiles), "Expected %d files", len(s.expectedFiles))
|
||||
@@ -111,7 +111,7 @@ func (s *HelmChartTestSuite) TestGetWorkloadsMissingValue() {
|
||||
values := chart.GetDefaultValues()
|
||||
delete(values, "image")
|
||||
|
||||
fileToWorkloads, _, errs := chart.GetWorkloads(values)
|
||||
fileToWorkloads, errs := chart.GetWorkloads(values)
|
||||
s.Nil(fileToWorkloads)
|
||||
s.Len(errs, 1, "Expected an error due to missing value")
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/k8s-interface/workloadinterface"
|
||||
"github.com/kubescape/opa-utils/objectsenvelopes/localworkload"
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/mikefarah/yq/v4/pkg/yqlib"
|
||||
"gopkg.in/op/go-logging.v1"
|
||||
)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package cautils
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
nethttp "net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
@@ -17,6 +17,40 @@ import (
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
)
|
||||
|
||||
var tmpDirPaths map[string]string
|
||||
|
||||
func hashRepoURL(repoURL string) string {
|
||||
h := sha256.New()
|
||||
h.Write([]byte(repoURL))
|
||||
return string(h.Sum(nil))
|
||||
}
|
||||
|
||||
func getDirPath(repoURL string) string {
|
||||
if tmpDirPaths == nil {
|
||||
return ""
|
||||
}
|
||||
return tmpDirPaths[hashRepoURL(repoURL)]
|
||||
}
|
||||
|
||||
// Create a temporary directory this function is called once
|
||||
func createTempDir(repoURL string) (string, error) {
|
||||
tmpDirPath := getDirPath(repoURL)
|
||||
if tmpDirPath != "" {
|
||||
return tmpDirPath, nil
|
||||
}
|
||||
// create temp directory
|
||||
tmpDir, err := os.MkdirTemp("", "")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create temporary directory: %w", err)
|
||||
}
|
||||
if tmpDirPaths == nil {
|
||||
tmpDirPaths = make(map[string]string)
|
||||
}
|
||||
tmpDirPaths[hashRepoURL(repoURL)] = tmpDir
|
||||
|
||||
return tmpDir, nil
|
||||
}
|
||||
|
||||
// To Check if the given repository is Public(No Authentication needed), send a HTTP GET request to the URL
|
||||
// If response code is 200, the repository is Public.
|
||||
func isGitRepoPublic(u string) bool {
|
||||
@@ -58,16 +92,21 @@ func getProviderError(gitURL giturl.IGitAPI) error {
|
||||
|
||||
// cloneRepo clones a repository to a local temporary directory and returns the directory
|
||||
func cloneRepo(gitURL giturl.IGitAPI) (string, error) {
|
||||
cloneURL := gitURL.GetHttpCloneURL()
|
||||
|
||||
// Check if directory exists
|
||||
if p := getDirPath(cloneURL); p != "" {
|
||||
// directory exists, meaning this repo was cloned
|
||||
return p, nil
|
||||
}
|
||||
// Get the URL to clone
|
||||
|
||||
// Create temp directory
|
||||
tmpDir, err := os.MkdirTemp("", "")
|
||||
tmpDir, err := createTempDir(cloneURL)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create temporary directory: %w", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Get the URL to clone
|
||||
cloneURL := gitURL.GetHttpCloneURL()
|
||||
|
||||
isGitTokenPresent := isGitTokenPresent(gitURL)
|
||||
|
||||
// Declare the authentication variable required for cloneOptions
|
||||
@@ -104,6 +143,8 @@ func cloneRepo(gitURL giturl.IGitAPI) (string, error) {
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to clone %s. %w", gitURL.GetRepoName(), err)
|
||||
}
|
||||
// tmpDir = filepath.Join(tmpDir, gitURL.GetRepoName())
|
||||
tmpDirPaths[hashRepoURL(cloneURL)] = tmpDir
|
||||
|
||||
return tmpDir, nil
|
||||
}
|
||||
@@ -125,9 +166,19 @@ func CloneGitRepo(path *string) (string, error) {
|
||||
logger.L().StopError("failed to clone git repo", helpers.String("url", gitURL.GetURL().String()), helpers.Error(err))
|
||||
return "", fmt.Errorf("failed to clone git repo '%s', %w", gitURL.GetURL().String(), err)
|
||||
}
|
||||
*path = clonedDir
|
||||
|
||||
*path = filepath.Join(clonedDir, gitURL.GetPath())
|
||||
logger.L().StopSuccess("Done accessing local objects")
|
||||
logger.L().StopSuccess("Done accessing remote repo")
|
||||
|
||||
return clonedDir, nil
|
||||
}
|
||||
|
||||
func GetClonedPath(path string) string {
|
||||
|
||||
gitURL, err := giturl.NewGitAPI(path)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return getDirPath(gitURL.GetHttpCloneURL())
|
||||
}
|
||||
|
||||
@@ -93,3 +93,63 @@ func TestCloneRepo(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
func TestGetClonedPath(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
path string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "Valid Git URL",
|
||||
path: "https://github.com/kubescape/kubescape.git",
|
||||
expected: "/path/to/cloned/repo", // replace with the expected path
|
||||
},
|
||||
{
|
||||
name: "Invalid Git URL",
|
||||
path: "invalid",
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
tmpDirPaths = make(map[string]string)
|
||||
tmpDirPaths[hashRepoURL("https://github.com/kubescape/kubescape.git")] = "/path/to/cloned/repo" // replace with the actual path
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := GetClonedPath(tc.path)
|
||||
if result != tc.expected {
|
||||
t.Errorf("Expected %q, got %q", tc.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
func TestGetDirPath(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
repoURL string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "Existing Repo URL",
|
||||
repoURL: "https://github.com/user/repo.git",
|
||||
expected: "/path/to/cloned/repo", // replace with the expected path
|
||||
},
|
||||
{
|
||||
name: "Non-Existing Repo URL",
|
||||
repoURL: "https://github.com/user/nonexistentrepo.git",
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
// Initialize tmpDirPaths
|
||||
tmpDirPaths = make(map[string]string)
|
||||
tmpDirPaths[hashRepoURL("https://github.com/user/repo.git")] = "/path/to/cloned/repo" // replace with the actual path
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := getDirPath(tc.repoURL)
|
||||
if result != tc.expected {
|
||||
t.Errorf("Expected %q, got %q", tc.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,10 +25,11 @@ import (
|
||||
type ScanningContext string
|
||||
|
||||
const (
|
||||
ContextCluster ScanningContext = "cluster"
|
||||
ContextFile ScanningContext = "single-file"
|
||||
ContextDir ScanningContext = "local-dir"
|
||||
ContextGitLocal ScanningContext = "git-local"
|
||||
ContextCluster ScanningContext = "cluster"
|
||||
ContextFile ScanningContext = "single-file"
|
||||
ContextDir ScanningContext = "local-dir"
|
||||
ContextGitLocal ScanningContext = "git-local"
|
||||
ContextGitRemote ScanningContext = "git-remote"
|
||||
)
|
||||
|
||||
const ( // deprecated
|
||||
@@ -281,6 +282,9 @@ func scanInfoToScanMetadata(ctx context.Context, scanInfo *ScanInfo) *reporthand
|
||||
case ContextGitLocal:
|
||||
// local-git
|
||||
metadata.ScanMetadata.ScanningTarget = reporthandlingv2.GitLocal
|
||||
case ContextGitRemote:
|
||||
// remote
|
||||
metadata.ScanMetadata.ScanningTarget = reporthandlingv2.Repo
|
||||
case ContextDir:
|
||||
// directory
|
||||
metadata.ScanMetadata.ScanningTarget = reporthandlingv2.Directory
|
||||
@@ -308,6 +312,7 @@ func (scanInfo *ScanInfo) GetScanningContext() ScanningContext {
|
||||
}
|
||||
|
||||
// getScanningContext get scanning context from the input param
|
||||
// this function should be called only once. Call GetScanningContext() to get the scanning context
|
||||
func (scanInfo *ScanInfo) getScanningContext(input string) ScanningContext {
|
||||
// cluster
|
||||
if input == "" {
|
||||
@@ -321,7 +326,7 @@ func (scanInfo *ScanInfo) getScanningContext(input string) ScanningContext {
|
||||
scanInfo.cleanups = append(scanInfo.cleanups, func() {
|
||||
_ = os.RemoveAll(repo)
|
||||
})
|
||||
return ContextGitLocal
|
||||
return ContextGitRemote
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -389,6 +394,13 @@ func (scanInfo *ScanInfo) setContextMetadata(ctx context.Context, contextMetadat
|
||||
logger.L().Ctx(ctx).Warning("in setContextMetadata", helpers.Interface("case", ContextGitLocal), helpers.Error(err))
|
||||
}
|
||||
contextMetadata.RepoContextMetadata = repoContext
|
||||
case ContextGitRemote:
|
||||
// remote
|
||||
repoContext, err := metadataGitLocal(GetClonedPath(input))
|
||||
if err != nil {
|
||||
logger.L().Ctx(ctx).Warning("in setContextMetadata", helpers.Interface("case", ContextGitRemote), helpers.Error(err))
|
||||
}
|
||||
contextMetadata.RepoContextMetadata = repoContext
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ func TestGetScanningContext(t *testing.T) {
|
||||
{
|
||||
name: "git URL input",
|
||||
input: "https://github.com/kubescape/http-request",
|
||||
want: ContextGitLocal,
|
||||
want: ContextGitRemote,
|
||||
},
|
||||
{
|
||||
name: "local git input",
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils/getter"
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
|
||||
|
||||
"github.com/kubescape/kubescape/v3/core/pkg/fixhandler"
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/grype/grype/presenter/models"
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
ksmetav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
|
||||
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling"
|
||||
|
||||
@@ -5,10 +5,11 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/anchore/grype/grype/presenter"
|
||||
"github.com/anchore/grype/grype/presenter/models"
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
@@ -17,8 +18,13 @@ import (
|
||||
"github.com/kubescape/kubescape/v3/pkg/imagescan"
|
||||
|
||||
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling"
|
||||
copa "github.com/project-copacetic/copacetic/pkg/patch"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
copaGrype "github.com/anubhav06/copa-grype/grype"
|
||||
"github.com/project-copacetic/copacetic/pkg/buildkit"
|
||||
"github.com/project-copacetic/copacetic/pkg/pkgmgr"
|
||||
"github.com/project-copacetic/copacetic/pkg/types/unversioned"
|
||||
"github.com/project-copacetic/copacetic/pkg/utils"
|
||||
)
|
||||
|
||||
func (ks *Kubescape) Patch(ctx context.Context, patchInfo *ksmetav1.PatchInfo, scanInfo *cautils.ScanInfo) (*models.PresenterConfig, error) {
|
||||
@@ -38,6 +44,11 @@ func (ks *Kubescape) Patch(ctx context.Context, patchInfo *ksmetav1.PatchInfo, s
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the scan results ID is empty, set it to "grype"
|
||||
if scanResults.ID.Name == "" {
|
||||
scanResults.ID.Name = "grype"
|
||||
}
|
||||
// Save the scan results to a file in json format
|
||||
pres := presenter.GetPresenter("json", "", false, *scanResults)
|
||||
|
||||
@@ -46,27 +57,27 @@ func (ks *Kubescape) Patch(ctx context.Context, patchInfo *ksmetav1.PatchInfo, s
|
||||
|
||||
writer := printer.GetWriter(ctx, fileName)
|
||||
|
||||
if err := pres.Present(writer); err != nil {
|
||||
if err = pres.Present(writer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger.L().StopSuccess(fmt.Sprintf("Successfully scanned image: %s", patchInfo.Image))
|
||||
|
||||
// ===================== Patch the image using copacetic =====================
|
||||
logger.L().Start("Patching image...")
|
||||
patchedImageName := fmt.Sprintf("%s:%s", patchInfo.ImageName, patchInfo.PatchedImageTag)
|
||||
|
||||
sout, serr := os.Stdout, os.Stderr
|
||||
if logger.L().GetLevel() != "debug" {
|
||||
disableCopaLogger()
|
||||
}
|
||||
|
||||
if err := copa.Patch(ctx, patchInfo.Timeout, patchInfo.BuildkitAddress, patchInfo.Image, fileName, patchInfo.PatchedImageTag, ""); err != nil {
|
||||
if err = copaPatch(ctx, patchInfo.Timeout, patchInfo.BuildkitAddress, patchInfo.Image, fileName, patchedImageName, "", patchInfo.IgnoreError, patchInfo.BuildKitOpts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Restore the output streams
|
||||
os.Stdout, os.Stderr = sout, serr
|
||||
|
||||
patchedImageName := fmt.Sprintf("%s:%s", patchInfo.ImageName, patchInfo.PatchedImageTag)
|
||||
logger.L().StopSuccess(fmt.Sprintf("Patched image successfully. Loaded image: %s", patchedImageName))
|
||||
|
||||
// ===================== Re-scan the image =====================
|
||||
@@ -97,7 +108,6 @@ func (ks *Kubescape) Patch(ctx context.Context, patchInfo *ksmetav1.PatchInfo, s
|
||||
Image: patchedImageName,
|
||||
},
|
||||
}
|
||||
resultsHandler.HandleResults(ctx)
|
||||
|
||||
return scanResultsPatched, resultsHandler.HandleResults(ctx)
|
||||
}
|
||||
@@ -107,3 +117,110 @@ func disableCopaLogger() {
|
||||
null, _ := os.Open(os.DevNull)
|
||||
log.SetOutput(null)
|
||||
}
|
||||
|
||||
// copaPatch is a slightly modified copy of the Patch function from the original "project-copacetic/copacetic" repo
|
||||
// https://github.com/project-copacetic/copacetic/blob/main/pkg/patch/patch.go
|
||||
func copaPatch(ctx context.Context, timeout time.Duration, buildkitAddr, image, reportFile, patchedImageName, workingFolder string, ignoreError bool, bkOpts buildkit.Opts) error {
|
||||
timeoutCtx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
ch := make(chan error)
|
||||
go func() {
|
||||
ch <- patchWithContext(timeoutCtx, buildkitAddr, image, reportFile, patchedImageName, workingFolder, ignoreError, bkOpts)
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-ch:
|
||||
return err
|
||||
case <-timeoutCtx.Done():
|
||||
// add a grace period for long running deferred cleanup functions to complete
|
||||
<-time.After(1 * time.Second)
|
||||
|
||||
err := fmt.Errorf("patch exceeded timeout %v", timeout)
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func patchWithContext(ctx context.Context, buildkitAddr, image, reportFile, patchedImageName, workingFolder string, ignoreError bool, bkOpts buildkit.Opts) error {
|
||||
// Ensure working folder exists for call to InstallUpdates
|
||||
if workingFolder == "" {
|
||||
var err error
|
||||
workingFolder, err = os.MkdirTemp("", "copa-*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(workingFolder)
|
||||
if err := os.Chmod(workingFolder, 0o744); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if isNew, err := utils.EnsurePath(workingFolder, 0o744); err != nil {
|
||||
log.Errorf("failed to create workingFolder %s", workingFolder)
|
||||
return err
|
||||
} else if isNew {
|
||||
defer os.RemoveAll(workingFolder)
|
||||
}
|
||||
}
|
||||
|
||||
// Parse report for update packages
|
||||
updates, err := tryParseScanReport(reportFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := buildkit.NewClient(ctx, bkOpts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
// Configure buildctl/client for use by package manager
|
||||
config, err := buildkit.InitializeBuildkitConfig(ctx, client, image, updates)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create package manager helper
|
||||
pkgmgr, err := pkgmgr.GetPackageManager(updates.Metadata.OS.Type, config, workingFolder)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Export the patched image state to Docker
|
||||
patchedImageState, _, err := pkgmgr.InstallUpdates(ctx, updates, ignoreError)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = buildkit.SolveToDocker(ctx, config.Client, patchedImageState, config.ConfigData, patchedImageName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// This function adds support to copa for patching Kubescape produced results
|
||||
func tryParseScanReport(file string) (*unversioned.UpdateManifest, error) {
|
||||
|
||||
parser := copaGrype.GrypeParser{}
|
||||
manifest, err := parser.Parse(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert from v1alpha1 to unversioned.UpdateManifest
|
||||
var um unversioned.UpdateManifest
|
||||
um.Metadata.OS.Type = manifest.Metadata.OS.Type
|
||||
um.Metadata.OS.Version = manifest.Metadata.OS.Version
|
||||
um.Metadata.Config.Arch = manifest.Metadata.Config.Arch
|
||||
um.Updates = make([]unversioned.UpdatePackage, len(manifest.Updates))
|
||||
for i, update := range manifest.Updates {
|
||||
um.Updates[i].Name = update.Name
|
||||
um.Updates[i].InstalledVersion = update.InstalledVersion
|
||||
um.Updates[i].FixedVersion = update.FixedVersion
|
||||
um.Updates[i].VulnerabilityID = update.VulnerabilityID
|
||||
}
|
||||
|
||||
return &um, nil
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*res
|
||||
|
||||
// ===================== resources =====================
|
||||
ctxResources, spanResources := otel.Tracer("").Start(ctxInit, "resources")
|
||||
err = resourcehandler.CollectResources(ctxResources, interfaces.resourceHandler, scanInfo.PolicyIdentifier, scanData, cautils.NewProgressHandler(""), scanInfo)
|
||||
err = resourcehandler.CollectResources(ctxResources, interfaces.resourceHandler, scanData, scanInfo)
|
||||
if err != nil {
|
||||
spanInit.End()
|
||||
return resultsHandling, err
|
||||
@@ -182,7 +182,7 @@ func (ks *Kubescape) Scan(ctx context.Context, scanInfo *cautils.ScanInfo) (*res
|
||||
defer spanOpa.End()
|
||||
|
||||
deps := resources.NewRegoDependenciesData(k8sinterface.GetK8sConfig(), interfaces.tenantConfig.GetContextName())
|
||||
reportResults := opaprocessor.NewOPAProcessor(scanData, deps, interfaces.tenantConfig.GetContextName())
|
||||
reportResults := opaprocessor.NewOPAProcessor(scanData, deps, interfaces.tenantConfig.GetContextName(), scanInfo.ExcludedNamespaces, scanInfo.IncludeNamespaces)
|
||||
if err = reportResults.ProcessRulesListener(ctxOpa, cautils.NewProgressHandler("")); err != nil {
|
||||
// TODO - do something
|
||||
return resultsHandling, fmt.Errorf("%w", err)
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
package v1
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/project-copacetic/copacetic/pkg/buildkit"
|
||||
)
|
||||
|
||||
type PatchInfo struct {
|
||||
Image string // image to be patched
|
||||
PatchedImageTag string // can be empty, if empty then the image tag will be patched with the latest tag
|
||||
BuildkitAddress string // buildkit address
|
||||
Timeout time.Duration // timeout for patching an image
|
||||
IgnoreError bool // ignore errors and continue patching
|
||||
BuildKitOpts buildkit.Opts //build kit options
|
||||
|
||||
// Image registry credentials
|
||||
Username string // username for registry login
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/armosec/armoapi-go/armotypes"
|
||||
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/opa-utils/objectsenvelopes"
|
||||
"github.com/kubescape/opa-utils/objectsenvelopes/localworkload"
|
||||
"github.com/kubescape/opa-utils/reporthandling"
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/armosec/armoapi-go/armotypes"
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
metav1 "github.com/kubescape/kubescape/v3/core/meta/datastructures/v1"
|
||||
"github.com/kubescape/kubescape/v3/internal/testutils"
|
||||
reporthandlingv2 "github.com/kubescape/opa-utils/reporthandling/v2"
|
||||
|
||||
@@ -14,6 +14,6 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx_container
|
||||
image: nginx
|
||||
securityContext:
|
||||
runAsRoot: true
|
||||
image: nginx
|
||||
|
||||
@@ -14,6 +14,6 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx_container
|
||||
image: nginx
|
||||
securityContext:
|
||||
runAsRoot: true
|
||||
image: nginx
|
||||
|
||||
@@ -16,6 +16,6 @@ spec:
|
||||
containers:
|
||||
# These are the first containers comments
|
||||
- name: nginx_container
|
||||
image: nginx
|
||||
securityContext:
|
||||
runAsRoot: true
|
||||
image: nginx
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
containers:
|
||||
- name: nginx_container
|
||||
|
||||
image: nginx
|
||||
|
||||
securityContext:
|
||||
runAsRoot: true
|
||||
|
||||
image: nginx
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
containers:
|
||||
- name: nginx_container
|
||||
|
||||
|
||||
image: nginx
|
||||
securityContext:
|
||||
runAsRoot: false
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ spec:
|
||||
containers:
|
||||
- name: nginx_container
|
||||
|
||||
image: nginx
|
||||
|
||||
securityContext:
|
||||
runAsRoot: true
|
||||
|
||||
image: nginx
|
||||
|
||||
@@ -16,7 +16,7 @@ spec:
|
||||
containers:
|
||||
- name: nginx_container
|
||||
|
||||
|
||||
image: nginx
|
||||
securityContext:
|
||||
runAsRoot: false
|
||||
|
||||
|
||||
@@ -9,6 +9,6 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx_container
|
||||
image: nginx
|
||||
securityContext:
|
||||
runAsRoot: false
|
||||
runAsRoot: false
|
||||
image: nginx
|
||||
|
||||
@@ -9,4 +9,4 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx_container
|
||||
image: nginx
|
||||
image: nginx
|
||||
|
||||
@@ -12,4 +12,5 @@ spec:
|
||||
image: nginx
|
||||
|
||||
- name: container_with_security_issues
|
||||
image: image_with_security_issues
|
||||
image: image_with_security_issues
|
||||
restartPolicy: Always
|
||||
|
||||
@@ -10,3 +10,5 @@ spec:
|
||||
containers:
|
||||
- name: nginx_container
|
||||
image: nginx
|
||||
|
||||
restartPolicy: Always
|
||||
|
||||
@@ -8,7 +8,7 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx1
|
||||
image: nginx
|
||||
securityContext:
|
||||
capabilities:
|
||||
drop: ["NET_RAW", "SYS_ADM"]
|
||||
drop: ["NET_RAW", "SYS_ADM"]
|
||||
image: nginx
|
||||
|
||||
@@ -8,7 +8,7 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx1
|
||||
image: nginx
|
||||
securityContext:
|
||||
capabilities:
|
||||
drop: ["NET_RAW"]
|
||||
drop: ["NET_RAW"]
|
||||
image: nginx
|
||||
|
||||
@@ -9,9 +9,9 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx_container
|
||||
image: nginx
|
||||
securityContext:
|
||||
runAsRoot: false
|
||||
image: nginx
|
||||
|
||||
---
|
||||
|
||||
@@ -29,4 +29,5 @@ spec:
|
||||
image: nginx
|
||||
|
||||
- name: container_with_security_issues
|
||||
image: image_with_security_issues
|
||||
image: image_with_security_issues
|
||||
restartPolicy: Always
|
||||
|
||||
@@ -25,3 +25,5 @@ spec:
|
||||
containers:
|
||||
- name: nginx_container
|
||||
image: nginx
|
||||
|
||||
restartPolicy: Always
|
||||
|
||||
@@ -9,6 +9,6 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx_container
|
||||
image: nginx
|
||||
securityContext:
|
||||
runAsRoot: true
|
||||
runAsRoot: true
|
||||
image: nginx
|
||||
|
||||
@@ -9,6 +9,6 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx_container
|
||||
image: nginx
|
||||
securityContext:
|
||||
runAsRoot: false
|
||||
runAsRoot: false
|
||||
image: nginx
|
||||
|
||||
@@ -10,9 +10,9 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx1
|
||||
image: nginx
|
||||
securityContext:
|
||||
capabilities:
|
||||
drop:
|
||||
- "NET_RAW"
|
||||
add: ["SYS_ADM"]
|
||||
add: ["SYS_ADM"]
|
||||
image: nginx
|
||||
|
||||
@@ -10,9 +10,9 @@ metadata:
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx1
|
||||
image: nginx
|
||||
securityContext:
|
||||
capabilities:
|
||||
drop:
|
||||
- "SYS_ADM"
|
||||
add: ["NET_RAW"]
|
||||
add: ["NET_RAW"]
|
||||
image: nginx
|
||||
|
||||
@@ -182,7 +182,7 @@ func addLinesToRemove(ctx context.Context, fixInfoMetadata *fixInfoMetadata) (in
|
||||
newOriginalListTracker := updateTracker(fixInfoMetadata.originalList, fixInfoMetadata.originalListTracker)
|
||||
*fixInfoMetadata.linesToRemove = append(*fixInfoMetadata.linesToRemove, linesToRemove{
|
||||
startLine: currentDFSNode.node.Line,
|
||||
endLine: getNodeLine(fixInfoMetadata.originalList, newOriginalListTracker),
|
||||
endLine: getNodeLine(fixInfoMetadata.originalList, newOriginalListTracker-1), // newOriginalListTracker is the next node
|
||||
})
|
||||
|
||||
return newOriginalListTracker, fixInfoMetadata.fixedListTracker
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/mikefarah/yq/v4/pkg/yqlib"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/k8s-interface/k8sinterface"
|
||||
"github.com/kubescape/k8s-interface/workloadinterface"
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/k8s-interface/k8sinterface"
|
||||
"github.com/kubescape/opa-utils/objectsenvelopes/hostsensor"
|
||||
"github.com/kubescape/opa-utils/reporthandling/apis"
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/opa-utils/objectsenvelopes/hostsensor"
|
||||
)
|
||||
|
||||
@@ -3,10 +3,11 @@ package opaprocessor
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/armosec/armoapi-go/armotypes"
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/k8s-interface/workloadinterface"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
@@ -37,10 +38,12 @@ type OPAProcessor struct {
|
||||
clusterName string
|
||||
regoDependenciesData *resources.RegoDependenciesData
|
||||
*cautils.OPASessionObj
|
||||
opaRegisterOnce sync.Once
|
||||
opaRegisterOnce sync.Once
|
||||
excludeNamespaces []string
|
||||
includeNamespaces []string
|
||||
}
|
||||
|
||||
func NewOPAProcessor(sessionObj *cautils.OPASessionObj, regoDependenciesData *resources.RegoDependenciesData, clusterName string) *OPAProcessor {
|
||||
func NewOPAProcessor(sessionObj *cautils.OPASessionObj, regoDependenciesData *resources.RegoDependenciesData, clusterName string, excludeNamespaces string, includeNamespaces string) *OPAProcessor {
|
||||
if regoDependenciesData != nil && sessionObj != nil {
|
||||
regoDependenciesData.PostureControlInputs = sessionObj.RegoInputData.PostureControlInputs
|
||||
regoDependenciesData.DataControlInputs = sessionObj.RegoInputData.DataControlInputs
|
||||
@@ -50,6 +53,8 @@ func NewOPAProcessor(sessionObj *cautils.OPASessionObj, regoDependenciesData *re
|
||||
OPASessionObj: sessionObj,
|
||||
regoDependenciesData: regoDependenciesData,
|
||||
clusterName: clusterName,
|
||||
excludeNamespaces: split(excludeNamespaces),
|
||||
includeNamespaces: split(includeNamespaces),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,6 +216,9 @@ func (opap *OPAProcessor) processRule(ctx context.Context, rule *reporthandling.
|
||||
inputResources = objectsenvelopes.ListMapToMeta(enumeratedData)
|
||||
|
||||
for i, inputResource := range inputResources {
|
||||
if opap.skipNamespace(inputResource.GetNamespace()) {
|
||||
continue
|
||||
}
|
||||
resources[inputResource.GetID()] = &resourcesresults.ResourceAssociatedRule{
|
||||
Name: rule.Name,
|
||||
ControlConfigurations: ruleRegoDependenciesData.PostureControlInputs,
|
||||
@@ -229,6 +237,9 @@ func (opap *OPAProcessor) processRule(ctx context.Context, rule *reporthandling.
|
||||
for _, ruleResponse := range ruleResponses {
|
||||
failedResources := objectsenvelopes.ListMapToMeta(ruleResponse.GetFailedResources())
|
||||
for _, failedResource := range failedResources {
|
||||
if opap.skipNamespace(failedResource.GetNamespace()) {
|
||||
continue
|
||||
}
|
||||
var ruleResult *resourcesresults.ResourceAssociatedRule
|
||||
if r, found := resources[failedResource.GetID()]; found {
|
||||
ruleResult = r
|
||||
@@ -387,3 +398,25 @@ func (opap *OPAProcessor) makeRegoDeps(configInputs []reporthandling.ControlConf
|
||||
PostureControlInputs: postureControlInputs,
|
||||
}
|
||||
}
|
||||
|
||||
func (opap *OPAProcessor) skipNamespace(ns string) bool {
|
||||
if includeNamespaces := opap.includeNamespaces; len(includeNamespaces) > 0 {
|
||||
if !slices.Contains(includeNamespaces, ns) {
|
||||
// skip ns not in IncludeNamespaces
|
||||
return true
|
||||
}
|
||||
} else if excludeNamespaces := opap.excludeNamespaces; len(excludeNamespaces) > 0 {
|
||||
if slices.Contains(excludeNamespaces, ns) {
|
||||
// skip ns in ExcludeNamespaces
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func split(namespaces string) []string {
|
||||
if namespaces == "" {
|
||||
return nil
|
||||
}
|
||||
return strings.Split(namespaces, ",")
|
||||
}
|
||||
|
||||
@@ -197,7 +197,7 @@ func TestProcessResourcesResult(t *testing.T) {
|
||||
opaSessionObj.K8SResources = k8sResources
|
||||
opaSessionObj.AllResources[deployment.GetID()] = deployment
|
||||
|
||||
opap := NewOPAProcessor(opaSessionObj, resources.NewRegoDependenciesDataMock(), "test")
|
||||
opap := NewOPAProcessor(opaSessionObj, resources.NewRegoDependenciesDataMock(), "test", "", "")
|
||||
opap.AllPolicies = policies
|
||||
opap.Process(context.TODO(), policies, nil)
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@ package opaprocessor
|
||||
import (
|
||||
"context"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/k8s-interface/k8sinterface"
|
||||
"github.com/kubescape/k8s-interface/workloadinterface"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
@@ -97,7 +99,7 @@ func isEmptyResources(counters reportsummary.ICounters) bool {
|
||||
|
||||
func getAllSupportedObjects(k8sResources cautils.K8SResources, externalResources cautils.ExternalResources, allResources map[string]workloadinterface.IMetadata, rule *reporthandling.PolicyRule) map[string][]workloadinterface.IMetadata {
|
||||
k8sObjects := getKubernetesObjects(k8sResources, allResources, rule.Match)
|
||||
externalObjs := getKubenetesObjectsFromExternalResources(externalResources, allResources, rule.DynamicMatch)
|
||||
externalObjs := getKubernetesObjectsFromExternalResources(externalResources, allResources, rule.DynamicMatch)
|
||||
if len(externalObjs) > 0 {
|
||||
l, ok := k8sObjects[clusterScope]
|
||||
if !ok {
|
||||
@@ -109,7 +111,7 @@ func getAllSupportedObjects(k8sResources cautils.K8SResources, externalResources
|
||||
return k8sObjects
|
||||
}
|
||||
|
||||
func getKubenetesObjectsFromExternalResources(externalResources cautils.ExternalResources, allResources map[string]workloadinterface.IMetadata, match []reporthandling.RuleMatchObjects) []workloadinterface.IMetadata {
|
||||
func getKubernetesObjectsFromExternalResources(externalResources cautils.ExternalResources, allResources map[string]workloadinterface.IMetadata, match []reporthandling.RuleMatchObjects) []workloadinterface.IMetadata {
|
||||
k8sObjects := []workloadinterface.IMetadata{}
|
||||
|
||||
for m := range match {
|
||||
@@ -215,16 +217,39 @@ func removePodData(workload workloadinterface.IWorkload) {
|
||||
workloadinterface.RemoveFromMap(workload.GetObject(), "metadata", "managedFields")
|
||||
workloadinterface.RemoveFromMap(workload.GetObject(), "status")
|
||||
|
||||
containers, err := workload.GetContainers()
|
||||
if err != nil || len(containers) == 0 {
|
||||
return
|
||||
// containers
|
||||
if containers, err := workload.GetContainers(); err == nil && len(containers) > 0 {
|
||||
removeContainersData(containers)
|
||||
workloadinterface.SetInMap(workload.GetObject(), workloadinterface.PodSpec(workload.GetKind()), "containers", containers)
|
||||
}
|
||||
|
||||
// init containers
|
||||
|
||||
if initContainers, err := workload.GetInitContainers(); err == nil && len(initContainers) > 0 {
|
||||
removeContainersData(initContainers)
|
||||
workloadinterface.SetInMap(workload.GetObject(), workloadinterface.PodSpec(workload.GetKind()), "initContainers", initContainers)
|
||||
}
|
||||
|
||||
// ephemeral containers
|
||||
if ephemeralContainers, err := workload.GetEphemeralContainers(); err == nil && len(ephemeralContainers) > 0 {
|
||||
removeEphemeralContainersData(ephemeralContainers)
|
||||
workloadinterface.SetInMap(workload.GetObject(), workloadinterface.PodSpec(workload.GetKind()), "ephemeralContainers", ephemeralContainers)
|
||||
}
|
||||
}
|
||||
|
||||
func removeContainersData(containers []corev1.Container) {
|
||||
for i := range containers {
|
||||
for j := range containers[i].Env {
|
||||
containers[i].Env[j].Value = "XXXXXX"
|
||||
}
|
||||
}
|
||||
}
|
||||
func removeEphemeralContainersData(containers []corev1.EphemeralContainer) {
|
||||
for i := range containers {
|
||||
for j := range containers[i].Env {
|
||||
containers[i].Env[j].Value = "XXXXXX"
|
||||
}
|
||||
}
|
||||
workloadinterface.SetInMap(workload.GetObject(), workloadinterface.PodSpec(workload.GetKind()), "containers", containers)
|
||||
}
|
||||
|
||||
func ruleData(rule *reporthandling.PolicyRule) string {
|
||||
|
||||
@@ -3,21 +3,152 @@ package opaprocessor
|
||||
import (
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/kubescape/k8s-interface/workloadinterface"
|
||||
)
|
||||
|
||||
func TestRemoveData(t *testing.T) {
|
||||
type args struct {
|
||||
w string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
}{
|
||||
{
|
||||
name: "remove data",
|
||||
args: args{
|
||||
w: `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"demoservice-server", "annotations": {"name": "kubectl.kubernetes.io/last-applied-configuration", "value": "blabla"}},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"demoservice-server"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}}}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove data with init containers and ephemeral containers",
|
||||
args: args{
|
||||
w: `{"apiVersion": "v1", "kind": "Pod", "metadata": {"name": "example-pod", "namespace": "default"}, "spec": {"containers": [{"name": "container1", "image": "nginx", "ports": [{"containerPort": 80}], "env": [{"name": "CONTAINER_ENV", "value": "container_value"}]}], "initContainers": [{"name": "init-container1", "image": "busybox", "command": ["sh", "-c", "echo 'Init Container'"], "env": [{"name": "INIT_CONTAINER_ENV", "value": "init_container_value"}]}], "ephemeralContainers": [{"name": "debug-container", "image": "busybox", "command": ["sh", "-c", "echo 'Ephemeral Container'"], "targetContainerName": "container1", "env": [{"name": "EPHEMERAL_CONTAINER_ENV", "value": "ephemeral_container_value"}]}]}}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove secret data",
|
||||
args: args{
|
||||
w: `{"apiVersion": "v1", "kind": "Secret", "metadata": {"name": "example-secret", "namespace": "default", "annotations": {"kubectl.kubernetes.io/last-applied-configuration": "{}"}}, "type": "Opaque", "data": {"username": "dXNlcm5hbWU=", "password": "cGFzc3dvcmQ="}}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove configMap data",
|
||||
args: args{
|
||||
w: `{"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "example-configmap", "namespace": "default", "annotations": {"kubectl.kubernetes.io/last-applied-configuration": "{}"}}, "data": {"exampleKey": "exampleValue"}}`,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
obj, _ := workloadinterface.NewWorkload([]byte(tt.args.w))
|
||||
removeData(obj)
|
||||
|
||||
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"demoservice-server"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"demoservice-server"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}}}`
|
||||
obj, _ := workloadinterface.NewWorkload([]byte(w))
|
||||
removeData(obj)
|
||||
workload := workloadinterface.NewWorkloadObj(obj.GetObject())
|
||||
|
||||
workload := workloadinterface.NewWorkloadObj(obj.GetObject())
|
||||
c, _ := workload.GetContainers()
|
||||
for i := range c {
|
||||
for _, e := range c[i].Env {
|
||||
_, found := workload.GetAnnotation("kubectl.kubernetes.io/last-applied-configuration")
|
||||
assert.False(t, found)
|
||||
|
||||
_, found = workloadinterface.InspectMap(workload.GetObject(), "metadata", "managedFields")
|
||||
assert.False(t, found)
|
||||
|
||||
_, found = workloadinterface.InspectMap(workload.GetObject(), "status")
|
||||
assert.False(t, found)
|
||||
|
||||
if d, ok := workloadinterface.InspectMap(workload.GetObject(), "data"); ok {
|
||||
data, ok := d.(map[string]interface{})
|
||||
assert.True(t, ok)
|
||||
for key := range data {
|
||||
assert.Equal(t, "XXXXXX", data[key])
|
||||
}
|
||||
}
|
||||
|
||||
if c, _ := workload.GetContainers(); c != nil {
|
||||
for i := range c {
|
||||
for _, e := range c[i].Env {
|
||||
assert.Equal(t, "XXXXXX", e.Value, e.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ic, _ := workload.GetInitContainers(); ic != nil {
|
||||
for i := range ic {
|
||||
for _, e := range ic[i].Env {
|
||||
assert.Equal(t, "XXXXXX", e.Value, e.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ec, _ := workload.GetEphemeralContainers(); ec != nil {
|
||||
for i := range ec {
|
||||
for _, e := range ec[i].Env {
|
||||
assert.Equal(t, "XXXXXX", e.Value, e.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveContainersData(t *testing.T) {
|
||||
containers := []corev1.Container{
|
||||
{
|
||||
Env: []corev1.EnvVar{
|
||||
{
|
||||
Name: "TEST_ENV",
|
||||
Value: "test_value",
|
||||
},
|
||||
{
|
||||
Name: "ENV_2",
|
||||
Value: "bla",
|
||||
},
|
||||
{
|
||||
Name: "EMPTY_ENV",
|
||||
Value: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
removeContainersData(containers)
|
||||
|
||||
for _, c := range containers {
|
||||
for _, e := range c.Env {
|
||||
assert.Equal(t, "XXXXXX", e.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveEphemeralContainersData(t *testing.T) {
|
||||
containers := []corev1.EphemeralContainer{
|
||||
{
|
||||
EphemeralContainerCommon: corev1.EphemeralContainerCommon{
|
||||
Env: []corev1.EnvVar{
|
||||
{
|
||||
Name: "TEST_ENV",
|
||||
Value: "test_value",
|
||||
},
|
||||
{
|
||||
Name: "ENV_2",
|
||||
Value: "bla",
|
||||
},
|
||||
{
|
||||
Name: "EMPTY_ENV",
|
||||
Value: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
removeEphemeralContainersData(containers)
|
||||
|
||||
for _, c := range containers {
|
||||
for _, e := range c.Env {
|
||||
assert.Equal(t, "XXXXXX", e.Value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/opa-utils/reporthandling"
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/armoapi-go/armotypes"
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils/getter"
|
||||
|
||||
@@ -3,7 +3,6 @@ package resourcehandler
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -15,7 +14,6 @@ import (
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/k8s-interface/k8sinterface"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/pkg/opaprocessor"
|
||||
)
|
||||
|
||||
// FileResourceHandler handle resources from files and URLs
|
||||
@@ -26,7 +24,7 @@ func NewFileResourceHandler() *FileResourceHandler {
|
||||
return &FileResourceHandler{}
|
||||
}
|
||||
|
||||
func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessionObj *cautils.OPASessionObj, _ opaprocessor.IJobProgressNotificationClient, scanInfo *cautils.ScanInfo) (cautils.K8SResources, map[string]workloadinterface.IMetadata, cautils.ExternalResources, map[string]bool, error) {
|
||||
func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessionObj *cautils.OPASessionObj, scanInfo *cautils.ScanInfo) (cautils.K8SResources, map[string]workloadinterface.IMetadata, cautils.ExternalResources, map[string]bool, error) {
|
||||
allResources := map[string]workloadinterface.IMetadata{}
|
||||
externalResources := cautils.ExternalResources{}
|
||||
|
||||
@@ -41,16 +39,12 @@ func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessio
|
||||
for path := range scanInfo.InputPatterns {
|
||||
var workloadIDToSource map[string]reporthandling.Source
|
||||
var workloads []workloadinterface.IMetadata
|
||||
var workloadIDToMappingNodes map[string]cautils.MappingNodes
|
||||
var err error
|
||||
|
||||
if scanInfo.ChartPath != "" && scanInfo.FilePath != "" {
|
||||
workloadIDToSource, workloads, workloadIDToMappingNodes, err = getWorkloadFromHelmChart(ctx, scanInfo.ChartPath, scanInfo.FilePath)
|
||||
if err != nil {
|
||||
// We should probably ignore the error so we can continue scanning other charts
|
||||
}
|
||||
workloadIDToSource, workloads, _ = getWorkloadFromHelmChart(ctx, scanInfo.InputPatterns[path], scanInfo.ChartPath, scanInfo.FilePath)
|
||||
} else {
|
||||
workloadIDToSource, workloads, workloadIDToMappingNodes, err = getResourcesFromPath(ctx, scanInfo.InputPatterns[path])
|
||||
workloadIDToSource, workloads, err = getResourcesFromPath(ctx, scanInfo.InputPatterns[path])
|
||||
if err != nil {
|
||||
return nil, allResources, nil, nil, err
|
||||
}
|
||||
@@ -61,7 +55,7 @@ func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessio
|
||||
|
||||
for k, v := range workloadIDToSource {
|
||||
sessionObj.ResourceSource[k] = v
|
||||
sessionObj.TemplateMapping[k] = workloadIDToMappingNodes[k]
|
||||
sessionObj.TemplateMapping[k] = cautils.MappingNodes{}
|
||||
}
|
||||
|
||||
// map all resources: map["/apiVersion/version/kind"][]<k8s workloads>
|
||||
@@ -107,56 +101,45 @@ func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessio
|
||||
func (fileHandler *FileResourceHandler) GetCloudProvider() string {
|
||||
return ""
|
||||
}
|
||||
func getWorkloadFromHelmChart(ctx context.Context, helmPath, workloadPath string) (map[string]reporthandling.Source, []workloadinterface.IMetadata, map[string]cautils.MappingNodes, error) {
|
||||
clonedRepo, err := cautils.CloneGitRepo(&helmPath)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
func getWorkloadFromHelmChart(ctx context.Context, path, helmPath, workloadPath string) (map[string]reporthandling.Source, []workloadinterface.IMetadata, error) {
|
||||
clonedRepo := cautils.GetClonedPath(path)
|
||||
|
||||
if clonedRepo != "" {
|
||||
defer func(path string) {
|
||||
_ = os.RemoveAll(path)
|
||||
}(clonedRepo)
|
||||
// if the repo was cloned, add the workload path to the cloned repo
|
||||
workloadPath = filepath.Join(clonedRepo, workloadPath)
|
||||
} else {
|
||||
// if the repo was not cloned
|
||||
clonedRepo = path
|
||||
}
|
||||
|
||||
// Get repo root
|
||||
repoRoot, gitRepo := extractGitRepo(helmPath)
|
||||
repoRoot, gitRepo := extractGitRepo(clonedRepo)
|
||||
|
||||
helmSourceToWorkloads, helmSourceToChart, helmSourceToNodes := cautils.LoadResourcesFromHelmCharts(ctx, helmPath)
|
||||
|
||||
if clonedRepo != "" {
|
||||
workloadPath = clonedRepo + workloadPath
|
||||
}
|
||||
helmSourceToWorkloads, helmSourceToChart := cautils.LoadResourcesFromHelmCharts(ctx, helmPath)
|
||||
|
||||
wlSource, ok := helmSourceToWorkloads[workloadPath]
|
||||
if !ok {
|
||||
return nil, nil, nil, fmt.Errorf("workload %s not found in chart %s", workloadPath, helmPath)
|
||||
return nil, nil, fmt.Errorf("workload %s not found in chart %s", workloadPath, helmPath)
|
||||
}
|
||||
|
||||
if len(wlSource) != 1 {
|
||||
return nil, nil, nil, fmt.Errorf("workload %s found multiple times in chart %s", workloadPath, helmPath)
|
||||
return nil, nil, fmt.Errorf("workload %s found multiple times in chart %s", workloadPath, helmPath)
|
||||
}
|
||||
|
||||
helmChart, ok := helmSourceToChart[workloadPath]
|
||||
if !ok {
|
||||
return nil, nil, nil, fmt.Errorf("helmChart not found for workload %s", workloadPath)
|
||||
}
|
||||
|
||||
templatesNodes, ok := helmSourceToNodes[workloadPath]
|
||||
if !ok {
|
||||
return nil, nil, nil, fmt.Errorf("templatesNodes not found for workload %s", workloadPath)
|
||||
return nil, nil, fmt.Errorf("helmChart not found for workload %s", workloadPath)
|
||||
}
|
||||
|
||||
workloadSource := getWorkloadSourceHelmChart(repoRoot, helmPath, gitRepo, helmChart)
|
||||
|
||||
workloadIDToSource := make(map[string]reporthandling.Source, 1)
|
||||
workloadIDToNodes := make(map[string]cautils.MappingNodes, 1)
|
||||
workloadIDToSource[wlSource[0].GetID()] = workloadSource
|
||||
workloadIDToNodes[wlSource[0].GetID()] = templatesNodes
|
||||
|
||||
var workloads []workloadinterface.IMetadata
|
||||
workloads = append(workloads, wlSource...)
|
||||
|
||||
return workloadIDToSource, workloads, workloadIDToNodes, nil
|
||||
return workloadIDToSource, workloads, nil
|
||||
|
||||
}
|
||||
|
||||
@@ -190,19 +173,14 @@ func getWorkloadSourceHelmChart(repoRoot string, source string, gitRepo *cautils
|
||||
}
|
||||
}
|
||||
|
||||
func getResourcesFromPath(ctx context.Context, path string) (map[string]reporthandling.Source, []workloadinterface.IMetadata, map[string]cautils.MappingNodes, error) {
|
||||
func getResourcesFromPath(ctx context.Context, path string) (map[string]reporthandling.Source, []workloadinterface.IMetadata, error) {
|
||||
workloadIDToSource := make(map[string]reporthandling.Source)
|
||||
workloadIDToNodes := make(map[string]cautils.MappingNodes)
|
||||
var workloads []workloadinterface.IMetadata
|
||||
|
||||
clonedRepo, err := cautils.CloneGitRepo(&path)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
clonedRepo := cautils.GetClonedPath(path)
|
||||
if clonedRepo != "" {
|
||||
defer func(path string) {
|
||||
_ = os.RemoveAll(path)
|
||||
}(clonedRepo)
|
||||
// if the repo was cloned, add the workload path to the cloned repo
|
||||
path = clonedRepo
|
||||
}
|
||||
|
||||
// Get repo root
|
||||
@@ -283,14 +261,10 @@ func getResourcesFromPath(ctx context.Context, path string) (map[string]reportha
|
||||
}
|
||||
|
||||
// load resources from helm charts
|
||||
helmSourceToWorkloads, helmSourceToChart, helmSourceToNodes := cautils.LoadResourcesFromHelmCharts(ctx, path)
|
||||
helmSourceToWorkloads, helmSourceToChart := cautils.LoadResourcesFromHelmCharts(ctx, path)
|
||||
for source, ws := range helmSourceToWorkloads {
|
||||
workloads = append(workloads, ws...)
|
||||
helmChart := helmSourceToChart[source]
|
||||
var templatesNodes cautils.MappingNodes
|
||||
if nodes, ok := helmSourceToNodes[source]; ok {
|
||||
templatesNodes = nodes
|
||||
}
|
||||
|
||||
if clonedRepo != "" && gitRepo != nil {
|
||||
url, err := gitRepo.GetRemoteUrl()
|
||||
@@ -301,14 +275,12 @@ func getResourcesFromPath(ctx context.Context, path string) (map[string]reportha
|
||||
helmChart.Path = strings.TrimSuffix(url, ".git")
|
||||
repoRoot = ""
|
||||
source = strings.TrimPrefix(source, fmt.Sprintf("%s/", clonedRepo))
|
||||
templatesNodes.TemplateFileName = source
|
||||
}
|
||||
|
||||
workloadSource := getWorkloadSourceHelmChart(repoRoot, source, gitRepo, helmChart)
|
||||
|
||||
for i := range ws {
|
||||
workloadIDToSource[ws[i].GetID()] = workloadSource
|
||||
workloadIDToNodes[ws[i].GetID()] = templatesNodes
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +327,7 @@ func getResourcesFromPath(ctx context.Context, path string) (map[string]reportha
|
||||
}
|
||||
}
|
||||
|
||||
return workloadIDToSource, workloads, workloadIDToNodes, nil
|
||||
return workloadIDToSource, workloads, nil
|
||||
}
|
||||
|
||||
func extractGitRepo(path string) (string, *cautils.LocalGitRepository) {
|
||||
|
||||
@@ -100,12 +100,12 @@ func Test_CollectResources(t *testing.T) {
|
||||
}
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
CollectResources(context.TODO(), resourceHandler, []cautils.PolicyIdentifier{}, objSession, cautils.NewProgressHandler(""), &cautils.ScanInfo{})
|
||||
CollectResources(context.TODO(), resourceHandler, objSession, &cautils.ScanInfo{})
|
||||
}, "Cluster named .*eks.* without a cloud config panics on cluster scan !")
|
||||
|
||||
assert.NotPanics(t, func() {
|
||||
objSession.Metadata.ScanMetadata.ScanningTarget = reportv2.File
|
||||
CollectResources(context.TODO(), resourceHandler, []cautils.PolicyIdentifier{}, objSession, cautils.NewProgressHandler(""), &cautils.ScanInfo{})
|
||||
CollectResources(context.TODO(), resourceHandler, objSession, &cautils.ScanInfo{})
|
||||
}, "Cluster named .*eks.* without a cloud config panics on non-cluster scan !")
|
||||
|
||||
}
|
||||
|
||||
@@ -4,19 +4,18 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
cloudsupportv1 "github.com/kubescape/k8s-interface/cloudsupport/v1"
|
||||
"github.com/kubescape/k8s-interface/k8sinterface"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/pkg/opaprocessor"
|
||||
"github.com/kubescape/opa-utils/reporthandling/apis"
|
||||
helpersv1 "github.com/kubescape/opa-utils/reporthandling/helpers/v1"
|
||||
reportv2 "github.com/kubescape/opa-utils/reporthandling/v2"
|
||||
"go.opentelemetry.io/otel"
|
||||
)
|
||||
|
||||
func CollectResources(ctx context.Context, rsrcHandler IResourceHandler, policyIdentifier []cautils.PolicyIdentifier, opaSessionObj *cautils.OPASessionObj, progressListener opaprocessor.IJobProgressNotificationClient, scanInfo *cautils.ScanInfo) error {
|
||||
func CollectResources(ctx context.Context, rsrcHandler IResourceHandler, opaSessionObj *cautils.OPASessionObj, scanInfo *cautils.ScanInfo) error {
|
||||
ctx, span := otel.Tracer("").Start(ctx, "resourcehandler.CollectResources")
|
||||
defer span.End()
|
||||
opaSessionObj.Report.ClusterAPIServerInfo = rsrcHandler.GetClusterAPIServerInfo(ctx)
|
||||
@@ -26,7 +25,7 @@ func CollectResources(ctx context.Context, rsrcHandler IResourceHandler, policyI
|
||||
setCloudMetadata(opaSessionObj, rsrcHandler.GetCloudProvider())
|
||||
}
|
||||
|
||||
resourcesMap, allResources, externalResources, excludedRulesMap, err := rsrcHandler.GetResources(ctx, opaSessionObj, progressListener, scanInfo)
|
||||
resourcesMap, allResources, externalResources, excludedRulesMap, err := rsrcHandler.GetResources(ctx, opaSessionObj, scanInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -5,12 +5,11 @@ import (
|
||||
|
||||
"github.com/kubescape/k8s-interface/workloadinterface"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/pkg/opaprocessor"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
)
|
||||
|
||||
type IResourceHandler interface {
|
||||
GetResources(context.Context, *cautils.OPASessionObj, opaprocessor.IJobProgressNotificationClient, *cautils.ScanInfo) (cautils.K8SResources, map[string]workloadinterface.IMetadata, cautils.ExternalResources, map[string]bool, error)
|
||||
GetResources(context.Context, *cautils.OPASessionObj, *cautils.ScanInfo) (cautils.K8SResources, map[string]workloadinterface.IMetadata, cautils.ExternalResources, map[string]bool, error)
|
||||
GetClusterAPIServerInfo(ctx context.Context) *version.Info
|
||||
GetCloudProvider() string
|
||||
}
|
||||
|
||||
@@ -5,14 +5,15 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/metrics"
|
||||
"github.com/kubescape/kubescape/v3/core/pkg/hostsensorutils"
|
||||
"github.com/kubescape/kubescape/v3/core/pkg/opaprocessor"
|
||||
"github.com/kubescape/opa-utils/objectsenvelopes"
|
||||
"github.com/kubescape/opa-utils/reporthandling/apis"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/tools/pager"
|
||||
|
||||
"github.com/kubescape/k8s-interface/cloudsupport"
|
||||
cloudapis "github.com/kubescape/k8s-interface/cloudsupport/apis"
|
||||
@@ -60,7 +61,7 @@ func NewK8sResourceHandler(k8s *k8sinterface.KubernetesApi, hostSensorHandler ho
|
||||
return k8sHandler
|
||||
}
|
||||
|
||||
func (k8sHandler *K8sResourceHandler) GetResources(ctx context.Context, sessionObj *cautils.OPASessionObj, progressListener opaprocessor.IJobProgressNotificationClient, scanInfo *cautils.ScanInfo) (cautils.K8SResources, map[string]workloadinterface.IMetadata, cautils.ExternalResources, map[string]bool, error) {
|
||||
func (k8sHandler *K8sResourceHandler) GetResources(ctx context.Context, sessionObj *cautils.OPASessionObj, scanInfo *cautils.ScanInfo) (cautils.K8SResources, map[string]workloadinterface.IMetadata, cautils.ExternalResources, map[string]bool, error) {
|
||||
logger.L().Start("Accessing Kubernetes objects...")
|
||||
var err error
|
||||
|
||||
@@ -145,7 +146,7 @@ func (k8sHandler *K8sResourceHandler) GetResources(ctx context.Context, sessionO
|
||||
|
||||
// check that controls use cloud resources
|
||||
if len(cloudResources) > 0 {
|
||||
err := k8sHandler.collectCloudResources(ctx, sessionObj, allResources, ksResourceMap, cloudResources, progressListener)
|
||||
err := k8sHandler.collectCloudResources(ctx, sessionObj, allResources, ksResourceMap, cloudResources)
|
||||
if err != nil {
|
||||
cautils.SetInfoMapForResources(err.Error(), cloudResources, sessionObj.InfoMap)
|
||||
logger.L().Debug("failed to collect cloud data", helpers.Error(err))
|
||||
@@ -173,9 +174,9 @@ func (k8sHandler *K8sResourceHandler) findScanObjectResource(resource *objectsen
|
||||
}
|
||||
|
||||
if resource.GetApiVersion() != "" {
|
||||
group, version := k8sinterface.SplitApiVersion(resource.GetApiVersion())
|
||||
gvr.Group = group
|
||||
gvr.Version = version
|
||||
g, v := k8sinterface.SplitApiVersion(resource.GetApiVersion())
|
||||
gvr.Group = g
|
||||
gvr.Version = v
|
||||
}
|
||||
|
||||
fieldSelectors := getNameFieldSelectorString(resource.GetName(), FieldSelectorsEqualsOperator)
|
||||
@@ -208,7 +209,7 @@ func (k8sHandler *K8sResourceHandler) findScanObjectResource(resource *objectsen
|
||||
return wl, nil
|
||||
}
|
||||
|
||||
func (k8sHandler *K8sResourceHandler) collectCloudResources(ctx context.Context, sessionObj *cautils.OPASessionObj, allResources map[string]workloadinterface.IMetadata, externalResourceMap cautils.ExternalResources, cloudResources []string, progressListener opaprocessor.IJobProgressNotificationClient) error {
|
||||
func (k8sHandler *K8sResourceHandler) collectCloudResources(ctx context.Context, sessionObj *cautils.OPASessionObj, allResources map[string]workloadinterface.IMetadata, externalResourceMap cautils.ExternalResources, cloudResources []string) error {
|
||||
|
||||
if k8sHandler.cloudProvider == "" {
|
||||
return fmt.Errorf("failed to get cloud provider, cluster: %s", k8sHandler.clusterName)
|
||||
@@ -322,7 +323,7 @@ func (k8sHandler *K8sResourceHandler) pullResources(queryableResources Queryable
|
||||
result, err := k8sHandler.pullSingleResource(&gvr, nil, queryableResources[i].FieldSelectors, globalFieldSelectors)
|
||||
if err != nil {
|
||||
if !strings.Contains(err.Error(), "the server could not find the requested resource") {
|
||||
logger.L().Error("failed to pull resource", helpers.String("resource", queryableResources[i].GroupVersionResourceTriplet), helpers.Error(err))
|
||||
logger.L().Warning("failed to pull resource", helpers.String("resource", queryableResources[i].GroupVersionResourceTriplet), helpers.Error(err))
|
||||
// handle error
|
||||
if errs == nil {
|
||||
errs = err
|
||||
@@ -356,7 +357,7 @@ func (k8sHandler *K8sResourceHandler) pullResources(queryableResources Queryable
|
||||
}
|
||||
|
||||
func (k8sHandler *K8sResourceHandler) pullSingleResource(resource *schema.GroupVersionResource, labels map[string]string, fields string, fieldSelector IFieldSelector) ([]unstructured.Unstructured, error) {
|
||||
resourceList := []unstructured.Unstructured{}
|
||||
var resourceList []unstructured.Unstructured
|
||||
// set labels
|
||||
listOptions := metav1.ListOptions{}
|
||||
fieldSelectors := fieldSelector.GetNamespacesSelectors(resource)
|
||||
@@ -376,30 +377,29 @@ func (k8sHandler *K8sResourceHandler) pullSingleResource(resource *schema.GroupV
|
||||
clientResource := k8sHandler.k8s.DynamicClient.Resource(*resource)
|
||||
|
||||
// list resources
|
||||
result, err := clientResource.List(context.Background(), listOptions)
|
||||
if err != nil || result == nil {
|
||||
return nil, fmt.Errorf("failed to get resource: %v, labelSelector: %v, fieldSelector: %v, reason: %v", resource, listOptions.LabelSelector, listOptions.FieldSelector, err)
|
||||
if err := pager.New(func(ctx context.Context, opts metav1.ListOptions) (runtime.Object, error) {
|
||||
return clientResource.List(ctx, opts)
|
||||
}).EachListItem(context.Background(), listOptions, func(obj runtime.Object) error {
|
||||
uObject := obj.(*unstructured.Unstructured)
|
||||
if k8sinterface.IsTypeWorkload(uObject.Object) && k8sinterface.WorkloadHasParent(workloadinterface.NewWorkloadObj(uObject.Object)) {
|
||||
logger.L().Debug("Skipping resource with parent", helpers.String("kind", uObject.GetKind()), helpers.String("name", uObject.GetName()))
|
||||
return nil
|
||||
}
|
||||
resourceList = append(resourceList, *obj.(*unstructured.Unstructured))
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("failed to get resource: %v, labelSelector: %v, fieldSelector: %v, reason: %w", resource, listOptions.LabelSelector, listOptions.FieldSelector, err)
|
||||
}
|
||||
|
||||
resourceList = append(resourceList, result.Items...)
|
||||
|
||||
}
|
||||
|
||||
return resourceList, nil
|
||||
|
||||
}
|
||||
func ConvertMapListToMeta(resourceMap []map[string]interface{}) []workloadinterface.IMetadata {
|
||||
workloads := []workloadinterface.IMetadata{}
|
||||
var workloads []workloadinterface.IMetadata
|
||||
for i := range resourceMap {
|
||||
r := resourceMap[i]
|
||||
|
||||
// skip workloads with parents. e.g. Pod with a ReplicaSet ownerReference. This will not skip resources with CRDs asa parents
|
||||
if k8sinterface.IsTypeWorkload(r) {
|
||||
if k8sinterface.WorkloadHasParent(workloadinterface.NewWorkloadObj(r)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if w := objectsenvelopes.NewObject(r); w != nil {
|
||||
workloads = append(workloads, w)
|
||||
}
|
||||
@@ -415,8 +415,8 @@ func (k8sHandler *K8sResourceHandler) collectHostResources(ctx context.Context,
|
||||
}
|
||||
|
||||
for rscIdx := range hostResources {
|
||||
group, version := getGroupNVersion(hostResources[rscIdx].GetApiVersion())
|
||||
groupResource := k8sinterface.JoinResourceTriplets(group, version, hostResources[rscIdx].GetKind())
|
||||
g, v := getGroupNVersion(hostResources[rscIdx].GetApiVersion())
|
||||
groupResource := k8sinterface.JoinResourceTriplets(g, v, hostResources[rscIdx].GetKind())
|
||||
allResources[hostResources[rscIdx].GetID()] = &hostResources[rscIdx]
|
||||
|
||||
grpResourceList, ok := externalResourceMap[groupResource]
|
||||
|
||||
@@ -229,8 +229,14 @@ func (g *GitHubRepository) getFilesFromTree(filesExtensions []string) []string {
|
||||
return []string{}
|
||||
}
|
||||
}
|
||||
|
||||
basePath := g.path
|
||||
if basePath != "" && !strings.HasSuffix(basePath, "/") {
|
||||
basePath += "/"
|
||||
}
|
||||
|
||||
for _, path := range g.tree.InnerTrees {
|
||||
if g.path != "" && !strings.HasPrefix(path.Path, g.path) {
|
||||
if basePath != "" && !strings.HasPrefix(path.Path, basePath) {
|
||||
continue
|
||||
}
|
||||
if slices.Contains(filesExtensions, getFileExtension(path.Path)) {
|
||||
@@ -240,6 +246,7 @@ func (g *GitHubRepository) getFilesFromTree(filesExtensions []string) []string {
|
||||
return urls
|
||||
}
|
||||
|
||||
|
||||
func (g *GitHubRepository) rowYamlUrl() string {
|
||||
return fmt.Sprintf("https://raw.githubusercontent.com/%s/%s", joinOwnerNRepo(g.owner, g.repo), g.branch)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package resourcehandler
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -13,6 +14,27 @@ var (
|
||||
// urlD = "https://raw.githubusercontent.com/kubescape/kubescape/master/examples/online-boutique/adservice.yaml"
|
||||
)
|
||||
|
||||
var mockTree = tree{
|
||||
InnerTrees: []innerTree{
|
||||
{Path: "charts/fluent-bit/values.yaml"},
|
||||
{Path: "charts/fluent-bit/templates/configmap.yaml"},
|
||||
{Path: "charts/other-chart/templates/deployment.yaml"},
|
||||
{Path: "README.md"},
|
||||
},
|
||||
}
|
||||
|
||||
func newMockGitHubRepository(path string, isFile bool) *GitHubRepository {
|
||||
return &GitHubRepository{
|
||||
host: "github.com",
|
||||
owner: "grafana",
|
||||
repo: "helm-charts",
|
||||
branch: "main",
|
||||
path: path,
|
||||
isFile: isFile,
|
||||
tree: mockTree,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
TODO: tests were commented out due to actual http calls ; http calls should be mocked.
|
||||
@@ -143,3 +165,60 @@ func TestGithubParse(t *testing.T) {
|
||||
assert.False(t, gh.isFile)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFilesFromTree(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
repo *GitHubRepository
|
||||
extensions []string
|
||||
expectedResults []string
|
||||
}{
|
||||
{
|
||||
name: "Scan entire repo for YAML files",
|
||||
repo: newMockGitHubRepository("", false),
|
||||
extensions: []string{"yaml", "yml"},
|
||||
expectedResults: []string{
|
||||
"https://raw.githubusercontent.com/grafana/helm-charts/main/charts/fluent-bit/values.yaml",
|
||||
"https://raw.githubusercontent.com/grafana/helm-charts/main/charts/fluent-bit/templates/configmap.yaml",
|
||||
"https://raw.githubusercontent.com/grafana/helm-charts/main/charts/other-chart/templates/deployment.yaml",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Scan specific folder (fluent-bit) for YAML files",
|
||||
repo: newMockGitHubRepository("charts/fluent-bit", false),
|
||||
extensions: []string{"yaml", "yml"},
|
||||
expectedResults: []string{
|
||||
"https://raw.githubusercontent.com/grafana/helm-charts/main/charts/fluent-bit/values.yaml",
|
||||
"https://raw.githubusercontent.com/grafana/helm-charts/main/charts/fluent-bit/templates/configmap.yaml",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Scan root with non-matching extension (JSON)",
|
||||
repo: newMockGitHubRepository("", false),
|
||||
extensions: []string{"json"},
|
||||
expectedResults: []string{},
|
||||
},
|
||||
{
|
||||
name: "Scan specific file",
|
||||
repo: newMockGitHubRepository("charts/fluent-bit/values.yaml", true),
|
||||
extensions: []string{"yaml"},
|
||||
expectedResults: []string{
|
||||
"https://raw.githubusercontent.com/grafana/helm-charts/main/charts/fluent-bit/values.yaml",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := tt.repo.getFilesFromTree(tt.extensions)
|
||||
|
||||
if len(got) == 0 && len(tt.expectedResults) == 0 {
|
||||
return // both are empty, so this test case passes
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, tt.expectedResults) {
|
||||
t.Errorf("getFilesFromTree() = %v, want %v", got, tt.expectedResults)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/k8s-interface/workloadinterface"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
)
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer"
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/k8s-interface/workloadinterface"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer"
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer"
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/grype/grype/presenter"
|
||||
"github.com/anchore/grype/grype/presenter/models"
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer"
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/k8s-interface/workloadinterface"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer"
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/anchore/grype/grype/presenter/models"
|
||||
"github.com/enescakir/emoji"
|
||||
"github.com/jwalton/gchalk"
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/k8s-interface/workloadinterface"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
|
||||
@@ -3,6 +3,7 @@ package prettyprinter
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
"github.com/kubescape/kubescape/v3/core/pkg/resultshandling/printer/v2/prettyprinter/tableprinter/configurationprinter"
|
||||
@@ -77,9 +78,8 @@ func (rp *RepoPrinter) getWorkloadScanCommand(ns, kind, name string, source repo
|
||||
}
|
||||
|
||||
if source.FileType == reporthandling.SourceTypeHelmChart {
|
||||
return fmt.Sprintf("%s --chart-path=%s --file-path=%s", cmd, source.HelmPath, fmt.Sprintf("%s/%s", source.Path, source.RelativePath))
|
||||
|
||||
return fmt.Sprintf("%s --chart-path=%s --file-path=%s", cmd, source.HelmPath, filepath.Join(source.Path, source.RelativePath))
|
||||
} else {
|
||||
return fmt.Sprintf("%s --file-path=%s", cmd, fmt.Sprintf("%s/%s", source.Path, source.RelativePath))
|
||||
return fmt.Sprintf("%s --file-path=%s", cmd, filepath.Join(source.Path, source.RelativePath))
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user