Merge pull request #584 from armosec/dev

Microservice swagger support
This commit is contained in:
David Wertenteil
2022-08-08 10:07:31 +03:00
committed by GitHub
52 changed files with 1303 additions and 691 deletions
+39 -1
View File
@@ -32,6 +32,39 @@ jobs:
with:
submodules: recursive
- name: Cache Go modules (Linux)
if: matrix.os == 'ubuntu-latest'
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (macOS)
if: matrix.os == 'macos-latest'
uses: actions/cache@v3
with:
path: |
~/Library/Caches/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (Windows)
if: matrix.os == 'windows-latest'
uses: actions/cache@v3
with:
path: |
~\AppData\Local\go-build
~\go\pkg\mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Set up Go
uses: actions/setup-go@v3
with:
@@ -39,7 +72,12 @@ jobs:
# - name: Test cmd pkg
# run: cd cmd && go test -v ./...
- name: Install libgit2
- name: Install MSYS2 & libgit2 (Windows)
shell: cmd
run: .\build.bat all
if: matrix.os == 'windows-latest'
- name: Install libgit2 (Linux/macOS)
run: make libgit2
if: matrix.os != 'windows-latest'
+40 -1
View File
@@ -14,6 +14,40 @@ jobs:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Cache Go modules (Linux)
if: matrix.os == 'ubuntu-latest'
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (macOS)
if: matrix.os == 'macos-latest'
uses: actions/cache@v3
with:
path: |
~/Library/Caches/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (Windows)
if: matrix.os == 'windows-latest'
uses: actions/cache@v3
with:
path: |
~\AppData\Local\go-build
~\go\pkg\mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Set up Go
uses: actions/setup-go@v3
with:
@@ -30,7 +64,12 @@ jobs:
# - name: Test cmd pkg
# run: cd cmd && go test -v ./...
- name: Install libgit2
- name: Install MSYS2 & libgit2 (Windows)
shell: cmd
run: .\build.bat all
if: matrix.os == 'windows-latest'
- name: Install libgit2 (Linux/macOS)
run: make libgit2
if: matrix.os != 'windows-latest'
+39 -1
View File
@@ -16,12 +16,50 @@ jobs:
with:
submodules: recursive
- name: Cache Go modules (Linux)
if: matrix.os == 'ubuntu-latest'
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (macOS)
if: matrix.os == 'macos-latest'
uses: actions/cache@v3
with:
path: |
~/Library/Caches/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Cache Go modules (Windows)
if: matrix.os == 'windows-latest'
uses: actions/cache@v3
with:
path: |
~\AppData\Local\go-build
~\go\pkg\mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.18
- name: Install libgit2
- name: Install MSYS2 & libgit2 (Windows)
shell: cmd
run: .\build.bat all
if: matrix.os == 'windows-latest'
- name: Install libgit2 (Linux/macOS)
run: make libgit2
if: matrix.os != 'windows-latest'
+70 -54
View File
@@ -6,7 +6,7 @@
Kubescape is a K8s open-source tool providing a multi-cloud K8s single pane of glass, including risk analysis, security compliance, RBAC visualizer and image vulnerabilities scanning.
Kubescape scans K8s clusters, YAML files, and HELM charts, detecting misconfigurations according to multiple frameworks (such as the [NSA-CISA](https://www.armosec.io/blog/kubernetes-hardening-guidance-summary-by-armo) , [MITRE ATT&CK®](https://www.microsoft.com/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/)), software vulnerabilities, and RBAC (role-based-access-control) violations at early stages of the CI/CD pipeline, calculates risk score instantly and shows risk trends over time.
Kubescape scans K8s clusters, YAML files, and HELM charts, detecting misconfigurations according to multiple frameworks (such as the [NSA-CISA](https://www.armosec.io/blog/kubernetes-hardening-guidance-summary-by-armo/?utm_source=github&utm_medium=repository), [MITRE ATT&CK®](https://www.microsoft.com/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/)), software vulnerabilities, and RBAC (role-based-access-control) violations at early stages of the CI/CD pipeline, calculates risk score instantly and shows risk trends over time.
It became one of the fastest-growing Kubernetes tools among developers due to its easy-to-use CLI interface, flexible output formats, and automated scanning capabilities, saving Kubernetes users and admins precious time, effort, and resources.
Kubescape integrates natively with other DevOps tools, including Jenkins, CircleCI, Github workflows, Prometheus, and Slack, and supports multi-cloud K8s deployments like EKS, GKE, and AKS.
@@ -66,7 +66,7 @@ Want to contribute? Want to discuss something? Have an issue?
* Feel free to pick a task from the [roadmap](docs/roadmap.md) or suggest a feature of your own. [Contact us](MAINTAINERS.md) directly for more information :)
* Open a issue, we are trying to respond within 48 hours
* [Join us](https://armosec.github.io/kubescape/) in a discussion on our discord server!
* [Join us](https://armosec.github.io/kubescape) in a discussion on our discord server!
[<img src="docs/discord-banner.png" width="100" alt="logo" align="center">](https://armosec.github.io/kubescape/)
![discord](https://img.shields.io/discord/893048809884643379)
@@ -74,10 +74,10 @@ Want to contribute? Want to discuss something? Have an issue?
# Options and examples
[Kubescape docs](https://hub.armosec.io/docs)
[Kubescape docs](https://hub.armosec.io/docs?utm_source=github&utm_medium=repository)
## Playground
* [Kubescape playground](https://www.katacoda.com/pathaksaiyam/scenarios/kubescape)
* [Kubescape playground](https://killercoda.com/saiyampathak/scenario/kubescape)
## Tutorials
@@ -89,6 +89,8 @@ Want to contribute? Want to discuss something? Have an issue?
* [Configure and run customized frameworks](https://youtu.be/12Sanq_rEhs)
* Customize controls configurations. [Kubescape CLI](https://youtu.be/955psg6TVu4), [Kubescape SaaS](https://youtu.be/lIMVSVhH33o)
<details><summary>Windows</summary>
## Install on Windows
**Requires powershell v5.0+**
@@ -102,6 +104,9 @@ Note: if you get an error you might need to change the execution policy (i.e. en
``` powershell
Set-ExecutionPolicy RemoteSigned -scope CurrentUser
```
</details>
<details><summary>MacOS</summary>
## Install on macOS
@@ -111,6 +116,9 @@ Set-ExecutionPolicy RemoteSigned -scope CurrentUser
2. ```sh
brew install kubescape
```
</details>
<details><summary>Nix/NixOS</summary>
## Install on NixOS or with nix (Community)
@@ -144,32 +152,33 @@ home-manager:
Or to your profile (not preferred): `nix-env --install -A nixpkgs.kubescape`
</details>
## Usage & Examples
### Examples
#### Scan a running Kubernetes cluster and submit results to the [Kubescape SaaS version](https://portal.armo.cloud/)
#### Scan a running Kubernetes cluster and submit results to the [Kubescape SaaS version](https://cloud.armosec.io?utm_source=github&utm_medium=repository)
```
kubescape scan --submit --enable-host-scan --verbose
```
> Read [here](https://hub.armosec.io/docs/host-sensor) more about the `enable-host-scan` flag
> Read [here](https://hub.armosec.io/docs/host-sensor?utm_source=github&utm_medium=repository) more about the `enable-host-scan` flag
#### Scan a running Kubernetes cluster with [`nsa`](https://www.nsa.gov/Press-Room/News-Highlights/Article/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/) framework and submit results to the [Kubescape SaaS version](https://portal.armo.cloud/)
#### Scan a running Kubernetes cluster with [`nsa`](https://www.nsa.gov/Press-Room/News-Highlights/Article/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/) framework and submit results to the [Kubescape SaaS version](https://cloud.armosec.io?utm_source=github&utm_medium=repository)
```
kubescape scan framework nsa --submit
```
#### Scan a running Kubernetes cluster with [`MITRE ATT&CK®`](https://www.microsoft.com/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/) framework and submit results to the [Kubescape SaaS version](https://portal.armo.cloud/)
#### Scan a running Kubernetes cluster with [`MITRE ATT&CK®`](https://www.microsoft.com/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/) framework and submit results to the [Kubescape SaaS version](https://cloud.armosec.io?utm_source=github&utm_medium=repository)
```
kubescape scan framework mitre --submit
```
#### Scan a running Kubernetes cluster with a specific control using the control name or control ID. [List of controls](https://hub.armosec.io/docs/controls)
#### Scan a running Kubernetes cluster with a specific control using the control name or control ID. [List of controls](https://hub.armosec.io/docs/controls?utm_source=github&utm_medium=repository)
```
kubescape scan control "Privileged container"
```
@@ -184,12 +193,12 @@ kubescape scan --include-namespaces development,staging,production
kubescape scan --exclude-namespaces kube-system,kube-public
```
#### Scan local `yaml`/`json` files before deploying. [Take a look at the demonstration](https://youtu.be/Ox6DaR7_4ZI) Submit the results in case the directory is a git repo. [docs](https://hub.armosec.io/docs/repository-scanning)
#### Scan local `yaml`/`json` files before deploying. [Take a look at the demonstration](https://youtu.be/Ox6DaR7_4ZI) Submit the results in case the directory is a git repo. [docs](https://hub.armosec.io/docs/repository-scanning?utm_source=github&utm_medium=repository)
```
kubescape scan *.yaml --submit
```
#### Scan kubernetes manifest files from a git repository [and submit the results](https://hub.armosec.io/docs/repository-scanning)
#### Scan kubernetes manifest files from a git repository [and submit the results](https://hub.armosec.io/docs/repository-scanning?utm_source=github&utm_medium=repository)
```
kubescape scan https://github.com/armosec/kubescape --submit
```
@@ -270,35 +279,10 @@ kubescape scan framework nsa --use-from /path/nsa.json
```
## Scan Periodically using Helm - Contributed by [@yonahd](https://github.com/yonahd)
[Please follow the instructions here](https://hub.armosec.io/docs/installation-of-armo-in-cluster)
## Scan Periodically using Helm
[Please follow the instructions here](https://hub.armosec.io/docs/installation-of-armo-in-cluster?utm_source=github&utm_medium=repository)
[helm chart repo](https://github.com/armosec/armo-helm)
## Scan using docker image
Official Docker image `quay.io/armosec/kubescape`
```
docker run -v "$(pwd)/example.yaml:/app/example.yaml quay.io/armosec/kubescape scan /app/example.yaml
```
If you wish, you can [build the docker image on your own](build/README.md)
# Submit data manually
Use the `submit` command if you wish to submit data manually
## Submit scan results manually
> Support forward compatibility by using the `--format-version v2` flag
First, scan your cluster using the `json` format flag: `kubescape scan framework <name> --format json --format-version v2 --output path/to/results.json`.
Now you can submit the results to the Kubescape SaaS version -
```
kubescape submit results path/to/results.json
```
# Integrations
## VS Code Extension
@@ -314,42 +298,70 @@ View Kubescape scan results directly in [Lens IDE](https://k8slens.dev/) using k
# Building Kubescape
<details><summary>Windows</summary>
## Windows
```
go build .
```
OR
```
make build
```
## Linux / MacOS
1. Install libgit2 dependency
1. Install MSYS2 & build libgit _(needed only for the first time)_
```
make libgit2
build.bat all
```
> `cmake` is required to build libgit2. You can install it by running `sudo apt-get install cmake` (Linux) or `brew install cmake` (MacOS)
> You can install MSYS2 separately by running `build.bat install` and build libgit2 separately by running `build.bat build`
2. Build
2. Build kubescape
```
make build
```
OR
```
go build -tags=static .
```
</details>
<details><summary>Linux / MacOS</summary>
## Linux / MacOS
1. Install libgit2 dependency _(needed only for the first time)_
```
make libgit2
```
> `cmake` is required to build libgit2. You can install it by running `sudo apt-get install cmake` (Linux) or `brew install cmake` (macOS)
2. Build kubescape
```
make build
```
OR
```
go build -tags=static .
```
3. Test
```
make test
```
</details>
## VS code configuration samples
You can use the samples files below to setup your VS code environment for building and debugging purposes.
<details><summary>.vscode/settings.json</summary>
```json5
// .vscode/settings.json
{
@@ -360,6 +372,9 @@ You can use the samples files below to setup your VS code environment for buildi
}
}
```
</details>
<details><summary>.vscode/launch.json</summary>
```json5
// .vscode/launch.json
@@ -382,13 +397,14 @@ You can use the samples files below to setup your VS code environment for buildi
]
}
```
</details>
# Under the hood
## Technology
Kubescape based on OPA engine: https://github.com/open-policy-agent/opa and ARMO's posture controls.
Kubescape based on [OPA engine](https://github.com/open-policy-agent/opa) and ARMO's posture controls.
The tools retrieves Kubernetes objects from the API server and runs a set of [rego's snippets](https://www.openpolicyagent.org/docs/latest/policy-language/) developed by [ARMO](https://www.armosec.io/).
The tools retrieves Kubernetes objects from the API server and runs a set of [rego's snippets](https://www.openpolicyagent.org/docs/latest/policy-language/) developed by [ARMO](https://www.armosec.io?utm_source=github&utm_medium=repository).
The results by default printed in a pretty "console friendly" manner, but they can be retrieved in JSON format for further processing.
+51
View File
@@ -0,0 +1,51 @@
@ECHO OFF
IF "%1"=="install" goto Install
IF "%1"=="build" goto Build
IF "%1"=="all" goto All
IF "%1"=="" goto Error ELSE goto Error
:Install
if exist C:\MSYS64\ (
echo "MSYS2 already installed"
) else (
mkdir temp_install & cd temp_install
echo "Downloading MSYS2..."
curl -L https://github.com/msys2/msys2-installer/releases/download/2022-06-03/msys2-x86_64-20220603.exe > msys2-x86_64-20220603.exe
echo "Installing MSYS2..."
msys2-x86_64-20220603.exe install --root C:\MSYS64 --confirm-command
cd .. && rmdir /s /q temp_install
)
echo "Adding MSYS2 to path..."
SET "PATH=C:\MSYS64\mingw64\bin;C:\MSYS64\usr\bin;%PATH%"
echo %PATH%
echo "Installing MSYS2 packages..."
pacman -S --needed --noconfirm make
pacman -S --needed --noconfirm mingw-w64-x86_64-cmake
pacman -S --needed --noconfirm mingw-w64-x86_64-gcc
pacman -S --needed --noconfirm mingw-w64-x86_64-pkg-config
pacman -S --needed --noconfirm msys2-w32api-runtime
IF "%1"=="all" GOTO Build
GOTO End
:Build
SET "PATH=C:\MSYS2\mingw64\bin;C:\MSYS2\usr\bin;%PATH%"
make libgit2
GOTO End
:All
GOTO Install
:Error
echo "Error: Unknown option"
GOTO End
:End
+1 -1
View File
@@ -12,7 +12,7 @@ import (
func getExceptionsCmd(ks meta.IKubescape, submitInfo *metav1.Submit) *cobra.Command {
return &cobra.Command{
Use: "exceptions <full path to exceptins file>",
Use: "exceptions <full path to exceptions file>",
Short: "Submit exceptions to the Kubescape SaaS version",
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
+23 -6
View File
@@ -1,27 +1,40 @@
package submit
import (
"fmt"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/armosec/kubescape/v2/core/cautils/getter"
"github.com/armosec/kubescape/v2/core/meta"
"github.com/armosec/kubescape/v2/core/meta/cliinterfaces"
v1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1"
reporterv2 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v2"
logger "github.com/dwertent/go-logger"
"github.com/dwertent/go-logger/helpers"
reporterv1 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v1"
"github.com/google/uuid"
"github.com/armosec/rbac-utils/rbacscanner"
"github.com/spf13/cobra"
)
var (
rbacExamples = `
# Submit cluster's Role-Based Access Control(RBAC)
kubescape submit rbac
# Submit cluster's Role-Based Access Control(RBAC) with account ID
kubescape submit rbac --account <account-id>
`
)
// getRBACCmd represents the RBAC command
func getRBACCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
return &cobra.Command{
Use: "rbac \nExample:\n$ kubescape submit rbac",
Short: "Submit cluster's Role-Based Access Control(RBAC)",
Long: ``,
Use: "rbac",
Example: rbacExamples,
Short: "Submit cluster's Role-Based Access Control(RBAC)",
Long: ``,
RunE: func(cmd *cobra.Command, args []string) error {
k8s := k8sinterface.NewKubernetesApi()
@@ -32,11 +45,15 @@ func getRBACCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
logger.L().Error("failed setting account ID", helpers.Error(err))
}
if clusterConfig.GetAccountID() == "" {
return fmt.Errorf("account ID is not set, run 'kubescape submit rbac --account <account-id>'")
}
// list RBAC
rbacObjects := cautils.NewRBACObjects(rbacscanner.NewRbacScannerFromK8sAPI(k8s, clusterConfig.GetAccountID(), clusterConfig.GetContextName()))
// submit resources
r := reporterv1.NewReportEventReceiver(clusterConfig.GetConfigObj())
r := reporterv2.NewReportEventReceiver(clusterConfig.GetConfigObj(), uuid.NewString(), reporterv2.SubmitContextRBAC)
submitInterfaces := cliinterfaces.SubmitInterfaces{
ClusterConfig: clusterConfig,
+12 -32
View File
@@ -4,20 +4,18 @@ import (
"encoding/json"
"fmt"
"os"
"time"
reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2"
"github.com/google/uuid"
"github.com/armosec/k8s-interface/workloadinterface"
"github.com/armosec/kubescape/v2/core/meta"
"github.com/armosec/kubescape/v2/core/meta/cliinterfaces"
v1 "github.com/armosec/kubescape/v2/core/meta/datastructures/v1"
"github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter"
reporterv1 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v1"
reporterv2 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v2"
logger "github.com/dwertent/go-logger"
"github.com/dwertent/go-logger/helpers"
"github.com/armosec/opa-utils/reporthandling"
"github.com/google/uuid"
"github.com/spf13/cobra"
)
@@ -37,19 +35,13 @@ func NewResultsObject(customerGUID, clusterName, filePath string) *ResultsObject
}
}
func (resultsObject *ResultsObject) SetResourcesReport() (*reporthandling.PostureReport, error) {
func (resultsObject *ResultsObject) SetResourcesReport() (*reporthandlingv2.PostureReport, error) {
// load framework results from json file
frameworkReports, err := loadResultsFromFile(resultsObject.filePath)
report, err := loadResultsFromFile(resultsObject.filePath)
if err != nil {
return nil, err
}
return &reporthandling.PostureReport{
FrameworkReports: frameworkReports,
ReportID: uuid.NewString(),
ReportGenerationTime: time.Now().UTC(),
CustomerGUID: resultsObject.customerGUID,
ClusterName: resultsObject.clusterName,
}, nil
return report, nil
}
func (resultsObject *ResultsObject) ListAllResources() (map[string]workloadinterface.IMetadata, error) {
@@ -76,15 +68,7 @@ func getResultsCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
resultsObjects := NewResultsObject(clusterConfig.GetAccountID(), clusterConfig.GetContextName(), args[0])
// submit resources
var r reporter.IReport
switch formatVersion {
case "v2":
r = reporterv2.NewReportEventReceiver(clusterConfig.GetConfigObj(), "")
default:
logger.L().Warning("Deprecated results version. run with '--format-version' flag", helpers.String("your version", formatVersion), helpers.String("latest version", "v2"))
r = reporterv1.NewReportEventReceiver(clusterConfig.GetConfigObj())
}
r := reporterv2.NewReportEventReceiver(clusterConfig.GetConfigObj(), uuid.NewString(), reporterv2.SubmitContextScan)
submitInterfaces := cliinterfaces.SubmitInterfaces{
ClusterConfig: clusterConfig,
@@ -102,18 +86,14 @@ func getResultsCmd(ks meta.IKubescape, submitInfo *v1.Submit) *cobra.Command {
return resultsCmd
}
func loadResultsFromFile(filePath string) ([]reporthandling.FrameworkReport, error) {
frameworkReports := []reporthandling.FrameworkReport{}
func loadResultsFromFile(filePath string) (*reporthandlingv2.PostureReport, error) {
report := &reporthandlingv2.PostureReport{}
f, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
if err = json.Unmarshal(f, &frameworkReports); err != nil {
frameworkReport := reporthandling.FrameworkReport{}
if err = json.Unmarshal(f, &frameworkReport); err != nil {
return frameworkReports, err
}
frameworkReports = append(frameworkReports, frameworkReport)
if err = json.Unmarshal(f, report); err != nil {
return report, fmt.Errorf("failed to unmarshal results file: %s, make sure you run kubescape with '--format=json --format-version=v2'", err.Error())
}
return frameworkReports, nil
return report, nil
}
+3 -3
View File
@@ -487,13 +487,13 @@ func getConfigMapNamespace() string {
func getAccountFromEnv(credentials *Credentials) {
// load from env
if accountID := os.Getenv("KS_ACCOUNT_ID"); credentials.Account != "" && accountID != "" {
if accountID := os.Getenv("KS_ACCOUNT_ID"); credentials.Account == "" && accountID != "" {
credentials.Account = accountID
}
if clientID := os.Getenv("KS_CLIENT_ID"); credentials.ClientID != "" && clientID != "" {
if clientID := os.Getenv("KS_CLIENT_ID"); credentials.ClientID == "" && clientID != "" {
credentials.ClientID = clientID
}
if secretKey := os.Getenv("KS_SECRET_KEY"); credentials.SecretKey != "" && secretKey != "" {
if secretKey := os.Getenv("KS_SECRET_KEY"); credentials.SecretKey == "" && secretKey != "" {
credentials.SecretKey = secretKey
}
}
+1 -12
View File
@@ -20,7 +20,6 @@ type OPASessionObj struct {
AllResources map[string]workloadinterface.IMetadata // all scanned resources, map[<rtesource ID>]<resource>
ResourcesResult map[string]resourcesresults.Result // resources scan results, map[<rtesource ID>]<resource result>
ResourceSource map[string]reporthandling.Source // resources sources, map[<rtesource ID>]<resource result>
PostureReport *reporthandling.PostureReport // scan results v1 - Remove
Report *reporthandlingv2.PostureReport // scan results v2 - Remove
Exceptions []armotypes.PostureExceptionPolicy // list of exceptions to apply on scan results
RegoInputData RegoInputData // input passed to rgo for scanning. map[<control name>][<input arguments>]
@@ -41,11 +40,7 @@ func NewOPASessionObj(frameworks []reporthandling.Framework, k8sResources *K8SRe
ResourceToControlsMap: make(map[string][]string),
ResourceSource: make(map[string]reporthandling.Source),
SessionID: scanInfo.ScanID,
PostureReport: &reporthandling.PostureReport{
ClusterName: ClusterName,
CustomerGUID: CustomerGUID,
},
Metadata: scanInfoToScanMetadata(scanInfo),
Metadata: scanInfoToScanMetadata(scanInfo),
}
}
@@ -56,12 +51,6 @@ func NewOPASessionObjMock() *OPASessionObj {
AllResources: make(map[string]workloadinterface.IMetadata),
ResourcesResult: make(map[string]resourcesresults.Result),
Report: &reporthandlingv2.PostureReport{},
PostureReport: &reporthandling.PostureReport{
ClusterName: "",
CustomerGUID: "",
ReportID: "",
JobID: "",
},
Metadata: &reporthandlingv2.Metadata{
ScanMetadata: reporthandlingv2.ScanMetadata{
ScanningTarget: 0,
+8 -5
View File
@@ -30,8 +30,8 @@ const (
JSON_FILE_FORMAT FileFormat = "json"
)
// LoadResourcesFromHelmCharts scans a given path (recuresively) for helm charts, renders the templates and returns a list of workloads
func LoadResourcesFromHelmCharts(basePath string) map[string][]workloadinterface.IMetadata {
// LoadResourcesFromHelmCharts scans a given path (recuresively) for helm charts, renders the templates and returns a map of workloads and a map of chart names
func LoadResourcesFromHelmCharts(basePath string) (map[string][]workloadinterface.IMetadata, map[string]string) {
directories, _ := listDirs(basePath)
helmDirectories := make([]string, 0)
for _, dir := range directories {
@@ -40,7 +40,8 @@ func LoadResourcesFromHelmCharts(basePath string) map[string][]workloadinterface
}
}
result := map[string][]workloadinterface.IMetadata{}
sourceToWorkloads := map[string][]workloadinterface.IMetadata{}
sourceToChartName := map[string]string{}
for _, helmDir := range helmDirectories {
chart, err := NewHelmChart(helmDir)
if err == nil {
@@ -50,12 +51,14 @@ func LoadResourcesFromHelmCharts(basePath string) map[string][]workloadinterface
continue
}
chartName := chart.GetName()
for k, v := range wls {
result[k] = v
sourceToWorkloads[k] = v
sourceToChartName[k] = chartName
}
}
}
return result
return sourceToWorkloads, sourceToChartName
}
func LoadResourcesFromFiles(input, rootPath string) map[string][]workloadinterface.IMetadata {
+2 -1
View File
@@ -44,7 +44,7 @@ func TestLoadResourcesFromFiles(t *testing.T) {
}
func TestLoadResourcesFromHelmCharts(t *testing.T) {
sourceToWorkloads := LoadResourcesFromHelmCharts(helmChartPath())
sourceToWorkloads, sourceToChartName := LoadResourcesFromHelmCharts(helmChartPath())
assert.Equal(t, 6, len(sourceToWorkloads))
for file, workloads := range sourceToWorkloads {
@@ -52,6 +52,7 @@ func TestLoadResourcesFromHelmCharts(t *testing.T) {
w := workloads[0]
assert.True(t, localworkload.IsTypeLocalWorkload(w.GetObject()), "Expected localworkload as object type")
assert.Equal(t, "kubescape", sourceToChartName[file])
switch filepath.Base(file) {
case "serviceaccount.yaml":
-3
View File
@@ -1,6 +1,3 @@
//go:build !windows
// +build !windows
package cautils
import (
+16 -17
View File
@@ -6,7 +6,6 @@ import (
"io"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
@@ -148,29 +147,29 @@ func (s *LocalGitRepositoryTestSuite) TestGetLastCommit() {
func (s *LocalGitRepositoryTestSuite) TestGetFileLastCommit() {
s.Run("fileA", func() {
if localRepo, err := NewLocalGitRepository(s.gitRepositoryPaths["localrepo"]); s.NoError(err) {
if runtime.GOOS != "windows" {
if commit, err := localRepo.GetFileLastCommit("fileA"); s.NoError(err) {
s.Equal("9fae4be19624297947d2b605cefbff516628612d", commit.SHA)
s.Equal("Amir Malka", commit.Author.Name)
s.Equal("amirm@armosec.io", commit.Author.Email)
s.Equal("2022-05-22 18:55:48 +0300 +0300", commit.Author.Date.String())
s.Equal("added file A\n", commit.Message)
}
if commit, err := localRepo.GetFileLastCommit("fileA"); s.NoError(err) {
s.Equal("9fae4be19624297947d2b605cefbff516628612d", commit.SHA)
s.Equal("Amir Malka", commit.Author.Name)
s.Equal("amirm@armosec.io", commit.Author.Email)
s.Equal("2022-05-22 18:55:48 +0300 +0300", commit.Author.Date.String())
s.Equal("added file A\n", commit.Message)
}
}
})
s.Run("fileB", func() {
if localRepo, err := NewLocalGitRepository(s.gitRepositoryPaths["localrepo"]); s.NoError(err) {
if runtime.GOOS != "windows" {
if commit, err := localRepo.GetFileLastCommit("dirA/fileB"); s.NoError(err) {
s.Equal("7e09312b8017695fadcd606882e3779f10a5c832", commit.SHA)
s.Equal("Amir Malka", commit.Author.Name)
s.Equal("amirm@armosec.io", commit.Author.Email)
s.Equal("2022-05-22 19:11:57 +0300 +0300", commit.Author.Date.String())
s.Equal("added file B\n", commit.Message)
}
if commit, err := localRepo.GetFileLastCommit("dirA/fileB"); s.NoError(err) {
s.Equal("7e09312b8017695fadcd606882e3779f10a5c832", commit.SHA)
s.Equal("Amir Malka", commit.Author.Name)
s.Equal("amirm@armosec.io", commit.Author.Email)
s.Equal("2022-05-22 19:11:57 +0300 +0300", commit.Author.Date.String())
s.Equal("added file B\n", commit.Message)
}
}
})
}
-125
View File
@@ -1,125 +0,0 @@
//go:build windows
// +build windows
package cautils
import (
"fmt"
"path"
"strings"
"github.com/armosec/go-git-url/apis"
gitv5 "github.com/go-git/go-git/v5"
configv5 "github.com/go-git/go-git/v5/config"
plumbingv5 "github.com/go-git/go-git/v5/plumbing"
)
type LocalGitRepository struct {
goGitRepo *gitv5.Repository
head *plumbingv5.Reference
config *configv5.Config
}
func NewLocalGitRepository(path string) (*LocalGitRepository, error) {
goGitRepo, err := gitv5.PlainOpenWithOptions(path, &gitv5.PlainOpenOptions{DetectDotGit: true})
if err != nil {
return nil, err
}
head, err := goGitRepo.Head()
if err != nil {
return nil, err
}
if !head.Name().IsBranch() {
return nil, fmt.Errorf("current HEAD reference is not a branch")
}
config, err := goGitRepo.Config()
if err != nil {
return nil, err
}
if len(config.Remotes) == 0 {
return nil, fmt.Errorf("no remotes found")
}
return &LocalGitRepository{
goGitRepo: goGitRepo,
head: head,
config: config,
}, nil
}
// GetBranchName get current branch name
func (g *LocalGitRepository) GetBranchName() string {
return g.head.Name().Short()
}
// GetRemoteUrl get default remote URL
func (g *LocalGitRepository) GetRemoteUrl() (string, error) {
branchName := g.GetBranchName()
if branchRef, branchFound := g.config.Branches[branchName]; branchFound {
remoteName := branchRef.Remote
if len(g.config.Remotes[remoteName].URLs) == 0 {
return "", fmt.Errorf("expected to find URLs for remote '%s', branch '%s'", remoteName, branchName)
}
return g.config.Remotes[remoteName].URLs[0], nil
}
const defaultRemoteName string = "origin"
if len(g.config.Remotes[defaultRemoteName].URLs) == 0 {
return "", fmt.Errorf("expected to find URLs for remote '%s'", defaultRemoteName)
}
return g.config.Remotes[defaultRemoteName].URLs[0], nil
}
// GetName get origin name without the .git suffix
func (g *LocalGitRepository) GetName() (string, error) {
originUrl, err := g.GetRemoteUrl()
if err != nil {
return "", err
}
baseName := path.Base(originUrl)
// remove .git
return strings.TrimSuffix(baseName, ".git"), nil
}
// GetLastCommit get latest commit object
func (g *LocalGitRepository) GetLastCommit() (*apis.Commit, error) {
cIter, err := g.goGitRepo.Log(&gitv5.LogOptions{})
if err != nil {
return nil, err
}
commit, err := cIter.Next()
defer cIter.Close()
if err != nil {
return nil, err
}
return &apis.Commit{
SHA: commit.Hash.String(),
Author: apis.Committer{
Name: commit.Author.Name,
Email: commit.Author.Email,
Date: commit.Author.When,
},
Message: commit.Message,
Committer: apis.Committer{},
Files: []apis.Files{},
}, nil
}
func (g *LocalGitRepository) GetFileLastCommit(filePath string) (*apis.Commit, error) {
return nil, nil
}
func (g *LocalGitRepository) GetRootDir() (string, error) {
wt, err := g.goGitRepo.Worktree()
if err != nil {
return "", fmt.Errorf("failed to get repo root")
}
return wt.Filesystem.Root(), nil
}
+12 -4
View File
@@ -4,8 +4,9 @@ import (
"encoding/json"
"time"
reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2"
"github.com/armosec/k8s-interface/workloadinterface"
"github.com/armosec/opa-utils/reporthandling"
"github.com/armosec/rbac-utils/rbacscanner"
"github.com/armosec/rbac-utils/rbacutils"
"github.com/google/uuid"
@@ -19,12 +20,19 @@ func NewRBACObjects(scanner *rbacscanner.RbacScannerFromK8sAPI) *RBACObjects {
return &RBACObjects{scanner: scanner}
}
func (rbacObjects *RBACObjects) SetResourcesReport() (*reporthandling.PostureReport, error) {
return &reporthandling.PostureReport{
func (rbacObjects *RBACObjects) SetResourcesReport() (*reporthandlingv2.PostureReport, error) {
return &reporthandlingv2.PostureReport{
ReportID: uuid.NewString(),
ReportGenerationTime: time.Now().UTC(),
CustomerGUID: rbacObjects.scanner.CustomerGUID,
ClusterName: rbacObjects.scanner.ClusterName,
Metadata: reporthandlingv2.Metadata{
ContextMetadata: reporthandlingv2.ContextMetadata{
ClusterContextMetadata: &reporthandlingv2.ClusterMetadata{
ContextName: rbacObjects.scanner.ClusterName,
},
},
},
}, nil
}
@@ -53,7 +61,7 @@ func (rbacObjects *RBACObjects) rbacObjectsToResources(resources *rbacutils.Rbac
************************************************************************************************************************
*/
// wrap rbac aggregated objects in IMetadata and add to allresources
// wrap rbac aggregated objects in IMetadata and add to AllResources
// TODO - DEPRECATE SA2WLIDmap
SA2WLIDmapIMeta, err := rbacutils.SA2WLIDmapIMetadataWrapper(resources.SA2WLIDmap)
if err != nil {
+4 -5
View File
@@ -7,10 +7,8 @@ import (
"github.com/armosec/opa-utils/reporthandling/results/v1/reportsummary"
)
func ReportV2ToV1(opaSessionObj *OPASessionObj) {
if len(opaSessionObj.PostureReport.FrameworkReports) > 0 {
return // report already converted
}
func ReportV2ToV1(opaSessionObj *OPASessionObj) *reporthandling.PostureReport {
report := &reporthandling.PostureReport{}
// opaSessionObj.PostureReport.ClusterCloudProvider = opaSessionObj.Report.ClusterCloudProvider
frameworks := []reporthandling.FrameworkReport{}
@@ -53,7 +51,8 @@ func ReportV2ToV1(opaSessionObj *OPASessionObj) {
// scoreutil := score.NewScore(opaSessionObj.AllResources)
// scoreutil.Calculate(frameworks)
opaSessionObj.PostureReport.FrameworkReports = frameworks
report.FrameworkReports = frameworks
return report
}
func controlReportV2ToV1(opaSessionObj *OPASessionObj, frameworkName string, controls map[string]reportsummary.ControlSummary) []reporthandling.ControlReport {
+6 -2
View File
@@ -48,9 +48,13 @@ func getRBACHandler(tenantConfig cautils.ITenantConfig, k8s *k8sinterface.Kubern
return nil
}
func getReporter(tenantConfig cautils.ITenantConfig, reportID string, submit, fwScan bool) reporter.IReport {
func getReporter(tenantConfig cautils.ITenantConfig, reportID string, submit, fwScan bool, scanningContext cautils.ScanningContext) reporter.IReport {
if submit {
return reporterv2.NewReportEventReceiver(tenantConfig.GetConfigObj(), reportID)
submitData := reporterv2.SubmitContextScan
if scanningContext != cautils.ContextCluster {
submitData = reporterv2.SubmitContextRepository
}
return reporterv2.NewReportEventReceiver(tenantConfig.GetConfigObj(), reportID, submitData)
}
if tenantConfig.GetAccountID() == "" {
// Add link only when scanning a cluster using a framework
+1 -1
View File
@@ -86,7 +86,7 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
// ================== setup reporter & printer objects ======================================
// reporting behavior - setup reporter
reportHandler := getReporter(tenantConfig, scanInfo.ScanID, scanInfo.Submit, scanInfo.FrameworkScan)
reportHandler := getReporter(tenantConfig, scanInfo.ScanID, scanInfo.Submit, scanInfo.FrameworkScan, scanInfo.GetScanningContext())
// setup printer
printerHandler := resultshandling.NewPrinter(scanInfo.Format, scanInfo.FormatVersion, scanInfo.VerboseMode, cautils.ViewTypes(scanInfo.View))
+8 -2
View File
@@ -4,6 +4,7 @@ import (
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/armosec/kubescape/v2/core/cautils/getter"
"github.com/armosec/kubescape/v2/core/meta/cliinterfaces"
logger "github.com/dwertent/go-logger"
"github.com/dwertent/go-logger/helpers"
)
@@ -11,7 +12,7 @@ import (
func (ks *Kubescape) Submit(submitInterfaces cliinterfaces.SubmitInterfaces) error {
// list resources
postureReport, err := submitInterfaces.SubmitObjects.SetResourcesReport()
report, err := submitInterfaces.SubmitObjects.SetResourcesReport()
if err != nil {
return err
}
@@ -20,7 +21,12 @@ func (ks *Kubescape) Submit(submitInterfaces cliinterfaces.SubmitInterfaces) err
return err
}
// report
if err := submitInterfaces.Reporter.Submit(&cautils.OPASessionObj{PostureReport: postureReport, AllResources: allresources}); err != nil {
o := &cautils.OPASessionObj{
Report: report,
AllResources: allresources,
Metadata: &report.Metadata,
}
if err := submitInterfaces.Reporter.Submit(o); err != nil {
return err
}
logger.L().Success("Data has been submitted successfully")
+2 -2
View File
@@ -4,11 +4,11 @@ import (
"github.com/armosec/k8s-interface/workloadinterface"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter"
"github.com/armosec/opa-utils/reporthandling"
reporthandlingv2 "github.com/armosec/opa-utils/reporthandling/v2"
)
type ISubmitObjects interface {
SetResourcesReport() (*reporthandling.PostureReport, error)
SetResourcesReport() (*reporthandlingv2.PostureReport, error)
ListAllResources() (map[string]workloadinterface.IMetadata, error)
}
@@ -6,12 +6,10 @@ import (
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/armosec/kubescape/v2/core/mocks"
"github.com/armosec/opa-utils/objectsenvelopes"
"github.com/armosec/opa-utils/reporthandling"
"github.com/armosec/opa-utils/resources"
"github.com/stretchr/testify/assert"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/k8s-interface/workloadinterface"
// _ "k8s.io/client-go/plugin/pkg/client/auth"
)
@@ -19,43 +17,6 @@ import (
func NewOPAProcessorMock() *OPAProcessor {
return &OPAProcessor{}
}
func TestProcess(t *testing.T) {
// set k8s
k8sResources := make(cautils.K8SResources)
allResources := make(map[string]workloadinterface.IMetadata)
imetaObj := objectsenvelopes.ListMapToMeta(k8sinterface.ConvertUnstructuredSliceToMap(k8sinterface.V1KubeSystemNamespaceMock().Items))
for i := range imetaObj {
allResources[imetaObj[i].GetID()] = imetaObj[i]
}
k8sResources["/v1/pods"] = workloadinterface.ListMetaIDs(imetaObj)
// set opaSessionObj
opaSessionObj := cautils.NewOPASessionObjMock()
opaSessionObj.Policies = []reporthandling.Framework{*reporthandling.MockFrameworkA()}
policies := ConvertFrameworksToPolicies(opaSessionObj.Policies, "")
opaSessionObj.K8SResources = &k8sResources
opaSessionObj.AllResources = allResources
opap := NewOPAProcessor(opaSessionObj, resources.NewRegoDependenciesDataMock()) // ,
opap.Process(policies)
opap.updateResults()
for _, f := range opap.PostureReport.FrameworkReports {
for _, c := range f.ControlReports {
for _, r := range c.RuleReports {
for _, rr := range r.RuleResponses {
// t.Errorf("AlertMessage: %v", rr.AlertMessage)
if rr.Exception != nil {
t.Errorf("Exception: %v", rr.Exception)
}
}
}
}
}
}
func TestProcessResourcesResult(t *testing.T) {
// set k8s
+1 -1
View File
@@ -7,7 +7,7 @@ For these controls to work properly, it is necessary to
# Integrate With Armosec Server
1. Navigate to the [armosec.io](https://portal.armo.cloud/)
1. Navigate to the [armosec.io](https://cloud.armosec.io?utm_source=github&utm_medium=repository)
2. Click Profile(top right icon)->"User Management"->"API Tokens" and Generate a token
3. Copy the clientID and secretKey and run:
```
+6 -4
View File
@@ -116,9 +116,10 @@ func (fileHandler *FileResourceHandler) GetResources(sessionObj *cautils.OPASess
}
// load resources from helm charts
helmSourceToWorkloads := cautils.LoadResourcesFromHelmCharts(path)
helmSourceToWorkloads, helmSourceToChartName := cautils.LoadResourcesFromHelmCharts(path)
for source, ws := range helmSourceToWorkloads {
workloads = append(workloads, ws...)
helmChartName := helmSourceToChartName[source]
relSource, err := filepath.Rel(repoRoot, source)
if err == nil {
@@ -140,9 +141,10 @@ func (fileHandler *FileResourceHandler) GetResources(sessionObj *cautils.OPASess
}
workloadSource := reporthandling.Source{
RelativePath: source,
FileType: reporthandling.SourceTypeHelmChart,
LastCommit: lastCommit,
RelativePath: source,
FileType: reporthandling.SourceTypeHelmChart,
HelmChartName: helmChartName,
LastCommit: lastCommit,
}
for i := range ws {
+1 -1
View File
@@ -86,7 +86,7 @@ func (k8sHandler *K8sResourceHandler) GetResources(sessionObj *cautils.OPASessio
if len(imgVulnResources) > 0 {
if err := k8sHandler.registryAdaptors.collectImagesVulnerabilities(k8sResourcesMap, allResources, armoResourceMap); err != nil {
logger.L().Warning("failed to collect image vulnerabilities", helpers.Error(err))
cautils.SetInfoMapForResources(fmt.Sprintf("failed to pull image scanning data: %s", err.Error()), imgVulnResources, sessionObj.InfoMap)
cautils.SetInfoMapForResources(fmt.Sprintf("failed to pull image scanning data: %s. for more information: https://hub.armosec.io/docs/configuration-of-image-vulnerabilities", err.Error()), imgVulnResources, sessionObj.InfoMap)
} else {
if isEmptyImgVulns(*armoResourceMap) {
cautils.SetInfoMapForResources("image scanning is not configured. for more information: https://hub.armosec.io/docs/configuration-of-image-vulnerabilities", imgVulnResources, sessionObj.InfoMap)
+10 -5
View File
@@ -38,6 +38,16 @@ func NewRegistryAdaptors() (*RegistryAdaptors, error) {
func (registryAdaptors *RegistryAdaptors) collectImagesVulnerabilities(k8sResourcesMap *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata, armoResourceMap *cautils.ArmoResources) error {
logger.L().Debug("Collecting images vulnerabilities")
if len(registryAdaptors.adaptors) == 0 {
return fmt.Errorf("credentials are not configured for any registry adaptor")
}
for i := range registryAdaptors.adaptors { // login and and get vulnerabilities
if err := registryAdaptors.adaptors[i].Login(); err != nil {
return fmt.Errorf("failed to login, adaptor: '%s', reason: '%s'", registryAdaptors.adaptors[i].DescribeAdaptor(), err.Error())
}
}
// list cluster images
images := listImagesTags(k8sResourcesMap, allResources)
imagesIdentifiers := imageTagsToContainerImageIdentifier(images)
@@ -45,11 +55,6 @@ func (registryAdaptors *RegistryAdaptors) collectImagesVulnerabilities(k8sResour
imagesVulnerability := map[string][]registryvulnerabilities.Vulnerability{}
for i := range registryAdaptors.adaptors { // login and and get vulnerabilities
if err := registryAdaptors.adaptors[i].Login(); err != nil {
if err != nil {
return fmt.Errorf("failed to login, adaptor: '%s', reason: '%s'", registryAdaptors.adaptors[i].DescribeAdaptor(), err.Error())
}
}
vulnerabilities, err := registryAdaptors.adaptors[i].GetImagesVulnerabilities(imagesIdentifiers)
if err != nil {
return err
@@ -27,15 +27,15 @@ func (jsonPrinter *JsonPrinter) Score(score float32) {
}
func (jsonPrinter *JsonPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
cautils.ReportV2ToV1(opaSessionObj)
report := cautils.ReportV2ToV1(opaSessionObj)
var postureReportStr []byte
var err error
if len(opaSessionObj.PostureReport.FrameworkReports) == 1 {
postureReportStr, err = json.Marshal(opaSessionObj.PostureReport.FrameworkReports[0])
if len(report.FrameworkReports) == 1 {
postureReportStr, err = json.Marshal(report.FrameworkReports[0])
} else {
postureReportStr, err = json.Marshal(opaSessionObj.PostureReport.FrameworkReports)
postureReportStr, err = json.Marshal(report.FrameworkReports)
}
if err != nil {
@@ -87,9 +87,9 @@ func (printer *PrometheusPrinter) printReports(allResources map[string]workloadi
}
func (printer *PrometheusPrinter) ActionPrint(opaSessionObj *cautils.OPASessionObj) {
cautils.ReportV2ToV1(opaSessionObj)
report := cautils.ReportV2ToV1(opaSessionObj)
err := printer.printReports(opaSessionObj.AllResources, opaSessionObj.PostureReport.FrameworkReports)
err := printer.printReports(opaSessionObj.AllResources, report.FrameworkReports)
if err != nil {
logger.L().Fatal(err.Error())
}
@@ -1,185 +0,0 @@
package v1
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"os"
"github.com/armosec/k8s-interface/workloadinterface"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/armosec/kubescape/v2/core/cautils/getter"
v2 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v2"
"github.com/armosec/opa-utils/reporthandling"
logger "github.com/dwertent/go-logger"
"github.com/dwertent/go-logger/helpers"
"github.com/google/uuid"
)
const MAX_REPORT_SIZE = 2097152 // 2 MB
type ReportEventReceiver struct {
httpClient *http.Client
clusterName string
customerGUID string
eventReceiverURL *url.URL
token string
customerAdminEMail string
message string
}
func NewReportEventReceiver(tenantConfig *cautils.ConfigObj) *ReportEventReceiver {
return &ReportEventReceiver{
httpClient: &http.Client{},
clusterName: tenantConfig.ClusterName,
customerGUID: tenantConfig.AccountID,
token: tenantConfig.Token,
customerAdminEMail: tenantConfig.CustomerAdminEMail,
}
}
func (report *ReportEventReceiver) Submit(opaSessionObj *cautils.OPASessionObj) error {
if opaSessionObj.PostureReport == nil && opaSessionObj.Report != nil {
cautils.ReportV2ToV1(opaSessionObj)
}
if report.customerGUID == "" {
report.message = "WARNING: Failed to publish results. Reason: Unknown accout ID. Run kubescape with the '--account <account ID>' flag. Please feel free to contact ARMO team for more details"
return nil
}
if report.clusterName == "" {
report.message = "WARNING: Failed to publish results because the cluster name is Unknown. If you are scanning YAML files the results are not submitted to the Kubescape SaaS.Please feel free to contact ARMO team for more details"
return nil
}
opaSessionObj.PostureReport.ReportID = uuid.NewString()
opaSessionObj.PostureReport.CustomerGUID = report.customerGUID
opaSessionObj.PostureReport.ClusterName = report.clusterName
if err := report.prepareReport(opaSessionObj.PostureReport, opaSessionObj.AllResources); err != nil {
report.message = err.Error()
return nil
}
return nil
}
func (report *ReportEventReceiver) SetCustomerGUID(customerGUID string) {
report.customerGUID = customerGUID
}
func (report *ReportEventReceiver) SetClusterName(clusterName string) {
report.clusterName = cautils.AdoptClusterName(clusterName) // clean cluster name
}
func (report *ReportEventReceiver) prepareReport(postureReport *reporthandling.PostureReport, allResources map[string]workloadinterface.IMetadata) error {
report.initEventReceiverURL()
host := hostToString(report.eventReceiverURL, postureReport.ReportID)
cautils.StartSpinner()
// send framework results
if err := report.sendReport(host, postureReport); err != nil {
cautils.StopSpinner()
return err
}
// send resources
if err := report.sendResources(host, postureReport, allResources); err != nil {
cautils.StopSpinner()
return err
}
cautils.StopSpinner()
report.generateMessage()
return nil
}
func (report *ReportEventReceiver) sendResources(host string, postureReport *reporthandling.PostureReport, allResources map[string]workloadinterface.IMetadata) error {
splittedPostureReport := setPaginationReport(postureReport)
counter := 0
for _, v := range allResources {
r, err := json.Marshal(*iMetaToResource(v))
if err != nil {
return fmt.Errorf("failed to unmarshal resource '%s', reason: %v", v.GetID(), err)
}
if counter+len(r) >= MAX_REPORT_SIZE && len(splittedPostureReport.Resources) > 0 {
// send report
if err := report.sendReport(host, splittedPostureReport); err != nil {
return err
}
// delete resources
splittedPostureReport.Resources = []reporthandling.Resource{}
// restart counter
counter = 0
}
counter += len(r)
splittedPostureReport.Resources = append(splittedPostureReport.Resources, *iMetaToResource(v))
}
return report.sendReport(host, splittedPostureReport)
}
func (report *ReportEventReceiver) sendReport(host string, postureReport *reporthandling.PostureReport) error {
reqBody, err := json.Marshal(postureReport)
if err != nil {
return fmt.Errorf("in 'sendReport' failed to json.Marshal, reason: %v", err)
}
msg, err := getter.HttpPost(report.httpClient, host, nil, reqBody)
if err != nil {
return fmt.Errorf("%s, %v:%s", host, err, msg)
}
return nil
}
func (report *ReportEventReceiver) generateMessage() {
message := "You can see the results in a user-friendly UI, choose your preferred compliance framework, check risk results history and trends, manage exceptions, get remediation recommendations and much more by registering here:"
u := url.URL{}
u.Host = getter.GetArmoAPIConnector().GetFrontendURL()
v2.ParseHost(&u)
if report.customerAdminEMail != "" {
logger.L().Debug("", helpers.String("account ID", report.customerGUID))
report.message = fmt.Sprintf("%s %s/configuration-scanning/%s", message, u.String(), report.clusterName)
return
}
u.Path = "account/sign-up"
q := u.Query()
q.Add("invitationToken", report.token)
q.Add("customerGUID", report.customerGUID)
u.RawQuery = q.Encode()
report.message = fmt.Sprintf("%s %s", message, u.String())
}
func (report *ReportEventReceiver) GetURL() string {
return getter.GetArmoAPIConnector().GetFrontendURL()
}
func (report *ReportEventReceiver) DisplayReportURL() {
cautils.InfoTextDisplay(os.Stderr, fmt.Sprintf("\n\n%s\n\n", report.message))
}
// func maskID(id string) string {
// sep := "-"
// splitted := strings.Split(id, sep)
// if len(splitted) != 5 {
// return ""
// }
// str := splitted[0][:4]
// splitted[0] = splitted[0][4:]
// for i := range splitted {
// for j := 0; j < len(splitted[i]); j++ {
// str += "X"
// }
// str += sep
// }
// return strings.TrimSuffix(str, sep)
// }
@@ -1,48 +0,0 @@
package v1
import (
"net/url"
"github.com/armosec/k8s-interface/workloadinterface"
"github.com/armosec/kubescape/v2/core/cautils/getter"
v2 "github.com/armosec/kubescape/v2/core/pkg/resultshandling/reporter/v2"
"github.com/armosec/opa-utils/reporthandling"
"github.com/google/uuid"
)
func (report *ReportEventReceiver) initEventReceiverURL() {
urlObj := url.URL{}
urlObj.Host = getter.GetArmoAPIConnector().GetReportReceiverURL()
v2.ParseHost(&urlObj)
urlObj.Path = "/k8s/postureReport"
q := urlObj.Query()
q.Add("customerGUID", uuid.MustParse(report.customerGUID).String())
q.Add("clusterName", report.clusterName)
urlObj.RawQuery = q.Encode()
report.eventReceiverURL = &urlObj
}
func hostToString(host *url.URL, reportID string) string {
q := host.Query()
q.Add("reportID", reportID) // TODO - do we add the reportID?
host.RawQuery = q.Encode()
return host.String()
}
func setPaginationReport(postureReport *reporthandling.PostureReport) *reporthandling.PostureReport {
return &reporthandling.PostureReport{
CustomerGUID: postureReport.CustomerGUID,
ClusterName: postureReport.ClusterName,
ReportID: postureReport.ReportID,
ReportGenerationTime: postureReport.ReportGenerationTime,
}
}
func iMetaToResource(obj workloadinterface.IMetadata) *reporthandling.Resource {
return &reporthandling.Resource{
ResourceID: obj.GetID(),
Object: obj.GetObject(),
}
}
@@ -1,20 +0,0 @@
package v1
import (
"net/url"
"testing"
)
func TestHostToString(t *testing.T) {
host := url.URL{
Scheme: "https",
Host: "report.eudev3.cyberarmorsoft.com",
Path: "k8srestapi/v1/postureReport",
RawQuery: "cluster=openrasty_seal-7fvz&customerGUID=5d817063-096f-4d91-b39b-8665240080af",
}
expectedHost := "https://report.eudev3.cyberarmorsoft.com/k8srestapi/v1/postureReport?cluster=openrasty_seal-7fvz&customerGUID=5d817063-096f-4d91-b39b-8665240080af&reportID=ffdd2a00-4dc8-4bf3-b97a-a6d4fd198a41"
receivedHost := hostToString(&host, "ffdd2a00-4dc8-4bf3-b97a-a6d4fd198a41")
if receivedHost != expectedHost {
t.Errorf("%s != %s", receivedHost, expectedHost)
}
}
@@ -1,48 +0,0 @@
package v2
import (
"testing"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/stretchr/testify/assert"
)
func TestGetURL(t *testing.T) {
// Test submit and registered url
{
reporter := NewReportEventReceiver(
&cautils.ConfigObj{
AccountID: "1234",
Token: "token",
CustomerAdminEMail: "my@email",
ClusterName: "test",
},
"",
)
assert.Equal(t, "https://cloud.armosec.io/configuration-scanning/test?utm_campaign=Submit&utm_medium=CLI&utm_source=GitHub", reporter.GetURL())
}
// Test submit and NOT registered url
{
reporter := NewReportEventReceiver(
&cautils.ConfigObj{
AccountID: "1234",
Token: "token",
ClusterName: "test",
},
"",
)
assert.Equal(t, "https://cloud.armosec.io/account/sign-up?customerGUID=1234&invitationToken=token&utm_campaign=Submit&utm_medium=CLI&utm_source=GitHub", reporter.GetURL())
}
// Test None submit url
{
reporter := NewReportMock(NO_SUBMIT_QUERY, "")
assert.Equal(t, "https://cloud.armosec.io/account/sign-up?utm_source=GitHub&utm_medium=CLI&utm_campaign=no_submit", reporter.GetURL())
}
// Test None report url
{
reporter := NewReportMock("", "")
assert.Equal(t, "https://cloud.armosec.io/account/sign-up", reporter.GetURL())
}
}
@@ -20,6 +20,14 @@ import (
const MAX_REPORT_SIZE = 2097152 // 2 MB
type SubmitContext string
const (
SubmitContextScan SubmitContext = "scan"
SubmitContextRBAC SubmitContext = "rbac"
SubmitContextRepository SubmitContext = "repository"
)
type ReportEventReceiver struct {
httpClient *http.Client
clusterName string
@@ -29,9 +37,10 @@ type ReportEventReceiver struct {
customerAdminEMail string
message string
reportID string
submitContext SubmitContext
}
func NewReportEventReceiver(tenantConfig *cautils.ConfigObj, reportID string) *ReportEventReceiver {
func NewReportEventReceiver(tenantConfig *cautils.ConfigObj, reportID string, submitContext SubmitContext) *ReportEventReceiver {
return &ReportEventReceiver{
httpClient: &http.Client{},
clusterName: tenantConfig.ClusterName,
@@ -39,6 +48,7 @@ func NewReportEventReceiver(tenantConfig *cautils.ConfigObj, reportID string) *R
token: tenantConfig.Token,
customerAdminEMail: tenantConfig.CustomerAdminEMail,
reportID: reportID,
submitContext: submitContext,
}
}
@@ -95,21 +105,11 @@ func (report *ReportEventReceiver) prepareReport(opaSessionObj *cautils.OPASessi
func (report *ReportEventReceiver) GetURL() string {
u := url.URL{}
u.Host = getter.GetArmoAPIConnector().GetFrontendURL()
ParseHost(&u)
parseHost(&u)
report.addPathURL(&u)
q := u.Query()
if report.customerAdminEMail != "" || report.token == "" { // data has been submitted
if report.clusterName != "" {
u.Path = fmt.Sprintf("configuration-scanning/%s", report.clusterName)
} else {
u.Path = fmt.Sprintf("repositories-scan/%s", report.reportID)
}
} else {
u.Path = "account/sign-up"
q.Add("invitationToken", report.token)
q.Add("customerGUID", report.customerGUID)
}
q.Add("utm_source", "GitHub")
q.Add("utm_medium", "CLI")
q.Add("utm_campaign", "Submit")
@@ -226,3 +226,26 @@ func (report *ReportEventReceiver) DisplayReportURL() {
cautils.InfoTextDisplay(os.Stderr, fmt.Sprintf("\n\n%s\n\n", report.message))
}
}
func (report *ReportEventReceiver) addPathURL(urlObj *url.URL) {
if report.customerAdminEMail != "" || report.token == "" { // data has been submitted
switch report.submitContext {
case SubmitContextScan:
urlObj.Path = fmt.Sprintf("configuration-scanning/%s", report.clusterName)
case SubmitContextRBAC:
urlObj.Path = "rbac-visualizer"
case SubmitContextRepository:
urlObj.Path = fmt.Sprintf("repository-scanning/%s", report.reportID)
default:
urlObj.Path = "dashboard"
}
return
}
urlObj.Path = "account/sign-up"
q := urlObj.Query()
q.Add("invitationToken", report.token)
q.Add("customerGUID", report.customerGUID)
urlObj.RawQuery = q.Encode()
}
@@ -0,0 +1,118 @@
package v2
import (
"net/url"
"testing"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/stretchr/testify/assert"
)
func TestReportEventReceiver_addPathURL(t *testing.T) {
tests := []struct {
name string
report *ReportEventReceiver
urlObj *url.URL
want *url.URL
}{
{
name: "add scan path",
report: &ReportEventReceiver{
clusterName: "test",
customerGUID: "FFFF",
token: "XXXX",
customerAdminEMail: "test@test",
reportID: "1234",
submitContext: SubmitContextScan,
},
urlObj: &url.URL{
Scheme: "https",
Host: "localhost:8080",
},
want: &url.URL{
Scheme: "https",
Host: "localhost:8080",
Path: "configuration-scanning/test",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.report.addPathURL(tt.urlObj)
assert.Equal(t, tt.want.String(), tt.urlObj.String())
})
}
}
func TestGetURL(t *testing.T) {
// Test submit and registered url
{
reporter := NewReportEventReceiver(
&cautils.ConfigObj{
AccountID: "1234",
Token: "token",
CustomerAdminEMail: "my@email",
ClusterName: "test",
},
"",
SubmitContextScan,
)
assert.Equal(t, "https://cloud.armosec.io/configuration-scanning/test?utm_campaign=Submit&utm_medium=CLI&utm_source=GitHub", reporter.GetURL())
}
// Test rbac submit and registered url
{
reporter := NewReportEventReceiver(
&cautils.ConfigObj{
AccountID: "1234",
Token: "token",
CustomerAdminEMail: "my@email",
ClusterName: "test",
},
"",
SubmitContextRBAC,
)
assert.Equal(t, "https://cloud.armosec.io/rbac-visualizer?utm_campaign=Submit&utm_medium=CLI&utm_source=GitHub", reporter.GetURL())
}
// Test repo submit and registered url
{
reporter := NewReportEventReceiver(
&cautils.ConfigObj{
AccountID: "1234",
Token: "token",
CustomerAdminEMail: "my@email",
ClusterName: "test",
},
"XXXX",
SubmitContextRepository,
)
assert.Equal(t, "https://cloud.armosec.io/repository-scanning/XXXX?utm_campaign=Submit&utm_medium=CLI&utm_source=GitHub", reporter.GetURL())
}
// Test submit and NOT registered url
{
reporter := NewReportEventReceiver(
&cautils.ConfigObj{
AccountID: "1234",
Token: "token",
ClusterName: "test",
},
"",
SubmitContextScan,
)
assert.Equal(t, "https://cloud.armosec.io/account/sign-up?customerGUID=1234&invitationToken=token&utm_campaign=Submit&utm_medium=CLI&utm_source=GitHub", reporter.GetURL())
}
// Test None submit url
{
reporter := NewReportMock(NO_SUBMIT_QUERY, "")
assert.Equal(t, "https://cloud.armosec.io/account/sign-up?utm_source=GitHub&utm_medium=CLI&utm_campaign=no_submit", reporter.GetURL())
}
// Test None report url
{
reporter := NewReportMock("", "")
assert.Equal(t, "https://cloud.armosec.io/account/sign-up", reporter.GetURL())
}
}
@@ -12,7 +12,8 @@ import (
func (report *ReportEventReceiver) initEventReceiverURL() {
urlObj := url.URL{}
urlObj.Host = getter.GetArmoAPIConnector().GetReportReceiverURL()
ParseHost(&urlObj)
parseHost(&urlObj)
urlObj.Path = "/k8s/v2/postureReport"
q := urlObj.Query()
q.Add("customerGUID", uuid.MustParse(report.customerGUID).String())
@@ -23,7 +23,7 @@ func maskID(id string) string {
return strings.TrimSuffix(str, sep)
}
func ParseHost(urlObj *url.URL) {
func parseHost(urlObj *url.URL) {
if strings.Contains(urlObj.Host, "http://") {
urlObj.Scheme = "http"
urlObj.Host = strings.Replace(urlObj.Host, "http://", "", 1)
@@ -11,27 +11,27 @@ func TestParseHost(t *testing.T) {
urlObj := url.URL{}
urlObj.Host = "http://localhost:7555"
ParseHost(&urlObj)
parseHost(&urlObj)
assert.Equal(t, "http", urlObj.Scheme)
assert.Equal(t, "localhost:7555", urlObj.Host)
urlObj.Host = "https://localhost:7555"
ParseHost(&urlObj)
parseHost(&urlObj)
assert.Equal(t, "https", urlObj.Scheme)
assert.Equal(t, "localhost:7555", urlObj.Host)
urlObj.Host = "http://portal-dev.armo.cloud"
ParseHost(&urlObj)
parseHost(&urlObj)
assert.Equal(t, "http", urlObj.Scheme)
assert.Equal(t, "portal-dev.armo.cloud", urlObj.Host)
urlObj.Host = "https://portal-dev.armo.cloud"
ParseHost(&urlObj)
parseHost(&urlObj)
assert.Equal(t, "https", urlObj.Scheme)
assert.Equal(t, "portal-dev.armo.cloud", urlObj.Host)
urlObj.Host = "portal-dev.armo.cloud"
ParseHost(&urlObj)
parseHost(&urlObj)
assert.Equal(t, "https", urlObj.Scheme)
assert.Equal(t, "portal-dev.armo.cloud", urlObj.Host)
-2
View File
@@ -26,8 +26,6 @@ const (
func (su *ScoreWrapper) Calculate(reportVersion PostureReportVersion) error {
switch reportVersion {
case EPostureReportV1:
return su.scoreUtil.Calculate(su.opaSessionObj.PostureReport.FrameworkReports)
case EPostureReportV2:
return su.scoreUtil.CalculatePostureReportV2(su.opaSessionObj.Report)
}
+3 -3
View File
@@ -4,11 +4,11 @@ go 1.18
require (
github.com/armosec/armoapi-go v0.0.98
github.com/armosec/go-git-url v0.0.13
github.com/armosec/go-git-url v0.0.15
github.com/armosec/k8s-interface v0.0.79
github.com/armosec/opa-utils v0.0.160
github.com/armosec/opa-utils v0.0.166
github.com/armosec/rbac-utils v0.0.14
github.com/armosec/utils-go v0.0.7
github.com/armosec/utils-go v0.0.9
github.com/armosec/utils-k8s-go v0.0.7
github.com/briandowns/spinner v1.18.1
github.com/dwertent/go-logger v0.0.2
+6 -6
View File
@@ -181,16 +181,16 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/armosec/armoapi-go v0.0.98 h1:sOBYW1QHmtLrxMz7Fmu+uLRc85YJrUa79fDSGLDjDHg=
github.com/armosec/armoapi-go v0.0.98/go.mod h1:v7rUl9eo5qL3kHxiP8tFCyRYERUGPgm2rwTIqkVOouw=
github.com/armosec/go-git-url v0.0.13 h1:kwzHBL7oFqf2UsR0h4Sx+HMYqO9xHHuBFXGIyuMqHAg=
github.com/armosec/go-git-url v0.0.13/go.mod h1:GzfssG3IW9KiURSpK7c/bySBRTlghpObQ7NQ1O4hcMI=
github.com/armosec/go-git-url v0.0.15 h1:sDtu0WNvAhrDJ2begTyWP8T4tE1j1K6D0ZJ6t3Cx8k4=
github.com/armosec/go-git-url v0.0.15/go.mod h1:GzfssG3IW9KiURSpK7c/bySBRTlghpObQ7NQ1O4hcMI=
github.com/armosec/k8s-interface v0.0.79 h1:K26itoRg1pCnBk+FR5UHF6sJTOvuJ/vP76I/9Yi/6eU=
github.com/armosec/k8s-interface v0.0.79/go.mod h1:JeaT4OTwXp8HhTHPIF3KJuY8H5lDZ+o4FGFy77J49xY=
github.com/armosec/opa-utils v0.0.160 h1:KgYN57Fh4t0+EELE/napAB+ysX1X3kDg28Sh34hxt1Y=
github.com/armosec/opa-utils v0.0.160/go.mod h1:ce7GrjUmp4A2bkw6ItJxnE1GeDeVQxwb3HiwKBbEZTA=
github.com/armosec/opa-utils v0.0.166 h1:g3gKpLf2YsGuQxSTTHQLb0k4+UHVC3NArv0lEtqpmbg=
github.com/armosec/opa-utils v0.0.166/go.mod h1:dFTUgba55U+P7E91g+UOOzfQ2deiKM5PP18XKlBkcCo=
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.7 h1:YGyIzfo7JKocx0SVrKEpQLSKLNLgm+JOEeH0XkO23yQ=
github.com/armosec/utils-go v0.0.7/go.mod h1:F/K1mI/qcj7fNuJl7xktoCeHM83azOF0Zq6eC2WuPyU=
github.com/armosec/utils-go v0.0.9 h1:Ri7SjCFHfORN1dBH4NYKYfVEEkt6Af4iFgbRskFeyac=
github.com/armosec/utils-go v0.0.9/go.mod h1:F/K1mI/qcj7fNuJl7xktoCeHM83azOF0Zq6eC2WuPyU=
github.com/armosec/utils-k8s-go v0.0.7 h1:g68GQ9Vm5LgVp/czcu2ZV1igm2azYLf9vNapyoXBfYI=
github.com/armosec/utils-k8s-go v0.0.7/go.mod h1:YFdWi3rEQQLbN6mZO21TSdoda8kGQYRV4rs5CRp8Kjs=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
+20
View File
@@ -0,0 +1,20 @@
.PHONY: test all build libgit2
# default task invoked while running make
all: libgit2 build
export CGO_ENABLED=1
# build and install libgit2
libgit2:
git submodule update --init --recursive
cd git2go; make install-static
# go build tags
TAGS = "static"
build:
go build -v -tags=$(TAGS) .
test:
go test -v -tags=$(TAGS) ./...
+12 -1
View File
@@ -1,6 +1,6 @@
# Kubescape HTTP Handler Package
Running `kubescape` will start up a webserver on port `8080` which will serve the following API's:
Running `kubescape` will start up a web-server on port `8080` which will serve the following API's:
### Trigger scan
@@ -153,6 +153,15 @@ curl --header "Content-Type: application/json" \
http://127.0.0.1:8080/v1/scan
```
#### Data profiling
Analyze profiled data using [pprof](https://github.com/google/pprof/blob/main/doc/README.md).
[How ro use](https://pkg.go.dev/net/http/pprof)
example:
```bash
go tool pprof http://localhost:6060/debug/pprof/heap
```
## Examples
* [Prometheus](examples/prometheus/README.md)
@@ -169,3 +178,5 @@ curl --header "Content-Type: application/json" \
* `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
* `KS_LOGGER_NAME`: Set logger name
* `KS_LOGGER_LEVEL`: Set logger level
+19
View File
@@ -0,0 +1,19 @@
// Package classification Kubescape Microservice
//
// The Kubescape Microservice API allows clients to interact with a Kubescape instance running in a Kubernetes cluster: trigger scans, retrieve and delete their results, enable exporting metrics to Prometheus etc.
//
// Schemes: http
// BasePath: /
// Version: 1.0.0
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Security:
// - basic
//
// swagger:meta
package docs
+57
View File
@@ -0,0 +1,57 @@
package docs
import (
"fmt"
"net/http"
_ "embed"
logger "github.com/dwertent/go-logger"
"github.com/go-openapi/runtime/middleware"
)
const (
OpenAPIDocsEndpoint = "docs"
OpenAPIRapiEndpoint = "rapi"
OpenAPISwaggerUIEndpoint = "swaggerui"
OpenAPIswaggerJSONEndpoint = "swagger.yaml"
OpenAPIV2Prefix = "/openapi/v2/"
)
//go:embed swagger.yaml
var specJSONBytes []byte
// ServeOpenAPISpec returns the OpenAPI specification file
func ServeOpenAPISpec(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write(specJSONBytes)
}
// NewOpenAPIUIHandler returns a handler that serves OpenAPI specs via UI
func NewOpenAPIUIHandler() http.Handler {
logstr := fmt.Sprintf("Starting swagger UI. baseURI: %v, docsEP: %v, rapidocEP: %v, swaggerui: %v", OpenAPIV2Prefix, OpenAPIDocsEndpoint, OpenAPIRapiEndpoint, OpenAPISwaggerUIEndpoint)
logger.L().Info(logstr)
redocOpts := middleware.RedocOpts{
BasePath: OpenAPIV2Prefix,
SpecURL: OpenAPIswaggerJSONEndpoint,
}
RapiDocOpts := middleware.RapiDocOpts{
BasePath: OpenAPIV2Prefix,
SpecURL: OpenAPIswaggerJSONEndpoint,
Path: OpenAPIRapiEndpoint,
}
opts := middleware.SwaggerUIOpts{
BasePath: OpenAPIV2Prefix,
SpecURL: OpenAPIswaggerJSONEndpoint,
Path: OpenAPISwaggerUIEndpoint,
}
var openAPISpecHandler http.Handler = http.HandlerFunc(ServeOpenAPISpec)
openAPIUIHandler := middleware.Redoc(redocOpts, openAPISpecHandler)
openAPIUIHandler = middleware.RapiDoc(RapiDocOpts, openAPIUIHandler)
openAPIUIHandler = middleware.SwaggerUI(opts, openAPIUIHandler)
return openAPIUIHandler
}
+448
View File
@@ -0,0 +1,448 @@
basePath: /
consumes:
- application/json
definitions:
PostScanRequest:
description: A request to trigger a Kubescape scan
properties:
account:
description: |-
A Kubescape account ID to use for scanning.
Same as `kubescape scan --account`.
example: d13791eb-19b1-4222-867b-9a7c1799cfac
format: uuid4
type: string
x-go-name: Account
excludedNamespaces:
description: |-
Namespaces to exclude.
Same as `kubescape scan --excluded-namespaces`.
example:
- armo-system
- kube-system
items:
type: string
type: array
x-go-name: ExcludedNamespaces
failThreshold:
description: |-
Threshold for a failing score.
Scores higher than the provided value will be considered failing.
example: 42
format: float
type: number
x-go-name: FailThreshold
format:
description: |-
Format of the results.
Same as `kubescape scan --format`.
example: json
type: string
x-go-name: Format
hostScanner:
description: |-
Deploy the Kubescape Kubernetes Host Scanner
Deploys the Armo K8s Host Scanner DeamonSet in the scanned cluster to collect data from certain controls.
example: true
type: boolean
x-go-name: HostScanner
includeNamespaces:
description: |-
Namespaces to include.
Same as `kubescape scan --include-namespaces`.
example:
- litmus-tests
- known-bad
items:
type: string
type: array
x-go-name: IncludeNamespaces
keepLocal:
description: |-
Do not submit results to Kubescape Cloud.
Same as `kubescape scan --keep-local`
example: true
type: boolean
x-go-name: KeepLocal
submit:
description: |-
Submit results to Kubescape Cloud.
Same as `kubescape scan --submit`.
type: boolean
x-go-name: Submit
targetNames:
default:
- all
description: |-
Name of the scan targets.
For example, if you select `targetType: "framework"`, you can trigger a scan using the NSA and MITRE ATT&CK Framework by passing `targetNames: ["nsa", "mitre"].
example:
- nsa
- mitre
items:
type: string
type: array
x-go-name: TargetNames
targetType:
default: framework
description: |-
Type of the target. "framework" or "control".
Framework KindFramework
Control KindControl
Rule KindRule
enum:
- Framework
- Control
- Rule
example: control
type: string
x-go-enum-desc: |-
Framework KindFramework
Control KindControl
Rule KindRule
x-go-name: TargetType
useCachedArtifacts:
description: Use the cached artifacts instead of downloading (offline support)
example: false
type: boolean
x-go-name: UseCachedArtifacts
type: object
x-go-package: github.com/armosec/opa-utils/httpserver/meta/v1
Response:
description: A Scan Response object
properties:
id:
description: ID of the scan
example: d13791eb-19b1-4222-867b-9a7c1799cfac
format: uuid4
type: string
x-go-name: ID
response:
description: The actual Response payload
example: d13791eb-19b1-4222-867b-9a7c1799cfac
type: object
x-go-name: Response
type:
description: |-
Type of this response
id IDScanResponseType Deprecated: will return busy / notBusy instead
error ErrorScanResponseType ErrorScanResponseType indicates a response that reports an error
v1results ResultsV1ScanResponseType ResultsV1ScanResponseType indicates a response that carries a v1 Results object as payload
busy BusyScanResponseType BusyScanResponseType indicates that a server is busy with a previous request
notBusy NotBusyScanResponseType NotBusyScanResponseType indicates that a server is not busy with a previous request
ready ReadyScanResponseType ReadyScanResponseType indicates that a server has successfully completed a request
enum:
- id
- error
- v1results
- busy
- notBusy
- ready
example: busy
type: string
x-go-enum-desc: |-
id IDScanResponseType Deprecated: will return busy / notBusy instead
error ErrorScanResponseType ErrorScanResponseType indicates a response that reports an error
v1results ResultsV1ScanResponseType ResultsV1ScanResponseType indicates a response that carries a v1 Results object as payload
busy BusyScanResponseType BusyScanResponseType indicates that a server is busy with a previous request
notBusy NotBusyScanResponseType NotBusyScanResponseType indicates that a server is not busy with a previous request
ready ReadyScanResponseType ReadyScanResponseType indicates that a server has successfully completed a request
x-go-name: Type
type: object
x-go-package: github.com/armosec/opa-utils/httpserver/meta/v1
info:
description: 'The Kubescape Microservice API allows clients to interact with a Kubescape
instance running in a Kubernetes cluster: trigger scans, retrieve and delete their
results, enable exporting metrics to Prometheus etc.'
title: Kubescape Microservice
version: 1.0.0
paths:
/livez:
get:
description: Returns the liveness status of the Kubescape microservice.
operationId: getLiveness
responses:
"200":
$ref: '#/responses/livenessProbeOK'
summary: Returns Kubescapes liveness status
tags:
- metrics
/readyz:
get:
description: Returns the readiness status of the Kubescape microservice.
operationId: getReadiness
responses:
"200":
$ref: '#/responses/readinessProbeOK'
summary: Returns Kubescapes readiness status
tags:
- metrics
/v1/metrics:
get:
description: Enables support for Prometheus metrics, runs a scan and returns
its result in Prometheus metrics format.
operationId: getMetrics
responses:
"200":
$ref: '#/responses/enableMetricsResponse'
summary: Returns current scan metrics in Prometheus format
tags:
- metrics
/v1/results:
delete:
description: Deletes Kubescape scan results from storage.
operationId: deleteScanResults
parameters:
- description: ID of the requested scan. If empty or not provided, defaults
to the latest scan.
in: query
name: id
type: string
x-go-name: ScanID
- default: false
description: |-
Keep the results in local storage after returning them.
By default, the Kubescape Microservice will delete scan results.
in: query
name: keep
type: boolean
x-go-name: KeepResults
- default: false
description: Whether to delete all results
in: query
name: all
type: boolean
x-go-name: AllResults
responses:
"200":
$ref: '#/responses/scanResponse'
"400":
$ref: '#/responses/scanResponseBadRequest'
summary: Deletes results of a scan
tags:
- scanning
get:
description: Returns the results of Kubescape scans
operationId: getScanResults
parameters:
- description: ID of the requested scan. If empty or not provided, defaults
to the latest scan.
in: query
name: id
type: string
x-go-name: ScanID
- default: false
description: |-
Keep the results in local storage after returning them.
By default, the Kubescape Microservice will delete scan results.
in: query
name: keep
type: boolean
x-go-name: KeepResults
responses:
"200":
$ref: '#/responses/scanResponse'
"204":
$ref: '#/responses/scanResponseNoContent'
"400":
$ref: '#/responses/scanResponseBadRequest'
summary: Returns results of a scan
tags:
- scanning
/v1/scan:
post:
description: The server will return a scan ID and execute the scan.
operationId: triggerScan
parameters:
- default: false
description: Wait for scanning to complete (synchronous request)
in: query
name: wait
type: boolean
x-go-name: ReturnResults
- default: false
description: Do not delete results after returning (relevant only for synchronous
requests)
in: query
name: keep
type: boolean
x-go-name: KeepResults
- description: Scan parameters
in: body
name: Body
schema:
$ref: '#/definitions/PostScanRequest'
responses:
"200":
$ref: '#/responses/scanResponse'
"400":
$ref: '#/responses/scanResponseBadRequest'
"500":
$ref: '#/responses/scanResponseInternalServerError'
summary: Triggers a Kubescape scan
tags:
- scanning
/v1/status:
get:
description: |-
Returns the current status of a scan with a given ID: whether it completed or not. Intended for asynchronous scanning, so you can check when a result is ready and fetch the results.
When a scan is in progress, the response's `type` will be `busy`. When a scan is complete, `type` is `notBusy`.
operationId: getStatus
parameters:
- description: ID of the scan to check
format: uuid4
in: query
name: id
type: string
x-go-name: ScanID
responses:
"200":
$ref: '#/responses/scanResponse'
summary: Returns a scans status
tags:
- scanning
produces:
- application/json
responses:
enableMetricsResponse:
description: Provided Prometheus metrics
schema:
type: string
livenessProbeOK:
description: Kubescape Microservice API is alive
readinessProbeOK:
description: Kubescape Microservice API is ready to serve requests
scanResponse:
description: A Scan Response object
schema:
$ref: '#/definitions/Response'
scanResponseBadRequest:
description: A Scan Response that occures after malformed requests
schema:
allOf:
- properties:
id:
description: ID of the scan
example: d13791eb-19b1-4222-867b-9a7c1799cfac
format: uuid4
type: string
x-go-name: ID
response:
description: The actual Response payload
example: d13791eb-19b1-4222-867b-9a7c1799cfac
type: object
x-go-name: Response
type:
description: |-
Type of this response
id IDScanResponseType Deprecated: will return busy / notBusy instead
error ErrorScanResponseType ErrorScanResponseType indicates a response that reports an error
v1results ResultsV1ScanResponseType ResultsV1ScanResponseType indicates a response that carries a v1 Results object as payload
busy BusyScanResponseType BusyScanResponseType indicates that a server is busy with a previous request
notBusy NotBusyScanResponseType NotBusyScanResponseType indicates that a server is not busy with a previous request
ready ReadyScanResponseType ReadyScanResponseType indicates that a server has successfully completed a request
enum:
- id
- error
- v1results
- busy
- notBusy
- ready
example: busy
type: string
x-go-enum-desc: |-
id IDScanResponseType Deprecated: will return busy / notBusy instead
error ErrorScanResponseType ErrorScanResponseType indicates a response that reports an error
v1results ResultsV1ScanResponseType ResultsV1ScanResponseType indicates a response that carries a v1 Results object as payload
busy BusyScanResponseType BusyScanResponseType indicates that a server is busy with a previous request
notBusy NotBusyScanResponseType NotBusyScanResponseType indicates that a server is not busy with a previous request
ready ReadyScanResponseType ReadyScanResponseType indicates that a server has successfully completed a request
x-go-name: Type
type: object
- properties:
response:
example: 'failed to parse query params, reason: schema: invalid path "waitr"'
type: object
x-go-name: Resp
type:
example: error
type: string
x-go-name: Type
type: object
scanResponseInternalServerError:
description: A Scan Response that occures after malformed requests
schema:
allOf:
- properties:
id:
description: ID of the scan
example: d13791eb-19b1-4222-867b-9a7c1799cfac
format: uuid4
type: string
x-go-name: ID
response:
description: The actual Response payload
example: d13791eb-19b1-4222-867b-9a7c1799cfac
type: object
x-go-name: Response
type:
description: |-
Type of this response
id IDScanResponseType Deprecated: will return busy / notBusy instead
error ErrorScanResponseType ErrorScanResponseType indicates a response that reports an error
v1results ResultsV1ScanResponseType ResultsV1ScanResponseType indicates a response that carries a v1 Results object as payload
busy BusyScanResponseType BusyScanResponseType indicates that a server is busy with a previous request
notBusy NotBusyScanResponseType NotBusyScanResponseType indicates that a server is not busy with a previous request
ready ReadyScanResponseType ReadyScanResponseType indicates that a server has successfully completed a request
enum:
- id
- error
- v1results
- busy
- notBusy
- ready
example: busy
type: string
x-go-enum-desc: |-
id IDScanResponseType Deprecated: will return busy / notBusy instead
error ErrorScanResponseType ErrorScanResponseType indicates a response that reports an error
v1results ResultsV1ScanResponseType ResultsV1ScanResponseType indicates a response that carries a v1 Results object as payload
busy BusyScanResponseType BusyScanResponseType indicates that a server is busy with a previous request
notBusy NotBusyScanResponseType NotBusyScanResponseType indicates that a server is not busy with a previous request
ready ReadyScanResponseType ReadyScanResponseType indicates that a server has successfully completed a request
x-go-name: Type
type: object
- properties:
id:
example: '""'
type: string
x-go-name: ID
response:
example: 'failed to scan. reason: ''empty list of resources'''
type: object
x-go-name: Resp
type:
example: error
type: string
x-go-name: Type
type: object
scanResponseNoContent:
description: |-
A Scan Response that indicates no response body.
Kubescape generates this response, for example, when results with given ID do not exist.
schemes:
- http
swagger: "2.0"
+17 -5
View File
@@ -6,9 +6,10 @@ replace github.com/armosec/kubescape/v2 => ../
require (
github.com/armosec/kubescape/v2 v2.0.0-00010101000000-000000000000
github.com/armosec/opa-utils v0.0.160
github.com/armosec/utils-go v0.0.7
github.com/armosec/opa-utils v0.0.166
github.com/armosec/utils-go v0.0.9
github.com/dwertent/go-logger v0.0.2
github.com/go-openapi/runtime v0.24.1
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0
github.com/gorilla/schema v1.2.0
@@ -42,10 +43,11 @@ require (
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/agnivade/levenshtein v1.0.1 // indirect
github.com/armosec/armoapi-go v0.0.98 // indirect
github.com/armosec/go-git-url v0.0.13 // indirect
github.com/armosec/go-git-url v0.0.15 // indirect
github.com/armosec/k8s-interface v0.0.79 // indirect
github.com/armosec/rbac-utils v0.0.14 // indirect
github.com/armosec/utils-k8s-go v0.0.7 // indirect
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
github.com/aws/aws-sdk-go v1.44.51 // indirect
github.com/aws/aws-sdk-go-v2 v1.16.7 // indirect
github.com/aws/aws-sdk-go-v2/config v1.15.13 // indirect
@@ -79,9 +81,16 @@ require (
github.com/go-git/go-git/v5 v5.4.2 // indirect
github.com/go-gota/gota v0.12.0 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-openapi/analysis v0.21.2 // indirect
github.com/go-openapi/errors v0.20.2 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.5 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/loads v0.21.1 // indirect
github.com/go-openapi/spec v0.20.4 // indirect
github.com/go-openapi/strfmt v0.21.2 // indirect
github.com/go-openapi/swag v0.21.1 // indirect
github.com/go-openapi/validate v0.21.0 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
@@ -109,10 +118,12 @@ require (
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/open-policy-agent/opa v0.42.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
@@ -133,6 +144,7 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/yashtewari/glob-intersection v0.1.0 // indirect
go.mongodb.org/mongo-driver v1.8.3 // indirect
go.opencensus.io v0.23.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
+98 -8
View File
@@ -181,19 +181,22 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/armosec/armoapi-go v0.0.98 h1:sOBYW1QHmtLrxMz7Fmu+uLRc85YJrUa79fDSGLDjDHg=
github.com/armosec/armoapi-go v0.0.98/go.mod h1:v7rUl9eo5qL3kHxiP8tFCyRYERUGPgm2rwTIqkVOouw=
github.com/armosec/go-git-url v0.0.13 h1:kwzHBL7oFqf2UsR0h4Sx+HMYqO9xHHuBFXGIyuMqHAg=
github.com/armosec/go-git-url v0.0.13/go.mod h1:GzfssG3IW9KiURSpK7c/bySBRTlghpObQ7NQ1O4hcMI=
github.com/armosec/go-git-url v0.0.15 h1:sDtu0WNvAhrDJ2begTyWP8T4tE1j1K6D0ZJ6t3Cx8k4=
github.com/armosec/go-git-url v0.0.15/go.mod h1:GzfssG3IW9KiURSpK7c/bySBRTlghpObQ7NQ1O4hcMI=
github.com/armosec/k8s-interface v0.0.79 h1:K26itoRg1pCnBk+FR5UHF6sJTOvuJ/vP76I/9Yi/6eU=
github.com/armosec/k8s-interface v0.0.79/go.mod h1:JeaT4OTwXp8HhTHPIF3KJuY8H5lDZ+o4FGFy77J49xY=
github.com/armosec/opa-utils v0.0.160 h1:KgYN57Fh4t0+EELE/napAB+ysX1X3kDg28Sh34hxt1Y=
github.com/armosec/opa-utils v0.0.160/go.mod h1:ce7GrjUmp4A2bkw6ItJxnE1GeDeVQxwb3HiwKBbEZTA=
github.com/armosec/opa-utils v0.0.166 h1:g3gKpLf2YsGuQxSTTHQLb0k4+UHVC3NArv0lEtqpmbg=
github.com/armosec/opa-utils v0.0.166/go.mod h1:dFTUgba55U+P7E91g+UOOzfQ2deiKM5PP18XKlBkcCo=
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.7 h1:YGyIzfo7JKocx0SVrKEpQLSKLNLgm+JOEeH0XkO23yQ=
github.com/armosec/utils-go v0.0.7/go.mod h1:F/K1mI/qcj7fNuJl7xktoCeHM83azOF0Zq6eC2WuPyU=
github.com/armosec/utils-go v0.0.9 h1:Ri7SjCFHfORN1dBH4NYKYfVEEkt6Af4iFgbRskFeyac=
github.com/armosec/utils-go v0.0.9/go.mod h1:F/K1mI/qcj7fNuJl7xktoCeHM83azOF0Zq6eC2WuPyU=
github.com/armosec/utils-k8s-go v0.0.7 h1:g68GQ9Vm5LgVp/czcu2ZV1igm2azYLf9vNapyoXBfYI=
github.com/armosec/utils-k8s-go v0.0.7/go.mod h1:YFdWi3rEQQLbN6mZO21TSdoda8kGQYRV4rs5CRp8Kjs=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/aws/aws-sdk-go v1.43.16/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go v1.44.51 h1:jO9hoLynZOrMM4dj0KjeKIK+c6PA+HQbKoHOkAEye2Y=
@@ -566,6 +569,12 @@ github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jT
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zapr v1.2.0 h1:n4JnPI1T3Qq1SFEi/F8rwLrZERp2bso19PJZDB9dayk=
github.com/go-logr/zapr v1.2.0/go.mod h1:Qa4Bsj2Vb+FAVeAKsLD8RLQ+YRJB8YDmOAKxaBQf7Ro=
github.com/go-openapi/analysis v0.21.2 h1:hXFrOYFHUAMQdu6zwAiKKJHJQ8kqZs1ux/ru1P1wLJU=
github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY=
github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8=
github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@@ -574,17 +583,58 @@ github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
github.com/go-openapi/loads v0.21.1 h1:Wb3nVZpdEzDTcly8S4HMkey6fjARRzb7iEaySimlDW0=
github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g=
github.com/go-openapi/runtime v0.24.1 h1:Sml5cgQKGYQHF+M7yYSHaH1eOjvTykrddTE/KtQVjqo=
github.com/go-openapi/runtime v0.24.1/go.mod h1:AKurw9fNre+h3ELZfk6ILsfvPN+bvvlaU/M9q/r9hpk=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg=
github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k=
github.com/go-openapi/strfmt v0.21.2 h1:5NDNgadiX1Vhemth/TH4gCGopWSTdDjxl60H3B7f+os=
github.com/go-openapi/strfmt v0.21.2/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU=
github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/validate v0.21.0 h1:+Wqk39yKOhfpLqNLEC0/eViCkzM5FVXVqrvt526+wcI=
github.com/go-openapi/validate v0.21.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
@@ -645,6 +695,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -799,6 +850,7 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC
github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
github.com/johnfercher/maroto v0.37.0 h1:W5xA6dixF7PwxT0N6mfbRg0zjQSMXeSIzplUucAuZTE=
github.com/johnfercher/maroto v0.37.0/go.mod h1:f9vLjznW+aVsf5R0F90P+PYi2maaYOHq8l07mvOP+ew=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -819,6 +871,8 @@ github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jung-kurt/gofpdf v1.16.2 h1:jgbatWHfRlPYiK85qgevsZTHviWXKwB1TTiKdz5PtRc=
github.com/jung-kurt/gofpdf v1.16.2/go.mod h1:1hl7y57EsiPAkLbOwzpzqgx1A30nQCk/YmFV8S2vmK0=
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
@@ -858,6 +912,8 @@ github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
@@ -902,7 +958,10 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
@@ -925,6 +984,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
@@ -941,6 +1001,7 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
@@ -1005,9 +1066,11 @@ github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xA
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
github.com/opencontainers/selinux v1.10.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
@@ -1077,6 +1140,8 @@ github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -1123,6 +1188,7 @@ github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5k
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
@@ -1187,6 +1253,8 @@ github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@@ -1215,6 +1283,9 @@ github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -1227,6 +1298,7 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yashtewari/glob-intersection v0.1.0 h1:6gJvMYQlTDOL3dMsPF6J0+26vwX9MB8/1q3uAdhmTrg=
github.com/yashtewari/glob-intersection v0.1.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -1251,6 +1323,10 @@ go.etcd.io/etcd/client/v3 v3.5.1/go.mod h1:OnjH4M8OnAotwaB2l9bVgZzRFKru7/ZMoS46O
go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE=
go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc=
go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4=
go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg=
go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng=
go.mongodb.org/mongo-driver v1.8.3 h1:TDKlTkGDKm9kkJVUOAXDK5/fkqKHJVwYQSpoRfB43R4=
go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -1319,6 +1395,7 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -1326,11 +1403,13 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
@@ -1446,6 +1525,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -1493,6 +1573,7 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1514,12 +1595,15 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1593,6 +1677,7 @@ golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1669,9 +1754,13 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
@@ -1986,6 +2075,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+44 -6
View File
@@ -57,18 +57,48 @@ func newScanResponseChan() *scanResponseChan {
}
type ScanQueryParams struct {
ReturnResults bool `schema:"wait"` // wait for scanning to complete (synchronized request)
KeepResults bool `schema:"keep"` // do not delete results after returning (relevant only for synchronized requests)
// Wait for scanning to complete (synchronous request)
// default: false
ReturnResults bool `schema:"wait" json:"wait"`
// Do not delete results after returning (relevant only for synchronous requests)
// default: false
KeepResults bool `schema:"keep" json:"keep"`
}
// swagger:parameters getScanResults
type GetResultsQueryParams struct {
// ID of the requested scan. If empty or not provided, defaults to the latest scan.
//
// in: query
ScanID string `schema:"id" json:"id"`
// Keep the results in local storage after returning them.
//
// By default, the Kubescape Microservice will delete scan results.
//
// in: query
// default: false
KeepResults bool `schema:"keep" json:"keep"`
}
// swagger:parameters deleteScanResults
type ResultsQueryParams struct {
ScanID string `schema:"id"`
KeepResults bool `schema:"keep"` // do not delete results after returning (default will delete results)
AllResults bool `schema:"all"` // delete all results
GetResultsQueryParams
// Whether to delete all results
//
// in: query
// default: false
AllResults bool `schema:"all" json:"all"`
}
// swagger:parameters getStatus
type StatusQueryParams struct {
ScanID string `schema:"id"`
// ID of the scan to check
//
// in:query
// swagger:strfmt uuid4
ScanID string `schema:"id" json:"id"`
}
// scanRequestParams params passed to channel
@@ -78,6 +108,14 @@ type scanRequestParams struct {
scanID string // generated scan ID
}
// swagger:parameters triggerScan
type ScanRequest struct {
ScanQueryParams
// Scan parameters
// in:body
Body utilsmetav1.PostScanRequest
}
func getScanParamsFromRequest(r *http.Request, scanID string) (*scanRequestParams, error) {
defer r.Body.Close()
@@ -3,6 +3,7 @@ package v1
import (
"fmt"
"net/http"
_ "net/http/pprof"
utilsapisv1 "github.com/armosec/opa-utils/httpserver/apis/v1"
utilsmetav1 "github.com/armosec/opa-utils/httpserver/meta/v1"
@@ -18,6 +19,14 @@ import (
var OutputDir = "./results"
var FailedOutputDir = "./failed"
// A Scan Response object
//
// swagger:response scanResponse
type ScanResponse struct {
// in:body
Body utilsmetav1.Response
}
type HTTPHandler struct {
state *serverState
scanResponseChan *scanResponseChan
+25 -2
View File
@@ -5,16 +5,39 @@ import (
"github.com/armosec/kubescape/v2/core/cautils/getter"
logger "github.com/dwertent/go-logger"
"github.com/dwertent/go-logger/helpers"
"github.com/dwertent/go-logger/zaplogger"
)
func initialize() error {
logger.InitLogger(zaplogger.LoggerName)
initializeLoggerName()
initializeLoggerLevel()
initializeSaaSEnv()
return nil
}
// initialize logger name
func initializeLoggerName() {
loggerName := zaplogger.LoggerName
if l := os.Getenv("KS_LOGGER_NAME"); l != "" {
loggerName = l
}
logger.InitLogger(loggerName)
}
// initialize logger level
func initializeLoggerLevel() {
loggerLevel := helpers.DebugLevel.String()
if l := os.Getenv("KS_LOGGER_LEVEL"); l != "" {
loggerLevel = l
}
if err := logger.L().SetLevel(loggerLevel); err != nil {
logger.L().SetLevel(helpers.DebugLevel.String())
logger.L().Error("failed to set logger level", helpers.String("level", loggerLevel), helpers.Error(err), helpers.String("default", helpers.DebugLevel.String()))
}
}
// SetupHTTPListener set up listening http servers
func initializeSaaSEnv() {
saasEnv := os.Getenv("KS_SAAS_ENV")
+18 -1
View File
@@ -7,6 +7,7 @@ import (
"os"
"github.com/armosec/kubescape/v2/core/cautils"
"github.com/armosec/kubescape/v2/httphandler/docs"
handlerequestsv1 "github.com/armosec/kubescape/v2/httphandler/handlerequests/v1"
logger "github.com/dwertent/go-logger"
"github.com/dwertent/go-logger/helpers"
@@ -51,10 +52,16 @@ func SetupHTTPListener() error {
rtr.HandleFunc(livePath, httpHandler.Live)
rtr.HandleFunc(readyPath, httpHandler.Ready)
// Setup the OpenAPI UI handler
handler := docs.NewOpenAPIUIHandler()
rtr.PathPrefix(docs.OpenAPIV2Prefix).Methods("GET").Handler(handler)
server.Handler = rtr
logger.L().Info("Started Kubescape server", helpers.String("port", getPort()), helpers.String("version", cautils.BuildNumber))
server.ListenAndServe()
servePprof()
if keyPair != nil {
return server.ListenAndServeTLS("", "")
}
@@ -79,3 +86,13 @@ func getPort() string {
}
return "8080"
}
func servePprof() {
go func() {
// start pprof server -> https://pkg.go.dev/net/http/pprof
if logger.L().GetLevel() == helpers.DebugLevel.String() {
logger.L().Info("starting pprof server", helpers.String("port", "6060"))
logger.L().Error(http.ListenAndServe(":6060", nil).Error())
}
}()
}
+1
View File
@@ -1,6 +1,7 @@
package main
import (
_ "github.com/armosec/kubescape/v2/httphandler/docs"
"github.com/armosec/kubescape/v2/httphandler/listener"
logger "github.com/dwertent/go-logger"
)