mirror of
https://github.com/kubescape/kubescape.git
synced 2026-02-14 18:09:55 +00:00
Add a new --labels-to-copy CLI flag that allows users to specify which labels from Kubernetes workloads should be extracted and included in scan reports. This makes it easier to tie scan results back to app teams or repositories by including relevant labels like 'app', 'team', or 'environment' in the report output. Changes: - Add LabelsToCopy field to ScanInfo and OPASessionObj structs - Add --labels-to-copy flag to scan command - Add ResourceLabels field to PostureReportWithSeverity for JSON output - Implement extractResourceLabels function to extract specified labels - Add unit tests for label extraction functionality Fixes #1660 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: majiayu000 <1835304752@qq.com>
152 lines
9.8 KiB
Go
152 lines
9.8 KiB
Go
package scan
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/kubescape/go-logger"
|
|
"github.com/kubescape/kubescape/v3/core/cautils"
|
|
"github.com/kubescape/kubescape/v3/core/cautils/getter"
|
|
"github.com/kubescape/kubescape/v3/core/meta"
|
|
v1 "github.com/kubescape/opa-utils/httpserver/apis/v1"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var scanCmdExamples = fmt.Sprintf(`
|
|
Scan command is for scanning an existing cluster or kubernetes manifest files based on pre-defined frameworks
|
|
|
|
# Scan current cluster
|
|
%[1]s scan
|
|
|
|
# Scan kubernetes manifest files
|
|
%[1]s scan .
|
|
|
|
# Scan and save the results in the JSON format
|
|
%[1]s scan --format json --output results.json
|
|
|
|
# Display all resources
|
|
%[1]s scan --verbose
|
|
|
|
# Scan different clusters from the kubectl context
|
|
%[1]s scan --kube-context <kubernetes context>
|
|
`, cautils.ExecName())
|
|
|
|
func GetScanCommand(ks meta.IKubescape) *cobra.Command {
|
|
var scanInfo cautils.ScanInfo
|
|
|
|
// scanCmd represents the scan command
|
|
scanCmd := &cobra.Command{
|
|
Use: "scan",
|
|
Short: "Scan a Kubernetes cluster or YAML files for image vulnerabilities and misconfigurations",
|
|
Long: `The action you want to perform`,
|
|
Example: scanCmdExamples,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
if scanInfo.View == string(cautils.SecurityViewType) {
|
|
setSecurityViewScanInfo(args, &scanInfo)
|
|
|
|
if err := securityScan(scanInfo, ks); err != nil {
|
|
logger.L().Fatal(err.Error())
|
|
}
|
|
} else if len(args) == 0 || (args[0] != "framework" && args[0] != "control") {
|
|
if err := getFrameworkCmd(ks, &scanInfo).RunE(cmd, append([]string{strings.Join(getter.NativeFrameworks, ",")}, args...)); err != nil {
|
|
logger.L().Fatal(err.Error())
|
|
}
|
|
} else {
|
|
return fmt.Errorf("kubescape did not do anything")
|
|
}
|
|
|
|
return nil
|
|
},
|
|
PostRun: func(cmd *cobra.Command, args []string) {
|
|
// TODO - revert context
|
|
},
|
|
}
|
|
|
|
scanInfo.TriggeredByCLI = true
|
|
|
|
scanCmd.PersistentFlags().StringVarP(&scanInfo.AccountID, "account", "", "", "Kubescape SaaS account ID. Default will load account ID from cache")
|
|
scanCmd.PersistentFlags().StringVarP(&scanInfo.AccessKey, "access-key", "", "", "Kubescape SaaS access key. Default will load access key from cache")
|
|
scanCmd.PersistentFlags().StringVar(&scanInfo.ControlsInputs, "controls-config", "", "Path to an controls-config obj. If not set will download controls-config from ARMO management portal")
|
|
scanCmd.PersistentFlags().StringVar(&scanInfo.UseExceptions, "exceptions", "", "Path to an exceptions obj. If not set will download exceptions from ARMO management portal")
|
|
scanCmd.PersistentFlags().StringVar(&scanInfo.UseArtifactsFrom, "use-artifacts-from", "", "Load artifacts from local directory. If not used will download them")
|
|
scanCmd.PersistentFlags().StringVarP(&scanInfo.ExcludedNamespaces, "exclude-namespaces", "e", "", "Namespaces to exclude from scanning. e.g: --exclude-namespaces ns-a,ns-b. Notice, when running with `exclude-namespace` kubescape does not scan cluster-scoped objects.")
|
|
|
|
scanCmd.PersistentFlags().Float32VarP(&scanInfo.FailThreshold, "fail-threshold", "t", 100, "Failure threshold is the percent above which the command fails and returns exit code 1")
|
|
scanCmd.PersistentFlags().Float32VarP(&scanInfo.ComplianceThreshold, "compliance-threshold", "", 0, "Compliance threshold is the percent below which the command fails and returns exit code 1")
|
|
|
|
scanCmd.PersistentFlags().StringVar(&scanInfo.FailThresholdSeverity, "severity-threshold", "", "Severity threshold is the severity of failed controls at which the command fails and returns exit code 1")
|
|
scanCmd.PersistentFlags().StringVarP(&scanInfo.Format, "format", "f", "pretty-printer", `Output file format. Supported formats: "pretty-printer", "json", "junit", "prometheus", "pdf", "html", "sarif"`)
|
|
scanCmd.PersistentFlags().StringVar(&scanInfo.IncludeNamespaces, "include-namespaces", "", "scan specific namespaces. e.g: --include-namespaces ns-a,ns-b")
|
|
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Local, "keep-local", "", false, "If you do not want your Kubescape results reported to configured backend.")
|
|
scanCmd.PersistentFlags().StringVarP(&scanInfo.Output, "output", "o", "", "Output file. Print output to file and not stdout")
|
|
scanCmd.PersistentFlags().BoolVarP(&scanInfo.VerboseMode, "verbose", "v", false, "Display all of the input resources and not only failed resources")
|
|
scanCmd.PersistentFlags().StringVar(&scanInfo.View, "view", string(cautils.SecurityViewType), fmt.Sprintf("View results based on the %s/%s/%s. default is --view=%s", cautils.ResourceViewType, cautils.ControlViewType, cautils.SecurityViewType, cautils.SecurityViewType))
|
|
scanCmd.PersistentFlags().BoolVar(&scanInfo.UseDefault, "use-default", false, "Load local policy object from default path. If not used will download latest")
|
|
scanCmd.PersistentFlags().StringSliceVar(&scanInfo.UseFrom, "use-from", nil, "Load local policy object from specified path. If not used will download latest")
|
|
scanCmd.PersistentFlags().StringVar(&scanInfo.HostSensorYamlPath, "host-scan-yaml", "", "Override default host scanner DaemonSet. Use this flag cautiously")
|
|
scanCmd.PersistentFlags().StringVar(&scanInfo.FormatVersion, "format-version", "v2", "Output object can be different between versions, this is for maintaining backward and forward compatibility. Supported:'v1'/'v2'")
|
|
scanCmd.PersistentFlags().StringVar(&scanInfo.CustomClusterName, "cluster-name", "", "Set the custom name of the cluster. Not same as the kube-context flag")
|
|
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Submit, "submit", "", false, "Submit the scan results to Kubescape SaaS where you can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more. By default the results are not submitted")
|
|
scanCmd.PersistentFlags().BoolVarP(&scanInfo.OmitRawResources, "omit-raw-resources", "", false, "Omit raw resources from the output. By default the raw resources are included in the output")
|
|
scanCmd.PersistentFlags().BoolVarP(&scanInfo.PrintAttackTree, "print-attack-tree", "", false, "Print attack tree")
|
|
scanCmd.PersistentFlags().BoolVarP(&scanInfo.EnableRegoPrint, "enable-rego-prints", "", false, "Enable sending to rego prints to the logs (use with debug log level: -l debug)")
|
|
scanCmd.PersistentFlags().BoolVarP(&scanInfo.ScanImages, "scan-images", "", false, "Scan resources images")
|
|
scanCmd.PersistentFlags().BoolVarP(&scanInfo.UseDefaultMatchers, "use-default-matchers", "", true, "Use default matchers (true) or CPE matchers (false) for image scanning")
|
|
scanCmd.PersistentFlags().StringSliceVar(&scanInfo.LabelsToCopy, "labels-to-copy", nil, "Labels to copy from workloads to scan reports for easy identification. e.g: --labels-to-copy=app,team,environment")
|
|
|
|
scanCmd.PersistentFlags().MarkDeprecated("fail-threshold", "use '--compliance-threshold' flag instead. Flag will be removed at 1.Dec.2023")
|
|
scanCmd.PersistentFlags().MarkDeprecated("create-account", "Create account is no longer supported. In case of a missing Account ID and a configured backend server, a new account id will be generated automatically by Kubescape. Feel free to contact the Kubescape maintainers for more information.")
|
|
|
|
// hidden flags
|
|
scanCmd.PersistentFlags().MarkHidden("omit-raw-resources")
|
|
scanCmd.PersistentFlags().MarkHidden("print-attack-tree")
|
|
scanCmd.PersistentFlags().MarkHidden("format-version")
|
|
|
|
// Retrieve --kubeconfig flag from https://github.com/kubernetes/kubectl/blob/master/pkg/cmd/cmd.go
|
|
scanCmd.PersistentFlags().AddGoFlag(flag.Lookup("kubeconfig"))
|
|
|
|
hostF := scanCmd.PersistentFlags().VarPF(&scanInfo.HostSensorEnabled, "enable-host-scan", "", "Deploy Kubescape host-sensor daemonset in the scanned cluster. Deleting it right after we collecting the data. Required to collect valuable data from cluster nodes for certain controls. Yaml file: https://github.com/kubescape/kubescape/blob/master/core/pkg/hostsensorutils/hostsensor.yaml")
|
|
hostF.NoOptDefVal = "true"
|
|
hostF.DefValue = "false, for no TTY in stdin"
|
|
scanCmd.PersistentFlags().MarkHidden("enable-host-scan")
|
|
scanCmd.PersistentFlags().MarkDeprecated("enable-host-scan", "To activate the host scanner capability, proceed with the installation of the kubescape operator chart found here: https://github.com/kubescape/helm-charts/tree/main/charts/kubescape-operator. The flag will be removed at 1.Dec.2023")
|
|
|
|
scanCmd.PersistentFlags().MarkHidden("host-scan-yaml") // this flag should be used very cautiously. We prefer users will not use it at all unless the DaemonSet can not run pods on the nodes
|
|
scanCmd.PersistentFlags().MarkDeprecated("host-scan-yaml", "To activate the host scanner capability, proceed with the installation of the kubescape operator chart found here: https://github.com/kubescape/helm-charts/tree/main/charts/kubescape-operator. The flag will be removed at 1.Dec.2023")
|
|
|
|
scanCmd.AddCommand(getControlCmd(ks, &scanInfo))
|
|
scanCmd.AddCommand(getFrameworkCmd(ks, &scanInfo))
|
|
scanCmd.AddCommand(getWorkloadCmd(ks, &scanInfo))
|
|
|
|
scanCmd.AddCommand(getImageCmd(ks, &scanInfo))
|
|
|
|
return scanCmd
|
|
}
|
|
|
|
func setSecurityViewScanInfo(args []string, scanInfo *cautils.ScanInfo) {
|
|
if len(args) > 0 {
|
|
scanInfo.SetScanType(cautils.ScanTypeRepo)
|
|
scanInfo.InputPatterns = args
|
|
scanInfo.SetPolicyIdentifiers([]string{"workloadscan", "allcontrols"}, v1.KindFramework)
|
|
} else {
|
|
scanInfo.SetScanType(cautils.ScanTypeCluster)
|
|
scanInfo.SetPolicyIdentifiers([]string{"clusterscan", "mitre", "nsa"}, v1.KindFramework)
|
|
}
|
|
}
|
|
|
|
func securityScan(scanInfo cautils.ScanInfo, ks meta.IKubescape) error {
|
|
results, err := ks.Scan(&scanInfo)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = results.HandleResults(ks.Context(), &scanInfo); err != nil {
|
|
return err
|
|
}
|
|
|
|
enforceSeverityThresholds(results.GetData().Report.SummaryDetails.GetResourcesSeverityCounters(), &scanInfo, terminateOnExceedingSeverity)
|
|
|
|
return nil
|
|
}
|