Merge pull request #208 from dwertent/master

Adding smoke testing and support inputs for controls
This commit is contained in:
David Wertenteil
2021-11-01 17:22:31 +02:00
committed by GitHub
25 changed files with 318 additions and 158 deletions
+6
View File
@@ -46,6 +46,12 @@ jobs:
CGO_ENABLED: 0
run: python3 --version && python3 build.py
- name: Smoke Testing
env:
RELEASE: v1.0.${{ github.run_number }}
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape
- name: Upload Release binaries
id: upload-release-asset
uses: actions/upload-release-asset@v1
+6
View File
@@ -30,6 +30,12 @@ jobs:
CGO_ENABLED: 0
run: python3 --version && python3 build.py
- name: Smoke Testing
env:
RELEASE: v1.0.${{ github.run_number }}
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape
- name: Upload build artifacts
uses: actions/upload-artifact@v2
with:
+6 -5
View File
@@ -31,8 +31,9 @@ jobs:
CGO_ENABLED: 0
run: python3 --version && python3 build.py
- name: Upload build artifacts
uses: actions/upload-artifact@v2
with:
name: kubescape-${{ matrix.os }}
path: build/${{ matrix.os }}/kubescape
- name: Smoke Testing
env:
RELEASE: v1.0.${{ github.run_number }}
KUBESCAPE_SKIP_UPDATE_CHECK: "true"
run: python3 smoke_testing/init.py ${PWD}/build/${{ matrix.os }}/kubescape
+1
View File
@@ -2,4 +2,5 @@
*kubescape*
*debug*
*vender*
*.pyc*
.idea
+5
View File
@@ -44,6 +44,11 @@ Want to contribute? Want to discuss something? Have an issue?
# Options and examples
## Tutorials
* [Overview](https://youtu.be/wdBkt_0Qhbg)
* [Scanning Kubernetes YAML files](https://youtu.be/Ox6DaR7_4ZI)
## Install on Windows
**Requires powershell v5.0+**
+2 -11
View File
@@ -60,9 +60,6 @@ def main():
status = subprocess.call(["go", "build", "-o", "%s/%s" % (buildDir, packageName), "-ldflags" ,ldflags])
checkStatus(status, "Failed to build kubescape")
test_cli_prints(buildDir,packageName)
sha1 = hashlib.sha1()
with open(buildDir + "/" + packageName, "rb") as kube:
sha1.update(kube.read())
@@ -70,13 +67,7 @@ def main():
kube_sha.write(sha1.hexdigest())
print("Build Done")
def test_cli_prints(buildDir,packageName):
bin_cli = os.path.abspath(os.path.join(buildDir,packageName))
print(f"testing CLI prints on {bin_cli}")
status = str(subprocess.check_output([bin_cli, "-h"]))
assert "download" in status, "download is missing: " + status
if __name__ == "__main__":
main()
+7
View File
@@ -13,6 +13,7 @@ type OPASessionObj struct {
K8SResources *K8SResources
Exceptions []armotypes.PostureExceptionPolicy
PostureReport *reporthandling.PostureReport
RegoInputData RegoInputData // map[<control name>][<input arguments>]
}
func NewOPASessionObj(frameworks []reporthandling.Framework, k8sResources *K8SResources) *OPASessionObj {
@@ -49,3 +50,9 @@ type Exception struct {
Namespaces []string `json:"namespaces"`
Regex string `json:"regex"` // not supported
}
type RegoInputData struct {
PostureControlInputs map[string][]string `json:"postureControlInputs"`
// ClusterName string `json:"clusterName"`
// K8sConfig RegoK8sConfig `json:"k8sconfig"`
}
+26
View File
@@ -0,0 +1,26 @@
package cautils
import (
"encoding/json"
"github.com/open-policy-agent/opa/storage"
"github.com/open-policy-agent/opa/storage/inmem"
"github.com/open-policy-agent/opa/util"
)
func (data *RegoInputData) SetControlsInputs(controlsInputs map[string][]string) {
data.PostureControlInputs = controlsInputs
}
func (data *RegoInputData) TOStorage() (storage.Store, error) {
var jsonObj map[string]interface{}
bytesData, err := json.Marshal(*data)
if err != nil {
return nil, err
}
// glog.Infof("RegoDependenciesData: %s", bytesData)
if err := util.UnmarshalJSON(bytesData, &jsonObj); err != nil {
return nil, err
}
return inmem.NewFromObject(jsonObj), nil
}
+27
View File
@@ -140,6 +140,33 @@ func (armoAPI *ArmoAPI) GetCustomerGUID(customerGUID string) (*TenantResponse, e
return tenant, nil
}
// ControlsInputs // map[<control name>][<input arguments>]
func (armoAPI *ArmoAPI) GetAccountConfig(customerGUID, clusterName string) (*armotypes.CustomerConfig, error) {
accountConfig := &armotypes.CustomerConfig{}
if customerGUID == "" {
return accountConfig, nil
}
respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getAccountConfig(customerGUID, clusterName))
if err != nil {
return nil, err
}
if err = JSONDecoder(respStr).Decode(&accountConfig); err != nil {
return nil, err
}
return accountConfig, nil
}
// ControlsInputs // map[<control name>][<input arguments>]
func (armoAPI *ArmoAPI) GetControlsInputs(customerGUID, clusterName string) (map[string][]string, error) {
accountConfig, err := armoAPI.GetAccountConfig(customerGUID, clusterName)
if err == nil {
return accountConfig.Settings.PostureControlInputs, nil
}
return nil, err
}
type TenantResponse struct {
TenantID string `json:"tenantId"`
Token string `json:"token"`
+16
View File
@@ -35,6 +35,22 @@ func (armoAPI *ArmoAPI) getExceptionsURL(customerGUID, clusterName string) strin
return u.String()
}
func (armoAPI *ArmoAPI) getAccountConfig(customerGUID, clusterName string) string {
u := url.URL{}
u.Scheme = "https"
u.Host = armoAPI.apiURL
u.Path = "api/v1/armoCustomerConfiguration"
q := u.Query()
q.Add("customerGUID", customerGUID)
if clusterName != "" { // TODO - fix customer name support in Armo BE
q.Add("clusterName", clusterName)
}
u.RawQuery = q.Encode()
return u.String()
}
func (armoAPI *ArmoAPI) getCustomerURL() string {
u := url.URL{}
u.Scheme = "https"
+5 -1
View File
@@ -7,7 +7,7 @@ import (
type IPolicyGetter interface {
GetFramework(name string) (*reporthandling.Framework, error)
GetControl(policyName string) (*reporthandling.Control, error)
GetControl(name string) (*reporthandling.Control, error)
}
type IExceptionsGetter interface {
@@ -16,3 +16,7 @@ type IExceptionsGetter interface {
type IBackend interface {
GetCustomerGUID(customerGUID string) (*TenantResponse, error)
}
type IControlsInputsGetter interface {
GetControlsInputs(customerGUID, clusterName string) (map[string][]string, error)
}
+20 -6
View File
@@ -30,7 +30,7 @@ func NewLoadPolicy(filePaths []string) *LoadPolicy {
func (lp *LoadPolicy) GetControl(controlName string) (*reporthandling.Control, error) {
control := &reporthandling.Control{}
filePath := lp.getFileForControl()
filePath := lp.filePath()
f, err := os.ReadFile(filePath)
if err != nil {
return nil, err
@@ -79,7 +79,7 @@ func (lp *LoadPolicy) GetFramework(frameworkName string) (*reporthandling.Framew
}
func (lp *LoadPolicy) GetExceptions(customerGUID, clusterName string) ([]armotypes.PostureExceptionPolicy, error) {
filePath := lp.getFileForException()
filePath := lp.filePath()
exception := []armotypes.PostureExceptionPolicy{}
f, err := os.ReadFile(filePath)
if err != nil {
@@ -90,10 +90,24 @@ func (lp *LoadPolicy) GetExceptions(customerGUID, clusterName string) ([]armotyp
return exception, err
}
func (lp *LoadPolicy) getFileForException() string {
return lp.filePaths[0]
func (lp *LoadPolicy) GetControlsInputs(customerGUID, clusterName string) (map[string][]string, error) {
filePath := lp.filePath()
accountConfig := &armotypes.CustomerConfig{}
f, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
if err = json.Unmarshal(f, &accountConfig); err == nil {
return accountConfig.Settings.PostureControlInputs, nil
}
return nil, err
}
func (lp *LoadPolicy) getFileForControl() string {
return lp.filePaths[0]
// temporary support for a list of files
func (lp *LoadPolicy) filePath() string {
if len(lp.filePaths) > 0 {
return lp.filePaths[0]
}
return ""
}
+14 -3
View File
@@ -10,7 +10,8 @@ import (
type ScanInfo struct {
Getters
PolicyIdentifier []reporthandling.PolicyIdentifier
UseExceptions string // Load exceptions configuration
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 ...)
@@ -26,13 +27,15 @@ type ScanInfo struct {
}
type Getters struct {
ExceptionsGetter getter.IExceptionsGetter
PolicyGetter getter.IPolicyGetter
ExceptionsGetter getter.IExceptionsGetter
ControlsInputsGetter getter.IControlsInputsGetter
PolicyGetter getter.IPolicyGetter
}
func (scanInfo *ScanInfo) Init() {
scanInfo.setUseFrom()
scanInfo.setUseExceptions()
scanInfo.setAccountConfig()
scanInfo.setOutputFile()
scanInfo.setGetter()
@@ -45,7 +48,15 @@ func (scanInfo *ScanInfo) setUseExceptions() {
} else {
scanInfo.ExceptionsGetter = getter.GetArmoAPIConnector()
}
}
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 {
+2 -1
View File
@@ -42,5 +42,6 @@ func init() {
scanCmd.PersistentFlags().Uint16VarP(&scanInfo.FailThreshold, "fail-threshold", "t", 0, "Failure threshold is the percent bellow which the command fails and returns exit code 1")
scanCmd.PersistentFlags().StringSliceVar(&scanInfo.UseFrom, "use-from", nil, "Load local policy object from specified path. If not used will download latest")
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.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")
}
+2 -1
View File
@@ -5,7 +5,7 @@ go 1.17
require (
github.com/armosec/armoapi-go v0.0.8
github.com/armosec/k8s-interface v0.0.8
github.com/armosec/opa-utils v0.0.18
github.com/armosec/opa-utils v0.0.21
github.com/armosec/utils-go v0.0.3
github.com/briandowns/spinner v1.16.0
github.com/enescakir/emoji v1.0.0
@@ -32,6 +32,7 @@ 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/rbac-utils v0.0.1 // indirect
github.com/armosec/utils-k8s-go v0.0.1 // indirect
github.com/coreos/go-oidc v2.2.1+incompatible // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
+5 -3
View File
@@ -87,11 +87,12 @@ github.com/armosec/armoapi-go v0.0.2/go.mod h1:vIK17yoKbJRQyZXWWLe3AqfqCRITxW8qm
github.com/armosec/armoapi-go v0.0.7/go.mod h1:iaVVGyc23QGGzAdv4n+szGQg3Rbpixn9yQTU3qWRpaw=
github.com/armosec/armoapi-go v0.0.8 h1:JPa9rZynuE2RucamDh6dsy/sjCScmWDsyt1zagJFCDo=
github.com/armosec/armoapi-go v0.0.8/go.mod h1:iaVVGyc23QGGzAdv4n+szGQg3Rbpixn9yQTU3qWRpaw=
github.com/armosec/k8s-interface v0.0.5/go.mod h1:xxS+V5QT3gVQTwZyAMMDrYLWGrfKOpiJ7Jfhfa0w9sM=
github.com/armosec/k8s-interface v0.0.8 h1:Eo3Qen4yFXxzVem49FNeij2ckyzHSAJ0w6PZMaSEIm8=
github.com/armosec/k8s-interface v0.0.8/go.mod h1:xxS+V5QT3gVQTwZyAMMDrYLWGrfKOpiJ7Jfhfa0w9sM=
github.com/armosec/opa-utils v0.0.18 h1:1hL5v2KCD8yStuwzul+gq1zg9+RCV9N3kHoRepKnrg0=
github.com/armosec/opa-utils v0.0.18/go.mod h1:E0mFTVx+4BYAVvO2hxWnIniv/IZIogRCak8BkKd7KK4=
github.com/armosec/opa-utils v0.0.21 h1:LzQ4LoY+fKQIhyLdR7cI/YfjdCztLdAeP40Ct3Wcw5o=
github.com/armosec/opa-utils v0.0.21/go.mod h1:JaE2a0kB2O22JZCqBBfOS9Pvh9rXs9xXXb9lVo01rTs=
github.com/armosec/rbac-utils v0.0.1 h1:N2MI98F/0zbDjmRZ29CNElU1AXkFLk5csd/qAHOBdXY=
github.com/armosec/rbac-utils v0.0.1/go.mod h1:pQ8CBiij8kSKV7aeZm9FMvtZN28VgA7LZcYyTWimq40=
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=
@@ -99,6 +100,7 @@ github.com/armosec/utils-k8s-go v0.0.1 h1:Ay3y7fW+4+FjVc0+obOWm8YsnEvM31vPAVoKTy
github.com/armosec/utils-k8s-go v0.0.1/go.mod h1:qrU4pmY2iZsOb39Eltpm0sTTNM3E4pmeyWx4dgDUC2U=
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/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
+20 -20
View File
@@ -15,42 +15,37 @@ import (
"github.com/golang/glog"
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/rego"
"github.com/open-policy-agent/opa/storage"
uuid "github.com/satori/go.uuid"
)
const ScoreConfigPath = "/resources/config"
var RegoK8sCredentials storage.Store
type OPAProcessorHandler struct {
processedPolicy *chan *cautils.OPASessionObj
reportResults *chan *cautils.OPASessionObj
// componentConfig cautils.ComponentConfig
processedPolicy *chan *cautils.OPASessionObj
reportResults *chan *cautils.OPASessionObj
regoDependenciesData *resources.RegoDependenciesData
}
type OPAProcessor struct {
*cautils.OPASessionObj
regoDependenciesData *resources.RegoDependenciesData
}
func NewOPAProcessor(sessionObj *cautils.OPASessionObj) *OPAProcessor {
func NewOPAProcessor(sessionObj *cautils.OPASessionObj, regoDependenciesData *resources.RegoDependenciesData) *OPAProcessor {
if regoDependenciesData != nil && sessionObj != nil {
regoDependenciesData.PostureControlInputs = sessionObj.RegoInputData.PostureControlInputs
}
return &OPAProcessor{
OPASessionObj: sessionObj,
OPASessionObj: sessionObj,
regoDependenciesData: regoDependenciesData,
}
}
func NewOPAProcessorHandler(processedPolicy, reportResults *chan *cautils.OPASessionObj) *OPAProcessorHandler {
regoDependenciesData := resources.NewRegoDependenciesData(k8sinterface.GetK8sConfig(), cautils.ClusterName)
store, err := regoDependenciesData.TOStorage()
if err != nil {
panic(err)
}
RegoK8sCredentials = store
return &OPAProcessorHandler{
processedPolicy: processedPolicy,
reportResults: reportResults,
processedPolicy: processedPolicy,
reportResults: reportResults,
regoDependenciesData: resources.NewRegoDependenciesData(k8sinterface.GetK8sConfig(), cautils.ClusterName),
}
}
@@ -58,7 +53,7 @@ func (opaHandler *OPAProcessorHandler) ProcessRulesListenner() {
for {
opaSessionObj := <-*opaHandler.processedPolicy
opap := NewOPAProcessor(opaSessionObj)
opap := NewOPAProcessor(opaSessionObj, opaHandler.regoDependenciesData)
// process
if err := opap.Process(); err != nil {
@@ -203,11 +198,16 @@ func (opap *OPAProcessor) runRegoOnK8s(rule *reporthandling.PolicyRule, k8sObjec
}
func (opap *OPAProcessor) regoEval(inputObj []map[string]interface{}, compiledRego *ast.Compiler) ([]reporthandling.RuleResponse, error) {
store, err := opap.regoDependenciesData.TOStorage() // get store
if err != nil {
return nil, err
}
rego := rego.New(
rego.Query("data.armo_builtins"), // get package name from rule
rego.Compiler(compiledRego),
rego.Input(inputObj),
rego.Store(RegoK8sCredentials),
rego.Store(store),
)
// Run evaluation
+2 -1
View File
@@ -5,6 +5,7 @@ import (
"github.com/armosec/kubescape/cautils"
"github.com/armosec/opa-utils/reporthandling"
"github.com/armosec/opa-utils/resources"
"github.com/armosec/k8s-interface/k8sinterface"
// _ "k8s.io/client-go/plugin/pkg/client/auth"
@@ -24,7 +25,7 @@ func TestProcess(t *testing.T) {
opaSessionObj.Frameworks = []reporthandling.Framework{*reporthandling.MockFrameworkA()}
opaSessionObj.K8SResources = &k8sResources
opap := NewOPAProcessor(opaSessionObj)
opap := NewOPAProcessor(opaSessionObj, resources.NewRegoDependenciesDataMock())
opap.Process()
opap.updateResults()
for _, f := range opap.PostureReport.FrameworkReports {
+1 -28
View File
@@ -6,8 +6,6 @@ import (
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/resourcehandler"
"github.com/armosec/opa-utils/reporthandling"
"github.com/armosec/armoapi-go/armotypes"
)
// PolicyHandler -
@@ -33,15 +31,9 @@ func (policyHandler *PolicyHandler) HandleNotificationRequest(notification *repo
policyHandler.getters = &scanInfo.Getters
// get policies
frameworks, exceptions, err := policyHandler.getPolicies(notification)
if err != nil {
if err := policyHandler.getPolicies(notification, opaSessionObj); err != nil {
return err
}
if len(frameworks) == 0 {
return fmt.Errorf("empty list of frameworks")
}
opaSessionObj.Frameworks = frameworks
opaSessionObj.Exceptions = exceptions
k8sResources, err := policyHandler.getResources(notification, opaSessionObj, scanInfo)
if err != nil {
@@ -57,25 +49,6 @@ func (policyHandler *PolicyHandler) HandleNotificationRequest(notification *repo
return nil
}
func (policyHandler *PolicyHandler) getPolicies(notification *reporthandling.PolicyNotification) ([]reporthandling.Framework, []armotypes.PostureExceptionPolicy, error) {
cautils.ProgressTextDisplay("Downloading/Loading policy definitions")
frameworks, exceptions, err := policyHandler.GetPoliciesFromBackend(notification)
if err != nil {
return frameworks, exceptions, err
}
if len(frameworks) == 0 {
err := fmt.Errorf("could not download any policies, please check previous logs")
return frameworks, exceptions, err
}
//if notification.Rules
cautils.SuccessTextDisplay("Downloaded/Loaded policy")
return frameworks, exceptions, nil
}
func (policyHandler *PolicyHandler) getResources(notification *reporthandling.PolicyNotification, opaSessionObj *cautils.OPASessionObj, scanInfo *cautils.ScanInfo) (*cautils.K8SResources, error) {
opaSessionObj.PostureReport.ClusterAPIServerInfo = policyHandler.resourceHandler.GetClusterAPIServerInfo()
+46 -78
View File
@@ -2,103 +2,71 @@ package policyhandler
import (
"fmt"
"strings"
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/opa-utils/reporthandling"
)
func (policyHandler *PolicyHandler) GetPoliciesFromBackend(notification *reporthandling.PolicyNotification) ([]reporthandling.Framework, []armotypes.PostureExceptionPolicy, error) {
var errs error
frameworks := []reporthandling.Framework{}
exceptionPolicies := []armotypes.PostureExceptionPolicy{}
// Get - cacli opa get
rule := GetScanKind(notification)
func (policyHandler *PolicyHandler) getPolicies(notification *reporthandling.PolicyNotification, policiesAndResources *cautils.OPASessionObj) error {
cautils.ProgressTextDisplay("Downloading/Loading policy definitions")
switch rule.Kind {
case reporthandling.KindFramework:
frameworks, err := policyHandler.getScanPolicies(notification)
if err != nil {
return err
}
if len(frameworks) == 0 {
return fmt.Errorf("failed to download policies, please ARMO team for more information")
}
policiesAndResources.Frameworks = frameworks
// get exceptions
exceptionPolicies, err := policyHandler.getters.ExceptionsGetter.GetExceptions(cautils.CustomerGUID, cautils.ClusterName)
if err == nil {
policiesAndResources.Exceptions = exceptionPolicies
}
// get account configuration
controlsInputs, err := policyHandler.getters.ControlsInputsGetter.GetControlsInputs(cautils.CustomerGUID, cautils.ClusterName)
if err == nil {
policiesAndResources.RegoInputData.PostureControlInputs = controlsInputs
}
cautils.SuccessTextDisplay("Downloaded/Loaded policy")
return nil
}
func (policyHandler *PolicyHandler) getScanPolicies(notification *reporthandling.PolicyNotification) ([]reporthandling.Framework, error) {
frameworks := []reporthandling.Framework{}
switch getScanKind(notification) {
case reporthandling.KindFramework: // Download frameworks
for _, rule := range notification.Rules {
receivedFramework, recExceptionPolicies, err := policyHandler.getFrameworkPolicies(rule.Name)
receivedFramework, err := policyHandler.getters.PolicyGetter.GetFramework(rule.Name)
if err != nil {
return frameworks, policyDownloadError(err)
}
if receivedFramework != nil {
frameworks = append(frameworks, *receivedFramework)
if recExceptionPolicies != nil {
exceptionPolicies = append(exceptionPolicies, recExceptionPolicies...)
}
} else if err != nil {
if strings.Contains(err.Error(), "unsupported protocol scheme") {
err = fmt.Errorf("failed to download from GitHub release, try running with `--use-default` flag")
}
return nil, nil, fmt.Errorf("kind: %v, name: %s, error: %s", rule.Kind, rule.Name, err.Error())
}
}
case reporthandling.KindControl:
case reporthandling.KindControl: // Download controls
f := reporthandling.Framework{}
var receivedControl *reporthandling.Control
var recExceptionPolicies []armotypes.PostureExceptionPolicy
var err error
for _, rule := range notification.Rules {
receivedControl, recExceptionPolicies, err = policyHandler.getControl(rule.Name)
if receivedControl != nil {
f.Controls = append(f.Controls, *receivedControl)
if recExceptionPolicies != nil {
exceptionPolicies = append(exceptionPolicies, recExceptionPolicies...)
}
} else if err != nil {
if strings.Contains(err.Error(), "unsupported protocol scheme") {
err = fmt.Errorf("failed to download from GitHub release, try running with `--use-default` flag")
}
return nil, nil, fmt.Errorf("error: %s", err.Error())
receivedControl, err = policyHandler.getters.PolicyGetter.GetControl(rule.Name)
if err != nil {
return frameworks, policyDownloadError(err)
}
}
if receivedControl != nil {
f.Controls = append(f.Controls, *receivedControl)
}
frameworks = append(frameworks, f)
// TODO: add case for control from file
default:
err := fmt.Errorf("missing rule kind, expected: %s", reporthandling.KindFramework)
errs = fmt.Errorf("%s", err.Error())
return frameworks, fmt.Errorf("unknown policy kind")
}
return frameworks, exceptionPolicies, errs
}
func (policyHandler *PolicyHandler) getFrameworkPolicies(policyName string) (*reporthandling.Framework, []armotypes.PostureExceptionPolicy, error) {
receivedFramework, err := policyHandler.getters.PolicyGetter.GetFramework(policyName)
if err != nil {
return nil, nil, err
}
receivedException, err := policyHandler.getters.ExceptionsGetter.GetExceptions(cautils.CustomerGUID, cautils.ClusterName)
if err != nil {
return receivedFramework, nil, err
}
return receivedFramework, receivedException, nil
}
func GetScanKind(notification *reporthandling.PolicyNotification) *reporthandling.PolicyIdentifier {
if len(notification.Rules) > 0 {
return &notification.Rules[0]
}
return nil
}
// Get control by name
func (policyHandler *PolicyHandler) getControl(policyName string) (*reporthandling.Control, []armotypes.PostureExceptionPolicy, error) {
control := &reporthandling.Control{}
var err error
control, err = policyHandler.getters.PolicyGetter.GetControl(policyName)
if err != nil {
return control, nil, err
}
// if control == nil {
// return control, nil, fmt.Errorf("control not found")
// }
exceptions, err := policyHandler.getters.ExceptionsGetter.GetExceptions(cautils.CustomerGUID, cautils.ClusterName)
if err != nil {
return control, nil, err
}
return control, exceptions, nil
return frameworks, nil
}
+21
View File
@@ -0,0 +1,21 @@
package policyhandler
import (
"fmt"
"strings"
"github.com/armosec/opa-utils/reporthandling"
)
func getScanKind(notification *reporthandling.PolicyNotification) reporthandling.NotificationPolicyKind {
if len(notification.Rules) > 0 {
return notification.Rules[0].Kind
}
return "unknown"
}
func policyDownloadError(err error) error {
if strings.Contains(err.Error(), "unsupported protocol scheme") {
err = fmt.Errorf("failed to download from GitHub release, try running with `--use-default` flag")
}
return err
}
+18
View File
@@ -0,0 +1,18 @@
import sys
import smoke_utils
tests_pkg = [
"test_command"
, "test_version"
]
def run(**kwargs):
for i in tests_pkg:
m = __import__(i)
m.run(**kwargs)
if __name__ == "__main__":
run(kubescape_exec=smoke_utils.get_exec_from_args(sys.argv))
+14
View File
@@ -0,0 +1,14 @@
from sys import stderr
import subprocess
def get_exec_from_args(args: list):
return args[1]
def run_command(command):
try:
return f"{subprocess.check_output(command)}"
except Exception as e:
return f"{e}"
+29
View File
@@ -0,0 +1,29 @@
import smoke_utils
import sys
def test_command(command: list):
print(f"Testing \"{' '.join(command[1:])}\" command")
msg = smoke_utils.run_command(command)
assert "unknown command" not in msg, f"{command[1:]} is missing: {msg}"
assert "invalid parameter" not in msg, f"{command[1:]} is invalid: {msg}"
print(f"Done testing \"{' '.join(command[1:])}\" command")
def run(kubescape_exec:str):
print("Testing supported commands")
test_command(command=[kubescape_exec, "version"])
test_command(command=[kubescape_exec, "download"])
test_command(command=[kubescape_exec, "config"])
test_command(command=[kubescape_exec, "help"])
test_command(command=[kubescape_exec, "scan", "framework"])
test_command(command=[kubescape_exec, "scan", "control"])
print("Done testing commands")
if __name__ == "__main__":
run(kubescape_exec=smoke_utils.get_exec_from_args(sys.argv))
+17
View File
@@ -0,0 +1,17 @@
import os
import smoke_utils
import sys
def run(kubescape_exec: str):
print("Testing version")
ver = os.getenv("RELEASE")
msg = smoke_utils.run_command(command=[kubescape_exec, "version"])
assert ver in msg, f"expected version: {ver}, found: {msg}"
print("Done testing version")
if __name__ == "__main__":
run(kubescape_exec=smoke_utils.get_exec_from_args(sys.argv))