mirror of
https://github.com/kubescape/kubescape.git
synced 2026-04-15 06:58:11 +00:00
Compare commits
151 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af8e786ab5 | ||
|
|
4f5839870b | ||
|
|
c0d7f51d6c | ||
|
|
a81d770360 | ||
|
|
17a2547f18 | ||
|
|
87a5cd66c8 | ||
|
|
9436ace64f | ||
|
|
fde00f6bd8 | ||
|
|
04a72a069a | ||
|
|
e2dcb5bc15 | ||
|
|
c7040a257c | ||
|
|
602dc00c65 | ||
|
|
0339691571 | ||
|
|
9e1f3ec131 | ||
|
|
b8589819dc | ||
|
|
a3e87f4c01 | ||
|
|
21ab5a602e | ||
|
|
5d97d7b4b2 | ||
|
|
d8d7d0b372 | ||
|
|
b8323d41fc | ||
|
|
d0b5314201 | ||
|
|
547e36e73f | ||
|
|
e593a772cb | ||
|
|
4da09529b6 | ||
|
|
de375992e8 | ||
|
|
0bc4a29881 | ||
|
|
9575c92713 | ||
|
|
cf277874eb | ||
|
|
746e060402 | ||
|
|
dd3a7c816e | ||
|
|
814bc3ab2c | ||
|
|
dbaf6761df | ||
|
|
580e45827d | ||
|
|
f3b8de9d1f | ||
|
|
fb1c728b12 | ||
|
|
6964ca0d18 | ||
|
|
6e9a2f55fd | ||
|
|
691fa61362 | ||
|
|
0c1eda0d08 | ||
|
|
767eac2fa6 | ||
|
|
6f651fa2d0 | ||
|
|
e3362c2e3d | ||
|
|
08b8b68f9a | ||
|
|
daf9ca9e7f | ||
|
|
d1024359c9 | ||
|
|
ed6070aff9 | ||
|
|
e4dbfa3534 | ||
|
|
ddd2b707c0 | ||
|
|
cd4f1077c2 | ||
|
|
b472d1cb9d | ||
|
|
922e2548f4 | ||
|
|
45caa7c120 | ||
|
|
670ae45d62 | ||
|
|
05bcf018d1 | ||
|
|
0af5d2e0bb | ||
|
|
eaf05fe9be | ||
|
|
e97b23f345 | ||
|
|
83a00ded3d | ||
|
|
78f81cc968 | ||
|
|
5d3347b4fe | ||
|
|
64d2ef8170 | ||
|
|
7c1e360b9a | ||
|
|
575d36dcde | ||
|
|
8dba8f7491 | ||
|
|
cc39e5b905 | ||
|
|
0be7e6018f | ||
|
|
7697e3f0c4 | ||
|
|
379800c49f | ||
|
|
79e2515807 | ||
|
|
342f5743e2 | ||
|
|
0e81870b85 | ||
|
|
dd7a8fd0c1 | ||
|
|
4277331ee2 | ||
|
|
53561a728f | ||
|
|
d0fd8c4fe4 | ||
|
|
398989510b | ||
|
|
f8e3ad5685 | ||
|
|
fbea7ef874 | ||
|
|
dc2c6f8a21 | ||
|
|
5ee08583b6 | ||
|
|
bfbd278e7c | ||
|
|
4c6e5903e3 | ||
|
|
a7cd5672c1 | ||
|
|
3373b728b7 | ||
|
|
22521b7159 | ||
|
|
e5fb14138e | ||
|
|
1b2242330c | ||
|
|
356958cc55 | ||
|
|
8f1da32001 | ||
|
|
686352a397 | ||
|
|
ef79c42ebc | ||
|
|
c8fc5378c1 | ||
|
|
c296666d8e | ||
|
|
f193e260b0 | ||
|
|
82981a9a54 | ||
|
|
3be54ca484 | ||
|
|
2f2c177674 | ||
|
|
1f47223918 | ||
|
|
eb646696a3 | ||
|
|
7cfe5160d5 | ||
|
|
95135c4379 | ||
|
|
7e604d6a5b | ||
|
|
64ac2666f9 | ||
|
|
05b3459342 | ||
|
|
92ad5f2407 | ||
|
|
e3c60e3202 | ||
|
|
7b5bcb05b1 | ||
|
|
154f94a0af | ||
|
|
063d3ee313 | ||
|
|
79859d05c0 | ||
|
|
acd3a94c46 | ||
|
|
13f09315e7 | ||
|
|
890528bf14 | ||
|
|
e4aafcf81e | ||
|
|
81c3c34ab8 | ||
|
|
b7b83b26b5 | ||
|
|
639cd3dfae | ||
|
|
7cf1302e8a | ||
|
|
dd5dd53a38 | ||
|
|
7275b8eac7 | ||
|
|
408c6fc998 | ||
|
|
5ce638572f | ||
|
|
4b98490ff9 | ||
|
|
6ea18ec75b | ||
|
|
56e2ffec5c | ||
|
|
fa204a208a | ||
|
|
9ab0fc593f | ||
|
|
3b9c454245 | ||
|
|
a6fc7a0da0 | ||
|
|
0f3ce6917e | ||
|
|
10fa3cb27d | ||
|
|
d8f95edff5 | ||
|
|
37ffe86d8b | ||
|
|
87fdbfdcc5 | ||
|
|
424a218860 | ||
|
|
12f81353e0 | ||
|
|
d6427f0fc8 | ||
|
|
f33a6d7634 | ||
|
|
5a01a1a30a | ||
|
|
ba588b9eef | ||
|
|
f48b848eb6 | ||
|
|
f81fd74aa3 | ||
|
|
ad608b08e0 | ||
|
|
f9e80b709a | ||
|
|
f75b62e62c | ||
|
|
1c24a55d4b | ||
|
|
03418299b8 | ||
|
|
f5bd86593c | ||
|
|
2af78eaab2 | ||
|
|
67cd003afe | ||
|
|
0bc542f851 |
80
.github/workflows/build-image.yaml
vendored
Normal file
80
.github/workflows/build-image.yaml
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
client:
|
||||
description: 'client name'
|
||||
required: true
|
||||
type: string
|
||||
image_tag:
|
||||
description: 'image tag'
|
||||
required: true
|
||||
type: string
|
||||
image_name:
|
||||
description: 'image registry and name'
|
||||
required: true
|
||||
type: string
|
||||
cosign:
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
description: 'run cosign on released image'
|
||||
support_platforms:
|
||||
required: false
|
||||
default: true
|
||||
type: boolean
|
||||
description: 'support amd64/arm64'
|
||||
|
||||
secrets:
|
||||
QUAYIO_REGISTRY_USERNAME:
|
||||
required: true
|
||||
QUAYIO_REGISTRY_PASSWORD:
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
build-image:
|
||||
name: Build image and upload to registry
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write
|
||||
packages: write
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to Quay.io
|
||||
env:
|
||||
QUAY_PASSWORD: ${{ secrets.QUAYIO_REGISTRY_PASSWORD }}
|
||||
QUAY_USERNAME: ${{ secrets.QUAYIO_REGISTRY_USERNAME }}
|
||||
run: docker login -u="${QUAY_USERNAME}" -p="${QUAY_PASSWORD}" quay.io
|
||||
|
||||
- name: Build and push image
|
||||
if: ${{ inputs.support_platforms }}
|
||||
run: docker buildx build . --file build/Dockerfile --tag ${{ inputs.image_name }}:${{ inputs.image_tag }} --tag ${{ inputs.image_name }}:latest --build-arg image_version=${{ inputs.image_tag }} --build-arg client=${{ inputs.client }} --push --platform linux/amd64,linux/arm64
|
||||
|
||||
- name: Build and push image without amd64/arm64 support
|
||||
if: ${{ !inputs.support_platforms }}
|
||||
run: docker buildx build . --file build/Dockerfile --tag ${{ inputs.image_name }}:${{ inputs.image_tag }} --tag ${{ inputs.image_name }}:latest --build-arg image_version=${{ inputs.image_tag }} --build-arg client=${{ inputs.client }} --push
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@main
|
||||
with:
|
||||
cosign-release: 'v1.12.0'
|
||||
- name: sign kubescape container image
|
||||
if: ${{ inputs.cosign }}
|
||||
env:
|
||||
COSIGN_EXPERIMENTAL: "true"
|
||||
run: |
|
||||
cosign sign --force ${{ inputs.image_name }}:latest
|
||||
cosign sign --force ${{ inputs.image_name }}:${{ inputs.image_tag }}
|
||||
|
||||
152
.github/workflows/build.yaml
vendored
152
.github/workflows/build.yaml
vendored
@@ -7,26 +7,26 @@ on:
|
||||
# Do not run the pipeline if only Markdown files changed
|
||||
- '**.md'
|
||||
jobs:
|
||||
once:
|
||||
name: Create release
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
steps:
|
||||
- name: Create a release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: v2.0.${{ github.run_number }}
|
||||
release_name: Release v2.0.${{ github.run_number }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
build:
|
||||
name: Create cross-platform release build, tag and upload binaries
|
||||
needs: once
|
||||
test:
|
||||
uses: ./.github/workflows/test.yaml
|
||||
with:
|
||||
release: "v2.0.${{ github.run_number }}"
|
||||
client: test
|
||||
|
||||
create-release:
|
||||
uses: ./.github/workflows/release.yaml
|
||||
needs: test
|
||||
with:
|
||||
release_name: "Release v2.0.${{ github.run_number }}"
|
||||
tag_name: "v2.0.${{ github.run_number }}"
|
||||
secrets: inherit
|
||||
|
||||
publish-artifacts:
|
||||
name: Build and publish artifacts
|
||||
needs: create-release
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
@@ -34,40 +34,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Cache Go modules (Linux)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Cache Go modules (macOS)
|
||||
if: matrix.os == 'macos-latest'
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/Library/Caches/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Cache Go modules (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~\AppData\Local\go-build
|
||||
~\go\pkg\mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
@@ -82,34 +49,20 @@ jobs:
|
||||
run: make libgit2
|
||||
if: matrix.os != 'windows-latest'
|
||||
|
||||
- name: Test core pkg
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: go test -tags=static -v ./...
|
||||
|
||||
- name: Test httphandler pkg
|
||||
run: cd httphandler && go test -tags=static -v ./...
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
RELEASE: v2.0.${{ github.run_number }}
|
||||
CLIENT: release
|
||||
CGO_ENABLED: 1
|
||||
run: python3 --version && python3 build.py
|
||||
|
||||
- name: Smoke Testing
|
||||
env:
|
||||
RELEASE: v2.0.${{ github.run_number }}
|
||||
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
|
||||
run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape
|
||||
|
||||
|
||||
- name: Upload release binaries
|
||||
id: upload-release-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.once.outputs.upload_url }}
|
||||
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||
asset_path: build/${{ matrix.os }}/kubescape
|
||||
asset_name: kubescape-${{ matrix.os }}
|
||||
asset_content_type: application/octet-stream
|
||||
@@ -120,56 +73,19 @@ jobs:
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ needs.once.outputs.upload_url }}
|
||||
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||
asset_path: build/${{ matrix.os }}/kubescape.sha256
|
||||
asset_name: kubescape-${{ matrix.os }}-sha256
|
||||
asset_content_type: application/octet-stream
|
||||
build-docker:
|
||||
name: Build docker container, tag and upload to registry
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
publish-image:
|
||||
if: ${{ github.repository == 'kubescape/kubescape' }} # TODO
|
||||
permissions:
|
||||
id-token: write
|
||||
packages: write
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set image version
|
||||
id: image-version
|
||||
run: echo '::set-output name=IMAGE_VERSION::v2.0.${{ github.run_number }}'
|
||||
|
||||
- name: Set image name
|
||||
id: image-name
|
||||
run: echo '::set-output name=IMAGE_NAME::quay.io/${{ github.repository_owner }}/kubescape'
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to Quay.io
|
||||
env:
|
||||
QUAY_PASSWORD: ${{ secrets.QUAYIO_REGISTRY_PASSWORD }}
|
||||
QUAY_USERNAME: ${{ secrets.QUAYIO_REGISTRY_USERNAME }}
|
||||
run: docker login -u="${QUAY_USERNAME}" -p="${QUAY_PASSWORD}" quay.io
|
||||
|
||||
- name: Build the Docker image
|
||||
run: docker buildx build . --file build/Dockerfile --tag ${{ steps.image-name.outputs.IMAGE_NAME }}:${{ steps.image-version.outputs.IMAGE_VERSION }} --tag ${{ steps.image-name.outputs.IMAGE_NAME }}:latest --build-arg image_version=${{ steps.image-version.outputs.IMAGE_VERSION }} --build-arg client=image-release --push --platform linux/amd64,linux/arm64
|
||||
|
||||
- name: Install cosign
|
||||
uses: sigstore/cosign-installer@main
|
||||
with:
|
||||
cosign-release: 'v1.12.0'
|
||||
- name: sign kubescape container image
|
||||
env:
|
||||
COSIGN_EXPERIMENTAL: "true"
|
||||
run: |
|
||||
cosign sign --force ${{ steps.image-name.outputs.IMAGE_NAME }}:latest
|
||||
cosign sign --force ${{ steps.image-name.outputs.IMAGE_NAME }}:${{ steps.image-version.outputs.IMAGE_VERSION }}
|
||||
|
||||
uses: ./.github/workflows/build-image.yaml
|
||||
needs: create-release
|
||||
with:
|
||||
client: "image-release"
|
||||
image_name: "quay.io/${{ github.repository_owner }}/kubescape"
|
||||
image_tag: "v2.0.${{ github.run_number }}"
|
||||
support_platforms: true
|
||||
cosign: true
|
||||
secrets: inherit
|
||||
|
||||
148
.github/workflows/build_dev.yaml
vendored
148
.github/workflows/build_dev.yaml
vendored
@@ -7,136 +7,20 @@ on:
|
||||
# Do not run the pipeline if only Markdown files changed
|
||||
- '**.md'
|
||||
jobs:
|
||||
build:
|
||||
name: Create cross-platform dev build
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Cache Go modules (Linux)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Cache Go modules (macOS)
|
||||
if: matrix.os == 'macos-latest'
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/Library/Caches/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Cache Go modules (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~\AppData\Local\go-build
|
||||
~\go\pkg\mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
|
||||
# - name: Test cmd pkg
|
||||
# run: cd cmd && go test -v ./...
|
||||
|
||||
# - name: Test core pkg
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# run: cd core && go test -v ./...
|
||||
|
||||
# - name: Test cmd pkg
|
||||
# run: cd cmd && go test -v ./...
|
||||
|
||||
- name: Install MSYS2 & libgit2 (Windows)
|
||||
shell: cmd
|
||||
run: .\build.bat all
|
||||
if: matrix.os == 'windows-latest'
|
||||
|
||||
- name: Install libgit2 (Linux/macOS)
|
||||
run: make libgit2
|
||||
if: matrix.os != 'windows-latest'
|
||||
|
||||
- name: Test core pkg
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: go test -tags=static -v ./...
|
||||
|
||||
- name: Test httphandler pkg
|
||||
run: cd httphandler && go test -tags=static -v ./...
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
RELEASE: v2.0.${{ github.run_number }}
|
||||
CLIENT: release-dev
|
||||
CGO_ENABLED: 1
|
||||
run: python3 --version && python3 build.py
|
||||
|
||||
- name: Smoke Testing
|
||||
env:
|
||||
RELEASE: v2.0.${{ github.run_number }}
|
||||
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
|
||||
run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: kubescape-${{ matrix.os }}
|
||||
path: build/${{ matrix.os }}/kubescape
|
||||
|
||||
build-docker:
|
||||
name: Build docker container, tag and upload to registry
|
||||
needs: build
|
||||
test:
|
||||
uses: ./.github/workflows/test.yaml
|
||||
with:
|
||||
release: "v2.0.${{ github.run_number }}"
|
||||
client: test
|
||||
|
||||
publish-dev-image:
|
||||
if: ${{ github.repository == 'kubescape/kubescape' }} # TODO
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write
|
||||
packages: write
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Set image version
|
||||
id: image-version
|
||||
run: echo '::set-output name=IMAGE_VERSION::dev-v2.0.${{ github.run_number }}'
|
||||
|
||||
- name: Set image name
|
||||
id: image-name
|
||||
run: echo '::set-output name=IMAGE_NAME::quay.io/${{ github.repository_owner }}/kubescape'
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to Quay.io
|
||||
env:
|
||||
QUAY_PASSWORD: ${{ secrets.QUAYIO_REGISTRY_PASSWORD }}
|
||||
QUAY_USERNAME: ${{ secrets.QUAYIO_REGISTRY_USERNAME }}
|
||||
run: docker login -u="${QUAY_USERNAME}" -p="${QUAY_PASSWORD}" quay.io
|
||||
|
||||
- name: Build the Docker image
|
||||
run: docker buildx build . --file build/Dockerfile --tag ${{ steps.image-name.outputs.IMAGE_NAME }}:${{ steps.image-version.outputs.IMAGE_VERSION }} --build-arg image_version=${{ steps.image-version.outputs.IMAGE_VERSION }} --build-arg client=image-dev --push --platform linux/amd64,linux/arm64
|
||||
uses: ./.github/workflows/build-image.yaml
|
||||
needs: test
|
||||
with:
|
||||
client: "image-dev"
|
||||
image_name: "quay.io/${{ github.repository_owner }}/kubescape"
|
||||
image_tag: "dev-v2.0.${{ github.run_number }}"
|
||||
support_platforms: true
|
||||
cosign: true
|
||||
secrets: inherit
|
||||
|
||||
88
.github/workflows/pr_checks.yaml
vendored
88
.github/workflows/pr_checks.yaml
vendored
@@ -6,87 +6,11 @@ on:
|
||||
types: [ edited, opened, synchronize, reopened ]
|
||||
paths-ignore:
|
||||
# Do not run the pipeline if only Markdown files changed
|
||||
- '**.yaml'
|
||||
- '**.md'
|
||||
jobs:
|
||||
build:
|
||||
name: Create cross-platform build
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Cache Go modules (Linux)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Cache Go modules (macOS)
|
||||
if: matrix.os == 'macos-latest'
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/Library/Caches/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Cache Go modules (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~\AppData\Local\go-build
|
||||
~\go\pkg\mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
|
||||
- name: Install MSYS2 & libgit2 (Windows)
|
||||
shell: cmd
|
||||
run: .\build.bat all
|
||||
if: matrix.os == 'windows-latest'
|
||||
|
||||
- name: Install libgit2 (Linux/macOS)
|
||||
run: make libgit2
|
||||
if: matrix.os != 'windows-latest'
|
||||
|
||||
# - name: Test cmd pkg
|
||||
# run: cd cmd && go test -v ./...
|
||||
|
||||
- name: Test core pkg
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: go test -tags=static -v ./...
|
||||
|
||||
- name: Test httphandler pkg
|
||||
run: cd httphandler && go test -tags=static -v ./...
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
RELEASE: v2.0.${{ github.run_number }}
|
||||
CLIENT: test
|
||||
CGO_ENABLED: 1
|
||||
run: python3 --version && python3 build.py
|
||||
|
||||
- name: Smoke Testing
|
||||
env:
|
||||
RELEASE: v2.0.${{ github.run_number }}
|
||||
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
|
||||
run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape
|
||||
|
||||
test:
|
||||
uses: ./.github/workflows/test.yaml
|
||||
with:
|
||||
release: "v2.0.${{ github.run_number }}"
|
||||
client: test
|
||||
|
||||
41
.github/workflows/release.yaml
vendored
Normal file
41
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
release_name:
|
||||
description: 'release'
|
||||
required: true
|
||||
type: string
|
||||
tag_name:
|
||||
description: 'tag'
|
||||
required: true
|
||||
type: string
|
||||
draft:
|
||||
description: 'create draft release'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
outputs:
|
||||
upload_url:
|
||||
description: "The first output string"
|
||||
value: ${{ jobs.release.outputs.upload_url }}
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Create release
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
steps:
|
||||
- name: Create a release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ inputs.tag_name }}
|
||||
release_name: ${{ inputs.release_name }}
|
||||
draft: ${{ inputs.draft }}
|
||||
prerelease: false
|
||||
|
||||
93
.github/workflows/test.yaml
vendored
Normal file
93
.github/workflows/test.yaml
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
name: test
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
release:
|
||||
description: 'release'
|
||||
required: true
|
||||
type: string
|
||||
client:
|
||||
description: 'Client name'
|
||||
required: true
|
||||
type: string
|
||||
jobs:
|
||||
build:
|
||||
name: Create cross-platform build
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Cache Go modules (Linux)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Cache Go modules (macOS)
|
||||
if: matrix.os == 'macos-latest'
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/Library/Caches/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Cache Go modules (Windows)
|
||||
if: matrix.os == 'windows-latest'
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~\AppData\Local\go-build
|
||||
~\go\pkg\mod
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
|
||||
- name: Install MSYS2 & libgit2 (Windows)
|
||||
shell: cmd
|
||||
run: .\build.bat all
|
||||
if: matrix.os == 'windows-latest'
|
||||
|
||||
- name: Install libgit2 (Linux/macOS)
|
||||
run: make libgit2
|
||||
if: matrix.os != 'windows-latest'
|
||||
|
||||
- name: Test core pkg
|
||||
run: go test -tags=static -v ./...
|
||||
|
||||
- name: Test httphandler pkg
|
||||
run: cd httphandler && go test -tags=static -v ./...
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
RELEASE: ${{ inputs.release }}
|
||||
CLIENT: test
|
||||
CGO_ENABLED: 1
|
||||
run: python3 --version && python3 build.py
|
||||
|
||||
- name: Smoke Testing
|
||||
env:
|
||||
RELEASE: ${{ inputs.release }}
|
||||
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
|
||||
run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape
|
||||
|
||||
44
README.md
44
README.md
@@ -40,7 +40,7 @@ curl -s https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh
|
||||
|
||||
## Run:
|
||||
```sh
|
||||
kubescape scan --submit --enable-host-scan --verbose
|
||||
kubescape scan --enable-host-scan --verbose
|
||||
```
|
||||
|
||||
<img src="docs/summary.png">
|
||||
@@ -51,6 +51,17 @@ kubescape scan --submit --enable-host-scan --verbose
|
||||
|
||||
</br>
|
||||
|
||||
## Architecture in short
|
||||
### [CLI](#kubescape-cli)
|
||||
<div align="center">
|
||||
<img src="docs/ks-cli-arch.png" width="300" alt="cli-diagram">
|
||||
</div>
|
||||
|
||||
### [Operator](https://github.com/kubescape/helm-charts#readme)
|
||||
<div align="center">
|
||||
<img src="docs/ks-operator-arch.png" width="300" alt="operator-diagram">
|
||||
</div>
|
||||
|
||||
### Please [star ⭐](https://github.com/kubescape/kubescape/stargazers) the repo if you want us to continue developing and improving Kubescape 😀
|
||||
|
||||
</br>
|
||||
@@ -85,6 +96,7 @@ We hold community meetings in [Zoom](https://us02web.zoom.us/j/84020231442) on t
|
||||
* [Overview](https://youtu.be/wdBkt_0Qhbg)
|
||||
* [How To Secure Kubernetes Clusters With Kubescape And Armo](https://youtu.be/ZATGiDIDBQk)
|
||||
* [Scan Kubernetes YAML files](https://youtu.be/Ox6DaR7_4ZI)
|
||||
* [Scan container image registry](https://youtu.be/iQ_k8EnK-3s)
|
||||
* [Scan Kubescape on an air-gapped environment (offline support)](https://youtu.be/IGXL9s37smM)
|
||||
* [Managing exceptions in the Kubescape SaaS version](https://youtu.be/OzpvxGmCR80)
|
||||
* [Configure and run customized frameworks](https://youtu.be/12Sanq_rEhs)
|
||||
@@ -163,22 +175,22 @@ Or to your profile (not preferred): `nix-env --install -A nixpkgs.kubescape`
|
||||
### Examples
|
||||
|
||||
|
||||
#### Scan a running Kubernetes cluster and submit results to the [Kubescape SaaS version](https://cloud.armosec.io?utm_source=github&utm_medium=repository)
|
||||
#### Scan a running Kubernetes cluster
|
||||
```
|
||||
kubescape scan --submit --enable-host-scan --verbose
|
||||
kubescape scan --enable-host-scan --verbose
|
||||
```
|
||||
|
||||
> Read [here](https://hub.armosec.io/docs/host-sensor?utm_source=github&utm_medium=repository) more about the `enable-host-scan` flag
|
||||
|
||||
#### Scan a running Kubernetes cluster with [`nsa`](https://www.nsa.gov/Press-Room/News-Highlights/Article/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/) framework and submit results to the [Kubescape SaaS version](https://cloud.armosec.io?utm_source=github&utm_medium=repository)
|
||||
#### Scan a running Kubernetes cluster with [`nsa`](https://www.nsa.gov/Press-Room/News-Highlights/Article/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/) framework
|
||||
```
|
||||
kubescape scan framework nsa --submit
|
||||
kubescape scan framework nsa
|
||||
```
|
||||
|
||||
|
||||
#### Scan a running Kubernetes cluster with [`MITRE ATT&CK®`](https://www.microsoft.com/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/) framework and submit results to the [Kubescape SaaS version](https://cloud.armosec.io?utm_source=github&utm_medium=repository)
|
||||
#### Scan a running Kubernetes cluster with [`MITRE ATT&CK®`](https://www.microsoft.com/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/) framework
|
||||
```
|
||||
kubescape scan framework mitre --submit
|
||||
kubescape scan framework mitre
|
||||
```
|
||||
|
||||
|
||||
@@ -187,6 +199,11 @@ kubescape scan framework mitre --submit
|
||||
kubescape scan control "Privileged container"
|
||||
```
|
||||
|
||||
#### Scan using an alternative kubeconfig file
|
||||
```
|
||||
kubescape scan --kubeconfig cluster.conf
|
||||
```
|
||||
|
||||
#### Scan specific namespaces
|
||||
```
|
||||
kubescape scan --include-namespaces development,staging,production
|
||||
@@ -197,14 +214,15 @@ kubescape scan --include-namespaces development,staging,production
|
||||
kubescape scan --exclude-namespaces kube-system,kube-public
|
||||
```
|
||||
|
||||
#### Scan local `yaml`/`json` files before deploying. [Take a look at the demonstration](https://youtu.be/Ox6DaR7_4ZI). Submit the results in case the directory is a git repo. [docs](https://hub.armosec.io/docs/repository-scanning?utm_source=github&utm_medium=repository)
|
||||
#### Scan local `yaml`/`json` files before deploying. [Take a look at the demonstration](https://youtu.be/Ox6DaR7_4ZI).
|
||||
```
|
||||
kubescape scan *.yaml --submit
|
||||
kubescape scan *.yaml
|
||||
```
|
||||
|
||||
#### Scan Kubernetes manifest files from a git repository [and submit the results](https://hub.armosec.io/docs/repository-scanning?utm_source=github&utm_medium=repository)
|
||||
#### Scan Kubernetes manifest files from a git repository
|
||||
|
||||
```
|
||||
kubescape scan https://github.com/kubescape/kubescape --submit
|
||||
kubescape scan https://github.com/kubescape/kubescape
|
||||
```
|
||||
|
||||
#### Display all scanned resources (including the resources which passed)
|
||||
@@ -251,13 +269,13 @@ kubescape scan --exceptions examples/exceptions/exclude-kube-namespaces.json
|
||||
|
||||
#### Scan Helm charts
|
||||
```
|
||||
kubescape scan </path/to/directory> --submit
|
||||
kubescape scan </path/to/directory>
|
||||
```
|
||||
> Kubescape will load the default value file
|
||||
|
||||
#### Scan Kustomize Directory
|
||||
```
|
||||
kubescape scan </path/to/directory> --submit
|
||||
kubescape scan </path/to/directory>
|
||||
```
|
||||
> Kubescape will generate Kubernetes Yaml Objects using 'Kustomize' file and scans them for security.
|
||||
|
||||
|
||||
13
build.py
13
build.py
@@ -14,14 +14,15 @@ def check_status(status, msg):
|
||||
|
||||
def get_build_dir():
|
||||
current_platform = platform.system()
|
||||
build_dir = "./build/"
|
||||
build_dir = ""
|
||||
|
||||
if current_platform == "Windows": build_dir += "windows-latest"
|
||||
elif current_platform == "Linux": build_dir += "ubuntu-latest"
|
||||
elif current_platform == "Darwin": build_dir += "macos-latest"
|
||||
if current_platform == "Windows": build_dir = "windows-latest"
|
||||
elif current_platform == "Linux": build_dir = "ubuntu-latest"
|
||||
elif current_platform == "Darwin": build_dir = "macos-latest"
|
||||
else: raise OSError("Platform %s is not supported!" % (current_platform))
|
||||
|
||||
return build_dir
|
||||
return os.path.join("build", build_dir)
|
||||
|
||||
|
||||
def get_package_name():
|
||||
package_name = "kubescape"
|
||||
@@ -56,7 +57,7 @@ def main():
|
||||
if client_name:
|
||||
ldflags += " -X {}={}".format(client_var, client_name)
|
||||
|
||||
build_command = ["go", "build", "-tags=static", "-o", ks_file, "-ldflags" ,ldflags]
|
||||
build_command = ["go", "build", "-buildmode=pie", "-tags=static", "-o", ks_file, "-ldflags" ,ldflags]
|
||||
|
||||
print("Building kubescape and saving here: {}".format(ks_file))
|
||||
print("Build command: {}".format(" ".join(build_command)))
|
||||
|
||||
@@ -12,7 +12,7 @@ ENV CGO_ENABLED=1
|
||||
|
||||
# Install required python/pip
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
RUN apk add --update --no-cache python3 git openssl-dev musl-dev gcc make cmake pkgconfig && ln -sf python3 /usr/bin/python
|
||||
RUN apk add --update --no-cache python3 gcc make git libc-dev binutils-gold cmake pkgconfig && ln -sf python3 /usr/bin/python
|
||||
RUN python3 -m ensurepip
|
||||
RUN pip3 install --no-cache --upgrade pip setuptools
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ var (
|
||||
|
||||
# Set access key
|
||||
kubescape config set secretKey <access key>
|
||||
|
||||
# Set cloudAPIURL
|
||||
kubescape config set cloudAPIURL <cloud API URL>
|
||||
`
|
||||
)
|
||||
|
||||
|
||||
@@ -33,9 +33,13 @@ func getSetCmd(ks meta.IKubescape) *cobra.Command {
|
||||
}
|
||||
|
||||
var supportConfigSet = map[string]func(*metav1.SetConfig, string){
|
||||
"accountID": func(s *metav1.SetConfig, account string) { s.Account = account },
|
||||
"clientID": func(s *metav1.SetConfig, clientID string) { s.ClientID = clientID },
|
||||
"secretKey": func(s *metav1.SetConfig, secretKey string) { s.SecretKey = secretKey },
|
||||
"accountID": func(s *metav1.SetConfig, account string) { s.Account = account },
|
||||
"clientID": func(s *metav1.SetConfig, clientID string) { s.ClientID = clientID },
|
||||
"secretKey": func(s *metav1.SetConfig, secretKey string) { s.SecretKey = secretKey },
|
||||
"cloudAPIURL": func(s *metav1.SetConfig, cloudAPIURL string) { s.CloudAPIURL = cloudAPIURL },
|
||||
"cloudAuthURL": func(s *metav1.SetConfig, cloudAuthURL string) { s.CloudAuthURL = cloudAuthURL },
|
||||
"cloudReportURL": func(s *metav1.SetConfig, cloudReportURL string) { s.CloudReportURL = cloudReportURL },
|
||||
"cloudUIURL": func(s *metav1.SetConfig, cloudUIURL string) { s.CloudUIURL = cloudUIURL },
|
||||
}
|
||||
|
||||
func stringKeysToSlice(m map[string]func(*metav1.SetConfig, string)) []string {
|
||||
|
||||
@@ -24,8 +24,8 @@ var (
|
||||
# Download the NSA framework. Run 'kubescape list frameworks' for all frameworks names
|
||||
kubescape download framework nsa
|
||||
|
||||
# Download the "Allowed hostPath" control. Run 'kubescape list controls' for all controls names
|
||||
kubescape download control "Allowed hostPath"
|
||||
# Download the "HostPath mount" control. Run 'kubescape list controls' for all controls names
|
||||
kubescape download control "HostPath mount"
|
||||
|
||||
# Download the "C-0001" control. Run 'kubescape list controls --id' for all controls ids
|
||||
kubescape download control C-0001
|
||||
|
||||
@@ -20,11 +20,8 @@ var (
|
||||
# List all supported frameworks names
|
||||
kubescape list frameworks --account <account id>
|
||||
|
||||
# List all supported controls names
|
||||
# List all supported controls names with ids
|
||||
kubescape list controls
|
||||
|
||||
# List all supported controls ids
|
||||
kubescape list controls --id
|
||||
|
||||
Control documentation:
|
||||
https://hub.armosec.io/docs/controls
|
||||
@@ -67,8 +64,8 @@ func GetListCmd(ks meta.IKubescape) *cobra.Command {
|
||||
listCmd.PersistentFlags().StringVarP(&listPolicies.Credentials.Account, "account", "", "", "Kubescape SaaS account ID. Default will load account ID from cache")
|
||||
listCmd.PersistentFlags().StringVarP(&listPolicies.Credentials.ClientID, "client-id", "", "", "Kubescape SaaS client ID. Default will load client ID from cache, read more - https://hub.armosec.io/docs/authentication")
|
||||
listCmd.PersistentFlags().StringVarP(&listPolicies.Credentials.SecretKey, "secret-key", "", "", "Kubescape SaaS secret key. Default will load secret key from cache, read more - https://hub.armosec.io/docs/authentication")
|
||||
listCmd.PersistentFlags().StringVar(&listPolicies.Format, "format", "pretty-print", "output format. supported: 'pretty-printer'/'json'")
|
||||
listCmd.PersistentFlags().BoolVarP(&listPolicies.ListIDs, "id", "", false, "List control ID's instead of controls names")
|
||||
listCmd.PersistentFlags().StringVar(&listPolicies.Format, "format", "pretty-print", "output format. supported: 'pretty-print'/'json'")
|
||||
listCmd.PersistentFlags().MarkDeprecated("id", "Control ID's are included in list outpus")
|
||||
|
||||
return listCmd
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/kubescape/kubescape/v2/cmd/list"
|
||||
"github.com/kubescape/kubescape/v2/cmd/scan"
|
||||
"github.com/kubescape/kubescape/v2/cmd/submit"
|
||||
"github.com/kubescape/kubescape/v2/cmd/update"
|
||||
"github.com/kubescape/kubescape/v2/cmd/version"
|
||||
"github.com/kubescape/kubescape/v2/core/cautils"
|
||||
"github.com/kubescape/kubescape/v2/core/cautils/getter"
|
||||
@@ -76,6 +77,7 @@ func getRootCmd(ks meta.IKubescape) *cobra.Command {
|
||||
rootCmd.AddCommand(completion.GetCompletionCmd())
|
||||
rootCmd.AddCommand(version.GetVersionCmd())
|
||||
rootCmd.AddCommand(config.GetConfigCmd(ks))
|
||||
rootCmd.AddCommand(update.GetUpdateCmd())
|
||||
|
||||
return rootCmd
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ var (
|
||||
kubescape scan control "privileged container"
|
||||
|
||||
# Scan list of controls separated with a comma
|
||||
kubescape scan control "privileged container","allowed hostpath"
|
||||
kubescape scan control "privileged container","HostPath mount"
|
||||
|
||||
# Scan list of controls using the control ID separated with a comma
|
||||
kubescape scan control C-0058,C-0057
|
||||
@@ -61,7 +61,7 @@ func getControlCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comman
|
||||
if err := validateFrameworkScanInfo(scanInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
// flagValidationControl(scanInfo)
|
||||
scanInfo.PolicyIdentifier = []cautils.PolicyIdentifier{}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package scan
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"github.com/kubescape/k8s-interface/k8sinterface"
|
||||
@@ -13,7 +14,7 @@ var scanCmdExamples = `
|
||||
Scan command is for scanning an existing cluster or kubernetes manifest files based on pre-defined frameworks
|
||||
|
||||
# Scan current cluster with all frameworks
|
||||
kubescape scan --submit --enable-host-scan --verbose
|
||||
kubescape scan --enable-host-scan --verbose
|
||||
|
||||
# Scan kubernetes YAML manifest files
|
||||
kubescape scan *.yaml
|
||||
@@ -70,34 +71,33 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
|
||||
scanCmd.PersistentFlags().StringVar(&scanInfo.ControlsInputs, "controls-config", "", "Path to an controls-config obj. If not set will download controls-config from ARMO management portal")
|
||||
scanCmd.PersistentFlags().StringVar(&scanInfo.UseExceptions, "exceptions", "", "Path to an exceptions obj. If not set will download exceptions from ARMO management portal")
|
||||
scanCmd.PersistentFlags().StringVar(&scanInfo.UseArtifactsFrom, "use-artifacts-from", "", "Load artifacts from local directory. If not used will download them")
|
||||
scanCmd.PersistentFlags().StringVarP(&scanInfo.ExcludedNamespaces, "exclude-namespaces", "e", "", "Namespaces to exclude from scanning. Recommended: kube-system,kube-public")
|
||||
scanCmd.PersistentFlags().StringVarP(&scanInfo.ExcludedNamespaces, "exclude-namespaces", "e", "", "Namespaces to exclude from scanning. Notice, when running with `exclude-namespace` kubescape does not scan cluster-scoped objects.")
|
||||
|
||||
scanCmd.PersistentFlags().Float32VarP(&scanInfo.FailThreshold, "fail-threshold", "t", 100, "Failure threshold is the percent above which the command fails and returns exit code 1")
|
||||
|
||||
scanCmd.PersistentFlags().StringVar(&scanInfo.FailThresholdSeverity, "severity-threshold", "", "Severity threshold is the severity of failed controls at which the command fails and returns exit code 1")
|
||||
scanCmd.PersistentFlags().StringVarP(&scanInfo.Format, "format", "f", "pretty-printer", `Output format. Supported formats: "pretty-printer", "json", "junit", "prometheus", "pdf", "html"`)
|
||||
scanCmd.PersistentFlags().StringVarP(&scanInfo.Format, "format", "f", "pretty-printer", `Output format. Supported formats: "pretty-printer", "json", "junit", "prometheus", "pdf", "html", "sarif"`)
|
||||
scanCmd.PersistentFlags().StringVar(&scanInfo.IncludeNamespaces, "include-namespaces", "", "scan specific namespaces. e.g: --include-namespaces ns-a,ns-b")
|
||||
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Local, "keep-local", "", false, "If you do not want your Kubescape results reported to ARMO backend. Use this flag if you ran with the '--submit' flag in the past and you do not want to submit your current scan results")
|
||||
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Local, "keep-local", "", false, "If you do not want your Kubescape results reported to configured backend.")
|
||||
scanCmd.PersistentFlags().StringVarP(&scanInfo.Output, "output", "o", "", "Output file. Print output to file and not stdout")
|
||||
scanCmd.PersistentFlags().BoolVarP(&scanInfo.VerboseMode, "verbose", "v", false, "Display all of the input resources and not only failed resources")
|
||||
scanCmd.PersistentFlags().StringVar(&scanInfo.View, "view", string(cautils.ResourceViewType), fmt.Sprintf("View results based on the %s/%s. default is --view=%s", cautils.ResourceViewType, cautils.ControlViewType, cautils.ResourceViewType))
|
||||
scanCmd.PersistentFlags().BoolVar(&scanInfo.UseDefault, "use-default", false, "Load local policy object from default path. If not used will download latest")
|
||||
scanCmd.PersistentFlags().StringSliceVar(&scanInfo.UseFrom, "use-from", nil, "Load local policy object from specified path. If not used will download latest")
|
||||
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Submit, "submit", "", false, "Send the scan results to ARMO management portal where you can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more. By default the results are not submitted")
|
||||
scanCmd.PersistentFlags().StringVar(&scanInfo.HostSensorYamlPath, "host-scan-yaml", "", "Override default host scanner DaemonSet. Use this flag cautiously")
|
||||
scanCmd.PersistentFlags().StringVar(&scanInfo.FormatVersion, "format-version", "v1", "Output object can be different between versions, this is for maintaining backward and forward compatibility. Supported:'v1'/'v2'")
|
||||
scanCmd.PersistentFlags().StringVar(&scanInfo.CustomClusterName, "cluster-name", "", "Set the custom name of the cluster. Not same as the kube-context flag")
|
||||
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Submit, "submit", "", false, "Submit the scan results to Kubescape SaaS where you can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more. By default the results are not submitted")
|
||||
|
||||
// Deprecated flags - remove 1.May.2022
|
||||
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Silent, "silent", "s", false, "Silent progress messages")
|
||||
scanCmd.PersistentFlags().MarkDeprecated("silent", "use '--logger' flag instead. Flag will be removed at 1.May.2022")
|
||||
|
||||
// hidden flags
|
||||
scanCmd.PersistentFlags().MarkHidden("host-scan-yaml") // this flag should be used very cautiously. We prefer users will not use it at all unless the DaemonSet can not run pods on the nodes
|
||||
scanCmd.PersistentFlags().MarkHidden("silent") // this flag should be deprecated since we added the --logger support
|
||||
// scanCmd.PersistentFlags().MarkHidden("format-version") // meant for testing different output approaches and not for common use
|
||||
|
||||
hostF := scanCmd.PersistentFlags().VarPF(&scanInfo.HostSensorEnabled, "enable-host-scan", "", "Deploy ARMO K8s host-sensor daemonset in the scanned cluster. Deleting it right after we collecting the data. Required to collect valuable data from cluster nodes for certain controls. Yaml file: https://github.com/kubescape/kubescape/blob/master/core/pkg/hostsensorutils/hostsensor.yaml")
|
||||
// Retrieve --kubeconfig flag from https://github.com/kubernetes/kubectl/blob/master/pkg/cmd/cmd.go
|
||||
scanCmd.PersistentFlags().AddGoFlag(flag.Lookup("kubeconfig"))
|
||||
|
||||
hostF := scanCmd.PersistentFlags().VarPF(&scanInfo.HostSensorEnabled, "enable-host-scan", "", "Deploy Kubescape host-sensor daemonset in the scanned cluster. Deleting it right after we collecting the data. Required to collect valuable data from cluster nodes for certain controls. Yaml file: https://github.com/kubescape/kubescape/blob/master/core/pkg/hostsensorutils/hostsensor.yaml")
|
||||
hostF.NoOptDefVal = "true"
|
||||
hostF.DefValue = "false, for no TTY in stdin"
|
||||
|
||||
|
||||
@@ -31,10 +31,11 @@ var (
|
||||
// getRBACCmd represents the RBAC command
|
||||
func getRBACCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "rbac",
|
||||
Example: rbacExamples,
|
||||
Short: "Submit cluster's Role-Based Access Control(RBAC)",
|
||||
Long: ``,
|
||||
Use: "rbac",
|
||||
Deprecated: "This command is deprecated and will not be supported after 1/Jan/2023. Please use the 'scan' command instead.",
|
||||
Example: rbacExamples,
|
||||
Short: "Submit cluster's Role-Based Access Control(RBAC)",
|
||||
Long: ``,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
if err := flagValidationSubmit(submitInfo); err != nil {
|
||||
|
||||
@@ -7,16 +7,21 @@ import (
|
||||
)
|
||||
|
||||
var submitCmdExamples = `
|
||||
# Submit Kubescape scan results file
|
||||
kubescape submit results
|
||||
|
||||
# Submit exceptions file to Kubescape SaaS
|
||||
kubescape submit exceptions
|
||||
`
|
||||
|
||||
func GetSubmitCmd(ks meta.IKubescape) *cobra.Command {
|
||||
var submitInfo metav1.Submit
|
||||
|
||||
submitCmd := &cobra.Command{
|
||||
Use: "submit <command>",
|
||||
Short: "Submit an object to the Kubescape SaaS version",
|
||||
Long: ``,
|
||||
Use: "submit <command>",
|
||||
Short: "Submit an object to the Kubescape SaaS version",
|
||||
Long: ``,
|
||||
Example: submitCmdExamples,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
},
|
||||
}
|
||||
|
||||
59
cmd/update/update.go
Normal file
59
cmd/update/update.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package update
|
||||
|
||||
//This update command updates to the latest kubescape release.
|
||||
//Example:-
|
||||
// kubescape update
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/kubescape/v2/core/cautils"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func GetUpdateCmd() *cobra.Command {
|
||||
updateCmd := &cobra.Command{
|
||||
Use: "update",
|
||||
Short: "Update your version",
|
||||
Long: ``,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
//Checking the user's version of kubescape to the latest release
|
||||
if cautils.BuildNumber == cautils.LatestReleaseVersion {
|
||||
//your version == latest version
|
||||
logger.L().Info(("You are in the latest version"))
|
||||
} else {
|
||||
|
||||
const OSTYPE string = runtime.GOOS
|
||||
var ShellToUse string
|
||||
switch OSTYPE {
|
||||
|
||||
case "windows":
|
||||
cautils.StartSpinner()
|
||||
//run the installation command for windows
|
||||
ShellToUse = "powershell"
|
||||
_, err := exec.Command(ShellToUse, "-c", "iwr -useb https://raw.githubusercontent.com/kubescape/kubescape/master/install.ps1 | iex").Output()
|
||||
|
||||
if err != nil {
|
||||
logger.L().Fatal(err.Error())
|
||||
}
|
||||
cautils.StopSpinner()
|
||||
|
||||
default:
|
||||
ShellToUse = "bash"
|
||||
cautils.StartSpinner()
|
||||
//run the installation command for linux and macOS
|
||||
_, err := exec.Command(ShellToUse, "-c", "curl -s https://raw.githubusercontent.com/kubescape/kubescape/master/install.sh | /bin/bash").Output()
|
||||
if err != nil {
|
||||
logger.L().Fatal(err.Error())
|
||||
}
|
||||
|
||||
cautils.StopSpinner()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
return updateCmd
|
||||
}
|
||||
12
core/cautils/controllink.go
Normal file
12
core/cautils/controllink.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package cautils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func GetControlLink(controlID string) string {
|
||||
// For CIS Controls, cis-1.1.3 will be transformed to cis-1-1-3 in documentation link.
|
||||
docLinkID := strings.ReplaceAll(controlID, ".", "-")
|
||||
return fmt.Sprintf("https://hub.armosec.io/docs/%s", strings.ToLower(docLinkID))
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/k8s-interface/k8sinterface"
|
||||
"github.com/kubescape/kubescape/v2/core/cautils/getter"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -32,6 +33,10 @@ type ConfigObj struct {
|
||||
Token string `json:"invitationParam,omitempty"`
|
||||
CustomerAdminEMail string `json:"adminMail,omitempty"`
|
||||
ClusterName string `json:"clusterName,omitempty"`
|
||||
CloudReportURL string `json:"cloudReportURL,omitempty"`
|
||||
CloudAPIURL string `json:"cloudAPIURL,omitempty"`
|
||||
CloudUIURL string `json:"cloudUIURL,omitempty"`
|
||||
CloudAuthURL string `json:"cloudAuthURL,omitempty"`
|
||||
}
|
||||
|
||||
// Config - convert ConfigObj to config file
|
||||
@@ -75,6 +80,10 @@ type ITenantConfig interface {
|
||||
GetClientID() string
|
||||
GetSecretKey() string
|
||||
GetConfigObj() *ConfigObj
|
||||
GetCloudReportURL() string
|
||||
GetCloudAPIURL() string
|
||||
GetCloudUIURL() string
|
||||
GetCloudAuthURL() string
|
||||
// GetBackendAPI() getter.IBackend
|
||||
// GenerateURL()
|
||||
|
||||
@@ -103,10 +112,11 @@ func NewLocalConfig(
|
||||
}
|
||||
|
||||
updateCredentials(lc.configObj, credentials)
|
||||
updateCloudURLs(lc.configObj)
|
||||
|
||||
// If a custom cluster name is provided then set that name, else use the cluster's original name
|
||||
if customClusterName != "" {
|
||||
lc.configObj.ClusterName = AdoptCustomClusterName(customClusterName)
|
||||
lc.configObj.ClusterName = AdoptClusterName(customClusterName)
|
||||
} else if clusterName != "" {
|
||||
lc.configObj.ClusterName = AdoptClusterName(clusterName) // override config clusterName
|
||||
}
|
||||
@@ -114,18 +124,43 @@ func NewLocalConfig(
|
||||
lc.backendAPI.SetAccountID(lc.configObj.AccountID)
|
||||
lc.backendAPI.SetClientID(lc.configObj.ClientID)
|
||||
lc.backendAPI.SetSecretKey(lc.configObj.SecretKey)
|
||||
if lc.configObj.CloudAPIURL != "" {
|
||||
lc.backendAPI.SetCloudAPIURL(lc.configObj.CloudAPIURL)
|
||||
} else {
|
||||
lc.configObj.CloudAPIURL = lc.backendAPI.GetCloudAPIURL()
|
||||
}
|
||||
if lc.configObj.CloudAuthURL != "" {
|
||||
lc.backendAPI.SetCloudAuthURL(lc.configObj.CloudAuthURL)
|
||||
} else {
|
||||
lc.configObj.CloudAuthURL = lc.backendAPI.GetCloudAuthURL()
|
||||
}
|
||||
if lc.configObj.CloudReportURL != "" {
|
||||
lc.backendAPI.SetCloudReportURL(lc.configObj.CloudReportURL)
|
||||
} else {
|
||||
lc.configObj.CloudReportURL = lc.backendAPI.GetCloudReportURL()
|
||||
}
|
||||
if lc.configObj.CloudUIURL != "" {
|
||||
lc.backendAPI.SetCloudUIURL(lc.configObj.CloudUIURL)
|
||||
} else {
|
||||
lc.configObj.CloudUIURL = lc.backendAPI.GetCloudUIURL()
|
||||
}
|
||||
logger.L().Debug("Kubescape Cloud URLs", helpers.String("api", lc.backendAPI.GetCloudAPIURL()), helpers.String("auth", lc.backendAPI.GetCloudAuthURL()), helpers.String("report", lc.backendAPI.GetCloudReportURL()), helpers.String("UI", lc.backendAPI.GetCloudUIURL()))
|
||||
|
||||
return lc
|
||||
}
|
||||
|
||||
func (lc *LocalConfig) GetConfigObj() *ConfigObj { return lc.configObj }
|
||||
func (lc *LocalConfig) GetTenantEmail() string { return lc.configObj.CustomerAdminEMail }
|
||||
func (lc *LocalConfig) GetAccountID() string { return lc.configObj.AccountID }
|
||||
func (lc *LocalConfig) GetClientID() string { return lc.configObj.ClientID }
|
||||
func (lc *LocalConfig) GetSecretKey() string { return lc.configObj.SecretKey }
|
||||
func (lc *LocalConfig) GetContextName() string { return lc.configObj.ClusterName }
|
||||
func (lc *LocalConfig) GetToken() string { return lc.configObj.Token }
|
||||
func (lc *LocalConfig) IsConfigFound() bool { return existsConfigFile() }
|
||||
func (lc *LocalConfig) GetConfigObj() *ConfigObj { return lc.configObj }
|
||||
func (lc *LocalConfig) GetTenantEmail() string { return lc.configObj.CustomerAdminEMail }
|
||||
func (lc *LocalConfig) GetAccountID() string { return lc.configObj.AccountID }
|
||||
func (lc *LocalConfig) GetClientID() string { return lc.configObj.ClientID }
|
||||
func (lc *LocalConfig) GetSecretKey() string { return lc.configObj.SecretKey }
|
||||
func (lc *LocalConfig) GetContextName() string { return lc.configObj.ClusterName }
|
||||
func (lc *LocalConfig) GetToken() string { return lc.configObj.Token }
|
||||
func (lc *LocalConfig) GetCloudReportURL() string { return lc.configObj.CloudReportURL }
|
||||
func (lc *LocalConfig) GetCloudAPIURL() string { return lc.configObj.CloudAPIURL }
|
||||
func (lc *LocalConfig) GetCloudUIURL() string { return lc.configObj.CloudUIURL }
|
||||
func (lc *LocalConfig) GetCloudAuthURL() string { return lc.configObj.CloudAuthURL }
|
||||
func (lc *LocalConfig) IsConfigFound() bool { return existsConfigFile() }
|
||||
func (lc *LocalConfig) SetTenant() error {
|
||||
|
||||
// Kubescape Cloud tenant GUID
|
||||
@@ -213,10 +248,11 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken
|
||||
loadConfigFromFile(c.configObj)
|
||||
}
|
||||
updateCredentials(c.configObj, credentials)
|
||||
updateCloudURLs(c.configObj)
|
||||
|
||||
// If a custom cluster name is provided then set that name, else use the cluster's original name
|
||||
if customClusterName != "" {
|
||||
c.configObj.ClusterName = AdoptCustomClusterName(customClusterName)
|
||||
c.configObj.ClusterName = AdoptClusterName(customClusterName)
|
||||
} else if clusterName != "" {
|
||||
c.configObj.ClusterName = AdoptClusterName(clusterName) // override config clusterName
|
||||
}
|
||||
@@ -230,18 +266,44 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken
|
||||
c.backendAPI.SetAccountID(c.configObj.AccountID)
|
||||
c.backendAPI.SetClientID(c.configObj.ClientID)
|
||||
c.backendAPI.SetSecretKey(c.configObj.SecretKey)
|
||||
if c.configObj.CloudAPIURL != "" {
|
||||
c.backendAPI.SetCloudAPIURL(c.configObj.CloudAPIURL)
|
||||
} else {
|
||||
c.configObj.CloudAPIURL = c.backendAPI.GetCloudAPIURL()
|
||||
}
|
||||
if c.configObj.CloudAuthURL != "" {
|
||||
c.backendAPI.SetCloudAuthURL(c.configObj.CloudAuthURL)
|
||||
} else {
|
||||
c.configObj.CloudAuthURL = c.backendAPI.GetCloudAuthURL()
|
||||
}
|
||||
if c.configObj.CloudReportURL != "" {
|
||||
c.backendAPI.SetCloudReportURL(c.configObj.CloudReportURL)
|
||||
} else {
|
||||
c.configObj.CloudReportURL = c.backendAPI.GetCloudReportURL()
|
||||
}
|
||||
if c.configObj.CloudUIURL != "" {
|
||||
c.backendAPI.SetCloudUIURL(c.configObj.CloudUIURL)
|
||||
} else {
|
||||
c.configObj.CloudUIURL = c.backendAPI.GetCloudUIURL()
|
||||
}
|
||||
logger.L().Debug("Kubescape Cloud URLs", helpers.String("api", c.backendAPI.GetCloudAPIURL()), helpers.String("auth", c.backendAPI.GetCloudAuthURL()), helpers.String("report", c.backendAPI.GetCloudReportURL()), helpers.String("UI", c.backendAPI.GetCloudUIURL()))
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *ClusterConfig) GetConfigObj() *ConfigObj { return c.configObj }
|
||||
func (c *ClusterConfig) GetDefaultNS() string { return c.configMapNamespace }
|
||||
func (c *ClusterConfig) GetAccountID() string { return c.configObj.AccountID }
|
||||
func (c *ClusterConfig) GetClientID() string { return c.configObj.ClientID }
|
||||
func (c *ClusterConfig) GetSecretKey() string { return c.configObj.SecretKey }
|
||||
func (c *ClusterConfig) GetTenantEmail() string { return c.configObj.CustomerAdminEMail }
|
||||
func (c *ClusterConfig) GetToken() string { return c.configObj.Token }
|
||||
func (c *ClusterConfig) IsConfigFound() bool { return existsConfigFile() || c.existsConfigMap() }
|
||||
func (c *ClusterConfig) GetConfigObj() *ConfigObj { return c.configObj }
|
||||
func (c *ClusterConfig) GetDefaultNS() string { return c.configMapNamespace }
|
||||
func (c *ClusterConfig) GetAccountID() string { return c.configObj.AccountID }
|
||||
func (c *ClusterConfig) GetClientID() string { return c.configObj.ClientID }
|
||||
func (c *ClusterConfig) GetSecretKey() string { return c.configObj.SecretKey }
|
||||
func (c *ClusterConfig) GetTenantEmail() string { return c.configObj.CustomerAdminEMail }
|
||||
func (c *ClusterConfig) GetToken() string { return c.configObj.Token }
|
||||
func (c *ClusterConfig) GetCloudReportURL() string { return c.configObj.CloudReportURL }
|
||||
func (c *ClusterConfig) GetCloudAPIURL() string { return c.configObj.CloudAPIURL }
|
||||
func (c *ClusterConfig) GetCloudUIURL() string { return c.configObj.CloudUIURL }
|
||||
func (c *ClusterConfig) GetCloudAuthURL() string { return c.configObj.CloudAuthURL }
|
||||
|
||||
func (c *ClusterConfig) IsConfigFound() bool { return existsConfigFile() || c.existsConfigMap() }
|
||||
|
||||
func (c *ClusterConfig) SetTenant() error {
|
||||
|
||||
@@ -474,19 +536,6 @@ func DeleteConfigFile() error {
|
||||
return os.Remove(ConfigFileFullPath())
|
||||
}
|
||||
|
||||
// To check if the custom cluster name is valid:
|
||||
func AdoptCustomClusterName(customClusterName string) string {
|
||||
is_alphanumeric := regexp.MustCompile(`^[a-zA-Z0-9]*$`).MatchString(customClusterName)
|
||||
|
||||
// Check it does not contain special-characters
|
||||
if is_alphanumeric == false {
|
||||
logger.L().Fatal("custom cluster name cannot contain special characters")
|
||||
} else if len(customClusterName) >= 256 { // Check it contains less than 256 characters
|
||||
logger.L().Fatal("custom cluster name cannot contain more than 255 characters")
|
||||
}
|
||||
return customClusterName
|
||||
}
|
||||
|
||||
func AdoptClusterName(clusterName string) string {
|
||||
re, err := regexp.Compile(`[^\w]+`)
|
||||
if err != nil {
|
||||
@@ -540,3 +589,39 @@ func updateCredentials(configObj *ConfigObj, credentials *Credentials) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getCloudURLsFromEnv(cloudURLs *CloudURLs) {
|
||||
// load from env
|
||||
if cloudAPIURL := os.Getenv("KS_CLOUD_API_URL"); cloudAPIURL != "" {
|
||||
cloudURLs.CloudAPIURL = cloudAPIURL
|
||||
}
|
||||
if cloudAuthURL := os.Getenv("KS_CLOUD_AUTH_URL"); cloudAuthURL != "" {
|
||||
cloudURLs.CloudAuthURL = cloudAuthURL
|
||||
}
|
||||
if cloudReportURL := os.Getenv("KS_CLOUD_REPORT_URL"); cloudReportURL != "" {
|
||||
cloudURLs.CloudReportURL = cloudReportURL
|
||||
}
|
||||
if cloudUIURL := os.Getenv("KS_CLOUD_UI_URL"); cloudUIURL != "" {
|
||||
cloudURLs.CloudUIURL = cloudUIURL
|
||||
}
|
||||
}
|
||||
|
||||
func updateCloudURLs(configObj *ConfigObj) {
|
||||
cloudURLs := &CloudURLs{}
|
||||
|
||||
getCloudURLsFromEnv(cloudURLs)
|
||||
|
||||
if cloudURLs.CloudAPIURL != "" {
|
||||
configObj.CloudAPIURL = cloudURLs.CloudAPIURL // override config CloudAPIURL
|
||||
}
|
||||
if cloudURLs.CloudAuthURL != "" {
|
||||
configObj.CloudAuthURL = cloudURLs.CloudAuthURL // override config CloudAuthURL
|
||||
}
|
||||
if cloudURLs.CloudReportURL != "" {
|
||||
configObj.CloudReportURL = cloudURLs.CloudReportURL // override config CloudReportURL
|
||||
}
|
||||
if cloudURLs.CloudUIURL != "" {
|
||||
configObj.CloudUIURL = cloudURLs.CloudUIURL // override config CloudUIURL
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package cautils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -16,6 +17,10 @@ func mockConfigObj() *ConfigObj {
|
||||
ClusterName: "ddd",
|
||||
CustomerAdminEMail: "ab@cd",
|
||||
Token: "eee",
|
||||
CloudReportURL: "report.armo.cloud",
|
||||
CloudAPIURL: "api.armosec.io",
|
||||
CloudUIURL: "cloud.armosec.io",
|
||||
CloudAuthURL: "auth.armosec.io",
|
||||
}
|
||||
}
|
||||
func mockLocalConfig() *LocalConfig {
|
||||
@@ -39,6 +44,10 @@ func TestConfig(t *testing.T) {
|
||||
assert.Equal(t, co.AccountID, cop.AccountID)
|
||||
assert.Equal(t, co.ClientID, cop.ClientID)
|
||||
assert.Equal(t, co.SecretKey, cop.SecretKey)
|
||||
assert.Equal(t, co.CloudReportURL, cop.CloudReportURL)
|
||||
assert.Equal(t, co.CloudAPIURL, cop.CloudAPIURL)
|
||||
assert.Equal(t, co.CloudUIURL, cop.CloudUIURL)
|
||||
assert.Equal(t, co.CloudAuthURL, cop.CloudAuthURL)
|
||||
assert.Equal(t, "", cop.ClusterName) // Not copied to bytes
|
||||
assert.Equal(t, "", cop.CustomerAdminEMail) // Not copied to bytes
|
||||
assert.Equal(t, "", cop.Token) // Not copied to bytes
|
||||
@@ -60,6 +69,10 @@ func TestITenantConfig(t *testing.T) {
|
||||
assert.Equal(t, co.ClusterName, lc.GetContextName())
|
||||
assert.Equal(t, co.CustomerAdminEMail, lc.GetTenantEmail())
|
||||
assert.Equal(t, co.Token, lc.GetToken())
|
||||
assert.Equal(t, co.CloudReportURL, lc.GetCloudReportURL())
|
||||
assert.Equal(t, co.CloudAPIURL, lc.GetCloudAPIURL())
|
||||
assert.Equal(t, co.CloudUIURL, lc.GetCloudUIURL())
|
||||
assert.Equal(t, co.CloudAuthURL, lc.GetCloudAuthURL())
|
||||
|
||||
// test ClusterConfig methods
|
||||
assert.Equal(t, co.AccountID, c.GetAccountID())
|
||||
@@ -68,6 +81,10 @@ func TestITenantConfig(t *testing.T) {
|
||||
assert.Equal(t, co.ClusterName, c.GetContextName())
|
||||
assert.Equal(t, co.CustomerAdminEMail, c.GetTenantEmail())
|
||||
assert.Equal(t, co.Token, c.GetToken())
|
||||
assert.Equal(t, co.CloudReportURL, c.GetCloudReportURL())
|
||||
assert.Equal(t, co.CloudAPIURL, c.GetCloudAPIURL())
|
||||
assert.Equal(t, co.CloudUIURL, c.GetCloudUIURL())
|
||||
assert.Equal(t, co.CloudAuthURL, c.GetCloudAuthURL())
|
||||
}
|
||||
|
||||
func TestUpdateConfigData(t *testing.T) {
|
||||
@@ -80,6 +97,10 @@ func TestUpdateConfigData(t *testing.T) {
|
||||
assert.Equal(t, c.GetAccountID(), configMap.Data["accountID"])
|
||||
assert.Equal(t, c.GetClientID(), configMap.Data["clientID"])
|
||||
assert.Equal(t, c.GetSecretKey(), configMap.Data["secretKey"])
|
||||
assert.Equal(t, c.GetCloudReportURL(), configMap.Data["cloudReportURL"])
|
||||
assert.Equal(t, c.GetCloudAPIURL(), configMap.Data["cloudAPIURL"])
|
||||
assert.Equal(t, c.GetCloudUIURL(), configMap.Data["cloudUIURL"])
|
||||
assert.Equal(t, c.GetCloudAuthURL(), configMap.Data["cloudAuthURL"])
|
||||
}
|
||||
|
||||
func TestReadConfig(t *testing.T) {
|
||||
@@ -97,6 +118,10 @@ func TestReadConfig(t *testing.T) {
|
||||
assert.Equal(t, com.ClusterName, co.ClusterName)
|
||||
assert.Equal(t, com.CustomerAdminEMail, co.CustomerAdminEMail)
|
||||
assert.Equal(t, com.Token, co.Token)
|
||||
assert.Equal(t, com.CloudReportURL, co.CloudReportURL)
|
||||
assert.Equal(t, com.CloudAPIURL, co.CloudAPIURL)
|
||||
assert.Equal(t, com.CloudUIURL, co.CloudUIURL)
|
||||
assert.Equal(t, com.CloudAuthURL, co.CloudAuthURL)
|
||||
}
|
||||
|
||||
func TestLoadConfigFromData(t *testing.T) {
|
||||
@@ -120,6 +145,10 @@ func TestLoadConfigFromData(t *testing.T) {
|
||||
assert.Equal(t, c.GetContextName(), co.ClusterName)
|
||||
assert.Equal(t, c.GetTenantEmail(), co.CustomerAdminEMail)
|
||||
assert.Equal(t, c.GetToken(), co.Token)
|
||||
assert.Equal(t, c.GetCloudReportURL(), co.CloudReportURL)
|
||||
assert.Equal(t, c.GetCloudAPIURL(), co.CloudAPIURL)
|
||||
assert.Equal(t, c.GetCloudUIURL(), co.CloudUIURL)
|
||||
assert.Equal(t, c.GetCloudAuthURL(), co.CloudAuthURL)
|
||||
}
|
||||
|
||||
// use case: all data is in config.json
|
||||
@@ -139,6 +168,10 @@ func TestLoadConfigFromData(t *testing.T) {
|
||||
assert.Equal(t, c.GetAccountID(), co.AccountID)
|
||||
assert.Equal(t, c.GetClientID(), co.ClientID)
|
||||
assert.Equal(t, c.GetSecretKey(), co.SecretKey)
|
||||
assert.Equal(t, c.GetCloudReportURL(), co.CloudReportURL)
|
||||
assert.Equal(t, c.GetCloudAPIURL(), co.CloudAPIURL)
|
||||
assert.Equal(t, c.GetCloudUIURL(), co.CloudUIURL)
|
||||
assert.Equal(t, c.GetCloudAuthURL(), co.CloudAuthURL)
|
||||
}
|
||||
|
||||
// use case: some data is in config.json
|
||||
@@ -151,10 +184,12 @@ func TestLoadConfigFromData(t *testing.T) {
|
||||
// add to map
|
||||
configMap.Data["clientID"] = c.configObj.ClientID
|
||||
configMap.Data["secretKey"] = c.configObj.SecretKey
|
||||
configMap.Data["cloudReportURL"] = c.configObj.CloudReportURL
|
||||
|
||||
// delete the content
|
||||
c.configObj.ClientID = ""
|
||||
c.configObj.SecretKey = ""
|
||||
c.configObj.CloudReportURL = ""
|
||||
|
||||
configMap.Data["config.json"] = string(c.GetConfigObj().Config())
|
||||
loadConfigFromData(c.configObj, configMap.Data)
|
||||
@@ -162,6 +197,7 @@ func TestLoadConfigFromData(t *testing.T) {
|
||||
assert.NotEmpty(t, c.GetAccountID())
|
||||
assert.NotEmpty(t, c.GetClientID())
|
||||
assert.NotEmpty(t, c.GetSecretKey())
|
||||
assert.NotEmpty(t, c.GetCloudReportURL())
|
||||
}
|
||||
|
||||
// use case: some data is in config.json
|
||||
@@ -222,3 +258,13 @@ func TestAdoptClusterName(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateCloudURLs(t *testing.T) {
|
||||
co := mockConfigObj()
|
||||
mockCloudAPIURL := "1-2-3-4.com"
|
||||
os.Setenv("KS_CLOUD_API_URL", mockCloudAPIURL)
|
||||
|
||||
assert.NotEqual(t, co.CloudAPIURL, mockCloudAPIURL)
|
||||
updateCloudURLs(co)
|
||||
assert.Equal(t, co.CloudAPIURL, mockCloudAPIURL)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/kubescape/opa-utils/objectsenvelopes"
|
||||
"github.com/kubescape/opa-utils/objectsenvelopes/localworkload"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -139,9 +139,9 @@ func loadFiles(rootPath string, filePaths []string) (map[string][]workloadinterf
|
||||
for j := range w {
|
||||
lw := localworkload.NewLocalWorkload(w[j].GetObject())
|
||||
if relPath, err := filepath.Rel(rootPath, path); err == nil {
|
||||
lw.SetPath(relPath)
|
||||
lw.SetPath(fmt.Sprintf("%s:%d", relPath, j))
|
||||
} else {
|
||||
lw.SetPath(path)
|
||||
lw.SetPath(fmt.Sprintf("%s:%d", path, j))
|
||||
}
|
||||
wSlice = append(wSlice, lw)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package getter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/armoapi-go/armotypes"
|
||||
"github.com/kubescape/opa-utils/gitregostore"
|
||||
"github.com/kubescape/opa-utils/reporthandling"
|
||||
"github.com/kubescape/opa-utils/reporthandling/attacktrack/v1alpha1"
|
||||
@@ -54,13 +56,21 @@ func (drp *DownloadReleasedPolicy) ListFrameworks() ([]string, error) {
|
||||
return drp.gs.GetOPAFrameworksNamesList()
|
||||
}
|
||||
|
||||
func (drp *DownloadReleasedPolicy) ListControls(listType ListType) ([]string, error) {
|
||||
switch listType {
|
||||
case ListID:
|
||||
return drp.gs.GetOPAControlsIDsList()
|
||||
default:
|
||||
return drp.gs.GetOPAControlsNamesList()
|
||||
func (drp *DownloadReleasedPolicy) ListControls() ([]string, error) {
|
||||
controlsIDsList, err := drp.gs.GetOPAControlsIDsList()
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
controlsNamesList, err := drp.gs.GetOPAControlsNamesList()
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
controlsNamesWithIDsList := make([]string, len(controlsIDsList))
|
||||
// by design both slices have the same length
|
||||
for i := range controlsIDsList {
|
||||
controlsNamesWithIDsList[i] = fmt.Sprintf("%v|%v", controlsIDsList[i], controlsNamesList[i])
|
||||
}
|
||||
return controlsNamesWithIDsList, nil
|
||||
}
|
||||
|
||||
func (drp *DownloadReleasedPolicy) GetControlsInputs(clusterName string) (map[string][]string, error) {
|
||||
@@ -99,3 +109,11 @@ func contains(s []string, str string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (drp *DownloadReleasedPolicy) GetExceptions(clusterName string) ([]armotypes.PostureExceptionPolicy, error) {
|
||||
exceptions, err := drp.gs.GetSystemPostureExceptionPolicies()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return exceptions, nil
|
||||
}
|
||||
|
||||
42
core/cautils/getter/gcpcloudapi.go
Normal file
42
core/cautils/getter/gcpcloudapi.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package getter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
containeranalysis "cloud.google.com/go/containeranalysis/apiv1"
|
||||
)
|
||||
|
||||
type GCPCloudAPI struct {
|
||||
credentialsPath string
|
||||
context context.Context
|
||||
client *containeranalysis.Client
|
||||
projectID string
|
||||
credentialsCheck bool
|
||||
}
|
||||
|
||||
func GetGlobalGCPCloudAPIConnector() *GCPCloudAPI {
|
||||
|
||||
if os.Getenv("KS_GCP_CREDENTIALS_PATH") == "" || os.Getenv("KS_GCP_PROJECT_ID") == "" {
|
||||
return &GCPCloudAPI{
|
||||
credentialsCheck: false,
|
||||
}
|
||||
} else {
|
||||
return &GCPCloudAPI{
|
||||
context: context.Background(),
|
||||
credentialsPath: os.Getenv("KS_GCP_CREDENTIALS_PATH"),
|
||||
projectID: os.Getenv("KS_GCP_PROJECT_ID"),
|
||||
credentialsCheck: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (api *GCPCloudAPI) SetClient(client *containeranalysis.Client) {
|
||||
api.client = client
|
||||
}
|
||||
|
||||
func (api *GCPCloudAPI) GetCredentialsPath() string { return api.credentialsPath }
|
||||
func (api *GCPCloudAPI) GetClient() *containeranalysis.Client { return api.client }
|
||||
func (api *GCPCloudAPI) GetProjectID() string { return api.projectID }
|
||||
func (api *GCPCloudAPI) GetCredentialsCheck() bool { return api.credentialsCheck }
|
||||
func (api *GCPCloudAPI) GetContext() context.Context { return api.context }
|
||||
@@ -6,19 +6,13 @@ import (
|
||||
"github.com/kubescape/opa-utils/reporthandling/attacktrack/v1alpha1"
|
||||
)
|
||||
|
||||
// supported listing
|
||||
type ListType string
|
||||
|
||||
const ListID ListType = "id"
|
||||
const ListName ListType = "name"
|
||||
|
||||
type IPolicyGetter interface {
|
||||
GetFramework(name string) (*reporthandling.Framework, error)
|
||||
GetFrameworks() ([]reporthandling.Framework, error)
|
||||
GetControl(name string) (*reporthandling.Control, error)
|
||||
|
||||
ListFrameworks() ([]string, error)
|
||||
ListControls(ListType) ([]string, error)
|
||||
ListControls() ([]string, error)
|
||||
}
|
||||
|
||||
type IExceptionsGetter interface {
|
||||
@@ -28,10 +22,18 @@ type IBackend interface {
|
||||
GetAccountID() string
|
||||
GetClientID() string
|
||||
GetSecretKey() string
|
||||
GetCloudReportURL() string
|
||||
GetCloudAPIURL() string
|
||||
GetCloudUIURL() string
|
||||
GetCloudAuthURL() string
|
||||
|
||||
SetAccountID(accountID string)
|
||||
SetClientID(clientID string)
|
||||
SetSecretKey(secretKey string)
|
||||
SetCloudReportURL(cloudReportURL string)
|
||||
SetCloudAPIURL(cloudAPIURL string)
|
||||
SetCloudUIURL(cloudUIURL string)
|
||||
SetCloudAuthURL(cloudAuthURL string)
|
||||
|
||||
GetTenant() (*TenantResponse, error)
|
||||
}
|
||||
|
||||
@@ -4,14 +4,12 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/armosec/armoapi-go/armotypes"
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/opa-utils/reporthandling"
|
||||
"github.com/kubescape/opa-utils/reporthandling/attacktrack/v1alpha1"
|
||||
)
|
||||
@@ -35,23 +33,22 @@ var (
|
||||
|
||||
// KSCloudAPI allows accessing the API of the Kubescape Cloud offering
|
||||
type KSCloudAPI struct {
|
||||
httpClient *http.Client
|
||||
apiURL string
|
||||
authURL string
|
||||
erURL string
|
||||
feURL string
|
||||
accountID string
|
||||
clientID string
|
||||
secretKey string
|
||||
authCookie string
|
||||
feToken FeLoginResponse
|
||||
loggedIn bool
|
||||
httpClient *http.Client
|
||||
cloudAPIURL string
|
||||
cloudAuthURL string
|
||||
cloudReportURL string
|
||||
cloudUIURL string
|
||||
accountID string
|
||||
clientID string
|
||||
secretKey string
|
||||
authCookie string
|
||||
feToken FeLoginResponse
|
||||
loggedIn bool
|
||||
}
|
||||
|
||||
var globalKSCloudAPIConnector *KSCloudAPI
|
||||
|
||||
func SetKSCloudAPIConnector(ksCloudAPI *KSCloudAPI) {
|
||||
logger.L().Debug("Kubescape Cloud URLs", helpers.String("api", ksCloudAPI.apiURL), helpers.String("auth", ksCloudAPI.authURL), helpers.String("report", ksCloudAPI.erURL), helpers.String("UI", ksCloudAPI.feURL))
|
||||
globalKSCloudAPIConnector = ksCloudAPI
|
||||
}
|
||||
|
||||
@@ -65,10 +62,10 @@ func GetKSCloudAPIConnector() *KSCloudAPI {
|
||||
func NewKSCloudAPIDev() *KSCloudAPI {
|
||||
apiObj := newKSCloudAPI()
|
||||
|
||||
apiObj.apiURL = ksCloudDevBEURL
|
||||
apiObj.authURL = ksCloudDevAUTHURL
|
||||
apiObj.erURL = ksCloudDevERURL
|
||||
apiObj.feURL = ksCloudDevFEURL
|
||||
apiObj.cloudAPIURL = ksCloudDevBEURL
|
||||
apiObj.cloudAuthURL = ksCloudDevAUTHURL
|
||||
apiObj.cloudReportURL = ksCloudDevERURL
|
||||
apiObj.cloudUIURL = ksCloudDevFEURL
|
||||
|
||||
return apiObj
|
||||
}
|
||||
@@ -76,10 +73,10 @@ func NewKSCloudAPIDev() *KSCloudAPI {
|
||||
func NewKSCloudAPIProd() *KSCloudAPI {
|
||||
apiObj := newKSCloudAPI()
|
||||
|
||||
apiObj.apiURL = ksCloudBEURL
|
||||
apiObj.erURL = ksCloudERURL
|
||||
apiObj.feURL = ksCloudFEURL
|
||||
apiObj.authURL = ksCloudAUTHURL
|
||||
apiObj.cloudAPIURL = ksCloudBEURL
|
||||
apiObj.cloudReportURL = ksCloudERURL
|
||||
apiObj.cloudUIURL = ksCloudFEURL
|
||||
apiObj.cloudAuthURL = ksCloudAUTHURL
|
||||
|
||||
return apiObj
|
||||
}
|
||||
@@ -87,10 +84,10 @@ func NewKSCloudAPIProd() *KSCloudAPI {
|
||||
func NewKSCloudAPIStaging() *KSCloudAPI {
|
||||
apiObj := newKSCloudAPI()
|
||||
|
||||
apiObj.apiURL = ksCloudStageBEURL
|
||||
apiObj.erURL = ksCloudStageERURL
|
||||
apiObj.feURL = ksCloudStageFEURL
|
||||
apiObj.authURL = ksCloudStageAUTHURL
|
||||
apiObj.cloudAPIURL = ksCloudStageBEURL
|
||||
apiObj.cloudReportURL = ksCloudStageERURL
|
||||
apiObj.cloudUIURL = ksCloudStageFEURL
|
||||
apiObj.cloudAuthURL = ksCloudStageAUTHURL
|
||||
|
||||
return apiObj
|
||||
}
|
||||
@@ -98,10 +95,10 @@ func NewKSCloudAPIStaging() *KSCloudAPI {
|
||||
func NewKSCloudAPICustomized(ksCloudERURL, ksCloudBEURL, ksCloudFEURL, ksCloudAUTHURL string) *KSCloudAPI {
|
||||
apiObj := newKSCloudAPI()
|
||||
|
||||
apiObj.erURL = ksCloudERURL
|
||||
apiObj.apiURL = ksCloudBEURL
|
||||
apiObj.feURL = ksCloudFEURL
|
||||
apiObj.authURL = ksCloudAUTHURL
|
||||
apiObj.cloudReportURL = ksCloudERURL
|
||||
apiObj.cloudAPIURL = ksCloudBEURL
|
||||
apiObj.cloudUIURL = ksCloudFEURL
|
||||
apiObj.cloudAuthURL = ksCloudAUTHURL
|
||||
|
||||
return apiObj
|
||||
}
|
||||
@@ -136,17 +133,22 @@ func (api *KSCloudAPI) Get(fullURL string, headers map[string]string) (string, e
|
||||
return HttpGetter(api.httpClient, fullURL, headers)
|
||||
}
|
||||
|
||||
func (api *KSCloudAPI) GetAccountID() string { return api.accountID }
|
||||
func (api *KSCloudAPI) IsLoggedIn() bool { return api.loggedIn }
|
||||
func (api *KSCloudAPI) GetClientID() string { return api.clientID }
|
||||
func (api *KSCloudAPI) GetSecretKey() string { return api.secretKey }
|
||||
func (api *KSCloudAPI) GetFrontendURL() string { return api.feURL }
|
||||
func (api *KSCloudAPI) GetApiURL() string { return api.apiURL }
|
||||
func (api *KSCloudAPI) GetAuthURL() string { return api.authURL }
|
||||
func (api *KSCloudAPI) GetReportReceiverURL() string { return api.erURL }
|
||||
func (api *KSCloudAPI) SetAccountID(accountID string) { api.accountID = accountID }
|
||||
func (api *KSCloudAPI) SetClientID(clientID string) { api.clientID = clientID }
|
||||
func (api *KSCloudAPI) SetSecretKey(secretKey string) { api.secretKey = secretKey }
|
||||
func (api *KSCloudAPI) GetAccountID() string { return api.accountID }
|
||||
func (api *KSCloudAPI) IsLoggedIn() bool { return api.loggedIn }
|
||||
func (api *KSCloudAPI) GetClientID() string { return api.clientID }
|
||||
func (api *KSCloudAPI) GetSecretKey() string { return api.secretKey }
|
||||
func (api *KSCloudAPI) GetCloudReportURL() string { return api.cloudReportURL }
|
||||
func (api *KSCloudAPI) GetCloudAPIURL() string { return api.cloudAPIURL }
|
||||
func (api *KSCloudAPI) GetCloudUIURL() string { return api.cloudUIURL }
|
||||
func (api *KSCloudAPI) GetCloudAuthURL() string { return api.cloudAuthURL }
|
||||
|
||||
func (api *KSCloudAPI) SetAccountID(accountID string) { api.accountID = accountID }
|
||||
func (api *KSCloudAPI) SetClientID(clientID string) { api.clientID = clientID }
|
||||
func (api *KSCloudAPI) SetSecretKey(secretKey string) { api.secretKey = secretKey }
|
||||
func (api *KSCloudAPI) SetCloudReportURL(cloudReportURL string) { api.cloudReportURL = cloudReportURL }
|
||||
func (api *KSCloudAPI) SetCloudAPIURL(cloudAPIURL string) { api.cloudAPIURL = cloudAPIURL }
|
||||
func (api *KSCloudAPI) SetCloudUIURL(cloudUIURL string) { api.cloudUIURL = cloudUIURL }
|
||||
func (api *KSCloudAPI) SetCloudAuthURL(cloudAuthURL string) { api.cloudAuthURL = cloudAuthURL }
|
||||
|
||||
func (api *KSCloudAPI) GetAttackTracks() ([]v1alpha1.AttackTrack, error) {
|
||||
respStr, err := api.Get(api.getAttackTracksURL(), nil)
|
||||
@@ -304,7 +306,7 @@ func (api *KSCloudAPI) ListFrameworks() ([]string, error) {
|
||||
return frameworkList, nil
|
||||
}
|
||||
|
||||
func (api *KSCloudAPI) ListControls(l ListType) ([]string, error) {
|
||||
func (api *KSCloudAPI) ListControls() ([]string, error) {
|
||||
return nil, fmt.Errorf("control api is not public")
|
||||
}
|
||||
|
||||
@@ -356,7 +358,7 @@ func (api *KSCloudAPI) Login() error {
|
||||
return fmt.Errorf("error authenticating: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
responseBody, err := ioutil.ReadAll(resp.Body)
|
||||
responseBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ var NativeFrameworks = []string{"nsa", "mitre", "armobest", "devopsbest"}
|
||||
|
||||
func (api *KSCloudAPI) getFrameworkURL(frameworkName string) string {
|
||||
u := url.URL{}
|
||||
u.Scheme, u.Host = parseHost(api.GetApiURL())
|
||||
u.Scheme, u.Host = parseHost(api.GetCloudAPIURL())
|
||||
u.Path = "api/v1/armoFrameworks"
|
||||
q := u.Query()
|
||||
q.Add("customerGUID", api.getCustomerGUIDFallBack())
|
||||
@@ -30,7 +30,7 @@ func (api *KSCloudAPI) getFrameworkURL(frameworkName string) string {
|
||||
|
||||
func (api *KSCloudAPI) getAttackTracksURL() string {
|
||||
u := url.URL{}
|
||||
u.Scheme, u.Host = parseHost(api.GetApiURL())
|
||||
u.Scheme, u.Host = parseHost(api.GetCloudAPIURL())
|
||||
u.Path = "api/v1/attackTracks"
|
||||
q := u.Query()
|
||||
q.Add("customerGUID", api.getCustomerGUIDFallBack())
|
||||
@@ -41,7 +41,7 @@ func (api *KSCloudAPI) getAttackTracksURL() string {
|
||||
|
||||
func (api *KSCloudAPI) getListFrameworkURL() string {
|
||||
u := url.URL{}
|
||||
u.Scheme, u.Host = parseHost(api.GetApiURL())
|
||||
u.Scheme, u.Host = parseHost(api.GetCloudAPIURL())
|
||||
u.Path = "api/v1/armoFrameworks"
|
||||
q := u.Query()
|
||||
q.Add("customerGUID", api.getCustomerGUIDFallBack())
|
||||
@@ -51,7 +51,7 @@ func (api *KSCloudAPI) getListFrameworkURL() string {
|
||||
}
|
||||
func (api *KSCloudAPI) getExceptionsURL(clusterName string) string {
|
||||
u := url.URL{}
|
||||
u.Scheme, u.Host = parseHost(api.GetApiURL())
|
||||
u.Scheme, u.Host = parseHost(api.GetCloudAPIURL())
|
||||
u.Path = "api/v1/armoPostureExceptions"
|
||||
|
||||
q := u.Query()
|
||||
@@ -66,7 +66,7 @@ func (api *KSCloudAPI) getExceptionsURL(clusterName string) string {
|
||||
|
||||
func (api *KSCloudAPI) exceptionsURL(exceptionsPolicyName string) string {
|
||||
u := url.URL{}
|
||||
u.Scheme, u.Host = parseHost(api.GetApiURL())
|
||||
u.Scheme, u.Host = parseHost(api.GetCloudAPIURL())
|
||||
u.Path = "api/v1/postureExceptionPolicy"
|
||||
|
||||
q := u.Query()
|
||||
@@ -88,7 +88,7 @@ func (api *KSCloudAPI) getAccountConfigDefault(clusterName string) string {
|
||||
|
||||
func (api *KSCloudAPI) getAccountConfig(clusterName string) string {
|
||||
u := url.URL{}
|
||||
u.Scheme, u.Host = parseHost(api.GetApiURL())
|
||||
u.Scheme, u.Host = parseHost(api.GetCloudAPIURL())
|
||||
u.Path = "api/v1/armoCustomerConfiguration"
|
||||
|
||||
q := u.Query()
|
||||
@@ -103,21 +103,21 @@ func (api *KSCloudAPI) getAccountConfig(clusterName string) string {
|
||||
|
||||
func (api *KSCloudAPI) getAccountURL() string {
|
||||
u := url.URL{}
|
||||
u.Scheme, u.Host = parseHost(api.GetApiURL())
|
||||
u.Scheme, u.Host = parseHost(api.GetCloudAPIURL())
|
||||
u.Path = "api/v1/createTenant"
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func (api *KSCloudAPI) getApiToken() string {
|
||||
u := url.URL{}
|
||||
u.Scheme, u.Host = parseHost(api.GetAuthURL())
|
||||
u.Scheme, u.Host = parseHost(api.GetCloudAuthURL())
|
||||
u.Path = "identity/resources/auth/v1/api-token"
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func (api *KSCloudAPI) getOpenidCustomers() string {
|
||||
u := url.URL{}
|
||||
u.Scheme, u.Host = parseHost(api.GetApiURL())
|
||||
u.Scheme, u.Host = parseHost(api.GetCloudAPIURL())
|
||||
u.Path = "api/v1/openid_customers"
|
||||
return u.String()
|
||||
}
|
||||
|
||||
@@ -65,16 +65,16 @@ func (lp *LoadPolicy) GetControl(controlName string) (*reporthandling.Control, e
|
||||
}
|
||||
|
||||
func (lp *LoadPolicy) GetFramework(frameworkName string) (*reporthandling.Framework, error) {
|
||||
framework := &reporthandling.Framework{}
|
||||
var framework reporthandling.Framework
|
||||
var err error
|
||||
for _, filePath := range lp.filePaths {
|
||||
framework = reporthandling.Framework{}
|
||||
f, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = json.Unmarshal(f, framework); err != nil {
|
||||
return framework, err
|
||||
if err = json.Unmarshal(f, &framework); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if strings.EqualFold(frameworkName, framework.Name) {
|
||||
break
|
||||
@@ -84,7 +84,7 @@ func (lp *LoadPolicy) GetFramework(frameworkName string) (*reporthandling.Framew
|
||||
|
||||
return nil, fmt.Errorf("framework from file not matching")
|
||||
}
|
||||
return framework, err
|
||||
return &framework, err
|
||||
}
|
||||
|
||||
func (lp *LoadPolicy) GetFrameworks() ([]reporthandling.Framework, error) {
|
||||
@@ -109,7 +109,7 @@ func (lp *LoadPolicy) ListFrameworks() ([]string, error) {
|
||||
return fwNames, nil
|
||||
}
|
||||
|
||||
func (lp *LoadPolicy) ListControls(listType ListType) ([]string, error) {
|
||||
func (lp *LoadPolicy) ListControls() ([]string, error) {
|
||||
// TODO - Support
|
||||
return []string{}, fmt.Errorf("loading controls list from file is not supported")
|
||||
}
|
||||
@@ -130,14 +130,19 @@ func (lp *LoadPolicy) GetControlsInputs(clusterName string) (map[string][]string
|
||||
filePath := lp.filePath()
|
||||
accountConfig := &armotypes.CustomerConfig{}
|
||||
f, err := os.ReadFile(filePath)
|
||||
fileName := filepath.Base(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
formattedError := fmt.Errorf("Error opening %s file, \"controls-config\" will be downloaded from ARMO management portal", fileName)
|
||||
return nil, formattedError
|
||||
}
|
||||
|
||||
if err = json.Unmarshal(f, &accountConfig.Settings.PostureControlInputs); err == nil {
|
||||
return accountConfig.Settings.PostureControlInputs, nil
|
||||
}
|
||||
return nil, err
|
||||
|
||||
formattedError := fmt.Errorf("Error reading %s file, %s, \"controls-config\" will be downloaded from ARMO management portal", fileName, err.Error())
|
||||
|
||||
return nil, formattedError
|
||||
}
|
||||
|
||||
// temporary support for a list of files
|
||||
|
||||
@@ -3,7 +3,6 @@ package cautils
|
||||
import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -39,7 +38,7 @@ func (s *HelmChartTestSuite) SetupSuite() {
|
||||
}
|
||||
|
||||
var obj interface{}
|
||||
file, _ := ioutil.ReadFile(filepath.Join("testdata", "helm_expected_default_values.json"))
|
||||
file, _ := os.ReadFile(filepath.Join("testdata", "helm_expected_default_values.json"))
|
||||
_ = json.Unmarshal([]byte(file), &obj)
|
||||
s.expectedDefaultValues = obj.(map[string]interface{})
|
||||
}
|
||||
|
||||
@@ -15,7 +15,12 @@ type RootInfo struct {
|
||||
|
||||
KSCloudBEURLs string // Kubescape Cloud URL
|
||||
KSCloudBEURLsDep string // Kubescape Cloud URL
|
||||
|
||||
}
|
||||
type CloudURLs struct {
|
||||
CloudReportURL string
|
||||
CloudAPIURL string
|
||||
CloudUIURL string
|
||||
CloudAuthURL string
|
||||
}
|
||||
|
||||
type Credentials struct {
|
||||
|
||||
@@ -3,7 +3,6 @@ package cautils
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -159,7 +158,7 @@ func (scanInfo *ScanInfo) setUseArtifactsFrom() {
|
||||
scanInfo.UseArtifactsFrom = dir
|
||||
}
|
||||
// set frameworks files
|
||||
files, err := ioutil.ReadDir(scanInfo.UseArtifactsFrom)
|
||||
files, err := os.ReadDir(scanInfo.UseArtifactsFrom)
|
||||
if err != nil {
|
||||
logger.L().Fatal("failed to read files from directory", helpers.String("dir", scanInfo.UseArtifactsFrom), helpers.Error(err))
|
||||
}
|
||||
@@ -419,6 +418,7 @@ func metadataGitLocal(input string) (*reporthandlingv2.RepoContextMetadata, erro
|
||||
Date: commit.Committer.Date,
|
||||
CommitterName: commit.Committer.Name,
|
||||
}
|
||||
context.LocalRootPath = getAbsPath(input)
|
||||
|
||||
return context, nil
|
||||
}
|
||||
|
||||
@@ -18,7 +18,8 @@ func TestSetContextMetadata(t *testing.T) {
|
||||
assert.Nil(t, ctx.HelmContextMetadata)
|
||||
assert.Nil(t, ctx.RepoContextMetadata)
|
||||
}
|
||||
{
|
||||
// TODO: tests were commented out due to actual http calls ; http calls should be mocked.
|
||||
/*{
|
||||
ctx := reporthandlingv2.ContextMetadata{}
|
||||
setContextMetadata(&ctx, "https://github.com/kubescape/kubescape")
|
||||
|
||||
@@ -31,7 +32,7 @@ func TestSetContextMetadata(t *testing.T) {
|
||||
assert.Equal(t, "kubescape", ctx.RepoContextMetadata.Repo)
|
||||
assert.Equal(t, "kubescape", ctx.RepoContextMetadata.Owner)
|
||||
assert.Equal(t, "master", ctx.RepoContextMetadata.Branch)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
func TestGetHostname(t *testing.T) {
|
||||
|
||||
@@ -19,6 +19,7 @@ const SKIP_VERSION_CHECK = "KS_SKIP_UPDATE_CHECK"
|
||||
|
||||
var BuildNumber string
|
||||
var Client string
|
||||
var LatestReleaseVersion string
|
||||
|
||||
const UnknownBuildNumber = "unknown"
|
||||
|
||||
@@ -108,9 +109,11 @@ func (v *VersionCheckHandler) CheckLatestVersion(versionData *VersionCheckReques
|
||||
return fmt.Errorf("failed to get latest version")
|
||||
}
|
||||
|
||||
LatestReleaseVersion := latestVersion.ClientUpdate
|
||||
|
||||
if latestVersion.ClientUpdate != "" {
|
||||
if BuildNumber != "" && semver.Compare(BuildNumber, latestVersion.ClientUpdate) == -1 {
|
||||
logger.L().Warning(warningMessage(latestVersion.ClientUpdate))
|
||||
if BuildNumber != "" && semver.Compare(BuildNumber, LatestReleaseVersion) == -1 {
|
||||
logger.L().Warning(warningMessage(LatestReleaseVersion))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package cautils
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/kubescape/k8s-interface/cloudsupport"
|
||||
"github.com/kubescape/opa-utils/reporthandling/apis"
|
||||
)
|
||||
|
||||
@@ -17,8 +18,12 @@ var (
|
||||
"LinuxKernelVariables",
|
||||
"KubeletInfo",
|
||||
"KubeProxyInfo",
|
||||
"ControlPlaneInfo",
|
||||
}
|
||||
CloudResources = []string{
|
||||
"ClusterDescribe",
|
||||
string(cloudsupport.TypeApiServerInfo),
|
||||
}
|
||||
CloudResources = []string{"ClusterDescribe"}
|
||||
)
|
||||
|
||||
func MapKSResource(ksResourceMap *KSResources, resources []string) []string {
|
||||
|
||||
@@ -19,6 +19,18 @@ func (ks *Kubescape) SetCachedConfig(setConfig *metav1.SetConfig) error {
|
||||
if setConfig.ClientID != "" {
|
||||
tenant.GetConfigObj().ClientID = setConfig.ClientID
|
||||
}
|
||||
if setConfig.CloudAPIURL != "" {
|
||||
tenant.GetConfigObj().CloudAPIURL = setConfig.CloudAPIURL
|
||||
}
|
||||
if setConfig.CloudAuthURL != "" {
|
||||
tenant.GetConfigObj().CloudAuthURL = setConfig.CloudAuthURL
|
||||
}
|
||||
if setConfig.CloudReportURL != "" {
|
||||
tenant.GetConfigObj().CloudReportURL = setConfig.CloudReportURL
|
||||
}
|
||||
if setConfig.CloudUIURL != "" {
|
||||
tenant.GetConfigObj().CloudUIURL = setConfig.CloudUIURL
|
||||
}
|
||||
|
||||
return tenant.UpdateCachedConfig()
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ func downloadExceptions(downloadInfo *metav1.DownloadInfo) error {
|
||||
var err error
|
||||
tenant := getTenantConfig(&downloadInfo.Credentials, "", "", getKubernetesApi())
|
||||
|
||||
exceptionsGetter := getExceptionsGetter("")
|
||||
exceptionsGetter := getExceptionsGetter("", tenant.GetAccountID(), nil)
|
||||
exceptions := []armotypes.PostureExceptionPolicy{}
|
||||
if tenant.GetAccountID() != "" {
|
||||
exceptions, err = exceptionsGetter.GetExceptions(tenant.GetContextName())
|
||||
|
||||
@@ -32,13 +32,24 @@ func getTenantConfig(credentials *cautils.Credentials, clusterName string, custo
|
||||
return cautils.NewClusterConfig(k8s, getter.GetKSCloudAPIConnector(), credentials, clusterName, customClusterName)
|
||||
}
|
||||
|
||||
func getExceptionsGetter(useExceptions string) getter.IExceptionsGetter {
|
||||
func getExceptionsGetter(useExceptions string, accountID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IExceptionsGetter {
|
||||
if useExceptions != "" {
|
||||
// load exceptions from file
|
||||
return getter.NewLoadPolicy([]string{useExceptions})
|
||||
} else {
|
||||
}
|
||||
if accountID != "" {
|
||||
// download exceptions from Kubescape Cloud backend
|
||||
return getter.GetKSCloudAPIConnector()
|
||||
}
|
||||
// download exceptions from GitHub
|
||||
if downloadReleasedPolicy == nil {
|
||||
downloadReleasedPolicy = getter.NewDownloadReleasedPolicy()
|
||||
}
|
||||
if err := downloadReleasedPolicy.SetRegoObjects(); err != nil {
|
||||
logger.L().Warning("failed to get exceptions from github release, this may affect the scanning results", helpers.Error(err))
|
||||
}
|
||||
return downloadReleasedPolicy
|
||||
|
||||
}
|
||||
|
||||
func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, submit bool) *cautils.RBACObjects {
|
||||
@@ -58,7 +69,7 @@ func getReporter(tenantConfig cautils.ITenantConfig, reportID string, submit, fw
|
||||
}
|
||||
if tenantConfig.GetAccountID() == "" {
|
||||
// Add link only when scanning a cluster using a framework
|
||||
return reporterv2.NewReportMock(reporterv2.NO_SUBMIT_QUERY, "run kubescape with the '--submit' flag")
|
||||
return reporterv2.NewReportMock("https://hub.armosec.io/docs/installing-kubescape", "run kubescape with the '--account' flag")
|
||||
}
|
||||
var message string
|
||||
if !fwScan {
|
||||
@@ -128,16 +139,21 @@ func policyIdentifierNames(pi []cautils.PolicyIdentifier) string {
|
||||
func setSubmitBehavior(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig) {
|
||||
|
||||
/*
|
||||
If "First run (local config not found)" -
|
||||
Default/keep-local - Do not send report
|
||||
Submit - Create tenant & Submit report
|
||||
If CloudReportURL not set - Do not send report
|
||||
|
||||
If "Submitted" -
|
||||
If There is no account - Do not send report
|
||||
|
||||
If There is account -
|
||||
keep-local - Do not send report
|
||||
Default/Submit - Submit report
|
||||
Default - Submit report
|
||||
|
||||
*/
|
||||
|
||||
if getter.GetKSCloudAPIConnector().GetCloudAPIURL() == "" {
|
||||
scanInfo.Submit = false
|
||||
return
|
||||
}
|
||||
|
||||
// do not submit control scanning
|
||||
if !scanInfo.FrameworkScan {
|
||||
scanInfo.Submit = false
|
||||
@@ -150,27 +166,26 @@ func setSubmitBehavior(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantC
|
||||
return
|
||||
}
|
||||
|
||||
if tenantConfig.IsConfigFound() { // config found in cache (submitted)
|
||||
if !scanInfo.Local {
|
||||
if tenantConfig.GetAccountID() != "" {
|
||||
if _, err := uuid.Parse(tenantConfig.GetAccountID()); err != nil {
|
||||
scanInfo.Submit = false
|
||||
return
|
||||
}
|
||||
}
|
||||
// Submit report
|
||||
scanInfo.Submit = true
|
||||
}
|
||||
if scanInfo.Local {
|
||||
scanInfo.Submit = false
|
||||
return
|
||||
}
|
||||
|
||||
// If There is no account, or if the account is not legal, do not submit
|
||||
if _, err := uuid.Parse(tenantConfig.GetAccountID()); err != nil {
|
||||
scanInfo.Submit = false
|
||||
} else {
|
||||
scanInfo.Submit = true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// setPolicyGetter set the policy getter - local file/github release/Kubescape Cloud API
|
||||
func getPolicyGetter(loadPoliciesFromFile []string, tennatEmail string, frameworkScope bool, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IPolicyGetter {
|
||||
func getPolicyGetter(loadPoliciesFromFile []string, tenantEmail string, frameworkScope bool, downloadReleasedPolicy *getter.DownloadReleasedPolicy) getter.IPolicyGetter {
|
||||
if len(loadPoliciesFromFile) > 0 {
|
||||
return getter.NewLoadPolicy(loadPoliciesFromFile)
|
||||
}
|
||||
if tennatEmail != "" && frameworkScope {
|
||||
if tenantEmail != "" && getter.GetKSCloudAPIConnector().GetCloudAPIURL() != "" && frameworkScope {
|
||||
g := getter.GetKSCloudAPIConnector() // download policy from Kubescape Cloud backend
|
||||
return g
|
||||
}
|
||||
|
||||
@@ -6,8 +6,11 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/kubescape/kubescape/v2/core/cautils/getter"
|
||||
"github.com/kubescape/kubescape/v2/core/cautils"
|
||||
metav1 "github.com/kubescape/kubescape/v2/core/meta/datastructures/v1"
|
||||
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer"
|
||||
v2 "github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer/v2"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
var listFunc = map[string]func(*metav1.ListPolicies) ([]string, error){
|
||||
@@ -16,7 +19,7 @@ var listFunc = map[string]func(*metav1.ListPolicies) ([]string, error){
|
||||
"exceptions": listExceptions,
|
||||
}
|
||||
|
||||
var listFormatFunc = map[string]func(*metav1.ListPolicies, []string){
|
||||
var listFormatFunc = map[string]func(string, []string){
|
||||
"pretty-print": prettyPrintListFormat,
|
||||
"json": jsonListFormat,
|
||||
}
|
||||
@@ -29,14 +32,18 @@ func ListSupportActions() []string {
|
||||
return commands
|
||||
}
|
||||
func (ks *Kubescape) List(listPolicies *metav1.ListPolicies) error {
|
||||
if f, ok := listFunc[listPolicies.Target]; ok {
|
||||
policies, err := f(listPolicies)
|
||||
if policyListerFunc, ok := listFunc[listPolicies.Target]; ok {
|
||||
policies, err := policyListerFunc(listPolicies)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sort.Strings(policies)
|
||||
|
||||
listFormatFunc[listPolicies.Format](listPolicies, policies)
|
||||
if listFormatFunction, ok := listFormatFunc[listPolicies.Format]; ok {
|
||||
listFormatFunction(listPolicies.Target, policies)
|
||||
} else {
|
||||
return fmt.Errorf("Invalid format \"%s\", Supported formats: 'pretty-print'/'json' ", listPolicies.Format)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -45,28 +52,24 @@ func (ks *Kubescape) List(listPolicies *metav1.ListPolicies) error {
|
||||
|
||||
func listFrameworks(listPolicies *metav1.ListPolicies) ([]string, error) {
|
||||
tenant := getTenantConfig(&listPolicies.Credentials, "", "", getKubernetesApi()) // change k8sinterface
|
||||
g := getPolicyGetter(nil, tenant.GetTenantEmail(), true, nil)
|
||||
policyGetter := getPolicyGetter(nil, tenant.GetTenantEmail(), true, nil)
|
||||
|
||||
return listFrameworksNames(g), nil
|
||||
return listFrameworksNames(policyGetter), nil
|
||||
}
|
||||
|
||||
func listControls(listPolicies *metav1.ListPolicies) ([]string, error) {
|
||||
tenant := getTenantConfig(&listPolicies.Credentials, "", "", getKubernetesApi()) // change k8sinterface
|
||||
|
||||
g := getPolicyGetter(nil, tenant.GetTenantEmail(), false, nil)
|
||||
l := getter.ListName
|
||||
if listPolicies.ListIDs {
|
||||
l = getter.ListID
|
||||
}
|
||||
return g.ListControls(l)
|
||||
policyGetter := getPolicyGetter(nil, tenant.GetTenantEmail(), false, nil)
|
||||
return policyGetter.ListControls()
|
||||
}
|
||||
|
||||
func listExceptions(listPolicies *metav1.ListPolicies) ([]string, error) {
|
||||
// load tenant metav1
|
||||
getTenantConfig(&listPolicies.Credentials, "", "", getKubernetesApi())
|
||||
tenant := getTenantConfig(&listPolicies.Credentials, "", "", getKubernetesApi())
|
||||
|
||||
var exceptionsNames []string
|
||||
ksCloudAPI := getExceptionsGetter("")
|
||||
ksCloudAPI := getExceptionsGetter("", tenant.GetAccountID(), nil)
|
||||
exceptions, err := ksCloudAPI.GetExceptions("")
|
||||
if err != nil {
|
||||
return exceptionsNames, err
|
||||
@@ -77,12 +80,73 @@ func listExceptions(listPolicies *metav1.ListPolicies) ([]string, error) {
|
||||
return exceptionsNames, nil
|
||||
}
|
||||
|
||||
func prettyPrintListFormat(listPolicies *metav1.ListPolicies, policies []string) {
|
||||
sep := "\n * "
|
||||
fmt.Printf("Supported %s:%s%s\n", listPolicies.Target, sep, strings.Join(policies, sep))
|
||||
func prettyPrintListFormat(targetPolicy string, policies []string) {
|
||||
if targetPolicy == "controls" {
|
||||
prettyPrintControls(policies)
|
||||
return
|
||||
}
|
||||
|
||||
header := fmt.Sprintf("Supported %s", targetPolicy)
|
||||
|
||||
policyTable := tablewriter.NewWriter(printer.GetWriter(""))
|
||||
policyTable.SetAutoWrapText(true)
|
||||
policyTable.SetHeader([]string{header})
|
||||
policyTable.SetHeaderLine(true)
|
||||
policyTable.SetRowLine(true)
|
||||
data := v2.Matrix{}
|
||||
|
||||
controlRows := generatePolicyRows(policies)
|
||||
data = append(data, controlRows...)
|
||||
|
||||
policyTable.SetAlignment(tablewriter.ALIGN_CENTER)
|
||||
policyTable.AppendBulk(data)
|
||||
policyTable.Render()
|
||||
}
|
||||
|
||||
func jsonListFormat(listPolicies *metav1.ListPolicies, policies []string) {
|
||||
func jsonListFormat(targetPolicy string, policies []string) {
|
||||
j, _ := json.MarshalIndent(policies, "", " ")
|
||||
|
||||
fmt.Printf("%s\n", j)
|
||||
}
|
||||
|
||||
func prettyPrintControls(policies []string) {
|
||||
controlsTable := tablewriter.NewWriter(printer.GetWriter(""))
|
||||
controlsTable.SetAutoWrapText(true)
|
||||
controlsTable.SetHeader([]string{"Control ID", "Control Name", "Docs"})
|
||||
controlsTable.SetHeaderLine(true)
|
||||
controlsTable.SetRowLine(true)
|
||||
data := v2.Matrix{}
|
||||
|
||||
controlRows := generateControlRows(policies)
|
||||
data = append(data, controlRows...)
|
||||
|
||||
controlsTable.AppendBulk(data)
|
||||
controlsTable.Render()
|
||||
}
|
||||
|
||||
func generateControlRows(policies []string) [][]string {
|
||||
rows := [][]string{}
|
||||
|
||||
for _, control := range policies {
|
||||
idAndControl := strings.Split(control, "|")
|
||||
id, control := idAndControl[0], idAndControl[1]
|
||||
|
||||
docs := cautils.GetControlLink(id)
|
||||
|
||||
currentRow := []string{id, control, docs}
|
||||
|
||||
rows = append(rows, currentRow)
|
||||
}
|
||||
|
||||
return rows
|
||||
}
|
||||
|
||||
func generatePolicyRows(policies []string) [][]string {
|
||||
rows := [][]string{}
|
||||
|
||||
for _, policy := range policies {
|
||||
currentRow := []string{policy}
|
||||
rows = append(rows, currentRow)
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsH
|
||||
// set policy getter only after setting the customerGUID
|
||||
scanInfo.Getters.PolicyGetter = getPolicyGetter(scanInfo.UseFrom, interfaces.tenantConfig.GetTenantEmail(), scanInfo.FrameworkScan, downloadReleasedPolicy)
|
||||
scanInfo.Getters.ControlsInputsGetter = getConfigInputsGetter(scanInfo.ControlsInputs, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
|
||||
scanInfo.Getters.ExceptionsGetter = getExceptionsGetter(scanInfo.UseExceptions)
|
||||
scanInfo.Getters.ExceptionsGetter = getExceptionsGetter(scanInfo.UseExceptions, interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
|
||||
scanInfo.Getters.AttackTracksGetter = getAttackTracksGetter(interfaces.tenantConfig.GetAccountID(), downloadReleasedPolicy)
|
||||
|
||||
// TODO - list supported frameworks/controls
|
||||
|
||||
@@ -3,9 +3,13 @@ package v1
|
||||
import "io"
|
||||
|
||||
type SetConfig struct {
|
||||
Account string
|
||||
ClientID string
|
||||
SecretKey string
|
||||
Account string
|
||||
ClientID string
|
||||
SecretKey string
|
||||
CloudReportURL string
|
||||
CloudAPIURL string
|
||||
CloudUIURL string
|
||||
CloudAuthURL string
|
||||
}
|
||||
|
||||
type ViewConfig struct {
|
||||
|
||||
@@ -4,7 +4,6 @@ import "github.com/kubescape/kubescape/v2/core/cautils"
|
||||
|
||||
type ListPolicies struct {
|
||||
Target string
|
||||
ListIDs bool
|
||||
Format string
|
||||
Credentials cautils.Credentials
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/kubescape/opa-utils/reporthandling"
|
||||
)
|
||||
|
||||
var mockControl_0006 = `{"guid":"","name":"Allowed hostPath","attributes":{"armoBuiltin":true},"id":"C-0006","controlID":"C-0006","creationTime":"","description":"Mounting host directory to the container can be abused to get access to sensitive data and gain persistence on the host machine.","remediation":"Refrain from using host path mount.","rules":[{"guid":"","name":"alert-rw-hostpath","attributes":{"armoBuiltin":true,"m$K8sThreatMatrix":"Persistence::Writable hostPath mount, Lateral Movement::Writable volume mounts on the host"},"creationTime":"","rule":"package armo_builtins\n\n# input: pod\n# apiversion: v1\n# does: returns hostPath volumes\n\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n volumes := pod.spec.volumes\n volume := volumes[_]\n volume.hostPath\n\tcontainer := pod.spec.containers[i]\n\tvolumeMount := container.volumeMounts[k]\n\tvolumeMount.name == volume.name\n\tbegginingOfPath := \"spec.\"\n\tresult := isRWMount(volumeMount, begginingOfPath, i, k)\n\n podname := pod.metadata.name\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"pod: %v has: %v as hostPath volume\", [podname, volume.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": [result],\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n#handles majority of workload resources\ndeny[msga] {\n\twl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n volumes := wl.spec.template.spec.volumes\n volume := volumes[_]\n volume.hostPath\n\tcontainer := wl.spec.template.spec.containers[i]\n\tvolumeMount := container.volumeMounts[k]\n\tvolumeMount.name == volume.name\n\tbegginingOfPath := \"spec.template.spec.\"\n\tresult := isRWMount(volumeMount, begginingOfPath, i, k)\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"%v: %v has: %v as hostPath volume\", [wl.kind, wl.metadata.name, volume.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": [result],\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t\n\t}\n}\n\n#handles CronJobs\ndeny[msga] {\n\twl := input[_]\n\twl.kind == \"CronJob\"\n volumes := wl.spec.jobTemplate.spec.template.spec.volumes\n volume := volumes[_]\n volume.hostPath\n\n\tcontainer = wl.spec.jobTemplate.spec.template.spec.containers[i]\n\tvolumeMount := container.volumeMounts[k]\n\tvolumeMount.name == volume.name\n\tbegginingOfPath := \"spec.jobTemplate.spec.template.spec.\"\n\tresult := isRWMount(volumeMount, begginingOfPath, i, k)\n\n\tmsga := {\n\t\"alertMessage\": sprintf(\"%v: %v has: %v as hostPath volume\", [wl.kind, wl.metadata.name, volume.name]),\n\t\"packagename\": \"armo_builtins\",\n\t\"alertScore\": 7,\n\t\"failedPaths\": [result],\n\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\nisRWMount(mount, begginingOfPath, i, k) = path {\n not mount.readOnly == true\n not mount.readOnly == false\n path = \"\"\n}\nisRWMount(mount, begginingOfPath, i, k) = path {\n mount.readOnly == false\n path = sprintf(\"%vcontainers[%v].volumeMounts[%v].readOnly\", [begginingOfPath, format_int(i, 10), format_int(k, 10)])\n} ","resourceEnumerator":"","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","CronJob","Pod"]}],"ruleDependencies":[{"packageName":"cautils"},{"packageName":"kubernetes.api.client"}],"configInputs":null,"controlConfigInputs":null,"description":"determines if any workload contains a hostPath volume with rw permissions","remediation":"Set the readOnly field of the mount to true","ruleQuery":""}],"rulesIDs":[""],"baseScore":6}`
|
||||
var mockControl_0006 = `{"guid":"","name":"HostPath mount","attributes":{"armoBuiltin":true},"id":"C-0048","controlID":"C-0048","creationTime":"","description":"Mounting host directory to the container can be abused to get access to sensitive data and gain persistence on the host machine.","remediation":"Refrain from using host path mount.","rules":[{"guid":"","name":"alert-rw-hostpath","attributes":{"armoBuiltin":true,"m$K8sThreatMatrix":"Persistence::Writable hostPath mount, Lateral Movement::Writable volume mounts on the host"},"creationTime":"","rule":"package armo_builtins\n\n# input: pod\n# apiversion: v1\n# does: returns hostPath volumes\n\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n volumes := pod.spec.volumes\n volume := volumes[_]\n volume.hostPath\n\tcontainer := pod.spec.containers[i]\n\tvolumeMount := container.volumeMounts[k]\n\tvolumeMount.name == volume.name\n\tbegginingOfPath := \"spec.\"\n\tresult := isRWMount(volumeMount, begginingOfPath, i, k)\n\n podname := pod.metadata.name\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"pod: %v has: %v as hostPath volume\", [podname, volume.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": [result],\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n#handles majority of workload resources\ndeny[msga] {\n\twl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n volumes := wl.spec.template.spec.volumes\n volume := volumes[_]\n volume.hostPath\n\tcontainer := wl.spec.template.spec.containers[i]\n\tvolumeMount := container.volumeMounts[k]\n\tvolumeMount.name == volume.name\n\tbegginingOfPath := \"spec.template.spec.\"\n\tresult := isRWMount(volumeMount, begginingOfPath, i, k)\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"%v: %v has: %v as hostPath volume\", [wl.kind, wl.metadata.name, volume.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": [result],\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t\n\t}\n}\n\n#handles CronJobs\ndeny[msga] {\n\twl := input[_]\n\twl.kind == \"CronJob\"\n volumes := wl.spec.jobTemplate.spec.template.spec.volumes\n volume := volumes[_]\n volume.hostPath\n\n\tcontainer = wl.spec.jobTemplate.spec.template.spec.containers[i]\n\tvolumeMount := container.volumeMounts[k]\n\tvolumeMount.name == volume.name\n\tbegginingOfPath := \"spec.jobTemplate.spec.template.spec.\"\n\tresult := isRWMount(volumeMount, begginingOfPath, i, k)\n\n\tmsga := {\n\t\"alertMessage\": sprintf(\"%v: %v has: %v as hostPath volume\", [wl.kind, wl.metadata.name, volume.name]),\n\t\"packagename\": \"armo_builtins\",\n\t\"alertScore\": 7,\n\t\"failedPaths\": [result],\n\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\nisRWMount(mount, begginingOfPath, i, k) = path {\n not mount.readOnly == true\n not mount.readOnly == false\n path = \"\"\n}\nisRWMount(mount, begginingOfPath, i, k) = path {\n mount.readOnly == false\n path = sprintf(\"%vcontainers[%v].volumeMounts[%v].readOnly\", [begginingOfPath, format_int(i, 10), format_int(k, 10)])\n} ","resourceEnumerator":"","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","CronJob","Pod"]}],"ruleDependencies":[{"packageName":"cautils"},{"packageName":"kubernetes.api.client"}],"configInputs":null,"controlConfigInputs":null,"description":"determines if any workload contains a hostPath volume with rw permissions","remediation":"Set the readOnly field of the mount to true","ruleQuery":""}],"rulesIDs":[""],"baseScore":6}`
|
||||
|
||||
var mockControl_0044 = `{"guid":"","name":"Container hostPort","attributes":{"armoBuiltin":true},"id":"C-0044","controlID":"C-0044","creationTime":"","description":"Configuring hostPort limits you to a particular port, and if any two workloads that specify the same HostPort they cannot be deployed to the same node. Therefore, if the number of replica of such workload is higher than the number of nodes, the deployment will fail.","remediation":"Avoid usage of hostPort unless it is absolutely necessary. Use NodePort / ClusterIP instead.","rules":[{"guid":"","name":"container-hostPort","attributes":{"armoBuiltin":true},"creationTime":"","rule":"package armo_builtins\n\n\n# Fails if pod has container with hostPort\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n container := pod.spec.containers[i]\n\tbegginingOfPath := \"spec.\"\n\tpath := isHostPort(container, i, begginingOfPath)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Container: %v has Host-port\", [ container.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": path,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [pod]\n\t\t}\n\t}\n}\n\n# Fails if workload has container with hostPort\ndeny[msga] {\n wl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n container := wl.spec.template.spec.containers[i]\n\tbegginingOfPath := \"spec.template.spec.\"\n path := isHostPort(container, i, begginingOfPath)\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Container: %v in %v: %v has Host-port\", [ container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": path,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n# Fails if cronjob has container with hostPort\ndeny[msga] {\n \twl := input[_]\n\twl.kind == \"CronJob\"\n\tcontainer = wl.spec.jobTemplate.spec.template.spec.containers[i]\n\tbegginingOfPath := \"spec.jobTemplate.spec.template.spec.\"\n path := isHostPort(container, i, begginingOfPath)\n msga := {\n\t\t\"alertMessage\": sprintf(\"Container: %v in %v: %v has Host-port\", [ container.name, wl.kind, wl.metadata.name]),\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertScore\": 7,\n\t\t\"failedPaths\": path,\n\t\t\"alertObject\": {\n\t\t\t\"k8sApiObjects\": [wl]\n\t\t}\n\t}\n}\n\n\n\nisHostPort(container, i, begginingOfPath) = path {\n\tpath = [sprintf(\"%vcontainers[%v].ports[%v].hostPort\", [begginingOfPath, format_int(i, 10), format_int(j, 10)]) | port = container.ports[j]; port.hostPort]\n\tcount(path) > 0\n}\n","resourceEnumerator":"","ruleLanguage":"Rego","match":[{"apiGroups":["*"],"apiVersions":["*"],"resources":["Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob"]}],"ruleDependencies":[],"configInputs":null,"controlConfigInputs":null,"description":"fails if container has hostPort","remediation":"Make sure you do not configure hostPort for the container, if necessary use NodePort / ClusterIP","ruleQuery":"armo_builtins"}],"rulesIDs":[""],"baseScore":4}`
|
||||
|
||||
@@ -31,7 +31,7 @@ func MockFramework_0013() *reporthandling.Framework {
|
||||
return fw
|
||||
}
|
||||
|
||||
// MockFramework_0006_0013 mock control 0013 and control 0006 - "Non-root containers" and "Allowed hostPath"
|
||||
// MockFramework_0006_0013 mock control 0013 and control 0006 - "Non-root containers" and "HostPath mount"
|
||||
func MockFramework_0006_0013() *reporthandling.Framework {
|
||||
fw := &reporthandling.Framework{
|
||||
PortalBase: armotypes.PortalBase{
|
||||
|
||||
@@ -28,12 +28,15 @@ spec:
|
||||
tolerations:
|
||||
# this toleration is to have the DaemonDet runnable on master nodes
|
||||
# remove it if your masters can't run pods
|
||||
- key: node-role.kubernetes.io/control-plane
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
- key: node-role.kubernetes.io/master
|
||||
operator: Exists
|
||||
effect: NoSchedule
|
||||
containers:
|
||||
- name: host-sensor
|
||||
image: quay.io/kubescape/host-scanner:v1.0.28
|
||||
image: quay.io/kubescape/host-scanner:v1.0.32
|
||||
securityContext:
|
||||
privileged: true
|
||||
readOnlyRootFilesystem: true
|
||||
@@ -69,4 +72,4 @@ spec:
|
||||
name: host-filesystem
|
||||
hostNetwork: true
|
||||
hostPID: true
|
||||
hostIPC: true
|
||||
hostIPC: true
|
||||
|
||||
@@ -129,6 +129,12 @@ func (hsh *HostSensorHandler) GetKubeProxyInfo() ([]hostsensor.HostSensorDataEnv
|
||||
return hsh.sendAllPodsHTTPGETRequest("/kubeProxyInfo", "KubeProxyInfo")
|
||||
}
|
||||
|
||||
// return list of KubeProxyInfo
|
||||
func (hsh *HostSensorHandler) GetControlPlaneInfo() ([]hostsensor.HostSensorDataEnvelope, error) {
|
||||
// loop over pods and port-forward it to each of them
|
||||
return hsh.sendAllPodsHTTPGETRequest("/controlPlaneInfo", ControlPlaneInfo)
|
||||
}
|
||||
|
||||
// return list of KubeletCommandLine
|
||||
func (hsh *HostSensorHandler) GetKubeletCommandLine() ([]hostsensor.HostSensorDataEnvelope, error) {
|
||||
// loop over pods and port-forward it to each of them
|
||||
@@ -269,6 +275,16 @@ func (hsh *HostSensorHandler) CollectResources() ([]hostsensor.HostSensorDataEnv
|
||||
res = append(res, kcData...)
|
||||
}
|
||||
|
||||
// GetControlPlaneInfo
|
||||
kcData, err = hsh.GetControlPlaneInfo()
|
||||
if err != nil {
|
||||
addInfoToMap(ControlPlaneInfo, infoMap, err)
|
||||
logger.L().Warning(err.Error())
|
||||
}
|
||||
if len(kcData) > 0 {
|
||||
res = append(res, kcData...)
|
||||
}
|
||||
|
||||
logger.L().Debug("Done reading information from host scanner")
|
||||
return res, infoMap, nil
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ var (
|
||||
KubeletCommandLine = "KubeletCommandLine"
|
||||
KubeletInfo = "KubeletInfo"
|
||||
KubeProxyInfo = "KubeProxyInfo"
|
||||
ControlPlaneInfo = "ControlPlaneInfo"
|
||||
|
||||
MapHostSensorResourceToApiGroup = map[string]string{
|
||||
KubeletConfiguration: "hostdata.kubescape.cloud/v1beta0",
|
||||
@@ -26,6 +27,7 @@ var (
|
||||
LinuxKernelVariables: "hostdata.kubescape.cloud/v1beta0",
|
||||
KubeletInfo: "hostdata.kubescape.cloud/v1beta0",
|
||||
KubeProxyInfo: "hostdata.kubescape.cloud/v1beta0",
|
||||
ControlPlaneInfo: "hostdata.kubescape.cloud/v1beta0",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ func (policyHandler *PolicyHandler) getPolicies(policyIdentifier []cautils.Polic
|
||||
controlsInputs, err := policyHandler.getters.ControlsInputsGetter.GetControlsInputs(cautils.ClusterName)
|
||||
if err == nil {
|
||||
policiesAndResources.RegoInputData.PostureControlInputs = controlsInputs
|
||||
} else {
|
||||
logger.L().Error(err.Error())
|
||||
}
|
||||
cautils.StopSpinner()
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ func (ksCivAdaptor *KSCivAdaptor) GetImageVulnerability(imageID *registryvulnera
|
||||
pageNumber := 1
|
||||
request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"}
|
||||
requestBody, _ := json.Marshal(request)
|
||||
requestUrl := fmt.Sprintf("https://%s/api/v1/vulnerability/scanResultsDetails?customerGUID=%s", ksCivAdaptor.ksCloudAPI.GetApiURL(), ksCivAdaptor.ksCloudAPI.GetAccountID())
|
||||
requestUrl := fmt.Sprintf("https://%s/api/v1/vulnerability/scanResultsDetails?customerGUID=%s", ksCivAdaptor.ksCloudAPI.GetCloudAPIURL(), ksCivAdaptor.ksCloudAPI.GetAccountID())
|
||||
|
||||
resp, err := ksCivAdaptor.ksCloudAPI.Post(requestUrl, map[string]string{"Content-Type": "application/json"}, requestBody)
|
||||
if err != nil {
|
||||
|
||||
@@ -14,7 +14,7 @@ func (armoCivAdaptor *KSCivAdaptor) getImageLastScanId(imageID *registryvulnerab
|
||||
pageNumber := 1
|
||||
request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"}
|
||||
requestBody, _ := json.Marshal(request)
|
||||
requestUrl := fmt.Sprintf("https://%s/api/v1/vulnerability/scanResultsSumSummary?customerGUID=%s", armoCivAdaptor.ksCloudAPI.GetApiURL(), armoCivAdaptor.ksCloudAPI.GetAccountID())
|
||||
requestUrl := fmt.Sprintf("https://%s/api/v1/vulnerability/scanResultsSumSummary?customerGUID=%s", armoCivAdaptor.ksCloudAPI.GetCloudAPIURL(), armoCivAdaptor.ksCloudAPI.GetAccountID())
|
||||
|
||||
resp, err := armoCivAdaptor.ksCloudAPI.Post(requestUrl, map[string]string{"Content-Type": "application/json"}, requestBody)
|
||||
if err != nil {
|
||||
|
||||
33
core/pkg/registryadaptors/gcp/v1/Readme.md
Normal file
33
core/pkg/registryadaptors/gcp/v1/Readme.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# GCP Adaptor
|
||||
|
||||
### How we add gcp adaptor
|
||||
|
||||
As there can be possiblities of use of multiple registries we check for each adaptor if we have required credentias. For every adaptor having credentials we append the adaptor to the adaptors slice.
|
||||
|
||||
Particularly for gcp, we frstly bring the `gcpCloudAPI` from the connector. We still haven't created a proper function that initiats the gcpCloudAPI with projectId, credentialsPath, credentialsCheck fields. We check for `credentialsCheck` bool which is set true when we have credentials(to be set when initializing the gcpCloudAPI)
|
||||
|
||||
### How we fetch vulnerabilities for images
|
||||
|
||||
Step 1:
|
||||
Get container analysis client
|
||||
For this we needs credentials of the service account. Out of few approaches here we are using [JSON key file](https://cloud.google.com/container-registry/docs/advanced-authentication#json-key) for credentials and path to this file should be stored in `credentialsPath`
|
||||
|
||||
Step 2:
|
||||
Do ListOccurrenceRequest
|
||||
For this we need the `projectID` and the `resourceUrl`. ProjectID should be provided by the users and resourceUrl is processed imageTag that we get from kubescape resources
|
||||
|
||||
Step 3:
|
||||
Get Occurrence iterator
|
||||
We use context and the request from the ListOccurenceRequest to get the iterators
|
||||
|
||||
|
||||
### How we convert the response to Vulnerabilities
|
||||
|
||||
Response from the iterator has two type of kinds i.e. Discovery and Vulnerabilties and both has differnent struct
|
||||
|
||||
### How can this adaptor be used by the user
|
||||
|
||||
To know about GCR service accounts follow https://cloud.google.com/container-registry/docs/gcr-service-account
|
||||
export variables
|
||||
`export KS_GCP_CREDENTIALS_PATH=<path to service account credentials file>`
|
||||
`export KS_GCP_PROJECT_ID=<your project ID>`
|
||||
24
core/pkg/registryadaptors/gcp/v1/datastructure.go
Normal file
24
core/pkg/registryadaptors/gcp/v1/datastructure.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/kubescape/kubescape/v2/core/cautils/getter"
|
||||
)
|
||||
|
||||
type GCPAdaptor struct {
|
||||
GCPCloudAPI *getter.GCPCloudAPI
|
||||
}
|
||||
|
||||
type Mock struct {
|
||||
Name string
|
||||
Notename string
|
||||
CvssScore float32
|
||||
CreatedTime int64
|
||||
UpdatedTime int64
|
||||
Type string
|
||||
ShortDescription string
|
||||
AffectedCPEURI string
|
||||
AffectedPackage string
|
||||
FixAvailable bool
|
||||
AffectedVersion string
|
||||
FixedVersion string
|
||||
}
|
||||
88
core/pkg/registryadaptors/gcp/v1/gcpadaptor.go
Normal file
88
core/pkg/registryadaptors/gcp/v1/gcpadaptor.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
containeranalysis "cloud.google.com/go/containeranalysis/apiv1"
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/kubescape/v2/core/cautils/getter"
|
||||
"github.com/kubescape/kubescape/v2/core/pkg/registryadaptors/registryvulnerabilities"
|
||||
"google.golang.org/api/iterator"
|
||||
"google.golang.org/api/option"
|
||||
grafeaspb "google.golang.org/genproto/googleapis/grafeas/v1"
|
||||
)
|
||||
|
||||
func NewGCPAdaptor(GCPCloudAPI *getter.GCPCloudAPI) *GCPAdaptor {
|
||||
return &GCPAdaptor{
|
||||
GCPCloudAPI: GCPCloudAPI,
|
||||
}
|
||||
}
|
||||
|
||||
func (GCPAdaptor *GCPAdaptor) Login() error {
|
||||
client, err := containeranalysis.NewClient(GCPAdaptor.GCPCloudAPI.GetContext(), option.WithCredentialsFile(GCPAdaptor.GCPCloudAPI.GetCredentialsPath()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
GCPAdaptor.GCPCloudAPI.SetClient(client)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (GCPAdaptor *GCPAdaptor) GetImagesVulnerabilities(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageVulnerabilityReport, error) {
|
||||
resultList := make([]registryvulnerabilities.ContainerImageVulnerabilityReport, 0)
|
||||
for _, imageID := range imageIDs {
|
||||
result, err := GCPAdaptor.GetImageVulnerability(&imageID)
|
||||
if err == nil {
|
||||
resultList = append(resultList, *result)
|
||||
} else {
|
||||
logger.L().Debug("failed to get image vulnerabilities", helpers.String("image", imageID.Tag), helpers.Error(err))
|
||||
}
|
||||
}
|
||||
return resultList, nil
|
||||
}
|
||||
|
||||
func (GCPAdaptor *GCPAdaptor) GetImageVulnerability(imageID *registryvulnerabilities.ContainerImageIdentifier) (*registryvulnerabilities.ContainerImageVulnerabilityReport, error) {
|
||||
|
||||
resourceUrl := fmt.Sprintf("https://%s", imageID.Tag)
|
||||
|
||||
req := &grafeaspb.ListOccurrencesRequest{
|
||||
Parent: fmt.Sprintf("projects/%s", GCPAdaptor.GCPCloudAPI.GetProjectID()),
|
||||
Filter: fmt.Sprintf(`resourceUrl=%q`, resourceUrl),
|
||||
}
|
||||
|
||||
it := GCPAdaptor.GCPCloudAPI.GetClient().GetGrafeasClient().ListOccurrences(GCPAdaptor.GCPCloudAPI.GetContext(), req)
|
||||
occs := []*grafeaspb.Occurrence{}
|
||||
var count int
|
||||
for {
|
||||
occ, err := it.Next()
|
||||
if err == iterator.Done {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
occs = append(occs, occ)
|
||||
count++
|
||||
}
|
||||
vulnerabilities := responseObjectToVulnerabilities(occs, count)
|
||||
|
||||
resultImageVulnerabilityReport := registryvulnerabilities.ContainerImageVulnerabilityReport{
|
||||
ImageID: *imageID,
|
||||
Vulnerabilities: vulnerabilities,
|
||||
}
|
||||
return &resultImageVulnerabilityReport, nil
|
||||
}
|
||||
|
||||
func (GCPAdaptor *GCPAdaptor) DescribeAdaptor() string {
|
||||
return "GCP image vulnerabilities scanner, docs: https://cloud.google.com/container-analysis/docs/container-analysis"
|
||||
}
|
||||
|
||||
func (GCPAdaptor *GCPAdaptor) GetImagesInformation(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageInformation, error) {
|
||||
// TODO
|
||||
return []registryvulnerabilities.ContainerImageInformation{}, nil
|
||||
}
|
||||
|
||||
func (GCPAdaptor *GCPAdaptor) GetImagesScanStatus(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageScanStatus, error) {
|
||||
// TODO
|
||||
return []registryvulnerabilities.ContainerImageScanStatus{}, nil
|
||||
}
|
||||
31
core/pkg/registryadaptors/gcp/v1/gcpadaptor_test.go
Normal file
31
core/pkg/registryadaptors/gcp/v1/gcpadaptor_test.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kubescape/kubescape/v2/core/pkg/registryadaptors/registryvulnerabilities"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSum(t *testing.T) {
|
||||
var err error
|
||||
var adaptor registryvulnerabilities.IContainerImageVulnerabilityAdaptor
|
||||
|
||||
adaptor, err = NewGCPAdaptorMock()
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NoError(t, adaptor.Login())
|
||||
|
||||
imageVulnerabilityReports, err := adaptor.GetImagesVulnerabilities([]registryvulnerabilities.ContainerImageIdentifier{{Tag: "gcr.io/myproject/nginx@sha256:1XXXXX"}, {Tag: "gcr.io/myproject/nginx@sha256:2XXXXX"}})
|
||||
assert.NoError(t, err)
|
||||
|
||||
for i := range imageVulnerabilityReports {
|
||||
var length int
|
||||
if i == 0 {
|
||||
length = 5
|
||||
} else if i == 1 {
|
||||
length = 3
|
||||
}
|
||||
assert.Equal(t, length, len(imageVulnerabilityReports[i].Vulnerabilities))
|
||||
}
|
||||
}
|
||||
185
core/pkg/registryadaptors/gcp/v1/gcpadaptormock.go
Normal file
185
core/pkg/registryadaptors/gcp/v1/gcpadaptormock.go
Normal file
@@ -0,0 +1,185 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/kubescape/kubescape/v2/core/pkg/registryadaptors/registryvulnerabilities"
|
||||
grafeaspb "google.golang.org/genproto/googleapis/grafeas/v1"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
type GCPAdaptorMock struct {
|
||||
resultList []registryvulnerabilities.ContainerImageVulnerabilityReport
|
||||
}
|
||||
|
||||
func NewGCPAdaptorMock() (*GCPAdaptorMock, error) {
|
||||
return &GCPAdaptorMock{}, nil
|
||||
}
|
||||
|
||||
func (GCPAdaptorMock *GCPAdaptorMock) Login() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (GCPAdaptorMock *GCPAdaptorMock) GetImagesVulnerabilities(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageVulnerabilityReport, error) {
|
||||
resultList := make([]registryvulnerabilities.ContainerImageVulnerabilityReport, 0)
|
||||
for _, imageID := range imageIDs {
|
||||
result, err := GCPAdaptorMock.GetImageVulnerability(&imageID)
|
||||
if err == nil {
|
||||
resultList = append(resultList, *result)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return resultList, nil
|
||||
}
|
||||
|
||||
GCPAdaptorMock.resultList = resultList
|
||||
return GCPAdaptorMock.resultList, nil
|
||||
}
|
||||
|
||||
func (GCPAdaptorMock *GCPAdaptorMock) GetImageVulnerability(imageID *registryvulnerabilities.ContainerImageIdentifier) (*registryvulnerabilities.ContainerImageVulnerabilityReport, error) {
|
||||
vulnerability := []*grafeaspb.Occurrence_Vulnerability{}
|
||||
occurrence := []*grafeaspb.Occurrence{}
|
||||
arr := GetMockData()
|
||||
|
||||
for i, _ := range arr {
|
||||
if imageID.Tag == "gcr.io/myproject/nginx@sha256:2XXXXX" && i == 4 {
|
||||
break
|
||||
}
|
||||
vulnerability = append(vulnerability, &grafeaspb.Occurrence_Vulnerability{
|
||||
Vulnerability: &grafeaspb.VulnerabilityOccurrence{
|
||||
Type: arr[i].Type,
|
||||
CvssScore: arr[i].CvssScore,
|
||||
ShortDescription: arr[i].ShortDescription,
|
||||
PackageIssue: []*grafeaspb.VulnerabilityOccurrence_PackageIssue{
|
||||
{
|
||||
FixedVersion: &grafeaspb.Version{
|
||||
FullName: arr[i].FixedVersion,
|
||||
},
|
||||
AffectedVersion: &grafeaspb.Version{
|
||||
FullName: arr[i].AffectedVersion,
|
||||
},
|
||||
AffectedCpeUri: arr[i].AffectedCPEURI,
|
||||
AffectedPackage: arr[i].AffectedPackage,
|
||||
},
|
||||
},
|
||||
FixAvailable: arr[i].FixAvailable,
|
||||
},
|
||||
})
|
||||
|
||||
occurrence = append(occurrence, &grafeaspb.Occurrence{
|
||||
Name: arr[i].Name,
|
||||
Kind: grafeaspb.NoteKind_ATTESTATION,
|
||||
NoteName: arr[i].Notename,
|
||||
CreateTime: ×tamppb.Timestamp{
|
||||
Seconds: arr[i].CreatedTime,
|
||||
},
|
||||
UpdateTime: ×tamppb.Timestamp{
|
||||
Seconds: arr[i].UpdatedTime,
|
||||
},
|
||||
Details: vulnerability[i],
|
||||
})
|
||||
}
|
||||
|
||||
vulnerabilities := responseObjectToVulnerabilities(occurrence, 5)
|
||||
|
||||
resultImageVulnerabilityReport := registryvulnerabilities.ContainerImageVulnerabilityReport{
|
||||
ImageID: *imageID,
|
||||
Vulnerabilities: vulnerabilities,
|
||||
}
|
||||
return &resultImageVulnerabilityReport, nil
|
||||
}
|
||||
|
||||
func (GCPAdaptorMock *GCPAdaptorMock) DescribeAdaptor() string {
|
||||
// TODO
|
||||
return ""
|
||||
}
|
||||
|
||||
func (GCPAdaptorMock *GCPAdaptorMock) GetImagesInformation(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageInformation, error) {
|
||||
// TODO
|
||||
return []registryvulnerabilities.ContainerImageInformation{}, nil
|
||||
}
|
||||
|
||||
func (GCPAdaptorMock *GCPAdaptorMock) GetImagesScanStatus(imageIDs []registryvulnerabilities.ContainerImageIdentifier) ([]registryvulnerabilities.ContainerImageScanStatus, error) {
|
||||
// TODO
|
||||
return []registryvulnerabilities.ContainerImageScanStatus{}, nil
|
||||
}
|
||||
|
||||
//==============================================================================================================================
|
||||
//==============================================================================================================================
|
||||
//==============================================================================================================================
|
||||
|
||||
func GetMockData() []Mock {
|
||||
arr := []Mock{
|
||||
{
|
||||
Name: "projects/stable-furnace-356005/occurrences/41fd9fec-6fab-4531-a4ee-e7b97d518554",
|
||||
Notename: "projects/goog-vulnz/notes/CVE-2009-4487",
|
||||
CvssScore: 6.8,
|
||||
CreatedTime: 1661061853,
|
||||
UpdatedTime: 1661061853,
|
||||
Type: "OS",
|
||||
ShortDescription: "CVE-2009-4487",
|
||||
AffectedCPEURI: "cpe:/o:debian:debian_linux:11",
|
||||
AffectedPackage: "nginx",
|
||||
FixAvailable: true,
|
||||
AffectedVersion: "1.23.1-1~bullseye",
|
||||
FixedVersion: "",
|
||||
},
|
||||
{
|
||||
Name: "projects/stable-furnace-356005/occurrences/b28fa29f-5c2b-45c7-9727-2f1f02ed1957",
|
||||
Notename: "projects/goog-vulnz/notes/CVE-2017-17740",
|
||||
CvssScore: 2.3,
|
||||
CreatedTime: 3237628,
|
||||
UpdatedTime: 5989893,
|
||||
Type: "OS",
|
||||
ShortDescription: "CVE-2017-17740",
|
||||
AffectedCPEURI: "cpe:/o:debian:debian_linux:11",
|
||||
AffectedPackage: "openldap",
|
||||
FixAvailable: false,
|
||||
AffectedVersion: "1.3.5",
|
||||
FixedVersion: "1.3.5",
|
||||
},
|
||||
{
|
||||
Name: "projects/stable-furnace-356005/occurrences/b28fa29f-5c2b-45c7-9727-2f1f02ed1957",
|
||||
Notename: "projects/goog-vulnz/notes/CVE-2017-17740",
|
||||
CvssScore: 2.3,
|
||||
CreatedTime: 3237628,
|
||||
UpdatedTime: 5989893,
|
||||
Type: "OS",
|
||||
ShortDescription: "CVE-2017-17740",
|
||||
AffectedCPEURI: "cpe:/o:debian:debian_linux:11",
|
||||
AffectedPackage: "openldap",
|
||||
FixAvailable: false,
|
||||
AffectedVersion: "1.3.5",
|
||||
FixedVersion: "1.3.5",
|
||||
},
|
||||
{
|
||||
Name: "projects/stable-furnace-356005/occurrences/b28fa29f-5c2b-45c7-9727-2f1f02ed1957",
|
||||
Notename: "projects/goog-vulnz/notes/CVE-2017-17740",
|
||||
CvssScore: 2.3,
|
||||
CreatedTime: 3237628,
|
||||
UpdatedTime: 5989893,
|
||||
Type: "OS",
|
||||
ShortDescription: "CVE-2017-17740",
|
||||
AffectedCPEURI: "cpe:/o:debian:debian_linux:11",
|
||||
AffectedPackage: "openldap",
|
||||
FixAvailable: false,
|
||||
AffectedVersion: "1.3.5",
|
||||
FixedVersion: "1.3.5",
|
||||
},
|
||||
{
|
||||
Name: "projects/stable-furnace-356005/occurrences/b28fa29f-5c2b-45c7-9727-2f1f02ed1957",
|
||||
Notename: "projects/goog-vulnz/notes/CVE-2017-17740",
|
||||
CvssScore: 2.3,
|
||||
CreatedTime: 3237628,
|
||||
UpdatedTime: 5989893,
|
||||
Type: "OS",
|
||||
ShortDescription: "CVE-2017-17740",
|
||||
AffectedCPEURI: "cpe:/o:debian:debian_linux:11",
|
||||
AffectedPackage: "openldap",
|
||||
FixAvailable: false,
|
||||
AffectedVersion: "1.3.5",
|
||||
FixedVersion: "1.3.5",
|
||||
},
|
||||
}
|
||||
|
||||
return arr
|
||||
}
|
||||
36
core/pkg/registryadaptors/gcp/v1/gcpadaptorutils.go
Normal file
36
core/pkg/registryadaptors/gcp/v1/gcpadaptorutils.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/kubescape/kubescape/v2/core/pkg/registryadaptors/registryvulnerabilities"
|
||||
grafeaspb "google.golang.org/genproto/googleapis/grafeas/v1"
|
||||
)
|
||||
|
||||
func responseObjectToVulnerabilities(vulnerabilityList []*grafeaspb.Occurrence, count int) []registryvulnerabilities.Vulnerability {
|
||||
vulnerabilities := make([]registryvulnerabilities.Vulnerability, count)
|
||||
for i, vulnerabilityEntry := range vulnerabilityList {
|
||||
if vulnerabilityEntry.GetKind().String() != "DISCOVERY" {
|
||||
vulnerabilities[i].Name = vulnerabilityEntry.Name
|
||||
vulnerabilities[i].NoteName = vulnerabilityEntry.NoteName
|
||||
vulnerabilities[i].CreateTime = vulnerabilityEntry.CreateTime.AsTime()
|
||||
vulnerabilities[i].UpdateTime = vulnerabilityEntry.UpdateTime.AsTime()
|
||||
vulnerabilities[i].CVSS = vulnerabilityEntry.GetVulnerability().CvssScore
|
||||
vulnerabilities[i].AffectedCPEURI = vulnerabilityEntry.GetVulnerability().PackageIssue[0].AffectedCpeUri
|
||||
vulnerabilities[i].AffectedPackage = vulnerabilityEntry.GetVulnerability().PackageIssue[0].AffectedPackage
|
||||
vulnerabilities[i].AffectedVersion = vulnerabilityEntry.GetVulnerability().PackageIssue[0].AffectedVersion.FullName
|
||||
vulnerabilities[i].FixedVersion = vulnerabilityEntry.GetVulnerability().PackageIssue[0].FixedVersion.FullName
|
||||
vulnerabilities[i].FixedCPEURI = vulnerabilityEntry.GetVulnerability().PackageIssue[0].FixedCpeUri
|
||||
vulnerabilities[i].FixedPackege = vulnerabilityEntry.GetVulnerability().PackageIssue[0].FixedPackage
|
||||
vulnerabilities[i].FixAvailablePackage = vulnerabilityEntry.GetVulnerability().PackageIssue[0].GetFixAvailable()
|
||||
vulnerabilities[i].PackageType = vulnerabilityEntry.GetVulnerability().PackageIssue[0].PackageType
|
||||
vulnerabilities[i].EffectiveSeverityPackage = vulnerabilityEntry.GetVulnerability().PackageIssue[0].EffectiveSeverity.String()
|
||||
vulnerabilities[i].AffectedPackage = vulnerabilityEntry.GetVulnerability().PackageIssue[0].AffectedPackage
|
||||
vulnerabilities[i].Severity = vulnerabilityEntry.GetVulnerability().Severity.Enum().String()
|
||||
vulnerabilities[i].ShortDescription = vulnerabilityEntry.GetVulnerability().ShortDescription
|
||||
vulnerabilities[i].LongDescription = vulnerabilityEntry.GetVulnerability().LongDescription
|
||||
} else {
|
||||
vulnerabilities[i].Description = vulnerabilityEntry.GetDiscovery().String()
|
||||
}
|
||||
}
|
||||
|
||||
return vulnerabilities
|
||||
}
|
||||
@@ -28,19 +28,36 @@ type Categories struct {
|
||||
}
|
||||
|
||||
type Vulnerability struct {
|
||||
Name string `json:"name"`
|
||||
RelatedPackageName string `json:"packageName"`
|
||||
PackageVersion string `json:"packageVersion"`
|
||||
Link string `json:"link"`
|
||||
Description string `json:"description"`
|
||||
Severity string `json:"severity"`
|
||||
Metadata interface{} `json:"metadata"`
|
||||
Fixes []FixedIn `json:"fixedIn"`
|
||||
Relevancy string `json:"relevant"` // use the related enum
|
||||
UrgentCount int `json:"urgent"`
|
||||
NeglectedCount int `json:"neglected"`
|
||||
HealthStatus string `json:"healthStatus"`
|
||||
Categories Categories `json:"categories"`
|
||||
Name string `json:"name"`
|
||||
RelatedPackageName string `json:"packageName"`
|
||||
PackageVersion string `json:"packageVersion"`
|
||||
Link string `json:"link"`
|
||||
Description string `json:"description"`
|
||||
Severity string `json:"severity"`
|
||||
Metadata interface{} `json:"metadata"`
|
||||
Fixes []FixedIn `json:"fixedIn"`
|
||||
Relevancy string `json:"relevant"` // use the related enum
|
||||
UrgentCount int `json:"urgent"`
|
||||
NeglectedCount int `json:"neglected"`
|
||||
HealthStatus string `json:"healthStatus"`
|
||||
Categories Categories `json:"categories"`
|
||||
NoteName string `json:",omitempty"`
|
||||
CreateTime time.Time `json:",omitempty"`
|
||||
UpdateTime time.Time `json:",omitempty"` // Vulnerablity started
|
||||
CVSS float32 `json:",omitempty"` // other cvss versions are available
|
||||
AffectedCPEURI string `json:",omitempty"` // Package issue
|
||||
AffectedPackage string `json:",omitempty"`
|
||||
AffectedVersion string `json:",omitempty"`
|
||||
FixedVersion string `json:",omitempty"`
|
||||
FixedCPEURI string `json:",omitempty"`
|
||||
FixedPackege string `json:",omitempty"`
|
||||
FixAvailablePackage bool `json:",omitempty"`
|
||||
PackageType string `json:",omitempty"`
|
||||
EffectiveSeverityPackage string `json:",omitempty"`
|
||||
ShortDescription string `json:",omitempty"` // Package issue ends
|
||||
LongDescription string `json:",omitempty"`
|
||||
EffectiveSeverity string `json:",omitempty"`
|
||||
FixAvailable bool `json:",omitempty"`
|
||||
}
|
||||
|
||||
type ContainerImageVulnerabilityReport struct {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
type IFieldSelector interface {
|
||||
GetNamespacesSelectors(*schema.GroupVersionResource) []string
|
||||
GetClusterScope(*schema.GroupVersionResource) bool
|
||||
}
|
||||
|
||||
type EmptySelector struct {
|
||||
@@ -19,6 +20,10 @@ func (es *EmptySelector) GetNamespacesSelectors(resource *schema.GroupVersionRes
|
||||
return []string{""} //
|
||||
}
|
||||
|
||||
func (es *EmptySelector) GetClusterScope(*schema.GroupVersionResource) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type ExcludeSelector struct {
|
||||
namespace string
|
||||
}
|
||||
@@ -27,6 +32,14 @@ func NewExcludeSelector(ns string) *ExcludeSelector {
|
||||
return &ExcludeSelector{namespace: ns}
|
||||
}
|
||||
|
||||
func (es *ExcludeSelector) GetClusterScope(resource *schema.GroupVersionResource) bool {
|
||||
// for selector, 'namespace' is in Namespaced scope
|
||||
if resource.Resource == "namespaces" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type IncludeSelector struct {
|
||||
namespace string
|
||||
}
|
||||
@@ -34,6 +47,15 @@ type IncludeSelector struct {
|
||||
func NewIncludeSelector(ns string) *IncludeSelector {
|
||||
return &IncludeSelector{namespace: ns}
|
||||
}
|
||||
|
||||
func (is *IncludeSelector) GetClusterScope(resource *schema.GroupVersionResource) bool {
|
||||
// for selector, 'namespace' is in Namespaced scope
|
||||
if resource.Resource == "namespaces" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (es *ExcludeSelector) GetNamespacesSelectors(resource *schema.GroupVersionResource) []string {
|
||||
fieldSelectors := ""
|
||||
for _, n := range strings.Split(es.namespace, ",") {
|
||||
|
||||
@@ -84,7 +84,7 @@ func (k8sHandler *K8sResourceHandler) GetResources(sessionObj *cautils.OPASessio
|
||||
|
||||
imgVulnResources := cautils.MapImageVulnResources(ksResourceMap)
|
||||
// check that controls use image vulnerability resources
|
||||
if false { //len(imgVulnResources) > 0 {
|
||||
if len(imgVulnResources) > 0 {
|
||||
logger.L().Info("Requesting images vulnerabilities results")
|
||||
cautils.StartSpinner()
|
||||
if err := k8sHandler.registryAdaptors.collectImagesVulnerabilities(k8sResourcesMap, allResources, ksResourceMap); err != nil {
|
||||
@@ -134,8 +134,6 @@ func (k8sHandler *K8sResourceHandler) GetResources(sessionObj *cautils.OPASessio
|
||||
|
||||
// check that controls use cloud resources
|
||||
if len(cloudResources) > 0 {
|
||||
logger.L().Info("Requesting cloud provider data")
|
||||
cautils.StartSpinner()
|
||||
provider, err := getCloudProviderDescription(allResources, ksResourceMap)
|
||||
if err != nil {
|
||||
cautils.SetInfoMapForResources(err.Error(), cloudResources, sessionObj.InfoMap)
|
||||
@@ -146,13 +144,29 @@ func (k8sHandler *K8sResourceHandler) GetResources(sessionObj *cautils.OPASessio
|
||||
sessionObj.Metadata.ContextMetadata.ClusterContextMetadata.CloudProvider = provider
|
||||
}
|
||||
}
|
||||
cautils.StopSpinner()
|
||||
logger.L().Info("Requested cloud provider data")
|
||||
|
||||
// api server info resource
|
||||
err = k8sHandler.collectAPIServerInfoResource(allResources, ksResourceMap)
|
||||
if err != nil {
|
||||
logger.L().Warning("failed to collect api server info resource", helpers.Error(err))
|
||||
}
|
||||
}
|
||||
|
||||
return k8sResourcesMap, allResources, ksResourceMap, nil
|
||||
}
|
||||
|
||||
func (k8sHandler *K8sResourceHandler) collectAPIServerInfoResource(allResources map[string]workloadinterface.IMetadata, ksResourceMap *cautils.KSResources) error {
|
||||
clusterAPIServerInfo, err := k8sHandler.k8s.DiscoveryClient.ServerVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resource := cloudsupport.NewApiServerVersionInfo(clusterAPIServerInfo)
|
||||
allResources[resource.GetID()] = resource
|
||||
(*ksResourceMap)[fmt.Sprintf("%s/%s", resource.GetApiVersion(), resource.GetKind())] = []string{resource.GetID()}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k8sHandler *K8sResourceHandler) GetClusterAPIServerInfo() *version.Info {
|
||||
clusterAPIServerInfo, err := k8sHandler.k8s.DiscoveryClient.ServerVersion()
|
||||
if err != nil {
|
||||
@@ -230,10 +244,14 @@ func (k8sHandler *K8sResourceHandler) pullSingleResource(resource *schema.GroupV
|
||||
|
||||
// set dynamic object
|
||||
var clientResource dynamic.ResourceInterface
|
||||
if namespace != "" && k8sinterface.IsNamespaceScope(resource) {
|
||||
clientResource = k8sHandler.k8s.DynamicClient.Resource(*resource).Namespace(namespace)
|
||||
} else {
|
||||
if namespace != "" {
|
||||
clientResource = k8sHandler.k8s.DynamicClient.Resource(*resource)
|
||||
} else if k8sinterface.IsNamespaceScope(resource) {
|
||||
clientResource = k8sHandler.k8s.DynamicClient.Resource(*resource).Namespace(namespace)
|
||||
} else if k8sHandler.fieldSelector.GetClusterScope(*&resource) {
|
||||
clientResource = k8sHandler.k8s.DynamicClient.Resource(*resource)
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
|
||||
// list resources
|
||||
|
||||
@@ -22,6 +22,7 @@ var (
|
||||
ImageVulnerabilities = "ImageVulnerabilities"
|
||||
KubeletInfo = "KubeletInfo"
|
||||
KubeProxyInfo = "KubeProxyInfo"
|
||||
ControlPlaneInfo = "ControlPlaneInfo"
|
||||
|
||||
MapResourceToApiGroup = map[string]string{
|
||||
KubeletConfiguration: "hostdata.kubescape.cloud/v1beta0",
|
||||
@@ -33,6 +34,7 @@ var (
|
||||
LinuxKernelVariables: "hostdata.kubescape.cloud/v1beta0",
|
||||
KubeletInfo: "hostdata.kubescape.cloud/v1beta0",
|
||||
KubeProxyInfo: "hostdata.kubescape.cloud/v1beta0",
|
||||
ControlPlaneInfo: "hostdata.kubescape.cloud/v1beta0",
|
||||
}
|
||||
MapResourceToApiGroupVuln = map[string][]string{
|
||||
ImageVulnerabilities: {"armo.vuln.images/v1", "image.vulnscan.com/v1"}}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/kubescape/k8s-interface/workloadinterface"
|
||||
"github.com/kubescape/kubescape/v2/core/cautils"
|
||||
"github.com/kubescape/kubescape/v2/core/cautils/getter"
|
||||
gcpadaptorv1 "github.com/kubescape/kubescape/v2/core/pkg/registryadaptors/gcp/v1"
|
||||
armosecadaptorv1 "github.com/kubescape/kubescape/v2/core/pkg/registryadaptors/armosec/v1"
|
||||
"github.com/kubescape/kubescape/v2/core/pkg/registryadaptors/registryvulnerabilities"
|
||||
|
||||
@@ -158,5 +159,12 @@ func listAdaptores() ([]registryvulnerabilities.IContainerImageVulnerabilityAdap
|
||||
}
|
||||
}
|
||||
|
||||
gcpCloudAPI := getter.GetGlobalGCPCloudAPIConnector()
|
||||
if gcpCloudAPI != nil {
|
||||
if gcpCloudAPI.GetCredentialsCheck() {
|
||||
adaptors = append(adaptors, gcpadaptorv1.NewGCPAdaptor(getter.GetGlobalGCPCloudAPIConnector()))
|
||||
}
|
||||
}
|
||||
|
||||
return adaptors, nil
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@ var (
|
||||
urlD = "https://raw.githubusercontent.com/kubescape/kubescape/master/examples/online-boutique/adservice.yaml"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
TODO: tests were commented out due to actual http calls ; http calls should be mocked.
|
||||
|
||||
func TestScanRepository(t *testing.T) {
|
||||
{
|
||||
files, err := ScanRepository(urlA, "")
|
||||
@@ -112,6 +116,7 @@ func TestGithubGetYamlFromTree(t *testing.T) {
|
||||
assert.Equal(t, 12, len(files))
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func TestGithubParse(t *testing.T) {
|
||||
{
|
||||
|
||||
@@ -2,12 +2,11 @@ package resourcehandler
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLoadResourcesFromUrl(t *testing.T) {
|
||||
{
|
||||
//TODO: tests were commented out due to actual http calls ; http calls should be mocked.
|
||||
/*{
|
||||
workloads, err := loadResourcesFromUrl([]string{"https://github.com/kubescape/kubescape/tree/master/examples/online-boutique"})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 12, len(workloads))
|
||||
@@ -62,5 +61,5 @@ func TestLoadResourcesFromUrl(t *testing.T) {
|
||||
assert.Equal(t, "/v1//Service/adservice", w[1].GetID())
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package resourcesprioritization
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/k8s-interface/workloadinterface"
|
||||
"github.com/kubescape/kubescape/v2/core/cautils"
|
||||
"github.com/kubescape/kubescape/v2/core/cautils/getter"
|
||||
@@ -55,7 +57,8 @@ func (handler *ResourcesPrioritizationHandler) PrioritizeResources(sessionObj *c
|
||||
resourcePriorityVector := []prioritization.ControlsVector{}
|
||||
resource, exist := sessionObj.AllResources[resourceId]
|
||||
if !exist {
|
||||
return fmt.Errorf("expected to find resource id '%s' in scanned resources map", resourceId)
|
||||
logger.L().Error("resource not found in resources map", helpers.String("resource ID", resourceId))
|
||||
continue
|
||||
}
|
||||
|
||||
workload := workloadinterface.NewWorkloadObj(resource.GetObject())
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package locationresolver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/kubescape/go-logger"
|
||||
"github.com/mikefarah/yq/v4/pkg/yqlib"
|
||||
|
||||
"gopkg.in/op/go-logging.v1"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type FixPathLocationResolver struct {
|
||||
yqlibEvaluator yqlib.Evaluator
|
||||
yamlPath string
|
||||
yamlNodes []*yaml.Node
|
||||
}
|
||||
|
||||
type Location struct {
|
||||
Line int
|
||||
Column int
|
||||
}
|
||||
|
||||
func NewFixPathLocationResolver(yamlPath string) (*FixPathLocationResolver, error) {
|
||||
file, err := os.Open(yamlPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
yamlNodes := make([]*yaml.Node, 0)
|
||||
|
||||
yamlDecoder := yaml.NewDecoder(file)
|
||||
for {
|
||||
var yamlNode yaml.Node
|
||||
err = yamlDecoder.Decode(&yamlNode)
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
yamlNodes = append(yamlNodes, &yamlNode)
|
||||
}
|
||||
}
|
||||
|
||||
evaluator := yqlib.NewAllAtOnceEvaluator()
|
||||
backendLoggerLeveled := logging.AddModuleLevel(logging.NewLogBackend(logger.L().GetWriter(), "", 0))
|
||||
backendLoggerLeveled.SetLevel(logging.ERROR, "")
|
||||
yqlib.GetLogger().SetBackend(backendLoggerLeveled)
|
||||
|
||||
return &FixPathLocationResolver{
|
||||
yamlPath: yamlPath,
|
||||
yqlibEvaluator: evaluator,
|
||||
yamlNodes: yamlNodes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *FixPathLocationResolver) ResolveLocation(fixPath string, nodeIndex int) (Location, error) {
|
||||
if nodeIndex >= len(l.yamlNodes) {
|
||||
return Location{}, fmt.Errorf("node index [%d] out of range [%d]", nodeIndex, len(l.yamlNodes))
|
||||
}
|
||||
|
||||
yamlExpression := FixPathToValidYamlExpression(fixPath)
|
||||
for strings.HasPrefix(yamlExpression, ".") && len(yamlExpression) > 1 {
|
||||
candidateNodes, err := l.yqlibEvaluator.EvaluateNodes(yamlExpression, l.yamlNodes[nodeIndex])
|
||||
if err != nil {
|
||||
return Location{}, err
|
||||
}
|
||||
|
||||
candidateNode := candidateNodes.Back().Value.(*yqlib.CandidateNode).Node
|
||||
|
||||
if candidateNode.Line != 0 || len(yamlExpression) <= 1 {
|
||||
return Location{Line: candidateNode.Line, Column: candidateNode.Column}, nil
|
||||
}
|
||||
|
||||
// for non-existent yaml expressions, remove the last part of the expression and try again
|
||||
yamlExpression = regexp.MustCompile(`(.*)(\.[^.]*)`).ReplaceAllString(yamlExpression, `${1}`)
|
||||
}
|
||||
return Location{}, nil
|
||||
}
|
||||
|
||||
func FixPathToValidYamlExpression(fixPath string) string {
|
||||
// remove everything after the first =
|
||||
yamlExpression := regexp.MustCompile(`(.*)=.*`).ReplaceAllString(fixPath, `${1}`)
|
||||
|
||||
// add a dot for the root node
|
||||
yamlExpression = "." + yamlExpression
|
||||
return yamlExpression
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package locationresolver
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func onlineBoutiquePath() string {
|
||||
o, _ := os.Getwd()
|
||||
return filepath.Join(filepath.Dir(o), "..", "..", "..", "examples", "online-boutique")
|
||||
}
|
||||
|
||||
func TestResolveLocation(t *testing.T) {
|
||||
yamlFilePath := filepath.Join(onlineBoutiquePath(), "adservice.yaml")
|
||||
fixPathToExpectedLineAndColumn := map[string]Location{
|
||||
"spec.template.spec.containers[0].securityContext.readOnlyRootFilesystem=true": {Line: 31, Column: 9},
|
||||
"spec.template.spec.containers[0].securityContext.runAsNonRoot=true": {Line: 31, Column: 9},
|
||||
"spec.template.spec.containers[0].securityContext.allowPrivilegeEscalation=false": {Line: 31, Column: 9},
|
||||
"spec.template.spec.containers[0].securityContext.capabilities.drop=NET_RAW": {Line: 31, Column: 9},
|
||||
"spec.template.spec.containers[0].securityContext.seLinuxOptions=YOUR_VALUE": {Line: 31, Column: 9},
|
||||
"spec.template.spec.containers[0].securityContext.seccompProfile=YOUR_VALUE": {Line: 31, Column: 9},
|
||||
"spec.template.spec.securityContext.runAsNonRoot=true": {Line: 28, Column: 7},
|
||||
"spec.template.spec.securityContext.allowPrivilegeEscalation=false": {Line: 28, Column: 7},
|
||||
"spec.template.spec.containers[0].securityContext.seccompProfile.type=RuntimeDefault": {Line: 31, Column: 9},
|
||||
"spec.template.spec.containers[0].image": {Line: 32, Column: 16},
|
||||
"spec.template.spec.containers[0].seccompProfile=YOUR_VALUE": {Line: 31, Column: 9},
|
||||
"spec.template.spec.containers[0].seLinuxOptions=YOUR_VALUE": {Line: 31, Column: 9},
|
||||
"spec.template.spec.containers[0].capabilities.drop=YOUR_VALUE": {Line: 31, Column: 9},
|
||||
"metadata.namespace=YOUR_NAMESPACE": {Line: 18, Column: 3},
|
||||
"metadata.labels=YOUR_VALUE": {Line: 18, Column: 3},
|
||||
"spec.template.metadata.labels=YOUR_VALUE": {Line: 26, Column: 9},
|
||||
"spec.template.spec.containers[0].resources.limits.cpu=YOUR_VALUE": {Line: 49, Column: 18},
|
||||
}
|
||||
|
||||
resolver, _ := NewFixPathLocationResolver(yamlFilePath)
|
||||
|
||||
for fixPath, expected := range fixPathToExpectedLineAndColumn {
|
||||
location, err := resolver.ResolveLocation(fixPath, 0)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equalf(t, expected.Line, location.Line, "fixPath %s, expected line: %d, actual line: %d", fixPath, expected.Line, location.Line)
|
||||
assert.Equalf(t, expected.Column, location.Column, "fixPath %s, expected column: %d, actual column: %d", fixPath, expected.Column, location.Column)
|
||||
}
|
||||
|
||||
fixPathToExpectedLineAndColumn = map[string]Location{
|
||||
"metadata.namespace=YOUR_NAMESPACE": {Line: 65, Column: 3},
|
||||
"metadata.labels=YOUR_VALUE": {Line: 65, Column: 3},
|
||||
}
|
||||
|
||||
for fixPath, expected := range fixPathToExpectedLineAndColumn {
|
||||
location, err := resolver.ResolveLocation(fixPath, 1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equalf(t, expected.Line, location.Line, "fixPath %s, expected line: %d, actual line: %d", fixPath, expected.Line, location.Line)
|
||||
assert.Equalf(t, expected.Column, location.Column, "fixPath %s, expected column: %d, actual column: %d", fixPath, expected.Column, location.Column)
|
||||
}
|
||||
|
||||
_, err := resolver.ResolveLocation("some invalid string as an input", 0)
|
||||
assert.ErrorContains(t, err, "invalid input")
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ const (
|
||||
PrometheusFormat string = "prometheus"
|
||||
PdfFormat string = "pdf"
|
||||
HtmlFormat string = "html"
|
||||
SARIFFormat string = "sarif"
|
||||
)
|
||||
|
||||
type IPrinter interface {
|
||||
|
||||
@@ -173,7 +173,7 @@ func failedControlsToFailureMessage(results *cautils.OPASessionObj, controls []r
|
||||
msg += fmt.Sprintf("Test: %s\n", control.GetName())
|
||||
msg += fmt.Sprintf("Severity: %s\n", apis.ControlSeverityToString(control.GetScoreFactor()))
|
||||
msg += fmt.Sprintf("Remediation: %s\n", control.GetRemediation())
|
||||
msg += fmt.Sprintf("Link: %s\n", getControlLink(control.GetID()))
|
||||
msg += fmt.Sprintf("Link: %s\n", cautils.GetControlLink(control.GetID()))
|
||||
if failedPaths := failedPathsToString(&c); len(failedPaths) > 0 {
|
||||
msg += fmt.Sprintf("Failed paths: \n - %s\n", strings.Join(failedPaths, "\n - "))
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/enescakir/emoji"
|
||||
"github.com/kubescape/k8s-interface/workloadinterface"
|
||||
@@ -84,7 +83,7 @@ func (prettyPrinter *PrettyPrinter) printSummary(controlName string, controlSumm
|
||||
|
||||
}
|
||||
func (prettyPrinter *PrettyPrinter) printTitle(controlSummary reportsummary.IControlSummary) {
|
||||
cautils.InfoDisplay(prettyPrinter.writer, "[control: %s - %s] ", controlSummary.GetName(), getControlLink(controlSummary.GetID()))
|
||||
cautils.InfoDisplay(prettyPrinter.writer, "[control: %s - %s] ", controlSummary.GetName(), cautils.GetControlLink(controlSummary.GetID()))
|
||||
switch controlSummary.GetStatus().Status() {
|
||||
case apis.StatusSkipped:
|
||||
cautils.InfoDisplay(prettyPrinter.writer, "skipped %v\n", emoji.ConfusedFace)
|
||||
@@ -255,10 +254,6 @@ func frameworksScoresToString(frameworks []reportsummary.IFrameworkSummary) stri
|
||||
return ""
|
||||
}
|
||||
|
||||
func getControlLink(controlID string) string {
|
||||
return fmt.Sprintf("https://hub.armosec.io/docs/%s", strings.ToLower(controlID))
|
||||
}
|
||||
|
||||
// renderSeverityCountersSummary renders the string that reports severity counters summary
|
||||
func renderSeverityCountersSummary(counters reportsummary.ISeverityCounters) string {
|
||||
critical := counters.NumberOfResourcesWithCriticalSeverity()
|
||||
|
||||
@@ -289,7 +289,7 @@ func (m *Metrics) setRiskScores(summaryDetails *reportsummary.SummaryDetails) {
|
||||
controlName: control.GetName(),
|
||||
controlID: control.GetID(),
|
||||
riskScore: cautils.Float32ToInt(control.GetScore()),
|
||||
link: getControlLink(control.GetID()),
|
||||
link: cautils.GetControlLink(control.GetID()),
|
||||
severity: apis.ControlSeverityToString(control.GetScoreFactor()),
|
||||
remediation: control.GetRemediation(),
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func generateResourceRows(controls []resourcesresults.ResourceAssociatedControl,
|
||||
continue
|
||||
}
|
||||
|
||||
row[resourceColumnURL] = fmt.Sprintf("https://hub.armosec.io/docs/%s", strings.ToLower(controls[i].GetID()))
|
||||
row[resourceColumnURL] = cautils.GetControlLink(controls[i].GetID())
|
||||
row[resourceColumnPath] = strings.Join(append(failedPathsToString(&controls[i]), fixPathsToString(&controls[i])...), "\n")
|
||||
row[resourceColumnName] = controls[i].GetName()
|
||||
|
||||
|
||||
204
core/pkg/resultshandling/printer/v2/sarifprinter.go
Normal file
204
core/pkg/resultshandling/printer/v2/sarifprinter.go
Normal file
@@ -0,0 +1,204 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/go-logger/helpers"
|
||||
"github.com/kubescape/kubescape/v2/core/cautils"
|
||||
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/locationresolver"
|
||||
"github.com/kubescape/kubescape/v2/core/pkg/resultshandling/printer"
|
||||
"github.com/kubescape/opa-utils/objectsenvelopes/localworkload"
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
"github.com/kubescape/opa-utils/reporthandling/results/v1/resourcesresults"
|
||||
v2 "github.com/kubescape/opa-utils/reporthandling/v2"
|
||||
"github.com/owenrumney/go-sarif/v2/sarif"
|
||||
)
|
||||
|
||||
const (
|
||||
sarifOutputFile = "report"
|
||||
sarifOutputExt = ".sarif"
|
||||
|
||||
toolName = "kubescape"
|
||||
toolInfoURI = "https://armosec.io"
|
||||
)
|
||||
|
||||
// sarifSeverityLevel is a SARIF-specific severity level for Rules and Results
|
||||
type sarifSeverityLevel string
|
||||
|
||||
const (
|
||||
sarifSeverityLevelNote sarifSeverityLevel = "note"
|
||||
sarifSeverityLevelWarning sarifSeverityLevel = "warning"
|
||||
sarifSeverityLevelError sarifSeverityLevel = "error"
|
||||
)
|
||||
|
||||
// scoreFactorToSARIFSeverityLevel returns a SARIF severity level that matches
|
||||
// a given Kubescape severity score
|
||||
func scoreFactorToSARIFSeverityLevel(score float32) sarifSeverityLevel {
|
||||
switch {
|
||||
case score >= 9.0:
|
||||
return sarifSeverityLevelError
|
||||
case score >= 4.0:
|
||||
return sarifSeverityLevelWarning
|
||||
}
|
||||
|
||||
return sarifSeverityLevelNote
|
||||
}
|
||||
|
||||
// SARIFPrinter is a printer that emits the report in the SARIF format
|
||||
type SARIFPrinter struct {
|
||||
// outputFile is the name of the output file
|
||||
writer *os.File
|
||||
}
|
||||
|
||||
// NewSARIFPrinter returns a new SARIF printer instance
|
||||
func NewSARIFPrinter() *SARIFPrinter {
|
||||
return &SARIFPrinter{}
|
||||
}
|
||||
|
||||
func (sp *SARIFPrinter) Score(score float32) {
|
||||
return
|
||||
}
|
||||
|
||||
func (sp *SARIFPrinter) SetWriter(outputFile string) {
|
||||
if outputFile == "" {
|
||||
outputFile = sarifOutputFile
|
||||
}
|
||||
if filepath.Ext(strings.TrimSpace(outputFile)) != sarifOutputExt {
|
||||
outputFile = outputFile + sarifOutputExt
|
||||
}
|
||||
sp.writer = printer.GetWriter(outputFile)
|
||||
}
|
||||
|
||||
// addRule adds a rule description to the scan run based on the given control summary
|
||||
func (sp *SARIFPrinter) addRule(scanRun *sarif.Run, control reportsummary.IControlSummary) {
|
||||
controlSARIFSeverity := string(scoreFactorToSARIFSeverityLevel(control.GetScoreFactor()))
|
||||
|
||||
configuration := sarif.NewReportingConfiguration().WithLevel(controlSARIFSeverity)
|
||||
|
||||
scanRun.AddRule(control.GetID()).
|
||||
WithDefaultConfiguration(configuration).
|
||||
WithShortDescription(sarif.NewMultiformatMessageString(control.GetName())).
|
||||
WithFullDescription(sarif.NewMultiformatMessageString(control.GetDescription())).
|
||||
WithHelp(sarif.NewMultiformatMessageString(sp.generateRemediationMessage(control)))
|
||||
}
|
||||
|
||||
// addResult adds a result of checking a rule to the scan run based on the given control summary
|
||||
func (sp *SARIFPrinter) addResult(scanRun *sarif.Run, ctl reportsummary.IControlSummary, filepath string, location locationresolver.Location) {
|
||||
scanRun.CreateResultForRule(ctl.GetID()).
|
||||
WithMessage(sarif.NewTextMessage(ctl.GetDescription())).
|
||||
AddLocation(
|
||||
sarif.NewLocationWithPhysicalLocation(
|
||||
sarif.NewPhysicalLocation().
|
||||
WithArtifactLocation(
|
||||
sarif.NewSimpleArtifactLocation(filepath),
|
||||
).WithRegion(
|
||||
sarif.NewRegion().WithStartLine(location.Line).WithStartColumn(location.Column),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func (sp *SARIFPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
|
||||
report, err := sarif.New(sarif.Version210)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
run := sarif.NewRunWithInformationURI(toolName, toolInfoURI)
|
||||
basePath := getBasePathFromMetadata(*opaSessionObj)
|
||||
|
||||
for resourceID, result := range opaSessionObj.ResourcesResult {
|
||||
if result.GetStatus(nil).IsFailed() {
|
||||
resourceSource := opaSessionObj.ResourceSource[resourceID]
|
||||
filepath := resourceSource.RelativePath
|
||||
|
||||
// Github Code Scanning considers results not associated to a file path meaningless and invalid when uploading
|
||||
if filepath == "" || basePath == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
rsrcAbsPath := path.Join(basePath, filepath)
|
||||
locationResolver, err := locationresolver.NewFixPathLocationResolver(rsrcAbsPath)
|
||||
if err != nil {
|
||||
logger.L().Debug("failed to create location resolver", helpers.Error(err))
|
||||
}
|
||||
|
||||
for _, ac := range result.AssociatedControls {
|
||||
if ac.GetStatus(nil).IsFailed() {
|
||||
ctl := opaSessionObj.Report.SummaryDetails.Controls.GetControl(reportsummary.EControlCriteriaID, ac.GetID())
|
||||
location := sp.resolveFixLocation(opaSessionObj, locationResolver, &ac, resourceID)
|
||||
|
||||
sp.addRule(run, ctl)
|
||||
sp.addResult(run, ctl, filepath, location)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
report.AddRun(run)
|
||||
|
||||
report.PrettyWrite(sp.writer)
|
||||
}
|
||||
|
||||
func (sp *SARIFPrinter) resolveFixLocation(opaSessionObj *cautils.OPASessionObj, locationResolver *locationresolver.FixPathLocationResolver, ac *resourcesresults.ResourceAssociatedControl, resourceID string) locationresolver.Location {
|
||||
defaultLocation := locationresolver.Location{Line: 1, Column: 1}
|
||||
if locationResolver == nil {
|
||||
return defaultLocation
|
||||
}
|
||||
|
||||
fixPaths := failedPathsToString(ac)
|
||||
if len(fixPaths) == 0 {
|
||||
fixPaths = fixPathsToString(ac)
|
||||
}
|
||||
var fixPath string
|
||||
if len(fixPaths) > 0 {
|
||||
fixPath = fixPaths[0]
|
||||
}
|
||||
|
||||
var location locationresolver.Location
|
||||
if fixPath == "" {
|
||||
return defaultLocation
|
||||
}
|
||||
|
||||
resource := opaSessionObj.AllResources[resourceID]
|
||||
localworkload, ok := resource.(*localworkload.LocalWorkload)
|
||||
if !ok {
|
||||
return defaultLocation
|
||||
}
|
||||
|
||||
splittedPath := strings.Split(localworkload.GetPath(), ":")
|
||||
if len(splittedPath) <= 1 {
|
||||
return defaultLocation
|
||||
}
|
||||
|
||||
docIndex, _ := strconv.Atoi(splittedPath[1])
|
||||
location, _ = locationResolver.ResolveLocation(fixPath, docIndex)
|
||||
if location.Line == 0 {
|
||||
return defaultLocation
|
||||
}
|
||||
|
||||
return location
|
||||
}
|
||||
|
||||
func getBasePathFromMetadata(opaSessionObj cautils.OPASessionObj) string {
|
||||
if opaSessionObj.Metadata.ScanMetadata.ScanningTarget == v2.GitLocal {
|
||||
return opaSessionObj.Metadata.ContextMetadata.RepoContextMetadata.LocalRootPath
|
||||
}
|
||||
|
||||
if opaSessionObj.Metadata.ScanMetadata.ScanningTarget == v2.Directory {
|
||||
return opaSessionObj.Metadata.ContextMetadata.DirectoryContextMetadata.BasePath
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// generateRemediationMessage generates a remediation message for the given control summary
|
||||
func (sp *SARIFPrinter) generateRemediationMessage(control reportsummary.IControlSummary) string {
|
||||
return fmt.Sprintf("Remediation: %s", control.GetRemediation())
|
||||
}
|
||||
27
core/pkg/resultshandling/printer/v2/sarifprinter_test.go
Normal file
27
core/pkg/resultshandling/printer/v2/sarifprinter_test.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package v2
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test_scoreToSeverityLevel(t *testing.T) {
|
||||
tc := []struct {
|
||||
Name string
|
||||
ScoreFactor float32
|
||||
ExpectedSARIFLevel sarifSeverityLevel
|
||||
}{
|
||||
{"Score factor 1.0 should map to 'note' SARIF level", 1.0, sarifSeverityLevelNote},
|
||||
{"Score facore 4.0 should map to 'warning' SARIF level", 4.0, sarifSeverityLevelWarning},
|
||||
{"Score facore 7.0 should map to 'warning' SARIF level", 7.0, sarifSeverityLevelWarning},
|
||||
{"Score facore 9.0 should map to 'error' SARIF level", 9.0, sarifSeverityLevelError},
|
||||
}
|
||||
|
||||
for _, testCase := range tc {
|
||||
t.Run(testCase.Name, func(t *testing.T) {
|
||||
got := scoreFactorToSARIFSeverityLevel(testCase.ScoreFactor)
|
||||
want := testCase.ExpectedSARIFLevel
|
||||
|
||||
if got != want {
|
||||
t.Errorf("got %s, want %s", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ func (reportMock *ReportMock) SetClusterName(clusterName string) {
|
||||
}
|
||||
|
||||
func (reportMock *ReportMock) GetURL() string {
|
||||
u := fmt.Sprintf("https://%s/account/sign-up", getter.GetKSCloudAPIConnector().GetFrontendURL())
|
||||
u := fmt.Sprintf("https://%s/account/sign-up", getter.GetKSCloudAPIConnector().GetCloudUIURL())
|
||||
if reportMock.query != "" {
|
||||
u += fmt.Sprintf("?%s", reportMock.query)
|
||||
}
|
||||
@@ -44,8 +44,9 @@ func (reportMock *ReportMock) DisplayReportURL() {
|
||||
sep := "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
|
||||
message := sep + "\n"
|
||||
message += "Scan results have not been submitted: " + reportMock.message + "\n"
|
||||
message += "Sign up for free: "
|
||||
message += reportMock.GetURL() + "\n"
|
||||
if reportMock.query != "" {
|
||||
message += "For more details: " + reportMock.query + "\n"
|
||||
}
|
||||
message += sep + "\n"
|
||||
cautils.InfoTextDisplay(os.Stderr, fmt.Sprintf("\n%s\n", message))
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ func (report *ReportEventReceiver) prepareReport(opaSessionObj *cautils.OPASessi
|
||||
|
||||
func (report *ReportEventReceiver) GetURL() string {
|
||||
u := url.URL{}
|
||||
u.Host = getter.GetKSCloudAPIConnector().GetFrontendURL()
|
||||
u.Host = getter.GetKSCloudAPIConnector().GetCloudUIURL()
|
||||
|
||||
parseHost(&u)
|
||||
report.addPathURL(&u)
|
||||
|
||||
@@ -105,14 +105,4 @@ func TestGetURL(t *testing.T) {
|
||||
)
|
||||
assert.Equal(t, "https://cloud.armosec.io/account/sign-up?customerGUID=1234&invitationToken=token&utm_campaign=Submit&utm_medium=CLI&utm_source=GitHub", reporter.GetURL())
|
||||
}
|
||||
// Test None submit url
|
||||
{
|
||||
reporter := NewReportMock(NO_SUBMIT_QUERY, "")
|
||||
assert.Equal(t, "https://cloud.armosec.io/account/sign-up?utm_source=GitHub&utm_medium=CLI&utm_campaign=no_submit", reporter.GetURL())
|
||||
}
|
||||
// Test None report url
|
||||
{
|
||||
reporter := NewReportMock("", "")
|
||||
assert.Equal(t, "https://cloud.armosec.io/account/sign-up", reporter.GetURL())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
func (report *ReportEventReceiver) initEventReceiverURL() {
|
||||
urlObj := url.URL{}
|
||||
urlObj.Host = getter.GetKSCloudAPIConnector().GetReportReceiverURL()
|
||||
urlObj.Host = getter.GetKSCloudAPIConnector().GetCloudReportURL()
|
||||
parseHost(&urlObj)
|
||||
|
||||
urlObj.Path = "/k8s/v2/postureReport"
|
||||
|
||||
@@ -86,7 +86,7 @@ func NewPrinter(printFormat, formatVersion string, verboseMode bool, viewType ca
|
||||
case "v2":
|
||||
return printerv2.NewJsonPrinter()
|
||||
default:
|
||||
logger.L().Warning("Deprecated format version", helpers.String("run", "--format-version=v2"))
|
||||
logger.L().Warning("Deprecated format version", helpers.String("run", "--format-version=v2"), helpers.String("This will not be supported after", "1/Jan/2023"))
|
||||
return printerv1.NewJsonPrinter()
|
||||
}
|
||||
case printer.JunitResultFormat:
|
||||
@@ -97,6 +97,8 @@ func NewPrinter(printFormat, formatVersion string, verboseMode bool, viewType ca
|
||||
return printerv2.NewPdfPrinter()
|
||||
case printer.HtmlFormat:
|
||||
return printerv2.NewHtmlPrinter()
|
||||
case printer.SARIFFormat:
|
||||
return printerv2.NewSARIFPrinter()
|
||||
default:
|
||||
return printerv2.NewPrettyPrinter(verboseMode, formatVersion, viewType)
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
BIN
docs/ks-cli-arch.png
Normal file
BIN
docs/ks-cli-arch.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
docs/ks-operator-arch.png
Normal file
BIN
docs/ks-operator-arch.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
@@ -64,24 +64,24 @@ But if you wish to exclude all namespaces **OR** any resource with the label `"e
|
||||
|
||||
Same works with the `posturePolicies` list ->
|
||||
|
||||
e.g. If you wish to exclude the resources declared in the `resources` list that failed when scanning the `NSA` framework **AND** failed the `Allowed hostPath` control, the `posturePolicies` list should look as follows:
|
||||
e.g. If you wish to exclude the resources declared in the `resources` list that failed when scanning the `NSA` framework **AND** failed the `HostPath mount` control, the `posturePolicies` list should look as follows:
|
||||
```
|
||||
"posturePolicies": [
|
||||
{
|
||||
"frameworkName": "NSA",
|
||||
"controlName": "Allowed hostPath"
|
||||
"controlName": "HostPath mount"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
But if you wish to exclude the resources declared in the `resources` list that failed when scanning the `NSA` framework **OR** failed the `Allowed hostPath` control, the `posturePolicies` list should look as follows:
|
||||
But if you wish to exclude the resources declared in the `resources` list that failed when scanning the `NSA` framework **OR** failed the `HostPath mount` control, the `posturePolicies` list should look as follows:
|
||||
```
|
||||
"posturePolicies": [
|
||||
{
|
||||
"frameworkName": "NSA"
|
||||
},
|
||||
{
|
||||
"controlName": "Allowed hostPath"
|
||||
"controlName": "HostPath mount"
|
||||
}
|
||||
]
|
||||
```
|
||||
@@ -122,7 +122,7 @@ The resources
|
||||
]
|
||||
```
|
||||
|
||||
### Exclude deployments in the default namespace that failed the "Allowed hostPath" control
|
||||
### Exclude deployments in the default namespace that failed the "HostPath mount" control
|
||||
```
|
||||
[
|
||||
{
|
||||
@@ -142,7 +142,7 @@ The resources
|
||||
],
|
||||
"posturePolicies": [
|
||||
{
|
||||
"controlName": "Allowed hostPath"
|
||||
"controlName": "HostPath mount"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
],
|
||||
"posturePolicies": [
|
||||
{
|
||||
"controlName": "Allowed hostPath"
|
||||
"controlName": "HostPath mount"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
],
|
||||
"posturePolicies": [
|
||||
{
|
||||
"controlName": "Allowed hostPath"
|
||||
"controlName": "HostPath mount"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -98,56 +98,56 @@ kubescape_object_failed_count{framework="NSA",control="Privileged container",nam
|
||||
kubescape_object_failed_count{framework="NSA",control="Privileged container",namespace="kubescape",name="armo-kubescape",groupVersionKind="batch/v1/CronJob"} 1
|
||||
# Failed object from "NSA" control "Privileged container"
|
||||
kubescape_object_failed_count{framework="NSA",control="Privileged container",namespace="kubescape",name="armo-scan-scheduler",groupVersionKind="batch/v1/CronJob"} 1
|
||||
# Number of resources found as part of NSA control Allowed hostPath
|
||||
kubescape_resources_found_count{framework="NSA",control="Allowed hostPath"} 22
|
||||
# Number of resources excluded as part of NSA control Allowed hostPath
|
||||
kubescape_resources_excluded_count{framework="NSA",control="Allowed hostPath"} 0
|
||||
# Number of resources failed as part of NSA control Allowed hostPath
|
||||
kubescape_resources_failed_count{framework="NSA",control="Allowed hostPath"} 7
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="kube-system",name="kube-proxy",groupVersionKind="apps/v1/DaemonSet"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="kube-system",name="etcd-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="kube-system",name="kube-controller-manager-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="kube-system",name="storage-provisioner",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-webhook",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-websocket",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="kubescape",name="armo-web-socket",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="kubescape",name="armo-vuln-scan",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-oracle",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-posture",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-rbac",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-vuln-scan",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-audit",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-dashboard-aggregator",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-notification-server",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-ocimage",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="kube-system",name="coredns",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="nginx-ingress",name="nginx-ingress",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="kubescape",name="armo-kubescape",groupVersionKind="batch/v1/CronJob"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="kubescape",name="armo-scan-scheduler",groupVersionKind="batch/v1/CronJob"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="kube-system",name="kube-apiserver-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="kube-system",name="kube-scheduler-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Number of resources found as part of NSA control HostPath mount
|
||||
kubescape_resources_found_count{framework="NSA",control="HostPath mount"} 22
|
||||
# Number of resources excluded as part of NSA control HostPath mount
|
||||
kubescape_resources_excluded_count{framework="NSA",control="HostPath mount"} 0
|
||||
# Number of resources failed as part of NSA control HostPath mount
|
||||
kubescape_resources_failed_count{framework="NSA",control="HostPath mount"} 7
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="kube-system",name="kube-proxy",groupVersionKind="apps/v1/DaemonSet"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="kube-system",name="etcd-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="kube-system",name="kube-controller-manager-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="kube-system",name="storage-provisioner",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="cyberarmor-system",name="ca-webhook",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="cyberarmor-system",name="ca-websocket",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="kubescape",name="armo-web-socket",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="kubescape",name="armo-vuln-scan",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="cyberarmor-system",name="ca-oracle",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="cyberarmor-system",name="ca-posture",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="cyberarmor-system",name="ca-rbac",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="cyberarmor-system",name="ca-vuln-scan",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="cyberarmor-system",name="ca-audit",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="cyberarmor-system",name="ca-dashboard-aggregator",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="cyberarmor-system",name="ca-notification-server",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="cyberarmor-system",name="ca-ocimage",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="kube-system",name="coredns",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="nginx-ingress",name="nginx-ingress",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="kubescape",name="armo-kubescape",groupVersionKind="batch/v1/CronJob"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="kubescape",name="armo-scan-scheduler",groupVersionKind="batch/v1/CronJob"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="kube-system",name="kube-apiserver-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="kube-system",name="kube-scheduler-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Number of resources found as part of NSA control Automatic mapping of service account
|
||||
kubescape_resources_found_count{framework="NSA",control="Automatic mapping of service account"} 47
|
||||
# Number of resources excluded as part of NSA control Automatic mapping of service account
|
||||
@@ -2668,56 +2668,56 @@ kubescape_object_failed_count{framework="ArmoBest",control="Privileged container
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Privileged container",namespace="kubescape",name="armo-kubescape",groupVersionKind="batch/v1/CronJob"} 1
|
||||
# Failed object from "ArmoBest" control "Privileged container"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Privileged container",namespace="kubescape",name="armo-scan-scheduler",groupVersionKind="batch/v1/CronJob"} 1
|
||||
# Number of resources found as part of ArmoBest control Allowed hostPath
|
||||
kubescape_resources_found_count{framework="ArmoBest",control="Allowed hostPath"} 22
|
||||
# Number of resources excluded as part of ArmoBest control Allowed hostPath
|
||||
kubescape_resources_excluded_count{framework="ArmoBest",control="Allowed hostPath"} 0
|
||||
# Number of resources failed as part of ArmoBest control Allowed hostPath
|
||||
kubescape_resources_failed_count{framework="ArmoBest",control="Allowed hostPath"} 7
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="kube-system",name="kube-proxy",groupVersionKind="apps/v1/DaemonSet"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="kube-system",name="kube-controller-manager-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="kube-system",name="storage-provisioner",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="kube-system",name="etcd-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="kubescape",name="armo-web-socket",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-webhook",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-websocket",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="kube-system",name="coredns",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="nginx-ingress",name="nginx-ingress",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="kubescape",name="armo-vuln-scan",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-oracle",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-posture",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-rbac",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-vuln-scan",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-audit",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-dashboard-aggregator",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-notification-server",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-ocimage",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="kubescape",name="armo-kubescape",groupVersionKind="batch/v1/CronJob"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="kubescape",name="armo-scan-scheduler",groupVersionKind="batch/v1/CronJob"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="kube-system",name="kube-apiserver-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="kube-system",name="kube-scheduler-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Number of resources found as part of ArmoBest control HostPath mount
|
||||
kubescape_resources_found_count{framework="ArmoBest",control="HostPath mount"} 22
|
||||
# Number of resources excluded as part of ArmoBest control HostPath mount
|
||||
kubescape_resources_excluded_count{framework="ArmoBest",control="HostPath mount"} 0
|
||||
# Number of resources failed as part of ArmoBest control HostPath mount
|
||||
kubescape_resources_failed_count{framework="ArmoBest",control="HostPath mount"} 7
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="kube-system",name="kube-proxy",groupVersionKind="apps/v1/DaemonSet"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="kube-system",name="kube-controller-manager-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="kube-system",name="storage-provisioner",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="kube-system",name="etcd-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="kubescape",name="armo-web-socket",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="cyberarmor-system",name="ca-webhook",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="cyberarmor-system",name="ca-websocket",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="kube-system",name="coredns",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="nginx-ingress",name="nginx-ingress",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="kubescape",name="armo-vuln-scan",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="cyberarmor-system",name="ca-oracle",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="cyberarmor-system",name="ca-posture",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="cyberarmor-system",name="ca-rbac",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="cyberarmor-system",name="ca-vuln-scan",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="cyberarmor-system",name="ca-audit",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="cyberarmor-system",name="ca-dashboard-aggregator",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="cyberarmor-system",name="ca-notification-server",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="cyberarmor-system",name="ca-ocimage",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="kubescape",name="armo-kubescape",groupVersionKind="batch/v1/CronJob"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="kubescape",name="armo-scan-scheduler",groupVersionKind="batch/v1/CronJob"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="kube-system",name="kube-apiserver-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="kube-system",name="kube-scheduler-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Number of resources found as part of ArmoBest control Automatic mapping of service account
|
||||
kubescape_resources_found_count{framework="ArmoBest",control="Automatic mapping of service account"} 47
|
||||
# Number of resources excluded as part of ArmoBest control Automatic mapping of service account
|
||||
|
||||
@@ -54,26 +54,26 @@ kubescape_resources_excluded_count{framework="NSA",control="Privileged container
|
||||
kubescape_resources_failed_count{framework="NSA",control="Privileged container"} 1
|
||||
# Failed object from "NSA" control "Privileged container"
|
||||
kubescape_object_failed_count{framework="NSA",control="Privileged container",namespace="kube-system",name="kube-proxy",groupVersionKind="apps/v1/DaemonSet"} 1
|
||||
# Number of resources found as part of NSA control Allowed hostPath
|
||||
kubescape_resources_found_count{framework="NSA",control="Allowed hostPath"} 22
|
||||
# Number of resources excluded as part of NSA control Allowed hostPath
|
||||
kubescape_resources_excluded_count{framework="NSA",control="Allowed hostPath"} 0
|
||||
# Number of resources failed as part of NSA control Allowed hostPath
|
||||
kubescape_resources_failed_count{framework="NSA",control="Allowed hostPath"} 7
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="kube-system",name="etcd-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="kube-system",name="kube-controller-manager-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="kube-system",name="storage-provisioner",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="kubescape",name="armo-web-socket",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-websocket",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-webhook",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="NSA",control="Allowed hostPath",namespace="kube-system",name="kube-proxy",groupVersionKind="apps/v1/DaemonSet"} 1
|
||||
# Number of resources found as part of NSA control HostPath mount
|
||||
kubescape_resources_found_count{framework="NSA",control="HostPath mount"} 22
|
||||
# Number of resources excluded as part of NSA control HostPath mount
|
||||
kubescape_resources_excluded_count{framework="NSA",control="HostPath mount"} 0
|
||||
# Number of resources failed as part of NSA control HostPath mount
|
||||
kubescape_resources_failed_count{framework="NSA",control="HostPath mount"} 7
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="kube-system",name="etcd-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="kube-system",name="kube-controller-manager-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="kube-system",name="storage-provisioner",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="kubescape",name="armo-web-socket",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="cyberarmor-system",name="ca-websocket",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="cyberarmor-system",name="ca-webhook",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "NSA" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="NSA",control="HostPath mount",namespace="kube-system",name="kube-proxy",groupVersionKind="apps/v1/DaemonSet"} 1
|
||||
# Number of resources found as part of NSA control Automatic mapping of service account
|
||||
kubescape_resources_found_count{framework="NSA",control="Automatic mapping of service account"} 47
|
||||
# Number of resources excluded as part of NSA control Automatic mapping of service account
|
||||
@@ -872,26 +872,26 @@ kubescape_resources_excluded_count{framework="ArmoBest",control="Privileged cont
|
||||
kubescape_resources_failed_count{framework="ArmoBest",control="Privileged container"} 1
|
||||
# Failed object from "ArmoBest" control "Privileged container"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Privileged container",namespace="kube-system",name="kube-proxy",groupVersionKind="apps/v1/DaemonSet"} 1
|
||||
# Number of resources found as part of ArmoBest control Allowed hostPath
|
||||
kubescape_resources_found_count{framework="ArmoBest",control="Allowed hostPath"} 22
|
||||
# Number of resources excluded as part of ArmoBest control Allowed hostPath
|
||||
kubescape_resources_excluded_count{framework="ArmoBest",control="Allowed hostPath"} 0
|
||||
# Number of resources failed as part of ArmoBest control Allowed hostPath
|
||||
kubescape_resources_failed_count{framework="ArmoBest",control="Allowed hostPath"} 7
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="kube-system",name="kube-proxy",groupVersionKind="apps/v1/DaemonSet"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="kube-system",name="etcd-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="kube-system",name="kube-controller-manager-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="kube-system",name="storage-provisioner",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="kubescape",name="armo-web-socket",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-webhook",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "Allowed hostPath"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="Allowed hostPath",namespace="cyberarmor-system",name="ca-websocket",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Number of resources found as part of ArmoBest control HostPath mount
|
||||
kubescape_resources_found_count{framework="ArmoBest",control="HostPath mount"} 22
|
||||
# Number of resources excluded as part of ArmoBest control HostPath mount
|
||||
kubescape_resources_excluded_count{framework="ArmoBest",control="HostPath mount"} 0
|
||||
# Number of resources failed as part of ArmoBest control HostPath mount
|
||||
kubescape_resources_failed_count{framework="ArmoBest",control="HostPath mount"} 7
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="kube-system",name="kube-proxy",groupVersionKind="apps/v1/DaemonSet"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="kube-system",name="etcd-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="kube-system",name="kube-controller-manager-david-virtualbox",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="kube-system",name="storage-provisioner",groupVersionKind="v1/Pod"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="kubescape",name="armo-web-socket",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="cyberarmor-system",name="ca-webhook",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Failed object from "ArmoBest" control "HostPath mount"
|
||||
kubescape_object_failed_count{framework="ArmoBest",control="HostPath mount",namespace="cyberarmor-system",name="ca-websocket",groupVersionKind="apps/v1/Deployment"} 1
|
||||
# Number of resources found as part of ArmoBest control Automatic mapping of service account
|
||||
kubescape_resources_found_count{framework="ArmoBest",control="Automatic mapping of service account"} 47
|
||||
# Number of resources excluded as part of ArmoBest control Automatic mapping of service account
|
||||
|
||||
72
go.mod
72
go.mod
@@ -3,7 +3,8 @@ module github.com/kubescape/kubescape/v2
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/armosec/armoapi-go v0.0.115
|
||||
cloud.google.com/go/containeranalysis v0.4.0
|
||||
github.com/armosec/armoapi-go v0.0.119
|
||||
github.com/armosec/go-git-url v0.0.15
|
||||
github.com/armosec/utils-go v0.0.12
|
||||
github.com/armosec/utils-k8s-go v0.0.12
|
||||
@@ -15,35 +16,44 @@ require (
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/johnfercher/maroto v0.37.0
|
||||
github.com/kubescape/go-logger v0.0.6
|
||||
github.com/kubescape/k8s-interface v0.0.83
|
||||
github.com/kubescape/opa-utils v0.0.194
|
||||
github.com/kubescape/rbac-utils v0.0.17
|
||||
github.com/kubescape/k8s-interface v0.0.89
|
||||
github.com/kubescape/opa-utils v0.0.200
|
||||
github.com/kubescape/rbac-utils v0.0.19
|
||||
github.com/libgit2/git2go/v33 v33.0.9
|
||||
github.com/mattn/go-isatty v0.0.14
|
||||
github.com/mikefarah/yq/v4 v4.29.1
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/open-policy-agent/opa v0.42.1
|
||||
github.com/spf13/cobra v1.5.0
|
||||
github.com/open-policy-agent/opa v0.45.0
|
||||
github.com/owenrumney/go-sarif/v2 v2.1.2
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/whilp/git-urls v1.0.0
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
google.golang.org/api v0.85.0
|
||||
google.golang.org/genproto v0.0.0-20220708155623-50e5f4832e73
|
||||
google.golang.org/protobuf v1.28.1
|
||||
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
helm.sh/helm/v3 v3.9.0
|
||||
k8s.io/api v0.24.3
|
||||
k8s.io/apimachinery v0.24.3
|
||||
k8s.io/client-go v0.24.3
|
||||
k8s.io/utils v0.0.0-20220706174534-f6158b442e7c
|
||||
k8s.io/api v0.25.3
|
||||
k8s.io/apimachinery v0.25.3
|
||||
k8s.io/client-go v0.25.3
|
||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed
|
||||
sigs.k8s.io/kustomize/api v0.11.4
|
||||
sigs.k8s.io/kustomize/kyaml v0.13.6
|
||||
sigs.k8s.io/yaml v1.3.0
|
||||
)
|
||||
|
||||
require github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute v1.7.0 // indirect
|
||||
cloud.google.com/go/container v1.2.0 // indirect
|
||||
cloud.google.com/go/grafeas v0.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go v66.0.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.24 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.27 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
@@ -60,8 +70,10 @@ require (
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/a8m/envsubst v1.3.0 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||
github.com/agnivade/levenshtein v1.0.1 // indirect
|
||||
github.com/agnivade/levenshtein v1.1.1 // indirect
|
||||
github.com/alecthomas/participle/v2 v2.0.0-beta.5 // indirect
|
||||
github.com/aws/aws-sdk-go v1.44.51 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.16.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.15.13 // indirect
|
||||
@@ -83,7 +95,8 @@ require (
|
||||
github.com/docker/docker v20.10.17+incompatible // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible // indirect
|
||||
github.com/elliotchance/orderedmap v1.5.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
@@ -96,6 +109,8 @@ require (
|
||||
github.com/go-openapi/jsonreference v0.19.5 // indirect
|
||||
github.com/go-openapi/swag v0.19.14 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/goccy/go-json v0.9.11 // indirect
|
||||
github.com/goccy/go-yaml v1.9.6 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
@@ -103,18 +118,19 @@ require (
|
||||
github.com/google/gnostic v0.5.7-v3refs // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.4.0 // indirect
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jinzhu/copier v0.3.5 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/jung-kurt/gofpdf v1.16.2 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mailru/easyjson v0.7.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
@@ -137,7 +153,7 @@ require (
|
||||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/vektah/gqlparser/v2 v2.4.5 // indirect
|
||||
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
@@ -150,28 +166,26 @@ require (
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.22.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220630143837-2104d58473e0 // indirect
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
|
||||
gonum.org/v1/gonum v0.9.1 // indirect
|
||||
google.golang.org/api v0.84.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220708155623-50e5f4832e73 // indirect
|
||||
google.golang.org/grpc v1.47.0 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
google.golang.org/grpc v1.49.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.24.2 // indirect
|
||||
k8s.io/klog/v2 v2.60.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect
|
||||
k8s.io/klog/v2 v2.70.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
|
||||
sigs.k8s.io/controller-runtime v0.12.3 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
)
|
||||
|
||||
replace github.com/libgit2/git2go/v33 => ./git2go
|
||||
|
||||
@@ -55,7 +55,7 @@ When scanning is in progress
|
||||
}
|
||||
```
|
||||
### Check scanning progress status
|
||||
Check the scanning status - is the scanning in progress or done? This is meant for a waiting mechanize since the API does not return the entire results object when the scanning is done
|
||||
Check the scanning status - is the scanning in progress or done. This is meant for a waiting mechanize since the API does not return the entire results object when the scanning is done
|
||||
|
||||
* GET `/v1/status` - Request kubescape scan status
|
||||
* * query `id=<string>` -> Check status of a specific scan. If empty, it will check if any scan is still in progress
|
||||
@@ -101,7 +101,6 @@ When scanning is not in progress
|
||||
"excludedNamespaces": [<str>], // list of namespaces to exclude (same as 'kubescape scan --excluded-namespaces')
|
||||
"includeNamespaces": [<str>], // list of namespaces to include (same as 'kubescape scan --include-namespaces')
|
||||
"useCachedArtifacts"`: <bool>, // use the cached artifacts instead of downloading (offline support)
|
||||
"submit": <bool>, // submit results to Kubescape cloud (same as 'kubescape scan --submit')
|
||||
"hostScanner": <bool>, // deploy Kubescape K8s host-scanner DaemonSet in the scanned cluster (same as 'kubescape scan --enable-host-scan')
|
||||
"keepLocal": <bool>, // do not submit results to Kubescape cloud (same as 'kubescape scan --keep-local')
|
||||
"account": <str>, // account ID (same as 'kubescape scan --account')
|
||||
@@ -132,7 +131,7 @@ When scanning is not in progress
|
||||
|
||||
1. Trigger kubescape scan
|
||||
```bash
|
||||
curl --header "Content-Type: application/json" --request POST --data '{"hostScanner":true, "submit": true}' http://127.0.0.1:8080/v1/scan
|
||||
curl --header "Content-Type: application/json" --request POST --data '{"hostScanner":true}' http://127.0.0.1:8080/v1/scan
|
||||
```
|
||||
|
||||
2. Get kubescape scan results
|
||||
@@ -143,13 +142,13 @@ When scanning is not in progress
|
||||
#### Trigger scan and wait for the scan to end
|
||||
|
||||
```bash
|
||||
curl --header "Content-Type: application/json" --request POST --data '{"hostScanner":true, "submit": true}' http://127.0.0.1:8080/v1/scan?wait -o scan_results.json
|
||||
curl --header "Content-Type: application/json" --request POST --data '{"hostScanner":true}' http://127.0.0.1:8080/v1/scan?wait -o scan_results.json
|
||||
```
|
||||
#### Scan single namespace with a specific framework
|
||||
```bash
|
||||
curl --header "Content-Type: application/json" \
|
||||
--request POST \
|
||||
--data '{"hostScanner":true, "submit":true, "includeNamespaces": ["kubescape"], "targetType": "framework", "targetNames": ["nsa"] }' \
|
||||
--data '{"hostScanner":true, "includeNamespaces": ["kubescape"], "targetType": "framework", "targetNames": ["nsa"] }' \
|
||||
http://127.0.0.1:8080/v1/scan
|
||||
```
|
||||
|
||||
@@ -171,7 +170,6 @@ go tool pprof http://localhost:6060/debug/pprof/heap
|
||||
## Supported environment variables
|
||||
|
||||
* `KS_ACCOUNT`: Account ID
|
||||
* `KS_SUBMIT`: Submit the results to the Kubescape SaaS version
|
||||
* `KS_EXCLUDE_NAMESPACES`: List of namespaces to exclude, e.g. `KS_EXCLUDE_NAMESPACES=kube-system,kube-public`
|
||||
* `KS_INCLUDE_NAMESPACES`: List of namespaces to include, rest of the namespaces will be ignored. e.g. `KS_INCLUDE_NAMESPACES=dev,prod`
|
||||
* `KS_HOST_SCAN_YAML`: Full path to the host scanner YAML
|
||||
|
||||
@@ -6,60 +6,63 @@ import subprocess
|
||||
|
||||
BASE_GETTER_CONST = "github.com/kubescape/kubescape/v2/core/cautils/getter"
|
||||
|
||||
def checkStatus(status, msg):
|
||||
def check_status(status, msg):
|
||||
if status != 0:
|
||||
sys.stderr.write(msg)
|
||||
exit(status)
|
||||
|
||||
|
||||
def getBuildDir():
|
||||
currentPlatform = platform.system()
|
||||
buildDir = "build/"
|
||||
def get_build_dir():
|
||||
current_platform = platform.system()
|
||||
build_dir = ""
|
||||
|
||||
if currentPlatform == "Windows": return os.path.join(buildDir, "windows-latest")
|
||||
if currentPlatform == "Linux": return os.path.join(buildDir, "ubuntu-latest")
|
||||
if currentPlatform == "Darwin": return os.path.join(buildDir, "macos-latest")
|
||||
raise OSError("Platform %s is not supported!" % (currentPlatform))
|
||||
if current_platform == "Windows": build_dir = "windows-latest"
|
||||
elif current_platform == "Linux": build_dir = "ubuntu-latest"
|
||||
elif current_platform == "Darwin": build_dir = "macos-latest"
|
||||
else: raise OSError("Platform %s is not supported!" % (current_platform))
|
||||
|
||||
def getPackageName():
|
||||
packageName = "kubescape"
|
||||
# if platform.system() == "Windows": packageName += ".exe"
|
||||
return os.path.join("build", build_dir)
|
||||
|
||||
return packageName
|
||||
def get_package_name():
|
||||
package_name = "kubescape"
|
||||
# TODO: if platform.system() == "Windows": packageName += ".exe" - we should find all places were we access the windows executable before changing the extension
|
||||
return package_name
|
||||
|
||||
|
||||
def main():
|
||||
print("Building Kubescape")
|
||||
|
||||
# print environment variables
|
||||
# print(os.environ)
|
||||
|
||||
# Set some variables
|
||||
packageName = getPackageName()
|
||||
buildUrl = "github.com/kubescape/kubescape/v2/core/cautils.BuildNumber"
|
||||
releaseVersion = os.getenv("RELEASE")
|
||||
package_name = get_package_name()
|
||||
build_url = "github.com/kubescape/kubescape/v2/core/cautils.BuildNumber"
|
||||
release_version = os.getenv("RELEASE")
|
||||
|
||||
client_var = "github.com/kubescape/kubescape/v2/core/cautils.Client"
|
||||
client_name = os.getenv("CLIENT")
|
||||
|
||||
# Create build directory
|
||||
buildDir = getBuildDir()
|
||||
build_dir = get_build_dir()
|
||||
|
||||
ks_file = os.path.join(buildDir, packageName)
|
||||
ks_file = os.path.join(build_dir, package_name)
|
||||
hash_file = ks_file + ".sha256"
|
||||
|
||||
if not os.path.isdir(buildDir):
|
||||
os.makedirs(buildDir)
|
||||
if not os.path.isdir(build_dir):
|
||||
os.makedirs(build_dir)
|
||||
|
||||
# Build kubescape
|
||||
ldflags = "-w -s"
|
||||
if releaseVersion:
|
||||
ldflags += " -X {}={}".format(buildUrl, releaseVersion)
|
||||
|
||||
build_command = ["go", "build", "-tags=static", "-o", ks_file, "-ldflags" ,ldflags]
|
||||
if release_version:
|
||||
ldflags += " -X {}={}".format(build_url, release_version)
|
||||
if client_name:
|
||||
ldflags += " -X {}={}".format(client_var, client_name)
|
||||
|
||||
build_command = ["go", "build", "-buildmode=pie", "-tags=static", "-o", ks_file, "-ldflags" ,ldflags]
|
||||
|
||||
print("Building kubescape and saving here: {}".format(ks_file))
|
||||
print("Build command: {}".format(" ".join(build_command)))
|
||||
|
||||
status = subprocess.call(build_command)
|
||||
checkStatus(status, "Failed to build kubescape")
|
||||
check_status(status, "Failed to build kubescape")
|
||||
|
||||
sha256 = hashlib.sha256()
|
||||
with open(ks_file, "rb") as kube:
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
```bash
|
||||
curl --header "Content-Type: application/json" \
|
||||
--request POST \
|
||||
--data '{"account":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","hostScanner":true, "submit":true}' \
|
||||
--data '{"account":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX","hostScanner":true}' \
|
||||
http://127.0.0.1:8080/v1/scan
|
||||
```
|
||||
|
||||
|
||||
@@ -12,18 +12,20 @@ require (
|
||||
github.com/gorilla/schema v1.2.0
|
||||
github.com/kubescape/go-logger v0.0.6
|
||||
github.com/kubescape/kubescape/v2 v2.0.0-00010101000000-000000000000
|
||||
github.com/kubescape/opa-utils v0.0.194
|
||||
github.com/kubescape/opa-utils v0.0.200
|
||||
github.com/stretchr/testify v1.8.0
|
||||
k8s.io/utils v0.0.0-20220706174534-f6158b442e7c
|
||||
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute v1.7.0 // indirect
|
||||
cloud.google.com/go/container v1.2.0 // indirect
|
||||
cloud.google.com/go/containeranalysis v0.4.0 // indirect
|
||||
cloud.google.com/go/grafeas v0.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go v66.0.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.24 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.27 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
@@ -40,9 +42,11 @@ require (
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/a8m/envsubst v1.3.0 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||
github.com/agnivade/levenshtein v1.0.1 // indirect
|
||||
github.com/armosec/armoapi-go v0.0.115 // indirect
|
||||
github.com/agnivade/levenshtein v1.1.1 // indirect
|
||||
github.com/alecthomas/participle/v2 v2.0.0-beta.5 // indirect
|
||||
github.com/armosec/armoapi-go v0.0.119 // indirect
|
||||
github.com/armosec/go-git-url v0.0.15 // indirect
|
||||
github.com/armosec/utils-k8s-go v0.0.12 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
|
||||
@@ -68,7 +72,8 @@ require (
|
||||
github.com/docker/docker v20.10.17+incompatible // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible // indirect
|
||||
github.com/elliotchance/orderedmap v1.5.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.8.0 // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/enescakir/emoji v1.0.0 // indirect
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
||||
@@ -92,6 +97,8 @@ require (
|
||||
github.com/go-openapi/validate v0.21.0 // indirect
|
||||
github.com/go-stack/stack v1.8.1 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/goccy/go-json v0.9.11 // indirect
|
||||
github.com/goccy/go-yaml v1.9.6 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
@@ -100,24 +107,27 @@ require (
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.4.0 // indirect
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jinzhu/copier v0.3.5 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/johnfercher/maroto v0.37.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/jung-kurt/gofpdf v1.16.2 // indirect
|
||||
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect
|
||||
github.com/kubescape/k8s-interface v0.0.83 // indirect
|
||||
github.com/kubescape/rbac-utils v0.0.17 // indirect
|
||||
github.com/kubescape/k8s-interface v0.0.89 // indirect
|
||||
github.com/kubescape/rbac-utils v0.0.19 // indirect
|
||||
github.com/libgit2/git2go/v33 v33.0.9 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/mikefarah/yq/v4 v4.29.1 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
@@ -128,9 +138,10 @@ require (
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/open-policy-agent/opa v0.42.1 // indirect
|
||||
github.com/open-policy-agent/opa v0.45.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
||||
github.com/owenrumney/go-sarif/v2 v2.1.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/pquerna/cachecontrol v0.1.0 // indirect
|
||||
@@ -140,7 +151,7 @@ require (
|
||||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/vektah/gqlparser/v2 v2.4.5 // indirect
|
||||
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
|
||||
github.com/whilp/git-urls v1.0.0 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.0 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
@@ -156,35 +167,37 @@ require (
|
||||
go.uber.org/zap v1.22.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
|
||||
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220630143837-2104d58473e0 // indirect
|
||||
golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
|
||||
gonum.org/v1/gonum v0.9.1 // indirect
|
||||
google.golang.org/api v0.84.0 // indirect
|
||||
google.golang.org/api v0.85.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220708155623-50e5f4832e73 // indirect
|
||||
google.golang.org/grpc v1.47.0 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
google.golang.org/grpc v1.49.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/op/go-logging.v1 v1.0.0-20160211212156-b2cb9fa56473 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
helm.sh/helm/v3 v3.9.0 // indirect
|
||||
k8s.io/api v0.24.3 // indirect
|
||||
k8s.io/api v0.25.3 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.24.2 // indirect
|
||||
k8s.io/apimachinery v0.24.3 // indirect
|
||||
k8s.io/client-go v0.24.3 // indirect
|
||||
k8s.io/klog/v2 v2.60.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect
|
||||
k8s.io/apimachinery v0.25.3 // indirect
|
||||
k8s.io/client-go v0.25.3 // indirect
|
||||
k8s.io/klog/v2 v2.70.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
|
||||
sigs.k8s.io/controller-runtime v0.12.3 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.11.4 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.13.6 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@ package v1
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
@@ -124,7 +124,7 @@ func getScanParamsFromRequest(r *http.Request, scanID string) (*scanRequestParam
|
||||
return scanRequestParams, fmt.Errorf("failed to parse query params, reason: %s", err.Error())
|
||||
}
|
||||
|
||||
readBuffer, err := ioutil.ReadAll(r.Body)
|
||||
readBuffer, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
// handler.writeError(w, fmt.Errorf("failed to read request body, reason: %s", err.Error()), scanID)
|
||||
return scanRequestParams, fmt.Errorf("failed to read request body, reason: %s", err.Error())
|
||||
|
||||
@@ -66,6 +66,6 @@ echo -e "\033[0m"
|
||||
$KUBESCAPE_EXEC version
|
||||
echo
|
||||
|
||||
echo -e "\033[35mUsage: $ $KUBESCAPE_EXEC scan --submit --enable-host-scan --verbose"
|
||||
echo -e "\033[35mUsage: $ $KUBESCAPE_EXEC scan --enable-host-scan --verbose"
|
||||
|
||||
echo -e "\033[0m"
|
||||
|
||||
2
main.go
2
main.go
@@ -3,10 +3,12 @@ package main
|
||||
import (
|
||||
logger "github.com/kubescape/go-logger"
|
||||
"github.com/kubescape/kubescape/v2/cmd"
|
||||
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := cmd.Execute(); err != nil {
|
||||
logger.L().Fatal(err.Error())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ def run(kubescape_exec:str):
|
||||
test_command(command=[kubescape_exec, "scan", "framework"])
|
||||
test_command(command=[kubescape_exec, "scan", "control"])
|
||||
test_command(command=[kubescape_exec, "submit", "results"])
|
||||
test_command(command=[kubescape_exec, "submit", "rbac"])
|
||||
|
||||
print("Done testing commands")
|
||||
|
||||
|
||||
@@ -13,15 +13,15 @@ def scan_all(kubescape_exec: str):
|
||||
|
||||
|
||||
def scan_control_name(kubescape_exec: str):
|
||||
return smoke_utils.run_command(command=[kubescape_exec, "scan", "control", 'Allowed hostPath', all_files, "--enable-host-scan=false"])
|
||||
return smoke_utils.run_command(command=[kubescape_exec, "scan", "control", 'HostPath mount', all_files, "--enable-host-scan=false"])
|
||||
|
||||
|
||||
def scan_control_id(kubescape_exec: str):
|
||||
return smoke_utils.run_command(command=[kubescape_exec, "scan", "control", 'C-0006', all_files, "--enable-host-scan=false"])
|
||||
return smoke_utils.run_command(command=[kubescape_exec, "scan", "control", 'C-0048', all_files, "--enable-host-scan=false"])
|
||||
|
||||
|
||||
def scan_controls(kubescape_exec: str):
|
||||
return smoke_utils.run_command(command=[kubescape_exec, "scan", "control", 'Allowed hostPath,Allow privilege escalation', all_files, "--enable-host-scan=false"])
|
||||
return smoke_utils.run_command(command=[kubescape_exec, "scan", "control", 'HostPath mount,Allow privilege escalation', all_files, "--enable-host-scan=false"])
|
||||
|
||||
|
||||
def scan_framework(kubescape_exec: str):
|
||||
|
||||
Reference in New Issue
Block a user