mirror of
https://github.com/kubescape/kubescape.git
synced 2026-03-03 02:00:27 +00:00
Compare commits
121 Commits
v3.0.9-rc.
...
v3.0.19-rc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
dfa5f1037e | ||
|
|
a15fc066e1 | ||
|
|
effc57dfda | ||
|
|
4b5c2dfed4 | ||
|
|
f39d4efd62 | ||
|
|
97ce466fbd | ||
|
|
a94dc85e14 | ||
|
|
7811b0a4a6 | ||
|
|
4c9cacecfe | ||
|
|
6ee6a78a75 | ||
|
|
e754ecff4f | ||
|
|
b9fd60b395 | ||
|
|
f0c3a568f0 | ||
|
|
a423b41e68 |
6
.github/workflows/00-pr-scanner.yaml
vendored
6
.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
|
||||
@@ -34,6 +33,8 @@ jobs:
|
||||
repository-projects: read
|
||||
security-events: read
|
||||
statuses: read
|
||||
attestations: read
|
||||
contents: write
|
||||
uses: ./.github/workflows/a-pr-scanner.yaml
|
||||
with:
|
||||
RELEASE: ""
|
||||
@@ -47,7 +48,7 @@ jobs:
|
||||
permissions:
|
||||
actions: read
|
||||
checks: read
|
||||
contents: read
|
||||
contents: write
|
||||
deployments: read
|
||||
discussions: read
|
||||
id-token: write
|
||||
@@ -58,6 +59,7 @@ jobs:
|
||||
repository-projects: read
|
||||
security-events: read
|
||||
statuses: read
|
||||
attestations: read
|
||||
uses: ./.github/workflows/b-binary-build-and-e2e-tests.yaml
|
||||
with:
|
||||
COMPONENT_NAME: kubescape
|
||||
|
||||
26
.github/workflows/02-release.yaml
vendored
26
.github/workflows/02-release.yaml
vendored
@@ -19,7 +19,6 @@ jobs:
|
||||
permissions:
|
||||
actions: read
|
||||
checks: read
|
||||
contents: read
|
||||
deployments: read
|
||||
discussions: read
|
||||
id-token: write
|
||||
@@ -30,6 +29,8 @@ 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
|
||||
with:
|
||||
@@ -55,6 +56,7 @@ jobs:
|
||||
repository-projects: read
|
||||
statuses: read
|
||||
security-events: read
|
||||
attestations: read
|
||||
needs: [retag, binary-build]
|
||||
uses: ./.github/workflows/c-create-release.yaml
|
||||
with:
|
||||
@@ -66,7 +68,6 @@ jobs:
|
||||
permissions:
|
||||
actions: read
|
||||
checks: read
|
||||
contents: read
|
||||
deployments: read
|
||||
discussions: read
|
||||
id-token: write
|
||||
@@ -77,6 +78,8 @@ jobs:
|
||||
repository-projects: read
|
||||
security-events: read
|
||||
statuses: read
|
||||
attestations: read
|
||||
contents: write
|
||||
uses: ./.github/workflows/d-publish-image.yaml
|
||||
needs: [create-release, retag]
|
||||
with:
|
||||
@@ -86,3 +89,22 @@ 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]
|
||||
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
|
||||
100
.github/workflows/b-binary-build-and-e2e-tests.yaml
vendored
100
.github/workflows/b-binary-build-and-e2e-tests.yaml
vendored
@@ -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:
|
||||
@@ -75,7 +118,7 @@ jobs:
|
||||
SECRET_KEY: ${{ secrets.SECRET_KEY_PROD }}
|
||||
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
||||
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
run: "echo \"is-secret-set=${{ env.CUSTOMER != '' && \n env.USERNAME != '' &&\n env.PASSWORD != '' &&\n env.CLIENT_ID != '' &&\n env.SECRET_KEY != '' &&\n env.REGISTRY_USERNAME != '' &&\n env.REGISTRY_PASSWORD != ''\n }}\" >> $GITHUB_OUTPUT\n"
|
||||
run: "echo \"is-secret-set=${{ env.CUSTOMER != '' && env.USERNAME != '' && env.PASSWORD != '' && env.CLIENT_ID != '' && env.SECRET_KEY != '' && env.REGISTRY_USERNAME != '' && env.REGISTRY_PASSWORD != '' }}\" >> $GITHUB_OUTPUT\n"
|
||||
|
||||
- id: export_tests_to_env
|
||||
name: set test name
|
||||
@@ -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
|
||||
@@ -290,5 +355,6 @@ jobs:
|
||||
uses: mikepenz/action-junit-report@6e9933f4a97f4d2b99acef4d7b97924466037882 # ratchet:mikepenz/action-junit-report@v3.6.1
|
||||
if: always() # always run even if the previous step fails
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
report_paths: '**/results_xml_format/**.xml'
|
||||
commit: ${{github.event.workflow_run.head_sha}}
|
||||
|
||||
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,17 @@
|
||||
name: 03-post_release
|
||||
name: e-post_release
|
||||
permissions: read-all
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
branches:
|
||||
- 'master'
|
||||
- 'main'
|
||||
workflow_call: {}
|
||||
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@92da038bbf995803124a8e50ebd438b2f37bbbb0 # ratchet:rajatjindal/krew-release-bot@v0.0.43
|
||||
- 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
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
|
||||
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.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Please follow our [code of conduct](CODE_OF_CONDUCT.md) in all of your interactions within the project.
|
||||
|
||||
## Decision Making
|
||||
|
||||
### Maintainers
|
||||
|
||||
35
README.md
35
README.md
@@ -20,13 +20,23 @@
|
||||
<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/).
|
||||
|
||||
@@ -68,20 +78,29 @@ Kubescape can be used as a GitHub Action. This is a great way to integrate Kubes
|
||||
## 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).
|
||||
For image scanning, it uses [Grype](https://github.com/anchore/grype).
|
||||
For image patching, it uses [Copacetic](https://github.com/project-copacetic/copacetic).
|
||||
|
||||
By default, the results are printed in a console-friendly manner, but they can be:
|
||||
|
||||
* exported to JSON or junit XML
|
||||
* 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).
|
||||
|
||||
## Architecture
|
||||

|
||||
|
||||
**Otel collector** - is not built-in, Otel endpoint spec is need to be added at setup [Setting Otel](https://kubescape.io/docs/operator/telemetry/)
|
||||
|
||||
|
||||
## 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.
|
||||
Kubescape is an open source project, we welcome your feedback and ideas for improvement. We are part of the cloud-native community and are enhancing the project as the ecosystem develops.
|
||||
|
||||
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)).
|
||||
|
||||
We hold [community meetings](https://zoom.us/j/95174063585) on Zoom, every other week, at 15:00 CET. ([See that in your local time zone](https://time.is/compare/1500_in_CET).
|
||||
|
||||
The Kubescape project follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
|
||||
|
||||
@@ -109,7 +128,7 @@ Kubescape changes are tracked on the [release](https://github.com/kubescape/kube
|
||||
|
||||
## 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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -83,7 +83,10 @@ func (a *OperatorAdapter) httpPostOperatorScanRequest(body apis.Commands) (strin
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return httputils.HttpRespToString(resp)
|
||||
if resp.StatusCode != 200 {
|
||||
return "", fmt.Errorf("http-error: %d", resp.StatusCode)
|
||||
}
|
||||
return "success", nil
|
||||
}
|
||||
|
||||
func (a *OperatorAdapter) OperatorScan() (string, error) {
|
||||
|
||||
@@ -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{}
|
||||
|
||||
@@ -45,10 +43,7 @@ func (fileHandler *FileResourceHandler) GetResources(ctx context.Context, sessio
|
||||
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, workloadIDToMappingNodes, _ = getWorkloadFromHelmChart(ctx, scanInfo.InputPatterns[path], scanInfo.ChartPath, scanInfo.FilePath)
|
||||
} else {
|
||||
workloadIDToSource, workloads, workloadIDToMappingNodes, err = getResourcesFromPath(ctx, scanInfo.InputPatterns[path])
|
||||
if err != nil {
|
||||
@@ -107,26 +102,22 @@ 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, map[string]cautils.MappingNodes, 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
|
||||
}
|
||||
|
||||
wlSource, ok := helmSourceToWorkloads[workloadPath]
|
||||
if !ok {
|
||||
return nil, nil, nil, fmt.Errorf("workload %s not found in chart %s", workloadPath, helmPath)
|
||||
@@ -195,14 +186,10 @@ func getResourcesFromPath(ctx context.Context, path string) (map[string]reportha
|
||||
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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package prettyprinter
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/kubescape/opa-utils/reporthandling"
|
||||
@@ -50,7 +51,18 @@ func TestRepoScan_getWorkloadScanCommand(t *testing.T) {
|
||||
Path: "path",
|
||||
RelativePath: "relativePath",
|
||||
},
|
||||
want: "$ kubescape scan workload kind/name --namespace ns --file-path=path/relativePath",
|
||||
want: "$ kubescape scan workload kind/name --namespace ns --file-path=" + filepath.Join("path", "relativePath"),
|
||||
},
|
||||
{
|
||||
testName: "relative file path",
|
||||
ns: "ns",
|
||||
kind: "kind",
|
||||
name: "name",
|
||||
source: reporthandling.Source{
|
||||
Path: "",
|
||||
RelativePath: "relativePath",
|
||||
},
|
||||
want: "$ kubescape scan workload kind/name --namespace ns --file-path=relativePath",
|
||||
},
|
||||
{
|
||||
testName: "helm path",
|
||||
@@ -63,7 +75,7 @@ func TestRepoScan_getWorkloadScanCommand(t *testing.T) {
|
||||
HelmPath: "helmPath",
|
||||
FileType: "Helm Chart",
|
||||
},
|
||||
want: "$ kubescape scan workload kind/name --namespace ns --chart-path=helmPath --file-path=path/relativePath",
|
||||
want: "$ kubescape scan workload kind/name --namespace ns --chart-path=helmPath --file-path=" + filepath.Join("path", "relativePath"),
|
||||
},
|
||||
{
|
||||
testName: "file path - no namespace",
|
||||
@@ -73,7 +85,7 @@ func TestRepoScan_getWorkloadScanCommand(t *testing.T) {
|
||||
Path: "path",
|
||||
RelativePath: "relativePath",
|
||||
},
|
||||
want: "$ kubescape scan workload kind/name --file-path=path/relativePath",
|
||||
want: "$ kubescape scan workload kind/name --file-path=" + filepath.Join("path", "relativePath"),
|
||||
},
|
||||
{
|
||||
testName: "helm path - no namespace",
|
||||
@@ -85,7 +97,7 @@ func TestRepoScan_getWorkloadScanCommand(t *testing.T) {
|
||||
HelmPath: "helmPath",
|
||||
FileType: "Helm Chart",
|
||||
},
|
||||
want: "$ kubescape scan workload kind/name --chart-path=helmPath --file-path=path/relativePath",
|
||||
want: "$ kubescape scan workload kind/name --chart-path=helmPath --file-path=" + filepath.Join("path", "relativePath"),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"os"
|
||||
|
||||
"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/k8s-interface/workloadinterface"
|
||||
"github.com/kubescape/kubescape/v3/core/cautils"
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
"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/fixhandler"
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/armosec/armoapi-go/apis"
|
||||
client "github.com/kubescape/backend/pkg/client/v1"
|
||||
v1 "github.com/kubescape/backend/pkg/server/v1"
|
||||
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"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user