host sensor flag + user input asking

This commit is contained in:
Bezalel Brandwine
2021-11-28 12:35:21 +02:00
parent aefc5fded7
commit 05305d858b
4 changed files with 112 additions and 31 deletions
+50 -16
View File
@@ -1,31 +1,65 @@
package cautils
import (
"fmt"
"path/filepath"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/opa-utils/reporthandling"
)
type BoolPtrFlag struct {
valPtr *bool
}
func (bpf *BoolPtrFlag) Type() string {
return "bool"
}
func (bpf *BoolPtrFlag) String() string {
if bpf.valPtr != nil {
return fmt.Sprintf("%v", *bpf.valPtr)
}
return ""
}
func (bpf *BoolPtrFlag) Get() *bool {
return bpf.valPtr
}
func (bpf *BoolPtrFlag) SetBool(val bool) {
bpf.valPtr = &val
}
func (bpf *BoolPtrFlag) Set(val string) error {
switch val {
case "true":
bpf.SetBool(true)
case "false":
bpf.SetBool(false)
}
return nil
}
type ScanInfo struct {
Getters
PolicyIdentifier []reporthandling.PolicyIdentifier
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
Format string // Format results (table, json, junit ...)
Output string // Store results in an output file, Output file name
ExcludedNamespaces string // DEPRECATED?
IncludeNamespaces string // DEPRECATED?
InputPatterns []string // Yaml files input patterns
Silent bool // Silent mode - Do not print progress logs
FailThreshold uint16 // Failure score threshold
Submit bool // Submit results to Armo BE
Local bool // Do not submit results
Account string // account ID
FrameworkScan bool // false if scanning control
ScanAll bool // true if scan all frameworks
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
Format string // Format results (table, json, junit ...)
Output string // Store results in an output file, Output file name
ExcludedNamespaces string // used for host sensor namespace
IncludeNamespaces string // DEPRECATED?
InputPatterns []string // Yaml files input patterns
Silent bool // Silent mode - Do not print progress logs
FailThreshold uint16 // Failure score threshold
Submit bool // Submit results to Armo BE
HostSensor BoolPtrFlag // Deploy ARMO K8s host sensor to collect data from certain controls
Local bool // Do not submit results
Account string // account ID
FrameworkScan bool // false if scanning control
ScanAll bool // true if scan all frameworks
}
type Getters struct {
+4
View File
@@ -48,4 +48,8 @@ func init() {
scanCmd.PersistentFlags().BoolVar(&scanInfo.UseDefault, "use-default", false, "Load local policy object from default path. If not used will download latest")
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.ControlsInputs, "controls-config", "", "Path to an controls-config obj. If not set will download controls-config from ARMO management portal")
hostF := scanCmd.PersistentFlags().VarPF(&scanInfo.HostSensor, "enable-host-scan", "", "Deploy ARMO K8s host-sensor daemonset in the scanned cluster. Deleting it right after we collecting the data. Required to collect valueable data from cluster nodes for certain controls")
hostF.NoOptDefVal = "true"
hostF.DefValue = "false, for no TTY in stdin"
}
+51 -15
View File
@@ -2,6 +2,7 @@ package clihandler
import (
"fmt"
"io/fs"
"os"
"github.com/armosec/armoapi-go/armotypes"
@@ -27,6 +28,38 @@ type componentInterfaces struct {
printerHandler printer.IPrinter
}
func initHostSensor(scanInfo *cautils.ScanInfo, k8s *k8sinterface.KubernetesApi) {
hasHostSensorControls := true
// we need to determined which controls needs host sensor
if scanInfo.HostSensor.Get() == nil && hasHostSensorControls {
scanInfo.HostSensor.SetBool(askUserForHostSensor())
}
if hostSensorVal := scanInfo.HostSensor.Get(); hostSensorVal != nil && *hostSensorVal {
hostSensorHandler, err := hostsensorutils.NewHostSensorHandler(k8s)
if hostSensorHandler != nil {
defer func(hostSensorHandler *hostsensorutils.HostSensorHandler) {
if err := hostSensorHandler.TearDown(); err != nil {
glog.Errorf("failed to tear down host sensor: %v", err)
}
}(hostSensorHandler)
}
if err != nil {
glog.Errorf("failed to deploy host sensor: %v", err)
return
}
scanInfo.ExcludedNamespaces = fmt.Sprintf("%s,%s", scanInfo.ExcludedNamespaces, hostSensorHandler.DaemonSet.Namespace)
data, err := hostSensorHandler.GetKubeletConfigurations()
if err != nil {
glog.Errorf("failed to get kubelet configuration from host sensor: %v", err)
} else {
glog.Infof("kubelet configurations from host sensor: %v", data)
}
} else {
fmt.Printf("Skipping nodes scanning\n")
}
}
func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
var resourceHandler resourcehandler.IResourceHandler
var clusterConfig cautils.IClusterConfig
@@ -45,21 +78,7 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
scanningTarget = "yaml"
} else {
k8s := k8sinterface.NewKubernetesApi()
hostSensorHandler, err := hostsensorutils.NewHostSensorHandler(k8s)
if err != nil {
glog.Errorf("failed to deploy host sensor: %v", err)
}
data, err := hostSensorHandler.GetKubeletConfigurations()
if err != nil {
glog.Errorf("failed to get kubelet configuration from host sensor: %v", err)
} else {
glog.Infof("kubelet configurations from host sensor: %v", data)
}
if hostSensorHandler != nil {
if err := hostSensorHandler.TearDown(); err != nil {
glog.Errorf("failed to tear down host sensor: %v", err)
}
}
initHostSensor(scanInfo, k8s)
resourceHandler = resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo))
clusterConfig = cautils.ClusterConfigSetup(scanInfo, k8s, getter.GetArmoAPIConnector())
@@ -189,3 +208,20 @@ func Submit(submitInterfaces cliinterfaces.SubmitInterfaces) error {
return nil
}
func askUserForHostSensor() bool {
if ssss, err := os.Stdin.Stat(); err == nil {
// fmt.Printf("Found stdin type: %s\n", ssss.Mode().Type())
if ssss.Mode().Type()&(fs.ModeDevice|fs.ModeCharDevice) > 0 { //has TTY
fmt.Printf("Would you like to scan K8s nodes? [y/N]. This is required to collect valuable data for certain controls\n")
fmt.Printf("Use --enable-host-scan flag to suppress this message\n")
var b []byte = make([]byte, 1)
if n, err := os.Stdin.Read(b); err == nil {
if n > 0 && len(b) > 0 && (b[0] == 'y' || b[0] == 'Y') {
return true
}
}
}
}
return false
}
+7
View File
@@ -37,6 +37,13 @@ func NewHostSensorHandler(k8sObj *k8sinterface.KubernetesApi) (*HostSensorHandle
if k8sObj == nil {
return nil, fmt.Errorf("nil k8s interface received")
}
// Don't deploy on cluster with no nodes. Some cloud providers prevents termination of K8s objects for cluster with no nodes!!!
if nodeList, err := k8sObj.KubernetesClient.NodeV1().RuntimeClasses().List(k8sObj.Context, metav1.ListOptions{}); err != nil || len(nodeList.Items) == 0 {
if err == nil {
err = fmt.Errorf("no nodes to scan")
}
return nil, fmt.Errorf("in NewHostSensorHandler, failed to get nodes list: %v", err)
}
hsh := &HostSensorHandler{
k8sObj: k8sObj,
HostSensorPodNames: map[string]string{},