mirror of
https://github.com/kubescape/kubescape.git
synced 2026-02-24 14:54:01 +00:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc9b713851 | ||
|
|
3f87610e8c | ||
|
|
63968b564b | ||
|
|
e237c48186 | ||
|
|
622b121535 | ||
|
|
20774d4a40 | ||
|
|
7bb6bb85ec | ||
|
|
da908a84bc | ||
|
|
b515e259c0 | ||
|
|
facd551518 | ||
|
|
0fc569d9d9 | ||
|
|
da27a27ad5 | ||
|
|
5d4a20f622 | ||
|
|
70b15a373b | ||
|
|
01353f81b3 | ||
|
|
22f10b6581 | ||
|
|
785178ffb1 | ||
|
|
f9b5c58402 | ||
|
|
8ed6d63ce5 | ||
|
|
990a7c2052 | ||
|
|
09b0c09472 | ||
|
|
f83c38b58e | ||
|
|
51e600797a | ||
|
|
39d6d1fd26 | ||
|
|
2dff63b101 | ||
|
|
b928892f0a | ||
|
|
c0188ea51d | ||
|
|
fa376ed5a4 | ||
|
|
6382edeb6e | ||
|
|
61d6c2dd1f | ||
|
|
44194f0b4e | ||
|
|
7103c7d32c | ||
|
|
b4e1663cd1 | ||
|
|
47412c89ca | ||
|
|
20d65f2ed3 | ||
|
|
46a559fb1d | ||
|
|
2769b22721 | ||
|
|
60ec6e8294 | ||
|
|
63520f9aff | ||
|
|
333b55a9f2 | ||
|
|
c6d3fd1a82 | ||
|
|
8106133ed0 | ||
|
|
b36111f63e | ||
|
|
3ad0284394 | ||
|
|
245ebf8c41 | ||
|
|
8309562da1 | ||
|
|
de807a65a6 | ||
|
|
92fe583421 | ||
|
|
b7ec05e88a | ||
|
|
203e925888 | ||
|
|
fde5453bf3 | ||
|
|
4c6a65565b | ||
|
|
e60ecfb8f5 | ||
|
|
b72e2610ca | ||
|
|
8d4bae06bc | ||
|
|
847b597d0f | ||
|
|
db1743f617 | ||
|
|
7ac1b8aacf | ||
|
|
55f8cb1f0e | ||
|
|
93574736cd | ||
|
|
e43f4b1a37 | ||
|
|
ae00866005 | ||
|
|
21cb4dae29 | ||
|
|
7d3ac98998 |
24
.github/workflows/build.yaml
vendored
24
.github/workflows/build.yaml
vendored
@@ -101,28 +101,28 @@ jobs:
|
||||
id: image-name
|
||||
run: echo '::set-output name=IMAGE_NAME::quay.io/${{ github.repository_owner }}/kubescape'
|
||||
|
||||
- name: Build the Docker image
|
||||
run: docker 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 }}
|
||||
|
||||
- name: Re-Tag Image to latest
|
||||
run: docker tag ${{ steps.image-name.outputs.IMAGE_NAME }}:${{ steps.image-version.outputs.IMAGE_VERSION }} ${{ steps.image-name.outputs.IMAGE_NAME }}:latest
|
||||
|
||||
- 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: # Or as an environment variable
|
||||
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 }} --push --platform linux/amd64,linux/arm64
|
||||
|
||||
# - name: Login to GitHub Container Registry
|
||||
# uses: docker/login-action@v1
|
||||
# with:
|
||||
# registry: ghcr.io
|
||||
# username: ${{ github.actor }}
|
||||
# password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Push Docker image
|
||||
run: |
|
||||
docker push ${{ steps.image-name.outputs.IMAGE_NAME }}:${{ steps.image-version.outputs.IMAGE_VERSION }}
|
||||
docker push ${{ steps.image-name.outputs.IMAGE_NAME }}:latest
|
||||
|
||||
|
||||
# TODO - Wait for casign to support fixed tags -> https://github.com/sigstore/cosign/issues/1424
|
||||
# - name: Install cosign
|
||||
# uses: sigstore/cosign-installer@main
|
||||
|
||||
22
.github/workflows/build_dev.yaml
vendored
22
.github/workflows/build_dev.yaml
vendored
@@ -80,21 +80,17 @@ jobs:
|
||||
id: image-name
|
||||
run: echo '::set-output name=IMAGE_NAME::quay.io/${{ github.repository_owner }}/kubescape'
|
||||
|
||||
- name: Build the Docker image
|
||||
run: docker 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 }}
|
||||
|
||||
- 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: Login to GitHub Container Registry
|
||||
# uses: docker/login-action@v1
|
||||
# with:
|
||||
# registry: ghcr.io
|
||||
# username: ${{ github.actor }}
|
||||
# password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Push Docker image
|
||||
run: |
|
||||
docker push ${{ steps.image-name.outputs.IMAGE_NAME }}:${{ steps.image-version.outputs.IMAGE_VERSION }}
|
||||
|
||||
|
||||
- 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 }} --push --platform linux/amd64,linux/arm64
|
||||
|
||||
@@ -38,7 +38,7 @@ curl -s https://raw.githubusercontent.com/armosec/kubescape/master/install.sh |
|
||||
|
||||
## Run:
|
||||
```
|
||||
kubescape scan --submit --enable-host-scan --format-version v2 --verbose
|
||||
kubescape scan --submit --enable-host-scan --verbose
|
||||
```
|
||||
|
||||
<img src="docs/summary.png">
|
||||
@@ -276,6 +276,9 @@ kubescape submit results path/to/results.json
|
||||
|
||||
Scan the YAML files while writing them using the [vs code extension](https://github.com/armosec/vscode-kubescape/blob/master/README.md)
|
||||
|
||||
## Lens Extension
|
||||
|
||||
View Kubescape scan results directly in [Lens IDE](https://k8slens.dev/) using kubescape [Lens extension](https://github.com/armosec/lens-kubescape/blob/master/README.md)
|
||||
|
||||
# Under the hood
|
||||
|
||||
|
||||
@@ -31,14 +31,17 @@ RUN /work/build/ubuntu-latest/kubescape download artifacts -o /work/artifacts
|
||||
|
||||
FROM alpine
|
||||
|
||||
RUN addgroup -S ks && adduser -S ks -G ks
|
||||
USER ks
|
||||
WORKDIR /home/ks/
|
||||
RUN addgroup -S armo && adduser -S armo -G armo
|
||||
|
||||
RUN mkdir /home/armo/.kubescape
|
||||
COPY --from=builder /work/artifacts/ /home/armo/.kubescape
|
||||
|
||||
RUN chown -R armo:armo /home/armo/.kubescape
|
||||
|
||||
USER armo
|
||||
WORKDIR /home/armo
|
||||
|
||||
COPY --from=builder /work/httphandler/build/ubuntu-latest/kubescape /usr/bin/ksserver
|
||||
COPY --from=builder /work/build/ubuntu-latest/kubescape /usr/bin/kubescape
|
||||
|
||||
RUN mkdir /home/ks/.kubescape && chmod 777 -R /home/ks/.kubescape
|
||||
COPY --from=builder /work/artifacts/ /home/ks/.kubescape
|
||||
|
||||
ENTRYPOINT ["ksserver"]
|
||||
|
||||
@@ -6,11 +6,12 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
apisv1 "github.com/armosec/opa-utils/httpserver/apis/v1"
|
||||
|
||||
"github.com/armosec/kubescape/v2/core/cautils"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/logger"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
|
||||
"github.com/armosec/kubescape/v2/core/meta"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"github.com/enescakir/emoji"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -57,14 +58,14 @@ func getControlCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comman
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// flagValidationControl(scanInfo)
|
||||
scanInfo.PolicyIdentifier = []reporthandling.PolicyIdentifier{}
|
||||
scanInfo.PolicyIdentifier = []cautils.PolicyIdentifier{}
|
||||
|
||||
if len(args) == 0 {
|
||||
scanInfo.ScanAll = true
|
||||
} else { // expected control or list of control sepparated by ","
|
||||
|
||||
// Read controls from input args
|
||||
scanInfo.SetPolicyIdentifiers(strings.Split(args[0], ","), reporthandling.KindControl)
|
||||
scanInfo.SetPolicyIdentifiers(strings.Split(args[0], ","), apisv1.KindControl)
|
||||
|
||||
if len(args) > 1 {
|
||||
if len(args[1:]) == 0 || args[1] != "-" {
|
||||
|
||||
@@ -6,11 +6,12 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
apisv1 "github.com/armosec/opa-utils/httpserver/apis/v1"
|
||||
|
||||
"github.com/armosec/kubescape/v2/core/cautils"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/logger"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
|
||||
"github.com/armosec/kubescape/v2/core/meta"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"github.com/enescakir/emoji"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -95,7 +96,7 @@ func getFrameworkCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comm
|
||||
}
|
||||
scanInfo.FrameworkScan = true
|
||||
|
||||
scanInfo.SetPolicyIdentifiers(frameworks, reporthandling.KindFramework)
|
||||
scanInfo.SetPolicyIdentifiers(frameworks, apisv1.KindFramework)
|
||||
|
||||
results, err := ks.Scan(scanInfo)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package scan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/armosec/k8s-interface/k8sinterface"
|
||||
"github.com/armosec/kubescape/v2/core/cautils"
|
||||
"github.com/armosec/kubescape/v2/core/meta"
|
||||
@@ -73,6 +75,7 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
|
||||
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().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")
|
||||
@@ -88,7 +91,7 @@ func GetScanCommand(ks meta.IKubescape) *cobra.Command {
|
||||
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://raw.githubusercontent.com/armosec/kubescape/master/hostsensorutils/hostsensor.yaml")
|
||||
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/armosec/kubescape/blob/master/core/pkg/hostsensorutils/hostsensor.yaml")
|
||||
hostF.NoOptDefVal = "true"
|
||||
hostF.DefValue = "false, for no TTY in stdin"
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package cautils
|
||||
|
||||
import (
|
||||
pkgcautils "github.com/armosec/utils-go/utils"
|
||||
"golang.org/x/mod/semver"
|
||||
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"github.com/armosec/utils-go/boolutils"
|
||||
)
|
||||
|
||||
func NewPolicies() *Policies {
|
||||
@@ -40,7 +40,7 @@ func ruleWithArmoOpaDependency(attributes map[string]interface{}) bool {
|
||||
return false
|
||||
}
|
||||
if s, ok := attributes["armoOpa"]; ok { // TODO - make global
|
||||
return pkgcautils.StringToBool(s.(string))
|
||||
return boolutils.StringToBool(s.(string))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -9,12 +9,23 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func parseHost(urlObj *url.URL) {
|
||||
if strings.Contains(urlObj.Host, "http://") {
|
||||
urlObj.Scheme = "http"
|
||||
urlObj.Host = strings.Replace(urlObj.Host, "http://", "", 1)
|
||||
} else {
|
||||
urlObj.Scheme = "https"
|
||||
urlObj.Host = strings.Replace(urlObj.Host, "https://", "", 1)
|
||||
}
|
||||
}
|
||||
|
||||
var NativeFrameworks = []string{"nsa", "mitre", "armobest", "devopsbest"}
|
||||
|
||||
func (armoAPI *ArmoAPI) getFrameworkURL(frameworkName string) string {
|
||||
u := url.URL{}
|
||||
u.Scheme = "https"
|
||||
u.Host = armoAPI.apiURL
|
||||
u.Host = armoAPI.GetAPIURL()
|
||||
parseHost(&u)
|
||||
|
||||
u.Path = "api/v1/armoFrameworks"
|
||||
q := u.Query()
|
||||
q.Add("customerGUID", armoAPI.getCustomerGUIDFallBack())
|
||||
@@ -31,8 +42,9 @@ func (armoAPI *ArmoAPI) getFrameworkURL(frameworkName string) string {
|
||||
|
||||
func (armoAPI *ArmoAPI) getListFrameworkURL() string {
|
||||
u := url.URL{}
|
||||
u.Scheme = "https"
|
||||
u.Host = armoAPI.apiURL
|
||||
u.Host = armoAPI.GetAPIURL()
|
||||
parseHost(&u)
|
||||
|
||||
u.Path = "api/v1/armoFrameworks"
|
||||
q := u.Query()
|
||||
q.Add("customerGUID", armoAPI.getCustomerGUIDFallBack())
|
||||
@@ -42,8 +54,8 @@ func (armoAPI *ArmoAPI) getListFrameworkURL() string {
|
||||
}
|
||||
func (armoAPI *ArmoAPI) getExceptionsURL(clusterName string) string {
|
||||
u := url.URL{}
|
||||
u.Scheme = "https"
|
||||
u.Host = armoAPI.apiURL
|
||||
u.Host = armoAPI.GetAPIURL()
|
||||
parseHost(&u)
|
||||
u.Path = "api/v1/armoPostureExceptions"
|
||||
|
||||
q := u.Query()
|
||||
@@ -58,8 +70,8 @@ func (armoAPI *ArmoAPI) getExceptionsURL(clusterName string) string {
|
||||
|
||||
func (armoAPI *ArmoAPI) exceptionsURL(exceptionsPolicyName string) string {
|
||||
u := url.URL{}
|
||||
u.Scheme = "https"
|
||||
u.Host = armoAPI.apiURL
|
||||
u.Host = armoAPI.GetAPIURL()
|
||||
parseHost(&u)
|
||||
u.Path = "api/v1/postureExceptionPolicy"
|
||||
|
||||
q := u.Query()
|
||||
@@ -81,8 +93,8 @@ func (armoAPI *ArmoAPI) getAccountConfigDefault(clusterName string) string {
|
||||
|
||||
func (armoAPI *ArmoAPI) getAccountConfig(clusterName string) string {
|
||||
u := url.URL{}
|
||||
u.Scheme = "https"
|
||||
u.Host = armoAPI.apiURL
|
||||
u.Host = armoAPI.GetAPIURL()
|
||||
parseHost(&u)
|
||||
u.Path = "api/v1/armoCustomerConfiguration"
|
||||
|
||||
q := u.Query()
|
||||
@@ -97,8 +109,8 @@ func (armoAPI *ArmoAPI) getAccountConfig(clusterName string) string {
|
||||
|
||||
func (armoAPI *ArmoAPI) getAccountURL() string {
|
||||
u := url.URL{}
|
||||
u.Scheme = "https"
|
||||
u.Host = armoAPI.apiURL
|
||||
u.Host = armoAPI.GetAPIURL()
|
||||
parseHost(&u)
|
||||
u.Path = "api/v1/createTenant"
|
||||
return u.String()
|
||||
}
|
||||
@@ -106,7 +118,6 @@ func (armoAPI *ArmoAPI) getAccountURL() string {
|
||||
func (armoAPI *ArmoAPI) getApiToken() string {
|
||||
u := url.URL{}
|
||||
u.Scheme = "https"
|
||||
u.Host = armoAPI.authURL
|
||||
u.Path = "frontegg/identity/resources/auth/v1/api-token"
|
||||
return u.String()
|
||||
}
|
||||
@@ -114,7 +125,6 @@ func (armoAPI *ArmoAPI) getApiToken() string {
|
||||
func (armoAPI *ArmoAPI) getOpenidCustomers() string {
|
||||
u := url.URL{}
|
||||
u.Scheme = "https"
|
||||
u.Host = armoAPI.apiURL
|
||||
u.Path = "api/v1/openid_customers"
|
||||
return u.String()
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/armoapi-go/armotypes"
|
||||
apisv1 "github.com/armosec/opa-utils/httpserver/apis/v1"
|
||||
|
||||
giturl "github.com/armosec/go-git-url"
|
||||
"github.com/armosec/k8s-interface/k8sinterface"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/getter"
|
||||
@@ -29,6 +32,10 @@ type BoolPtrFlag struct {
|
||||
valPtr *bool
|
||||
}
|
||||
|
||||
func NewBoolPtr(b *bool) BoolPtrFlag {
|
||||
return BoolPtrFlag{valPtr: b}
|
||||
}
|
||||
|
||||
func (bpf *BoolPtrFlag) Type() string {
|
||||
return "bool"
|
||||
}
|
||||
@@ -64,32 +71,46 @@ func (bpf *BoolPtrFlag) Set(val string) error {
|
||||
}
|
||||
|
||||
// TODO - UPDATE
|
||||
type ViewTypes string
|
||||
|
||||
const (
|
||||
ResourceViewType ViewTypes = "resource"
|
||||
ControlViewType ViewTypes = "control"
|
||||
)
|
||||
|
||||
type PolicyIdentifier struct {
|
||||
Name string // policy name e.g. nsa,mitre,c-0012
|
||||
Kind apisv1.NotificationPolicyKind // policy kind e.g. Framework,Control,Rule
|
||||
Designators armotypes.PortalDesignator
|
||||
}
|
||||
|
||||
type ScanInfo struct {
|
||||
Getters // TODO - remove from object
|
||||
PolicyIdentifier []reporthandling.PolicyIdentifier // TODO - remove from object
|
||||
UseExceptions string // Load file with exceptions configuration
|
||||
ControlsInputs string // Load file with inputs for controls
|
||||
UseFrom []string // Load framework from local file (instead of download). Use when running offline
|
||||
UseDefault bool // Load framework from cached file (instead of download). Use when running offline
|
||||
UseArtifactsFrom string // Load artifacts from local path. Use when running offline
|
||||
VerboseMode bool // Display all of the input resources and not only failed resources
|
||||
Format string // Format results (table, json, junit ...)
|
||||
Output string // Store results in an output file, Output file name
|
||||
FormatVersion string // Output object can be differnet between versions, this is for testing and backward compatibility
|
||||
ExcludedNamespaces string // used for host scanner namespace
|
||||
IncludeNamespaces string // DEPRECATED?
|
||||
InputPatterns []string // Yaml files input patterns
|
||||
Silent bool // Silent mode - Do not print progress logs
|
||||
FailThreshold float32 // Failure score threshold
|
||||
Submit bool // Submit results to Armo BE
|
||||
ScanID string // Report id of the current scan
|
||||
HostSensorEnabled BoolPtrFlag // Deploy ARMO K8s host scanner to collect data from certain controls
|
||||
HostSensorYamlPath string // Path to hostsensor file
|
||||
Local bool // Do not submit results
|
||||
Account string // account ID
|
||||
KubeContext string // context name
|
||||
FrameworkScan bool // false if scanning control
|
||||
ScanAll bool // true if scan all frameworks
|
||||
Getters // TODO - remove from object
|
||||
PolicyIdentifier []PolicyIdentifier // TODO - remove from object
|
||||
UseExceptions string // Load file with exceptions configuration
|
||||
ControlsInputs string // Load file with inputs for controls
|
||||
UseFrom []string // Load framework from local file (instead of download). Use when running offline
|
||||
UseDefault bool // Load framework from cached file (instead of download). Use when running offline
|
||||
UseArtifactsFrom string // Load artifacts from local path. Use when running offline
|
||||
VerboseMode bool // Display all of the input resources and not only failed resources
|
||||
View string // Display all of the input resources and not only failed resources
|
||||
Format string // Format results (table, json, junit ...)
|
||||
Output string // Store results in an output file, Output file name
|
||||
FormatVersion string // Output object can be differnet between versions, this is for testing and backward compatibility
|
||||
ExcludedNamespaces string // used for host scanner namespace
|
||||
IncludeNamespaces string //
|
||||
InputPatterns []string // Yaml files input patterns
|
||||
Silent bool // Silent mode - Do not print progress logs
|
||||
FailThreshold float32 // Failure score threshold
|
||||
Submit bool // Submit results to Armo BE
|
||||
ScanID string // Report id of the current scan
|
||||
HostSensorEnabled BoolPtrFlag // Deploy ARMO K8s host scanner to collect data from certain controls
|
||||
HostSensorYamlPath string // Path to hostsensor file
|
||||
Local bool // Do not submit results
|
||||
Account string // account ID
|
||||
KubeContext string // context name
|
||||
FrameworkScan bool // false if scanning control
|
||||
ScanAll bool // true if scan all frameworks
|
||||
}
|
||||
|
||||
type Getters struct {
|
||||
@@ -176,11 +197,11 @@ func (scanInfo *ScanInfo) GetScanningEnvironment() string {
|
||||
return ScanCluster
|
||||
}
|
||||
|
||||
func (scanInfo *ScanInfo) SetPolicyIdentifiers(policies []string, kind reporthandling.NotificationPolicyKind) {
|
||||
func (scanInfo *ScanInfo) SetPolicyIdentifiers(policies []string, kind apisv1.NotificationPolicyKind) {
|
||||
for _, policy := range policies {
|
||||
if !scanInfo.contains(policy) {
|
||||
newPolicy := reporthandling.PolicyIdentifier{}
|
||||
newPolicy.Kind = kind // reporthandling.KindFramework
|
||||
newPolicy := PolicyIdentifier{}
|
||||
newPolicy.Kind = kind
|
||||
newPolicy.Name = policy
|
||||
scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy)
|
||||
}
|
||||
@@ -220,6 +241,7 @@ func scanInfoToScanMetadata(scanInfo *ScanInfo) *reporthandlingv2.Metadata {
|
||||
metadata.ScanMetadata.TargetNames = append(metadata.ScanMetadata.TargetNames, policy.Name)
|
||||
}
|
||||
|
||||
metadata.ScanMetadata.KubescapeVersion = BuildNumber
|
||||
metadata.ScanMetadata.VerboseMode = scanInfo.VerboseMode
|
||||
metadata.ScanMetadata.FailThreshold = scanInfo.FailThreshold
|
||||
metadata.ScanMetadata.HostScanner = scanInfo.HostSensorEnabled.GetBool()
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/armosec/kubescape/v2/core/cautils/getter"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/logger"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
|
||||
pkgutils "github.com/armosec/utils-go/utils"
|
||||
"github.com/armosec/utils-go/boolutils"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
@@ -28,9 +28,9 @@ func NewIVersionCheckHandler() IVersionCheckHandler {
|
||||
if BuildNumber == "" {
|
||||
logger.L().Warning("unknown build number, this might affect your scan results. Please make sure you are updated to latest version")
|
||||
}
|
||||
if v, ok := os.LookupEnv(SKIP_VERSION_CHECK); ok && pkgutils.StringToBool(v) {
|
||||
if v, ok := os.LookupEnv(SKIP_VERSION_CHECK); ok && boolutils.StringToBool(v) {
|
||||
return NewVersionCheckHandlerMock()
|
||||
} else if v, ok := os.LookupEnv(SKIP_VERSION_CHECK_DEPRECATED); ok && pkgutils.StringToBool(v) {
|
||||
} else if v, ok := os.LookupEnv(SKIP_VERSION_CHECK_DEPRECATED); ok && boolutils.StringToBool(v) {
|
||||
return NewVersionCheckHandlerMock()
|
||||
}
|
||||
return NewVersionCheckHandler()
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter"
|
||||
reporterv2 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v2"
|
||||
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"github.com/armosec/rbac-utils/rbacscanner"
|
||||
)
|
||||
|
||||
@@ -105,7 +104,7 @@ func getFieldSelector(scanInfo *cautils.ScanInfo) resourcehandler.IFieldSelector
|
||||
return &resourcehandler.EmptySelector{}
|
||||
}
|
||||
|
||||
func policyIdentifierNames(pi []reporthandling.PolicyIdentifier) string {
|
||||
func policyIdentifierNames(pi []cautils.PolicyIdentifier) string {
|
||||
policiesNames := ""
|
||||
for i := range pi {
|
||||
policiesNames += pi[i].Name
|
||||
|
||||
@@ -3,7 +3,8 @@ package core
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/armosec/armoapi-go/armotypes"
|
||||
apisv1 "github.com/armosec/opa-utils/httpserver/apis/v1"
|
||||
|
||||
"github.com/armosec/k8s-interface/k8sinterface"
|
||||
|
||||
"github.com/armosec/kubescape/v2/core/cautils"
|
||||
@@ -18,7 +19,6 @@ import (
|
||||
"github.com/armosec/kubescape/v2/core/pkg/resultshandling/printer"
|
||||
"github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter"
|
||||
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"github.com/armosec/opa-utils/resources"
|
||||
)
|
||||
|
||||
@@ -94,7 +94,7 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
|
||||
reportHandler := getReporter(tenantConfig, scanInfo.ScanID, scanInfo.Submit, scanInfo.FrameworkScan)
|
||||
|
||||
// setup printer
|
||||
printerHandler := resultshandling.NewPrinter(scanInfo.Format, scanInfo.FormatVersion, scanInfo.VerboseMode)
|
||||
printerHandler := resultshandling.NewPrinter(scanInfo.Format, scanInfo.FormatVersion, scanInfo.VerboseMode, cautils.ViewTypes(scanInfo.View))
|
||||
printerHandler.SetWriter(scanInfo.Output)
|
||||
|
||||
// ================== return interface ======================================
|
||||
@@ -130,7 +130,7 @@ func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsH
|
||||
|
||||
// TODO - list supported frameworks/controls
|
||||
if scanInfo.ScanAll {
|
||||
scanInfo.SetPolicyIdentifiers(listFrameworksNames(scanInfo.Getters.PolicyGetter), reporthandling.KindFramework)
|
||||
scanInfo.SetPolicyIdentifiers(listFrameworksNames(scanInfo.Getters.PolicyGetter), apisv1.KindFramework)
|
||||
}
|
||||
|
||||
// remove host scanner components
|
||||
@@ -144,7 +144,7 @@ func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsH
|
||||
|
||||
// ===================== policies & resources =====================
|
||||
policyHandler := policyhandler.NewPolicyHandler(interfaces.resourceHandler)
|
||||
scanData, err := collectResources(policyHandler, scanInfo)
|
||||
scanData, err := policyHandler.CollectResources(scanInfo.PolicyIdentifier, scanInfo)
|
||||
if err != nil {
|
||||
return resultsHandling, err
|
||||
}
|
||||
@@ -167,28 +167,6 @@ func (ks *Kubescape) Scan(scanInfo *cautils.ScanInfo) (*resultshandling.ResultsH
|
||||
return resultsHandling, nil
|
||||
}
|
||||
|
||||
// TODO - remove function
|
||||
func collectResources(policyHandler *policyhandler.PolicyHandler, scanInfo *cautils.ScanInfo) (*cautils.OPASessionObj, error) {
|
||||
policyNotification := &reporthandling.PolicyNotification{
|
||||
Rules: scanInfo.PolicyIdentifier,
|
||||
KubescapeNotification: reporthandling.KubescapeNotification{
|
||||
Designators: armotypes.PortalDesignator{},
|
||||
NotificationType: reporthandling.TypeExecPostureScan,
|
||||
},
|
||||
}
|
||||
switch policyNotification.KubescapeNotification.NotificationType {
|
||||
case reporthandling.TypeExecPostureScan:
|
||||
collectedResources, err := policyHandler.CollectResources(policyNotification, scanInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return collectedResources, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("notification type '%s' Unknown", policyNotification.KubescapeNotification.NotificationType)
|
||||
}
|
||||
}
|
||||
|
||||
// func askUserForHostSensor() bool {
|
||||
// return false
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ var (
|
||||
LinuxKernelVariables = "LinuxKernelVariables"
|
||||
KubeletCommandLine = "KubeletCommandLine"
|
||||
|
||||
MapResourceToApiGroup = map[string]string{
|
||||
MapHostSensorResourceToApiGroup = map[string]string{
|
||||
KubeletConfiguration: "hostdata.kubescape.cloud/v1beta0",
|
||||
OsReleaseFile: "hostdata.kubescape.cloud/v1beta0",
|
||||
KubeletCommandLine: "hostdata.kubescape.cloud/v1beta0",
|
||||
@@ -26,7 +26,7 @@ var (
|
||||
)
|
||||
|
||||
func addInfoToMap(resource string, infoMap map[string]apis.StatusInfo, err error) {
|
||||
group, version := k8sinterface.SplitApiVersion(MapResourceToApiGroup[resource])
|
||||
group, version := k8sinterface.SplitApiVersion(MapHostSensorResourceToApiGroup[resource])
|
||||
r := k8sinterface.JoinResourceTriplets(group, version, resource)
|
||||
infoMap[r] = apis.StatusInfo{
|
||||
InnerStatus: apis.StatusSkipped,
|
||||
|
||||
@@ -180,6 +180,9 @@ func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule) (map[stri
|
||||
for j := range ruleResponses[i].FixPaths {
|
||||
ruleResult.Paths = append(ruleResult.Paths, armotypes.PosturePaths{FixPath: ruleResponses[i].FixPaths[j]})
|
||||
}
|
||||
if ruleResponses[i].FixCommand != "" {
|
||||
ruleResult.Paths = append(ruleResult.Paths, armotypes.PosturePaths{FixCommand: ruleResponses[i].FixCommand})
|
||||
}
|
||||
resources[failedResources[j].GetID()] = ruleResult
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/armosec/k8s-interface/workloadinterface"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"github.com/armosec/opa-utils/reporthandling/apis"
|
||||
"github.com/armosec/opa-utils/reporthandling/results/v1/reportsummary"
|
||||
resources "github.com/armosec/opa-utils/resources"
|
||||
)
|
||||
|
||||
@@ -46,7 +47,7 @@ func (opap *OPAProcessor) updateResults() {
|
||||
|
||||
// set result summary
|
||||
// map control to error
|
||||
controlToInfoMap := mapControlToInfo(opap.ResourceToControlsMap, opap.InfoMap)
|
||||
controlToInfoMap := mapControlToInfo(opap.ResourceToControlsMap, opap.InfoMap, opap.Report.SummaryDetails.Controls)
|
||||
opap.Report.SummaryDetails.InitResourcesSummary(controlToInfoMap)
|
||||
// for f := range opap.PostureReport.FrameworkReports {
|
||||
// // set exceptions
|
||||
@@ -60,17 +61,29 @@ func (opap *OPAProcessor) updateResults() {
|
||||
// }
|
||||
}
|
||||
|
||||
func mapControlToInfo(mapResourceToControls map[string][]string, infoMap map[string]apis.StatusInfo) map[string]apis.StatusInfo {
|
||||
func mapControlToInfo(mapResourceToControls map[string][]string, infoMap map[string]apis.StatusInfo, controlSummary reportsummary.ControlSummaries) map[string]apis.StatusInfo {
|
||||
controlToInfoMap := make(map[string]apis.StatusInfo)
|
||||
for resource, statusInfo := range infoMap {
|
||||
controls := mapResourceToControls[resource]
|
||||
for _, control := range controls {
|
||||
controlToInfoMap[control] = statusInfo
|
||||
controlIDs := mapResourceToControls[resource]
|
||||
for _, controlID := range controlIDs {
|
||||
ctrl := controlSummary.GetControl(reportsummary.EControlCriteriaID, controlID)
|
||||
if ctrl != nil {
|
||||
resources := ctrl.NumberOfResources()
|
||||
// Check that there are no K8s resources too
|
||||
if isEmptyResources(resources) {
|
||||
controlToInfoMap[controlID] = statusInfo
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return controlToInfoMap
|
||||
}
|
||||
|
||||
func isEmptyResources(counters reportsummary.ICounters) bool {
|
||||
return counters.Failed() == 0 && counters.Excluded() == 0 && counters.Passed() == 0
|
||||
}
|
||||
|
||||
func getAllSupportedObjects(k8sResources *cautils.K8SResources, armoResources *cautils.ArmoResources, allResources map[string]workloadinterface.IMetadata, rule *reporthandling.PolicyRule) []workloadinterface.IMetadata {
|
||||
k8sObjects := []workloadinterface.IMetadata{}
|
||||
k8sObjects = append(k8sObjects, getKubernetesObjects(k8sResources, allResources, rule.Match)...)
|
||||
|
||||
@@ -3,9 +3,9 @@ package policyhandler
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/armosec/armoapi-go/armotypes"
|
||||
"github.com/armosec/kubescape/v2/core/cautils"
|
||||
"github.com/armosec/kubescape/v2/core/pkg/resourcehandler"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
)
|
||||
|
||||
// PolicyHandler -
|
||||
@@ -22,7 +22,7 @@ func NewPolicyHandler(resourceHandler resourcehandler.IResourceHandler) *PolicyH
|
||||
}
|
||||
}
|
||||
|
||||
func (policyHandler *PolicyHandler) CollectResources(notification *reporthandling.PolicyNotification, scanInfo *cautils.ScanInfo) (*cautils.OPASessionObj, error) {
|
||||
func (policyHandler *PolicyHandler) CollectResources(policyIdentifier []cautils.PolicyIdentifier, scanInfo *cautils.ScanInfo) (*cautils.OPASessionObj, error) {
|
||||
opaSessionObj := cautils.NewOPASessionObj(nil, nil, scanInfo)
|
||||
|
||||
// validate notification
|
||||
@@ -30,11 +30,11 @@ func (policyHandler *PolicyHandler) CollectResources(notification *reporthandlin
|
||||
policyHandler.getters = &scanInfo.Getters
|
||||
|
||||
// get policies
|
||||
if err := policyHandler.getPolicies(notification, opaSessionObj); err != nil {
|
||||
if err := policyHandler.getPolicies(policyIdentifier, opaSessionObj); err != nil {
|
||||
return opaSessionObj, err
|
||||
}
|
||||
|
||||
err := policyHandler.getResources(notification, opaSessionObj, scanInfo)
|
||||
err := policyHandler.getResources(policyIdentifier, opaSessionObj, scanInfo)
|
||||
if err != nil {
|
||||
return opaSessionObj, err
|
||||
}
|
||||
@@ -46,10 +46,10 @@ func (policyHandler *PolicyHandler) CollectResources(notification *reporthandlin
|
||||
return opaSessionObj, nil
|
||||
}
|
||||
|
||||
func (policyHandler *PolicyHandler) getResources(notification *reporthandling.PolicyNotification, opaSessionObj *cautils.OPASessionObj, scanInfo *cautils.ScanInfo) error {
|
||||
func (policyHandler *PolicyHandler) getResources(policyIdentifier []cautils.PolicyIdentifier, opaSessionObj *cautils.OPASessionObj, scanInfo *cautils.ScanInfo) error {
|
||||
opaSessionObj.Report.ClusterAPIServerInfo = policyHandler.resourceHandler.GetClusterAPIServerInfo()
|
||||
|
||||
resourcesMap, allResources, armoResources, err := policyHandler.resourceHandler.GetResources(opaSessionObj, ¬ification.Designators)
|
||||
resourcesMap, allResources, armoResources, err := policyHandler.resourceHandler.GetResources(opaSessionObj, &policyIdentifier[0].Designators)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -60,3 +60,10 @@ func (policyHandler *PolicyHandler) getResources(notification *reporthandling.Po
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getDesignator(policyIdentifier []cautils.PolicyIdentifier) *armotypes.PortalDesignator {
|
||||
if len(policyIdentifier) > 0 {
|
||||
return &policyIdentifier[0].Designators
|
||||
}
|
||||
return &armotypes.PortalDesignator{}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
apisv1 "github.com/armosec/opa-utils/httpserver/apis/v1"
|
||||
|
||||
"github.com/armosec/kubescape/v2/core/cautils"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/getter"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/logger"
|
||||
@@ -11,18 +13,18 @@ import (
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
)
|
||||
|
||||
func (policyHandler *PolicyHandler) getPolicies(notification *reporthandling.PolicyNotification, policiesAndResources *cautils.OPASessionObj) error {
|
||||
func (policyHandler *PolicyHandler) getPolicies(policyIdentifier []cautils.PolicyIdentifier, policiesAndResources *cautils.OPASessionObj) error {
|
||||
logger.L().Info("Downloading/Loading policy definitions")
|
||||
|
||||
cautils.StartSpinner()
|
||||
defer cautils.StopSpinner()
|
||||
|
||||
policies, err := policyHandler.getScanPolicies(notification)
|
||||
policies, err := policyHandler.getScanPolicies(policyIdentifier)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(policies) == 0 {
|
||||
return fmt.Errorf("failed to download policies: '%s'. Make sure the policy exist and you spelled it correctly. For more information, please feel free to contact ARMO team", strings.Join(policyIdentifierToSlice(notification.Rules), ", "))
|
||||
return fmt.Errorf("failed to download policies: '%s'. Make sure the policy exist and you spelled it correctly. For more information, please feel free to contact ARMO team", strings.Join(policyIdentifierToSlice(policyIdentifier), ", "))
|
||||
}
|
||||
|
||||
policiesAndResources.Policies = policies
|
||||
@@ -44,12 +46,12 @@ func (policyHandler *PolicyHandler) getPolicies(notification *reporthandling.Pol
|
||||
return nil
|
||||
}
|
||||
|
||||
func (policyHandler *PolicyHandler) getScanPolicies(notification *reporthandling.PolicyNotification) ([]reporthandling.Framework, error) {
|
||||
func (policyHandler *PolicyHandler) getScanPolicies(policyIdentifier []cautils.PolicyIdentifier) ([]reporthandling.Framework, error) {
|
||||
frameworks := []reporthandling.Framework{}
|
||||
|
||||
switch getScanKind(notification) {
|
||||
case reporthandling.KindFramework: // Download frameworks
|
||||
for _, rule := range notification.Rules {
|
||||
switch getScanKind(policyIdentifier) {
|
||||
case apisv1.KindFramework: // Download frameworks
|
||||
for _, rule := range policyIdentifier {
|
||||
receivedFramework, err := policyHandler.getters.PolicyGetter.GetFramework(rule.Name)
|
||||
if err != nil {
|
||||
return frameworks, policyDownloadError(err)
|
||||
@@ -63,11 +65,11 @@ func (policyHandler *PolicyHandler) getScanPolicies(notification *reporthandling
|
||||
}
|
||||
}
|
||||
}
|
||||
case reporthandling.KindControl: // Download controls
|
||||
case apisv1.KindControl: // Download controls
|
||||
f := reporthandling.Framework{}
|
||||
var receivedControl *reporthandling.Control
|
||||
var err error
|
||||
for _, rule := range notification.Rules {
|
||||
for _, rule := range policyIdentifier {
|
||||
receivedControl, err = policyHandler.getters.PolicyGetter.GetControl(rule.Name)
|
||||
if err != nil {
|
||||
return frameworks, policyDownloadError(err)
|
||||
@@ -89,7 +91,7 @@ func (policyHandler *PolicyHandler) getScanPolicies(notification *reporthandling
|
||||
return frameworks, nil
|
||||
}
|
||||
|
||||
func policyIdentifierToSlice(rules []reporthandling.PolicyIdentifier) []string {
|
||||
func policyIdentifierToSlice(rules []cautils.PolicyIdentifier) []string {
|
||||
s := []string{}
|
||||
for i := range rules {
|
||||
s = append(s, fmt.Sprintf("%s: %s", rules[i].Kind, rules[i].Name))
|
||||
|
||||
@@ -4,12 +4,14 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
apisv1 "github.com/armosec/opa-utils/httpserver/apis/v1"
|
||||
|
||||
"github.com/armosec/kubescape/v2/core/cautils"
|
||||
)
|
||||
|
||||
func getScanKind(notification *reporthandling.PolicyNotification) reporthandling.NotificationPolicyKind {
|
||||
if len(notification.Rules) > 0 {
|
||||
return notification.Rules[0].Kind
|
||||
func getScanKind(policyIdentifier []cautils.PolicyIdentifier) apisv1.NotificationPolicyKind {
|
||||
if len(policyIdentifier) > 0 {
|
||||
return policyIdentifier[0].Kind
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
|
||||
"github.com/armosec/armoapi-go/armotypes"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
k8slabels "k8s.io/apimachinery/pkg/labels"
|
||||
@@ -86,6 +87,9 @@ func (k8sHandler *K8sResourceHandler) GetResources(sessionObj *cautils.OPASessio
|
||||
if err := k8sHandler.registryAdaptors.collectImagesVulnerabilities(k8sResourcesMap, allResources, armoResourceMap); err != nil {
|
||||
logger.L().Warning("failed to collect image vulnerabilities", helpers.Error(err))
|
||||
}
|
||||
if isEmptyImgVulns(*armoResourceMap) {
|
||||
cautils.SetInfoMapForResources("image scanning not configured. For more information: https://hub.armo.cloud/docs/cluster-vulnerability-scanning", imgVulnResources, sessionObj.InfoMap)
|
||||
}
|
||||
}
|
||||
|
||||
hostResources := cautils.MapHostResources(armoResourceMap)
|
||||
@@ -103,7 +107,7 @@ func (k8sHandler *K8sResourceHandler) GetResources(sessionObj *cautils.OPASessio
|
||||
sessionObj.InfoMap = infoMap
|
||||
}
|
||||
} else {
|
||||
cautils.SetInfoMapForResources("enable-host-scan flag not used", hostResources, sessionObj.InfoMap)
|
||||
cautils.SetInfoMapForResources("enable-host-scan flag not used. For more information: https://hub.armo.cloud/docs/host-sensor", hostResources, sessionObj.InfoMap)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,17 +291,31 @@ func getCloudProviderDescription(allResources map[string]workloadinterface.IMeta
|
||||
}
|
||||
|
||||
func (k8sHandler *K8sResourceHandler) pullWorkerNodesNumber() (int, error) {
|
||||
// labels used for control plane
|
||||
listOptions := metav1.ListOptions{
|
||||
LabelSelector: "!node-role.kubernetes.io/control-plane,!node-role.kubernetes.io/master",
|
||||
nodesList, err := k8sHandler.k8s.KubernetesClient.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
|
||||
scheduableNodes := v1.NodeList{}
|
||||
if nodesList != nil {
|
||||
for _, node := range nodesList.Items {
|
||||
if len(node.Spec.Taints) == 0 {
|
||||
scheduableNodes.Items = append(scheduableNodes.Items, node)
|
||||
} else {
|
||||
if !isMasterNodeTaints(node.Spec.Taints) {
|
||||
scheduableNodes.Items = append(scheduableNodes.Items, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
nodesList, err := k8sHandler.k8s.KubernetesClient.CoreV1().Nodes().List(context.TODO(), listOptions)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
nodesNumber := 0
|
||||
if nodesList != nil {
|
||||
nodesNumber = len(nodesList.Items)
|
||||
}
|
||||
return nodesNumber, nil
|
||||
return len(scheduableNodes.Items), nil
|
||||
}
|
||||
|
||||
// NoSchedule taint with empty value is usually applied to controlplane
|
||||
func isMasterNodeTaints(taints []v1.Taint) bool {
|
||||
for _, taint := range taints {
|
||||
if taint.Effect == v1.TaintEffectNoSchedule && taint.Value == "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
532
core/pkg/resourcehandler/k8sresources_test.go
Normal file
532
core/pkg/resourcehandler/k8sresources_test.go
Normal file
@@ -0,0 +1,532 @@
|
||||
package resourcehandler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestIsMasterNodeTaints(t *testing.T) {
|
||||
noTaintNode := `
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Node",
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"kubeadm.alpha.kubernetes.io/cri-socket": "/var/run/dockershim.sock",
|
||||
"node.alpha.kubernetes.io/ttl": "0",
|
||||
"volumes.kubernetes.io/controller-managed-attach-detach": "true"
|
||||
},
|
||||
"creationTimestamp": "2022-05-16T10:52:32Z",
|
||||
"labels": {
|
||||
"beta.kubernetes.io/arch": "amd64",
|
||||
"beta.kubernetes.io/os": "linux",
|
||||
"kubernetes.io/arch": "amd64",
|
||||
"kubernetes.io/hostname": "danielg-minikube",
|
||||
"kubernetes.io/os": "linux",
|
||||
"minikube.k8s.io/commit": "3e64b11ed75e56e4898ea85f96b2e4af0301f43d",
|
||||
"minikube.k8s.io/name": "danielg-minikube",
|
||||
"minikube.k8s.io/updated_at": "2022_05_16T13_52_35_0700",
|
||||
"minikube.k8s.io/version": "v1.25.1",
|
||||
"node-role.kubernetes.io/control-plane": "",
|
||||
"node-role.kubernetes.io/master": "",
|
||||
"node.kubernetes.io/exclude-from-external-load-balancers": ""
|
||||
},
|
||||
"name": "danielg-minikube",
|
||||
"resourceVersion": "9432",
|
||||
"uid": "fc4afcb6-4ca4-4038-ba54-5e16065a614a"
|
||||
},
|
||||
"spec": {
|
||||
"podCIDR": "10.244.0.0/24",
|
||||
"podCIDRs": [
|
||||
"10.244.0.0/24"
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"addresses": [
|
||||
{
|
||||
"address": "192.168.49.2",
|
||||
"type": "InternalIP"
|
||||
},
|
||||
{
|
||||
"address": "danielg-minikube",
|
||||
"type": "Hostname"
|
||||
}
|
||||
],
|
||||
"allocatable": {
|
||||
"cpu": "4",
|
||||
"ephemeral-storage": "94850516Ki",
|
||||
"hugepages-2Mi": "0",
|
||||
"memory": "10432976Ki",
|
||||
"pods": "110"
|
||||
},
|
||||
"capacity": {
|
||||
"cpu": "4",
|
||||
"ephemeral-storage": "94850516Ki",
|
||||
"hugepages-2Mi": "0",
|
||||
"memory": "10432976Ki",
|
||||
"pods": "110"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
|
||||
"lastTransitionTime": "2022-05-16T10:52:29Z",
|
||||
"message": "kubelet has sufficient memory available",
|
||||
"reason": "KubeletHasSufficientMemory",
|
||||
"status": "False",
|
||||
"type": "MemoryPressure"
|
||||
},
|
||||
{
|
||||
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
|
||||
"lastTransitionTime": "2022-05-16T10:52:29Z",
|
||||
"message": "kubelet has no disk pressure",
|
||||
"reason": "KubeletHasNoDiskPressure",
|
||||
"status": "False",
|
||||
"type": "DiskPressure"
|
||||
},
|
||||
{
|
||||
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
|
||||
"lastTransitionTime": "2022-05-16T10:52:29Z",
|
||||
"message": "kubelet has sufficient PID available",
|
||||
"reason": "KubeletHasSufficientPID",
|
||||
"status": "False",
|
||||
"type": "PIDPressure"
|
||||
},
|
||||
{
|
||||
"lastHeartbeatTime": "2022-05-16T14:14:31Z",
|
||||
"lastTransitionTime": "2022-05-16T10:52:45Z",
|
||||
"message": "kubelet is posting ready status",
|
||||
"reason": "KubeletReady",
|
||||
"status": "True",
|
||||
"type": "Ready"
|
||||
}
|
||||
],
|
||||
"daemonEndpoints": {
|
||||
"kubeletEndpoint": {
|
||||
"Port": 10250
|
||||
}
|
||||
},
|
||||
"images": [
|
||||
{
|
||||
"names": [
|
||||
"requarks/wiki@sha256:dd83fff15e77843ff934b25c28c865ac000edf7653e5d11adad1dd51df87439d"
|
||||
],
|
||||
"sizeBytes": 441083858
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"mariadb@sha256:821d0411208eaa88f9e1f0daccd1d534f88d19baf724eb9a2777cbedb10b6c66"
|
||||
],
|
||||
"sizeBytes": 400782682
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"k8s.gcr.io/etcd@sha256:64b9ea357325d5db9f8a723dcf503b5a449177b17ac87d69481e126bb724c263",
|
||||
"k8s.gcr.io/etcd:3.5.1-0"
|
||||
],
|
||||
"sizeBytes": 292558922
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"kubernetesui/dashboard@sha256:ec27f462cf1946220f5a9ace416a84a57c18f98c777876a8054405d1428cc92e",
|
||||
"kubernetesui/dashboard:v2.3.1"
|
||||
],
|
||||
"sizeBytes": 220033604
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"k8s.gcr.io/kube-apiserver@sha256:f54681a71cce62cbc1b13ebb3dbf1d880f849112789811f98b6aebd2caa2f255",
|
||||
"k8s.gcr.io/kube-apiserver:v1.23.1"
|
||||
],
|
||||
"sizeBytes": 135162256
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"k8s.gcr.io/kube-controller-manager@sha256:a7ed87380108a2d811f0d392a3fe87546c85bc366e0d1e024dfa74eb14468604",
|
||||
"k8s.gcr.io/kube-controller-manager:v1.23.1"
|
||||
],
|
||||
"sizeBytes": 124971684
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"k8s.gcr.io/kube-proxy@sha256:e40f3a28721588affcf187f3f246d1e078157dabe274003eaa2957a83f7170c8",
|
||||
"k8s.gcr.io/kube-proxy:v1.23.1"
|
||||
],
|
||||
"sizeBytes": 112327826
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"quay.io/armosec/kubescape@sha256:6196f766be50d94b45d903a911f5ee95ac99bc392a1324c3e063bec41efd98ba",
|
||||
"quay.io/armosec/kubescape:v2.0.153"
|
||||
],
|
||||
"sizeBytes": 110345054
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d"
|
||||
],
|
||||
"sizeBytes": 109129446
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"quay.io/armosec/action-trigger@sha256:b93707d10ff86aac8dfa42ad37192d6bcf9aceeb4321b21756e438389c26e07c",
|
||||
"quay.io/armosec/action-trigger:v0.0.5"
|
||||
],
|
||||
"sizeBytes": 65127067
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"quay.io/armosec/images-vulnerabilities-scan@sha256:a5f9ddc04a7fdce6d52ef85a21f0de567d8e04d418c2bc5bf5d72b151c997625",
|
||||
"quay.io/armosec/images-vulnerabilities-scan:v0.0.7"
|
||||
],
|
||||
"sizeBytes": 61446712
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"quay.io/armosec/images-vulnerabilities-scan@sha256:2f879858da89f6542e3223fb18d6d793810cc2ad6e398b66776475e4218b6af5",
|
||||
"quay.io/armosec/images-vulnerabilities-scan:v0.0.8"
|
||||
],
|
||||
"sizeBytes": 61446528
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"quay.io/armosec/cluster-collector@sha256:2c4f733d09f7f4090ace04585230bdfacbbc29a3ade38a2e1233d2c0f730d9b6",
|
||||
"quay.io/armosec/cluster-collector:v0.0.9"
|
||||
],
|
||||
"sizeBytes": 53699576
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"k8s.gcr.io/kube-scheduler@sha256:8be4eb1593cf9ff2d91b44596633b7815a3753696031a1eb4273d1b39427fa8c",
|
||||
"k8s.gcr.io/kube-scheduler:v1.23.1"
|
||||
],
|
||||
"sizeBytes": 53488305
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"k8s.gcr.io/coredns/coredns@sha256:5b6ec0d6de9baaf3e92d0f66cd96a25b9edbce8716f5f15dcd1a616b3abd590e",
|
||||
"k8s.gcr.io/coredns/coredns:v1.8.6"
|
||||
],
|
||||
"sizeBytes": 46829283
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"kubernetesui/metrics-scraper@sha256:36d5b3f60e1a144cc5ada820910535074bdf5cf73fb70d1ff1681537eef4e172",
|
||||
"kubernetesui/metrics-scraper:v1.0.7"
|
||||
],
|
||||
"sizeBytes": 34446077
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"gcr.io/k8s-minikube/storage-provisioner@sha256:18eb69d1418e854ad5a19e399310e52808a8321e4c441c1dddad8977a0d7a944",
|
||||
"gcr.io/k8s-minikube/storage-provisioner:v5"
|
||||
],
|
||||
"sizeBytes": 31465472
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"quay.io/armosec/notification-server@sha256:b6e9b296cd53bd3b2b42c516d8ab43db998acff1124a57aff8d66b3dd7881979",
|
||||
"quay.io/armosec/notification-server:v0.0.3"
|
||||
],
|
||||
"sizeBytes": 20209940
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"quay.io/armosec/kube-host-sensor@sha256:82139d2561039726be060df2878ef023c59df7c536fbd7f6d766af5a99569fee",
|
||||
"quay.io/armosec/kube-host-sensor:latest"
|
||||
],
|
||||
"sizeBytes": 11796788
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"k8s.gcr.io/pause@sha256:3d380ca8864549e74af4b29c10f9cb0956236dfb01c40ca076fb6c37253234db",
|
||||
"k8s.gcr.io/pause:3.6"
|
||||
],
|
||||
"sizeBytes": 682696
|
||||
}
|
||||
],
|
||||
"nodeInfo": {
|
||||
"architecture": "amd64",
|
||||
"bootID": "828cbe73-120b-43cf-aae0-9e2d15b8c873",
|
||||
"containerRuntimeVersion": "docker://20.10.12",
|
||||
"kernelVersion": "5.13.0-40-generic",
|
||||
"kubeProxyVersion": "v1.23.1",
|
||||
"kubeletVersion": "v1.23.1",
|
||||
"machineID": "8de776e053e140d6a14c2d2def3d6bb8",
|
||||
"operatingSystem": "linux",
|
||||
"osImage": "Ubuntu 20.04.2 LTS",
|
||||
"systemUUID": "da12dc19-10bf-4033-a440-2d9aa33d6fe3"
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
var l v1.Node
|
||||
_ = json.Unmarshal([]byte(noTaintNode), &l)
|
||||
assert.False(t, isMasterNodeTaints(l.Spec.Taints))
|
||||
|
||||
taintNode :=
|
||||
`
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Node",
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"kubeadm.alpha.kubernetes.io/cri-socket": "/var/run/dockershim.sock",
|
||||
"node.alpha.kubernetes.io/ttl": "0",
|
||||
"volumes.kubernetes.io/controller-managed-attach-detach": "true"
|
||||
},
|
||||
"creationTimestamp": "2022-05-16T10:52:32Z",
|
||||
"labels": {
|
||||
"beta.kubernetes.io/arch": "amd64",
|
||||
"beta.kubernetes.io/os": "linux",
|
||||
"kubernetes.io/arch": "amd64",
|
||||
"kubernetes.io/hostname": "danielg-minikube",
|
||||
"kubernetes.io/os": "linux",
|
||||
"minikube.k8s.io/commit": "3e64b11ed75e56e4898ea85f96b2e4af0301f43d",
|
||||
"minikube.k8s.io/name": "danielg-minikube",
|
||||
"minikube.k8s.io/updated_at": "2022_05_16T13_52_35_0700",
|
||||
"minikube.k8s.io/version": "v1.25.1",
|
||||
"node-role.kubernetes.io/control-plane": "",
|
||||
"node-role.kubernetes.io/master": "",
|
||||
"node.kubernetes.io/exclude-from-external-load-balancers": ""
|
||||
},
|
||||
"name": "danielg-minikube",
|
||||
"resourceVersion": "9871",
|
||||
"uid": "fc4afcb6-4ca4-4038-ba54-5e16065a614a"
|
||||
},
|
||||
"spec": {
|
||||
"podCIDR": "10.244.0.0/24",
|
||||
"podCIDRs": [
|
||||
"10.244.0.0/24"
|
||||
],
|
||||
"taints": [
|
||||
{
|
||||
"effect": "NoSchedule",
|
||||
"key": "key1",
|
||||
"value": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
"addresses": [
|
||||
{
|
||||
"address": "192.168.49.2",
|
||||
"type": "InternalIP"
|
||||
},
|
||||
{
|
||||
"address": "danielg-minikube",
|
||||
"type": "Hostname"
|
||||
}
|
||||
],
|
||||
"allocatable": {
|
||||
"cpu": "4",
|
||||
"ephemeral-storage": "94850516Ki",
|
||||
"hugepages-2Mi": "0",
|
||||
"memory": "10432976Ki",
|
||||
"pods": "110"
|
||||
},
|
||||
"capacity": {
|
||||
"cpu": "4",
|
||||
"ephemeral-storage": "94850516Ki",
|
||||
"hugepages-2Mi": "0",
|
||||
"memory": "10432976Ki",
|
||||
"pods": "110"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"lastHeartbeatTime": "2022-05-16T14:24:45Z",
|
||||
"lastTransitionTime": "2022-05-16T10:52:29Z",
|
||||
"message": "kubelet has sufficient memory available",
|
||||
"reason": "KubeletHasSufficientMemory",
|
||||
"status": "False",
|
||||
"type": "MemoryPressure"
|
||||
},
|
||||
{
|
||||
"lastHeartbeatTime": "2022-05-16T14:24:45Z",
|
||||
"lastTransitionTime": "2022-05-16T10:52:29Z",
|
||||
"message": "kubelet has no disk pressure",
|
||||
"reason": "KubeletHasNoDiskPressure",
|
||||
"status": "False",
|
||||
"type": "DiskPressure"
|
||||
},
|
||||
{
|
||||
"lastHeartbeatTime": "2022-05-16T14:24:45Z",
|
||||
"lastTransitionTime": "2022-05-16T10:52:29Z",
|
||||
"message": "kubelet has sufficient PID available",
|
||||
"reason": "KubeletHasSufficientPID",
|
||||
"status": "False",
|
||||
"type": "PIDPressure"
|
||||
},
|
||||
{
|
||||
"lastHeartbeatTime": "2022-05-16T14:24:45Z",
|
||||
"lastTransitionTime": "2022-05-16T10:52:45Z",
|
||||
"message": "kubelet is posting ready status",
|
||||
"reason": "KubeletReady",
|
||||
"status": "True",
|
||||
"type": "Ready"
|
||||
}
|
||||
],
|
||||
"daemonEndpoints": {
|
||||
"kubeletEndpoint": {
|
||||
"Port": 10250
|
||||
}
|
||||
},
|
||||
"images": [
|
||||
{
|
||||
"names": [
|
||||
"requarks/wiki@sha256:dd83fff15e77843ff934b25c28c865ac000edf7653e5d11adad1dd51df87439d"
|
||||
],
|
||||
"sizeBytes": 441083858
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"mariadb@sha256:821d0411208eaa88f9e1f0daccd1d534f88d19baf724eb9a2777cbedb10b6c66"
|
||||
],
|
||||
"sizeBytes": 400782682
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"k8s.gcr.io/etcd@sha256:64b9ea357325d5db9f8a723dcf503b5a449177b17ac87d69481e126bb724c263",
|
||||
"k8s.gcr.io/etcd:3.5.1-0"
|
||||
],
|
||||
"sizeBytes": 292558922
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"kubernetesui/dashboard@sha256:ec27f462cf1946220f5a9ace416a84a57c18f98c777876a8054405d1428cc92e",
|
||||
"kubernetesui/dashboard:v2.3.1"
|
||||
],
|
||||
"sizeBytes": 220033604
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"k8s.gcr.io/kube-apiserver@sha256:f54681a71cce62cbc1b13ebb3dbf1d880f849112789811f98b6aebd2caa2f255",
|
||||
"k8s.gcr.io/kube-apiserver:v1.23.1"
|
||||
],
|
||||
"sizeBytes": 135162256
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"k8s.gcr.io/kube-controller-manager@sha256:a7ed87380108a2d811f0d392a3fe87546c85bc366e0d1e024dfa74eb14468604",
|
||||
"k8s.gcr.io/kube-controller-manager:v1.23.1"
|
||||
],
|
||||
"sizeBytes": 124971684
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"k8s.gcr.io/kube-proxy@sha256:e40f3a28721588affcf187f3f246d1e078157dabe274003eaa2957a83f7170c8",
|
||||
"k8s.gcr.io/kube-proxy:v1.23.1"
|
||||
],
|
||||
"sizeBytes": 112327826
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"quay.io/armosec/kubescape@sha256:6196f766be50d94b45d903a911f5ee95ac99bc392a1324c3e063bec41efd98ba",
|
||||
"quay.io/armosec/kubescape:v2.0.153"
|
||||
],
|
||||
"sizeBytes": 110345054
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"nginx@sha256:f7988fb6c02e0ce69257d9bd9cf37ae20a60f1df7563c3a2a6abe24160306b8d"
|
||||
],
|
||||
"sizeBytes": 109129446
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"quay.io/armosec/action-trigger@sha256:b93707d10ff86aac8dfa42ad37192d6bcf9aceeb4321b21756e438389c26e07c",
|
||||
"quay.io/armosec/action-trigger:v0.0.5"
|
||||
],
|
||||
"sizeBytes": 65127067
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"quay.io/armosec/images-vulnerabilities-scan@sha256:a5f9ddc04a7fdce6d52ef85a21f0de567d8e04d418c2bc5bf5d72b151c997625",
|
||||
"quay.io/armosec/images-vulnerabilities-scan:v0.0.7"
|
||||
],
|
||||
"sizeBytes": 61446712
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"quay.io/armosec/images-vulnerabilities-scan@sha256:2f879858da89f6542e3223fb18d6d793810cc2ad6e398b66776475e4218b6af5",
|
||||
"quay.io/armosec/images-vulnerabilities-scan:v0.0.8"
|
||||
],
|
||||
"sizeBytes": 61446528
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"quay.io/armosec/cluster-collector@sha256:2c4f733d09f7f4090ace04585230bdfacbbc29a3ade38a2e1233d2c0f730d9b6",
|
||||
"quay.io/armosec/cluster-collector:v0.0.9"
|
||||
],
|
||||
"sizeBytes": 53699576
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"k8s.gcr.io/kube-scheduler@sha256:8be4eb1593cf9ff2d91b44596633b7815a3753696031a1eb4273d1b39427fa8c",
|
||||
"k8s.gcr.io/kube-scheduler:v1.23.1"
|
||||
],
|
||||
"sizeBytes": 53488305
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"k8s.gcr.io/coredns/coredns@sha256:5b6ec0d6de9baaf3e92d0f66cd96a25b9edbce8716f5f15dcd1a616b3abd590e",
|
||||
"k8s.gcr.io/coredns/coredns:v1.8.6"
|
||||
],
|
||||
"sizeBytes": 46829283
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"kubernetesui/metrics-scraper@sha256:36d5b3f60e1a144cc5ada820910535074bdf5cf73fb70d1ff1681537eef4e172",
|
||||
"kubernetesui/metrics-scraper:v1.0.7"
|
||||
],
|
||||
"sizeBytes": 34446077
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"gcr.io/k8s-minikube/storage-provisioner@sha256:18eb69d1418e854ad5a19e399310e52808a8321e4c441c1dddad8977a0d7a944",
|
||||
"gcr.io/k8s-minikube/storage-provisioner:v5"
|
||||
],
|
||||
"sizeBytes": 31465472
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"quay.io/armosec/notification-server@sha256:b6e9b296cd53bd3b2b42c516d8ab43db998acff1124a57aff8d66b3dd7881979",
|
||||
"quay.io/armosec/notification-server:v0.0.3"
|
||||
],
|
||||
"sizeBytes": 20209940
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"quay.io/armosec/kube-host-sensor@sha256:82139d2561039726be060df2878ef023c59df7c536fbd7f6d766af5a99569fee",
|
||||
"quay.io/armosec/kube-host-sensor:latest"
|
||||
],
|
||||
"sizeBytes": 11796788
|
||||
},
|
||||
{
|
||||
"names": [
|
||||
"k8s.gcr.io/pause@sha256:3d380ca8864549e74af4b29c10f9cb0956236dfb01c40ca076fb6c37253234db",
|
||||
"k8s.gcr.io/pause:3.6"
|
||||
],
|
||||
"sizeBytes": 682696
|
||||
}
|
||||
],
|
||||
"nodeInfo": {
|
||||
"architecture": "amd64",
|
||||
"bootID": "828cbe73-120b-43cf-aae0-9e2d15b8c873",
|
||||
"containerRuntimeVersion": "docker://20.10.12",
|
||||
"kernelVersion": "5.13.0-40-generic",
|
||||
"kubeProxyVersion": "v1.23.1",
|
||||
"kubeletVersion": "v1.23.1",
|
||||
"machineID": "8de776e053e140d6a14c2d2def3d6bb8",
|
||||
"operatingSystem": "linux",
|
||||
"osImage": "Ubuntu 20.04.2 LTS",
|
||||
"systemUUID": "da12dc19-10bf-4033-a440-2d9aa33d6fe3"
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
_ = json.Unmarshal([]byte(taintNode), &l)
|
||||
assert.True(t, isMasterNodeTaints(l.Spec.Taints))
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/kubescape/v2/core/cautils"
|
||||
"github.com/armosec/kubescape/v2/core/pkg/hostsensorutils"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"k8s.io/utils/strings/slices"
|
||||
|
||||
@@ -12,12 +11,43 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ClusterDescribe = "ClusterDescribe"
|
||||
ClusterDescribe = "ClusterDescribe"
|
||||
KubeletConfiguration = "KubeletConfiguration"
|
||||
OsReleaseFile = "OsReleaseFile"
|
||||
KernelVersion = "KernelVersion"
|
||||
LinuxSecurityHardeningStatus = "LinuxSecurityHardeningStatus"
|
||||
OpenPortsList = "OpenPortsList"
|
||||
LinuxKernelVariables = "LinuxKernelVariables"
|
||||
KubeletCommandLine = "KubeletCommandLine"
|
||||
ImageVulnerabilities = "ImageVulnerabilities"
|
||||
|
||||
MapResourceToApiGroup = map[string]string{
|
||||
KubeletConfiguration: "hostdata.kubescape.cloud/v1beta0",
|
||||
OsReleaseFile: "hostdata.kubescape.cloud/v1beta0",
|
||||
KubeletCommandLine: "hostdata.kubescape.cloud/v1beta0",
|
||||
KernelVersion: "hostdata.kubescape.cloud/v1beta0",
|
||||
LinuxSecurityHardeningStatus: "hostdata.kubescape.cloud/v1beta0",
|
||||
OpenPortsList: "hostdata.kubescape.cloud/v1beta0",
|
||||
LinuxKernelVariables: "hostdata.kubescape.cloud/v1beta0",
|
||||
}
|
||||
MapResourceToApiGroupVuln = map[string][]string{
|
||||
ImageVulnerabilities: {"armo.vuln.images/v1", "image.vulnscan.com/v1"}}
|
||||
MapResourceToApiGroupCloud = map[string][]string{
|
||||
ClusterDescribe: {"container.googleapis.com/v1", "eks.amazonaws.com/v1", "management.azure.com/v1"}}
|
||||
)
|
||||
|
||||
func isEmptyImgVulns(armoResourcesMap cautils.ArmoResources) bool {
|
||||
imgVulnResources := cautils.MapImageVulnResources(&armoResourcesMap)
|
||||
for _, resource := range imgVulnResources {
|
||||
if val, ok := armoResourcesMap[resource]; ok {
|
||||
if len(val) > 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func setK8sResourceMap(frameworks []reporthandling.Framework) *cautils.K8SResources {
|
||||
k8sResources := make(cautils.K8SResources)
|
||||
complexMap := setComplexK8sResourceMap(frameworks)
|
||||
@@ -80,10 +110,16 @@ func setComplexArmoResourceMap(frameworks []reporthandling.Framework, resourceTo
|
||||
}
|
||||
|
||||
func mapArmoResourceToApiGroup(resource string) []string {
|
||||
if val, ok := hostsensorutils.MapResourceToApiGroup[resource]; ok {
|
||||
if val, ok := MapResourceToApiGroup[resource]; ok {
|
||||
return []string{val}
|
||||
}
|
||||
return MapResourceToApiGroupCloud[resource]
|
||||
if val, ok := MapResourceToApiGroupCloud[resource]; ok {
|
||||
return val
|
||||
}
|
||||
if val, ok := MapResourceToApiGroupVuln[resource]; ok {
|
||||
return val
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func insertControls(resource string, resourceToControl map[string][]string, control reporthandling.Control) {
|
||||
|
||||
@@ -2,7 +2,9 @@ package resourcehandler
|
||||
|
||||
import (
|
||||
"github.com/armosec/k8s-interface/k8sinterface"
|
||||
"github.com/armosec/kubescape/v2/core/cautils"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"testing"
|
||||
)
|
||||
@@ -24,6 +26,18 @@ func TestSetResourceMap(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
func TestSsEmptyImgVulns(t *testing.T) {
|
||||
armoResourcesMap := make(cautils.ArmoResources, 0)
|
||||
armoResourcesMap["container.googleapis.com/v1"] = []string{"fsdfds"}
|
||||
assert.Equal(t, true, isEmptyImgVulns(armoResourcesMap))
|
||||
|
||||
armoResourcesMap["armo.vuln.images/v1/ImageVulnerabilities"] = []string{"dada"}
|
||||
assert.Equal(t, false, isEmptyImgVulns(armoResourcesMap))
|
||||
|
||||
armoResourcesMap["armo.vuln.images/v1/ImageVulnerabilities"] = []string{}
|
||||
armoResourcesMap["bla"] = []string{"blu"}
|
||||
assert.Equal(t, true, isEmptyImgVulns(armoResourcesMap))
|
||||
}
|
||||
|
||||
func TestInsertK8sResources(t *testing.T) {
|
||||
// insertK8sResources
|
||||
|
||||
@@ -28,7 +28,7 @@ func (jsonPrinter *JsonPrinter) Score(score float32) {
|
||||
}
|
||||
|
||||
func (jsonPrinter *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
|
||||
r, err := json.Marshal(DataToJson(opaSessionObj))
|
||||
r, err := json.Marshal(FinalizeResults(opaSessionObj))
|
||||
if err != nil {
|
||||
logger.L().Fatal("failed to Marshal posture report object")
|
||||
}
|
||||
|
||||
@@ -19,14 +19,16 @@ import (
|
||||
|
||||
type PrettyPrinter struct {
|
||||
formatVersion string
|
||||
viewType cautils.ViewTypes
|
||||
writer *os.File
|
||||
verboseMode bool
|
||||
}
|
||||
|
||||
func NewPrettyPrinter(verboseMode bool, formatVersion string) *PrettyPrinter {
|
||||
func NewPrettyPrinter(verboseMode bool, formatVersion string, viewType cautils.ViewTypes) *PrettyPrinter {
|
||||
return &PrettyPrinter{
|
||||
verboseMode: verboseMode,
|
||||
formatVersion: formatVersion,
|
||||
viewType: viewType,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,9 +37,15 @@ func (prettyPrinter *PrettyPrinter) ActionPrint(opaSessionObj *cautils.OPASessio
|
||||
|
||||
sortedControlNames := getSortedControlsNames(opaSessionObj.Report.SummaryDetails.Controls) // ListControls().All())
|
||||
|
||||
if prettyPrinter.verboseMode {
|
||||
prettyPrinter.resourceTable(opaSessionObj)
|
||||
switch prettyPrinter.viewType {
|
||||
case cautils.ControlViewType:
|
||||
prettyPrinter.printResults(&opaSessionObj.Report.SummaryDetails.Controls, opaSessionObj.AllResources, sortedControlNames)
|
||||
case cautils.ResourceViewType:
|
||||
if prettyPrinter.verboseMode {
|
||||
prettyPrinter.resourceTable(opaSessionObj)
|
||||
}
|
||||
}
|
||||
|
||||
prettyPrinter.printSummaryTable(&opaSessionObj.Report.SummaryDetails, sortedControlNames)
|
||||
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
// finalizeV2Report finalize the results objects by copying data from map to lists
|
||||
func DataToJson(data *cautils.OPASessionObj) *reporthandlingv2.PostureReport {
|
||||
func FinalizeResults(data *cautils.OPASessionObj) *reporthandlingv2.PostureReport {
|
||||
report := reporthandlingv2.PostureReport{
|
||||
SummaryDetails: data.Report.SummaryDetails,
|
||||
ClusterAPIServerInfo: data.Report.ClusterAPIServerInfo,
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/armosec/kubescape/v2/core/cautils/getter"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/logger"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
|
||||
v2 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v2"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
@@ -140,8 +141,8 @@ func (report *ReportEventReceiver) generateMessage() {
|
||||
message := "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 registering here:"
|
||||
|
||||
u := url.URL{}
|
||||
u.Scheme = "https"
|
||||
u.Host = getter.GetArmoAPIConnector().GetFrontendURL()
|
||||
v2.ParseHost(&u)
|
||||
|
||||
if report.customerAdminEMail != "" {
|
||||
logger.L().Debug("", helpers.String("account ID", report.customerGUID))
|
||||
|
||||
@@ -5,15 +5,16 @@ import (
|
||||
|
||||
"github.com/armosec/k8s-interface/workloadinterface"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/getter"
|
||||
v2 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v2"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func (report *ReportEventReceiver) initEventReceiverURL() {
|
||||
urlObj := url.URL{}
|
||||
|
||||
urlObj.Scheme = "https"
|
||||
urlObj.Host = getter.GetArmoAPIConnector().GetReportReceiverURL()
|
||||
v2.ParseHost(&urlObj)
|
||||
|
||||
urlObj.Path = "/k8s/postureReport"
|
||||
q := urlObj.Query()
|
||||
q.Add("customerGUID", uuid.MustParse(report.customerGUID).String())
|
||||
|
||||
@@ -89,9 +89,8 @@ func (report *ReportEventReceiver) prepareReport(opaSessionObj *cautils.OPASessi
|
||||
|
||||
func (report *ReportEventReceiver) GetURL() string {
|
||||
u := url.URL{}
|
||||
u.Scheme = "https"
|
||||
u.Host = getter.GetArmoAPIConnector().GetFrontendURL()
|
||||
|
||||
ParseHost(&u)
|
||||
q := u.Query()
|
||||
|
||||
if report.customerAdminEMail != "" || report.token == "" { // data has been submitted
|
||||
@@ -113,6 +112,7 @@ func (report *ReportEventReceiver) GetURL() string {
|
||||
}
|
||||
func (report *ReportEventReceiver) sendResources(host string, opaSessionObj *cautils.OPASessionObj) error {
|
||||
splittedPostureReport := report.setSubReport(opaSessionObj)
|
||||
|
||||
counter := 0
|
||||
reportCounter := 0
|
||||
if err := report.setResources(splittedPostureReport, opaSessionObj.AllResources, opaSessionObj.ResourceSource, &counter, &reportCounter, host); err != nil {
|
||||
@@ -121,7 +121,6 @@ func (report *ReportEventReceiver) sendResources(host string, opaSessionObj *cau
|
||||
if err := report.setResults(splittedPostureReport, opaSessionObj.ResourcesResult, &counter, &reportCounter, host); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return report.sendReport(host, splittedPostureReport, reportCounter, true)
|
||||
}
|
||||
func (report *ReportEventReceiver) setResults(reportObj *reporthandlingv2.PostureReport, results map[string]resourcesresults.Result, counter, reportCounter *int, host string) error {
|
||||
|
||||
@@ -11,11 +11,9 @@ import (
|
||||
|
||||
func (report *ReportEventReceiver) initEventReceiverURL() {
|
||||
urlObj := url.URL{}
|
||||
|
||||
urlObj.Scheme = "https"
|
||||
urlObj.Host = getter.GetArmoAPIConnector().GetReportReceiverURL()
|
||||
ParseHost(&urlObj)
|
||||
urlObj.Path = "/k8s/v2/postureReport"
|
||||
|
||||
q := urlObj.Query()
|
||||
q.Add("customerGUID", uuid.MustParse(report.customerGUID).String())
|
||||
q.Add("clusterName", report.clusterName)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -21,3 +22,13 @@ func maskID(id string) string {
|
||||
|
||||
return strings.TrimSuffix(str, sep)
|
||||
}
|
||||
|
||||
func ParseHost(urlObj *url.URL) {
|
||||
if strings.Contains(urlObj.Host, "http://") {
|
||||
urlObj.Scheme = "http"
|
||||
urlObj.Host = strings.Replace(urlObj.Host, "http://", "", 1)
|
||||
} else {
|
||||
urlObj.Scheme = "https"
|
||||
urlObj.Host = strings.Replace(urlObj.Host, "https://", "", 1)
|
||||
}
|
||||
}
|
||||
|
||||
38
core/pkg/resultshandling/reporter/v2/utils_test.go
Normal file
38
core/pkg/resultshandling/reporter/v2/utils_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseHost(t *testing.T) {
|
||||
urlObj := url.URL{}
|
||||
|
||||
urlObj.Host = "http://localhost:7555"
|
||||
ParseHost(&urlObj)
|
||||
assert.Equal(t, "http", urlObj.Scheme)
|
||||
assert.Equal(t, "localhost:7555", urlObj.Host)
|
||||
|
||||
urlObj.Host = "https://localhost:7555"
|
||||
ParseHost(&urlObj)
|
||||
assert.Equal(t, "https", urlObj.Scheme)
|
||||
assert.Equal(t, "localhost:7555", urlObj.Host)
|
||||
|
||||
urlObj.Host = "http://portal-dev.armo.cloud"
|
||||
ParseHost(&urlObj)
|
||||
assert.Equal(t, "http", urlObj.Scheme)
|
||||
assert.Equal(t, "portal-dev.armo.cloud", urlObj.Host)
|
||||
|
||||
urlObj.Host = "https://portal-dev.armo.cloud"
|
||||
ParseHost(&urlObj)
|
||||
assert.Equal(t, "https", urlObj.Scheme)
|
||||
assert.Equal(t, "portal-dev.armo.cloud", urlObj.Host)
|
||||
|
||||
urlObj.Host = "portal-dev.armo.cloud"
|
||||
ParseHost(&urlObj)
|
||||
assert.Equal(t, "https", urlObj.Scheme)
|
||||
assert.Equal(t, "portal-dev.armo.cloud", urlObj.Host)
|
||||
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
printerv1 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/printer/v1"
|
||||
printerv2 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/printer/v2"
|
||||
"github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter"
|
||||
reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2"
|
||||
)
|
||||
|
||||
type ResultsHandler struct {
|
||||
@@ -52,7 +53,12 @@ func (resultsHandler *ResultsHandler) GetReporter() reporter.IReport {
|
||||
|
||||
// ToJson return results in json format
|
||||
func (resultsHandler *ResultsHandler) ToJson() ([]byte, error) {
|
||||
return json.Marshal(printerv2.DataToJson(resultsHandler.scanData))
|
||||
return json.Marshal(printerv2.FinalizeResults(resultsHandler.scanData))
|
||||
}
|
||||
|
||||
// GetResults return results
|
||||
func (resultsHandler *ResultsHandler) GetResults() *reporthandlingv2.PostureReport {
|
||||
return printerv2.FinalizeResults(resultsHandler.scanData)
|
||||
}
|
||||
|
||||
// HandleResults handle the scan results according to the pre defind interfaces
|
||||
@@ -72,7 +78,7 @@ func (resultsHandler *ResultsHandler) HandleResults() error {
|
||||
}
|
||||
|
||||
// NewPrinter defind output format
|
||||
func NewPrinter(printFormat, formatVersion string, verboseMode bool) printer.IPrinter {
|
||||
func NewPrinter(printFormat, formatVersion string, verboseMode bool, viewType cautils.ViewTypes) printer.IPrinter {
|
||||
|
||||
switch printFormat {
|
||||
case printer.JsonFormat:
|
||||
@@ -90,6 +96,6 @@ func NewPrinter(printFormat, formatVersion string, verboseMode bool) printer.IPr
|
||||
case printer.PdfFormat:
|
||||
return printerv2.NewPdfPrinter()
|
||||
default:
|
||||
return printerv2.NewPrettyPrinter(verboseMode, formatVersion)
|
||||
return printerv2.NewPrettyPrinter(verboseMode, formatVersion, viewType)
|
||||
}
|
||||
}
|
||||
|
||||
10
go.mod
10
go.mod
@@ -3,13 +3,13 @@ module github.com/armosec/kubescape/v2
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/armosec/armoapi-go v0.0.66
|
||||
github.com/armosec/armoapi-go v0.0.73
|
||||
github.com/armosec/go-git-url v0.0.4
|
||||
github.com/armosec/k8s-interface v0.0.69
|
||||
github.com/armosec/opa-utils v0.0.130
|
||||
github.com/armosec/k8s-interface v0.0.76
|
||||
github.com/armosec/opa-utils v0.0.140
|
||||
github.com/armosec/rbac-utils v0.0.14
|
||||
github.com/armosec/utils-go v0.0.3
|
||||
github.com/armosec/utils-k8s-go v0.0.5
|
||||
github.com/armosec/utils-go v0.0.5
|
||||
github.com/armosec/utils-k8s-go v0.0.6
|
||||
github.com/briandowns/spinner v1.18.1
|
||||
github.com/enescakir/emoji v1.0.0
|
||||
github.com/fatih/color v1.13.0
|
||||
|
||||
23
go.sum
23
go.sum
@@ -113,28 +113,28 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armosec/armoapi-go v0.0.2/go.mod h1:vIK17yoKbJRQyZXWWLe3AqfqCRITxW8qmSkApyq5xFs=
|
||||
github.com/armosec/armoapi-go v0.0.23/go.mod h1:iaVVGyc23QGGzAdv4n+szGQg3Rbpixn9yQTU3qWRpaw=
|
||||
github.com/armosec/armoapi-go v0.0.58/go.mod h1:U/Axd+D5N00x9Ekr7t+5HXqLCMO+98NfJSVAggqJftI=
|
||||
github.com/armosec/armoapi-go v0.0.66 h1:SEm4nTwtexlMqYQr7sj3rWIrDYZj3BQ76FvKLA8hiLo=
|
||||
github.com/armosec/armoapi-go v0.0.66/go.mod h1:U/Axd+D5N00x9Ekr7t+5HXqLCMO+98NfJSVAggqJftI=
|
||||
github.com/armosec/armoapi-go v0.0.73 h1:LMf+eCkkf+W9NVvOzHKFgVUEpBMvh27M7//UQP3aiO8=
|
||||
github.com/armosec/armoapi-go v0.0.73/go.mod h1:/9SQAgtLbYkfFneRRm/zkIn3zz+4Y2xv6N3vtFcyF8s=
|
||||
github.com/armosec/go-git-url v0.0.4 h1:emG9Yfl53rHpuX41fXLD92ehzhRoNSSnGT6Pr7ogWMY=
|
||||
github.com/armosec/go-git-url v0.0.4/go.mod h1:PJqdEyJyFxTQvawBcyOM0Ies6+uezire5gpwfr1XX5M=
|
||||
github.com/armosec/k8s-interface v0.0.8/go.mod h1:xxS+V5QT3gVQTwZyAMMDrYLWGrfKOpiJ7Jfhfa0w9sM=
|
||||
github.com/armosec/k8s-interface v0.0.37/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU=
|
||||
github.com/armosec/k8s-interface v0.0.66/go.mod h1:vwprS8qn/iowd5yf0JHpqDsLA5I8W2muqX9AxKhkb0Q=
|
||||
github.com/armosec/k8s-interface v0.0.69 h1:rpZElGJjt9xlYrBc5IGKECybf7mZeu+rMEVEQyJOmbg=
|
||||
github.com/armosec/k8s-interface v0.0.69/go.mod h1:MmpOS7RselE+tZgojx5PcBXVbKjWBfHHd/hZ2tWXBdQ=
|
||||
github.com/armosec/k8s-interface v0.0.76 h1:pQaF+8BcNMm6GTYTjdG7vCM1l4BIk7oALXoT6v5gCAk=
|
||||
github.com/armosec/k8s-interface v0.0.76/go.mod h1:8NX4xWXh8mwW7QyZdZea1czNdM2azCK9BbUNmiZYXW0=
|
||||
github.com/armosec/opa-utils v0.0.64/go.mod h1:6tQP8UDq2EvEfSqh8vrUdr/9QVSCG4sJfju1SXQOn4c=
|
||||
github.com/armosec/opa-utils v0.0.130 h1:uP60M0PzmDtLqvsA/jX8BED9/Ava4n2QG7VCkuI+hwI=
|
||||
github.com/armosec/opa-utils v0.0.130/go.mod h1:gap+EaLG5rnyqvIRGxtdNDC9y7VvoGNm90zK8Ls7avQ=
|
||||
github.com/armosec/opa-utils v0.0.140 h1:iv6inb6+D0qgeVkv7f+ZIHpy239IUpAwg6Dau0JAWzg=
|
||||
github.com/armosec/opa-utils v0.0.140/go.mod h1:Hwm9ZkcW87mB2567WT6mBuSBEzaKowBNfrl3Q0IVsV8=
|
||||
github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40=
|
||||
github.com/armosec/rbac-utils v0.0.14 h1:CKYKcgqJEXWF2Hen/B1pVGtS3nDAG1wp9dDv6oNtq90=
|
||||
github.com/armosec/rbac-utils v0.0.14/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0=
|
||||
github.com/armosec/utils-go v0.0.2/go.mod h1:itWmRLzRdsnwjpEOomL0mBWGnVNNIxSjDAdyc+b0iUo=
|
||||
github.com/armosec/utils-go v0.0.3 h1:uyQI676yRciQM0sSN9uPoqHkbspTxHO0kmzXhBeE/xU=
|
||||
github.com/armosec/utils-go v0.0.3/go.mod h1:itWmRLzRdsnwjpEOomL0mBWGnVNNIxSjDAdyc+b0iUo=
|
||||
github.com/armosec/utils-go v0.0.4/go.mod h1:itWmRLzRdsnwjpEOomL0mBWGnVNNIxSjDAdyc+b0iUo=
|
||||
github.com/armosec/utils-go v0.0.5 h1:+pfZirWrOvfqvVYlL7OG1wMQD4T4YMwC78zzosB+mlQ=
|
||||
github.com/armosec/utils-go v0.0.5/go.mod h1:itWmRLzRdsnwjpEOomL0mBWGnVNNIxSjDAdyc+b0iUo=
|
||||
github.com/armosec/utils-k8s-go v0.0.1/go.mod h1:qrU4pmY2iZsOb39Eltpm0sTTNM3E4pmeyWx4dgDUC2U=
|
||||
github.com/armosec/utils-k8s-go v0.0.5 h1:zlw6lidAVbUs4cxlm30BOhxKWA/iuVUZTgZMhDC2wtQ=
|
||||
github.com/armosec/utils-k8s-go v0.0.5/go.mod h1:n6V42HYZZBDzMZMiAgUHROZcp4/Wz+wrBm+L6/m6Sdg=
|
||||
github.com/armosec/utils-k8s-go v0.0.6 h1:GriAQZeKsVdlM64lwRnh4EDKlb2R9tK7WXtRYQOrPwk=
|
||||
github.com/armosec/utils-k8s-go v0.0.6/go.mod h1:YFdWi3rEQQLbN6mZO21TSdoda8kGQYRV4rs5CRp8Kjs=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/aws/aws-sdk-go v1.41.1/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
|
||||
github.com/aws/aws-sdk-go v1.41.11 h1:QLouWsiYQ8i22kD8k58Dpdhio1A0MpT7bg9ZNXqEjuI=
|
||||
@@ -1149,7 +1149,6 @@ golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
||||
@@ -4,44 +4,106 @@
|
||||
|
||||
Running `kubescape` will start up a webserver on port `8080` which will serve the following paths:
|
||||
|
||||
### Trigger scan
|
||||
|
||||
* POST `/v1/scan` - Trigger a kubescape scan. The server will return an ID and will execute the scanning asynchronously
|
||||
* * `wait`: scan synchronously (return results and not ID). Use only in small clusters are with an increased timeout
|
||||
* * `keep`: Do not delete results from local storage after returning
|
||||
|
||||
### Get results
|
||||
* GET `/v1/results` - Request kubescape scan results
|
||||
* * query `id=<string>` -> ID returned when triggering the scan action. ~If empty will return latest results~ (not supported)
|
||||
* * query `remove` -> Remove results from storage after reading the results
|
||||
* DELETE `/v1/results` - Delete kubescape scan results from storage. ~If empty will delete latest results~ (not supported)
|
||||
* * query `id=<string>` -> ID returned when triggering the scan action. If empty will return latest results
|
||||
* * query `keep` -> Do not delete results from local storage after returning
|
||||
|
||||
### 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
|
||||
|
||||
* GET `/v1/status` - Request kubescape scan status
|
||||
* * query `id=<string>` -> Check status of a specific scan. If empty will check if any scan is in progress
|
||||
|
||||
### Delete cached results
|
||||
* DELETE `/v1/results` - Delete kubescape scan results from storage. If empty will delete latest results
|
||||
* * query `id=<string>`: Delete ID of specific results
|
||||
* * query `all`: Delete all cached results
|
||||
|
||||
### Prometheus support API
|
||||
|
||||
* GET/POST `/v1/metrics` - will trigger cluster scan. will respond with prometheus metrics once they have been scanned. This will respond 503 if the scan failed.
|
||||
* `/livez` - will respond 200 is server is alive
|
||||
* `/readyz` - will respond 200 if server can receive requests
|
||||
|
||||
## Trigger Kubescape scan
|
||||
|
||||
POST /v1/results
|
||||
POST /v1/scan
|
||||
body:
|
||||
```
|
||||
{
|
||||
"format": <str>, // results format [default: json] (same as 'kubescape scan --format')
|
||||
"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')
|
||||
"format": <str>, // results format [default: json] (same as 'kubescape scan --format')
|
||||
"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')
|
||||
"targetType": <str>, // framework/control
|
||||
"targetNames": [<str>] // names. e.g. when targetType==framework, targetNames=["nsa", "mitre"]
|
||||
}
|
||||
```
|
||||
|
||||
e.g.:
|
||||
Response body:
|
||||
```
|
||||
{
|
||||
"id": <str>, // scan ID
|
||||
"type": <responseType:str>, // response object type
|
||||
"response": <object:interface> // response payload as list of bytes
|
||||
}
|
||||
```
|
||||
|
||||
Response body types:
|
||||
* "v1results" - v1 results object
|
||||
* "id" - id string
|
||||
* "error" - error object
|
||||
|
||||
## API Examples
|
||||
#### Default scan
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
2. Get kubescape scan results
|
||||
```bash
|
||||
curl --request GET http://127.0.0.1:8080/v1/results -o response.json
|
||||
```
|
||||
|
||||
#### Trigger scan and wait for 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
|
||||
```
|
||||
#### Scan single namespace with a specific framework
|
||||
```bash
|
||||
curl --header "Content-Type: application/json" \
|
||||
--request POST \
|
||||
--data '{"hostScanner":true, "submit":true}' \
|
||||
--data '{"hostScanner":true, "submit":true, "includeNamespaces": ["ks-scanner"], "targetType": "framework", "targetNames": ["nsa"] }' \
|
||||
http://127.0.0.1:8080/v1/scan
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
* [Prometheus](examples/prometheus/README.md)
|
||||
* [Microservice](examples/microservice/README.md)
|
||||
|
||||
|
||||
## Supported environment variables
|
||||
|
||||
* `KS_ACCOUNT`: Account ID
|
||||
* `KS_SUBMIT`: Submit the results to 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
|
||||
* `KS_FORMAT`: Output file format. default is json
|
||||
* `KS_ENABLE_HOST_SCANNER`: Enable the host scanner feature
|
||||
* `KS_DOWNLOAD_ARTIFACTS`: Download the artifacts every scan
|
||||
|
||||
@@ -43,10 +43,10 @@ subjects:
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: kubescape-service
|
||||
name: kubescape
|
||||
namespace: ks-scanner
|
||||
labels:
|
||||
app: kubescape-service
|
||||
app: kubescape
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
@@ -89,13 +89,20 @@ spec:
|
||||
port: 8080
|
||||
initialDelaySeconds: 3
|
||||
periodSeconds: 3
|
||||
image: quay.io/armosec/kubescape:prometheus.v2
|
||||
image: quay.io/armosec/kubescape:latest
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: KS_DEFAULT_CONFIGMAP_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
apiVersion: v1
|
||||
fieldPath: metadata.namespace
|
||||
- name: "KS_SKIP_UPDATE_CHECK" # do not check latest version
|
||||
value: "true"
|
||||
- name: KS_ENABLE_HOST_SCANNER # enable host scanner -> https://hub.armo.cloud/docs/host-sensor
|
||||
value: "true"
|
||||
- name: KS_DOWNLOAD_ARTIFACTS # When set to true the artifacts will be downloaded every scan execution
|
||||
value: "true"
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: http
|
||||
|
||||
@@ -43,12 +43,12 @@ subjects:
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: kubescape-service
|
||||
name: kubescape
|
||||
namespace: ks-scanner
|
||||
labels:
|
||||
app: kubescape-service
|
||||
app: kubescape
|
||||
spec:
|
||||
type: NodePort
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 8080
|
||||
name: http
|
||||
|
||||
@@ -6,10 +6,11 @@ replace github.com/armosec/kubescape/v2 => ../
|
||||
|
||||
require (
|
||||
github.com/armosec/kubescape/v2 v2.0.0-00010101000000-000000000000
|
||||
github.com/armosec/opa-utils v0.0.130
|
||||
github.com/armosec/utils-go v0.0.3
|
||||
github.com/armosec/opa-utils v0.0.140
|
||||
github.com/armosec/utils-go v0.0.5
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/gorilla/schema v1.2.0
|
||||
github.com/stretchr/testify v1.7.1
|
||||
)
|
||||
|
||||
@@ -28,10 +29,11 @@ require (
|
||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/OneOfOne/xxhash v1.2.8 // indirect
|
||||
github.com/armosec/armoapi-go v0.0.66 // indirect
|
||||
github.com/armosec/k8s-interface v0.0.69 // indirect
|
||||
github.com/armosec/armoapi-go v0.0.73 // indirect
|
||||
github.com/armosec/go-git-url v0.0.4 // indirect
|
||||
github.com/armosec/k8s-interface v0.0.76 // indirect
|
||||
github.com/armosec/rbac-utils v0.0.14 // indirect
|
||||
github.com/armosec/utils-k8s-go v0.0.5 // indirect
|
||||
github.com/armosec/utils-k8s-go v0.0.6 // indirect
|
||||
github.com/aws/aws-sdk-go v1.41.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.12.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.12.0 // indirect
|
||||
|
||||
@@ -113,26 +113,30 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armosec/armoapi-go v0.0.2/go.mod h1:vIK17yoKbJRQyZXWWLe3AqfqCRITxW8qmSkApyq5xFs=
|
||||
github.com/armosec/armoapi-go v0.0.23/go.mod h1:iaVVGyc23QGGzAdv4n+szGQg3Rbpixn9yQTU3qWRpaw=
|
||||
github.com/armosec/armoapi-go v0.0.58/go.mod h1:U/Axd+D5N00x9Ekr7t+5HXqLCMO+98NfJSVAggqJftI=
|
||||
github.com/armosec/armoapi-go v0.0.66 h1:SEm4nTwtexlMqYQr7sj3rWIrDYZj3BQ76FvKLA8hiLo=
|
||||
github.com/armosec/armoapi-go v0.0.66/go.mod h1:U/Axd+D5N00x9Ekr7t+5HXqLCMO+98NfJSVAggqJftI=
|
||||
github.com/armosec/armoapi-go v0.0.73 h1:LMf+eCkkf+W9NVvOzHKFgVUEpBMvh27M7//UQP3aiO8=
|
||||
github.com/armosec/armoapi-go v0.0.73/go.mod h1:/9SQAgtLbYkfFneRRm/zkIn3zz+4Y2xv6N3vtFcyF8s=
|
||||
github.com/armosec/go-git-url v0.0.4 h1:emG9Yfl53rHpuX41fXLD92ehzhRoNSSnGT6Pr7ogWMY=
|
||||
github.com/armosec/go-git-url v0.0.4/go.mod h1:PJqdEyJyFxTQvawBcyOM0Ies6+uezire5gpwfr1XX5M=
|
||||
github.com/armosec/k8s-interface v0.0.8/go.mod h1:xxS+V5QT3gVQTwZyAMMDrYLWGrfKOpiJ7Jfhfa0w9sM=
|
||||
github.com/armosec/k8s-interface v0.0.37/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU=
|
||||
github.com/armosec/k8s-interface v0.0.66/go.mod h1:vwprS8qn/iowd5yf0JHpqDsLA5I8W2muqX9AxKhkb0Q=
|
||||
github.com/armosec/k8s-interface v0.0.69 h1:rpZElGJjt9xlYrBc5IGKECybf7mZeu+rMEVEQyJOmbg=
|
||||
github.com/armosec/k8s-interface v0.0.69/go.mod h1:MmpOS7RselE+tZgojx5PcBXVbKjWBfHHd/hZ2tWXBdQ=
|
||||
github.com/armosec/k8s-interface v0.0.70/go.mod h1:8NX4xWXh8mwW7QyZdZea1czNdM2azCK9BbUNmiZYXW0=
|
||||
github.com/armosec/k8s-interface v0.0.76 h1:pQaF+8BcNMm6GTYTjdG7vCM1l4BIk7oALXoT6v5gCAk=
|
||||
github.com/armosec/k8s-interface v0.0.76/go.mod h1:8NX4xWXh8mwW7QyZdZea1czNdM2azCK9BbUNmiZYXW0=
|
||||
github.com/armosec/opa-utils v0.0.64/go.mod h1:6tQP8UDq2EvEfSqh8vrUdr/9QVSCG4sJfju1SXQOn4c=
|
||||
github.com/armosec/opa-utils v0.0.130 h1:uP60M0PzmDtLqvsA/jX8BED9/Ava4n2QG7VCkuI+hwI=
|
||||
github.com/armosec/opa-utils v0.0.130/go.mod h1:gap+EaLG5rnyqvIRGxtdNDC9y7VvoGNm90zK8Ls7avQ=
|
||||
github.com/armosec/opa-utils v0.0.139/go.mod h1:VnRVJgDDPFAprGDcibTtKHf9wgkoyTU8wmX2BxEIwok=
|
||||
github.com/armosec/opa-utils v0.0.140 h1:iv6inb6+D0qgeVkv7f+ZIHpy239IUpAwg6Dau0JAWzg=
|
||||
github.com/armosec/opa-utils v0.0.140/go.mod h1:Hwm9ZkcW87mB2567WT6mBuSBEzaKowBNfrl3Q0IVsV8=
|
||||
github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40=
|
||||
github.com/armosec/rbac-utils v0.0.14 h1:CKYKcgqJEXWF2Hen/B1pVGtS3nDAG1wp9dDv6oNtq90=
|
||||
github.com/armosec/rbac-utils v0.0.14/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0=
|
||||
github.com/armosec/utils-go v0.0.2/go.mod h1:itWmRLzRdsnwjpEOomL0mBWGnVNNIxSjDAdyc+b0iUo=
|
||||
github.com/armosec/utils-go v0.0.3 h1:uyQI676yRciQM0sSN9uPoqHkbspTxHO0kmzXhBeE/xU=
|
||||
github.com/armosec/utils-go v0.0.3/go.mod h1:itWmRLzRdsnwjpEOomL0mBWGnVNNIxSjDAdyc+b0iUo=
|
||||
github.com/armosec/utils-go v0.0.4/go.mod h1:itWmRLzRdsnwjpEOomL0mBWGnVNNIxSjDAdyc+b0iUo=
|
||||
github.com/armosec/utils-go v0.0.5 h1:+pfZirWrOvfqvVYlL7OG1wMQD4T4YMwC78zzosB+mlQ=
|
||||
github.com/armosec/utils-go v0.0.5/go.mod h1:itWmRLzRdsnwjpEOomL0mBWGnVNNIxSjDAdyc+b0iUo=
|
||||
github.com/armosec/utils-k8s-go v0.0.1/go.mod h1:qrU4pmY2iZsOb39Eltpm0sTTNM3E4pmeyWx4dgDUC2U=
|
||||
github.com/armosec/utils-k8s-go v0.0.5 h1:zlw6lidAVbUs4cxlm30BOhxKWA/iuVUZTgZMhDC2wtQ=
|
||||
github.com/armosec/utils-k8s-go v0.0.5/go.mod h1:n6V42HYZZBDzMZMiAgUHROZcp4/Wz+wrBm+L6/m6Sdg=
|
||||
github.com/armosec/utils-k8s-go v0.0.6 h1:GriAQZeKsVdlM64lwRnh4EDKlb2R9tK7WXtRYQOrPwk=
|
||||
github.com/armosec/utils-k8s-go v0.0.6/go.mod h1:YFdWi3rEQQLbN6mZO21TSdoda8kGQYRV4rs5CRp8Kjs=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/aws/aws-sdk-go v1.41.1/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
|
||||
github.com/aws/aws-sdk-go v1.41.11 h1:QLouWsiYQ8i22kD8k58Dpdhio1A0MpT7bg9ZNXqEjuI=
|
||||
@@ -459,6 +463,8 @@ github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97Dwqy
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
|
||||
github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
@@ -1146,7 +1152,6 @@ golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211029165221-6e7872819dc8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/armosec/kubescape/v2/core/cautils"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
)
|
||||
|
||||
type PostScanRequest struct {
|
||||
Format string `json:"format"` // Format results (table, json, junit ...) - default json
|
||||
Account string `json:"account"` // account ID
|
||||
Logger string `json:"-"` // logger level - debug/info/error - default is debug
|
||||
FailThreshold float32 `json:"failThreshold"` // Failure score threshold
|
||||
ExcludedNamespaces []string `json:"excludedNamespaces"` // used for host scanner namespace
|
||||
IncludeNamespaces []string `json:"includeNamespaces"` // DEPRECATED?
|
||||
TargetNames []string `json:"targetNames"` // default is all
|
||||
TargetType *reporthandling.NotificationPolicyKind `json:"targetType"` // framework/control - default is framework
|
||||
Submit cautils.BoolPtrFlag `json:"submit"` // Submit results to Armo BE - default will
|
||||
HostScanner cautils.BoolPtrFlag `json:"hostScanner"` // Deploy ARMO K8s host scanner to collect data from certain controls
|
||||
KeepLocal cautils.BoolPtrFlag `json:"keepLocal"` // Do not submit results
|
||||
UseCachedArtifacts cautils.BoolPtrFlag `json:"useCachedArtifacts"` // Use the cached artifacts instead of downloading
|
||||
// UseExceptions string // Load file with exceptions configuration
|
||||
// ControlsInputs string // Load file with inputs for controls
|
||||
// VerboseMode bool // Display all of the input resources and not only failed resources
|
||||
}
|
||||
@@ -3,23 +3,18 @@ package v1
|
||||
import (
|
||||
"strings"
|
||||
|
||||
apisv1 "github.com/armosec/opa-utils/httpserver/apis/v1"
|
||||
utilsmetav1 "github.com/armosec/opa-utils/httpserver/meta/v1"
|
||||
"k8s.io/utils/strings/slices"
|
||||
|
||||
"github.com/armosec/kubescape/v2/core/cautils"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/getter"
|
||||
"github.com/armosec/opa-utils/reporthandling"
|
||||
)
|
||||
|
||||
func (scanRequest *PostScanRequest) ToScanInfo() *cautils.ScanInfo {
|
||||
func ToScanInfo(scanRequest *utilsmetav1.PostScanRequest) *cautils.ScanInfo {
|
||||
scanInfo := defaultScanInfo()
|
||||
|
||||
if scanRequest.TargetType != nil && len(scanRequest.TargetNames) > 0 {
|
||||
if *scanRequest.TargetType == reporthandling.KindFramework {
|
||||
scanInfo.FrameworkScan = true
|
||||
}
|
||||
scanInfo.SetPolicyIdentifiers(scanRequest.TargetNames, *scanRequest.TargetType)
|
||||
scanInfo.ScanAll = false
|
||||
} else {
|
||||
scanInfo.ScanAll = true
|
||||
}
|
||||
setTargetInScanInfo(scanRequest, scanInfo)
|
||||
|
||||
if scanRequest.Account != "" {
|
||||
scanInfo.Account = scanRequest.Account
|
||||
@@ -31,21 +26,64 @@ func (scanRequest *PostScanRequest) ToScanInfo() *cautils.ScanInfo {
|
||||
scanInfo.IncludeNamespaces = strings.Join(scanRequest.IncludeNamespaces, ",")
|
||||
}
|
||||
|
||||
if scanRequest.Format == "" {
|
||||
scanInfo.Format = scanRequest.Format // TODO - handle default
|
||||
if scanRequest.Format != "" {
|
||||
scanInfo.Format = scanRequest.Format
|
||||
}
|
||||
|
||||
if scanRequest.UseCachedArtifacts.Get() != nil && !*scanRequest.UseCachedArtifacts.Get() {
|
||||
scanInfo.UseArtifactsFrom = getter.DefaultLocalStore // Load files from cache (this will prevent kubescape fom downloading the artifacts every time)
|
||||
// UseCachedArtifacts
|
||||
if scanRequest.UseCachedArtifacts != nil {
|
||||
if useCachedArtifacts := cautils.NewBoolPtr(scanRequest.UseCachedArtifacts); useCachedArtifacts.Get() != nil && !*useCachedArtifacts.Get() {
|
||||
scanInfo.UseArtifactsFrom = getter.DefaultLocalStore // Load files from cache (this will prevent kubescape fom downloading the artifacts every time)
|
||||
}
|
||||
}
|
||||
|
||||
if scanRequest.KeepLocal.Get() != nil {
|
||||
scanInfo.Local = *scanRequest.KeepLocal.Get() // Load files from cache (this will prevent kubescape fom downloading the artifacts every time)
|
||||
// KeepLocal
|
||||
if scanRequest.KeepLocal != nil {
|
||||
if keepLocal := cautils.NewBoolPtr(scanRequest.KeepLocal); keepLocal.Get() != nil {
|
||||
scanInfo.Local = *keepLocal.Get() // Load files from cache (this will prevent kubescape fom downloading the artifacts every time)
|
||||
}
|
||||
}
|
||||
if scanRequest.Submit.Get() != nil {
|
||||
scanInfo.Submit = *scanRequest.Submit.Get()
|
||||
|
||||
// submit
|
||||
if scanRequest.Submit != nil {
|
||||
if submit := cautils.NewBoolPtr(scanRequest.Submit); submit.Get() != nil {
|
||||
scanInfo.Submit = *submit.Get()
|
||||
}
|
||||
}
|
||||
|
||||
// host scanner
|
||||
if scanRequest.HostScanner != nil {
|
||||
scanInfo.HostSensorEnabled = cautils.NewBoolPtr(scanRequest.HostScanner)
|
||||
}
|
||||
scanInfo.HostSensorEnabled = scanRequest.HostScanner
|
||||
|
||||
return scanInfo
|
||||
}
|
||||
|
||||
func setTargetInScanInfo(scanRequest *utilsmetav1.PostScanRequest, scanInfo *cautils.ScanInfo) {
|
||||
// remove empty targets from slice
|
||||
scanRequest.TargetNames = slices.Filter(nil, scanRequest.TargetNames, func(e string) bool { return e != "" })
|
||||
|
||||
if scanRequest.TargetType != "" && len(scanRequest.TargetNames) > 0 {
|
||||
if strings.EqualFold(string(scanRequest.TargetType), string(apisv1.KindFramework)) {
|
||||
scanRequest.TargetType = apisv1.KindFramework
|
||||
scanInfo.FrameworkScan = true
|
||||
scanInfo.ScanAll = false
|
||||
if cautils.StringInSlice(scanRequest.TargetNames, "all") != cautils.ValueNotFound { // if scan all frameworks
|
||||
scanRequest.TargetNames = []string{}
|
||||
scanInfo.ScanAll = true
|
||||
}
|
||||
} else if strings.EqualFold(string(scanRequest.TargetType), string(apisv1.KindControl)) {
|
||||
scanRequest.TargetType = apisv1.KindControl
|
||||
scanInfo.ScanAll = false
|
||||
} else {
|
||||
// unknown policy kind - set scan all
|
||||
scanInfo.FrameworkScan = true
|
||||
scanInfo.ScanAll = true
|
||||
scanRequest.TargetNames = []string{}
|
||||
}
|
||||
scanInfo.SetPolicyIdentifiers(scanRequest.TargetNames, scanRequest.TargetType)
|
||||
} else {
|
||||
scanInfo.FrameworkScan = true
|
||||
scanInfo.ScanAll = true
|
||||
}
|
||||
}
|
||||
|
||||
116
httphandler/handlerequests/v1/datastructuremethods_test.go
Normal file
116
httphandler/handlerequests/v1/datastructuremethods_test.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/armosec/kubescape/v2/core/cautils"
|
||||
apisv1 "github.com/armosec/opa-utils/httpserver/apis/v1"
|
||||
utilsmetav1 "github.com/armosec/opa-utils/httpserver/meta/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestToScanInfo(t *testing.T) {
|
||||
{
|
||||
req := &utilsmetav1.PostScanRequest{
|
||||
TargetType: apisv1.KindFramework,
|
||||
Account: "abc",
|
||||
Logger: "info",
|
||||
Format: "pdf",
|
||||
FailThreshold: 50,
|
||||
ExcludedNamespaces: []string{"kube-system", "kube-public"},
|
||||
TargetNames: []string{"nsa", "mitre"},
|
||||
}
|
||||
s := ToScanInfo(req)
|
||||
assert.Equal(t, "abc", s.Account)
|
||||
assert.Equal(t, "v2", s.FormatVersion)
|
||||
assert.Equal(t, "pdf", s.Format)
|
||||
assert.Equal(t, 2, len(s.PolicyIdentifier))
|
||||
assert.Equal(t, "kube-system,kube-public", s.ExcludedNamespaces)
|
||||
|
||||
assert.False(t, s.HostSensorEnabled.GetBool())
|
||||
assert.False(t, s.Local)
|
||||
assert.False(t, s.Submit)
|
||||
assert.False(t, s.ScanAll)
|
||||
assert.True(t, s.FrameworkScan)
|
||||
assert.Equal(t, "nsa", s.PolicyIdentifier[0].Name)
|
||||
assert.Equal(t, apisv1.KindFramework, s.PolicyIdentifier[0].Kind)
|
||||
assert.Equal(t, "mitre", s.PolicyIdentifier[1].Name)
|
||||
assert.Equal(t, apisv1.KindFramework, s.PolicyIdentifier[1].Kind)
|
||||
}
|
||||
{
|
||||
req := &utilsmetav1.PostScanRequest{
|
||||
TargetType: apisv1.KindControl,
|
||||
TargetNames: []string{"c-0001"},
|
||||
IncludeNamespaces: []string{"kube-system", "kube-public"},
|
||||
}
|
||||
s := ToScanInfo(req)
|
||||
assert.False(t, s.ScanAll)
|
||||
assert.False(t, s.FrameworkScan)
|
||||
assert.Equal(t, "kube-system,kube-public", s.IncludeNamespaces)
|
||||
assert.Equal(t, "", s.ExcludedNamespaces)
|
||||
assert.Equal(t, 1, len(s.PolicyIdentifier))
|
||||
assert.Equal(t, "c-0001", s.PolicyIdentifier[0].Name)
|
||||
assert.Equal(t, apisv1.KindControl, s.PolicyIdentifier[0].Kind)
|
||||
}
|
||||
{
|
||||
req := &utilsmetav1.PostScanRequest{}
|
||||
s := ToScanInfo(req)
|
||||
assert.True(t, s.ScanAll)
|
||||
assert.True(t, s.FrameworkScan)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetTargetInScanInfo(t *testing.T) {
|
||||
{
|
||||
req := &utilsmetav1.PostScanRequest{
|
||||
TargetType: apisv1.KindFramework,
|
||||
TargetNames: []string{""},
|
||||
}
|
||||
scanInfo := &cautils.ScanInfo{}
|
||||
setTargetInScanInfo(req, scanInfo)
|
||||
assert.True(t, scanInfo.FrameworkScan)
|
||||
assert.True(t, scanInfo.ScanAll)
|
||||
assert.Equal(t, 0, len(scanInfo.PolicyIdentifier))
|
||||
}
|
||||
{
|
||||
req := &utilsmetav1.PostScanRequest{
|
||||
TargetType: apisv1.KindFramework,
|
||||
TargetNames: []string{"nsa", "mitre"},
|
||||
}
|
||||
scanInfo := &cautils.ScanInfo{}
|
||||
setTargetInScanInfo(req, scanInfo)
|
||||
assert.True(t, scanInfo.FrameworkScan)
|
||||
assert.False(t, scanInfo.ScanAll)
|
||||
assert.Equal(t, 2, len(scanInfo.PolicyIdentifier))
|
||||
}
|
||||
{
|
||||
req := &utilsmetav1.PostScanRequest{
|
||||
TargetType: apisv1.KindFramework,
|
||||
TargetNames: []string{"all"},
|
||||
}
|
||||
scanInfo := &cautils.ScanInfo{}
|
||||
setTargetInScanInfo(req, scanInfo)
|
||||
assert.True(t, scanInfo.FrameworkScan)
|
||||
assert.True(t, scanInfo.ScanAll)
|
||||
assert.Equal(t, 0, len(scanInfo.PolicyIdentifier))
|
||||
}
|
||||
{
|
||||
req := &utilsmetav1.PostScanRequest{}
|
||||
scanInfo := &cautils.ScanInfo{}
|
||||
setTargetInScanInfo(req, scanInfo)
|
||||
assert.True(t, scanInfo.FrameworkScan)
|
||||
assert.True(t, scanInfo.ScanAll)
|
||||
assert.Equal(t, 0, len(scanInfo.PolicyIdentifier))
|
||||
}
|
||||
{
|
||||
req := &utilsmetav1.PostScanRequest{
|
||||
TargetType: apisv1.KindControl,
|
||||
TargetNames: []string{"c-0001"},
|
||||
}
|
||||
scanInfo := &cautils.ScanInfo{}
|
||||
setTargetInScanInfo(req, scanInfo)
|
||||
assert.False(t, scanInfo.FrameworkScan)
|
||||
assert.False(t, scanInfo.ScanAll)
|
||||
assert.Equal(t, 1, len(scanInfo.PolicyIdentifier))
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/armosec/kubescape/v2/core/cautils"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/logger"
|
||||
@@ -28,10 +29,12 @@ func (handler *HTTPHandler) Metrics(w http.ResponseWriter, r *http.Request) {
|
||||
scanID := uuid.NewString()
|
||||
handler.state.setID(scanID)
|
||||
|
||||
resultsFile := filepath.Join(OutputDir, scanID)
|
||||
|
||||
// trigger scanning
|
||||
logger.L().Info(handler.state.getID(), helpers.String("action", "triggering scan"), helpers.Time())
|
||||
ks := core.NewKubescape()
|
||||
results, err := ks.Scan(getPrometheusDefaultScanCommand(scanID))
|
||||
results, err := ks.Scan(getPrometheusDefaultScanCommand(scanID, resultsFile))
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("failed to complete scan. reason: %s", err.Error())))
|
||||
@@ -40,26 +43,25 @@ func (handler *HTTPHandler) Metrics(w http.ResponseWriter, r *http.Request) {
|
||||
results.HandleResults()
|
||||
logger.L().Info(handler.state.getID(), helpers.String("action", "done scanning"), helpers.Time())
|
||||
|
||||
f, err := os.ReadFile(scanID)
|
||||
// res, err := results.ToJson()
|
||||
f, err := os.ReadFile(resultsFile)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("failed read results from file. reason: %s", err.Error())))
|
||||
return
|
||||
}
|
||||
os.Remove(scanID)
|
||||
os.Remove(resultsFile)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(f)
|
||||
}
|
||||
|
||||
func getPrometheusDefaultScanCommand(scanID string) *cautils.ScanInfo {
|
||||
func getPrometheusDefaultScanCommand(scanID, resultsFile string) *cautils.ScanInfo {
|
||||
scanInfo := defaultScanInfo()
|
||||
scanInfo.FrameworkScan = true
|
||||
scanInfo.ScanAll = true // scan all frameworks
|
||||
scanInfo.ScanID = scanID // scan ID
|
||||
scanInfo.FailThreshold = 100 // Do not fail scanning
|
||||
scanInfo.Output = scanID // results output
|
||||
scanInfo.Output = resultsFile // results output
|
||||
scanInfo.Format = envToString("KS_FORMAT", "prometheus") // default output should be json
|
||||
scanInfo.HostSensorEnabled.SetBool(envToBool("KS_ENABLE_HOST_SCANNER", false)) // enable host scanner
|
||||
return scanInfo
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/armosec/kubescape/v2/core/cautils/getter"
|
||||
@@ -9,10 +10,11 @@ import (
|
||||
|
||||
func TestGetPrometheusDefaultScanCommand(t *testing.T) {
|
||||
scanID := "1234"
|
||||
scanInfo := getPrometheusDefaultScanCommand(scanID)
|
||||
outputFile := filepath.Join(OutputDir, scanID)
|
||||
scanInfo := getPrometheusDefaultScanCommand(scanID, outputFile)
|
||||
|
||||
assert.Equal(t, scanID, scanInfo.ScanID)
|
||||
assert.Equal(t, scanID, scanInfo.Output)
|
||||
assert.Equal(t, outputFile, scanInfo.Output)
|
||||
assert.Equal(t, "prometheus", scanInfo.Format)
|
||||
// assert.False(t, *scanInfo.HostSensorEnabled.Get())
|
||||
assert.Equal(t, getter.DefaultLocalStore, scanInfo.UseArtifactsFrom)
|
||||
|
||||
@@ -7,6 +7,10 @@ import (
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
utilsapisv1 "github.com/armosec/opa-utils/httpserver/apis/v1"
|
||||
utilsmetav1 "github.com/armosec/opa-utils/httpserver/meta/v1"
|
||||
"github.com/gorilla/schema"
|
||||
|
||||
"github.com/armosec/kubescape/v2/core/cautils/logger"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
|
||||
"github.com/google/uuid"
|
||||
@@ -15,6 +19,21 @@ import (
|
||||
var OutputDir = "./results"
|
||||
var FailedOutputDir = "./failed"
|
||||
|
||||
type ScanQueryParams struct {
|
||||
ReturnResults bool `schema:"wait"` // wait for scanning to complete (synchronized request)
|
||||
KeepResults bool `schema:"keep"` // do not delete results after returning (relevant only for synchronized requests)
|
||||
}
|
||||
|
||||
type ResultsQueryParams struct {
|
||||
ScanID string `schema:"id"`
|
||||
KeepResults bool `schema:"keep"` // do not delete results after returning (default will delete results)
|
||||
AllResults bool `schema:"all"` // delete all results
|
||||
}
|
||||
|
||||
type StatusQueryParams struct {
|
||||
ScanID string `schema:"id"`
|
||||
}
|
||||
|
||||
type HTTPHandler struct {
|
||||
state *serverState
|
||||
}
|
||||
@@ -25,33 +44,71 @@ func NewHTTPHandler() *HTTPHandler {
|
||||
}
|
||||
}
|
||||
|
||||
func (handler *HTTPHandler) Scan(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
handler.state.setNotBusy()
|
||||
logger.L().Error("Scan recover", helpers.Error(fmt.Errorf("%v", err)))
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("%v", err)))
|
||||
}
|
||||
}()
|
||||
// ============================================== STATUS ========================================================
|
||||
// Status API
|
||||
func (handler *HTTPHandler) Status(w http.ResponseWriter, r *http.Request) {
|
||||
defer handler.recover(w)
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodGet: // return request template
|
||||
json.NewEncoder(w).Encode(PostScanRequest{})
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
case http.MethodPost:
|
||||
default:
|
||||
if r.Method != http.MethodGet {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
response := utilsmetav1.Response{}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
statusQueryParams := &StatusQueryParams{}
|
||||
if err := schema.NewDecoder().Decode(statusQueryParams, r.URL.Query()); err != nil {
|
||||
handler.writeError(w, fmt.Errorf("failed to parse query params, reason: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if !handler.state.isBusy() {
|
||||
response.Type = utilsapisv1.NotBusyScanResponseType
|
||||
w.Write(responseToBytes(&response))
|
||||
return
|
||||
}
|
||||
|
||||
currentScanID := handler.state.getID()
|
||||
if statusQueryParams.ScanID != "" && currentScanID != statusQueryParams.ScanID {
|
||||
response.Type = utilsapisv1.NotBusyScanResponseType
|
||||
w.Write(responseToBytes(&response))
|
||||
return
|
||||
}
|
||||
|
||||
response.Response = currentScanID
|
||||
response.ID = currentScanID
|
||||
response.Type = utilsapisv1.BusyScanResponseType
|
||||
w.Write(responseToBytes(&response))
|
||||
}
|
||||
|
||||
// ============================================== SCAN ========================================================
|
||||
// Scan API - TODO: break down to functions
|
||||
func (handler *HTTPHandler) Scan(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
defer handler.recover(w)
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
if r.Method != http.MethodPost {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
response := utilsmetav1.Response{}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
scanQueryParams := &ScanQueryParams{}
|
||||
if err := schema.NewDecoder().Decode(scanQueryParams, r.URL.Query()); err != nil {
|
||||
handler.writeError(w, fmt.Errorf("failed to parse query params, reason: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
if handler.state.isBusy() {
|
||||
w.Write([]byte(handler.state.getID()))
|
||||
// TODO - Add to queue
|
||||
w.WriteHeader(http.StatusOK)
|
||||
response.Response = handler.state.getID()
|
||||
response.ID = handler.state.getID()
|
||||
response.Type = utilsapisv1.IDScanResponseType
|
||||
w.Write(responseToBytes(&response))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -60,32 +117,30 @@ func (handler *HTTPHandler) Scan(w http.ResponseWriter, r *http.Request) {
|
||||
// generate id
|
||||
scanID := uuid.NewString()
|
||||
handler.state.setID(scanID)
|
||||
response.ID = scanID
|
||||
response.Type = utilsapisv1.IDScanResponseType
|
||||
|
||||
readBuffer, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
defer handler.state.setNotBusy()
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(fmt.Sprintf("failed to read request body, reason: %s", err.Error())))
|
||||
handler.writeError(w, fmt.Errorf("failed to read request body, reason: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
scanRequest := PostScanRequest{}
|
||||
|
||||
logger.L().Info("REST API received scan request", helpers.String("body", string(readBuffer)))
|
||||
|
||||
scanRequest := utilsmetav1.PostScanRequest{}
|
||||
if err := json.Unmarshal(readBuffer, &scanRequest); err != nil {
|
||||
defer handler.state.setNotBusy()
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(fmt.Sprintf("failed to parse request payload, reason: %s", err.Error())))
|
||||
handler.writeError(w, fmt.Errorf("failed to parse request payload, reason: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
response := []byte(scanID)
|
||||
|
||||
returnResults := r.URL.Query().Has("wait")
|
||||
var wg sync.WaitGroup
|
||||
if returnResults {
|
||||
if scanQueryParams.ReturnResults {
|
||||
wg.Add(1)
|
||||
} else {
|
||||
wg.Add(0)
|
||||
}
|
||||
|
||||
statusCode := http.StatusOK
|
||||
go func() {
|
||||
// execute scan in the background
|
||||
|
||||
@@ -94,72 +149,100 @@ func (handler *HTTPHandler) Scan(w http.ResponseWriter, r *http.Request) {
|
||||
results, err := scan(&scanRequest, scanID)
|
||||
if err != nil {
|
||||
logger.L().Error("scanning failed", helpers.String("ID", scanID), helpers.Error(err))
|
||||
if returnResults {
|
||||
response = []byte(err.Error())
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
if scanQueryParams.ReturnResults {
|
||||
response.Type = utilsapisv1.ErrorScanResponseType
|
||||
response.Response = err.Error()
|
||||
statusCode = http.StatusInternalServerError
|
||||
}
|
||||
} else {
|
||||
logger.L().Success("done scanning", helpers.String("ID", scanID))
|
||||
if returnResults {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
response = results
|
||||
if scanQueryParams.ReturnResults {
|
||||
response.Type = utilsapisv1.ResultsV1ScanResponseType
|
||||
response.Response = results
|
||||
wg.Done()
|
||||
}
|
||||
}
|
||||
if scanQueryParams.ReturnResults && !scanQueryParams.KeepResults {
|
||||
logger.L().Debug("deleting results", helpers.String("ID", scanID))
|
||||
removeResultsFile(scanID)
|
||||
}
|
||||
handler.state.setNotBusy()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(response)
|
||||
|
||||
w.WriteHeader(statusCode)
|
||||
w.Write(responseToBytes(&response))
|
||||
}
|
||||
|
||||
// ============================================== RESULTS ========================================================
|
||||
|
||||
// Results API - TODO: break down to functions
|
||||
func (handler *HTTPHandler) Results(w http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
handler.state.setNotBusy()
|
||||
logger.L().Error("Results recover", helpers.Error(fmt.Errorf("%v", err)))
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("%v", err)))
|
||||
}
|
||||
}()
|
||||
response := utilsmetav1.Response{}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
defer handler.recover(w)
|
||||
|
||||
defer r.Body.Close()
|
||||
|
||||
var scanID string
|
||||
if scanID = r.URL.Query().Get("scanID"); scanID == "" {
|
||||
scanID = handler.state.getLatestID()
|
||||
resultsQueryParams := &ResultsQueryParams{}
|
||||
if err := schema.NewDecoder().Decode(resultsQueryParams, r.URL.Query()); err != nil {
|
||||
handler.writeError(w, fmt.Errorf("failed to parse query params, reason: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if resultsQueryParams.ScanID == "" {
|
||||
resultsQueryParams.ScanID = handler.state.getLatestID()
|
||||
}
|
||||
|
||||
if resultsQueryParams.ScanID == "" { // if no scan found
|
||||
logger.L().Info("empty scan ID")
|
||||
w.WriteHeader(http.StatusBadRequest) // Should we return ok?
|
||||
response.Response = "latest scan not found. trigger again"
|
||||
response.Type = utilsapisv1.ErrorScanResponseType
|
||||
w.Write(responseToBytes(&response))
|
||||
return
|
||||
}
|
||||
response.ID = resultsQueryParams.ScanID
|
||||
|
||||
if handler.state.isBusy() { // if requested ID is still scanning
|
||||
if scanID == handler.state.getID() {
|
||||
logger.L().Info("scan in process", helpers.String("ID", scanID))
|
||||
w.WriteHeader(http.StatusOK) // Should we return ok?
|
||||
w.Write([]byte(handler.state.getID()))
|
||||
if resultsQueryParams.ScanID == handler.state.getID() {
|
||||
logger.L().Info("scan in process", helpers.String("ID", resultsQueryParams.ScanID))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
response.Response = "scanning in progress"
|
||||
w.Write(responseToBytes(&response))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
logger.L().Info("requesting results", helpers.String("ID", scanID))
|
||||
logger.L().Info("requesting results", helpers.String("ID", resultsQueryParams.ScanID))
|
||||
|
||||
if r.URL.Query().Has("remove") {
|
||||
defer removeResultsFile(scanID)
|
||||
}
|
||||
if res, err := readResultsFile(scanID); err != nil {
|
||||
if res, err := readResultsFile(resultsQueryParams.ScanID); err != nil {
|
||||
logger.L().Info("scan result not found", helpers.String("ID", resultsQueryParams.ScanID))
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
w.Write([]byte(err.Error()))
|
||||
response.Response = err.Error()
|
||||
} else {
|
||||
logger.L().Info("scan result found", helpers.String("ID", resultsQueryParams.ScanID))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write(res)
|
||||
}
|
||||
case http.MethodDelete:
|
||||
logger.L().Info("deleting results", helpers.String("ID", scanID))
|
||||
response.Response = res
|
||||
|
||||
if r.URL.Query().Has("all") {
|
||||
if !resultsQueryParams.KeepResults {
|
||||
logger.L().Info("deleting results", helpers.String("ID", resultsQueryParams.ScanID))
|
||||
defer removeResultsFile(resultsQueryParams.ScanID)
|
||||
}
|
||||
|
||||
}
|
||||
w.Write(responseToBytes(&response))
|
||||
case http.MethodDelete:
|
||||
logger.L().Info("deleting results", helpers.String("ID", resultsQueryParams.ScanID))
|
||||
|
||||
if resultsQueryParams.AllResults {
|
||||
removeResultDirs()
|
||||
} else {
|
||||
removeResultsFile(scanID)
|
||||
removeResultsFile(resultsQueryParams.ScanID)
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
default:
|
||||
@@ -175,3 +258,29 @@ func (handler *HTTPHandler) Live(w http.ResponseWriter, r *http.Request) {
|
||||
func (handler *HTTPHandler) Ready(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
func responseToBytes(res *utilsmetav1.Response) []byte {
|
||||
b, _ := json.Marshal(res)
|
||||
return b
|
||||
}
|
||||
|
||||
func (handler *HTTPHandler) recover(w http.ResponseWriter) {
|
||||
response := utilsmetav1.Response{}
|
||||
if err := recover(); err != nil {
|
||||
handler.state.setNotBusy()
|
||||
logger.L().Error("recover", helpers.Error(fmt.Errorf("%v", err)))
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
response.Response = fmt.Sprintf("%v", err)
|
||||
response.Type = utilsapisv1.ErrorScanResponseType
|
||||
w.Write(responseToBytes(&response))
|
||||
}
|
||||
}
|
||||
|
||||
func (handler *HTTPHandler) writeError(w http.ResponseWriter, err error) {
|
||||
response := utilsmetav1.Response{}
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
response.Response = err.Error()
|
||||
response.Type = utilsapisv1.ErrorScanResponseType
|
||||
w.Write(responseToBytes(&response))
|
||||
handler.state.setNotBusy()
|
||||
}
|
||||
|
||||
34
httphandler/handlerequests/v1/requestshandlerutil_test.go
Normal file
34
httphandler/handlerequests/v1/requestshandlerutil_test.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
apisv1 "github.com/armosec/opa-utils/httpserver/apis/v1"
|
||||
utilsmetav1 "github.com/armosec/opa-utils/httpserver/meta/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDefaultScanInfo(t *testing.T) {
|
||||
s := defaultScanInfo()
|
||||
|
||||
assert.Equal(t, "", s.Account)
|
||||
assert.Equal(t, "v2", s.FormatVersion)
|
||||
assert.Equal(t, "json", s.Format)
|
||||
assert.False(t, s.HostSensorEnabled.GetBool())
|
||||
assert.False(t, s.Local)
|
||||
assert.False(t, s.Submit)
|
||||
}
|
||||
|
||||
func TestGetScanCommand(t *testing.T) {
|
||||
req := utilsmetav1.PostScanRequest{
|
||||
TargetType: apisv1.KindFramework,
|
||||
}
|
||||
s := getScanCommand(&req, "abc")
|
||||
assert.Equal(t, "", s.Account)
|
||||
assert.Equal(t, "abc", s.ScanID)
|
||||
assert.Equal(t, "v2", s.FormatVersion)
|
||||
assert.Equal(t, "json", s.Format)
|
||||
assert.False(t, s.HostSensorEnabled.GetBool())
|
||||
assert.False(t, s.Local)
|
||||
assert.False(t, s.Submit)
|
||||
}
|
||||
@@ -1,42 +1,43 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
pkgcautils "github.com/armosec/utils-go/utils"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/kubescape/v2/core/cautils"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/getter"
|
||||
"github.com/armosec/kubescape/v2/core/core"
|
||||
utilsmetav1 "github.com/armosec/opa-utils/httpserver/meta/v1"
|
||||
reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2"
|
||||
"github.com/armosec/utils-go/boolutils"
|
||||
)
|
||||
|
||||
func scan(scanRequest *PostScanRequest, scanID string) ([]byte, error) {
|
||||
func scan(scanRequest *utilsmetav1.PostScanRequest, scanID string) (*reporthandlingv2.PostureReport, error) {
|
||||
scanInfo := getScanCommand(scanRequest, scanID)
|
||||
|
||||
ks := core.NewKubescape()
|
||||
result, err := ks.Scan(scanInfo)
|
||||
if err != nil {
|
||||
f, e := os.Open(filepath.Join(FailedOutputDir, scanID))
|
||||
if e != nil {
|
||||
return []byte{}, fmt.Errorf("failed to scan. reason: '%s'. failed to save error in file. reason: %s", err.Error(), e.Error())
|
||||
}
|
||||
defer f.Close()
|
||||
f.Write([]byte(e.Error()))
|
||||
|
||||
return nil, writeScanErrorToFile(err, scanID)
|
||||
}
|
||||
result.HandleResults()
|
||||
b, err := result.ToJson()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to parse results to json, reason: %s", err.Error())
|
||||
if err := result.HandleResults(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, err
|
||||
return result.GetResults(), nil
|
||||
}
|
||||
|
||||
func readResultsFile(fileID string) ([]byte, error) {
|
||||
func readResultsFile(fileID string) (*reporthandlingv2.PostureReport, error) {
|
||||
if fileName := searchFile(fileID); fileName != "" {
|
||||
return os.ReadFile(fileName)
|
||||
f, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
postureReport := &reporthandlingv2.PostureReport{}
|
||||
err = json.Unmarshal(f, postureReport)
|
||||
return postureReport, err
|
||||
}
|
||||
return nil, fmt.Errorf("file %s not found", fileID)
|
||||
}
|
||||
@@ -62,21 +63,25 @@ func searchFile(fileID string) string {
|
||||
}
|
||||
|
||||
func findFile(targetDir string, fileName string) (string, error) {
|
||||
|
||||
matches, err := filepath.Glob(filepath.Join(targetDir, fileName))
|
||||
var files []string
|
||||
err := filepath.Walk(targetDir, func(path string, info os.FileInfo, err error) error {
|
||||
files = append(files, path)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(matches) != 0 {
|
||||
return matches[0], nil
|
||||
for i := range files {
|
||||
if strings.Contains(files[i], fileName) {
|
||||
return files[i], nil
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func getScanCommand(scanRequest *PostScanRequest, scanID string) *cautils.ScanInfo {
|
||||
func getScanCommand(scanRequest *utilsmetav1.PostScanRequest, scanID string) *cautils.ScanInfo {
|
||||
|
||||
scanInfo := scanRequest.ToScanInfo()
|
||||
scanInfo := ToScanInfo(scanRequest)
|
||||
scanInfo.ScanID = scanID
|
||||
|
||||
// *** start ***
|
||||
@@ -98,14 +103,15 @@ func getScanCommand(scanRequest *PostScanRequest, scanID string) *cautils.ScanIn
|
||||
func defaultScanInfo() *cautils.ScanInfo {
|
||||
scanInfo := &cautils.ScanInfo{}
|
||||
scanInfo.FailThreshold = 100
|
||||
scanInfo.Account = envToString("KS_ACCOUNT", "") // publish results to Kubescape SaaS
|
||||
scanInfo.ExcludedNamespaces = envToString("KS_EXCLUDE_NAMESPACES", "") // namespace to exclude
|
||||
scanInfo.IncludeNamespaces = envToString("KS_INCLUDE_NAMESPACES", "") // namespace to include
|
||||
scanInfo.FormatVersion = envToString("KS_FORMAT_VERSION", "v2") // output format version
|
||||
scanInfo.Format = envToString("KS_FORMAT", "json") // default output should be json
|
||||
scanInfo.Submit = envToBool("KS_SUBMIT", false) // publish results to Kubescape SaaS
|
||||
scanInfo.HostSensorEnabled.SetBool(envToBool("KS_ENABLE_HOST_SCANNER", true)) // enable host scanner
|
||||
scanInfo.Local = envToBool("KS_KEEP_LOCAL", false) // do not publish results to Kubescape SaaS
|
||||
scanInfo.Account = envToString("KS_ACCOUNT", "") // publish results to Kubescape SaaS
|
||||
scanInfo.ExcludedNamespaces = envToString("KS_EXCLUDE_NAMESPACES", "") // namespace to exclude
|
||||
scanInfo.HostSensorYamlPath = envToString("KS_HOST_SCAN_YAML", "") // namespace to exclude
|
||||
scanInfo.IncludeNamespaces = envToString("KS_INCLUDE_NAMESPACES", "") // namespace to include
|
||||
scanInfo.FormatVersion = envToString("KS_FORMAT_VERSION", "v2") // output format version
|
||||
scanInfo.Format = envToString("KS_FORMAT", "json") // default output should be json
|
||||
scanInfo.Submit = envToBool("KS_SUBMIT", false) // publish results to Kubescape SaaS
|
||||
scanInfo.HostSensorEnabled.SetBool(envToBool("KS_ENABLE_HOST_SCANNER", false)) // enable host scanner
|
||||
scanInfo.Local = envToBool("KS_KEEP_LOCAL", false) // do not publish results to Kubescape SaaS
|
||||
if !envToBool("KS_DOWNLOAD_ARTIFACTS", false) {
|
||||
scanInfo.UseArtifactsFrom = getter.DefaultLocalStore // Load files from cache (this will prevent kubescape fom downloading the artifacts every time)
|
||||
}
|
||||
@@ -114,7 +120,7 @@ func defaultScanInfo() *cautils.ScanInfo {
|
||||
|
||||
func envToBool(env string, defaultValue bool) bool {
|
||||
if d, ok := os.LookupEnv(env); ok {
|
||||
return pkgcautils.StringToBool(d)
|
||||
return boolutils.StringToBool(d)
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
@@ -125,3 +131,19 @@ func envToString(env string, defaultValue string) string {
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func writeScanErrorToFile(err error, scanID string) error {
|
||||
if e := os.MkdirAll(filepath.Dir(FailedOutputDir), os.ModePerm); e != nil {
|
||||
return fmt.Errorf("failed to scan. reason: '%s'. failed to save error in file - failed to create directory. reason: %s", err.Error(), e.Error())
|
||||
}
|
||||
f, e := os.Create(filepath.Join(FailedOutputDir, scanID))
|
||||
if e != nil {
|
||||
return fmt.Errorf("failed to scan. reason: '%s'. failed to save error in file - failed to open file for writing. reason: %s", err.Error(), e.Error())
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, e := f.Write([]byte(err.Error())); e != nil {
|
||||
return fmt.Errorf("failed to scan. reason: '%s'. failed to save error in file - failed to write. reason: %s", err.Error(), e.Error())
|
||||
}
|
||||
return fmt.Errorf("failed to scan. reason: '%s'", err.Error())
|
||||
}
|
||||
|
||||
32
httphandler/listener/init.go
Normal file
32
httphandler/listener/init.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package listener
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/armosec/kubescape/v2/core/cautils/getter"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/logger"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/logger/zaplogger"
|
||||
)
|
||||
|
||||
func initialize() error {
|
||||
logger.InitLogger(zaplogger.LoggerName)
|
||||
|
||||
initializeSaaSEnv()
|
||||
return nil
|
||||
}
|
||||
|
||||
func initializeSaaSEnv() {
|
||||
|
||||
saasEnv := os.Getenv("KS_SAAS_ENV")
|
||||
switch saasEnv {
|
||||
case "dev", "development":
|
||||
logger.L().Debug("setting dev env")
|
||||
getter.SetARMOAPIConnector(getter.NewARMOAPIDev())
|
||||
case "stage", "staging":
|
||||
logger.L().Debug("setting staging env")
|
||||
getter.SetARMOAPIConnector(getter.NewARMOAPIStaging())
|
||||
default:
|
||||
logger.L().Debug("setting prod env")
|
||||
getter.SetARMOAPIConnector(getter.NewARMOAPIProd())
|
||||
}
|
||||
}
|
||||
@@ -9,13 +9,13 @@ import (
|
||||
"github.com/armosec/kubescape/v2/core/cautils"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/logger"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/logger/helpers"
|
||||
"github.com/armosec/kubescape/v2/core/cautils/logger/zaplogger"
|
||||
handlerequestsv1 "github.com/armosec/kubescape/v2/httphandler/handlerequests/v1"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
const (
|
||||
scanPath = "/v1/scan"
|
||||
statusPath = "/v1/status"
|
||||
resultsPath = "/v1/results"
|
||||
prometheusMmeticsPath = "/v1/metrics"
|
||||
livePath = "/livez"
|
||||
@@ -24,7 +24,7 @@ const (
|
||||
|
||||
// SetupHTTPListener set up listening http servers
|
||||
func SetupHTTPListener() error {
|
||||
logger.InitLogger(zaplogger.LoggerName)
|
||||
initialize()
|
||||
|
||||
keyPair, err := loadTLSKey("", "") // TODO - support key and crt files
|
||||
if err != nil {
|
||||
@@ -45,6 +45,7 @@ func SetupHTTPListener() error {
|
||||
|
||||
rtr.HandleFunc(prometheusMmeticsPath, httpHandler.Metrics)
|
||||
rtr.HandleFunc(scanPath, httpHandler.Scan)
|
||||
rtr.HandleFunc(statusPath, httpHandler.Status)
|
||||
rtr.HandleFunc(resultsPath, httpHandler.Results)
|
||||
rtr.HandleFunc(livePath, httpHandler.Live)
|
||||
rtr.HandleFunc(readyPath, httpHandler.Ready)
|
||||
|
||||
@@ -54,6 +54,6 @@ echo -e "\033[0m"
|
||||
$KUBESCAPE_EXEC version
|
||||
echo
|
||||
|
||||
echo -e "\033[35mUsage: $ $KUBESCAPE_EXEC scan --submit --enable-host-scan --format-version v2 --verbose"
|
||||
echo -e "\033[35mUsage: $ $KUBESCAPE_EXEC scan --submit --enable-host-scan --verbose"
|
||||
|
||||
echo -e "\033[0m"
|
||||
|
||||
Reference in New Issue
Block a user