diff --git a/build/Dockerfile b/build/Dockerfile index aa8a11b5..99feb36b 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -31,14 +31,15 @@ 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 +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 +RUN mkdir /home/armo/.kubescape +COPY --from=builder /work/artifacts/ /home/armo/.kubescape +RUN chmod 777 -R /home/armo/.kubescape ENTRYPOINT ["ksserver"] diff --git a/cmd/scan/control.go b/cmd/scan/control.go index 3b031087..9521afe4 100644 --- a/cmd/scan/control.go +++ b/cmd/scan/control.go @@ -6,6 +6,8 @@ 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" @@ -64,7 +66,7 @@ func getControlCmd(ks meta.IKubescape, scanInfo *cautils.ScanInfo) *cobra.Comman } 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] != "-" { diff --git a/cmd/scan/framework.go b/cmd/scan/framework.go index e743bef3..1b54281a 100644 --- a/cmd/scan/framework.go +++ b/cmd/scan/framework.go @@ -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 { diff --git a/core/cautils/datastructuresmethods.go b/core/cautils/datastructuresmethods.go index 8d7d757f..481b231d 100644 --- a/core/cautils/datastructuresmethods.go +++ b/core/cautils/datastructuresmethods.go @@ -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 } diff --git a/core/cautils/scaninfo.go b/core/cautils/scaninfo.go index 2d3df713..6b75d882 100644 --- a/core/cautils/scaninfo.go +++ b/core/cautils/scaninfo.go @@ -8,6 +8,8 @@ import ( "path/filepath" "strings" + 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" @@ -180,11 +182,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.Kind = reporthandling.NotificationPolicyKind(kind) // reporthandling.KindFramework newPolicy.Name = policy scanInfo.PolicyIdentifier = append(scanInfo.PolicyIdentifier, newPolicy) } diff --git a/core/cautils/versioncheck.go b/core/cautils/versioncheck.go index 1baf8819..a3ebfd7e 100644 --- a/core/cautils/versioncheck.go +++ b/core/cautils/versioncheck.go @@ -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() diff --git a/core/core/scan.go b/core/core/scan.go index b73cc090..68a90b5c 100644 --- a/core/core/scan.go +++ b/core/core/scan.go @@ -3,6 +3,8 @@ package core import ( "fmt" + apisv1 "github.com/armosec/opa-utils/httpserver/apis/v1" + "github.com/armosec/armoapi-go/armotypes" "github.com/armosec/k8s-interface/k8sinterface" @@ -130,7 +132,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 diff --git a/go.mod b/go.mod index a1a49d2f..4a2e717f 100644 --- a/go.mod +++ b/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.67 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.70 + github.com/armosec/opa-utils v0.0.135 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 diff --git a/go.sum b/go.sum index 75056c28..d3663e3f 100644 --- a/go.sum +++ b/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.67 h1:XHm3nasLjzQBQJVQKVCA9MpFVQsqyl6JnN4lx2IVHpU= +github.com/armosec/armoapi-go v0.0.67/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 h1:NU3UIaNl7H3hsRecwggiaQbZXTwXtOKg3GOBjq6/XJw= +github.com/armosec/k8s-interface v0.0.70/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.135 h1:7JMHGKMD13XWwwd4g11tw6T9C4BRPifDiqdeDJEz638= +github.com/armosec/opa-utils v0.0.135/go.mod h1:mCFQzz4E227f7V2jQVQ9XCivkNNK3UWCTaZ0HE5rBWk= 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= diff --git a/httphandler/README.md b/httphandler/README.md index 1cb4e4e0..8fcdfe06 100644 --- a/httphandler/README.md +++ b/httphandler/README.md @@ -6,10 +6,11 @@ Running `kubescape` will start up a webserver on port `8080` which will serve th * 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 `/v1/results` - Request kubescape scan results -* * query `id=` -> 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=` -> ID returned when triggering the scan action. If empty will return latest results +* * query `keep` -> Do not delete results from local storage after returning +* DELETE `/v1/results` - Delete kubescape scan results from storage. If empty will delete latest results * * query `id=`: Delete ID of specific results * * query `all`: Delete all cached results * 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. @@ -39,22 +40,15 @@ body: 1. Trigger kubescape scan ```bash - curl --header "Content-Type: application/json" --request POST --data '{}' http://127.0.0.1:8080/v1/scan -o scan_id + curl --header "Content-Type: application/json" --request POST --data '{"hostScanner":true}' http://127.0.0.1:8080/v1/scan -o scan_id ``` + 2. Get kubescape scan results ```bash curl --request GET http://127.0.0.1:8080/v1/results?id=$(cat scan_id) ``` -#### Scan single namespace with specific framework -```bash -curl --header "Content-Type: application/json" \ - --request POST \ - --data '{"hostScanner":true, "submit":true}' \ - http://127.0.0.1:8080/v1/scan -``` - -#### Scan single namespace with specific framework +#### Scan single namespace with a specific framework ```bash curl --header "Content-Type: application/json" \ --request POST \ @@ -65,3 +59,15 @@ curl --header "Content-Type: application/json" \ * [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 diff --git a/httphandler/go.mod b/httphandler/go.mod index aa45e603..e1becf30 100644 --- a/httphandler/go.mod +++ b/httphandler/go.mod @@ -6,8 +6,8 @@ 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.135 + github.com/armosec/utils-go v0.0.5 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/stretchr/testify v1.7.1 @@ -28,11 +28,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/armoapi-go v0.0.67 // indirect github.com/armosec/go-git-url v0.0.4 // indirect - github.com/armosec/k8s-interface v0.0.69 // indirect + github.com/armosec/k8s-interface v0.0.70 // 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 diff --git a/httphandler/go.sum b/httphandler/go.sum index 2edffd2e..7946e491 100644 --- a/httphandler/go.sum +++ b/httphandler/go.sum @@ -113,28 +113,29 @@ 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.67 h1:XHm3nasLjzQBQJVQKVCA9MpFVQsqyl6JnN4lx2IVHpU= +github.com/armosec/armoapi-go v0.0.67/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 h1:NU3UIaNl7H3hsRecwggiaQbZXTwXtOKg3GOBjq6/XJw= +github.com/armosec/k8s-interface v0.0.70/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.134/go.mod h1:mCFQzz4E227f7V2jQVQ9XCivkNNK3UWCTaZ0HE5rBWk= +github.com/armosec/opa-utils v0.0.135 h1:7JMHGKMD13XWwwd4g11tw6T9C4BRPifDiqdeDJEz638= +github.com/armosec/opa-utils v0.0.135/go.mod h1:mCFQzz4E227f7V2jQVQ9XCivkNNK3UWCTaZ0HE5rBWk= 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= @@ -1148,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= diff --git a/httphandler/handlerequests/v1/datastructure.go b/httphandler/handlerequests/v1/datastructure.go deleted file mode 100644 index 67f6983c..00000000 --- a/httphandler/handlerequests/v1/datastructure.go +++ /dev/null @@ -1,23 +0,0 @@ -package v1 - -import ( - "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 *bool `json:"submit"` // Submit results to Armo BE - default will - HostScanner *bool `json:"hostScanner"` // Deploy ARMO K8s host scanner to collect data from certain controls - KeepLocal *bool `json:"keepLocal"` // Do not submit results - UseCachedArtifacts *bool `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 -} diff --git a/httphandler/handlerequests/v1/datastructuremethods.go b/httphandler/handlerequests/v1/datastructuremethods.go index b4409b73..90b3d841 100644 --- a/httphandler/handlerequests/v1/datastructuremethods.go +++ b/httphandler/handlerequests/v1/datastructuremethods.go @@ -3,20 +3,23 @@ package v1 import ( "strings" + apisv1 "github.com/armosec/opa-utils/httpserver/apis/v1" + utilsmetav1 "github.com/armosec/opa-utils/httpserver/meta/v1" + "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 != "" && len(scanRequest.TargetNames) > 0 { if strings.EqualFold(string(scanRequest.TargetType), string(reporthandling.KindFramework)) { - scanRequest.TargetType = reporthandling.KindFramework + scanRequest.TargetType = apisv1.KindFramework scanInfo.FrameworkScan = true } else if strings.EqualFold(string(scanRequest.TargetType), string(reporthandling.KindControl)) { - scanRequest.TargetType = reporthandling.KindControl + scanRequest.TargetType = apisv1.KindControl } else { // unknown policy kind - set scan all scanInfo.FrameworkScan = true diff --git a/httphandler/handlerequests/v1/datastructuremethods_test.go b/httphandler/handlerequests/v1/datastructuremethods_test.go index a4a46a0b..62b851db 100644 --- a/httphandler/handlerequests/v1/datastructuremethods_test.go +++ b/httphandler/handlerequests/v1/datastructuremethods_test.go @@ -3,14 +3,16 @@ package v1 import ( "testing" + apisv1 "github.com/armosec/opa-utils/httpserver/apis/v1" + utilsmetav1 "github.com/armosec/opa-utils/httpserver/meta/v1" "github.com/armosec/opa-utils/reporthandling" "github.com/stretchr/testify/assert" ) func TestToScanInfo(t *testing.T) { { - req := PostScanRequest{ - TargetType: reporthandling.KindFramework, + req := &utilsmetav1.PostScanRequest{ + TargetType: apisv1.KindFramework, Account: "abc", Logger: "info", Format: "pdf", @@ -18,7 +20,7 @@ func TestToScanInfo(t *testing.T) { ExcludedNamespaces: []string{"kube-system", "kube-public"}, TargetNames: []string{"nsa", "mitre"}, } - s := req.ToScanInfo() + s := ToScanInfo(req) assert.Equal(t, "abc", s.Account) assert.Equal(t, "v2", s.FormatVersion) assert.Equal(t, "pdf", s.Format) @@ -36,12 +38,12 @@ func TestToScanInfo(t *testing.T) { assert.Equal(t, reporthandling.KindFramework, s.PolicyIdentifier[1].Kind) } { - req := PostScanRequest{ - TargetType: reporthandling.KindControl, + req := &utilsmetav1.PostScanRequest{ + TargetType: apisv1.KindControl, TargetNames: []string{"c-0001"}, IncludeNamespaces: []string{"kube-system", "kube-public"}, } - s := req.ToScanInfo() + s := ToScanInfo(req) assert.False(t, s.ScanAll) assert.False(t, s.FrameworkScan) assert.Equal(t, "kube-system,kube-public", s.IncludeNamespaces) @@ -51,8 +53,8 @@ func TestToScanInfo(t *testing.T) { assert.Equal(t, reporthandling.KindControl, s.PolicyIdentifier[0].Kind) } { - req := PostScanRequest{} - s := req.ToScanInfo() + req := &utilsmetav1.PostScanRequest{} + s := ToScanInfo(req) assert.True(t, s.ScanAll) assert.True(t, s.FrameworkScan) } diff --git a/httphandler/handlerequests/v1/requestshandler.go b/httphandler/handlerequests/v1/requestshandler.go index c2d30b82..e8a81f01 100644 --- a/httphandler/handlerequests/v1/requestshandler.go +++ b/httphandler/handlerequests/v1/requestshandler.go @@ -7,6 +7,8 @@ import ( "net/http" "sync" + utilsmetav1 "github.com/armosec/opa-utils/httpserver/meta/v1" + "github.com/armosec/kubescape/v2/core/cautils/logger" "github.com/armosec/kubescape/v2/core/cautils/logger/helpers" "github.com/google/uuid" @@ -24,14 +26,18 @@ func NewHTTPHandler() *HTTPHandler { state: newServerState(), } } - func (handler *HTTPHandler) Scan(w http.ResponseWriter, r *http.Request) { + response := utilsmetav1.Response{} + w.Header().Set("Content-Type", "application/json") + 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))) + response.Response = []byte(fmt.Sprintf("%v", err)) + response.Type = utilsmetav1.ErrorScanResponseType + w.Write(responseToBytes(&response)) } }() @@ -39,7 +45,7 @@ func (handler *HTTPHandler) Scan(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: // return request template - json.NewEncoder(w).Encode(PostScanRequest{}) + json.NewEncoder(w).Encode(utilsmetav1.PostScanRequest{}) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) return @@ -50,8 +56,8 @@ func (handler *HTTPHandler) Scan(w http.ResponseWriter, r *http.Request) { } if handler.state.isBusy() { - w.Write([]byte(handler.state.getID())) w.WriteHeader(http.StatusOK) + w.Write([]byte(handler.state.getID())) return } @@ -60,25 +66,31 @@ func (handler *HTTPHandler) Scan(w http.ResponseWriter, r *http.Request) { // generate id scanID := uuid.NewString() handler.state.setID(scanID) + response.ID = scanID + response.Type = utilsmetav1.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()))) + response.Response = []byte(fmt.Sprintf("failed to read request body, reason: %s", err.Error())) + response.Type = utilsmetav1.ErrorScanResponseType + w.Write(responseToBytes(&response)) return } - scanRequest := PostScanRequest{} + 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()))) + response.Response = []byte(fmt.Sprintf("failed to parse request payload, reason: %s", err.Error())) + response.Type = utilsmetav1.ErrorScanResponseType + w.Write(responseToBytes(&response)) return } - response := []byte(scanID + "\n") - returnResults := r.URL.Query().Has("wait") + keepResults := r.URL.Query().Has("keep") + var wg sync.WaitGroup if returnResults { wg.Add(1) @@ -95,31 +107,42 @@ func (handler *HTTPHandler) Scan(w http.ResponseWriter, r *http.Request) { if err != nil { logger.L().Error("scanning failed", helpers.String("ID", scanID), helpers.Error(err)) if returnResults { - response = []byte(err.Error()) + response.Type = utilsmetav1.ErrorScanResponseType + response.Response = []byte(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 + response.Type = utilsmetav1.ResultsV1ScanResponseType + response.Response = results wg.Done() } } + if !keepResults { + logger.L().Debug("deleting results", helpers.String("ID", scanID)) + removeResultsFile(scanID) + } handler.state.setNotBusy() }() wg.Wait() + w.WriteHeader(statusCode) - w.Write(response) + w.Write(responseToBytes(&response)) } func (handler *HTTPHandler) Results(w http.ResponseWriter, r *http.Request) { + response := utilsmetav1.Response{} + w.Header().Set("Content-Type", "application/json") + 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.Response = []byte(fmt.Sprintf("%v", err)) + response.Type = utilsmetav1.ErrorScanResponseType + w.Write(responseToBytes(&response)) } }() @@ -132,14 +155,19 @@ func (handler *HTTPHandler) Results(w http.ResponseWriter, r *http.Request) { if scanID == "" { // if no scan found logger.L().Info("empty scan ID") w.WriteHeader(http.StatusBadRequest) // Should we return ok? - w.Write([]byte("latest scan not found. trigger again")) + response.Response = []byte("latest scan not found. trigger again") + response.Type = utilsmetav1.ErrorScanResponseType + w.Write(responseToBytes(&response)) return } + response.ID = 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())) + w.WriteHeader(http.StatusOK) + response.Response = []byte("scanning in progress") + w.Write(responseToBytes(&response)) return } } @@ -148,19 +176,20 @@ func (handler *HTTPHandler) Results(w http.ResponseWriter, r *http.Request) { case http.MethodGet: logger.L().Info("requesting results", helpers.String("ID", scanID)) - if r.URL.Query().Has("remove") { + if !r.URL.Query().Has("keep") { logger.L().Info("deleting results", helpers.String("ID", scanID)) defer removeResultsFile(scanID) } if res, err := readResultsFile(scanID); err != nil { logger.L().Info("scan result not found", helpers.String("ID", scanID)) w.WriteHeader(http.StatusNoContent) - w.Write([]byte(err.Error())) + response.Response = []byte(err.Error()) } else { logger.L().Info("scan result found", helpers.String("ID", scanID)) w.WriteHeader(http.StatusOK) - w.Write(res) + response.Response = res } + w.Write(responseToBytes(&response)) case http.MethodDelete: logger.L().Info("deleting results", helpers.String("ID", scanID)) @@ -183,3 +212,8 @@ 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 +} diff --git a/httphandler/handlerequests/v1/requestshandlerutil_test.go b/httphandler/handlerequests/v1/requestshandlerutil_test.go index 09228cbd..a5094e8b 100644 --- a/httphandler/handlerequests/v1/requestshandlerutil_test.go +++ b/httphandler/handlerequests/v1/requestshandlerutil_test.go @@ -3,7 +3,8 @@ package v1 import ( "testing" - "github.com/armosec/opa-utils/reporthandling" + apisv1 "github.com/armosec/opa-utils/httpserver/apis/v1" + utilsmetav1 "github.com/armosec/opa-utils/httpserver/meta/v1" "github.com/stretchr/testify/assert" ) @@ -19,8 +20,8 @@ func TestDefaultScanInfo(t *testing.T) { } func TestGetScanCommand(t *testing.T) { - req := PostScanRequest{ - TargetType: reporthandling.KindFramework, + req := utilsmetav1.PostScanRequest{ + TargetType: apisv1.KindFramework, } s := getScanCommand(&req, "abc") assert.Equal(t, "", s.Account) diff --git a/httphandler/handlerequests/v1/requestshandlerutils.go b/httphandler/handlerequests/v1/requestshandlerutils.go index 48fbd9c1..6f80eae6 100644 --- a/httphandler/handlerequests/v1/requestshandlerutils.go +++ b/httphandler/handlerequests/v1/requestshandlerutils.go @@ -6,14 +6,14 @@ import ( "path/filepath" "strings" - pkgcautils "github.com/armosec/utils-go/utils" - "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" + "github.com/armosec/utils-go/boolutils" ) -func scan(scanRequest *PostScanRequest, scanID string) ([]byte, error) { +func scan(scanRequest *utilsmetav1.PostScanRequest, scanID string) ([]byte, error) { scanInfo := getScanCommand(scanRequest, scanID) ks := core.NewKubescape() @@ -81,9 +81,9 @@ func findFile(targetDir string, fileName string) (string, error) { 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 *** @@ -107,6 +107,7 @@ func defaultScanInfo() *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.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 @@ -121,7 +122,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 } diff --git a/httphandler/listener/init.go b/httphandler/listener/init.go new file mode 100644 index 00000000..071a8ac7 --- /dev/null +++ b/httphandler/listener/init.go @@ -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()) + } +} diff --git a/httphandler/listener/setup.go b/httphandler/listener/setup.go index 64024e69..e456e6e0 100644 --- a/httphandler/listener/setup.go +++ b/httphandler/listener/setup.go @@ -9,7 +9,6 @@ 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" ) @@ -24,7 +23,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 {