Merge pull request #294 from armosec/dev

Minor features and improvements
This commit is contained in:
Rotem Refael
2022-01-04 15:29:55 +02:00
committed by GitHub
21 changed files with 308 additions and 94 deletions
+58 -35
View File
@@ -14,10 +14,7 @@ import (
corev1 "k8s.io/api/core/v1"
)
const (
configMapName = "kubescape"
configFileName = "config"
)
const configFileName = "config"
func ConfigFileFullPath() string { return getter.GetDefaultPath(configFileName + ".json") }
@@ -145,26 +142,38 @@ func getTenantConfigFromBE(backendAPI getter.IBackend, configObj *ConfigObj) err
// ========================== Cluster Config ============================================
// ======================================================================================
// ClusterConfig configuration of specific cluster
/*
Supported environments variables:
KS_DEFAULT_CONFIGMAP_NAME // name of configmap, if not set default is 'kubescape'
KS_DEFAULT_CONFIGMAP_NAMESPACE // configmap namespace, if not set default is 'default'
TODO - supprot:
KS_ACCOUNT // Account ID
KS_CACHE // path to cached files
*/
type ClusterConfig struct {
k8s *k8sinterface.KubernetesApi
defaultNS string
backendAPI getter.IBackend
configObj *ConfigObj
k8s *k8sinterface.KubernetesApi
configMapName string
configMapNamespace string
backendAPI getter.IBackend
configObj *ConfigObj
}
func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBackend, customerGUID string) *ClusterConfig {
defaultNS := k8sinterface.GetDefaultNamespace()
var configObj *ConfigObj
c := &ClusterConfig{
k8s: k8s,
backendAPI: backendAPI,
configObj: &ConfigObj{},
defaultNS: defaultNS,
k8s: k8s,
backendAPI: backendAPI,
configObj: &ConfigObj{},
configMapName: getConfigMapName(),
configMapNamespace: getConfigMapNamespace(),
}
// get from configMap
if existsConfigMap(k8s, defaultNS) {
configObj, _ = loadConfigFromConfigMap(k8s, defaultNS)
if c.existsConfigMap() {
configObj, _ = c.loadConfigFromConfigMap()
} else if existsConfigFile() { // get from file
configObj, _ = loadConfigFromFile()
}
@@ -189,10 +198,10 @@ func NewClusterConfig(k8s *k8sinterface.KubernetesApi, backendAPI getter.IBacken
}
func (c *ClusterConfig) GetConfigObj() *ConfigObj { return c.configObj }
func (c *ClusterConfig) GetDefaultNS() string { return c.defaultNS }
func (c *ClusterConfig) GetDefaultNS() string { return c.configMapNamespace }
func (c *ClusterConfig) GetCustomerGUID() string { return c.configObj.CustomerGUID }
func (c *ClusterConfig) IsConfigFound() bool {
return existsConfigFile() || existsConfigMap(c.k8s, c.defaultNS)
return existsConfigFile() || c.existsConfigMap()
}
func (c *ClusterConfig) SetTenant() error {
@@ -202,7 +211,7 @@ func (c *ClusterConfig) SetTenant() error {
return err
}
// update/create config
if existsConfigMap(c.k8s, c.defaultNS) {
if c.existsConfigMap() {
c.updateConfigMap()
} else {
c.createConfigMap()
@@ -223,8 +232,8 @@ func (c *ClusterConfig) ToMapString() map[string]interface{} {
}
return m
}
func loadConfigFromConfigMap(k8s *k8sinterface.KubernetesApi, ns string) (*ConfigObj, error) {
configMap, err := k8s.KubernetesClient.CoreV1().ConfigMaps(ns).Get(context.Background(), configMapName, metav1.GetOptions{})
func (c *ClusterConfig) loadConfigFromConfigMap() (*ConfigObj, error) {
configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Get(context.Background(), c.configMapName, metav1.GetOptions{})
if err != nil {
return nil, err
}
@@ -235,15 +244,15 @@ func loadConfigFromConfigMap(k8s *k8sinterface.KubernetesApi, ns string) (*Confi
return nil, nil
}
func existsConfigMap(k8s *k8sinterface.KubernetesApi, ns string) bool {
_, err := k8s.KubernetesClient.CoreV1().ConfigMaps(ns).Get(context.Background(), configMapName, metav1.GetOptions{})
func (c *ClusterConfig) existsConfigMap() bool {
_, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Get(context.Background(), c.configMapName, metav1.GetOptions{})
// TODO - check if has customerGUID
return err == nil
}
func (c *ClusterConfig) GetValueByKeyFromConfigMap(key string) (string, error) {
configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Get(context.Background(), configMapName, metav1.GetOptions{})
configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Get(context.Background(), c.configMapName, metav1.GetOptions{})
if err != nil {
return "", err
@@ -295,11 +304,11 @@ func SetKeyValueInConfigJson(key string, value string) error {
func (c *ClusterConfig) SetKeyValueInConfigmap(key string, value string) error {
configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Get(context.Background(), configMapName, metav1.GetOptions{})
configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Get(context.Background(), c.configMapName, metav1.GetOptions{})
if err != nil {
configMap = &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: configMapName,
Name: c.configMapName,
},
}
}
@@ -311,9 +320,9 @@ func (c *ClusterConfig) SetKeyValueInConfigmap(key string, value string) error {
configMap.Data[key] = value
if err != nil {
_, err = c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Create(context.Background(), configMap, metav1.CreateOptions{})
_, err = c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Create(context.Background(), configMap, metav1.CreateOptions{})
} else {
_, err = c.k8s.KubernetesClient.CoreV1().ConfigMaps(configMap.Namespace).Update(context.Background(), configMap, metav1.UpdateOptions{})
_, err = c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Update(context.Background(), configMap, metav1.UpdateOptions{})
}
return err
@@ -330,12 +339,12 @@ func (c *ClusterConfig) createConfigMap() error {
}
configMap := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: configMapName,
Name: c.configMapName,
},
}
c.updateConfigData(configMap)
_, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Create(context.Background(), configMap, metav1.CreateOptions{})
_, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Create(context.Background(), configMap, metav1.CreateOptions{})
return err
}
@@ -343,7 +352,7 @@ func (c *ClusterConfig) updateConfigMap() error {
if c.k8s == nil {
return nil
}
configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Get(context.Background(), configMapName, metav1.GetOptions{})
configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Get(context.Background(), c.configMapName, metav1.GetOptions{})
if err != nil {
return err
@@ -351,7 +360,7 @@ func (c *ClusterConfig) updateConfigMap() error {
c.updateConfigData(configMap)
_, err = c.k8s.KubernetesClient.CoreV1().ConfigMaps(configMap.Namespace).Update(context.Background(), configMap, metav1.UpdateOptions{})
_, err = c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.configMapNamespace).Update(context.Background(), configMap, metav1.UpdateOptions{})
return err
}
@@ -394,7 +403,7 @@ func readConfig(dat []byte) (*ConfigObj, error) {
// Check if the customer is submitted
func (clusterConfig *ClusterConfig) IsSubmitted() bool {
return existsConfigMap(clusterConfig.k8s, clusterConfig.defaultNS) || existsConfigFile()
return clusterConfig.existsConfigMap() || existsConfigFile()
}
// Check if the customer is registered
@@ -411,7 +420,7 @@ func (clusterConfig *ClusterConfig) IsRegistered() bool {
}
func (clusterConfig *ClusterConfig) DeleteConfig() error {
if err := DeleteConfigMap(clusterConfig.k8s); err != nil {
if err := clusterConfig.DeleteConfigMap(); err != nil {
return err
}
if err := DeleteConfigFile(); err != nil {
@@ -419,8 +428,8 @@ func (clusterConfig *ClusterConfig) DeleteConfig() error {
}
return nil
}
func DeleteConfigMap(k8s *k8sinterface.KubernetesApi) error {
return k8s.KubernetesClient.CoreV1().ConfigMaps(k8sinterface.GetDefaultNamespace()).Delete(context.Background(), configMapName, metav1.DeleteOptions{})
func (clusterConfig *ClusterConfig) DeleteConfigMap() error {
return clusterConfig.k8s.KubernetesClient.CoreV1().ConfigMaps(clusterConfig.configMapNamespace).Delete(context.Background(), clusterConfig.configMapName, metav1.DeleteOptions{})
}
func DeleteConfigFile() error {
@@ -430,3 +439,17 @@ func DeleteConfigFile() error {
func AdoptClusterName(clusterName string) string {
return strings.ReplaceAll(clusterName, "/", "-")
}
func getConfigMapName() string {
if n := os.Getenv("KS_DEFAULT_CONFIGMAP_NAME"); n != "" {
return n
}
return "kubescape"
}
func getConfigMapNamespace() string {
if n := os.Getenv("KS_DEFAULT_CONFIGMAP_NAMESPACE"); n != "" {
return n
}
return "default"
}
+5 -5
View File
@@ -35,15 +35,15 @@ func ScanStartDisplay() {
if IsSilent() {
return
}
InfoDisplay(os.Stdout, "ARMO security scanner starting\n")
InfoDisplay(os.Stderr, "ARMO security scanner starting\n")
}
func SuccessTextDisplay(str string) {
if IsSilent() {
return
}
SuccessDisplay(os.Stdout, "[success] ")
SimpleDisplay(os.Stdout, fmt.Sprintf("%s\n", str))
SuccessDisplay(os.Stderr, "[success] ")
SimpleDisplay(os.Stderr, fmt.Sprintf("%s\n", str))
}
@@ -60,8 +60,8 @@ func ProgressTextDisplay(str string) {
if IsSilent() {
return
}
InfoDisplay(os.Stdout, "[progress] ")
SimpleDisplay(os.Stdout, fmt.Sprintf("%s\n", str))
InfoDisplay(os.Stderr, "[progress] ")
SimpleDisplay(os.Stderr, fmt.Sprintf("%s\n", str))
}
func StartSpinner() {
+1 -1
View File
@@ -100,7 +100,7 @@ func (armoAPI *ArmoAPI) GetReportReceiverURL() string {
func (armoAPI *ArmoAPI) GetFramework(name string) (*reporthandling.Framework, error) {
respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getFrameworkURL(name), nil)
if err != nil {
return nil, err
return nil, nil
}
framework := &reporthandling.Framework{}
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"strings"
)
var NativeFrameworks = []string{"nsa", "mitre", "armobest"}
var NativeFrameworks = []string{"nsa", "mitre", "armobest", "devopsbest"}
func (armoAPI *ArmoAPI) getFrameworkURL(frameworkName string) string {
u := url.URL{}
+12
View File
@@ -41,7 +41,19 @@ func (drp *DownloadReleasedPolicy) GetFramework(name string) (*reporthandling.Fr
return framework, err
}
func (drp *DownloadReleasedPolicy) GetControlsInputs(customerGUID, clusterName string) (map[string][]string, error) {
defaultConfigInputs, err := drp.gs.GetDefaultConfigInputs()
if err != nil {
return nil, err
}
return defaultConfigInputs.Settings.PostureControlInputs, err
}
func (drp *DownloadReleasedPolicy) SetRegoObjects() error {
fwNames, err := drp.gs.GetOPAFrameworksNamesList()
if len(fwNames) != 0 && err == nil {
return nil
}
return drp.gs.SetRegoObjects()
}
-10
View File
@@ -43,16 +43,6 @@ func (rbacObjects *RBACObjects) ListAllResources() (map[string]workloadinterface
func (rbacObjects *RBACObjects) rbacObjectsToResources(resources *rbacutils.RbacObjects) (map[string]workloadinterface.IMetadata, error) {
allresources := map[string]workloadinterface.IMetadata{}
// wrap rbac aggregated objects in IMetadata and add to allresources
rbacIMeta, err := rbacutils.RbacObjectIMetadataWrapper(resources.Rbac)
if err != nil {
return nil, err
}
allresources[rbacIMeta.GetID()] = rbacIMeta
rbacTableIMeta, err := rbacutils.RbacTableObjectIMetadataWrapper(resources.RbacT)
if err != nil {
return nil, err
}
allresources[rbacTableIMeta.GetID()] = rbacTableIMeta
SA2WLIDmapIMeta, err := rbacutils.SA2WLIDmapIMetadataWrapper(resources.SA2WLIDmap)
if err != nil {
return nil, err
-9
View File
@@ -77,7 +77,6 @@ type Getters struct {
func (scanInfo *ScanInfo) Init() {
scanInfo.setUseFrom()
scanInfo.setUseExceptions()
scanInfo.setAccountConfig()
scanInfo.setOutputFile()
}
@@ -91,14 +90,6 @@ func (scanInfo *ScanInfo) setUseExceptions() {
}
}
func (scanInfo *ScanInfo) setAccountConfig() {
if scanInfo.ControlsInputs != "" {
// load account config from file
scanInfo.ControlsInputsGetter = getter.NewLoadPolicy([]string{scanInfo.ControlsInputs})
} else {
scanInfo.ControlsInputsGetter = getter.GetArmoAPIConnector()
}
}
func (scanInfo *ScanInfo) setUseFrom() {
if scanInfo.UseDefault {
for _, policy := range scanInfo.PolicyIdentifier {
+1 -1
View File
@@ -22,7 +22,7 @@ type IVersionCheckHandler interface {
func NewIVersionCheckHandler() IVersionCheckHandler {
if BuildNumber == "" {
WarningDisplay(os.Stdout, "Warning: unknown build number, this might affect your scan results. Please make sure you are updated to latest version.\n")
WarningDisplay(os.Stderr, "Warning: unknown build number, this might affect your scan results. Please make sure you are updated to latest version.\n")
}
if v, ok := os.LookupEnv(SKIP_VERSION_CHECK); ok && pkgutils.StringToBool(v) {
return NewVersionCheckHandlerMock()
+5 -2
View File
@@ -7,6 +7,7 @@ import (
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/clihandler/cliinterfaces"
"github.com/armosec/kubescape/hostsensorutils"
"github.com/armosec/kubescape/opaprocessor"
@@ -85,8 +86,10 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error {
interfaces.report.SetClusterName(interfaces.tenantConfig.GetClusterName())
interfaces.report.SetCustomerGUID(interfaces.tenantConfig.GetCustomerGUID())
downloadReleasedPolicy := getter.NewDownloadReleasedPolicy() // download config inputs from github release
// set policy getter only after setting the customerGUID
setPolicyGetter(scanInfo, interfaces.tenantConfig.GetCustomerGUID())
setPolicyGetter(scanInfo, interfaces.tenantConfig.GetCustomerGUID(), downloadReleasedPolicy)
setConfigInputsGetter(scanInfo, interfaces.tenantConfig.GetCustomerGUID(), downloadReleasedPolicy)
defer func() {
if err := interfaces.hostSensorHandler.TearDown(); err != nil {
@@ -121,7 +124,7 @@ func ScanCliSetup(scanInfo *cautils.ScanInfo) error {
// print report url
interfaces.report.DisplayReportURL()
if score >= float32(scanInfo.FailThreshold) {
if score > float32(scanInfo.FailThreshold) {
return fmt.Errorf("scan risk-score %.2f is above permitted threshold %d", score, scanInfo.FailThreshold)
}
+25 -9
View File
@@ -57,11 +57,12 @@ func getHostSensorHandler(scanInfo *cautils.ScanInfo, k8s *k8sinterface.Kubernet
// we need to determined which controls needs host sensor
if scanInfo.HostSensor.Get() == nil && hasHostSensorControls {
scanInfo.HostSensor.SetBool(askUserForHostSensor())
cautils.WarningDisplay(os.Stderr, "Warning: Kubernetes cluster nodes scanning is disabled. This is required to collect valuable data for certain controls. You can enable it using the --enable-host-scan flag\n")
}
if hostSensorVal := scanInfo.HostSensor.Get(); hostSensorVal != nil && *hostSensorVal {
hostSensorHandler, err := hostsensorutils.NewHostSensorHandler(k8s)
if err != nil || hostSensorHandler == nil {
glog.Errorf("failed to create host sensor: %v", err)
if err != nil {
cautils.WarningDisplay(os.Stderr, fmt.Sprintf("Warning: failed to create host sensor: %v\n", err.Error()))
return &hostsensorutils.HostSensorHandlerMock{}
}
return hostSensorHandler
@@ -136,25 +137,40 @@ func setSubmitBehavior(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantC
}
// setPolicyGetter set the policy getter - local file/github release/ArmoAPI
func setPolicyGetter(scanInfo *cautils.ScanInfo, customerGUID string) {
func setPolicyGetter(scanInfo *cautils.ScanInfo, customerGUID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) {
if len(scanInfo.UseFrom) > 0 {
scanInfo.PolicyGetter = getter.NewLoadPolicy(scanInfo.UseFrom)
} else {
if customerGUID == "" || !scanInfo.FrameworkScan {
setDownloadReleasedPolicy(scanInfo)
setDownloadReleasedPolicy(scanInfo, downloadReleasedPolicy)
} else {
setGetArmoAPIConnector(scanInfo, customerGUID)
}
}
}
func setDownloadReleasedPolicy(scanInfo *cautils.ScanInfo) {
g := getter.NewDownloadReleasedPolicy() // download policy from github release
if err := g.SetRegoObjects(); err != nil { // if failed to pull policy, fallback to cache
cautils.WarningDisplay(os.Stdout, "Warning: failed to get policies from github release, loading policies from cache\n")
// setConfigInputsGetter sets the config input getter - local file/github release/ArmoAPI
func setConfigInputsGetter(scanInfo *cautils.ScanInfo, customerGUID string, downloadReleasedPolicy *getter.DownloadReleasedPolicy) {
if len(scanInfo.ControlsInputs) > 0 {
scanInfo.Getters.ControlsInputsGetter = getter.NewLoadPolicy([]string{scanInfo.ControlsInputs})
} else {
if customerGUID != "" {
scanInfo.Getters.ControlsInputsGetter = getter.GetArmoAPIConnector()
} else {
if err := downloadReleasedPolicy.SetRegoObjects(); err != nil { // if failed to pull config inputs, fallback to BE
cautils.WarningDisplay(os.Stderr, "Warning: failed to get config inputs from github release, this may affect the scanning results\n")
}
scanInfo.Getters.ControlsInputsGetter = downloadReleasedPolicy
}
}
}
func setDownloadReleasedPolicy(scanInfo *cautils.ScanInfo, downloadReleasedPolicy *getter.DownloadReleasedPolicy) {
if err := downloadReleasedPolicy.SetRegoObjects(); err != nil { // if failed to pull policy, fallback to cache
cautils.WarningDisplay(os.Stderr, "Warning: failed to get policies from github release, loading policies from cache\n")
scanInfo.PolicyGetter = getter.NewLoadPolicy(getDefaultFrameworksPaths())
} else {
scanInfo.PolicyGetter = g
scanInfo.PolicyGetter = downloadReleasedPolicy
}
}
func setGetArmoAPIConnector(scanInfo *cautils.ScanInfo, customerGUID string) {
@@ -0,0 +1,117 @@
# Container image vulnerabilty adaptor interface proposal
## Rationale
source #287
### Big picture
* Kubescape team planning to create controls which take into account image vulnerabilities, example: looking for public internet facing workloads with critical vulnerabilities. These are seriously effecting the security health of a cluster and therefore we think it is important to cover it. We think that most container registries are/will support image scanning like Harbor and therefore the ability to get information from them is important.
* There are information in the image repository which is important for existing controls as well. They are incomplete without it, example see this issue: Non-root containers check is broken #19 . These are not necessarily image vulnerability related. Can be information in the image manifest (like the issue before), but it can be the image BOM related.
### Relation to this proposal
There are multiple changes and design decisions needs to be made before Kubescape will support the before outlined controls. However, a focal point the whole picutre is the ability to access vulnerabilty databases of container images. We anticiapte that most container image repositories will support image vulnerabilty scanning, some major players are already do. Since there is no a single API available which all of these data sources support it is important to create an adaption layer within Kubescape so different datasources can serve Kubescape's goals.
## High level design of Kubescape
### Layers
* Controls and Rules: that actual control logic implementation, the "tests" themselves. Implemented in rego
* OPA engine: the [OPA](https://github.com/open-policy-agent/opa) rego interpreter
* Rules processor: Kubescape component, it enumerates and runs the controls while also preparing the all the input data that the controls need for running
* Data sources: set of different modules providing data to the Rules processor so it can run the controls with them. Examples: Kubernetes objects, cloud vendor API objects and adding in this proposal the vulnerability infomration
* Cloud Image Vulnerability adaption interface: the subject of this proposal, it gives a common interface for different registry/vulnerabilty vendors to adapt to.
* CIV adaptors: specific implementation of the CIV interface, example Harbor adaption
```
-----------------------
| Controls/Rules (rego) |
-----------------------
|
-----------------------
| OPA engine |
-----------------------
|
-----------------------
| Rules processor |
-----------------------
|
-----------------------
| Data sources |
-----------------------
|
=======================
| CIV adaption interface| <- Adding this layer in this proposal
=======================
|
-----------------------
| Specific CIV adaptors | <- will be implemented based on this proposal
-----------------------
```
## Functionalities to cover
The interface needs to cover the following functionalities:
* Authentication against the information source (abstracted login)
* Triggering image scan (if applicable, the source might store vulnerabilities for images but cannot scan alone)
* Reading image scan status (with last scan date and etc.)
* Getting vulnerability information for a given image
* Getting image information
* Image manifests
* Image BOMs (bill of material)
## Go API proposal
```
/*type ContainerImageRegistryCredentials struct {
map[string]string
Password string
Tag string
Hash string
}*/
type ContainerImageIdentifier struct {
Registry string
Repository string
Tag string
Hash string
}
type ContainerImageScanStatus struct {
ImageID ContainerImageIdentifier
IsScanAvailable bool
IsBomAvailable bool
LastScanDate time.Time
}
type ContainerImageVulnerability struct {
ImageID ContainerImageIdentifier
// TBD
}
type ContainerImageInformation struct {
ImageID ContainerImageIdentifier
Bom []string
ImageManifest Manifest // will use here Docker package definition
}
type IContainerImageVulnerabilityAdaptor interface {
// Credentials are coming from user input (CLI or configuration file) and they are abstracted at string to string map level
// so and example use would be like registry: "simpledockerregistry:80" and credentials like {"username":"joedoe","password":"abcd1234"}
Login(registry string, credentials map[string]string) error
// For "help" purposes
DescribeAdaptor() string
GetImagesScanStatus(imageIDs []ContainerImageIdentifier) ([]ContainerImageScanStatus, error)
GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerability, error)
GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error)
}
```
+3 -3
View File
@@ -3,10 +3,10 @@ module github.com/armosec/kubescape
go 1.17
require (
github.com/armosec/armoapi-go v0.0.23
github.com/armosec/armoapi-go v0.0.40
github.com/armosec/k8s-interface v0.0.50
github.com/armosec/opa-utils v0.0.75
github.com/armosec/rbac-utils v0.0.9
github.com/armosec/opa-utils v0.0.78
github.com/armosec/rbac-utils v0.0.10
github.com/armosec/utils-go v0.0.3
github.com/briandowns/spinner v1.18.0
github.com/enescakir/emoji v1.0.0
+6 -5
View File
@@ -84,18 +84,19 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/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 h1:jqoLIWM5CR7DCD9fpFgN0ePqtHvOCoZv/XzCwsUluJU=
github.com/armosec/armoapi-go v0.0.23/go.mod h1:iaVVGyc23QGGzAdv4n+szGQg3Rbpixn9yQTU3qWRpaw=
github.com/armosec/armoapi-go v0.0.40 h1:KQRJXFqw95s6cV7HoGgw1x8qrRZ9eNVze//yQbo24Lk=
github.com/armosec/armoapi-go v0.0.40/go.mod h1:iaVVGyc23QGGzAdv4n+szGQg3Rbpixn9yQTU3qWRpaw=
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.50 h1:iLPGI0j85vwKANr9QDAnba4Efjg3DyIJg15jRJdvOnc=
github.com/armosec/k8s-interface v0.0.50/go.mod h1:vHxGWqD/uh6+GQb9Sqv7OGMs+Rvc2dsFVc0XtgRh1ZU=
github.com/armosec/opa-utils v0.0.64/go.mod h1:6tQP8UDq2EvEfSqh8vrUdr/9QVSCG4sJfju1SXQOn4c=
github.com/armosec/opa-utils v0.0.75 h1:GBI3K18xc3WXJHIorIu4bGNAsfMYHUc1x7zueDz2ZbY=
github.com/armosec/opa-utils v0.0.75/go.mod h1:L7d+uiIIXAZ3LEyKtmEIbMcI1hWgWaXGpn5zVCqzwSU=
github.com/armosec/opa-utils v0.0.78 h1:wFkVqJ1vEftn3E1hHdUfKlp5xHgof616ljyKKebuNkI=
github.com/armosec/opa-utils v0.0.78/go.mod h1:ZOXYVTtuyrV4TldcfbzgRqP6F9Drlf4hB0zr210OXgM=
github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40=
github.com/armosec/rbac-utils v0.0.9 h1:rIOWp4K7BELUNX32ktSjVbb8d/0SpH7W76W6Tf+8rzw=
github.com/armosec/rbac-utils v0.0.9/go.mod h1:Ex/IdGWhGv9HZq6Hs8N/ApzCKSIvpNe/ETqDfnuyah0=
github.com/armosec/rbac-utils v0.0.10 h1:bFjesO8+xJS1ryR9vqj4xFEo1cQ0HvClzR+LWHzozW4=
github.com/armosec/rbac-utils v0.0.10/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=
+17 -2
View File
@@ -104,7 +104,22 @@ func (hsh *HostSensorHandler) GetLinuxSecurityHardeningStatus() ([]hostsensor.Ho
// return list of KubeletCommandLine
func (hsh *HostSensorHandler) GetKubeletCommandLine() ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
return hsh.sendAllPodsHTTPGETRequest("/kubeletCommandLine", "KubeletCommandLine")
resps, err := hsh.sendAllPodsHTTPGETRequest("/kubeletCommandLine", "KubeletCommandLine")
if err != nil {
return resps, err
}
for resp := range resps {
var data = make(map[string]interface{})
data["fullCommand"] = string(resps[resp].Data)
resBytesMarshal, err := json.Marshal(data)
// TODO catch error
if err == nil {
resps[resp].Data = json.RawMessage(resBytesMarshal)
}
}
return resps, nil
}
// return list of
@@ -122,7 +137,7 @@ func (hsh *HostSensorHandler) GetOsReleaseFile() ([]hostsensor.HostSensorDataEnv
// return list of
func (hsh *HostSensorHandler) GetKubeletConfigurations() ([]hostsensor.HostSensorDataEnvelope, error) {
// loop over pods and port-forward it to each of them
res, err := hsh.sendAllPodsHTTPGETRequest("/kubeletConfigurations", "KubeletConfigurations") // empty kind, will be overridden
res, err := hsh.sendAllPodsHTTPGETRequest("/kubeletConfigurations", "KubeletConfiguration") // empty kind, will be overridden
for resIdx := range res {
jsonBytes, err := yaml.YAMLToJSON(res[resIdx].Data)
if err != nil {
+26 -1
View File
@@ -41,6 +41,7 @@ func getAllSupportedObjects(k8sResources *cautils.K8SResources, allResources map
func getKubernetesObjects(k8sResources *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata, match []reporthandling.RuleMatchObjects) []workloadinterface.IMetadata {
k8sObjects := []workloadinterface.IMetadata{}
for m := range match {
for _, groups := range match[m].APIGroups {
for _, version := range match[m].APIVersions {
@@ -62,9 +63,33 @@ func getKubernetesObjects(k8sResources *cautils.K8SResources, allResources map[s
}
}
return k8sObjects
return filterOutChildResources(k8sObjects, match)
}
// filterOutChildResources filter out child resources if the parent resource is in the list
func filterOutChildResources(objects []workloadinterface.IMetadata, match []reporthandling.RuleMatchObjects) []workloadinterface.IMetadata {
response := []workloadinterface.IMetadata{}
owners := []string{}
for m := range match {
for i := range match[m].Resources {
owners = append(owners, match[m].Resources[i])
}
}
for i := range objects {
if !k8sinterface.IsTypeWorkload(objects[i].GetObject()) {
response = append(response, objects[i])
continue
}
w := workloadinterface.NewWorkloadObj(objects[i].GetObject())
ownerReferences, err := w.GetOwnerReferences()
if err != nil || len(ownerReferences) == 0 {
response = append(response, w)
} else if !k8sinterface.IsStringInSlice(owners, ownerReferences[0].Kind) {
response = append(response, w)
}
}
return response
}
func getRuleDependencies() (map[string]string, error) {
modules := resources.LoadRegoModules()
if len(modules) == 0 {
+22 -1
View File
@@ -213,7 +213,11 @@ func readYamlFile(yamlFile []byte) ([]workloadinterface.IMetadata, []error) {
}
if obj, ok := j.(map[string]interface{}); ok {
if o := objectsenvelopes.NewObject(obj); o != nil {
yamlObjs = append(yamlObjs, o)
if o.GetKind() == "List" {
yamlObjs = append(yamlObjs, handleListObject(o)...)
} else {
yamlObjs = append(yamlObjs, o)
}
}
} else {
errs = append(errs, fmt.Errorf("failed to convert yaml file to map[string]interface, file content: %v", j))
@@ -303,3 +307,20 @@ func getFileFormat(filePath string) FileFormat {
return FileFormat(filePath)
}
}
// handleListObject handle a List manifest
func handleListObject(obj workloadinterface.IMetadata) []workloadinterface.IMetadata {
yamlObjs := []workloadinterface.IMetadata{}
if i, ok := workloadinterface.InspectMap(obj.GetObject(), "items"); ok && i != nil {
if items, ok := i.([]interface{}); ok && items != nil {
for item := range items {
if m, ok := items[item].(map[string]interface{}); ok && m != nil {
if o := objectsenvelopes.NewObject(m); o != nil {
yamlObjs = append(yamlObjs, o)
}
}
}
}
}
return yamlObjs
}
+4 -4
View File
@@ -61,14 +61,14 @@ func (k8sHandler *K8sResourceHandler) GetResources(frameworks []reporthandling.F
return k8sResourcesMap, allResources, err
}
if err := k8sHandler.collectHostResources(allResources, k8sResourcesMap); err != nil {
return k8sResourcesMap, allResources, err
cautils.WarningDisplay(os.Stderr, "Warning: failed to collect host sensor resources\n")
}
if err := k8sHandler.collectRbacResources(allResources); err != nil {
cautils.WarningDisplay(os.Stdout, "Warning: failed to collect rbac resources\n")
cautils.WarningDisplay(os.Stderr, "Warning: failed to collect rbac resources\n")
}
if err := getCloudProviderDescription(allResources, k8sResourcesMap); err != nil {
cautils.WarningDisplay(os.Stdout, fmt.Sprintf("Warning: %v\n", err.Error()))
cautils.WarningDisplay(os.Stderr, fmt.Sprintf("Warning: %v\n", err.Error()))
}
cautils.StopSpinner()
@@ -105,7 +105,7 @@ func (k8sHandler *K8sResourceHandler) pullResources(k8sResources *cautils.K8SRes
continue
}
// store result as []map[string]interface{}
metaObjs := ConvertMapListToMeta(k8sinterface.ConvertUnstructuredSliceToMap(k8sinterface.FilterOutOwneredResources(result)))
metaObjs := ConvertMapListToMeta(k8sinterface.ConvertUnstructuredSliceToMap(result))
for i := range metaObjs {
allResources[metaObjs[i].GetID()] = metaObjs[i]
}
+1 -1
View File
@@ -21,7 +21,7 @@ func (jsonPrinter *JsonPrinter) SetWriter(outputFile string) {
}
func (jsonPrinter *JsonPrinter) Score(score float32) {
fmt.Printf("\nOverall risk-score (0- Excellent, 100- All failed): %d\n", int(score))
fmt.Fprintf(os.Stderr, "\nOverall risk-score (0- Excellent, 100- All failed): %d\n", int(score))
}
func (jsonPrinter *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
+1 -1
View File
@@ -22,7 +22,7 @@ func (junitPrinter *JunitPrinter) SetWriter(outputFile string) {
}
func (junitPrinter *JunitPrinter) Score(score float32) {
fmt.Printf("\nOverall risk-score (0- Excellent, 100- All failed): %d\n", int(score))
fmt.Fprintf(os.Stderr, "\nOverall risk-score (0- Excellent, 100- All failed): %d\n", int(score))
}
func (junitPrinter *JunitPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
+1 -1
View File
@@ -26,5 +26,5 @@ func (reportMock *ReportMock) SetClusterName(clusterName string) {
func (reportMock *ReportMock) DisplayReportURL() {
message := fmt.Sprintf("\nYou 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: https://%s/cli-signup \n", getter.GetArmoAPIConnector().GetFrontendURL())
cautils.InfoTextDisplay(os.Stdout, fmt.Sprintf("\n%s\n", message))
cautils.InfoTextDisplay(os.Stderr, fmt.Sprintf("\n%s\n", message))
}
@@ -130,7 +130,7 @@ func (report *ReportEventReceiver) DisplayReportURL() {
u.Host = getter.GetArmoAPIConnector().GetFrontendURL()
if report.customerAdminEMail != "" {
cautils.InfoTextDisplay(os.Stdout, fmt.Sprintf("\n\n%s %s/risk/%s\n(Account: %s)\n\n", message, u.String(), report.clusterName, report.customerGUID))
cautils.InfoTextDisplay(os.Stderr, fmt.Sprintf("\n\n%s %s/risk/%s\n(Account: %s)\n\n", message, u.String(), report.clusterName, report.customerGUID))
return
}
u.Path = "account/sign-up"
@@ -139,5 +139,5 @@ func (report *ReportEventReceiver) DisplayReportURL() {
q.Add("customerGUID", report.customerGUID)
u.RawQuery = q.Encode()
cautils.InfoTextDisplay(os.Stdout, fmt.Sprintf("\n\n%s %s\n\n", message, u.String()))
cautils.InfoTextDisplay(os.Stderr, fmt.Sprintf("\n\n%s %s\n\n", message, u.String()))
}