This commit is contained in:
Ben Hirschberg
2021-11-01 16:02:23 +02:00
133 changed files with 1848 additions and 12567 deletions

View File

@@ -29,7 +29,6 @@ jobs:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v1
- name: Set up Go
uses: actions/setup-go@v2
with:
@@ -54,6 +53,36 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ needs.once.outputs.upload_url }}
asset_path: build/${{ matrix.os }}/kubescape.zip
asset_name: kubescape-${{ matrix.os }}.zip
asset_content_type: application/zip
asset_path: build/${{ matrix.os }}/kubescape
asset_name: kubescape-${{ matrix.os }}
asset_content_type: application/octet-stream
build-docker:
name: Build docker container, tag and upload to registry
needs: build
if: ${{ github.repository == 'armosec/kubescape' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set name
run: echo quay.io/armosec/kubescape:v1.0.${{ github.run_number }} > build_tag.txt
- name: Build the Docker image
run: docker build . --file build/Dockerfile --tag $(cat build_tag.txt)
- name: Re-Tag Image to latest
run: docker tag $(cat build_tag.txt) quay.io/armosec/kubescape:latest
- name: Login to Quay.io
env: # Or as an environment variable
QUAY_PASSWORD: ${{ secrets.QUAYIO_REGISTRY_PASSWORD }}
QUAY_USERNAME: ${{ secrets.QUAYIO_REGISTRY_USERNAME }}
run: docker login -u="${QUAY_USERNAME}" -p="${QUAY_PASSWORD}" quay.io
- name: Push Docker image
run: |
docker push $(cat build_tag.txt)
docker push quay.io/armosec/kubescape:latest

View File

@@ -3,9 +3,6 @@ name: build-dev
on:
push:
branches: [ dev ]
pull_request:
branches: [ dev ]
types: [ closed ]
jobs:
build:
name: Create cross-platform dev build
@@ -38,3 +35,31 @@ jobs:
with:
name: kubescape-${{ matrix.os }}
path: build/${{ matrix.os }}/kubescape
build-docker:
name: Build docker container, tag and upload to registry
needs: build
if: ${{ github.repository == 'armosec/kubescape' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set name
run: echo quay.io/armosec/kubescape:dev-v1.0.${{ github.run_number }} > build_tag.txt
- name: Build the Docker image
run: docker build . --file build/Dockerfile --tag $(cat build_tag.txt)
- name: Login to Quay.io
env: # Or as an environment variable
QUAY_PASSWORD: ${{ secrets.QUAYIO_REGISTRY_PASSWORD }}
QUAY_USERNAME: ${{ secrets.QUAYIO_REGISTRY_USERNAME }}
run: docker login -u="${QUAY_USERNAME}" -p="${QUAY_PASSWORD}" quay.io
- name: Push Docker image
run: |
docker push $(cat build_tag.txt)

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
*.vs*
*kubescape*
*debug*
*vender*
.idea

View File

@@ -3,9 +3,12 @@
[![build](https://github.com/armosec/kubescape/actions/workflows/build.yaml/badge.svg)](https://github.com/armosec/kubescape/actions/workflows/build.yaml)
[![Go Report Card](https://goreportcard.com/badge/github.com/armosec/kubescape)](https://goreportcard.com/report/github.com/armosec/kubescape)
Kubescape is the first tool for testing if Kubernetes is deployed securely as defined in [Kubernetes Hardening Guidance by NSA and CISA](https://www.nsa.gov/Press-Room/News-Highlights/Article/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/)
Kubescape is the first open-source tool for testing if Kubernetes is deployed securely according to multiple frameworks:
regulatory, customized company policies and DevSecOps best practices, such as the [NSA-CISA](https://www.armosec.io/blog/kubernetes-hardening-guidance-summary-by-armo) and the [MITRE ATT&CK®](https://www.microsoft.com/security/blog/2021/03/23/secure-containerized-environments-with-updated-threat-matrix-for-kubernetes/) .
Kubescape scans K8s clusters, YAML files, and HELM charts, and detect misconfigurations and software vulnerabilities at early stages of the CI/CD pipeline and provides a risk score instantly and risk trends over time.
Kubescape integrates natively with other DevOps tools, including Jenkins, CircleCI and Github workflows.
Use Kubescape to test clusters or scan single YAML files and integrate it to your processes.
</br>
<img src="docs/demo.gif">
@@ -21,11 +24,9 @@ curl -s https://raw.githubusercontent.com/armosec/kubescape/master/install.sh |
## Run:
```
kubescape scan framework nsa --exclude-namespaces kube-system,kube-public
kubescape scan framework nsa
```
If you wish to scan all namespaces in your cluster, remove the `--exclude-namespaces` flag.
<img src="docs/summary.png">
### Click [👍](https://github.com/armosec/kubescape/stargazers) if you want us to continue to develop and improve Kubescape 😀
@@ -86,15 +87,21 @@ Set-ExecutionPolicy RemoteSigned -scope CurrentUser
### Examples
* Scan a running Kubernetes cluster with [`nsa`](https://www.nsa.gov/News-Features/Feature-Stories/Article-View/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/) framework and submit results to [Armo portal](https://portal.armo.cloud/)
* Scan a running Kubernetes cluster with [`nsa`](https://www.nsa.gov/News-Features/Feature-Stories/Article-View/Article/2716980/nsa-cisa-release-kubernetes-hardening-guidance/) framework and submit results to [ARMO portal](https://portal.armo.cloud/)
```
kubescape scan framework nsa --exclude-namespaces kube-system,kube-public --submit
kubescape scan framework nsa --submit
```
* Scan a running Kubernetes cluster with [`mitre`](https://www.microsoft.com/security/blog/2020/04/02/attack-matrix-kubernetes/) framework and submit results to [Armo portal](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 [ARMO portal](https://portal.armo.cloud/)
```
kubescape scan framework mitre --exclude-namespaces kube-system,kube-public --submit
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.armo.cloud/docs/controls)
```
kubescape scan control "Privileged container"
```
* Scan local `yaml`/`json` files before deploying. [Take a look at the demonstration](https://youtu.be/Ox6DaR7_4ZI)
@@ -103,9 +110,9 @@ kubescape scan framework nsa *.yaml
```
* Scan `yaml`/`json` files from url
* Scan kubernetes manifest files from a public github repository
```
kubescape scan framework nsa https://raw.githubusercontent.com/GoogleCloudPlatform/microservices-demo/master/release/kubernetes-manifests.yaml
kubescape scan framework nsa https://github.com/armosec/kubescape
```
* Output in `json` format
@@ -118,7 +125,7 @@ kubescape scan framework nsa --exclude-namespaces kube-system,kube-public --form
kubescape scan framework nsa --exclude-namespaces kube-system,kube-public --format junit --output results.xml
```
* Scan with exceptions, objects with exceptions will be presented as `warning` and not `fail` <img src="docs/new-feature.svg">
* Scan with exceptions, objects with exceptions will be presented as `exclude` and not `fail`
```
kubescape scan framework nsa --exceptions examples/exceptions.json
```
@@ -187,12 +194,18 @@ go build -o kubescape .
3. Run
```
./kubescape scan framework nsa --exclude-namespaces kube-system,kube-public
./kubescape scan framework nsa
```
4. Enjoy :zany_face:
## How to build in Docker
## Docker Support
### Official Docker image
```
quay.io/armosec/kubescape
```
### Build your own Docker image
1. Clone Project
```

View File

@@ -41,7 +41,7 @@ def main():
# Set some variables
packageName = getPackageName()
buildUrl = "github.com/armosec/kubescape/cmd.BuildNumber"
buildUrl = "github.com/armosec/kubescape/clihandler/cmd.BuildNumber"
releaseVersion = os.getenv("RELEASE")
ArmoBEServer = os.getenv("ArmoBEServer")
ArmoERServer = os.getenv("ArmoERServer")

View File

@@ -1,15 +1,27 @@
FROM golang:1.17-alpine as builder
ENV GOPROXY=https://goproxy.io,direct
ENV GO111MODULE=on
#ENV GOPROXY=https://goproxy.io,direct
ENV GO111MODULE=
ENV CGO_ENABLED=0
# Install required python/pip
ENV PYTHONUNBUFFERED=1
RUN apk add --update --no-cache python3 && ln -sf python3 /usr/bin/python
RUN python3 -m ensurepip
RUN pip3 install --no-cache --upgrade pip setuptools
WORKDIR /work
ADD . .
RUN GOOS=linux CGO_ENABLED=0 go build -ldflags="-s -w " -installsuffix cgo -o kubescape .
RUN python build.py
RUN ls -ltr build/ubuntu-latest
RUN cat /work/build/ubuntu-latest/kubescape.sha1
FROM alpine
COPY --from=builder /work/kubescape /usr/bin/kubescape
COPY --from=builder /work/build/ubuntu-latest/kubescape /usr/bin/kubescape
# # Download the frameworks. Use the "--use-default" flag when running kubescape
# RUN kubescape download framework nsa && kubescape download framework mitre
CMD ["kubescape"]
ENTRYPOINT ["kubescape"]

View File

@@ -1,101 +0,0 @@
package apis
import (
"bytes"
"fmt"
"io"
"net/http"
)
// HTTPReqFunc allows you to insert query params and more to aggregation message while using update aggregator
type HTTPReqFunc func(req *http.Request, qryData interface{})
func BasicBEQuery(req *http.Request, qryData interface{}) {
q := req.URL.Query()
if notificationData, isok := qryData.(*LoginObject); isok {
q.Add("customerGUID", notificationData.GUID)
}
req.URL.RawQuery = q.Encode()
}
func EmptyQuery(req *http.Request, qryData interface{}) {
q := req.URL.Query()
req.URL.RawQuery = q.Encode()
}
func MapQuery(req *http.Request, qryData interface{}) {
q := req.URL.Query()
if qryMap, isok := qryData.(map[string]string); isok {
for k, v := range qryMap {
q.Add(k, v)
}
}
req.URL.RawQuery = q.Encode()
}
func BEHttpRequest(loginobj *LoginObject, beURL,
httpverb string,
endpoint string,
payload []byte,
f HTTPReqFunc,
qryData interface{}) ([]byte, error) {
client := &http.Client{}
beURL = fmt.Sprintf("%v/%v", beURL, endpoint)
req, err := http.NewRequest(httpverb, beURL, bytes.NewReader(payload))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", loginobj.Authorization)
f(req, qryData)
for _, cookie := range loginobj.Cookies {
req.AddCookie(cookie)
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
fmt.Printf("req:\n%v\nresp:%v\n", req, resp)
return nil, fmt.Errorf("Error #%v Due to: %v", resp.StatusCode, resp.Status)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
}
type BELoginResponse struct {
Name string `json:"name"`
PreferredUsername string `json:"preferred_username"`
Email string `json:"email"`
CustomerGuid string `json:"customerGuid"`
Expires string `json:"expires"`
Authorization string `json:"authorization"`
Cookies []*http.Cookie
}
func (r *BELoginResponse) ToLoginObject() *LoginObject {
l := &LoginObject{}
l.Authorization = r.Authorization
l.Cookies = r.Cookies
l.Expires = r.Expires
l.GUID = r.CustomerGuid
return l
}
type BackendConnector struct {
BaseURL string
BELoginResponse *BELoginResponse
Credentials *CustomerLoginDetails
HTTPClient *http.Client
}

View File

@@ -1,128 +0,0 @@
package apis
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
)
func MakeBackendConnector(client *http.Client, baseURL string, loginDetails *CustomerLoginDetails) (*BackendConnector, error) {
if err := ValidateBEConnectorMakerInput(client, baseURL, loginDetails); err != nil {
return nil, err
}
conn := &BackendConnector{BaseURL: baseURL, Credentials: loginDetails, HTTPClient: client}
err := conn.Login()
return conn, err
}
func ValidateBEConnectorMakerInput(client *http.Client, baseURL string, loginDetails *CustomerLoginDetails) error {
if client == nil {
return fmt.Errorf("You must provide an initialized httpclient")
}
if len(baseURL) == 0 {
return fmt.Errorf("you must provide a valid backend url")
}
if loginDetails == nil || (len(loginDetails.Email) == 0 && len(loginDetails.Password) == 0) {
return fmt.Errorf("you must provide valid login details")
}
return nil
}
func (r *BackendConnector) Login() error {
if !r.IsExpired() {
return nil
}
loginInfoBytes, err := json.Marshal(r.Credentials)
if err != nil {
return fmt.Errorf("unable to marshal credentials properly")
}
beURL := fmt.Sprintf("%v/%v", r.BaseURL, "login")
req, err := http.NewRequest("POST", beURL, bytes.NewReader(loginInfoBytes))
if err != nil {
return err
}
req.Header.Set("Referer", strings.Replace(beURL, "dashbe", "cpanel", 1))
resp, err := r.HTTPClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("unable to read login response")
}
loginS := &BELoginResponse{}
json.Unmarshal(body, &loginS)
loginS.Cookies = resp.Cookies()
r.BELoginResponse = loginS
return nil
}
func (r *BackendConnector) IsExpired() bool {
return r.BELoginResponse == nil || r.BELoginResponse.ToLoginObject().IsExpired()
}
func (r *BackendConnector) GetBaseURL() string {
return r.BaseURL
}
func (r *BackendConnector) GetLoginObj() *LoginObject {
return r.BELoginResponse.ToLoginObject()
}
func (r *BackendConnector) GetClient() *http.Client {
return r.HTTPClient
}
func (r *BackendConnector) HTTPSend(httpverb string,
endpoint string,
payload []byte,
f HTTPReqFunc,
qryData interface{}) ([]byte, error) {
beURL := fmt.Sprintf("%v/%v", r.GetBaseURL(), endpoint)
req, err := http.NewRequest(httpverb, beURL, bytes.NewReader(payload))
if err != nil {
return nil, err
}
if r.IsExpired() {
r.Login()
}
loginobj := r.GetLoginObj()
req.Header.Set("Authorization", loginobj.Authorization)
f(req, qryData)
q := req.URL.Query()
q.Set("customerGUID", loginobj.GUID)
req.URL.RawQuery = q.Encode()
for _, cookie := range loginobj.Cookies {
req.AddCookie(cookie)
}
resp, err := r.GetClient().Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
fmt.Printf("req:\n%v\nresp:%v\n", req, resp)
return nil, fmt.Errorf("Error #%v Due to: %v", resp.StatusCode, resp.Status)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
}

View File

@@ -1,25 +0,0 @@
package apis
// WebsocketScanCommand api
const (
WebsocketScanCommandVersion string = "v1"
WebsocketScanCommandPath string = "scanImage"
)
// commands send via websocket
const (
UPDATE string = "update"
ATTACH string = "Attach"
REMOVE string = "remove"
DETACH string = "Detach"
INCOMPATIBLE string = "Incompatible"
REPLACE_HEADERS string = "ReplaceHeaders"
IMAGE_UNREACHABLE string = "ImageUnreachable"
SIGN string = "sign"
UNREGISTERED string = "unregistered"
INJECT string = "inject"
RESTART string = "restart"
ENCRYPT string = "encryptSecret"
DECRYPT string = "decryptSecret"
SCAN string = "scan"
)

View File

@@ -1,78 +0,0 @@
package apis
import (
"encoding/json"
"fmt"
"net/http"
"github.com/docker/docker/api/types"
)
// WebsocketScanCommand trigger scan thru the websocket
type WebsocketScanCommand struct {
// CustomerGUID string `json:"customerGUID"`
ImageTag string `json:"imageTag"`
Wlid string `json:"wlid"`
IsScanned bool `json:"isScanned"`
ContainerName string `json:"containerName"`
JobID string `json:"jobID,omitempty"`
LastAction int `json:"actionIDN"`
// ImageHash string `json:"imageHash"`
Credentials *types.AuthConfig `json:"credentials,omitempty"`
}
//taken from BE
// ElasticRespTotal holds the total struct in Elastic array response
type ElasticRespTotal struct {
Value int `json:"value"`
Relation string `json:"relation"`
}
// V2ListResponse holds the response of some list request with some metadata
type V2ListResponse struct {
Total ElasticRespTotal `json:"total"`
Response interface{} `json:"response"`
// Cursor for quick access to the next page. Not supported yet
Cursor string `json:"cursor"`
}
// Oauth2Customer returns inside the "ca_groups" field in claims section of
// Oauth2 verification process
type Oauth2Customer struct {
CustomerName string `json:"customerName"`
CustomerGUID string `json:"customerGUID"`
}
type LoginObject struct {
Authorization string `json:"authorization"`
GUID string
Cookies []*http.Cookie
Expires string
}
type SafeMode struct {
Reporter string `json:"reporter"` // "Agent"
Action string `json:"action,omitempty"` // "action"
Wlid string `json:"wlid"` // CAA_WLID
PodName string `json:"podName"` // CAA_POD_NAME
InstanceID string `json:"instanceID"` // CAA_POD_NAME
ContainerName string `json:"containerName,omitempty"` // CAA_CONTAINER_NAME
ProcessName string `json:"processName,omitempty"`
ProcessID int `json:"processID,omitempty"`
ProcessCMD string `json:"processCMD,omitempty"`
ComponentGUID string `json:"componentGUID,omitempty"` // CAA_GUID
StatusCode int `json:"statusCode"` // 0/1/2
ProcessExitCode int `json:"processExitCode"` // 0 +
Timestamp int64 `json:"timestamp"`
Message string `json:"message,omitempty"` // any string
JobID string `json:"jobID,omitempty"` // any string
Compatible *bool `json:"compatible,omitempty"`
}
func (safeMode *SafeMode) Json() string {
b, err := json.Marshal(*safeMode)
if err != nil {
return ""
}
return fmt.Sprintf("%s", b)
}

View File

@@ -1,26 +0,0 @@
package apis
// import (
// "fmt"
// "net/http"
// "testing"
// )
// func TestAuditStructure(t *testing.T) {
// c := http.Client{}
// be, err := MakeBackendConnector(&c, "https://dashbe.eudev3.cyberarmorsoft.com", &CustomerLoginDetails{Email: "lalafi@cyberarmor.io", Password: "*", CustomerName: "CyberArmorTests"})
// if err != nil {
// t.Errorf("sad1")
// }
// b, err := be.HTTPSend("GET", "v1/microservicesOverview", nil, MapQuery, map[string]string{"wlid": "wlid://cluster-childrenofbodom/namespace-default/deployment-pos"})
// if err != nil {
// t.Errorf("sad2")
// }
// fmt.Printf("%v", string(b))
// t.Errorf("sad")
// }

View File

@@ -1,27 +0,0 @@
package apis
import (
"net/http"
)
// type Dashboard interface {
// OPAFRAMEWORKGet(string, bool) ([]opapolicy.Framework, error)
// }
// Connector - interface for any connector (BE/Portal and so on)
type Connector interface {
//may used for a more generic httpsend interface based method
GetBaseURL() string
GetLoginObj() *LoginObject
GetClient() *http.Client
Login() error
IsExpired() bool
HTTPSend(httpverb string,
endpoint string,
payload []byte,
f HTTPReqFunc,
qryData interface{}) ([]byte, error)
}

View File

@@ -1,255 +0,0 @@
package apis
import (
"bytes"
"io"
"net/http"
"time"
oidc "github.com/coreos/go-oidc"
uuid "github.com/satori/go.uuid"
// "go.uber.org/zap"
"context"
"encoding/json"
"fmt"
"strings"
"golang.org/x/oauth2"
)
func GetOauth2TokenURL() string {
return "https://idens.eudev3.cyberarmorsoft.com/auth/realms/CyberArmorSites"
}
func GetLoginStruct() (LoginAux, error) {
return LoginAux{Referer: "https://cpanel.eudev3.cyberarmorsoft.com/login", Url: "https://cpanel.eudev3.cyberarmorsoft.com/login"}, nil
}
func LoginWithKeycloak(loginDetails CustomerLoginDetails) ([]uuid.UUID, *oidc.IDToken, error) {
// var custGUID uuid.UUID
// config.Oauth2TokenURL
if GetOauth2TokenURL() == "" {
return nil, nil, fmt.Errorf("missing oauth2 token URL")
}
urlaux, _ := GetLoginStruct()
conf, err := getOauth2Config(urlaux)
if err != nil {
return nil, nil, err
}
ctx := context.Background()
provider, err := oidc.NewProvider(ctx, GetOauth2TokenURL())
if err != nil {
return nil, nil, err
}
// "Oauth2ClientID": "golang-client"
oidcConfig := &oidc.Config{
ClientID: "golang-client",
SkipClientIDCheck: true,
}
verifier := provider.Verifier(oidcConfig)
ouToken, err := conf.PasswordCredentialsToken(ctx, loginDetails.Email, loginDetails.Password)
if err != nil {
return nil, nil, err
}
// "Authorization",
authorization := fmt.Sprintf("%s %s", ouToken.Type(), ouToken.AccessToken)
// oidc.IDTokenVerifier
tkn, err := verifier.Verify(ctx, ouToken.AccessToken)
if err != nil {
return nil, tkn, err
}
tkn.Nonce = authorization
if loginDetails.CustomerName == "" {
customers, err := getCustomersNames(tkn)
if err != nil {
return nil, tkn, err
}
if len(customers) == 1 {
loginDetails.CustomerName = customers[0]
} else {
return nil, tkn, fmt.Errorf("login with one of the following customers: %v", customers)
}
}
custGUID, err := getCustomerGUID(tkn, &loginDetails)
if err != nil {
return nil, tkn, err
}
return []uuid.UUID{custGUID}, tkn, nil
}
func getOauth2Config(urlaux LoginAux) (*oauth2.Config, error) {
reURLSlices := strings.Split(urlaux.Referer, "/")
if len(reURLSlices) == 0 {
reURLSlices = strings.Split(urlaux.Url, "/")
}
// zapLogger.With(zap.Strings("referer", reURLSlices)).Info("Searching oauth2Config for")
if len(reURLSlices) < 3 {
reURLSlices = []string{reURLSlices[0], reURLSlices[0], reURLSlices[0]}
}
lg, _ := GetLoginStruct()
provider, _ := oidc.NewProvider(context.Background(), GetOauth2TokenURL())
//provider.Endpoint {"AuthURL":"https://idens.eudev3.cyberarmorsoft.com/auth/realms/CyberArmorSites/protocol/openid-connect/auth","TokenURL":"https://idens.eudev3.cyberarmorsoft.com/auth/realms/CyberArmorSites/protocol/openid-connect/token","AuthStyle":0}
conf := oauth2.Config{
ClientID: "golang-client",
ClientSecret: "4e33bad2-3491-41a6-b486-93c492cfb4a2",
RedirectURL: lg.Referer,
// Discovery returns the OAuth2 endpoints.
Endpoint: provider.Endpoint(),
// "openid" is a required scope for OpenID Connect flows.
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
}
return &conf, nil
// return nil, fmt.Errorf("canno't find oauth2Config for referer '%+v'.\nPlease set referer or origin headers", reURLSlices)
}
func getCustomersNames(oauth2Details *oidc.IDToken) ([]string, error) {
var claimsJSON Oauth2Claims
if err := oauth2Details.Claims(&claimsJSON); err != nil {
return nil, err
}
customersList := make([]string, 0, len(claimsJSON.CAGroups))
for _, v := range claimsJSON.CAGroups {
var caCustomer Oauth2Customer
if err := json.Unmarshal([]byte(v), &caCustomer); err == nil {
customersList = append(customersList, caCustomer.CustomerName)
}
}
return customersList, nil
}
func getCustomerGUID(tkn *oidc.IDToken, loginDetails *CustomerLoginDetails) (uuid.UUID, error) {
customers, err := getCustomersList(tkn)
if err != nil {
return uuid.UUID{}, err
}
// if customer name not provided - use default customer
if loginDetails.CustomerName == "" && len(customers) > 0 {
return uuid.FromString(customers[0].CustomerGUID)
}
for _, i := range customers {
if i.CustomerName == loginDetails.CustomerName {
return uuid.FromString(i.CustomerGUID)
}
}
return uuid.UUID{}, fmt.Errorf("customer name not found in customer list")
}
func getCustomersList(oauth2Details *oidc.IDToken) ([]Oauth2Customer, error) {
var claimsJSON Oauth2Claims
if err := oauth2Details.Claims(&claimsJSON); err != nil {
return nil, err
}
customersList := make([]Oauth2Customer, 0, len(claimsJSON.CAGroups))
for _, v := range claimsJSON.CAGroups {
var caCustomer Oauth2Customer
if err := json.Unmarshal([]byte(v), &caCustomer); err == nil {
customersList = append(customersList, caCustomer)
}
}
return customersList, nil
}
// func MakeAuthCookies(custGUID uuid.UUID, ouToken *oidc.IDToken) (*http.Cookie, error) {
// var ccc http.Cookie
// var responseData AuthenticationCookie
// expireDate := time.Now().UTC().Add(time.Duration(config.CookieExpirationHours) * time.Hour)
// if ouToken != nil {
// expireDate = ouToken.Expiry
// }
// ccc.Expires = expireDate
// responseData.CustomerGUID = custGUID
// responseData.Expires = ccc.Expires
// responseData.Version = 0
// authorizationStr := ""
// if ouToken != nil {
// authorizationStr = ouToken.Nonce
// if err := ouToken.Claims(&responseData.Oauth2Claims); err != nil {
// errStr := fmt.Sprintf("failed to get claims from JWT")
// return nil, fmt.Errorf("%v", errStr)
// }
// }
// jsonBytes, err := json.Marshal(responseData)
// if err != nil {
// errStr := fmt.Sprintf("failed to get claims from JWT")
// return nil, fmt.Errorf("%v", errStr)
// }
// ccc.Name = "auth"
// ccc.Value = hex.EncodeToString(jsonBytes) + "." + cacheaccess.CalcHmac256(jsonBytes)
// // TODO: HttpOnly for security...
// ccc.HttpOnly = false
// ccc.Path = "/"
// ccc.Secure = true
// ccc.SameSite = http.SameSiteNoneMode
// http.SetCookie(w, &ccc)
// responseData.Authorization = authorizationStr
// jsonBytes, err = json.Marshal(responseData)
// if err != nil {
// w.WriteHeader(http.StatusInternalServerError)
// fmt.Fprintf(w, "error while marshaling response(2) %s", err)
// return
// }
// w.Write(jsonBytes)
// }
func Login(loginDetails CustomerLoginDetails) (*LoginObject, error) {
return nil, nil
}
func GetBEInfo(cfgFile string) string {
return "https://dashbe.eudev3.cyberarmorsoft.com"
}
func BELogin(loginDetails *CustomerLoginDetails, login string, cfg string) (*BELoginResponse, error) {
client := &http.Client{}
basebeURL := GetBEInfo(cfg)
beURL := fmt.Sprintf("%v/%v", basebeURL, login)
loginInfoBytes, err := json.Marshal(loginDetails)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", beURL, bytes.NewReader(loginInfoBytes))
if err != nil {
return nil, err
}
req.Header.Set("Referer", strings.Replace(beURL, "dashbe", "cpanel", 1))
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
loginS := &BELoginResponse{}
json.Unmarshal(body, &loginS)
loginS.Cookies = resp.Cookies()
return loginS, nil
}
func (r *LoginObject) IsExpired() bool {
if r == nil {
return true
}
t, err := time.Parse(time.RFC3339, r.Expires)
if err != nil {
return true
}
return t.UTC().Before(time.Now().UTC())
}

View File

@@ -1,38 +0,0 @@
package apis
import (
"time"
"github.com/gofrs/uuid"
)
// AuthenticationCookie is what it is
type AuthenticationCookie struct {
Oauth2Claims `json:",inline"`
CustomerGUID uuid.UUID `json:"customerGuid"`
Expires time.Time `json:"expires"`
Version int `json:"version"`
Authorization string `json:"authorization,omitempty"`
}
type LoginAux struct {
Referer string
Url string
}
// CustomerLoginDetails is what it is
type CustomerLoginDetails struct {
Email string `json:"email"`
Password string `json:"password"`
CustomerName string `json:"customer,omitempty"`
CustomerGUID uuid.UUID `json:"customerGuid,omitempty"`
}
// Oauth2Claims returns in claims section of Oauth2 verification process
type Oauth2Claims struct {
Sub string `json:"sub"`
Name string `json:"name"`
PreferredUserName string `json:"preferred_username"`
CAGroups []string `json:"ca_groups"`
Email string `json:"email"`
}

View File

@@ -1,132 +0,0 @@
package apis
import (
"encoding/json"
"fmt"
)
// Commands list of commands received from websocket
type Commands struct {
Commands []Command `json:"commands"`
}
// Command structure of command received from websocket
type Command struct {
CommandName string `json:"commandName"`
ResponseID string `json:"responseID"`
Wlid string `json:"wlid,omitempty"`
WildWlid string `json:"wildWlid,omitempty"`
Sid string `json:"sid,omitempty"`
WildSid string `json:"wildSid,omitempty"`
JobTracking JobTracking `json:"jobTracking"`
Args map[string]interface{} `json:"args,omitempty"`
}
type JobTracking struct {
JobID string `json:"jobID,omitempty"`
ParentID string `json:"parentAction,omitempty"`
LastActionNumber int `json:"numSeq,omitempty"`
}
func (c *Command) DeepCopy() *Command {
newCommand := &Command{}
newCommand.CommandName = c.CommandName
newCommand.ResponseID = c.ResponseID
newCommand.Wlid = c.Wlid
newCommand.WildWlid = c.WildWlid
if c.Args != nil {
newCommand.Args = make(map[string]interface{})
for i, j := range c.Args {
newCommand.Args[i] = j
}
}
return newCommand
}
func (c *Command) GetLabels() map[string]string {
if c.Args != nil {
if ilabels, ok := c.Args["labels"]; ok {
labels := map[string]string{}
if b, e := json.Marshal(ilabels); e == nil {
if e = json.Unmarshal(b, &labels); e == nil {
return labels
}
}
}
}
return map[string]string{}
}
func (c *Command) SetLabels(labels map[string]string) {
if c.Args == nil {
c.Args = make(map[string]interface{})
}
c.Args["labels"] = labels
}
func (c *Command) GetFieldSelector() map[string]string {
if c.Args != nil {
if ilabels, ok := c.Args["fieldSelector"]; ok {
labels := map[string]string{}
if b, e := json.Marshal(ilabels); e == nil {
if e = json.Unmarshal(b, &labels); e == nil {
return labels
}
}
}
}
return map[string]string{}
}
func (c *Command) SetFieldSelector(labels map[string]string) {
if c.Args == nil {
c.Args = make(map[string]interface{})
}
c.Args["fieldSelector"] = labels
}
func (c *Command) GetID() string {
if c.WildWlid != "" {
return c.WildWlid
}
if c.WildSid != "" {
return c.WildSid
}
if c.Wlid != "" {
return c.Wlid
}
if c.Sid != "" {
return c.Sid
}
return ""
}
func (c *Command) Json() string {
b, _ := json.Marshal(*c)
return fmt.Sprintf("%s", b)
}
func SIDFallback(c *Command) {
if c.GetID() == "" {
sid, err := getSIDFromArgs(c.Args)
if err != nil || sid == "" {
return
}
c.Sid = sid
}
}
func getSIDFromArgs(args map[string]interface{}) (string, error) {
sidInterface, ok := args["sid"]
if !ok {
return "", nil
}
sid, ok := sidInterface.(string)
if !ok || sid == "" {
return "", fmt.Errorf("sid found in args but empty")
}
// if _, err := secrethandling.SplitSecretID(sid); err != nil {
// return "", err
// }
return sid, nil
}

View File

@@ -1,16 +0,0 @@
package armotypes
type EnforcementsRule struct {
MonitoredObject []string `json:"monitoredObject"`
MonitoredObjectExistence []string `json:"objectExistence"`
MonitoredObjectEvent []string `json:"event"`
Action []string `json:"action"`
}
type ExecutionPolicy struct {
PortalBase `json:",inline"`
Designators []PortalDesignator `json:"designators"`
PolicyType string `json:"policyType"`
CreationTime string `json:"creation_time"`
ExecutionEnforcementsRule []EnforcementsRule `json:"enforcementRules"`
}

View File

@@ -1,66 +0,0 @@
package armotypes
import "strings"
const (
CostumerGuidQuery = "costumerGUID"
ClusterNameQuery = "cluster"
DatacenterNameQuery = "datacenter"
NamespaceQuery = "namespace"
ProjectQuery = "project"
WlidQuery = "wlid"
SidQuery = "sid"
)
// PortalBase holds basic items data from portal BE
type PortalBase struct {
GUID string `json:"guid"`
Name string `json:"name"`
Attributes map[string]interface{} `json:"attributes,omitempty"` // could be string
}
type DesignatorType string
// Supported designators
const (
DesignatorAttributes DesignatorType = "Attributes"
DesignatorAttribute DesignatorType = "Attribute" // Deprecated
/*
WorkloadID format.
k8s format: wlid://cluster-<cluster>/namespace-<namespace>/<kind>-<name>
native format: wlid://datacenter-<datacenter>/project-<project>/native-<name>
*/
DesignatorWlid DesignatorType = "Wlid"
/*
Wild card - subset of wlid. e.g.
1. Include cluster:
wlid://cluster-<cluster>/
2. Include cluster and namespace (filter out all other namespaces):
wlid://cluster-<cluster>/namespace-<namespace>/
*/
DesignatorWildWlid DesignatorType = "WildWlid"
DesignatorWlidContainer DesignatorType = "WlidContainer"
DesignatorWlidProcess DesignatorType = "WlidProcess"
DesignatorSid DesignatorType = "Sid" // secret id
)
func (dt DesignatorType) ToLower() DesignatorType {
return DesignatorType(strings.ToLower(string(dt)))
}
// attributes
const (
AttributeCluster = "cluster"
AttributeNamespace = "namespace"
AttributeKind = "kind"
AttributeName = "name"
)
// PortalDesignator represented single designation options
type PortalDesignator struct {
DesignatorType DesignatorType `json:"designatorType"`
WLID string `json:"wlid"`
WildWLID string `json:"wildwlid"`
SID string `json:"sid"`
Attributes map[string]string `json:"attributes"`
}

View File

@@ -1,18 +0,0 @@
package armotypes
func MockPortalBase(customerGUID, name string, attributes map[string]interface{}) *PortalBase {
if customerGUID == "" {
customerGUID = "36b6f9e1-3b63-4628-994d-cbe16f81e9c7"
}
if name == "" {
name = "portalbase-a"
}
if attributes == nil {
attributes = make(map[string]interface{})
}
return &PortalBase{
GUID: customerGUID,
Name: name,
Attributes: attributes,
}
}

View File

@@ -1,113 +0,0 @@
package armotypes
import (
"github.com/armosec/kubescape/cautils/cautils"
"github.com/golang/glog"
)
var IgnoreLabels = []string{AttributeCluster, AttributeNamespace}
func (designator *PortalDesignator) GetCluster() string {
cluster, _, _, _, _ := designator.DigestPortalDesignator()
return cluster
}
func (designator *PortalDesignator) GetNamespace() string {
_, namespace, _, _, _ := designator.DigestPortalDesignator()
return namespace
}
func (designator *PortalDesignator) GetKind() string {
_, _, kind, _, _ := designator.DigestPortalDesignator()
return kind
}
func (designator *PortalDesignator) GetName() string {
_, _, _, name, _ := designator.DigestPortalDesignator()
return name
}
func (designator *PortalDesignator) GetLabels() map[string]string {
_, _, _, _, labels := designator.DigestPortalDesignator()
return labels
}
// DigestPortalDesignator - get cluster namespace and labels from designator
func (designator *PortalDesignator) DigestPortalDesignator() (string, string, string, string, map[string]string) {
switch designator.DesignatorType.ToLower() {
case DesignatorAttributes.ToLower(), DesignatorAttribute.ToLower():
return designator.DigestAttributesDesignator()
case DesignatorWlid.ToLower(), DesignatorWildWlid.ToLower():
return cautils.GetClusterFromWlid(designator.WLID), cautils.GetNamespaceFromWlid(designator.WLID), cautils.GetKindFromWlid(designator.WLID), cautils.GetNameFromWlid(designator.WLID), map[string]string{}
// case DesignatorSid: // TODO
default:
glog.Warningf("in 'digestPortalDesignator' designator type: '%v' not yet supported. please contact Armo team", designator.DesignatorType)
}
return "", "", "", "", nil
}
func (designator *PortalDesignator) DigestAttributesDesignator() (string, string, string, string, map[string]string) {
cluster := ""
namespace := ""
kind := ""
name := ""
labels := map[string]string{}
attributes := designator.Attributes
if attributes == nil {
return cluster, namespace, kind, name, labels
}
for k, v := range attributes {
labels[k] = v
}
if v, ok := attributes[AttributeNamespace]; ok {
namespace = v
delete(labels, AttributeNamespace)
}
if v, ok := attributes[AttributeCluster]; ok {
cluster = v
delete(labels, AttributeCluster)
}
if v, ok := attributes[AttributeKind]; ok {
kind = v
delete(labels, AttributeKind)
}
if v, ok := attributes[AttributeName]; ok {
name = v
delete(labels, AttributeName)
}
return cluster, namespace, kind, name, labels
}
// DigestPortalDesignator DEPRECATED. use designator.DigestPortalDesignator() - get cluster namespace and labels from designator
func DigestPortalDesignator(designator *PortalDesignator) (string, string, map[string]string) {
switch designator.DesignatorType {
case DesignatorAttributes, DesignatorAttribute:
return DigestAttributesDesignator(designator.Attributes)
case DesignatorWlid, DesignatorWildWlid:
return cautils.GetClusterFromWlid(designator.WLID), cautils.GetNamespaceFromWlid(designator.WLID), map[string]string{}
// case DesignatorSid: // TODO
default:
glog.Warningf("in 'digestPortalDesignator' designator type: '%v' not yet supported. please contact Armo team", designator.DesignatorType)
}
return "", "", nil
}
func DigestAttributesDesignator(attributes map[string]string) (string, string, map[string]string) {
cluster := ""
namespace := ""
labels := map[string]string{}
if attributes == nil {
return cluster, namespace, labels
}
for k, v := range attributes {
labels[k] = v
}
if v, ok := attributes[AttributeNamespace]; ok {
namespace = v
delete(labels, AttributeNamespace)
}
if v, ok := attributes[AttributeCluster]; ok {
cluster = v
delete(labels, AttributeCluster)
}
return cluster, namespace, labels
}

View File

@@ -1,42 +0,0 @@
package armotypes
type PostureExceptionPolicyActions string
const AlertOnly PostureExceptionPolicyActions = "alertOnly"
const Disable PostureExceptionPolicyActions = "disable"
type PostureExceptionPolicy struct {
PortalBase `json:",inline"`
PolicyType string `json:"policyType"`
CreationTime string `json:"creationTime"`
Actions []PostureExceptionPolicyActions `json:"actions"`
Resources []PortalDesignator `json:"resources"`
PosturePolicies []PosturePolicy `json:"posturePolicies"`
}
type PosturePolicy struct {
FrameworkName string `json:"frameworkName"`
ControlName string `json:"controlName"`
RuleName string `json:"ruleName"`
}
func (exceptionPolicy *PostureExceptionPolicy) IsAlertOnly() bool {
if exceptionPolicy.IsDisable() {
return false
}
for i := range exceptionPolicy.Actions {
if exceptionPolicy.Actions[i] == AlertOnly {
return true
}
}
return false
}
func (exceptionPolicy *PostureExceptionPolicy) IsDisable() bool {
for i := range exceptionPolicy.Actions {
if exceptionPolicy.Actions[i] == Disable {
return true
}
}
return false
}

View File

@@ -1 +0,0 @@
package armotypes

View File

@@ -1,196 +0,0 @@
package cautils
import (
"encoding/json"
"fmt"
"os"
"strings"
"github.com/golang/glog"
)
// labels added to the workload
const (
ArmoPrefix string = "armo"
ArmoAttach string = ArmoPrefix + ".attach"
ArmoInitialSecret string = ArmoPrefix + ".initial"
ArmoSecretStatus string = ArmoPrefix + ".secret"
ArmoCompatibleLabel string = ArmoPrefix + ".compatible"
ArmoSecretProtectStatus string = "protect"
ArmoSecretClearStatus string = "clear"
)
// annotations added to the workload
const (
ArmoUpdate string = ArmoPrefix + ".last-update"
ArmoWlid string = ArmoPrefix + ".wlid"
ArmoSid string = ArmoPrefix + ".sid"
ArmoJobID string = ArmoPrefix + ".job"
ArmoJobIDPath string = ArmoJobID + "/id"
ArmoJobParentPath string = ArmoJobID + "/parent"
ArmoJobActionPath string = ArmoJobID + "/action"
ArmoCompatibleAnnotation string = ArmoAttach + "/compatible"
ArmoReplaceheaders string = ArmoAttach + "/replaceheaders"
)
const ( // DEPRECATED
CAAttachLabel string = "cyberarmor"
Patched string = "Patched"
Done string = "Done"
Encrypted string = "Protected"
CAInjectOld = "injectCyberArmor"
CAPrefix string = "cyberarmor"
CAProtectedSecret string = CAPrefix + ".secret"
CAInitialSecret string = CAPrefix + ".initial"
CAInject string = CAPrefix + ".inject"
CAIgnore string = CAPrefix + ".ignore"
CAReplaceHeaders string = CAPrefix + ".removeSecurityHeaders"
)
const ( // DEPRECATED
CAUpdate string = CAPrefix + ".last-update"
CAStatus string = CAPrefix + ".status"
CAWlid string = CAPrefix + ".wlid"
)
type ClusterConfig struct {
EventReceiverREST string `json:"eventReceiverREST"`
EventReceiverWS string `json:"eventReceiverWS"`
MaserNotificationServer string `json:"maserNotificationServer"`
Postman string `json:"postman"`
Dashboard string `json:"dashboard"`
Portal string `json:"portal"`
CustomerGUID string `json:"customerGUID"`
ClusterGUID string `json:"clusterGUID"`
ClusterName string `json:"clusterName"`
OciImageURL string `json:"ociImageURL"`
NotificationWSURL string `json:"notificationWSURL"`
NotificationRestURL string `json:"notificationRestURL"`
VulnScanURL string `json:"vulnScanURL"`
OracleURL string `json:"oracleURL"`
ClairURL string `json:"clairURL"`
}
// represents workload basic info
type SpiffeBasicInfo struct {
//cluster/datacenter
Level0 string `json:"level0"`
Level0Type string `json:"level0Type"`
//namespace/project
Level1 string `json:"level0"`
Level1Type string `json:"level0Type"`
Kind string `json:"kind"`
Name string `json:"name"`
}
type ImageInfo struct {
Registry string `json:"registry"`
VersionImage string `json:"versionImage"`
}
func IsAttached(labels map[string]string) *bool {
attach := false
if labels == nil {
return nil
}
if attached, ok := labels[ArmoAttach]; ok {
if strings.ToLower(attached) == "true" {
attach = true
return &attach
} else {
return &attach
}
}
// deprecated
if _, ok := labels[CAAttachLabel]; ok {
attach = true
return &attach
}
// deprecated
if inject, ok := labels[CAInject]; ok {
if strings.ToLower(inject) == "true" {
attach = true
return &attach
}
}
// deprecated
if ignore, ok := labels[CAIgnore]; ok {
if strings.ToLower(ignore) == "true" {
return &attach
}
}
return nil
}
func IsSecretProtected(labels map[string]string) *bool {
protect := false
if labels == nil {
return nil
}
if protected, ok := labels[ArmoSecretStatus]; ok {
if strings.ToLower(protected) == ArmoSecretProtectStatus {
protect = true
return &protect
} else {
return &protect
}
}
return nil
}
func LoadConfig(configPath string, loadToEnv bool) (*ClusterConfig, error) {
if configPath == "" {
configPath = "/etc/config/clusterData.json"
}
dat, err := os.ReadFile(configPath)
if err != nil || len(dat) == 0 {
return nil, fmt.Errorf("Config empty or not found. path: %s", configPath)
}
componentConfig := &ClusterConfig{}
if err := json.Unmarshal(dat, componentConfig); err != nil {
return componentConfig, fmt.Errorf("Failed to read component config, path: %s, reason: %s", configPath, err.Error())
}
if loadToEnv {
componentConfig.LoadConfigToEnv()
}
return componentConfig, nil
}
func (clusterConfig *ClusterConfig) LoadConfigToEnv() {
SetEnv("CA_CLUSTER_NAME", clusterConfig.ClusterName)
SetEnv("CA_CLUSTER_GUID", clusterConfig.ClusterGUID)
SetEnv("CA_ORACLE_SERVER", clusterConfig.OracleURL)
SetEnv("CA_CUSTOMER_GUID", clusterConfig.CustomerGUID)
SetEnv("CA_DASHBOARD_BACKEND", clusterConfig.Dashboard)
SetEnv("CA_NOTIFICATION_SERVER_REST", clusterConfig.NotificationWSURL)
SetEnv("CA_NOTIFICATION_SERVER_WS", clusterConfig.NotificationWSURL)
SetEnv("CA_NOTIFICATION_SERVER_REST", clusterConfig.NotificationRestURL)
SetEnv("CA_OCIMAGE_URL", clusterConfig.OciImageURL)
SetEnv("CA_K8S_REPORT_URL", clusterConfig.EventReceiverWS)
SetEnv("CA_EVENT_RECEIVER_HTTP", clusterConfig.EventReceiverREST)
SetEnv("CA_VULNSCAN", clusterConfig.VulnScanURL)
SetEnv("CA_POSTMAN", clusterConfig.Postman)
SetEnv("MASTER_NOTIFICATION_SERVER_HOST", clusterConfig.MaserNotificationServer)
SetEnv("CLAIR_URL", clusterConfig.ClairURL)
}
func SetEnv(key, value string) {
if e := os.Getenv(key); e == "" {
if err := os.Setenv(key, value); err != nil {
glog.Warningf("%s: %s", key, err.Error())
}
}
}

View File

@@ -1,29 +0,0 @@
package cautils
import (
"testing"
)
// tests wlid parse
func TestSpiffeWLIDToInfoSuccess(t *testing.T) {
WLID := "wlid://cluster-HipsterShopCluster2/namespace-prod/deployment-cartservice"
ms, er := SpiffeToSpiffeInfo(WLID)
if er != nil || ms.Level0 != "HipsterShopCluster2" || ms.Level0Type != "cluster" || ms.Level1 != "prod" || ms.Level1Type != "namespace" ||
ms.Kind != "deployment" || ms.Name != "cartservice" {
t.Errorf("TestSpiffeWLIDToInfoSuccess failed to parse %v", WLID)
}
}
func TestSpiffeSIDInfoSuccess(t *testing.T) {
SID := "sid://cluster-HipsterShopCluster2/namespace-dev/secret-caregcred"
ms, er := SpiffeToSpiffeInfo(SID)
if er != nil || ms.Level0 != "HipsterShopCluster2" || ms.Level0Type != "cluster" || ms.Level1 != "dev" || ms.Level1Type != "namespace" ||
ms.Kind != "secret" || ms.Name != "caregcred" {
t.Errorf("TestSpiffeSIDInfoSuccess failed to parse %v", SID)
}
}

View File

@@ -1,118 +0,0 @@
package cautils
import (
"crypto/sha256"
"fmt"
"strings"
)
// wlid/ sid utils
const (
SpiffePrefix = "://"
)
// wlid/ sid utils
const (
PackagePath = "vendor/github.com/armosec/capacketsgo"
)
//AsSHA256 takes anything turns it into string :) https://blog.8bitzen.com/posts/22-08-2019-how-to-hash-a-struct-in-go
func AsSHA256(v interface{}) string {
h := sha256.New()
h.Write([]byte(fmt.Sprintf("%v", v)))
return fmt.Sprintf("%x", h.Sum(nil))
}
func SpiffeToSpiffeInfo(spiffe string) (*SpiffeBasicInfo, error) {
basicInfo := &SpiffeBasicInfo{}
pos := strings.Index(spiffe, SpiffePrefix)
if pos < 0 {
return nil, fmt.Errorf("invalid spiffe %s", spiffe)
}
pos += len(SpiffePrefix)
spiffeNoPrefix := spiffe[pos:]
splits := strings.Split(spiffeNoPrefix, "/")
if len(splits) < 3 {
return nil, fmt.Errorf("invalid spiffe %s", spiffe)
}
p0 := strings.Index(splits[0], "-")
p1 := strings.Index(splits[1], "-")
p2 := strings.Index(splits[2], "-")
if p0 == -1 || p1 == -1 || p2 == -1 {
return nil, fmt.Errorf("invalid spiffe %s", spiffe)
}
basicInfo.Level0Type = splits[0][:p0]
basicInfo.Level0 = splits[0][p0+1:]
basicInfo.Level1Type = splits[1][:p1]
basicInfo.Level1 = splits[1][p1+1:]
basicInfo.Kind = splits[2][:p2]
basicInfo.Name = splits[2][p2+1:]
return basicInfo, nil
}
func ImageTagToImageInfo(imageTag string) (*ImageInfo, error) {
ImageInfo := &ImageInfo{}
spDelimiter := "/"
pos := strings.Index(imageTag, spDelimiter)
if pos < 0 {
ImageInfo.Registry = ""
ImageInfo.VersionImage = imageTag
return ImageInfo, nil
}
splits := strings.Split(imageTag, spDelimiter)
if len(splits) == 0 {
return nil, fmt.Errorf("Invalid image info %s", imageTag)
}
ImageInfo.Registry = splits[0]
if len(splits) > 1 {
ImageInfo.VersionImage = splits[len(splits)-1]
} else {
ImageInfo.VersionImage = ""
}
return ImageInfo, nil
}
func BoolPointer(b bool) *bool { return &b }
func BoolToString(b bool) string {
if b {
return "true"
}
return "false"
}
func BoolPointerToString(b *bool) string {
if b == nil {
return ""
}
if *b {
return "true"
}
return "false"
}
func StringToBool(s string) bool {
if strings.ToLower(s) == "true" || strings.ToLower(s) == "1" {
return true
}
return false
}
func StringToBoolPointer(s string) *bool {
if strings.ToLower(s) == "true" || strings.ToLower(s) == "1" {
return BoolPointer(true)
}
if strings.ToLower(s) == "false" || strings.ToLower(s) == "0" {
return BoolPointer(false)
}
return nil
}

View File

@@ -1,52 +0,0 @@
package cautils
import (
"fmt"
"hash/fnv"
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
var NamespacesListToIgnore = make([]string, 0)
var KubeNamespaces = []string{metav1.NamespaceSystem, metav1.NamespacePublic}
// NamespacesListToIgnore namespaces to ignore if a pod
func InitNamespacesListToIgnore(caNamespace string) {
if len(NamespacesListToIgnore) > 0 {
return
}
NamespacesListToIgnore = append(NamespacesListToIgnore, KubeNamespaces...)
NamespacesListToIgnore = append(NamespacesListToIgnore, caNamespace)
}
func IfIgnoreNamespace(ns string) bool {
for i := range NamespacesListToIgnore {
if NamespacesListToIgnore[i] == ns {
return true
}
}
return false
}
func IfKubeNamespace(ns string) bool {
for i := range KubeNamespaces {
if NamespacesListToIgnore[i] == ns {
return true
}
}
return false
}
func hash(s string) string {
h := fnv.New32a()
h.Write([]byte(s))
return fmt.Sprintf("%d", h.Sum32())
}
func GenarateConfigMapName(wlid string) string {
name := strings.ToLower(fmt.Sprintf("ca-%s-%s-%s", GetNamespaceFromWlid(wlid), GetKindFromWlid(wlid), GetNameFromWlid(wlid)))
if len(name) >= 63 {
name = hash(name)
}
return name
}

View File

@@ -1,238 +0,0 @@
package cautils
import (
"fmt"
"strings"
)
// API fields
var (
WlidPrefix = "wlid://"
SidPrefix = "sid://"
ClusterWlidPrefix = "cluster-"
NamespaceWlidPrefix = "namespace-"
DataCenterWlidPrefix = "datacenter-"
ProjectWlidPrefix = "project-"
SecretSIDPrefix = "secret-"
SubSecretSIDPrefix = "subsecret-"
K8SKindsList = []string{"ComponentStatus", "ConfigMap", "ControllerRevision", "CronJob",
"CustomResourceDefinition", "DaemonSet", "Deployment", "Endpoints", "Event", "HorizontalPodAutoscaler",
"Ingress", "Job", "Lease", "LimitRange", "LocalSubjectAccessReview", "MutatingWebhookConfiguration",
"Namespace", "NetworkPolicy", "Node", "PersistentVolume", "PersistentVolumeClaim", "Pod",
"PodDisruptionBudget", "PodSecurityPolicy", "PodTemplate", "PriorityClass", "ReplicaSet",
"ReplicationController", "ResourceQuota", "Role", "RoleBinding", "Secret", "SelfSubjectAccessReview",
"SelfSubjectRulesReview", "Service", "ServiceAccount", "StatefulSet", "StorageClass",
"SubjectAccessReview", "TokenReview", "ValidatingWebhookConfiguration", "VolumeAttachment"}
NativeKindsList = []string{"Dockerized", "Native"}
KindReverseMap = map[string]string{}
dataImagesList = []string{}
)
func IsWlid(id string) bool {
return strings.HasPrefix(id, WlidPrefix)
}
func IsSid(id string) bool {
return strings.HasPrefix(id, SidPrefix)
}
// GetK8SKindFronList get the calculated wlid
func GetK8SKindFronList(kind string) string { // TODO GetK8SKindFromList
for i := range K8SKindsList {
if strings.ToLower(kind) == strings.ToLower(K8SKindsList[i]) {
return K8SKindsList[i]
}
}
return kind
}
// IsK8SKindInList Check if the kind is a known kind
func IsK8SKindInList(kind string) bool {
for i := range K8SKindsList {
if strings.ToLower(kind) == strings.ToLower(K8SKindsList[i]) {
return true
}
}
return false
}
// generateWLID
func generateWLID(pLevel0, level0, pLevel1, level1, k, name string) string {
kind := strings.ToLower(k)
kind = strings.Replace(kind, "-", "", -1)
wlid := WlidPrefix
wlid += fmt.Sprintf("%s%s", pLevel0, level0)
if level1 == "" {
return wlid
}
wlid += fmt.Sprintf("/%s%s", pLevel1, level1)
if kind == "" {
return wlid
}
wlid += fmt.Sprintf("/%s", kind)
if name == "" {
return wlid
}
wlid += fmt.Sprintf("-%s", name)
return wlid
}
// GetWLID get the calculated wlid
func GetWLID(level0, level1, k, name string) string {
return generateWLID(ClusterWlidPrefix, level0, NamespaceWlidPrefix, level1, k, name)
}
// GetK8sWLID get the k8s calculated wlid
func GetK8sWLID(level0, level1, k, name string) string {
return generateWLID(ClusterWlidPrefix, level0, NamespaceWlidPrefix, level1, k, name)
}
// GetNativeWLID get the native calculated wlid
func GetNativeWLID(level0, level1, k, name string) string {
return generateWLID(DataCenterWlidPrefix, level0, ProjectWlidPrefix, level1, k, name)
}
// WildWlidContainsWlid does WildWlid contains Wlid
func WildWlidContainsWlid(wildWlid, wlid string) bool { // TODO- test
if wildWlid == wlid {
return true
}
wildWlidR, _ := RestoreMicroserviceIDsFromSpiffe(wildWlid)
wlidR, _ := RestoreMicroserviceIDsFromSpiffe(wlid)
if len(wildWlidR) > len(wildWlidR) {
// invalid wlid
return false
}
for i := range wildWlidR {
if wildWlidR[i] != wlidR[i] {
return false
}
}
return true
}
func restoreInnerIdentifiersFromID(spiffeSlices []string) []string {
if len(spiffeSlices) >= 1 && strings.HasPrefix(spiffeSlices[0], ClusterWlidPrefix) {
spiffeSlices[0] = spiffeSlices[0][len(ClusterWlidPrefix):]
}
if len(spiffeSlices) >= 2 && strings.HasPrefix(spiffeSlices[1], NamespaceWlidPrefix) {
spiffeSlices[1] = spiffeSlices[1][len(NamespaceWlidPrefix):]
}
if len(spiffeSlices) >= 3 && strings.Contains(spiffeSlices[2], "-") {
dashIdx := strings.Index(spiffeSlices[2], "-")
spiffeSlices = append(spiffeSlices, spiffeSlices[2][dashIdx+1:])
spiffeSlices[2] = spiffeSlices[2][:dashIdx]
if val, ok := KindReverseMap[spiffeSlices[2]]; ok {
spiffeSlices[2] = val
}
}
return spiffeSlices
}
// RestoreMicroserviceIDsFromSpiffe -
func RestoreMicroserviceIDsFromSpiffe(spiffe string) ([]string, error) {
if spiffe == "" {
return nil, fmt.Errorf("in RestoreMicroserviceIDsFromSpiffe, expecting valid wlid recieved empty string")
}
if StringHasWhitespace(spiffe) {
return nil, fmt.Errorf("wlid %s invalid. whitespace found", spiffe)
}
if strings.HasPrefix(spiffe, WlidPrefix) {
spiffe = spiffe[len(WlidPrefix):]
} else if strings.HasPrefix(spiffe, SidPrefix) {
spiffe = spiffe[len(SidPrefix):]
}
spiffeSlices := strings.Split(spiffe, "/")
// The documented WLID format (https://cyberarmorio.sharepoint.com/sites/development2/Shared%20Documents/kubernetes_design1.docx?web=1)
if len(spiffeSlices) <= 3 {
spiffeSlices = restoreInnerIdentifiersFromID(spiffeSlices)
}
if len(spiffeSlices) != 4 { // first used WLID, deprecated since 24.10.2019
return spiffeSlices, fmt.Errorf("invalid WLID format. format received: %v", spiffeSlices)
}
for i := range spiffeSlices {
if spiffeSlices[i] == "" {
return spiffeSlices, fmt.Errorf("one or more entities are empty, spiffeSlices: %v", spiffeSlices)
}
}
return spiffeSlices, nil
}
// RestoreMicroserviceIDsFromSpiffe -
func RestoreMicroserviceIDs(spiffe string) []string {
if spiffe == "" {
return []string{}
}
if StringHasWhitespace(spiffe) {
return []string{}
}
if strings.HasPrefix(spiffe, WlidPrefix) {
spiffe = spiffe[len(WlidPrefix):]
} else if strings.HasPrefix(spiffe, SidPrefix) {
spiffe = spiffe[len(SidPrefix):]
}
spiffeSlices := strings.Split(spiffe, "/")
return restoreInnerIdentifiersFromID(spiffeSlices)
}
// GetClusterFromWlid parse wlid and get cluster
func GetClusterFromWlid(wlid string) string {
r := RestoreMicroserviceIDs(wlid)
if len(r) >= 1 {
return r[0]
}
return ""
}
// GetNamespaceFromWlid parse wlid and get Namespace
func GetNamespaceFromWlid(wlid string) string {
r := RestoreMicroserviceIDs(wlid)
if len(r) >= 2 {
return r[1]
}
return ""
}
// GetKindFromWlid parse wlid and get kind
func GetKindFromWlid(wlid string) string {
r := RestoreMicroserviceIDs(wlid)
if len(r) >= 3 {
return GetK8SKindFronList(r[2])
}
return ""
}
// GetNameFromWlid parse wlid and get name
func GetNameFromWlid(wlid string) string {
r := RestoreMicroserviceIDs(wlid)
if len(r) >= 4 {
return GetK8SKindFronList(r[3])
}
return ""
}
// IsWlidValid test if wlid is a valid wlid
func IsWlidValid(wlid string) error {
_, err := RestoreMicroserviceIDsFromSpiffe(wlid)
return err
}
// StringHasWhitespace check if a string has whitespace
func StringHasWhitespace(str string) bool {
if whitespace := strings.Index(str, " "); whitespace != -1 {
return true
}
return false
}

View File

@@ -11,7 +11,7 @@ import (
"github.com/armosec/kubescape/cautils/getter"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/armosec/kubescape/cautils/k8sinterface"
"github.com/armosec/k8s-interface/k8sinterface"
corev1 "k8s.io/api/core/v1"
)
@@ -30,6 +30,7 @@ type ConfigObj struct {
CustomerGUID string `json:"customerGUID"`
Token string `json:"invitationParam"`
CustomerAdminEMail string `json:"adminMail"`
ClusterName string `json:"clusterName"`
}
func (co *ConfigObj) Json() []byte {
@@ -39,14 +40,30 @@ func (co *ConfigObj) Json() []byte {
return []byte{}
}
// Config - convert ConfigObj to config file
func (co *ConfigObj) Config() []byte {
clusterName := co.ClusterName
co.ClusterName = "" // remove cluster name before saving to file
b, err := json.Marshal(co)
co.ClusterName = clusterName
if err == nil {
return b
}
return []byte{}
}
// ======================================================================================
// =============================== interface ============================================
// ======================================================================================
type IClusterConfig interface {
// setters
SetCustomerGUID(customerGUID string) error
// set
SetConfig(customerGUID string) error
// getters
GetClusterName() string
GetCustomerGUID() string
GetConfigObj() *ConfigObj
GetK8sAPI() *k8sinterface.KubernetesApi
@@ -92,8 +109,10 @@ func ClusterConfigSetup(scanInfo *ScanInfo, k8s *k8sinterface.KubernetesApi, beA
return NewEmptyConfig() // local - Delete local config & Do not send report
}
if scanInfo.Local {
scanInfo.Submit = false
return NewEmptyConfig() // local - Do not send report
}
scanInfo.Submit = true
return clusterConfig // submit/default - Submit report
}
@@ -103,16 +122,17 @@ func ClusterConfigSetup(scanInfo *ScanInfo, k8s *k8sinterface.KubernetesApi, beA
type EmptyConfig struct {
}
func NewEmptyConfig() *EmptyConfig { return &EmptyConfig{} }
func (c *EmptyConfig) GetConfigObj() *ConfigObj { return &ConfigObj{} }
func (c *EmptyConfig) SetCustomerGUID(customerGUID string) error { return nil }
func (c *EmptyConfig) GetCustomerGUID() string { return "" }
func (c *EmptyConfig) GetK8sAPI() *k8sinterface.KubernetesApi { return nil } // TODO: return mock obj
func (c *EmptyConfig) GetDefaultNS() string { return k8sinterface.GetDefaultNamespace() }
func (c *EmptyConfig) GetBackendAPI() getter.IBackend { return nil } // TODO: return mock obj
func NewEmptyConfig() *EmptyConfig { return &EmptyConfig{} }
func (c *EmptyConfig) SetConfig(customerGUID string) error { return nil }
func (c *EmptyConfig) GetConfigObj() *ConfigObj { return &ConfigObj{} }
func (c *EmptyConfig) GetCustomerGUID() string { return "" }
func (c *EmptyConfig) GetK8sAPI() *k8sinterface.KubernetesApi { return nil } // TODO: return mock obj
func (c *EmptyConfig) GetDefaultNS() string { return k8sinterface.GetDefaultNamespace() }
func (c *EmptyConfig) GetBackendAPI() getter.IBackend { return nil } // TODO: return mock obj
func (c *EmptyConfig) GetClusterName() string { return k8sinterface.GetClusterName() }
func (c *EmptyConfig) GenerateURL() {
message := fmt.Sprintf("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: https://%s", getter.GetArmoAPIConnector().GetFrontendURL())
InfoTextDisplay(os.Stdout, message+"\n")
message := fmt.Sprintf("\nCheckout for more cool features: https://%s\n", getter.GetArmoAPIConnector().GetFrontendURL())
InfoTextDisplay(os.Stdout, fmt.Sprintf("\n%s\n", message))
}
// ======================================================================================
@@ -140,6 +160,7 @@ func (c *ClusterConfig) GetDefaultNS() string { return c.defau
func (c *ClusterConfig) GetBackendAPI() getter.IBackend { return c.backendAPI }
func (c *ClusterConfig) GenerateURL() {
message := "Checkout for more cool features: "
u := url.URL{}
u.Scheme = "https"
@@ -147,9 +168,8 @@ func (c *ClusterConfig) GenerateURL() {
if c.configObj == nil {
return
}
message := fmt.Sprintf("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: %s", u.String())
if c.configObj.CustomerAdminEMail != "" {
InfoTextDisplay(os.Stdout, message+"\n")
InfoTextDisplay(os.Stdout, "\n\n"+message+u.String()+"\n\n")
return
}
u.Path = "account/sign-up"
@@ -158,8 +178,7 @@ func (c *ClusterConfig) GenerateURL() {
q.Add("customerGUID", c.configObj.CustomerGUID)
u.RawQuery = q.Encode()
InfoTextDisplay(os.Stdout, message+"\n")
InfoTextDisplay(os.Stdout, "\n\n"+message+u.String()+"\n\n")
}
func (c *ClusterConfig) GetCustomerGUID() string {
@@ -169,10 +188,19 @@ func (c *ClusterConfig) GetCustomerGUID() string {
return ""
}
func (c *ClusterConfig) SetCustomerGUID(customerGUID string) error {
func (c *ClusterConfig) SetConfig(customerGUID string) error {
if c.configObj == nil {
c.configObj = &ConfigObj{}
}
// cluster name
if c.GetClusterName() == "" {
c.setClusterName(k8sinterface.GetClusterName())
}
// ARMO customer GUID
if customerGUID != "" && c.GetCustomerGUID() != customerGUID {
c.configObj.CustomerGUID = customerGUID // override config customerGUID
c.setCustomerGUID(customerGUID) // override config customerGUID
}
customerGUID = c.GetCustomerGUID()
@@ -181,10 +209,10 @@ func (c *ClusterConfig) SetCustomerGUID(customerGUID string) error {
tenantResponse, err := c.backendAPI.GetCustomerGUID(customerGUID)
if err == nil && tenantResponse != nil {
if tenantResponse.AdminMail != "" { // this customer already belongs to some user
c.configObj.CustomerAdminEMail = tenantResponse.AdminMail
c.setCustomerAdminEMail(tenantResponse.AdminMail)
} else {
c.configObj.Token = tenantResponse.Token
c.configObj.CustomerGUID = tenantResponse.TenantID
c.setToken(tenantResponse.Token)
c.setCustomerGUID(tenantResponse.TenantID)
}
} else {
if err != nil && !strings.Contains(err.Error(), "already exists") {
@@ -198,15 +226,28 @@ func (c *ClusterConfig) SetCustomerGUID(customerGUID string) error {
} else {
c.createConfigMap()
}
if existsConfigFile() {
c.updateConfigFile()
} else {
c.createConfigFile()
}
c.updateConfigFile()
return nil
}
func (c *ClusterConfig) setToken(token string) {
c.configObj.Token = token
}
func (c *ClusterConfig) setCustomerAdminEMail(customerAdminEMail string) {
c.configObj.CustomerAdminEMail = customerAdminEMail
}
func (c *ClusterConfig) setCustomerGUID(customerGUID string) {
c.configObj.CustomerGUID = customerGUID
}
func (c *ClusterConfig) setClusterName(clusterName string) {
c.configObj.ClusterName = adoptClusterName(clusterName)
}
func (c *ClusterConfig) GetClusterName() string {
return c.configObj.ClusterName
}
func (c *ClusterConfig) LoadConfig() {
// get from configMap
if c.existsConfigMap() {
@@ -220,8 +261,9 @@ func (c *ClusterConfig) LoadConfig() {
func (c *ClusterConfig) ToMapString() map[string]interface{} {
m := map[string]interface{}{}
bc, _ := json.Marshal(c.configObj)
json.Unmarshal(bc, &m)
if bc, err := json.Marshal(c.configObj); err == nil {
json.Unmarshal(bc, &m)
}
return m
}
func (c *ClusterConfig) loadConfigFromConfigMap() (*ConfigObj, error) {
@@ -360,14 +402,7 @@ func (c *ClusterConfig) updateConfigMap() error {
}
func (c *ClusterConfig) updateConfigFile() error {
if err := os.WriteFile(ConfigFileFullPath(), c.configObj.Json(), 0664); err != nil {
return err
}
return nil
}
func (c *ClusterConfig) createConfigFile() error {
if err := os.WriteFile(ConfigFileFullPath(), c.configObj.Json(), 0664); err != nil {
if err := os.WriteFile(ConfigFileFullPath(), c.configObj.Config(), 0664); err != nil {
return err
}
return nil
@@ -437,3 +472,7 @@ func DeleteConfigMap(k8s *k8sinterface.KubernetesApi) error {
func DeleteConfigFile() error {
return os.Remove(ConfigFileFullPath())
}
func adoptClusterName(clusterName string) string {
return strings.ReplaceAll(clusterName, "/", "-")
}

View File

@@ -1,25 +1,25 @@
package cautils
import (
"github.com/armosec/kubescape/cautils/armotypes"
"github.com/armosec/kubescape/cautils/opapolicy"
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/opa-utils/reporthandling"
)
// K8SResources map[<api group>/<api version>/<resource>]<resource object>
type K8SResources map[string]interface{}
type OPASessionObj struct {
Frameworks []opapolicy.Framework
Frameworks []reporthandling.Framework
K8SResources *K8SResources
Exceptions []armotypes.PostureExceptionPolicy
PostureReport *opapolicy.PostureReport
PostureReport *reporthandling.PostureReport
}
func NewOPASessionObj(frameworks []opapolicy.Framework, k8sResources *K8SResources) *OPASessionObj {
func NewOPASessionObj(frameworks []reporthandling.Framework, k8sResources *K8SResources) *OPASessionObj {
return &OPASessionObj{
Frameworks: frameworks,
K8SResources: k8sResources,
PostureReport: &opapolicy.PostureReport{
PostureReport: &reporthandling.PostureReport{
ClusterName: ClusterName,
CustomerGUID: CustomerGUID,
},
@@ -30,7 +30,7 @@ func NewOPASessionObjMock() *OPASessionObj {
return &OPASessionObj{
Frameworks: nil,
K8SResources: nil,
PostureReport: &opapolicy.PostureReport{
PostureReport: &reporthandling.PostureReport{
ClusterName: "",
CustomerGUID: "",
ReportID: "",
@@ -44,8 +44,8 @@ type ComponentConfig struct {
}
type Exception struct {
Ignore *bool `json:"ignore"` // ignore test results
MultipleScore *opapolicy.AlertScore `json:"multipleScore"` // MultipleScore number - float32
Namespaces []string `json:"namespaces"`
Regex string `json:"regex"` // not supported
Ignore *bool `json:"ignore"` // ignore test results
MultipleScore *reporthandling.AlertScore `json:"multipleScore"` // MultipleScore number - float32
Namespaces []string `json:"namespaces"`
Regex string `json:"regex"` // not supported
}

View File

@@ -3,4 +3,5 @@ package cautils
type DownloadInfo struct {
Path string
FrameworkName string
ControlName string
}

View File

@@ -5,8 +5,8 @@ import (
"net/http"
"time"
"github.com/armosec/kubescape/cautils/armotypes"
"github.com/armosec/kubescape/cautils/opapolicy"
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/opa-utils/reporthandling"
"github.com/golang/glog"
)
@@ -91,13 +91,13 @@ func (armoAPI *ArmoAPI) GetReportReceiverURL() string {
return armoAPI.erURL
}
func (armoAPI *ArmoAPI) GetFramework(name string) (*opapolicy.Framework, error) {
func (armoAPI *ArmoAPI) GetFramework(name string) (*reporthandling.Framework, error) {
respStr, err := HttpGetter(armoAPI.httpClient, armoAPI.getFrameworkURL(name))
if err != nil {
return nil, err
}
framework := &opapolicy.Framework{}
framework := &reporthandling.Framework{}
if err = JSONDecoder(respStr).Decode(framework); err != nil {
return nil, err
}

View File

@@ -1,13 +1,10 @@
package getter
import (
"encoding/json"
"fmt"
"io"
"net/http"
"time"
"strings"
"github.com/armosec/kubescape/cautils/opapolicy"
"github.com/armosec/opa-utils/gitregostore"
"github.com/armosec/opa-utils/reporthandling"
)
// =======================================================================================================================
@@ -16,71 +13,34 @@ import (
// Download released version
type DownloadReleasedPolicy struct {
hostURL string
httpClient *http.Client
gs *gitregostore.GitRegoStore
}
func NewDownloadReleasedPolicy() *DownloadReleasedPolicy {
return &DownloadReleasedPolicy{
hostURL: "",
httpClient: &http.Client{Timeout: 61 * time.Second},
gs: gitregostore.InitDefaultGitRegoStore(-1),
}
}
func (drp *DownloadReleasedPolicy) GetFramework(name string) (*opapolicy.Framework, error) {
if err := drp.setURL(name); err != nil {
return nil, err
// Return control per name/id using ARMO api
func (drp *DownloadReleasedPolicy) GetControl(policyName string) (*reporthandling.Control, error) {
var control *reporthandling.Control
var err error
if strings.HasPrefix(policyName, "C-") || strings.HasPrefix(policyName, "c-") {
control, err = drp.gs.GetOPAControlByID(policyName)
} else {
control, err = drp.gs.GetOPAControlByName(policyName)
}
respStr, err := HttpGetter(drp.httpClient, drp.hostURL)
if err != nil {
return nil, err
}
return control, nil
}
framework := &opapolicy.Framework{}
if err = JSONDecoder(respStr).Decode(framework); err != nil {
return framework, err
func (drp *DownloadReleasedPolicy) GetFramework(name string) (*reporthandling.Framework, error) {
framework, err := drp.gs.GetOPAFrameworkByName(name)
if err != nil {
return nil, err
}
SaveFrameworkInFile(framework, GetDefaultPath(name+".json"))
return framework, err
}
func (drp *DownloadReleasedPolicy) setURL(frameworkName string) error {
latestReleases := "https://api.github.com/repos/armosec/regolibrary/releases/latest"
resp, err := http.Get(latestReleases)
if err != nil {
return fmt.Errorf("failed to get latest releases from '%s', reason: %s", latestReleases, err.Error())
}
defer resp.Body.Close()
if resp.StatusCode < 200 || 301 < resp.StatusCode {
return fmt.Errorf("failed to download file, status code: %s", resp.Status)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read response body from '%s', reason: %s", latestReleases, err.Error())
}
var data map[string]interface{}
err = json.Unmarshal(body, &data)
if err != nil {
return fmt.Errorf("failed to unmarshal response body from '%s', reason: %s", latestReleases, err.Error())
}
if assets, ok := data["assets"].([]interface{}); ok {
for i := range assets {
if asset, ok := assets[i].(map[string]interface{}); ok {
if name, ok := asset["name"].(string); ok {
if name == frameworkName {
if url, ok := asset["browser_download_url"].(string); ok {
drp.hostURL = url
return nil
}
}
}
}
}
}
return fmt.Errorf("failed to download '%s' - not found", frameworkName)
}

View File

@@ -1,12 +1,13 @@
package getter
import (
"github.com/armosec/kubescape/cautils/armotypes"
"github.com/armosec/kubescape/cautils/opapolicy"
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/opa-utils/reporthandling"
)
type IPolicyGetter interface {
GetFramework(name string) (*opapolicy.Framework, error)
GetFramework(name string) (*reporthandling.Framework, error)
GetControl(policyName string) (*reporthandling.Control, error)
}
type IExceptionsGetter interface {

View File

@@ -10,7 +10,7 @@ import (
"path/filepath"
"strings"
"github.com/armosec/kubescape/cautils/opapolicy"
"github.com/armosec/opa-utils/reporthandling"
)
func GetDefaultPath(name string) string {
@@ -21,7 +21,32 @@ func GetDefaultPath(name string) string {
return defaultfilePath
}
func SaveFrameworkInFile(framework *opapolicy.Framework, pathStr string) error {
// Save control as json in file
func SaveControlInFile(control *reporthandling.Control, pathStr string) error {
encodedData, err := json.Marshal(control)
if err != nil {
return err
}
err = os.WriteFile(pathStr, []byte(fmt.Sprintf("%v", string(encodedData))), 0644)
if err != nil {
if os.IsNotExist(err) {
pathDir := path.Dir(pathStr)
if err := os.Mkdir(pathDir, 0744); err != nil {
return err
}
} else {
return err
}
err = os.WriteFile(pathStr, []byte(fmt.Sprintf("%v", string(encodedData))), 0644)
if err != nil {
return err
}
}
return nil
}
func SaveFrameworkInFile(framework *reporthandling.Framework, pathStr string) error {
encodedData, err := json.Marshal(framework)
if err != nil {
return err

View File

@@ -6,8 +6,8 @@ import (
"os"
"strings"
"github.com/armosec/kubescape/cautils/armotypes"
"github.com/armosec/kubescape/cautils/opapolicy"
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/opa-utils/reporthandling"
)
// =======================================================================================================================
@@ -26,15 +26,37 @@ func NewLoadPolicy(filePath string) *LoadPolicy {
}
}
func (lp *LoadPolicy) GetFramework(frameworkName string) (*opapolicy.Framework, error) {
// Return control from file
func (lp *LoadPolicy) GetControl(controlName string) (*reporthandling.Control, error) {
framework := &opapolicy.Framework{}
control := &reporthandling.Control{}
f, err := os.ReadFile(lp.filePath)
if err != nil {
return nil, err
}
err = json.Unmarshal(f, framework)
if err = json.Unmarshal(f, control); err != nil {
return control, err
}
if controlName != "" && !strings.EqualFold(controlName, control.Name) && !strings.EqualFold(controlName, control.ControlID) {
return nil, fmt.Errorf("control from file not matching")
}
return control, err
}
func (lp *LoadPolicy) GetFramework(frameworkName string) (*reporthandling.Framework, error) {
framework := &reporthandling.Framework{}
f, err := os.ReadFile(lp.filePath)
if err != nil {
return nil, err
}
if err = json.Unmarshal(f, framework); err != nil {
return framework, err
}
if frameworkName != "" && !strings.EqualFold(frameworkName, framework.Name) {
return nil, fmt.Errorf("framework from file not matching")
}

View File

@@ -1,265 +0,0 @@
package k8sinterface
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ecr"
"github.com/docker/docker/api/types"
)
// For GCR there are some permissions one need to assign in order to allow ARMO to pull images:
// https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity
// gcloud iam service-accounts create armo-controller-sa
// gcloud projects add-iam-policy-binding <PROJECT_NAME> --role roles/storage.objectViewer --member "serviceAccount:armo-controller-sa@<PROJECT_NAME>.iam.gserviceaccount.com"
// gcloud iam service-accounts add-iam-policy-binding --role roles/iam.workloadIdentityUser --member "serviceAccount:<PROJECT_NAME>.svc.id.goog[cyberarmor-system/ca-controller-service-account]" armo-controller-sa@<PROJECT_NAME>.iam.gserviceaccount.com
// kubectl annotate serviceaccount --overwrite --namespace cyberarmor-system ca-controller-service-account iam.gke.io/gcp-service-account=armo-controller-sa@<PROJECT_NAME>.iam.gserviceaccount.com
const (
gcrDefaultServiceAccountName = "default"
// armoServiceAccountName = "ca-controller-service-account"
)
var (
httpClient = http.Client{Timeout: 5 * time.Second}
)
// CheckIsECRImage check if this image is suspected as ECR hosted image
func CheckIsECRImage(imageTag string) bool {
return strings.Contains(imageTag, "dkr.ecr")
}
// GetLoginDetailsForECR return user name + password using the default iam-role OR ~/.aws/config of the machine
func GetLoginDetailsForECR(imageTag string) (string, string, error) {
// imageTag := "015253967648.dkr.ecr.eu-central-1.amazonaws.com/armo:1"
imageTagSlices := strings.Split(imageTag, ".")
repo := imageTagSlices[0]
region := imageTagSlices[3]
mySession := session.Must(session.NewSession())
ecrClient := ecr.New(mySession, aws.NewConfig().WithRegion(region))
input := &ecr.GetAuthorizationTokenInput{
RegistryIds: []*string{&repo},
}
res, err := ecrClient.GetAuthorizationToken(input)
if err != nil {
return "", "", fmt.Errorf("in PullFromECR, failed to GetAuthorizationToken: %v", err)
}
res64 := (*res.AuthorizationData[0].AuthorizationToken)
resB, err := base64.StdEncoding.DecodeString(res64)
if err != nil {
return "", "", fmt.Errorf("in PullFromECR, failed to DecodeString: %v", err)
}
delimiterIdx := bytes.IndexByte(resB, ':')
// userName := resB[:delimiterIdx]
// resB = resB[delimiterIdx+1:]
// resB, err = base64.StdEncoding.DecodeString(string(resB))
// if err != nil {
// t.Errorf("failed to DecodeString #2: %v\n\n", err)
// }
return string(resB[:delimiterIdx]), string(resB[delimiterIdx+1:]), nil
}
func CheckIsACRImage(imageTag string) bool {
// atest1.azurecr.io/go-inf:1
return strings.Contains(imageTag, ".azurecr.io/")
}
type azureADDResponseJson struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ExpiresIn string `json:"expires_in"`
ExpiresOn string `json:"expires_on"`
NotBefore string `json:"not_before"`
Resource string `json:"resource"`
TokenType string `json:"token_type"`
}
func getAzureAADAccessToken() (string, error) {
msi_endpoint, err := url.Parse("http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01")
if err != nil {
return "", fmt.Errorf("creating URL : %v", err)
}
msi_parameters := url.Values{}
msi_parameters.Add("resource", "https://management.azure.com/")
msi_parameters.Add("api-version", "2018-02-01")
msi_endpoint.RawQuery = msi_parameters.Encode()
req, err := http.NewRequest("GET", msi_endpoint.String(), nil)
if err != nil {
return "", fmt.Errorf("creating HTTP request : %v", err)
}
req.Header.Add("Metadata", "true")
// Call managed services for Azure resources token endpoint
resp, err := httpClient.Do(req)
if err != nil {
return "", fmt.Errorf("calling token endpoint : %v", err)
}
// Pull out response body
responseBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return "", fmt.Errorf("reading response body : %v", err)
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return "", fmt.Errorf("azure ActiveDirectory AT resp: %v, %v", resp.Status, string(responseBytes))
}
// Unmarshall response body into struct
var r azureADDResponseJson
err = json.Unmarshal(responseBytes, &r)
if err != nil {
return "", fmt.Errorf("unmarshalling the response: %v", err)
}
return r.AccessToken, nil
}
// GetLoginDetailsForAzurCR return user name + password to use
func GetLoginDetailsForAzurCR(imageTag string) (string, string, error) {
// imageTag := "atest1.azurecr.io/go-inf:1"
imageTagSlices := strings.Split(imageTag, "/")
azureIdensAT, err := getAzureAADAccessToken()
if err != nil {
return "", "", err
}
atMap := make(map[string]interface{})
azureIdensATSlices := strings.Split(azureIdensAT, ".")
if len(azureIdensATSlices) < 2 {
return "", "", fmt.Errorf("len(azureIdensATSlices) < 2")
}
resB, err := base64.RawStdEncoding.DecodeString(azureIdensATSlices[1])
if err != nil {
return "", "", fmt.Errorf("in GetLoginDetailsForAzurCR, failed to DecodeString: %v, %s", err, azureIdensATSlices[1])
}
if err := json.Unmarshal(resB, &atMap); err != nil {
return "", "", fmt.Errorf("failed to unmarshal azureIdensAT: %v, %s", err, string(resB))
}
// excahnging AAD for ACR refresh token
refreshToken, err := excahngeAzureAADAccessTokenForACRRefreshToken(imageTagSlices[0], fmt.Sprintf("%v", atMap["tid"]), azureIdensAT)
if err != nil {
return "", "", fmt.Errorf("failed to excahngeAzureAADAccessTokenForACRRefreshToken: %v, registry: %s, tenantID: %s, azureAADAT: %s", err, imageTagSlices[0], fmt.Sprintf("%v", atMap["tid"]), azureIdensAT)
}
return "00000000-0000-0000-0000-000000000000", refreshToken, nil
}
func excahngeAzureAADAccessTokenForACRRefreshToken(registry, tenantID, azureAADAT string) (string, error) {
msi_parameters := url.Values{}
msi_parameters.Add("service", registry)
msi_parameters.Add("grant_type", "access_token")
msi_parameters.Add("tenant", tenantID)
msi_parameters.Add("access_token", azureAADAT)
postBodyStr := msi_parameters.Encode()
req, err := http.NewRequest("POST", fmt.Sprintf("https://%v/oauth2/exchange", registry), strings.NewReader(postBodyStr))
if err != nil {
return "", fmt.Errorf("creating HTTP request : %v", err)
}
req.Header.Add("Metadata", "true")
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
// Call managed services for Azure resources token endpoint
resp, err := httpClient.Do(req)
if err != nil {
return "", fmt.Errorf("calling token endpoint : %v", err)
}
// Pull out response body
responseBytes, err := io.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return "", fmt.Errorf("reading response body : %v", err)
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return "", fmt.Errorf("azure exchange AT resp: %v, %v", resp.Status, string(responseBytes))
}
resultMap := make(map[string]string)
err = json.Unmarshal(responseBytes, &resultMap)
if err != nil {
return "", fmt.Errorf("unmarshalling the response: %v", err)
}
return resultMap["refresh_token"], nil
}
func CheckIsGCRImage(imageTag string) bool {
// gcr.io/elated-pottery-310110/golang-inf:2
return strings.Contains(imageTag, "gcr.io/")
}
// GetLoginDetailsForGCR return user name + password to use
func GetLoginDetailsForGCR(imageTag string) (string, string, error) {
msi_endpoint, err := url.Parse(fmt.Sprintf("http://169.254.169.254/computeMetadata/v1/instance/service-accounts/%s/token", gcrDefaultServiceAccountName))
if err != nil {
return "", "", fmt.Errorf("creating URL : %v", err)
}
req, err := http.NewRequest("GET", msi_endpoint.String(), nil)
if err != nil {
return "", "", fmt.Errorf("creating HTTP request : %v", err)
}
req.Header.Add("Metadata-Flavor", "Google")
// Call managed services for Azure resources token endpoint
resp, err := httpClient.Do(req)
if err != nil {
return "", "", fmt.Errorf("calling token endpoint : %v", err)
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return "", "", fmt.Errorf("HTTP Status : %v, make sure the '%s' service account is configured for ARMO pod", resp.Status, gcrDefaultServiceAccountName)
}
defer resp.Body.Close()
respMap := make(map[string]interface{})
if err := json.NewDecoder(resp.Body).Decode(&respMap); err != nil {
return "", "", fmt.Errorf("json Decode : %v", err)
}
return "oauth2accesstoken", fmt.Sprintf("%v", respMap["access_token"]), nil
}
func GetCloudVendorRegistryCredentials(imageTag string) (map[string]types.AuthConfig, error) {
secrets := map[string]types.AuthConfig{}
var errRes error
if CheckIsACRImage(imageTag) {
userName, password, err := GetLoginDetailsForAzurCR(imageTag)
if err != nil {
errRes = fmt.Errorf("failed to GetLoginDetailsForACR(%s): %v", imageTag, err)
} else {
secrets[imageTag] = types.AuthConfig{
Username: userName,
Password: password,
}
}
}
if CheckIsECRImage(imageTag) {
userName, password, err := GetLoginDetailsForECR(imageTag)
if err != nil {
errRes = fmt.Errorf("failed to GetLoginDetailsForECR(%s): %v", imageTag, err)
} else {
secrets[imageTag] = types.AuthConfig{
Username: userName,
Password: password,
}
}
}
if CheckIsGCRImage(imageTag) {
userName, password, err := GetLoginDetailsForGCR(imageTag)
if err != nil {
errRes = fmt.Errorf("failed to GetLoginDetailsForGCR(%s): %v", imageTag, err)
} else {
secrets[imageTag] = types.AuthConfig{
Username: userName,
Password: password,
}
}
}
return secrets, errRes
}

View File

@@ -1,121 +0,0 @@
package k8sinterface
import (
"context"
"fmt"
"os"
"strings"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
// DO NOT REMOVE - load cloud providers auth
_ "k8s.io/client-go/plugin/pkg/client/auth"
"sigs.k8s.io/controller-runtime/pkg/client/config"
)
var ConnectedToCluster = true
// K8SConfig pointer to k8s config
var K8SConfig *restclient.Config
// KubernetesApi -
type KubernetesApi struct {
KubernetesClient kubernetes.Interface
DynamicClient dynamic.Interface
Context context.Context
}
// NewKubernetesApi -
func NewKubernetesApi() *KubernetesApi {
var kubernetesClient *kubernetes.Clientset
var err error
if !IsConnectedToCluster() {
fmt.Println(fmt.Errorf("failed to load kubernetes config: no configuration has been provided, try setting KUBECONFIG environment variable"))
os.Exit(1)
}
kubernetesClient, err = kubernetes.NewForConfig(GetK8sConfig())
if err != nil {
fmt.Printf("Failed to load config file, reason: %s", err.Error())
os.Exit(1)
}
dynamicClient, err := dynamic.NewForConfig(K8SConfig)
if err != nil {
fmt.Printf("Failed to load config file, reason: %s", err.Error())
os.Exit(1)
}
return &KubernetesApi{
KubernetesClient: kubernetesClient,
DynamicClient: dynamicClient,
Context: context.Background(),
}
}
// RunningIncluster whether running in cluster
var RunningIncluster bool
// LoadK8sConfig load config from local file or from cluster
func LoadK8sConfig() error {
kubeconfig, err := config.GetConfig()
if err != nil {
return fmt.Errorf("failed to load kubernetes config: %s", strings.ReplaceAll(err.Error(), "KUBERNETES_MASTER", "KUBECONFIG"))
}
if _, err := restclient.InClusterConfig(); err == nil {
RunningIncluster = true
}
K8SConfig = kubeconfig
return nil
}
// GetK8sConfig get config. load if not loaded yet
func GetK8sConfig() *restclient.Config {
if !IsConnectedToCluster() {
return nil
}
return K8SConfig
}
func IsConnectedToCluster() bool {
if K8SConfig == nil {
if err := LoadK8sConfig(); err != nil {
ConnectedToCluster = false
}
}
return ConnectedToCluster
}
func GetClusterName() string {
if !ConnectedToCluster {
return ""
}
kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{})
config, err := kubeConfig.RawConfig()
if err != nil {
return ""
}
// TODO - Handle if empty
return config.CurrentContext
}
func GetDefaultNamespace() string {
defaultNamespace := "default"
clientCfg, err := clientcmd.NewDefaultClientConfigLoadingRules().Load()
if err != nil {
return defaultNamespace
}
apiContext, ok := clientCfg.Contexts[clientCfg.CurrentContext]
if !ok || apiContext == nil {
return defaultNamespace
}
namespace := apiContext.Namespace
if apiContext.Namespace == "" {
namespace = defaultNamespace
}
return namespace
}

View File

@@ -1,34 +0,0 @@
package k8sinterface
import (
"testing"
"github.com/armosec/kubescape/cautils/cautils"
)
func TestGetGroupVersionResource(t *testing.T) {
wlid := "wlid://cluster-david-v1/namespace-default/deployment-nginx-deployment"
r, err := GetGroupVersionResource(cautils.GetKindFromWlid(wlid))
if err != nil {
t.Error(err)
return
}
if r.Group != "apps" {
t.Errorf("wrong group")
}
if r.Version != "v1" {
t.Errorf("wrong Version")
}
if r.Resource != "deployments" {
t.Errorf("wrong Resource")
}
r2, err := GetGroupVersionResource("NetworkPolicy")
if err != nil {
t.Error(err)
return
}
if r2.Resource != "networkpolicies" {
t.Errorf("wrong Resource")
}
}

View File

@@ -1,145 +0,0 @@
package k8sinterface
import (
"fmt"
"strings"
"github.com/armosec/kubescape/cautils/cautils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
//
// Uncomment to load all auth plugins
// _ "k8s.io/client-go/plugin/pkg/client/auth
//
// Or uncomment to load specific auth plugins
// _ "k8s.io/client-go/plugin/pkg/client/auth/azure"
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
// _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
// _ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
)
func (k8sAPI *KubernetesApi) GetWorkloadByWlid(wlid string) (*Workload, error) {
return k8sAPI.GetWorkload(cautils.GetNamespaceFromWlid(wlid), cautils.GetKindFromWlid(wlid), cautils.GetNameFromWlid(wlid))
}
func (k8sAPI *KubernetesApi) GetWorkload(namespace, kind, name string) (*Workload, error) {
groupVersionResource, err := GetGroupVersionResource(kind)
if err != nil {
return nil, err
}
w, err := k8sAPI.ResourceInterface(&groupVersionResource, namespace).Get(k8sAPI.Context, name, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("failed to GET resource, kind: '%s', namespace: '%s', name: '%s', reason: %s", kind, namespace, name, err.Error())
}
return NewWorkloadObj(w.Object), nil
}
func (k8sAPI *KubernetesApi) ListWorkloads(groupVersionResource *schema.GroupVersionResource, namespace string, podLabels, fieldSelector map[string]string) ([]Workload, error) {
listOptions := metav1.ListOptions{}
if podLabels != nil && len(podLabels) > 0 {
set := labels.Set(podLabels)
listOptions.LabelSelector = SelectorToString(set)
}
if fieldSelector != nil && len(fieldSelector) > 0 {
set := labels.Set(fieldSelector)
listOptions.FieldSelector = SelectorToString(set)
}
uList, err := k8sAPI.ResourceInterface(groupVersionResource, namespace).List(k8sAPI.Context, listOptions)
if err != nil {
return nil, fmt.Errorf("failed to LIST resources, reason: %s", err.Error())
}
workloads := make([]Workload, len(uList.Items))
for i := range uList.Items {
workloads[i] = *NewWorkloadObj(uList.Items[i].Object)
}
return workloads, nil
}
func (k8sAPI *KubernetesApi) DeleteWorkloadByWlid(wlid string) error {
groupVersionResource, err := GetGroupVersionResource(cautils.GetKindFromWlid(wlid))
if err != nil {
return err
}
err = k8sAPI.ResourceInterface(&groupVersionResource, cautils.GetNamespaceFromWlid(wlid)).Delete(k8sAPI.Context, cautils.GetNameFromWlid(wlid), metav1.DeleteOptions{})
if err != nil {
return fmt.Errorf("failed to DELETE resource, workloadID: '%s', reason: %s", wlid, err.Error())
}
return nil
}
func (k8sAPI *KubernetesApi) CreateWorkload(workload *Workload) (*Workload, error) {
groupVersionResource, err := GetGroupVersionResource(workload.GetKind())
if err != nil {
return nil, err
}
obj, err := workload.ToUnstructured()
if err != nil {
return nil, err
}
w, err := k8sAPI.ResourceInterface(&groupVersionResource, workload.GetNamespace()).Create(k8sAPI.Context, obj, metav1.CreateOptions{})
if err != nil {
return nil, fmt.Errorf("failed to CREATE resource, workload: '%s', reason: %s", workload.Json(), err.Error())
}
return NewWorkloadObj(w.Object), nil
}
func (k8sAPI *KubernetesApi) UpdateWorkload(workload *Workload) (*Workload, error) {
groupVersionResource, err := GetGroupVersionResource(workload.GetKind())
if err != nil {
return nil, err
}
obj, err := workload.ToUnstructured()
if err != nil {
return nil, err
}
w, err := k8sAPI.ResourceInterface(&groupVersionResource, workload.GetNamespace()).Update(k8sAPI.Context, obj, metav1.UpdateOptions{})
if err != nil {
return nil, fmt.Errorf("failed to UPDATE resource, workload: '%s', reason: %s", workload.Json(), err.Error())
}
return NewWorkloadObj(w.Object), nil
}
func (k8sAPI *KubernetesApi) GetNamespace(ns string) (*Workload, error) {
groupVersionResource, err := GetGroupVersionResource("namespace")
if err != nil {
return nil, err
}
w, err := k8sAPI.DynamicClient.Resource(groupVersionResource).Get(k8sAPI.Context, ns, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get namespace: '%s', reason: %s", ns, err.Error())
}
return NewWorkloadObj(w.Object), nil
}
func (k8sAPI *KubernetesApi) ResourceInterface(resource *schema.GroupVersionResource, namespace string) dynamic.ResourceInterface {
if IsNamespaceScope(resource.Group, resource.Resource) {
return k8sAPI.DynamicClient.Resource(*resource).Namespace(namespace)
}
return k8sAPI.DynamicClient.Resource(*resource)
}
func (k8sAPI *KubernetesApi) CalculateWorkloadParentRecursive(workload *Workload) (string, string, error) {
ownerReferences, err := workload.GetOwnerReferences() // OwnerReferences in workload
if err != nil {
return workload.GetKind(), workload.GetName(), err
}
if len(ownerReferences) == 0 {
return workload.GetKind(), workload.GetName(), nil // parent found
}
ownerReference := ownerReferences[0]
parentWorkload, err := k8sAPI.GetWorkload(workload.GetNamespace(), ownerReference.Kind, ownerReference.Name)
if err != nil {
if strings.Contains(err.Error(), "not found in resourceMap") { // if parent is RCD
return workload.GetKind(), workload.GetName(), nil // parent found
}
return workload.GetKind(), workload.GetName(), err
}
return k8sAPI.CalculateWorkloadParentRecursive(parentWorkload)
}

View File

@@ -1,43 +0,0 @@
package k8sinterface
import (
"context"
"k8s.io/apimachinery/pkg/runtime"
dynamicfake "k8s.io/client-go/dynamic/fake"
kubernetesfake "k8s.io/client-go/kubernetes/fake"
//
// metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// Uncomment to load all auth plugins
// _ "k8s.io/client-go/plugin/pkg/client/auth
//
// Or uncomment to load specific auth plugins
// _ "k8s.io/client-go/plugin/pkg/client/auth/azure"
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
// _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
// _ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
)
// NewKubernetesApi -
func NewKubernetesApiMock() *KubernetesApi {
return &KubernetesApi{
KubernetesClient: kubernetesfake.NewSimpleClientset(),
DynamicClient: dynamicfake.NewSimpleDynamicClient(&runtime.Scheme{}),
Context: context.Background(),
}
}
// func TestListDynamic(t *testing.T) {
// k8s := NewKubernetesApi()
// resource := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
// clientResource, err := k8s.DynamicClient.Resource(resource).Namespace("default").List(k8s.Context, metav1.ListOptions{})
// if err != nil {
// t.Errorf("err: %v", err)
// } else {
// bla, _ := json.Marshal(clientResource)
// // t.Errorf("BearerToken: %v", *K8SConfig)
// // os.WriteFile("bla.json", bla, 777)
// t.Errorf("clientResource: %s", string(bla))
// }
// }

View File

@@ -1,66 +0,0 @@
package k8sinterface
import (
"sort"
"strings"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels"
)
//
// Uncomment to load all auth plugins
// _ "k8s.io/client-go/plugin/pkg/client/auth
//
// Or uncomment to load specific auth plugins
// _ "k8s.io/client-go/plugin/pkg/client/auth/azure"
// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
// _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
// _ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
func ConvertUnstructuredSliceToMap(unstructuredSlice []unstructured.Unstructured) []map[string]interface{} {
converted := make([]map[string]interface{}, len(unstructuredSlice))
for i := range unstructuredSlice {
converted[i] = unstructuredSlice[i].Object
}
return converted
}
func FilterOutOwneredResources(result []unstructured.Unstructured) []unstructured.Unstructured {
response := []unstructured.Unstructured{}
recognizedOwners := []string{"Deployment", "ReplicaSet", "DaemonSet", "StatefulSet", "Job", "CronJob"}
for i := range result {
ownerReferences := result[i].GetOwnerReferences()
if len(ownerReferences) == 0 {
response = append(response, result[i])
} else if !IsStringInSlice(recognizedOwners, ownerReferences[0].Kind) {
response = append(response, result[i])
}
}
return response
}
func IsStringInSlice(slice []string, val string) bool {
for _, item := range slice {
if item == val {
return true
}
}
return false
}
// String returns all labels listed as a human readable string.
// Conveniently, exactly the format that ParseSelector takes.
func SelectorToString(ls labels.Set) string {
selector := make([]string, 0, len(ls))
for key, value := range ls {
if value != "" {
selector = append(selector, key+"="+value)
} else {
selector = append(selector, key)
}
}
// Sort for determinism.
sort.StringSlice(selector).Sort()
return strings.Join(selector, ",")
}

View File

@@ -1,10 +0,0 @@
package k8sinterface
import "testing"
func TestConvertUnstructuredSliceToMap(t *testing.T) {
converted := ConvertUnstructuredSliceToMap(V1KubeSystemNamespaceMock().Items)
if len(converted) == 0 { // != 7
t.Errorf("len(converted) == 0")
}
}

View File

@@ -1,71 +0,0 @@
package k8sinterface
import (
"context"
"github.com/armosec/kubescape/cautils/cautils"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
)
func IsAttached(labels map[string]string) *bool {
return IsLabel(labels, cautils.ArmoAttach)
}
func IsAgentCompatibleLabel(labels map[string]string) *bool {
return IsLabel(labels, cautils.ArmoCompatibleLabel)
}
func IsAgentCompatibleAnnotation(annotations map[string]string) *bool {
return IsLabel(annotations, cautils.ArmoCompatibleAnnotation)
}
func SetAgentCompatibleLabel(labels map[string]string, val bool) {
SetLabel(labels, cautils.ArmoCompatibleLabel, val)
}
func SetAgentCompatibleAnnotation(annotations map[string]string, val bool) {
SetLabel(annotations, cautils.ArmoCompatibleAnnotation, val)
}
func IsLabel(labels map[string]string, key string) *bool {
if labels == nil || len(labels) == 0 {
return nil
}
var k bool
if l, ok := labels[key]; ok {
if l == "true" {
k = true
} else if l == "false" {
k = false
}
return &k
}
return nil
}
func SetLabel(labels map[string]string, key string, val bool) {
if labels == nil {
return
}
v := ""
if val {
v = "true"
} else {
v = "false"
}
labels[key] = v
}
func (k8sAPI *KubernetesApi) ListAttachedPods(namespace string) ([]corev1.Pod, error) {
return k8sAPI.ListPods(namespace, map[string]string{cautils.ArmoAttach: cautils.BoolToString(true)})
}
func (k8sAPI *KubernetesApi) ListPods(namespace string, podLabels map[string]string) ([]corev1.Pod, error) {
listOptions := metav1.ListOptions{}
if podLabels != nil && len(podLabels) > 0 {
set := labels.Set(podLabels)
listOptions.LabelSelector = set.AsSelector().String()
}
pods, err := k8sAPI.KubernetesClient.CoreV1().Pods(namespace).List(context.Background(), listOptions)
if err != nil {
return []corev1.Pod{}, err
}
return pods.Items, nil
}

File diff suppressed because one or more lines are too long

View File

@@ -1,142 +0,0 @@
package k8sinterface
import (
"fmt"
"strings"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const ValueNotFound = -1
// https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.21/#-strong-api-groups-strong-
var ResourceGroupMapping = map[string]string{
"services": "/v1",
"pods": "/v1",
"replicationcontrollers": "/v1",
"podtemplates": "/v1",
"namespaces": "/v1",
"nodes": "/v1",
"configmaps": "/v1",
"secrets": "/v1",
"serviceaccounts": "/v1",
"persistentvolumeclaims": "/v1",
"limitranges": "/v1",
"resourcequotas": "/v1",
"daemonsets": "apps/v1",
"deployments": "apps/v1",
"replicasets": "apps/v1",
"statefulsets": "apps/v1",
"controllerrevisions": "apps/v1",
"jobs": "batch/v1",
"cronjobs": "batch/v1beta1",
"horizontalpodautoscalers": "autoscaling/v1",
"ingresses": "extensions/v1beta1",
"networkpolicies": "networking.k8s.io/v1",
"clusterroles": "rbac.authorization.k8s.io/v1",
"clusterrolebindings": "rbac.authorization.k8s.io/v1",
"roles": "rbac.authorization.k8s.io/v1",
"rolebindings": "rbac.authorization.k8s.io/v1",
"mutatingwebhookconfigurations": "admissionregistration.k8s.io/v1",
"validatingwebhookconfigurations": "admissionregistration.k8s.io/v1",
}
var GroupsClusterScope = []string{}
var ResourceClusterScope = []string{"nodes", "namespaces", "clusterroles", "clusterrolebindings"}
func GetGroupVersionResource(resource string) (schema.GroupVersionResource, error) {
resource = updateResourceKind(resource)
if r, ok := ResourceGroupMapping[resource]; ok {
gv := strings.Split(r, "/")
return schema.GroupVersionResource{Group: gv[0], Version: gv[1], Resource: resource}, nil
}
return schema.GroupVersionResource{}, fmt.Errorf("resource '%s' not found in resourceMap", resource)
}
func IsNamespaceScope(apiGroup, resource string) bool {
return StringInSlice(GroupsClusterScope, apiGroup) == ValueNotFound &&
StringInSlice(ResourceClusterScope, resource) == ValueNotFound
}
func StringInSlice(strSlice []string, str string) int {
for i := range strSlice {
if strSlice[i] == str {
return i
}
}
return ValueNotFound
}
func JoinResourceTriplets(group, version, resource string) string {
return fmt.Sprintf("%s/%s/%s", group, version, resource)
}
func GetResourceTriplets(group, version, resource string) []string {
resourceTriplets := []string{}
if resource == "" {
// load full map
for k, v := range ResourceGroupMapping {
g := strings.Split(v, "/")
resourceTriplets = append(resourceTriplets, JoinResourceTriplets(g[0], g[1], k))
}
} else if version == "" {
// load by resource
if v, ok := ResourceGroupMapping[resource]; ok {
g := strings.Split(v, "/")
if group == "" {
group = g[0]
}
resourceTriplets = append(resourceTriplets, JoinResourceTriplets(group, g[1], resource))
} else {
glog.Errorf("Resource '%s' unknown", resource)
}
} else if group == "" {
// load by resource and version
if v, ok := ResourceGroupMapping[resource]; ok {
g := strings.Split(v, "/")
resourceTriplets = append(resourceTriplets, JoinResourceTriplets(g[0], version, resource))
} else {
glog.Errorf("Resource '%s' unknown", resource)
}
} else {
resourceTriplets = append(resourceTriplets, JoinResourceTriplets(group, version, resource))
}
return resourceTriplets
}
func ResourceGroupToString(group, version, resource string) []string {
if group == "*" {
group = ""
}
if version == "*" {
version = ""
}
if resource == "*" {
resource = ""
}
resource = updateResourceKind(resource)
return GetResourceTriplets(group, version, resource)
}
func StringToResourceGroup(str string) (string, string, string) {
splitted := strings.Split(str, "/")
for i := range splitted {
if splitted[i] == "*" {
splitted[i] = ""
}
}
return splitted[0], splitted[1], splitted[2]
}
func updateResourceKind(resource string) string {
resource = strings.ToLower(resource)
if resource != "" && !strings.HasSuffix(resource, "s") {
if strings.HasSuffix(resource, "y") {
return fmt.Sprintf("%sies", strings.TrimSuffix(resource, "y")) // e.g. NetworkPolicy -> networkpolicies
} else {
return fmt.Sprintf("%ss", resource) // add 's' at the end of a resource
}
}
return resource
}

View File

@@ -1,22 +0,0 @@
package k8sinterface
import "testing"
func TestResourceGroupToString(t *testing.T) {
allResources := ResourceGroupToString("*", "*", "*")
if len(allResources) != len(ResourceGroupMapping) {
t.Errorf("Expected len: %d, received: %d", len(ResourceGroupMapping), len(allResources))
}
pod := ResourceGroupToString("*", "*", "Pod")
if len(pod) == 0 || pod[0] != "/v1/pods" {
t.Errorf("pod: %v", pod)
}
deployments := ResourceGroupToString("*", "*", "Deployment")
if len(deployments) == 0 || deployments[0] != "apps/v1/deployments" {
t.Errorf("deployments: %v", deployments)
}
cronjobs := ResourceGroupToString("*", "*", "cronjobs")
if len(cronjobs) == 0 || cronjobs[0] != "batch/v1beta1/cronjobs" {
t.Errorf("cronjobs: %v", cronjobs)
}
}

View File

@@ -1,161 +0,0 @@
package k8sinterface
import (
"encoding/json"
"github.com/armosec/kubescape/cautils/apis"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
type IWorkload interface {
IBasicWorkload
// Convert
ToUnstructured() (*unstructured.Unstructured, error)
ToString() string
Json() string // DEPRECATED
// GET
GetWlid() string
GetJobID() *apis.JobTracking
GetVersion() string
GetGroup() string
// SET
SetWlid(string)
SetInject()
SetIgnore()
SetUpdateTime()
SetJobID(apis.JobTracking)
SetCompatible()
SetIncompatible()
SetReplaceheaders()
// EXIST
IsIgnore() bool
IsInject() bool
IsAttached() bool
IsCompatible() bool
IsIncompatible() bool
// REMOVE
RemoveWlid()
RemoveSecretData()
RemoveInject()
RemoveIgnore()
RemoveUpdateTime()
RemoveJobID()
RemoveCompatible()
RemoveArmoMetadata()
RemoveArmoLabels()
RemoveArmoAnnotations()
}
type IBasicWorkload interface {
// Set
SetKind(string)
SetWorkload(map[string]interface{})
SetLabel(key, value string)
SetAnnotation(key, value string)
SetNamespace(string)
SetName(string)
// Get
GetNamespace() string
GetName() string
GetGenerateName() string
GetApiVersion() string
GetKind() string
GetInnerAnnotation(string) (string, bool)
GetPodAnnotation(string) (string, bool)
GetAnnotation(string) (string, bool)
GetLabel(string) (string, bool)
GetAnnotations() map[string]string
GetInnerAnnotations() map[string]string
GetPodAnnotations() map[string]string
GetLabels() map[string]string
GetInnerLabels() map[string]string
GetPodLabels() map[string]string
GetVolumes() ([]corev1.Volume, error)
GetReplicas() int
GetContainers() ([]corev1.Container, error)
GetInitContainers() ([]corev1.Container, error)
GetOwnerReferences() ([]metav1.OwnerReference, error)
GetImagePullSecret() ([]corev1.LocalObjectReference, error)
GetServiceAccountName() string
GetSelector() (*metav1.LabelSelector, error)
GetResourceVersion() string
GetUID() string
GetPodSpec() (*corev1.PodSpec, error)
GetWorkload() map[string]interface{}
// REMOVE
RemoveLabel(string)
RemoveAnnotation(string)
RemovePodStatus()
RemoveResourceVersion()
}
type Workload struct {
workload map[string]interface{}
}
func NewWorkload(bWorkload []byte) (*Workload, error) {
workload := make(map[string]interface{})
if bWorkload != nil {
if err := json.Unmarshal(bWorkload, &workload); err != nil {
return nil, err
}
}
return &Workload{
workload: workload,
}, nil
}
func NewWorkloadObj(workload map[string]interface{}) *Workload {
return &Workload{
workload: workload,
}
}
func (w *Workload) Json() string {
return w.ToString()
}
func (w *Workload) ToString() string {
if w.GetWorkload() == nil {
return ""
}
bWorkload, err := json.Marshal(w.GetWorkload())
if err != nil {
return err.Error()
}
return string(bWorkload)
}
func (workload *Workload) DeepCopy(w map[string]interface{}) {
workload.workload = make(map[string]interface{})
byt, _ := json.Marshal(w)
json.Unmarshal(byt, &workload.workload)
}
func (w *Workload) ToUnstructured() (*unstructured.Unstructured, error) {
obj := &unstructured.Unstructured{}
if w.workload == nil {
return obj, nil
}
bWorkload, err := json.Marshal(w.workload)
if err != nil {
return obj, err
}
if err := json.Unmarshal(bWorkload, obj); err != nil {
return obj, err
}
return obj, nil
}

View File

@@ -1,642 +0,0 @@
package k8sinterface
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"time"
"github.com/armosec/kubescape/cautils/apis"
"github.com/armosec/kubescape/cautils/cautils"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ======================================= DELETE ========================================
func (w *Workload) RemoveInject() {
w.RemovePodLabel(cautils.CAInject) // DEPRECATED
w.RemovePodLabel(cautils.CAAttachLabel) // DEPRECATED
w.RemovePodLabel(cautils.ArmoAttach)
w.RemoveLabel(cautils.CAInject) // DEPRECATED
w.RemoveLabel(cautils.CAAttachLabel) // DEPRECATED
w.RemoveLabel(cautils.ArmoAttach)
}
func (w *Workload) RemoveIgnore() {
w.RemovePodLabel(cautils.CAIgnore) // DEPRECATED
w.RemovePodLabel(cautils.ArmoAttach)
w.RemoveLabel(cautils.CAIgnore) // DEPRECATED
w.RemoveLabel(cautils.ArmoAttach)
}
func (w *Workload) RemoveWlid() {
w.RemovePodAnnotation(cautils.CAWlid) // DEPRECATED
w.RemovePodAnnotation(cautils.ArmoWlid)
w.RemoveAnnotation(cautils.CAWlid) // DEPRECATED
w.RemoveAnnotation(cautils.ArmoWlid)
}
func (w *Workload) RemoveCompatible() {
w.RemovePodAnnotation(cautils.ArmoCompatibleAnnotation)
}
func (w *Workload) RemoveJobID() {
w.RemovePodAnnotation(cautils.ArmoJobIDPath)
w.RemovePodAnnotation(cautils.ArmoJobParentPath)
w.RemovePodAnnotation(cautils.ArmoJobActionPath)
w.RemoveAnnotation(cautils.ArmoJobIDPath)
w.RemoveAnnotation(cautils.ArmoJobParentPath)
w.RemoveAnnotation(cautils.ArmoJobActionPath)
}
func (w *Workload) RemoveArmoMetadata() {
w.RemoveArmoLabels()
w.RemoveArmoAnnotations()
}
func (w *Workload) RemoveArmoAnnotations() {
l := w.GetAnnotations()
if l != nil {
for k := range l {
if strings.HasPrefix(k, cautils.ArmoPrefix) {
w.RemoveAnnotation(k)
}
if strings.HasPrefix(k, cautils.CAPrefix) { // DEPRECATED
w.RemoveAnnotation(k)
}
}
}
lp := w.GetPodAnnotations()
if lp != nil {
for k := range lp {
if strings.HasPrefix(k, cautils.ArmoPrefix) {
w.RemovePodAnnotation(k)
}
if strings.HasPrefix(k, cautils.CAPrefix) { // DEPRECATED
w.RemovePodAnnotation(k)
}
}
}
}
func (w *Workload) RemoveArmoLabels() {
l := w.GetLabels()
if l != nil {
for k := range l {
if strings.HasPrefix(k, cautils.ArmoPrefix) {
w.RemoveLabel(k)
}
if strings.HasPrefix(k, cautils.CAPrefix) { // DEPRECATED
w.RemoveLabel(k)
}
}
}
lp := w.GetPodLabels()
if lp != nil {
for k := range lp {
if strings.HasPrefix(k, cautils.ArmoPrefix) {
w.RemovePodLabel(k)
}
if strings.HasPrefix(k, cautils.CAPrefix) { // DEPRECATED
w.RemovePodLabel(k)
}
}
}
}
func (w *Workload) RemoveUpdateTime() {
// remove from pod
w.RemovePodAnnotation(cautils.CAUpdate) // DEPRECATED
w.RemovePodAnnotation(cautils.ArmoUpdate)
// remove from workload
w.RemoveAnnotation(cautils.CAUpdate) // DEPRECATED
w.RemoveAnnotation(cautils.ArmoUpdate)
}
func (w *Workload) RemoveSecretData() {
w.RemoveAnnotation("kubectl.kubernetes.io/last-applied-configuration")
delete(w.workload, "data")
}
func (w *Workload) RemovePodStatus() {
delete(w.workload, "status")
}
func (w *Workload) RemoveResourceVersion() {
if _, ok := w.workload["metadata"]; !ok {
return
}
meta, _ := w.workload["metadata"].(map[string]interface{})
delete(meta, "resourceVersion")
}
func (w *Workload) RemoveLabel(key string) {
w.RemoveMetadata([]string{"metadata"}, "labels", key)
}
func (w *Workload) RemoveAnnotation(key string) {
w.RemoveMetadata([]string{"metadata"}, "annotations", key)
}
func (w *Workload) RemovePodAnnotation(key string) {
w.RemoveMetadata(PodMetadata(w.GetKind()), "annotations", key)
}
func (w *Workload) RemovePodLabel(key string) {
w.RemoveMetadata(PodMetadata(w.GetKind()), "labels", key)
}
func (w *Workload) RemoveMetadata(scope []string, metadata, key string) {
workload := w.workload
for i := range scope {
if _, ok := workload[scope[i]]; !ok {
return
}
workload, _ = workload[scope[i]].(map[string]interface{})
}
if _, ok := workload[metadata]; !ok {
return
}
labels, _ := workload[metadata].(map[string]interface{})
delete(labels, key)
}
// ========================================= SET =========================================
func (w *Workload) SetWorkload(workload map[string]interface{}) {
w.workload = workload
}
func (w *Workload) SetKind(kind string) {
w.workload["kind"] = kind
}
func (w *Workload) SetInject() {
w.SetPodLabel(cautils.ArmoAttach, cautils.BoolToString(true))
}
func (w *Workload) SetJobID(jobTracking apis.JobTracking) {
w.SetPodAnnotation(cautils.ArmoJobIDPath, jobTracking.JobID)
w.SetPodAnnotation(cautils.ArmoJobParentPath, jobTracking.ParentID)
w.SetPodAnnotation(cautils.ArmoJobActionPath, fmt.Sprintf("%d", jobTracking.LastActionNumber))
}
func (w *Workload) SetIgnore() {
w.SetPodLabel(cautils.ArmoAttach, cautils.BoolToString(false))
}
func (w *Workload) SetCompatible() {
w.SetPodAnnotation(cautils.ArmoCompatibleAnnotation, cautils.BoolToString(true))
}
func (w *Workload) SetIncompatible() {
w.SetPodAnnotation(cautils.ArmoCompatibleAnnotation, cautils.BoolToString(false))
}
func (w *Workload) SetReplaceheaders() {
w.SetPodAnnotation(cautils.ArmoReplaceheaders, cautils.BoolToString(true))
}
func (w *Workload) SetWlid(wlid string) {
w.SetPodAnnotation(cautils.ArmoWlid, wlid)
}
func (w *Workload) SetUpdateTime() {
w.SetPodAnnotation(cautils.ArmoUpdate, string(time.Now().UTC().Format("02-01-2006 15:04:05")))
}
func (w *Workload) SetNamespace(namespace string) {
w.SetMetadata([]string{"metadata"}, "namespace", namespace)
}
func (w *Workload) SetName(name string) {
w.SetMetadata([]string{"metadata"}, "name", name)
}
func (w *Workload) SetLabel(key, value string) {
w.SetMetadata([]string{"metadata", "labels"}, key, value)
}
func (w *Workload) SetPodLabel(key, value string) {
w.SetMetadata(append(PodMetadata(w.GetKind()), "labels"), key, value)
}
func (w *Workload) SetAnnotation(key, value string) {
w.SetMetadata([]string{"metadata", "annotations"}, key, value)
}
func (w *Workload) SetPodAnnotation(key, value string) {
w.SetMetadata(append(PodMetadata(w.GetKind()), "annotations"), key, value)
}
func (w *Workload) SetMetadata(scope []string, key string, val interface{}) {
workload := w.workload
for i := range scope {
if _, ok := workload[scope[i]]; !ok {
workload[scope[i]] = make(map[string]interface{})
}
workload, _ = workload[scope[i]].(map[string]interface{})
}
workload[key] = val
}
// ========================================= GET =========================================
func (w *Workload) GetWorkload() map[string]interface{} {
return w.workload
}
func (w *Workload) GetNamespace() string {
if v, ok := InspectWorkload(w.workload, "metadata", "namespace"); ok {
return v.(string)
}
return ""
}
func (w *Workload) GetName() string {
if v, ok := InspectWorkload(w.workload, "metadata", "name"); ok {
return v.(string)
}
return ""
}
func (w *Workload) GetApiVersion() string {
if v, ok := InspectWorkload(w.workload, "apiVersion"); ok {
return v.(string)
}
return ""
}
func (w *Workload) GetVersion() string {
apiVersion := w.GetApiVersion()
splitted := strings.Split(apiVersion, "/")
if len(splitted) == 1 {
return splitted[0]
} else if len(splitted) == 2 {
return splitted[1]
}
return ""
}
func (w *Workload) GetGroup() string {
apiVersion := w.GetApiVersion()
splitted := strings.Split(apiVersion, "/")
if len(splitted) == 2 {
return splitted[0]
}
return ""
}
func (w *Workload) GetGenerateName() string {
if v, ok := InspectWorkload(w.workload, "metadata", "generateName"); ok {
return v.(string)
}
return ""
}
func (w *Workload) GetReplicas() int {
if v, ok := InspectWorkload(w.workload, "spec", "replicas"); ok {
replicas, isok := v.(float64)
if isok {
return int(replicas)
}
}
return 1
}
func (w *Workload) GetKind() string {
if v, ok := InspectWorkload(w.workload, "kind"); ok {
return v.(string)
}
return ""
}
func (w *Workload) GetSelector() (*metav1.LabelSelector, error) {
selector := &metav1.LabelSelector{}
if v, ok := InspectWorkload(w.workload, "spec", "selector", "matchLabels"); ok && v != nil {
b, err := json.Marshal(v)
if err != nil {
return selector, err
}
if err := json.Unmarshal(b, selector); err != nil {
return selector, err
}
return selector, nil
}
return selector, nil
}
func (w *Workload) GetAnnotation(annotation string) (string, bool) {
if v, ok := InspectWorkload(w.workload, "metadata", "annotations", annotation); ok {
return v.(string), ok
}
return "", false
}
func (w *Workload) GetLabel(label string) (string, bool) {
if v, ok := InspectWorkload(w.workload, "metadata", "labels", label); ok {
return v.(string), ok
}
return "", false
}
func (w *Workload) GetPodLabel(label string) (string, bool) {
if v, ok := InspectWorkload(w.workload, append(PodMetadata(w.GetKind()), "labels", label)...); ok && v != nil {
return v.(string), ok
}
return "", false
}
func (w *Workload) GetLabels() map[string]string {
if v, ok := InspectWorkload(w.workload, "metadata", "labels"); ok && v != nil {
labels := make(map[string]string)
for k, i := range v.(map[string]interface{}) {
labels[k] = i.(string)
}
return labels
}
return nil
}
// GetInnerLabels - DEPRECATED
func (w *Workload) GetInnerLabels() map[string]string {
return w.GetPodLabels()
}
func (w *Workload) GetPodLabels() map[string]string {
if v, ok := InspectWorkload(w.workload, append(PodMetadata(w.GetKind()), "labels")...); ok && v != nil {
labels := make(map[string]string)
for k, i := range v.(map[string]interface{}) {
labels[k] = i.(string)
}
return labels
}
return nil
}
// GetInnerAnnotations - DEPRECATED
func (w *Workload) GetInnerAnnotations() map[string]string {
return w.GetPodAnnotations()
}
// GetPodAnnotations
func (w *Workload) GetPodAnnotations() map[string]string {
if v, ok := InspectWorkload(w.workload, append(PodMetadata(w.GetKind()), "annotations")...); ok && v != nil {
annotations := make(map[string]string)
for k, i := range v.(map[string]interface{}) {
annotations[k] = fmt.Sprintf("%v", i)
}
return annotations
}
return nil
}
// GetInnerAnnotation DEPRECATED
func (w *Workload) GetInnerAnnotation(annotation string) (string, bool) {
return w.GetPodAnnotation(annotation)
}
func (w *Workload) GetPodAnnotation(annotation string) (string, bool) {
if v, ok := InspectWorkload(w.workload, append(PodMetadata(w.GetKind()), "annotations", annotation)...); ok && v != nil {
return v.(string), ok
}
return "", false
}
func (w *Workload) GetAnnotations() map[string]string {
if v, ok := InspectWorkload(w.workload, "metadata", "annotations"); ok && v != nil {
annotations := make(map[string]string)
for k, i := range v.(map[string]interface{}) {
annotations[k] = fmt.Sprintf("%v", i)
}
return annotations
}
return nil
}
// GetVolumes -
func (w *Workload) GetVolumes() ([]corev1.Volume, error) {
volumes := []corev1.Volume{}
interVolumes, _ := InspectWorkload(w.workload, append(PodSpec(w.GetKind()), "volumes")...)
if interVolumes == nil {
return volumes, nil
}
volumesBytes, err := json.Marshal(interVolumes)
if err != nil {
return volumes, err
}
err = json.Unmarshal(volumesBytes, &volumes)
return volumes, err
}
func (w *Workload) GetServiceAccountName() string {
if v, ok := InspectWorkload(w.workload, append(PodSpec(w.GetKind()), "serviceAccountName")...); ok && v != nil {
return v.(string)
}
return ""
}
func (w *Workload) GetPodSpec() (*corev1.PodSpec, error) {
podSpec := &corev1.PodSpec{}
podSepcRaw, _ := InspectWorkload(w.workload, PodSpec(w.GetKind())...)
if podSepcRaw == nil {
return podSpec, fmt.Errorf("no PodSpec for workload: %v", w)
}
b, err := json.Marshal(podSepcRaw)
if err != nil {
return podSpec, err
}
err = json.Unmarshal(b, podSpec)
return podSpec, err
}
func (w *Workload) GetImagePullSecret() ([]corev1.LocalObjectReference, error) {
imgPullSecrets := []corev1.LocalObjectReference{}
iImgPullSecrets, _ := InspectWorkload(w.workload, append(PodSpec(w.GetKind()), "imagePullSecrets")...)
b, err := json.Marshal(iImgPullSecrets)
if err != nil {
return imgPullSecrets, err
}
err = json.Unmarshal(b, &imgPullSecrets)
return imgPullSecrets, err
}
// GetContainers -
func (w *Workload) GetContainers() ([]corev1.Container, error) {
containers := []corev1.Container{}
interContainers, _ := InspectWorkload(w.workload, append(PodSpec(w.GetKind()), "containers")...)
if interContainers == nil {
return containers, nil
}
containersBytes, err := json.Marshal(interContainers)
if err != nil {
return containers, err
}
err = json.Unmarshal(containersBytes, &containers)
return containers, err
}
// GetInitContainers -
func (w *Workload) GetInitContainers() ([]corev1.Container, error) {
containers := []corev1.Container{}
interContainers, _ := InspectWorkload(w.workload, append(PodSpec(w.GetKind()), "initContainers")...)
if interContainers == nil {
return containers, nil
}
containersBytes, err := json.Marshal(interContainers)
if err != nil {
return containers, err
}
err = json.Unmarshal(containersBytes, &containers)
return containers, err
}
// GetOwnerReferences -
func (w *Workload) GetOwnerReferences() ([]metav1.OwnerReference, error) {
ownerReferences := []metav1.OwnerReference{}
interOwnerReferences, ok := InspectWorkload(w.workload, "metadata", "ownerReferences")
if !ok {
return ownerReferences, nil
}
ownerReferencesBytes, err := json.Marshal(interOwnerReferences)
if err != nil {
return ownerReferences, err
}
err = json.Unmarshal(ownerReferencesBytes, &ownerReferences)
if err != nil {
return ownerReferences, err
}
return ownerReferences, nil
}
func (w *Workload) GetResourceVersion() string {
if v, ok := InspectWorkload(w.workload, "metadata", "resourceVersion"); ok {
return v.(string)
}
return ""
}
func (w *Workload) GetUID() string {
if v, ok := InspectWorkload(w.workload, "metadata", "uid"); ok {
return v.(string)
}
return ""
}
func (w *Workload) GetWlid() string {
if wlid, ok := w.GetAnnotation(cautils.ArmoWlid); ok {
return wlid
}
return ""
}
func (w *Workload) GetJobID() *apis.JobTracking {
jobTracking := apis.JobTracking{}
if job, ok := w.GetPodAnnotation(cautils.ArmoJobIDPath); ok {
jobTracking.JobID = job
}
if parent, ok := w.GetPodAnnotation(cautils.ArmoJobParentPath); ok {
jobTracking.ParentID = parent
}
if action, ok := w.GetPodAnnotation(cautils.ArmoJobActionPath); ok {
if i, err := strconv.Atoi(action); err == nil {
jobTracking.LastActionNumber = i
}
}
if jobTracking.LastActionNumber == 0 { // start the counter at 1
jobTracking.LastActionNumber = 1
}
return &jobTracking
}
// func (w *Workload) GetJobID() string {
// if status, ok := w.GetAnnotation(cautils.ArmoJobID); ok {
// return status
// }
// return ""
// }
// ========================================= IS =========================================
func (w *Workload) IsInject() bool {
return w.IsAttached()
}
func (w *Workload) IsIgnore() bool {
if attach := cautils.IsAttached(w.GetPodLabels()); attach != nil {
return !(*attach)
}
if attach := cautils.IsAttached(w.GetLabels()); attach != nil {
return !(*attach)
}
return false
}
func (w *Workload) IsCompatible() bool {
if c, ok := w.GetPodAnnotation(cautils.ArmoCompatibleAnnotation); ok {
return cautils.StringToBool(c)
}
if c, ok := w.GetAnnotation(cautils.ArmoCompatibleAnnotation); ok {
return cautils.StringToBool(c)
}
return false
}
func (w *Workload) IsIncompatible() bool {
if c, ok := w.GetPodAnnotation(cautils.ArmoCompatibleAnnotation); ok {
return !cautils.StringToBool(c)
}
if c, ok := w.GetAnnotation(cautils.ArmoCompatibleAnnotation); ok {
return !cautils.StringToBool(c)
}
return false
}
func (w *Workload) IsAttached() bool {
if attach := cautils.IsAttached(w.GetPodLabels()); attach != nil {
return *attach
}
if attach := cautils.IsAttached(w.GetLabels()); attach != nil {
return *attach
}
return false
}
func (w *Workload) IsReplaceheaders() bool {
if c, ok := w.GetPodAnnotation(cautils.ArmoReplaceheaders); ok {
return cautils.StringToBool(c)
}
return false
}
// ======================================= UTILS =========================================
// InspectWorkload -
func InspectWorkload(workload interface{}, scopes ...string) (val interface{}, k bool) {
val, k = nil, false
if len(scopes) == 0 {
if workload != nil {
return workload, true
}
return nil, false
}
if data, ok := workload.(map[string]interface{}); ok {
val, k = InspectWorkload(data[scopes[0]], scopes[1:]...)
}
return val, k
}

View File

@@ -1,155 +0,0 @@
package k8sinterface
import (
"testing"
)
// ========================================= IS =========================================
func TestLabels(t *testing.T) {
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"deployment.kubernetes.io/revision":"1"},"creationTimestamp":"2021-05-03T13:10:32Z","generation":1,"labels":{"app":"demoservice-server","cyberarmor.inject":"true"},"managedFields":[{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:labels":{".":{},"f:app":{},"f:cyberarmor.inject":{}}},"f:spec":{"f:progressDeadlineSeconds":{},"f:replicas":{},"f:revisionHistoryLimit":{},"f:selector":{},"f:strategy":{"f:rollingUpdate":{".":{},"f:maxSurge":{},"f:maxUnavailable":{}},"f:type":{}},"f:template":{"f:metadata":{"f:labels":{".":{},"f:app":{}}},"f:spec":{"f:containers":{"k:{\"name\":\"demoservice\"}":{".":{},"f:env":{".":{},"k:{\"name\":\"ARMO_TEST_NAME\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"CAA_ENABLE_CRASH_REPORTER\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"DEMO_FOLDERS\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SERVER_PORT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SLEEP_DURATION\"}":{".":{},"f:name":{},"f:value":{}}},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:ports":{".":{},"k:{\"containerPort\":8089,\"protocol\":\"TCP\"}":{".":{},"f:containerPort":{},"f:protocol":{}}},"f:resources":{},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}}}},"manager":"OpenAPI-Generator","operation":"Update","time":"2021-05-03T13:10:32Z"},{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:deployment.kubernetes.io/revision":{}}},"f:status":{"f:availableReplicas":{},"f:conditions":{".":{},"k:{\"type\":\"Available\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Progressing\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}}},"f:observedGeneration":{},"f:readyReplicas":{},"f:replicas":{},"f:updatedReplicas":{}}},"manager":"kube-controller-manager","operation":"Update","time":"2021-05-03T13:52:58Z"}],"name":"demoservice-server","namespace":"default","resourceVersion":"1016043","uid":"e9e8a3e9-6cb4-4301-ace1-2c0cef3bd61e"},"spec":{"progressDeadlineSeconds":600,"replicas":1,"revisionHistoryLimit":10,"selector":{"matchLabels":{"app":"demoservice-server"}},"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}},"status":{"availableReplicas":1,"conditions":[{"lastTransitionTime":"2021-05-03T13:10:32Z","lastUpdateTime":"2021-05-03T13:10:37Z","message":"ReplicaSet \"demoservice-server-7d478b6998\" has successfully progressed.","reason":"NewReplicaSetAvailable","status":"True","type":"Progressing"},{"lastTransitionTime":"2021-05-03T13:52:58Z","lastUpdateTime":"2021-05-03T13:52:58Z","message":"Deployment has minimum availability.","reason":"MinimumReplicasAvailable","status":"True","type":"Available"}],"observedGeneration":1,"readyReplicas":1,"replicas":1,"updatedReplicas":1}}`
workload, err := NewWorkload([]byte(w))
if err != nil {
t.Errorf(err.Error())
}
if workload.GetKind() != "Deployment" {
t.Errorf("wrong kind")
}
if workload.GetNamespace() != "default" {
t.Errorf("wrong namespace")
}
if workload.GetName() != "demoservice-server" {
t.Errorf("wrong name")
}
if !workload.IsInject() {
t.Errorf("expect to find inject label")
}
if workload.IsIgnore() {
t.Errorf("expect to find ignore label")
}
}
func TestSetNamespace(t *testing.T) {
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"demoservice-server"},"spec":{"replicas":1,"selector":{"matchLabels":{"app":"demoservice-server"}},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}}}`
workload, err := NewWorkload([]byte(w))
if err != nil {
t.Errorf(err.Error())
}
workload.SetNamespace("default")
if workload.GetNamespace() != "default" {
t.Errorf("wrong namespace")
}
}
func TestSetLabels(t *testing.T) {
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"deployment.kubernetes.io/revision":"1"},"creationTimestamp":"2021-05-03T13:10:32Z","generation":1,"managedFields":[{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:labels":{".":{},"f:app":{},"f:cyberarmor.inject":{}}},"f:spec":{"f:progressDeadlineSeconds":{},"f:replicas":{},"f:revisionHistoryLimit":{},"f:selector":{},"f:strategy":{"f:rollingUpdate":{".":{},"f:maxSurge":{},"f:maxUnavailable":{}},"f:type":{}},"f:template":{"f:metadata":{"f:labels":{".":{},"f:app":{}}},"f:spec":{"f:containers":{"k:{\"name\":\"demoservice\"}":{".":{},"f:env":{".":{},"k:{\"name\":\"ARMO_TEST_NAME\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"CAA_ENABLE_CRASH_REPORTER\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"DEMO_FOLDERS\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SERVER_PORT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SLEEP_DURATION\"}":{".":{},"f:name":{},"f:value":{}}},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:ports":{".":{},"k:{\"containerPort\":8089,\"protocol\":\"TCP\"}":{".":{},"f:containerPort":{},"f:protocol":{}}},"f:resources":{},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}}}},"manager":"OpenAPI-Generator","operation":"Update","time":"2021-05-03T13:10:32Z"},{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:deployment.kubernetes.io/revision":{}}},"f:status":{"f:availableReplicas":{},"f:conditions":{".":{},"k:{\"type\":\"Available\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Progressing\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}}},"f:observedGeneration":{},"f:readyReplicas":{},"f:replicas":{},"f:updatedReplicas":{}}},"manager":"kube-controller-manager","operation":"Update","time":"2021-05-03T13:52:58Z"}],"name":"demoservice-server","namespace":"default","resourceVersion":"1016043","uid":"e9e8a3e9-6cb4-4301-ace1-2c0cef3bd61e"},"spec":{"progressDeadlineSeconds":600,"replicas":1,"revisionHistoryLimit":10,"selector":{"matchLabels":{"app":"demoservice-server"}},"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}},"status":{"availableReplicas":1,"conditions":[{"lastTransitionTime":"2021-05-03T13:10:32Z","lastUpdateTime":"2021-05-03T13:10:37Z","message":"ReplicaSet \"demoservice-server-7d478b6998\" has successfully progressed.","reason":"NewReplicaSetAvailable","status":"True","type":"Progressing"},{"lastTransitionTime":"2021-05-03T13:52:58Z","lastUpdateTime":"2021-05-03T13:52:58Z","message":"Deployment has minimum availability.","reason":"MinimumReplicasAvailable","status":"True","type":"Available"}],"observedGeneration":1,"readyReplicas":1,"replicas":1,"updatedReplicas":1}}`
workload, err := NewWorkload([]byte(w))
if err != nil {
t.Errorf(err.Error())
}
workload.SetLabel("bla", "daa")
v, ok := workload.GetLabel("bla")
if !ok || v != "daa" {
t.Errorf("expect to find label")
}
workload.RemoveLabel("bla")
v2, ok2 := workload.GetLabel("bla")
if ok2 || v2 == "daa" {
t.Errorf("label not deleted")
}
}
func TestSetAnnotations(t *testing.T) {
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"deployment.kubernetes.io/revision":"1"},"creationTimestamp":"2021-05-03T13:10:32Z","generation":1,"managedFields":[{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:labels":{".":{},"f:app":{},"f:cyberarmor.inject":{}}},"f:spec":{"f:progressDeadlineSeconds":{},"f:replicas":{},"f:revisionHistoryLimit":{},"f:selector":{},"f:strategy":{"f:rollingUpdate":{".":{},"f:maxSurge":{},"f:maxUnavailable":{}},"f:type":{}},"f:template":{"f:metadata":{"f:labels":{".":{},"f:app":{}}},"f:spec":{"f:containers":{"k:{\"name\":\"demoservice\"}":{".":{},"f:env":{".":{},"k:{\"name\":\"ARMO_TEST_NAME\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"CAA_ENABLE_CRASH_REPORTER\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"DEMO_FOLDERS\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SERVER_PORT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SLEEP_DURATION\"}":{".":{},"f:name":{},"f:value":{}}},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:ports":{".":{},"k:{\"containerPort\":8089,\"protocol\":\"TCP\"}":{".":{},"f:containerPort":{},"f:protocol":{}}},"f:resources":{},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}}}},"manager":"OpenAPI-Generator","operation":"Update","time":"2021-05-03T13:10:32Z"},{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:deployment.kubernetes.io/revision":{}}},"f:status":{"f:availableReplicas":{},"f:conditions":{".":{},"k:{\"type\":\"Available\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Progressing\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}}},"f:observedGeneration":{},"f:readyReplicas":{},"f:replicas":{},"f:updatedReplicas":{}}},"manager":"kube-controller-manager","operation":"Update","time":"2021-05-03T13:52:58Z"}],"name":"demoservice-server","namespace":"default","resourceVersion":"1016043","uid":"e9e8a3e9-6cb4-4301-ace1-2c0cef3bd61e"},"spec":{"progressDeadlineSeconds":600,"replicas":1,"revisionHistoryLimit":10,"selector":{"matchLabels":{"app":"demoservice-server"}},"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}},"status":{"availableReplicas":1,"conditions":[{"lastTransitionTime":"2021-05-03T13:10:32Z","lastUpdateTime":"2021-05-03T13:10:37Z","message":"ReplicaSet \"demoservice-server-7d478b6998\" has successfully progressed.","reason":"NewReplicaSetAvailable","status":"True","type":"Progressing"},{"lastTransitionTime":"2021-05-03T13:52:58Z","lastUpdateTime":"2021-05-03T13:52:58Z","message":"Deployment has minimum availability.","reason":"MinimumReplicasAvailable","status":"True","type":"Available"}],"observedGeneration":1,"readyReplicas":1,"replicas":1,"updatedReplicas":1}}`
workload, err := NewWorkload([]byte(w))
if err != nil {
t.Errorf(err.Error())
}
workload.SetAnnotation("bla", "daa")
v, ok := workload.GetAnnotation("bla")
if !ok || v != "daa" {
t.Errorf("expect to find annotation")
}
workload.RemoveAnnotation("bla")
v2, ok2 := workload.GetAnnotation("bla")
if ok2 || v2 == "daa" {
t.Errorf("annotation not deleted")
}
}
func TestSetPodLabels(t *testing.T) {
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"deployment.kubernetes.io/revision":"1"},"creationTimestamp":"2021-05-03T13:10:32Z","generation":1,"managedFields":[{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:labels":{".":{},"f:app":{},"f:cyberarmor.inject":{}}},"f:spec":{"f:progressDeadlineSeconds":{},"f:replicas":{},"f:revisionHistoryLimit":{},"f:selector":{},"f:strategy":{"f:rollingUpdate":{".":{},"f:maxSurge":{},"f:maxUnavailable":{}},"f:type":{}},"f:template":{"f:metadata":{"f:labels":{".":{},"f:app":{}}},"f:spec":{"f:containers":{"k:{\"name\":\"demoservice\"}":{".":{},"f:env":{".":{},"k:{\"name\":\"ARMO_TEST_NAME\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"CAA_ENABLE_CRASH_REPORTER\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"DEMO_FOLDERS\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SERVER_PORT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SLEEP_DURATION\"}":{".":{},"f:name":{},"f:value":{}}},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:ports":{".":{},"k:{\"containerPort\":8089,\"protocol\":\"TCP\"}":{".":{},"f:containerPort":{},"f:protocol":{}}},"f:resources":{},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}}}},"manager":"OpenAPI-Generator","operation":"Update","time":"2021-05-03T13:10:32Z"},{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:deployment.kubernetes.io/revision":{}}},"f:status":{"f:availableReplicas":{},"f:conditions":{".":{},"k:{\"type\":\"Available\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Progressing\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}}},"f:observedGeneration":{},"f:readyReplicas":{},"f:replicas":{},"f:updatedReplicas":{}}},"manager":"kube-controller-manager","operation":"Update","time":"2021-05-03T13:52:58Z"}],"name":"demoservice-server","namespace":"default","resourceVersion":"1016043","uid":"e9e8a3e9-6cb4-4301-ace1-2c0cef3bd61e"},"spec":{"progressDeadlineSeconds":600,"replicas":1,"revisionHistoryLimit":10,"selector":{"matchLabels":{"app":"demoservice-server"}},"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}},"status":{"availableReplicas":1,"conditions":[{"lastTransitionTime":"2021-05-03T13:10:32Z","lastUpdateTime":"2021-05-03T13:10:37Z","message":"ReplicaSet \"demoservice-server-7d478b6998\" has successfully progressed.","reason":"NewReplicaSetAvailable","status":"True","type":"Progressing"},{"lastTransitionTime":"2021-05-03T13:52:58Z","lastUpdateTime":"2021-05-03T13:52:58Z","message":"Deployment has minimum availability.","reason":"MinimumReplicasAvailable","status":"True","type":"Available"}],"observedGeneration":1,"readyReplicas":1,"replicas":1,"updatedReplicas":1}}`
workload, err := NewWorkload([]byte(w))
if err != nil {
t.Errorf(err.Error())
}
workload.SetPodLabel("bla", "daa")
v, ok := workload.GetPodLabel("bla")
if !ok || v != "daa" {
t.Errorf("expect to find label")
}
workload.RemovePodLabel("bla")
v2, ok2 := workload.GetPodLabel("bla")
if ok2 || v2 == "daa" {
t.Errorf("label not deleted")
}
}
func TestRemoveArmo(t *testing.T) {
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"deployment.kubernetes.io/revision":"1"},"creationTimestamp":"2021-05-03T13:10:32Z","generation":1,"managedFields":[{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:labels":{".":{},"f:app":{},"f:cyberarmor.inject":{}}},"f:spec":{"f:progressDeadlineSeconds":{},"f:replicas":{},"f:revisionHistoryLimit":{},"f:selector":{},"f:strategy":{"f:rollingUpdate":{".":{},"f:maxSurge":{},"f:maxUnavailable":{}},"f:type":{}},"f:template":{"f:metadata":{"f:labels":{".":{},"f:app":{}}},"f:spec":{"f:containers":{"k:{\"name\":\"demoservice\"}":{".":{},"f:env":{".":{},"k:{\"name\":\"ARMO_TEST_NAME\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"CAA_ENABLE_CRASH_REPORTER\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"DEMO_FOLDERS\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SERVER_PORT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SLEEP_DURATION\"}":{".":{},"f:name":{},"f:value":{}}},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:ports":{".":{},"k:{\"containerPort\":8089,\"protocol\":\"TCP\"}":{".":{},"f:containerPort":{},"f:protocol":{}}},"f:resources":{},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}}}},"manager":"OpenAPI-Generator","operation":"Update","time":"2021-05-03T13:10:32Z"},{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:deployment.kubernetes.io/revision":{}}},"f:status":{"f:availableReplicas":{},"f:conditions":{".":{},"k:{\"type\":\"Available\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Progressing\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}}},"f:observedGeneration":{},"f:readyReplicas":{},"f:replicas":{},"f:updatedReplicas":{}}},"manager":"kube-controller-manager","operation":"Update","time":"2021-05-03T13:52:58Z"}],"name":"demoservice-server","namespace":"default","resourceVersion":"1016043","uid":"e9e8a3e9-6cb4-4301-ace1-2c0cef3bd61e"},"spec":{"progressDeadlineSeconds":600,"replicas":1,"revisionHistoryLimit":10,"selector":{"matchLabels":{"app":"demoservice-server"}},"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server", "armo.attach": "true"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}},"status":{"availableReplicas":1,"conditions":[{"lastTransitionTime":"2021-05-03T13:10:32Z","lastUpdateTime":"2021-05-03T13:10:37Z","message":"ReplicaSet \"demoservice-server-7d478b6998\" has successfully progressed.","reason":"NewReplicaSetAvailable","status":"True","type":"Progressing"},{"lastTransitionTime":"2021-05-03T13:52:58Z","lastUpdateTime":"2021-05-03T13:52:58Z","message":"Deployment has minimum availability.","reason":"MinimumReplicasAvailable","status":"True","type":"Available"}],"observedGeneration":1,"readyReplicas":1,"replicas":1,"updatedReplicas":1}}`
workload, err := NewWorkload([]byte(w))
if err != nil {
t.Errorf(err.Error())
}
if !workload.IsAttached() {
t.Errorf("expect to be attached")
}
workload.RemoveArmoMetadata()
if workload.IsAttached() {
t.Errorf("expect to be clear")
}
}
func TestSetWlid(t *testing.T) {
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"deployment.kubernetes.io/revision":"1"},"creationTimestamp":"2021-05-03T13:10:32Z","generation":1,"managedFields":[{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:labels":{".":{},"f:app":{},"f:cyberarmor.inject":{}}},"f:spec":{"f:progressDeadlineSeconds":{},"f:replicas":{},"f:revisionHistoryLimit":{},"f:selector":{},"f:strategy":{"f:rollingUpdate":{".":{},"f:maxSurge":{},"f:maxUnavailable":{}},"f:type":{}},"f:template":{"f:metadata":{"f:labels":{".":{},"f:app":{}}},"f:spec":{"f:containers":{"k:{\"name\":\"demoservice\"}":{".":{},"f:env":{".":{},"k:{\"name\":\"ARMO_TEST_NAME\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"CAA_ENABLE_CRASH_REPORTER\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"DEMO_FOLDERS\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SERVER_PORT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SLEEP_DURATION\"}":{".":{},"f:name":{},"f:value":{}}},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:ports":{".":{},"k:{\"containerPort\":8089,\"protocol\":\"TCP\"}":{".":{},"f:containerPort":{},"f:protocol":{}}},"f:resources":{},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}}}},"manager":"OpenAPI-Generator","operation":"Update","time":"2021-05-03T13:10:32Z"},{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:deployment.kubernetes.io/revision":{}}},"f:status":{"f:availableReplicas":{},"f:conditions":{".":{},"k:{\"type\":\"Available\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Progressing\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}}},"f:observedGeneration":{},"f:readyReplicas":{},"f:replicas":{},"f:updatedReplicas":{}}},"manager":"kube-controller-manager","operation":"Update","time":"2021-05-03T13:52:58Z"}],"name":"demoservice-server","namespace":"default","resourceVersion":"1016043","uid":"e9e8a3e9-6cb4-4301-ace1-2c0cef3bd61e"},"spec":{"progressDeadlineSeconds":600,"replicas":1,"revisionHistoryLimit":10,"selector":{"matchLabels":{"app":"demoservice-server"}},"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}},"status":{"availableReplicas":1,"conditions":[{"lastTransitionTime":"2021-05-03T13:10:32Z","lastUpdateTime":"2021-05-03T13:10:37Z","message":"ReplicaSet \"demoservice-server-7d478b6998\" has successfully progressed.","reason":"NewReplicaSetAvailable","status":"True","type":"Progressing"},{"lastTransitionTime":"2021-05-03T13:52:58Z","lastUpdateTime":"2021-05-03T13:52:58Z","message":"Deployment has minimum availability.","reason":"MinimumReplicasAvailable","status":"True","type":"Available"}],"observedGeneration":1,"readyReplicas":1,"replicas":1,"updatedReplicas":1}}`
workload, err := NewWorkload([]byte(w))
if err != nil {
t.Errorf(err.Error())
}
workload.SetWlid("wlid://bla")
// t.Errorf(workload.Json())
}
func TestGetResourceVersion(t *testing.T) {
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"deployment.kubernetes.io/revision":"1"},"creationTimestamp":"2021-05-03T13:10:32Z","generation":1,"managedFields":[{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:labels":{".":{},"f:app":{},"f:cyberarmor.inject":{}}},"f:spec":{"f:progressDeadlineSeconds":{},"f:replicas":{},"f:revisionHistoryLimit":{},"f:selector":{},"f:strategy":{"f:rollingUpdate":{".":{},"f:maxSurge":{},"f:maxUnavailable":{}},"f:type":{}},"f:template":{"f:metadata":{"f:labels":{".":{},"f:app":{}}},"f:spec":{"f:containers":{"k:{\"name\":\"demoservice\"}":{".":{},"f:env":{".":{},"k:{\"name\":\"ARMO_TEST_NAME\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"CAA_ENABLE_CRASH_REPORTER\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"DEMO_FOLDERS\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SERVER_PORT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SLEEP_DURATION\"}":{".":{},"f:name":{},"f:value":{}}},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:ports":{".":{},"k:{\"containerPort\":8089,\"protocol\":\"TCP\"}":{".":{},"f:containerPort":{},"f:protocol":{}}},"f:resources":{},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}}}},"manager":"OpenAPI-Generator","operation":"Update","time":"2021-05-03T13:10:32Z"},{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:deployment.kubernetes.io/revision":{}}},"f:status":{"f:availableReplicas":{},"f:conditions":{".":{},"k:{\"type\":\"Available\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Progressing\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}}},"f:observedGeneration":{},"f:readyReplicas":{},"f:replicas":{},"f:updatedReplicas":{}}},"manager":"kube-controller-manager","operation":"Update","time":"2021-05-03T13:52:58Z"}],"name":"demoservice-server","namespace":"default","resourceVersion":"1016043","uid":"e9e8a3e9-6cb4-4301-ace1-2c0cef3bd61e"},"spec":{"progressDeadlineSeconds":600,"replicas":1,"revisionHistoryLimit":10,"selector":{"matchLabels":{"app":"demoservice-server"}},"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}},"status":{"availableReplicas":1,"conditions":[{"lastTransitionTime":"2021-05-03T13:10:32Z","lastUpdateTime":"2021-05-03T13:10:37Z","message":"ReplicaSet \"demoservice-server-7d478b6998\" has successfully progressed.","reason":"NewReplicaSetAvailable","status":"True","type":"Progressing"},{"lastTransitionTime":"2021-05-03T13:52:58Z","lastUpdateTime":"2021-05-03T13:52:58Z","message":"Deployment has minimum availability.","reason":"MinimumReplicasAvailable","status":"True","type":"Available"}],"observedGeneration":1,"readyReplicas":1,"replicas":1,"updatedReplicas":1}}`
workload, err := NewWorkload([]byte(w))
if err != nil {
t.Errorf(err.Error())
}
if workload.GetResourceVersion() != "1016043" {
t.Errorf("wrong resourceVersion")
}
}
func TestGetUID(t *testing.T) {
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"deployment.kubernetes.io/revision":"1"},"creationTimestamp":"2021-05-03T13:10:32Z","generation":1,"managedFields":[{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:labels":{".":{},"f:app":{},"f:cyberarmor.inject":{}}},"f:spec":{"f:progressDeadlineSeconds":{},"f:replicas":{},"f:revisionHistoryLimit":{},"f:selector":{},"f:strategy":{"f:rollingUpdate":{".":{},"f:maxSurge":{},"f:maxUnavailable":{}},"f:type":{}},"f:template":{"f:metadata":{"f:labels":{".":{},"f:app":{}}},"f:spec":{"f:containers":{"k:{\"name\":\"demoservice\"}":{".":{},"f:env":{".":{},"k:{\"name\":\"ARMO_TEST_NAME\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"CAA_ENABLE_CRASH_REPORTER\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"DEMO_FOLDERS\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SERVER_PORT\"}":{".":{},"f:name":{},"f:value":{}},"k:{\"name\":\"SLEEP_DURATION\"}":{".":{},"f:name":{},"f:value":{}}},"f:image":{},"f:imagePullPolicy":{},"f:name":{},"f:ports":{".":{},"k:{\"containerPort\":8089,\"protocol\":\"TCP\"}":{".":{},"f:containerPort":{},"f:protocol":{}}},"f:resources":{},"f:terminationMessagePath":{},"f:terminationMessagePolicy":{}}},"f:dnsPolicy":{},"f:restartPolicy":{},"f:schedulerName":{},"f:securityContext":{},"f:terminationGracePeriodSeconds":{}}}}},"manager":"OpenAPI-Generator","operation":"Update","time":"2021-05-03T13:10:32Z"},{"apiVersion":"apps/v1","fieldsType":"FieldsV1","fieldsV1":{"f:metadata":{"f:annotations":{".":{},"f:deployment.kubernetes.io/revision":{}}},"f:status":{"f:availableReplicas":{},"f:conditions":{".":{},"k:{\"type\":\"Available\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}},"k:{\"type\":\"Progressing\"}":{".":{},"f:lastTransitionTime":{},"f:lastUpdateTime":{},"f:message":{},"f:reason":{},"f:status":{},"f:type":{}}},"f:observedGeneration":{},"f:readyReplicas":{},"f:replicas":{},"f:updatedReplicas":{}}},"manager":"kube-controller-manager","operation":"Update","time":"2021-05-03T13:52:58Z"}],"name":"demoservice-server","namespace":"default","resourceVersion":"1016043","uid":"e9e8a3e9-6cb4-4301-ace1-2c0cef3bd61e"},"spec":{"progressDeadlineSeconds":600,"replicas":1,"revisionHistoryLimit":10,"selector":{"matchLabels":{"app":"demoservice-server"}},"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"},"template":{"metadata":{"creationTimestamp":null,"labels":{"app":"demoservice-server"}},"spec":{"containers":[{"env":[{"name":"SERVER_PORT","value":"8089"},{"name":"SLEEP_DURATION","value":"1"},{"name":"DEMO_FOLDERS","value":"/app"},{"name":"ARMO_TEST_NAME","value":"auto_attach_deployment"},{"name":"CAA_ENABLE_CRASH_REPORTER","value":"1"}],"image":"quay.io/armosec/demoservice:v25","imagePullPolicy":"IfNotPresent","name":"demoservice","ports":[{"containerPort":8089,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"terminationGracePeriodSeconds":30}}},"status":{"availableReplicas":1,"conditions":[{"lastTransitionTime":"2021-05-03T13:10:32Z","lastUpdateTime":"2021-05-03T13:10:37Z","message":"ReplicaSet \"demoservice-server-7d478b6998\" has successfully progressed.","reason":"NewReplicaSetAvailable","status":"True","type":"Progressing"},{"lastTransitionTime":"2021-05-03T13:52:58Z","lastUpdateTime":"2021-05-03T13:52:58Z","message":"Deployment has minimum availability.","reason":"MinimumReplicasAvailable","status":"True","type":"Available"}],"observedGeneration":1,"readyReplicas":1,"replicas":1,"updatedReplicas":1}}`
workload, err := NewWorkload([]byte(w))
if err != nil {
t.Errorf(err.Error())
}
if workload.GetUID() != "e9e8a3e9-6cb4-4301-ace1-2c0cef3bd61e" {
t.Errorf("wrong UID")
}
}
func TestIsAttached(t *testing.T) {
w := `{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"deployment.kubernetes.io/revision":"3"},"creationTimestamp":"2021-06-21T04:52:05Z","generation":3,"name":"emailservice","namespace":"default"},"spec":{"progressDeadlineSeconds":600,"replicas":1,"revisionHistoryLimit":10,"selector":{"matchLabels":{"app":"emailservice"}},"strategy":{"rollingUpdate":{"maxSurge":"25%","maxUnavailable":"25%"},"type":"RollingUpdate"},"template":{"metadata":{"annotations":{"armo.last-update":"21-06-2021 06:40:42","armo.wlid":"wlid://cluster-david-demo/namespace-default/deployment-emailservice"},"creationTimestamp":null,"labels":{"app":"emailservice","armo.attach":"true"}},"spec":{"containers":[{"env":[{"name":"PORT","value":"8080"},{"name":"DISABLE_PROFILER","value":"1"}],"image":"gcr.io/google-samples/microservices-demo/emailservice:v0.2.3","imagePullPolicy":"IfNotPresent","livenessProbe":{"exec":{"command":["/bin/grpc_health_probe","-addr=:8080"]},"failureThreshold":3,"periodSeconds":5,"successThreshold":1,"timeoutSeconds":1},"name":"server","ports":[{"containerPort":8080,"protocol":"TCP"}],"readinessProbe":{"exec":{"command":["/bin/grpc_health_probe","-addr=:8080"]},"failureThreshold":3,"periodSeconds":5,"successThreshold":1,"timeoutSeconds":1},"resources":{"limits":{"cpu":"200m","memory":"128Mi"},"requests":{"cpu":"100m","memory":"64Mi"}},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File"}],"dnsPolicy":"ClusterFirst","restartPolicy":"Always","schedulerName":"default-scheduler","securityContext":{},"serviceAccount":"default","serviceAccountName":"default","terminationGracePeriodSeconds":5}}}}`
workload, err := NewWorkload([]byte(w))
if err != nil {
t.Errorf(err.Error())
}
if !workload.IsAttached() {
t.Errorf("expected attached")
}
}

View File

@@ -1,23 +0,0 @@
package k8sinterface
func PodSpec(kind string) []string {
switch kind {
case "Pod", "Namespace":
return []string{"spec"}
case "CronJob":
return []string{"spec", "jobTemplate", "spec", "template", "spec"}
default:
return []string{"spec", "template", "spec"}
}
}
func PodMetadata(kind string) []string {
switch kind {
case "Pod", "Namespace", "Secret":
return []string{"metadata"}
case "CronJob":
return []string{"spec", "jobTemplate", "spec", "template", "metadata"}
default:
return []string{"spec", "template", "metadata"}
}
}

View File

@@ -1,7 +0,0 @@
package opapolicy
const (
PostureRestAPIPathV1 = "/v1/posture"
PostureRedisPrefix = "_postureReportv1"
K8sPostureNotification = "/k8srestapi/v1/newPostureReport"
)

View File

@@ -1,163 +0,0 @@
package opapolicy
import (
"time"
armotypes "github.com/armosec/kubescape/cautils/armotypes"
)
type AlertScore float32
type RuleLanguages string
const (
RegoLanguage RuleLanguages = "Rego"
RegoLanguage2 RuleLanguages = "rego"
)
// RegoResponse the expected response of single run of rego policy
type RuleResponse struct {
AlertMessage string `json:"alertMessage"`
RuleStatus string `json:"ruleStatus"`
PackageName string `json:"packagename"`
AlertScore AlertScore `json:"alertScore"`
AlertObject AlertObject `json:"alertObject"`
Context []string `json:"context,omitempty"` // TODO - Remove
Rulename string `json:"rulename,omitempty"` // TODO - Remove
ExceptionName string `json:"exceptionName,omitempty"` // Not in use
Exception *armotypes.PostureExceptionPolicy `json:"exception,omitempty"`
}
type AlertObject struct {
K8SApiObjects []map[string]interface{} `json:"k8sApiObjects,omitempty"`
ExternalObjects map[string]interface{} `json:"externalObjects,omitempty"`
}
type FrameworkReport struct {
Name string `json:"name"`
ControlReports []ControlReport `json:"controlReports"`
Score float32 `json:"score,omitempty"`
ARMOImprovement float32 `json:"ARMOImprovement,omitempty"`
WCSScore float32 `json:"wcsScore,omitempty"`
}
type ControlReport struct {
armotypes.PortalBase `json:",inline"`
Control_ID string `json:"id,omitempty"` // to be Deprecated
ControlID string `json:"controlID"`
Name string `json:"name"`
RuleReports []RuleReport `json:"ruleReports"`
Remediation string `json:"remediation"`
Description string `json:"description"`
Score float32 `json:"score"`
BaseScore float32 `json:"baseScore,omitempty"`
ARMOImprovement float32 `json:"ARMOImprovement,omitempty"`
}
type RuleReport struct {
Name string `json:"name"`
Remediation string `json:"remediation"`
RuleStatus RuleStatus `json:"ruleStatus"` // did we run the rule or not (if there where compile errors, the value will be failed)
RuleResponses []RuleResponse `json:"ruleResponses"`
ListInputResources []map[string]interface{} `json:"-"`
ListInputKinds []string `json:"-"`
}
type RuleStatus struct {
Status string `json:"status"`
Message string `json:"message"`
}
// PostureReport
type PostureReport struct {
CustomerGUID string `json:"customerGUID"`
ClusterName string `json:"clusterName"`
ReportID string `json:"reportID"`
JobID string `json:"jobID"`
ReportGenerationTime time.Time `json:"generationTime"`
FrameworkReports []FrameworkReport `json:"frameworks"`
}
// RuleMatchObjects defines which objects this rule applied on
type RuleMatchObjects struct {
APIGroups []string `json:"apiGroups"` // apps
APIVersions []string `json:"apiVersions"` // v1/ v1beta1 / *
Resources []string `json:"resources"` // dep.., pods,
}
// RuleMatchObjects defines which objects this rule applied on
type RuleDependency struct {
PackageName string `json:"packageName"` // package name
}
// PolicyRule represents single rule, the fundamental executable block of policy
type PolicyRule struct {
armotypes.PortalBase `json:",inline"`
CreationTime string `json:"creationTime"`
Rule string `json:"rule"` // multiline string!
RuleLanguage RuleLanguages `json:"ruleLanguage"`
Match []RuleMatchObjects `json:"match"`
RuleDependencies []RuleDependency `json:"ruleDependencies"`
Description string `json:"description"`
Remediation string `json:"remediation"`
RuleQuery string `json:"ruleQuery"` // default "armo_builtins" - DEPRECATED
}
// Control represents a collection of rules which are combined together to single purpose
type Control struct {
armotypes.PortalBase `json:",inline"`
Control_ID string `json:"id,omitempty"` // to be Deprecated
ControlID string `json:"controlID"`
CreationTime string `json:"creationTime"`
Description string `json:"description"`
Remediation string `json:"remediation"`
Rules []PolicyRule `json:"rules"`
// for new list of rules in POST/UPADTE requests
RulesIDs *[]string `json:"rulesIDs,omitempty"`
}
type UpdatedControl struct {
Control `json:",inline"`
Rules []interface{} `json:"rules"`
}
// Framework represents a collection of controls which are combined together to expose comprehensive behavior
type Framework struct {
armotypes.PortalBase `json:",inline"`
CreationTime string `json:"creationTime"`
Description string `json:"description"`
Controls []Control `json:"controls"`
// for new list of controls in POST/UPADTE requests
ControlsIDs *[]string `json:"controlsIDs,omitempty"`
}
type UpdatedFramework struct {
Framework `json:",inline"`
Controls []interface{} `json:"controls"`
}
type NotificationPolicyType string
type NotificationPolicyKind string
// Supported NotificationTypes
const (
TypeValidateRules NotificationPolicyType = "validateRules"
TypeExecPostureScan NotificationPolicyType = "execPostureScan"
TypeUpdateRules NotificationPolicyType = "updateRules"
)
// Supported NotificationKinds
const (
KindFramework NotificationPolicyKind = "Framework"
KindControl NotificationPolicyKind = "Control"
KindRule NotificationPolicyKind = "Rule"
)
type PolicyNotification struct {
NotificationType NotificationPolicyType `json:"notificationType"`
Rules []PolicyIdentifier `json:"rules"`
ReportID string `json:"reportID"`
JobID string `json:"jobID"`
Designators armotypes.PortalDesignator `json:"designators"`
}
type PolicyIdentifier struct {
Kind NotificationPolicyKind `json:"kind"`
Name string `json:"name"`
}

View File

@@ -1,301 +0,0 @@
package opapolicy
import (
"time"
armotypes "github.com/armosec/kubescape/cautils/armotypes"
)
// Mock A
var (
AMockCustomerGUID = "5d817063-096f-4d91-b39b-8665240080af"
AMockJobID = "36b6f9e1-3b63-4628-994d-cbe16f81e9c7"
AMockReportID = "2c31e4da-c6fe-440d-9b8a-785b80c8576a"
AMockClusterName = "clusterA"
AMockFrameworkName = "testFrameworkA"
AMockControlName = "testControlA"
AMockRuleName = "testRuleA"
AMockPortalBase = *armotypes.MockPortalBase(AMockCustomerGUID, "", nil)
)
func MockRuleResponseA() *RuleResponse {
return &RuleResponse{
AlertMessage: "test alert message A",
AlertScore: 0,
Rulename: AMockRuleName,
PackageName: "test.package.name.A",
Context: []string{},
}
}
func MockFrameworkReportA() *FrameworkReport {
return &FrameworkReport{
Name: AMockFrameworkName,
ControlReports: []ControlReport{
{
ControlID: "C-0010",
Name: AMockControlName,
RuleReports: []RuleReport{
{
Name: AMockRuleName,
Remediation: "remove privilegedContainer: True flag from your pod spec",
RuleResponses: []RuleResponse{
*MockRuleResponseA(),
},
},
},
},
},
}
}
func MockPostureReportA() *PostureReport {
return &PostureReport{
CustomerGUID: AMockCustomerGUID,
ClusterName: AMockClusterName,
ReportID: AMockReportID,
JobID: AMockJobID,
ReportGenerationTime: time.Now().UTC(),
FrameworkReports: []FrameworkReport{*MockFrameworkReportA()},
}
}
func MockFrameworkA() *Framework {
return &Framework{
PortalBase: *armotypes.MockPortalBase("aaaaaaaa-096f-4d91-b39b-8665240080af", AMockFrameworkName, nil),
CreationTime: "",
Description: "mock framework descryption",
Controls: []Control{
{
PortalBase: *armotypes.MockPortalBase("aaaaaaaa-aaaa-4d91-b39b-8665240080af", AMockControlName, nil),
Rules: []PolicyRule{
*MockRuleA(),
},
},
},
}
}
func MockRuleUntrustedRegistries() *PolicyRule {
return &PolicyRule{
PortalBase: *armotypes.MockPortalBase("aaaaaaaa-aaaa-aaaa-b39b-8665240080af", AMockControlName, nil),
Rule: `
package armo_builtins
# Check for images from blacklisted repos
untrusted_registries(z) = x {
x := ["015253967648.dkr.ecr.eu-central-1.amazonaws.com/"]
}
public_registries(z) = y{
y := ["quay.io/kiali/","quay.io/datawire/","quay.io/keycloak/","quay.io/bitnami/"]
}
untrustedImageRepo[msga] {
pod := input[_]
k := pod.kind
k == "Pod"
container := pod.spec.containers[_]
image := container.image
repo_prefix := untrusted_registries(image)[_]
startswith(image, repo_prefix)
selfLink := pod.metadata.selfLink
containerName := container.name
msga := {
"alertMessage": sprintf("image '%v' in container '%s' in [%s] comes from untrusted registry", [image, containerName, selfLink]),
"alert": true,
"prevent": false,
"alertScore": 2,
"alertObject": [{"pod":pod}]
}
}
untrustedImageRepo[msga] {
pod := input[_]
k := pod.kind
k == "Pod"
container := pod.spec.containers[_]
image := container.image
repo_prefix := public_registries(image)[_]
startswith(pod, repo_prefix)
selfLink := input.metadata.selfLink
containerName := container.name
msga := {
"alertMessage": sprintf("image '%v' in container '%s' in [%s] comes from public registry", [image, containerName, selfLink]),
"alert": true,
"prevent": false,
"alertScore": 1,
"alertObject": [{"pod":pod}]
}
}
`,
RuleLanguage: RegoLanguage,
Match: []RuleMatchObjects{
{
APIVersions: []string{"v1"},
APIGroups: []string{"*"},
Resources: []string{"pods"},
},
},
RuleDependencies: []RuleDependency{
{
PackageName: "kubernetes.api.client",
},
},
}
}
func MockRuleA() *PolicyRule {
return &PolicyRule{
PortalBase: *armotypes.MockPortalBase("aaaaaaaa-aaaa-aaaa-b39b-8665240080af", AMockControlName, nil),
Rule: MockRegoPrivilegedPods(), //
RuleLanguage: RegoLanguage,
Match: []RuleMatchObjects{
{
APIVersions: []string{"v1"},
APIGroups: []string{"*"},
Resources: []string{"pods"},
},
},
RuleDependencies: []RuleDependency{
{
PackageName: "kubernetes.api.client",
},
},
}
}
func MockRuleB() *PolicyRule {
return &PolicyRule{
PortalBase: *armotypes.MockPortalBase("bbbbbbbb-aaaa-aaaa-b39b-8665240080af", AMockControlName, nil),
Rule: MockExternalFacingService(), //
RuleLanguage: RegoLanguage,
Match: []RuleMatchObjects{
{
APIVersions: []string{"v1"},
APIGroups: []string{""},
Resources: []string{"pods"},
},
},
RuleDependencies: []RuleDependency{
{
PackageName: "kubernetes.api.client",
},
},
}
}
func MockPolicyNotificationA() *PolicyNotification {
return &PolicyNotification{
NotificationType: TypeExecPostureScan,
ReportID: AMockReportID,
JobID: AMockJobID,
Designators: armotypes.PortalDesignator{},
Rules: []PolicyIdentifier{
{
Kind: KindFramework,
Name: AMockFrameworkName,
}},
}
}
func MockTemp() string {
return `
package armo_builtins
import data.kubernetes.api.client as client
deny[msga] {
#object := input[_]
object := client.query_all("pods")
obj := object.body.items[_]
msga := {
"packagename": "armo_builtins",
"alertMessage": "found object",
"alertScore": 3,
"alertObject": {"object": obj},
}
}
`
}
func MockRegoPrivilegedPods() string {
return `package armo_builtins
import data.kubernetes.api.client as client
# Deny mutating action unless user is in group owning the resource
#privileged pods
deny[msga] {
pod := input[_]
containers := pod.spec.containers[_]
containers.securityContext.privileged == true
msga := {
"packagename": "armo_builtins",
"alertMessage": sprintf("the following pods are defined as privileged: %v", [pod]),
"alertScore": 3,
"alertObject": pod,
}
}
#handles majority of workload resources
deny[msga] {
wl := input[_]
spec_template_spec_patterns := {"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job"}
spec_template_spec_patterns[wl.kind]
containers := wl.spec.template.spec.containers[_]
containers.securityContext.privileged == true
msga := {
"packagename": "armo_builtins",
"alertMessage": sprintf("the following workloads are defined as privileged: %v", [wl]),
"alertScore": 3,
"alertObject": wl,
}
}
#handles cronjob
deny[msga] {
wl := input[_]
wl.kind == "CronJob"
containers := wl.spec.jobTemplate.spec.template.spec.containers[_]
containers.securityContext.privileged == true
msga := {
"packagename": "armo_builtins",
"alertMessage": sprintf("the following cronjobs are defined as privileged: %v", [wl]),
"alertScore": 3,
"alertObject": wl,
}
}
`
}
func MockExternalFacingService() string {
return "\n\tpackage armo_builtins\n\n\timport data.kubernetes.api.client as client\n\timport data.cautils as cautils\n\ndeny[msga] {\n\n\twl := input[_]\n\tcluster_resource := client.query_all(\n\t\t\"services\"\n\t)\n\n\tlabels := wl.metadata.labels\n\tfiltered_labels := json.remove(labels, [\"pod-template-hash\"])\n \n#service := cluster_resource.body.items[i]\nservices := [svc | cluster_resource.body.items[i].metadata.namespace == wl.metadata.namespace; svc := cluster_resource.body.items[i]]\nservice := services[_]\nnp_or_lb := {\"NodePort\", \"LoadBalancer\"}\nnp_or_lb[service.spec.type]\ncautils.is_subobject(service.spec.selector,filtered_labels)\n\n msga := {\n\t\t\"alertMessage\": sprintf(\"%v pod %v expose external facing service: %v\",[wl.metadata.namespace, wl.metadata.name, service.metadata.name]),\n\t\t\"alertScore\": 2,\n\t\t\"packagename\": \"armo_builtins\",\n\t\t\"alertObject\": {\"srvc\":service}\n\t}\n}\n\t"
}
func GetRuntimePods() string {
return `
package armo_builtins
import data.kubernetes.api.client as client
deny[msga] {
cluster_resource := client.query_all(
"pods"
)
pod := cluster_resource.body.items[i]
msga := {
"alertMessage": "got something",
"alertScore": 2,
"packagename": "armo_builtins",
"alertObject": {"pod": pod}
}
}
`
}

View File

@@ -1,42 +0,0 @@
package opapolicy
import (
"encoding/json"
"testing"
)
func TestMockPolicyNotificationA(t *testing.T) {
policy := MockPolicyNotificationA()
bp, err := json.Marshal(policy)
if err != nil {
t.Error(err)
} else {
t.Logf("%s\n", string(bp))
// t.Errorf("%s\n", string(bp))
}
}
func TestMockFrameworkA(t *testing.T) {
policy := MockFrameworkA()
bp, err := json.Marshal(policy)
if err != nil {
t.Error(err)
} else {
t.Logf("%s\n", string(bp))
// t.Errorf("%s\n", string(bp))
}
}
func TestMockPostureReportA(t *testing.T) {
policy := MockPostureReportA()
bp, err := json.Marshal(policy)
if err != nil {
t.Error(err)
} else {
// t.Errorf("%s\n", string(bp))
t.Logf("%s\n", string(bp))
}
}

View File

@@ -1,198 +0,0 @@
package opapolicy
import (
"bytes"
"encoding/json"
)
func (pn *PolicyNotification) ToJSONBytesBuffer() (*bytes.Buffer, error) {
res, err := json.Marshal(pn)
if err != nil {
return nil, err
}
return bytes.NewBuffer(res), err
}
func (RuleResponse *RuleResponse) GetSingleResultStatus() string {
if RuleResponse.Exception != nil {
if RuleResponse.Exception.IsAlertOnly() {
return "warning"
}
if RuleResponse.Exception.IsDisable() {
return "ignore"
}
}
return "failed"
}
func (ruleReport *RuleReport) GetRuleStatus() (string, []RuleResponse, []RuleResponse) {
if len(ruleReport.RuleResponses) == 0 {
return "success", nil, nil
}
exceptions := make([]RuleResponse, 0)
failed := make([]RuleResponse, 0)
for _, rule := range ruleReport.RuleResponses {
if rule.ExceptionName != "" {
exceptions = append(exceptions, rule)
} else if rule.Exception != nil {
exceptions = append(exceptions, rule)
} else {
failed = append(failed, rule)
}
}
status := "failed"
if len(failed) == 0 && len(exceptions) > 0 {
status = "warning"
}
return status, failed, exceptions
}
func (controlReport *ControlReport) GetNumberOfResources() int {
sum := 0
for i := range controlReport.RuleReports {
sum += controlReport.RuleReports[i].GetNumberOfResources()
}
return sum
}
func (controlReport *ControlReport) GetNumberOfFailedResources() int {
sum := 0
for i := range controlReport.RuleReports {
sum += controlReport.RuleReports[i].GetNumberOfFailedResources()
}
return sum
}
func (controlReport *ControlReport) GetNumberOfWarningResources() int {
sum := 0
for i := range controlReport.RuleReports {
sum += controlReport.RuleReports[i].GetNumberOfWarningResources()
}
return sum
}
func (controlReport *ControlReport) ListControlsInputKinds() []string {
listControlsInputKinds := []string{}
for i := range controlReport.RuleReports {
listControlsInputKinds = append(listControlsInputKinds, controlReport.RuleReports[i].ListInputKinds...)
}
return listControlsInputKinds
}
func (controlReport *ControlReport) Passed() bool {
for i := range controlReport.RuleReports {
if len(controlReport.RuleReports[i].RuleResponses) != 0 {
return false
}
}
return true
}
func (controlReport *ControlReport) Warning() bool {
if controlReport.Passed() || controlReport.Failed() {
return false
}
for i := range controlReport.RuleReports {
if status, _, _ := controlReport.RuleReports[i].GetRuleStatus(); status == "warning" {
return true
}
}
return false
}
func (controlReport *ControlReport) Failed() bool {
if controlReport.Passed() {
return false
}
for i := range controlReport.RuleReports {
if status, _, _ := controlReport.RuleReports[i].GetRuleStatus(); status == "failed" {
return true
}
}
return false
}
func (ruleReport *RuleReport) GetNumberOfResources() int {
return len(ruleReport.ListInputResources)
}
func (ruleReport *RuleReport) GetNumberOfFailedResources() int {
sum := 0
for i := len(ruleReport.RuleResponses) - 1; i >= 0; i-- {
if ruleReport.RuleResponses[i].GetSingleResultStatus() == "failed" {
sum += len(ruleReport.RuleResponses[i].AlertObject.K8SApiObjects)
}
}
return sum
}
func (ruleReport *RuleReport) GetNumberOfWarningResources() int {
sum := 0
for i := range ruleReport.RuleResponses {
if ruleReport.RuleResponses[i].GetSingleResultStatus() == "warning" {
sum += len(ruleReport.RuleResponses[i].AlertObject.K8SApiObjects)
}
}
return sum
}
func (postureReport *PostureReport) RemoveData() {
for i := range postureReport.FrameworkReports {
postureReport.FrameworkReports[i].RemoveData()
}
}
func (frameworkReport *FrameworkReport) RemoveData() {
for i := range frameworkReport.ControlReports {
frameworkReport.ControlReports[i].RemoveData()
}
}
func (controlReport *ControlReport) RemoveData() {
for i := range controlReport.RuleReports {
controlReport.RuleReports[i].RemoveData()
}
}
func (ruleReport *RuleReport) RemoveData() {
for i := range ruleReport.RuleResponses {
ruleReport.RuleResponses[i].RemoveData()
}
}
func (r *RuleResponse) RemoveData() {
r.AlertObject.ExternalObjects = nil
keepFields := []string{"kind", "apiVersion", "metadata"}
keepMetadataFields := []string{"name", "namespace", "labels"}
for i := range r.AlertObject.K8SApiObjects {
deleteFromMap(r.AlertObject.K8SApiObjects[i], keepFields)
for k := range r.AlertObject.K8SApiObjects[i] {
if k == "metadata" {
if b, ok := r.AlertObject.K8SApiObjects[i][k].(map[string]interface{}); ok {
deleteFromMap(b, keepMetadataFields)
r.AlertObject.K8SApiObjects[i][k] = b
}
}
}
}
}
func deleteFromMap(m map[string]interface{}, keepFields []string) {
for k := range m {
if StringInSlice(keepFields, k) {
continue
}
delete(m, k)
}
}
func StringInSlice(strSlice []string, str string) bool {
for i := range strSlice {
if strSlice[i] == str {
return true
}
}
return false
}

View File

@@ -1,47 +0,0 @@
package opapolicy
import (
"github.com/francoispqt/gojay"
"time"
)
/*
responsible on fast unmarshaling of various COMMON containerscan structures and substructures
*/
// UnmarshalJSONObject - File inside a pkg
func (r *PostureReport) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) {
switch key {
case "customerGUID":
err = dec.String(&(r.CustomerGUID))
case "clusterName":
err = dec.String(&(r.ClusterName))
case "reportID":
err = dec.String(&(r.ReportID))
case "jobID":
err = dec.String(&(r.JobID))
case "generationTime":
err = dec.Time(&(r.ReportGenerationTime), time.RFC3339)
r.ReportGenerationTime = r.ReportGenerationTime.Local()
}
return err
}
// func (files *PkgFiles) UnmarshalJSONArray(dec *gojay.Decoder) error {
// lae := PackageFile{}
// if err := dec.Object(&lae); err != nil {
// return err
// }
// *files = append(*files, lae)
// return nil
// }
func (file *PostureReport) NKeys() int {
return 0
}
//------------------------

View File

@@ -1,219 +0,0 @@
package resources
var RegoCAUtils = `
package cautils
list_contains(lista,element) {
some i
lista[i] == element
}
# getPodName(metadata) = name {
# name := metadata.generateName
#}
getPodName(metadata) = name {
name := metadata.name
}
#returns subobject ,sub1 is partial to parent, e.g parent = {a:a,b:b,c:c,d:d}
# sub1 = {b:b,c:c} - result is {b:b,c:c}, if sub1={b:b,e:f} returns {b:b}
object_intersection(parent,sub1) = r{
r := {k:p | p := sub1[k]
parent[k]== p
}
}
#returns if parent contains sub(both are objects not sets!!)
is_subobject(sub,parent) {
object_intersection(sub,parent) == sub
}
`
var RegoDesignators = `
package designators
import data.cautils
#functions that related to designators
#allowed_namespace
#@input@: receive as part of the input object "included_namespaces" list
#@input@: item's namespace as "namespace"
#returns true if namespace exists in that list
included_namespaces(namespace){
cautils.list_contains(["default"],namespace)
}
#forbidden_namespaces
#@input@: receive as part of the input object "forbidden_namespaces" list
#@input@: item's namespace as "namespace"
#returns true if namespace exists in that list
excluded_namespaces(namespace){
not cautils.list_contains(["excluded"],namespace)
}
forbidden_wlids(wlid){
input.forbidden_wlids[_] == wlid
}
filter_k8s_object(obj) = filtered {
#put
filtered := obj
#filtered := [ x | cautils.list_contains(["default"],obj[i].metadata.namespace) ; x := obj[i] ]
# filtered := [ x | not cautils.list_contains([],filter1Set[i].metadata.namespace); x := filter1Set[i]]
}
`
var RegoKubernetesApiClient = `
package kubernetes.api.client
# service account token
token := data.k8sconfig.token
# Cluster host
host := data.k8sconfig.host
# default certificate path
# crt_file := "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
crt_file := data.k8sconfig.crtfile
client_crt_file := data.k8sconfig.clientcrtfile
client_key_file := data.k8sconfig.clientkeyfile
# This information could be retrieved from the kubernetes API
# too, but would essentially require a request per API group,
# so for now use a lookup table for the most common resources.
resource_group_mapping := {
"services": "api/v1",
"pods": "api/v1",
"configmaps": "api/v1",
"secrets": "api/v1",
"persistentvolumeclaims": "api/v1",
"daemonsets": "apis/apps/v1",
"deployments": "apis/apps/v1",
"statefulsets": "apis/apps/v1",
"horizontalpodautoscalers": "api/autoscaling/v1",
"jobs": "apis/batch/v1",
"cronjobs": "apis/batch/v1beta1",
"ingresses": "api/extensions/v1beta1",
"replicasets": "apis/apps/v1",
"networkpolicies": "apis/networking.k8s.io/v1",
"clusterroles": "apis/rbac.authorization.k8s.io/v1",
"clusterrolebindings": "apis/rbac.authorization.k8s.io/v1",
"roles": "apis/rbac.authorization.k8s.io/v1",
"rolebindings": "apis/rbac.authorization.k8s.io/v1",
"serviceaccounts": "api/v1"
}
# Query for given resource/name in provided namespace
# Example: query_ns("deployments", "my-app", "default")
query_name_ns(resource, name, namespace) = http.send({
"url": sprintf("%v/%v/namespaces/%v/%v/%v", [
host,
resource_group_mapping[resource],
namespace,
resource,
name,
]),
"method": "get",
"headers": {"authorization": token},
"tls_client_cert_file": client_crt_file,
"tls_client_key_file": client_key_file,
"tls_ca_cert_file": crt_file,
"raise_error": true,
})
# Query for given resource type using label selectors
# https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api
# Example: query_label_selector_ns("deployments", {"app": "opa-kubernetes-api-client"}, "default")
query_label_selector_ns(resource, selector, namespace) = http.send({
"url": sprintf("%v/%v/namespaces/%v/%v?labelSelector=%v", [
host,
resource_group_mapping[resource],
namespace,
resource,
label_map_to_query_string(selector),
]),
"method": "get",
"headers": {"authorization": token},
"tls_client_cert_file": client_crt_file,
"tls_client_key_file": client_key_file,
"tls_ca_cert_file": crt_file,
"raise_error": true,
})
# x := field_transform_to_qry_param("spec.selector",input)
# input = {"app": "acmefit", "service": "catalog-db"}
# result: "spec.selector.app%3Dacmefit,spec.selector.service%3Dcatalog-db"
query_field_selector_ns(resource, field, selector, namespace) = http.send({
"url": sprintf("%v/%v/namespaces/%v/%v?fieldSelector=%v", [
host,
resource_group_mapping[resource],
namespace,
resource,
field_transform_to_qry_param(field,selector),
]),
"method": "get",
"headers": {"authorization": token},
"tls_client_cert_file": client_crt_file,
"tls_client_key_file": client_key_file,
"tls_ca_cert_file": crt_file,
"raise_error": true,
})
# # Query for all resources of type resource in all namespaces
# # Example: query_all("deployments")
# query_all(resource) = http.send({
# "url": sprintf("https://%v:%v/%v/%v", [
# ip,
# port,
# resource_group_mapping[resource],
# resource,
# ]),
# "method": "get",
# "headers": {"authorization": sprintf("Bearer %v", [token])},
# "tls_client_cert_file": crt_file,
# "raise_error": true,
# })
# Query for all resources of type resource in all namespaces
# Example: query_all("deployments")
query_all(resource) = http.send({
"url": sprintf("%v/%v/%v", [
host,
resource_group_mapping[resource],
resource,
]),
"method": "get",
"headers": {"authorization": token},
"tls_client_cert_file": client_crt_file,
"tls_client_key_file": client_key_file,
"tls_ca_cert_file": crt_file,
"raise_error": true,
})
# Query for all resources of type resource in all namespaces - without authentication
# Example: query_all("deployments")
query_all_no_auth(resource) = http.send({
"url": sprintf("%v/%v/namespaces/default/%v", [
host,
resource_group_mapping[resource],
resource,
]),
"method": "get",
"raise_error": true,
"tls_insecure_skip_verify" : true,
})
field_transform_to_qry_param(field,map) = finala {
mid := {concat(".",[field,key]): val | val := map[key]}
finala := label_map_to_query_string(mid)
}
label_map_to_query_string(map) = concat(",", [str | val := map[key]; str := concat("%3D", [key, val])])
`

View File

@@ -1,20 +0,0 @@
package armo_builtins
# import data.kubernetes.api.client as client
import data.cautils as cautils
# alert cronjobs
#handles cronjob
deny[msga] {
wl := input[_]
wl.kind == "CronJob"
msga := {
"alertMessage": sprintf("the following cronjobs are defined: %v", [wl]),
"alertScore": 2,
"packagename": "armo_builtins",
"alertObject": wl
}
}

View File

@@ -1,44 +0,0 @@
package armo_builtins
import data.kubernetes.api.client as client
# input: pod
# apiversion: v1
# does:
# returns the external facing services of that pod
#
#
deny[msga] {
pod := input[_]
podns := pod.metadata.namespace
podname := getName(pod.metadata)
# pod := client.query_name_ns("pods","frontend-86c5ffb485-kfp9d", "default")
labels := pod.body.metadata.labels
filtered_labels := json.remove(labels, ["pod-template-hash"])
cluster_resource := client.query_all(
"services"
)
services := [svc | cluster_resource.body.items[i].metadata.namespace == podns; svc := cluster_resource.body.items[i]]
service := services[_]
np_or_lb := {"NodePort", "LoadBalancer"}
np_or_lb[service.spec.type]
service.spec.selector == filtered_labels
msga := {
"packagename": "armo_builtins",
"alertMessage": sprintf("pod %v/%v exposed services: %v\n", [podns,podname,service]),
"alertScore": 7,
"alertObject": {"service":service,"labels":filtered_labels, "podname":podname,"namespace":podns}
}
}
getName(metadata) = name {
name := metadata.generateName
}
getName(metadata) = name {
name := metadata.name
}

View File

@@ -1,57 +0,0 @@
package armo_builtins
#import data.kubernetes.api.client as client
import data.cautils as cautils
# input: pod
# apiversion: v1
# does:
# returns hostPath volumes
#
#
deny[msga] {
pod := input[_]
pod.kind == "Pod"
volumes := pod.spec.volumes
volume := volumes[_]
# crsrcs.body.spec.containers[_].volumeMounts[_].name = volume.name
volume.hostPath
podname := cautils.getPodName(pod.metadata)
obj := {"volume":volume,"podname": podname}
msga := {
"packagename": "armo_builtins",
"alertMessage": sprintf("pod: %v has {%v,%v} ashostPath volume \n\n\n", [podname, volume]),
"alertScore": 7,
"alertObject": [obj]
}
}
isRWMount(mount) {
not mount.readOnly
}
isRWMount(mount) {
mount.readOnly == false
}
#handles majority of workload resources
deny[msga] {
wl := input[_]
spec_template_spec_patterns := {"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job"}
spec_template_spec_patterns[wl.kind]
volumes := wl.spec.template.spec.volumes
volume := volumes[_]
volume.hostPath
wlname := cautils.getPodName(wl.metadata)
obj := {"volume":volume,"podname": wlname}
msga := {
"packagename": "armo_builtins",
"alertMessage": sprintf("%v: %v has {%v,%v} as hostPath volume\n\n\n", [wl.kind,wlname, volume]),
"alertScore": 7,
"alertObject": [obj]
}
}

View File

@@ -1,56 +0,0 @@
package armo_builtins
#import data.kubernetes.api.client as client
# Deny mutating action unless user is in group owning the resource
#privileged pods
deny[msga] {
pod := input[_]
containers := pod.spec.containers[_]
containers.securityContext.privileged == true
msga := {
"packagename": "armo_builtins",
"alertMessage": sprintf("the following pods are defined as privileged: %v", [pod]),
"alertScore": 3,
"alertObject": pod,
}
}
#handles majority of workload resources
deny[msga] {
wl := input[_]
spec_template_spec_patterns := {"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job"}
spec_template_spec_patterns[wl.kind]
containers := wl.spec.template.spec.containers[_]
containers.securityContext.privileged == true
msga := {
"packagename": "armo_builtins",
"alertMessage": sprintf("the following workloads are defined as privileged: %v", [wl]),
"alertScore": 3,
"alertObject": wl,
}
}
#handles cronjob
deny[msga] {
wl := input[_]
wl.kind == "CronJob"
containers := wl.spec.jobTemplate.spec.template.spec.containers[_]
containers.securityContext.privileged == true
msga := {
"packagename": "armo_builtins",
"alertMessage": sprintf("the following cronjobs are defined as privileged: %v", [wl]),
"alertScore": 3,
"alertObject": wl,
}
}

View File

@@ -1,98 +0,0 @@
package armo_builtins
import data.kubernetes.api.client as client
import data.cautils as cautils
# input: None
# apiversion: v1
# does:
# returns roles+ related subjects in rolebinding
deny[msga] {
# rsrc := client.query_all("roles")
# role := rsrc.body.items[_]
role := input[_]
role.kind == "Role"
rule := role.rules[_]
cautils.list_contains(rule.resources,"secrets")
canViewSecrets(rule)
rbsrc := client.query_all("rolebindings")
rolebinding := rbsrc.body.items[_]
rolebinding.roleRef.kind == "Role"
rolebinding.roleRef.name == role.metadata.name
msga := {
"alertMessage": sprintf("the following users: %v , got read secret access roles", [rolebinding.subjects]),
"alertScore": 9,
"packagename": "armo_builtins",
"alertObject": {"role":role,"users":rolebinding.subjects}
}
}
# input: None
# apiversion: v1
# does:
# returns clusterroles+ related subjects in rolebinding
deny[msga] {
# rsrc := client.query_all("clusterroles")
# role := rsrc.body.items[_]
role := input[_]
role.kind == "ClusterRole"
rule := role.rules[_]
cautils.list_contains(rule.resources,"secrets")
canViewSecrets(rule)
rbsrc := client.query_all("rolebindings")
rolebinding := rbsrc.body.items[_]
rolebinding.roleRef.kind == "ClusterRole"
rolebinding.roleRef.name == role.metadata.name
msga := {
"alertMessage": sprintf("the following users: %v , got read secret access roles", [rolebinding.subjects]),
"alertScore": 9,
"packagename": "armo_builtins",
"alertObject": {"clusterrole":role,"users":rolebinding.subjects}
}
}
# input: None
# apiversion: v1
# does:
# returns clusterroles+ related subjects in clusterrolebinding
#
#
deny[msga] {
# rsrc := client.query_all("clusterroles")
# role := rsrc.body.items[_]
role := input[_]
role.kind == "ClusterRole"
rule := role.rules[_]
cautils.list_contains(rule.resources,"secrets")
canViewSecrets(rule)
rbsrc := client.query_all("clusterrolebindings")
rolebinding := rbsrc.body.items[_]
rolebinding.roleRef.kind == "ClusterRole"
rolebinding.roleRef.name == role.metadata.name
msga := {
"alertMessage": sprintf("the following users: %v , got read secret access roles", [rolebinding.subjects]),
"alertScore": 9,
"packagename": "armo_builtins",
"alertObject": {"clusterrole":role,"users":rolebinding.subjects}
}
}
canViewSecrets(rule) {
cautils.list_contains(rule.verbs,"get")
}
canViewSecrets(rule) {
cautils.list_contains(rule.verbs,"watch")
}

View File

@@ -1,64 +0,0 @@
package armo_builtins
#import data.kubernetes.api.client as client
import data.cautils as cautils
# input: pod
# apiversion: v1
# does:
# returns rw hostpath volumes of that pod
#
#
deny[msga] {
pod := input[_]
pod.kind == "Pod"
volumes := pod.spec.volumes
volume := volumes[_]
# crsrcs.body.spec.containers[_].volumeMounts[_].name = volume.name
mount := pod.spec.containers[_].volumeMounts[_]
mount.name == volume.name
volume.hostPath
isRWMount(mount)
podname := cautils.getPodName(pod.metadata)
obj := {"volume":volume,"mount":mount,"podname": podname}
msga := {
"packagename": "armo_builtins",
"alertMessage": sprintf("pod: %v has {%v,%v} as rw hostPath volume and volumemount pair\n\n\n", [podname, volume,mount]),
"alertScore": 7,
"alertObject": [obj],
}
}
isRWMount(mount) {
not mount.readOnly
}
isRWMount(mount) {
mount.readOnly == false
}
#handles majority of workload resources
deny[msga] {
wl := input[_]
spec_template_spec_patterns := {"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job"}
spec_template_spec_patterns[wl.kind]
volumes := wl.spec.template.spec.volumes
volume := volumes[_]
mount := wl.spec.template.spec.containers[_].volumeMounts[_]
mount.name == volume.name
volume.hostPath
isRWMount(mount)
wlname := cautils.getPodName(wl.metadata)
obj := {"volume":volume,"mount":mount,"podname": wlname}
msga := {
"packagename": "armo_builtins",
"alertMessage": sprintf("%v: %v has {%v,%v} as rw hostPath volume and volumemount pair\n\n\n", [wl.kind,wlname, volume,mount]),
"alertScore": 7,
"alertObject": [obj],
}
}

View File

@@ -1,57 +0,0 @@
package armo_builtins
import data.kubernetes.api.client as client
import data.cautils as cautils
# input: pod
# apiversion: v1
# does:
# returns the external facing services of that pod
#
#
deny[msga] {
pod := input[_]
podns := pod.metadata.namespace
podname := cautils.getPodName(pod.metadata)
# pod := client.query_name_ns("pods", "catalog-mongo-6f468d99b4-pn242", "default")
labels := pod.body.metadata.labels
filtered_labels := json.remove(labels, ["pod-template-hash"])
cluster_resource := client.query_all(
"services"
)
services := [svc | cluster_resource.body.items[i].metadata.namespace == podns; svc := cluster_resource.body.items[i]]
service := services[_]
service.spec.selector == filtered_labels
hasSSHPorts(service)
msga := {
"alertMessage": sprintf("pod %v/%v exposed by SSH services: %v\n", [podns,podname,service]),
"packagename": "armo_builtins",
"alertScore": 7,
"alertObject": [{"pod":pod,"service":{service}}]
}
}
hasSSHPorts(service) {
port := service.spec.ports[_]
port.port == 22
}
hasSSHPorts(service) {
port := service.spec.ports[_]
port.port == 2222
}
hasSSHPorts(service) {
port := service.spec.ports[_]
port.targetPort == 22
}
hasSSHPorts(service) {
port := service.spec.ports[_]
port.targetPort == 2222
}

View File

@@ -1,33 +0,0 @@
{
"guid": "3b0467c9-488d-c244-99d0-90fbf600aaff",
"name": "[Builtin] rule-deny-access-to-secrets",
"creationTime": "2019-09-04T12:04:58.461455",
"description": "determines which users can get/list/watch secrets",
"attributes": {
"m$K8sThreatMatrix": "Credential Access::List k8s Secrets"
},
"ruleDependencies": [
{
"packageName":"cautils"
},
{
"packageName":"kubernetes.api.client"
}
],
"remediation": "",
"match": [
{
"resources": [
"Role","ClusterRole"
],
"apiVersions": [
"v1"
],
"apiGroups": [
"rbac.authorization.k8s.io"
]
}
],
"ruleLanguage": "Rego",
"rule": "\npackage armo_builtins\nimport data.kubernetes.api.client as client\nimport data.cautils as cautils\n\n\n# input: None\n# apiversion: v1\n# does: \n#\treturns roles+ related subjects in rolebinding\n\n\ndeny[msga] {\n\t# rsrc := client.query_all(\"roles\")\n\t# role := rsrc.body.items[_]\n\trole := input[_]\n\trole.kind == \"Role\"\n\trule := role.rules[_]\n\tcautils.list_contains(rule.resources,\"secrets\")\n\tcanViewSecrets(rule)\n\trbsrc := client.query_all(\"rolebindings\")\n\trolebinding := rbsrc.body.items[_]\n\trolebinding.roleRef.kind == \"Role\"\n\trolebinding.roleRef.name == role.metadata.name\n\t\n \n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"the following users: %v , got read secret access roles\", [rolebinding.subjects]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 9,\n\t\t\"alertObject\": {\"role\":role,\"users\":rolebinding.subjects}\n\t\n\t}\n}\n\n\n\n# input: None\n# apiversion: v1\n# does: \n#\treturns clusterroles+ related subjects in rolebinding\n\n\ndeny[msga] {\n\t# rsrc := client.query_all(\"clusterroles\")\n\t# role := rsrc.body.items[_]\n\trole := input[_]\n\trole.kind == \"ClusterRole\"\n\trule := role.rules[_]\n\tcautils.list_contains(rule.resources,\"secrets\")\n\tcanViewSecrets(rule)\n\trbsrc := client.query_all(\"rolebindings\")\n\trolebinding := rbsrc.body.items[_]\n\trolebinding.roleRef.kind == \"ClusterRole\"\n\trolebinding.roleRef.name == role.metadata.name\n\t\n \n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"the following users: %v , got read secret access roles\", [rolebinding.subjects]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 9,\n\t\t\"alertObject\": {\"clusterrole\":role,\"users\":rolebinding.subjects}\n\t\n\t}\n}\n\n\n# input: None\n# apiversion: v1\n# does: \n#\treturns clusterroles+ related subjects in clusterrolebinding\n#\n#\ndeny[msga] {\n\t# rsrc := client.query_all(\"clusterroles\")\n\t# role := rsrc.body.items[_]\n\trole := input[_]\n\trole.kind == \"ClusterRole\"\n\trule := role.rules[_]\n\tcautils.list_contains(rule.resources,\"secrets\")\n\tcanViewSecrets(rule)\n\trbsrc := client.query_all(\"clusterrolebindings\")\n\trolebinding := rbsrc.body.items[_]\n\trolebinding.roleRef.kind == \"ClusterRole\"\n\trolebinding.roleRef.name == role.metadata.name\n\t\n \n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"the following users: %v , got read secret access roles\", [rolebinding.subjects]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 9,\n\t\t\"alertObject\": {\"clusterrole\":role,\"users\":rolebinding.subjects}\n\t\n\t}\n}\n\ncanViewSecrets(rule) {\n\tcautils.list_contains(rule.verbs,\"get\")\n}\ncanViewSecrets(rule) {\n\tcautils.list_contains(rule.verbs,\"watch\")\n}\n"
}

View File

@@ -1,34 +0,0 @@
{
"guid": "3b0467c9-488d-c244-99d0-90fbf600aaff",
"name": "[Builtin] rule-can-ssh-to-pod",
"creationTime": "2019-09-04T12:04:58.461455",
"description": "denies pods with SSH ports opened(22/222)",
"attributes": {
"microsoftK8sThreatMatrix": "val1"
},
"ruleDependencies": [
{
"packageName":"cautils"
},
{
"packageName":"kubernetes.api.client"
}
],
"remediation": "create a network policy that protects SSH ports",
"match": [
{
"resources": [
"Pods"
],
"apiVersions": [
"v1"
],
"apiGroups": [
"*"
]
}
],
"ruleLanguage": "Rego",
"rule": "\npackage armo_builtins\nimport data.kubernetes.api.client as client\nimport data.cautils as cautils\n\n# input: pod\n# apiversion: v1\n# does: \n#\treturns the external facing services of that pod\n#\n#\ndeny[msga] {\n\tpod := input[_]\n\tpodns := pod.metadata.namespace\n\tpodname := cautils.getPodName(pod.metadata)\n\t# pod := client.query_name_ns(\"pods\", \"catalog-mongo-6f468d99b4-pn242\", \"default\")\n\tlabels := pod.body.metadata.labels\n\tfiltered_labels := json.remove(labels, [\"pod-template-hash\"])\n \n\t cluster_resource := client.query_all(\n\t \t\"services\"\n\t )\n\n\tservices := [svc | cluster_resource.body.items[i].metadata.namespace == podns; svc := cluster_resource.body.items[i]]\n\tservice := \tservices[_]\n\tservice.spec.selector == filtered_labels\n \n\thasSSHPorts(service)\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"pod %v/%v exposed by SSH services: %v\n\", [podns,podname,service]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": [{\"pod\":pod,\"service\":{service}}]\n\t\n\t}\n}\n\nhasSSHPorts(service) {\n\tport := service.spec.ports[_]\n\tport.port == 22\n}\n\n\nhasSSHPorts(service) {\n\tport := service.spec.ports[_]\n\tport.port == 2222\n}\n\nhasSSHPorts(service) {\n\tport := service.spec.ports[_]\n\tport.targetPort == 22\n}\n\n\nhasSSHPorts(service) {\n\tport := service.spec.ports[_]\n\tport.targetPort == 2222\n}\n"
}

View File

@@ -1,33 +0,0 @@
{
"guid": "",
"name": "[Builtin] rule-identify-blacklisted-image-registries",
"creationTime": "",
"description": "Identifying if pod container images are from unallowed registries",
"attributes": {
"m$K8sThreatMatrix": "Initial Access::Compromised images in registry"
},
"ruleDependencies": [
{
"packageName": "cautils"
},
{
"packageName": "kubernetes.api.client"
}
],
"remediation": "Use images from safe registry",
"match": [
{
"resources": [
"Pods"
],
"apiVersions": [
"v1"
],
"apiGroups": [
"*"
]
}
],
"ruleLanguage": "Rego",
"rule": "\npackage armo_builtins\n# Check for images from blacklisted repos\n\nuntrusted_registries(z) = x {\n\tx := [\"015253967648.dkr.ecr.eu-central-1.amazonaws.com/\"]\t\n}\n\npublic_registries(z) = y{\n\ty := [\"quay.io/kiali/\",\"quay.io/datawire/\",\"quay.io/keycloak/\",\"quay.io/bitnami/\"]\n}\n\nuntrustedImageRepo[msga] {\n\tpod := input[_]\n\tk := pod.kind\n\tk == \"Pod\"\n\tcontainer := pod.spec.containers[_]\n\timage := container.image\n repo_prefix := untrusted_registries(image)[_]\n\tstartswith(image, repo_prefix)\n\tselfLink := pod.metadata.selfLink\n\tcontainerName := container.name\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"image '%v' in container '%s' in [%s] comes from untrusted registry\", [image, containerName, selfLink]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 2,\n\t\t\"alertObject\": [{\"pod\":pod}]\n\t}\n}\n\nuntrustedImageRepo[msga] {\n pod := input[_]\n\tk := pod.kind\n\tk == \"Pod\"\n\tcontainer := pod.spec.containers[_]\n\timage := container.image\n repo_prefix := public_registries(image)[_]\n\tstartswith(pod, repo_prefix)\n\tselfLink := input.metadata.selfLink\n\tcontainerName := container.name\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"image '%v' in container '%s' in [%s] comes from public registry\", [image, containerName, selfLink]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 1,\n\t\t\"alertObject\": [{\"pod\":pod}]\n\t}\n}"
}

View File

@@ -1,31 +0,0 @@
{
"guid": "3b0467c9-488d-c244-99d0-90fbf600aaff",
"name": "[Builtin] rule-pod-external-facing",
"creationTime": "2019-09-04T12:04:58.461455",
"description": "denies pods with external facing services, grabs related services",
"attributes": {
"microsoftK8sThreatMatrix": "val1"
},
"ruleDependencies": [
{
"packageName":"kubernetes.api.client"
}
],
"remediation": "create a network policy that controls which protect your cluster from unwanted connections and the outside world",
"match": [
{
"resources": [
"Pods"
],
"apiVersions": [
"v1"
],
"apiGroups": [
"*"
]
}
],
"ruleLanguage": "Rego",
"rule": "\npackage armo_builtins\n\nimport data.kubernetes.api.client as client\n\n\n# input: pod\n# apiversion: v1\n# does: \n#\treturns the external facing services of that pod\n#\n#\ndeny[msga] {\n\tpod := input[_]\n\tpodns := pod.metadata.namespace\n\tpodname := getName(pod.metadata)\n\t# pod := client.query_name_ns(\"pods\",\"frontend-86c5ffb485-kfp9d\", \"default\")\n\tlabels := pod.body.metadata.labels\n\tfiltered_labels := json.remove(labels, [\"pod-template-hash\"])\n \n\t cluster_resource := client.query_all(\n\t \t\"services\"\n\t )\n\n\n\tservices := [svc | cluster_resource.body.items[i].metadata.namespace == podns; svc := cluster_resource.body.items[i]]\n\tservice := \tservices[_]\n\tnp_or_lb := {\"NodePort\", \"LoadBalancer\"}\n\tnp_or_lb[service.spec.type]\n\tservice.spec.selector == filtered_labels\n \n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"pod %v/%v exposed services: %v\n\", [podns,podname,service]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": {\"service\":service,\"labels\":filtered_labels, \"podname\":podname,\"namespace\":podns}\n\t\n\t}\n}\n\ngetName(metadata) = name {\n\tname := metadata.generateName\n}\ngetName(metadata) = name {\n\tname := metadata.name\n}\n"
}

View File

@@ -1,31 +0,0 @@
{
"guid": "3b0467c9-488d-c244-99d0-90fbf600aaff",
"name": "[Builtin] alert-any-hostpath",
"creationTime": "2019-09-04T12:04:58.461455",
"description": "determines if any workload contains a hostPath volume",
"attributes": {
"m$K8sThreatMatrix": "Privilege Escalation::hostPath mount"
},
"ruleDependencies": [
{
"packageName":"cautils"
}
],
"remediation": "consider if hostPath is really necessary - reading sensitive data like hostPath credentials might endanger cluster, if so consider encrypting the data",
"match": [
{
"resources": [
"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod"
],
"apiVersions": [
"v1"
],
"apiGroups": [
"*"
]
}
],
"ruleLanguage": "Rego",
"rule": "\npackage armo_builtins\nimport data.kubernetes.api.client as client\nimport data.cautils as cautils\n\n# input: pod\n# apiversion: v1\n# does: \n#\treturns hostPath volumes\n#\n#\ndeny[msga] {\n pod := input[_]\n pod.kind == \"Pod\"\n volumes := pod.spec.volumes\n volume := volumes[_]\n # crsrcs.body.spec.containers[_].volumeMounts[_].name = volume.name\n volume.hostPath\n podname := cautils.getPodName(pod.metadata)\n obj := {\"volume\":volume,\"podname\": podname}\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"pod: %v has {%v,%v} ashostPath volume \n\n\n\", [podname, volume]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": [obj],\n\t\n\t}\n}\n\nisRWMount(mount) {\n not mount.readOnly\n}\nisRWMount(mount) {\n mount.readOnly == false\n}\n\n\n#handles majority of workload resources\ndeny[msga] {\n\n\twl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n volumes := wl.spec.template.spec.volumes\n volume := volumes[_]\n volume.hostPath\n wlname := cautils.getPodName(wl.metadata)\n obj := {\"volume\":volume,\"podname\": wlname}\n\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"%v: %v has {%v,%v} as hostPath volume\n\n\n\", [wl.kind,wlname, volume]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 7,\n\t\t\"alertObject\": [obj],\n\t\n\t}\n}\n\n\n"
}

View File

@@ -1,27 +0,0 @@
{
"guid": "82f19070-2826-4fe4-a079-f5f7e7a1b04d",
"name": "[Builtin] instance-metadata-api-access",
"attributes": {
"m$K8sThreatMatrix": "Credential Access::Instance Metadata API"
},
"creationTime": "2021-04-25T10:48:48.861806",
"rule": "package armo_builtins\n# Check for images from blacklisted repos\n\nmetadata_azure(z) = http.send({\n\t\"url\": \"http://169.254.169.254/metadata/instance?api-version=2020-09-01\",\n\t\"method\": \"get\",\n\t\"headers\": {\"Metadata\": \"true\"},\n\t\"raise_error\": true,\t\n})\n\nmetadata_gcp(z) = http.send({\n\t\"url\": \"http://169.254.169.254/computeMetadata/v1/?alt=json&recursive=true\",\n\t\"method\": \"get\",\n\t\"headers\": {\"Metadata-Flavor\": \"Google\"},\n\t\"raise_error\": true,\t\n})\n\nmetadata_aws(z) = metadata_object { \n\thostname := http.send({\n\t\"url\": \"http://169.254.169.254/latest/meta-data/local-hostname\",\n\t\"method\": \"get\",\n\t\"raise_error\": true,\t\n })\n\tmetadata_object := {\n\t\t\"raw_body\": hostname.raw_body,\n\t\t\"hostname\" : hostname.raw_body,\n\t\t\"status_code\" : hostname.status_code\n\t}\n}\n\nazure_metadata[msga] {\t\n\tmetadata_object := metadata_azure(\"aaa\")\n\tmetadata_object.status_code == 200\n\tnode_name := metadata_object.body.compute.name\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Node '%s' has access to Instance Metadata Services of Azure.\", [node_name]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 1,\n\t\t\"alertObject\": [{\"nodeMetadata\":metadata_object.body}]\n\t}\n}\n\ngcp_metadata[msga] {\t\n\tmetadata_object := metadata_gcp(\"aaa\")\n\tmetadata_object.status_code == 200\n\tnode_name := metadata_object.body.instance.hostname\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Node '%s' has access to Instance Metadata Services of GCP.\", [node_name]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 1,\n\t\t\"alertObject\": [{\"nodeMetadata\": metadata_object.raw_body}]\n\t}\n}\n\naws_metadata[msga] {\t\n\tmetadata_object := metadata_aws(\"aaa\")\n\tmetadata_object.status_code == 200\n\tnode_name := metadata_object.hostname\n\tmsga := {\n\t\t\"alertMessage\": sprintf(\"Node '%s' has access to Instance Metadata Services of AWS.\", [node_name]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 1,\n\t\t\"alertObject\": [{\"nodeMetadata\": metadata_object.raw_body}]\n\t}\n}",
"ruleLanguage": "Rego",
"match": [
{
"apiGroups": [
"*"
],
"apiVersions": [
"*"
],
"resources": [
"nodes"
]
}
],
"ruleDependencies": [],
"description": "Checks if there is access from the nodes to cloud prividers instance metadata services",
"remediation": "From https://attack.mitre.org/techniques/T1552/005/ :Option A: Disable or Remove Feature or Program, Option B: Filter Network Traffic",
"ruleQuery": ""
}

View File

@@ -1,30 +0,0 @@
{
"guid": "[Builtin] 3b0467c9-488d-c244-99d0-90fbf600aaff",
"name": "rule-deny-cronjobs",
"creationTime": "2019-09-04T12:04:58.461455",
"description": "determines if it's cronjob",
"attributes": {
"m$K8sThreatMatrix": "Persistence::Cronjob"
},
"ruleDependencies": [
{
"packageName":"cautils"
}
],
"remediation": "",
"match": [
{
"resources": [
"CronJob"
],
"apiVersions": [
"v1beta1"
],
"apiGroups": [
"batch"
]
}
],
"ruleLanguage": "Rego",
"rule": "\npackage armo_builtins\n\n# import data.kubernetes.api.client as client\nimport data.cautils as cautils\n\n\n# alert cronjobs\n\n#handles cronjob\ndeny[msga] {\n\n\twl := input[_]\n\twl.kind == \"CronJob\"\n msga := {\n\t\t\"alertMessage\": sprintf(\"the following cronjobs are defined: %v\", [wl]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 2,\n\t\t\"alertObject\": wl\n\t\n\t}\n}\n"
}

View File

@@ -1,29 +0,0 @@
{
"guid": "",
"name": "[Builtin] rule-privilege-escalation",
"creationTime": "2019-09-04T12:04:58.461455",
"description": "determines if pods/deployments defined as privileged true",
"attributes": {
"mitre": "Privilege Escalation",
"mitreCode": "TA0004",
"m$K8sThreatMatrix": "Privilege Escalation::privileged container"
},
"ruleDependencies": [
],
"remediation": "avoid defining pods as privilleged",
"match": [
{
"resources": [
"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod","CronJob"
],
"apiVersions": [
"v1"
],
"apiGroups": [
"*"
]
}
],
"ruleLanguage": "Rego",
"rule": "\npackage armo_builtins\n\nimport data.kubernetes.api.client as client\nimport data.designators as scope\nimport data.cautils as cautils\n\n\n# Deny mutating action unless user is in group owning the resource\n\n\n#privileged pods\ndeny[msga] {\n\n \n\tpod := input[_]\n\tcontainers := pod.spec.containers[_]\n\tcontainers.securityContext.privileged == true\n msga := {\n\t\t\"alertMessage\": sprintf(\"the following pods are defined as privileged: %v\", [pod]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 3,\n\t\t\"alertObject\": pod,\n\t\n\t}\n}\n\n\n#handles majority of workload resources\ndeny[msga] {\n\n\twl := input[_]\n\tspec_template_spec_patterns := {\"Deployment\",\"ReplicaSet\",\"DaemonSet\",\"StatefulSet\",\"Job\"}\n\tspec_template_spec_patterns[wl.kind]\n\tcontainers := wl.spec.template.spec.containers[_]\n\tcontainers.securityContext.privileged == true\n msga := {\n\t\t\"alertMessage\": sprintf(\"the following workloads are defined as privileged: %v\", [wl]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 3,\n\t\t\"alertObject\": wl,\n\t\n\t}\n}\n\n\n\n#handles cronjob\ndeny[msga] {\n\n\twl := input[_]\n\twl.kind == \"CronJob\"\n\tcontainers := wl.spec.jobTemplate.spec.template.spec.containers[_]\n\tcontainers.securityContext.privileged == true\n msga := {\n\t\t\"alertMessage\": sprintf(\"the following cronjobs are defined as privileged: %v\", [wl]),\n\t\t\"alert\": true,\n\t\t\"prevent\": false,\n\t\t\"alertScore\": 3,\n\t\t\"alertObject\": wl,\n\t\n\t}\n}\n\n"
}

View File

@@ -1,31 +0,0 @@
{
"guid": "3b0467c9-488d-c244-99d0-90fbf600aaff",
"name": "[Builtin] alert-rw-hostpath",
"creationTime": "2019-09-04T12:04:58.461455",
"description": "determines if any workload contains a hostPath volume with rw permissions",
"attributes": {
"m$K8sThreatMatrix": "Persistance::Writable hostPath mount"
},
"ruleDependencies": [
{
"packageName":"cautils"
}
],
"remediation": "consider if hostPath is really necessary- sensitive data like hostPath credentials might endanger cluster, if so consider encrypting the data",
"match": [
{
"resources": [
"Deployment","ReplicaSet","DaemonSet","StatefulSet","Job","Pod"
],
"apiVersions": [
"v1"
],
"apiGroups": [
"*"
]
}
],
"ruleLanguage": "Rego",
"rule": "\"\\npackage armo_builtins\\nimport data.kubernetes.api.client as client\\nimport data.cautils as cautils\\n\\n# input: pod\\n# apiversion: v1\\n# does: \\n#\\treturns hostPath volumes\\n#\\n#\\ndeny[msga] {\\n pod := input[_]\\n pod.kind == \\\"Pod\\\"\\n volumes := pod.spec.volumes\\n volume := volumes[_]\\n # crsrcs.body.spec.containers[_].volumeMounts[_].name = volume.name\\n volume.hostPath\\n podname := cautils.getPodName(pod.metadata)\\n obj := {\\\"volume\\\":volume,\\\"podname\\\": podname}\\n\\n\\tmsga := {\\n\\t\\t\\\"alertMessage\\\": sprintf(\\\"pod: %v has {%v,%v} ashostPath volume \\n\\n\\n\\\", [podname, volume]),\\n\\t\\t\\\"alert\\\": true,\\n\\t\\t\\\"prevent\\\": false,\\n\\t\\t\\\"alertScore\\\": 7,\\n\\t\\t\\\"alertObject\\\": [obj],\\n\\t\\n\\t}\\n}\\n\\nisRWMount(mount) {\\n not mount.readOnly\\n}\\nisRWMount(mount) {\\n mount.readOnly == false\\n}\\n\\n\\n#handles majority of workload resources\\ndeny[msga] {\\n\\n\\twl := input[_]\\n\\tspec_template_spec_patterns := {\\\"Deployment\\\",\\\"ReplicaSet\\\",\\\"DaemonSet\\\",\\\"StatefulSet\\\",\\\"Job\\\"}\\n\\tspec_template_spec_patterns[wl.kind]\\n volumes := wl.spec.template.spec.volumes\\n volume := volumes[_]\\n volume.hostPath\\n wlname := cautils.getPodName(wl.metadata)\\n obj := {\\\"volume\\\":volume,\\\"podname\\\": wlname}\\n\\n\\tmsga := {\\n\\t\\t\\\"alertMessage\\\": sprintf(\\\"%v: %v has {%v,%v} as hostPath volume\\n\\n\\n\\\", [wl.kind,wlname, volume]),\\n\\t\\t\\\"alert\\\": true,\\n\\t\\t\\\"prevent\\\": false,\\n\\t\\t\\\"alertScore\\\": 7,\\n\\t\\t\\\"alertObject\\\": [obj],\\n\\t\\n\\t}\\n}\\n\\n\\n\""
}

View File

@@ -1,118 +0,0 @@
package resources
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/armosec/kubescape/cautils/k8sinterface"
"github.com/golang/glog"
"github.com/open-policy-agent/opa/storage"
"github.com/open-policy-agent/opa/storage/inmem"
"github.com/open-policy-agent/opa/util"
"k8s.io/client-go/rest"
)
var (
RegoDependenciesPath = "/resources/rego/dependencies"
)
type RegoDependenciesData struct {
K8sConfig RegoK8sConfig `json:"k8sconfig"`
}
type RegoK8sConfig struct {
Token string `json:"token"`
IP string `json:"ip"`
Host string `json:"host"`
Port string `json:"port"`
CrtFile string `json:"crtfile"`
ClientCrtFile string `json:"clientcrtfile"`
ClientKeyFile string `json:"clientkeyfile"`
// ClientKeyFile string `json:"crtfile"`
}
func NewRegoDependenciesDataMock() *RegoDependenciesData {
return NewRegoDependenciesData(k8sinterface.GetK8sConfig())
}
func NewRegoDependenciesData(k8sConfig *rest.Config) *RegoDependenciesData {
regoDependenciesData := RegoDependenciesData{}
if k8sConfig != nil {
regoDependenciesData.K8sConfig = *NewRegoK8sConfig(k8sConfig)
}
return &regoDependenciesData
}
func NewRegoK8sConfig(k8sConfig *rest.Config) *RegoK8sConfig {
host := k8sConfig.Host
if host == "" {
ip := os.Getenv("KUBERNETES_SERVICE_HOST")
port := os.Getenv("KUBERNETES_SERVICE_PORT")
host = fmt.Sprintf("https://%s:%s", ip, port)
}
token := ""
if k8sConfig.BearerToken != "" {
token = fmt.Sprintf("Bearer %s", k8sConfig.BearerToken)
}
regoK8sConfig := RegoK8sConfig{
Token: token,
Host: host,
CrtFile: k8sConfig.CAFile,
ClientCrtFile: k8sConfig.CertFile,
ClientKeyFile: k8sConfig.KeyFile,
}
return &regoK8sConfig
}
func (data *RegoDependenciesData) TOStorage() (storage.Store, error) {
var jsonObj map[string]interface{}
bytesData, err := json.Marshal(*data)
if err != nil {
return nil, err
}
// glog.Infof("RegoDependenciesData: %s", bytesData)
if err := util.UnmarshalJSON(bytesData, &jsonObj); err != nil {
return nil, err
}
return inmem.NewFromObject(jsonObj), nil
}
// LoadRegoDependenciesFromDir loads the policies list from *.rego file in given directory
func LoadRegoFiles(dir string) map[string]string {
modules := make(map[string]string)
// Compile the module. The keys are used as identifiers in error messages.
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err == nil && strings.HasSuffix(path, ".rego") && !info.IsDir() {
content, err := os.ReadFile(path)
if err != nil {
glog.Errorf("LoadRegoFiles, Failed to load: %s: %v", path, err)
} else {
modules[strings.Trim(filepath.Base(path), ".rego")] = string(content)
}
}
return nil
})
return modules
}
// LoadRegoModules loads the policies from variables
func LoadRegoModules() map[string]string {
modules := make(map[string]string)
modules["cautils"] = RegoCAUtils
modules["designators"] = RegoDesignators
modules["kubernetes.api.client"] = RegoKubernetesApiClient
return modules
}

View File

@@ -1 +0,0 @@
package resources

View File

@@ -4,12 +4,12 @@ import (
"path/filepath"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/cautils/opapolicy"
"github.com/armosec/opa-utils/reporthandling"
)
type ScanInfo struct {
Getters
PolicyIdentifier opapolicy.PolicyIdentifier
PolicyIdentifier reporthandling.PolicyIdentifier
UseExceptions string // Load exceptions configuration
UseFrom string // Load framework from local file (instead of download). Use when running offline
UseDefault bool // Load framework from cached file (instead of download). Use when running offline
@@ -19,10 +19,10 @@ type ScanInfo struct {
InputPatterns []string // Yaml files input patterns
Silent bool // Silent mode - Do not print progress logs
FailThreshold uint16 // Failure score threshold
DoNotSendResults bool // DEPRECATED
Submit bool // Submit results to Armo BE
Local bool // Do not submit results
Account string // account ID
FrameworkScan bool // false if scanning control
}
type Getters struct {
@@ -54,7 +54,6 @@ func (scanInfo *ScanInfo) setUseFrom() {
if scanInfo.UseDefault {
scanInfo.UseFrom = getter.GetDefaultPath(scanInfo.PolicyIdentifier.Name + ".json")
}
}
func (scanInfo *ScanInfo) setGetter() {
if scanInfo.UseFrom != "" {
@@ -84,8 +83,3 @@ func (scanInfo *ScanInfo) setOutputFile() {
func (scanInfo *ScanInfo) ScanRunningCluster() bool {
return len(scanInfo.InputPatterns) == 0
}
// func (scanInfo *ScanInfo) ConnectedToCluster(k8s k8sinterface.) bool {
// _, err := k8s.KubernetesClient.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{})
// return err == nil
// }

View File

@@ -4,9 +4,10 @@ import (
"fmt"
"strings"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/cautils/k8sinterface"
"github.com/armosec/kubescape/clihandler"
"github.com/spf13/cobra"
)
@@ -14,7 +15,7 @@ var getCmd = &cobra.Command{
Use: "get <key>",
Short: "Get configuration in cluster",
Long: ``,
ValidArgs: supportedFrameworks,
ValidArgs: clihandler.SupportedFrameworks,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 || len(args) > 1 {
return fmt.Errorf("requires one argument")

View File

@@ -4,9 +4,9 @@ import (
"fmt"
"strings"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/cautils/k8sinterface"
"github.com/spf13/cobra"
)

53
clihandler/cmd/control.go Normal file
View File

@@ -0,0 +1,53 @@
package cmd
import (
"fmt"
"os"
"strings"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/clihandler"
"github.com/armosec/opa-utils/reporthandling"
"github.com/spf13/cobra"
)
// controlCmd represents the control command
var controlCmd = &cobra.Command{
Use: "control <control name>/<control id>",
Short: fmt.Sprintf("The control you wish to use for scan. It must be present in at least one of the folloiwng frameworks: %s", clihandler.ValidFrameworks),
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 && !(cmd.Flags().Lookup("use-from").Changed) {
return fmt.Errorf("requires at least one argument")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
flagValidationControl()
scanInfo.PolicyIdentifier = reporthandling.PolicyIdentifier{}
if !(cmd.Flags().Lookup("use-from").Changed) {
scanInfo.PolicyIdentifier.Name = strings.ToLower(args[0])
}
scanInfo.FrameworkScan = false
scanInfo.PolicyIdentifier.Kind = reporthandling.KindControl
scanInfo.Init()
cautils.SetSilentMode(scanInfo.Silent)
err := clihandler.CliSetup(&scanInfo)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
return nil
},
}
func init() {
scanInfo = cautils.ScanInfo{}
scanCmd.AddCommand(controlCmd)
}
func flagValidationControl() {
if 100 < scanInfo.FailThreshold {
fmt.Println("bad argument: out of range threshold")
os.Exit(1)
}
}

View File

@@ -0,0 +1,67 @@
package cmd
import (
"fmt"
"strings"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/clihandler"
"github.com/spf13/cobra"
)
var downloadInfo cautils.DownloadInfo
var downloadCmd = &cobra.Command{
Use: fmt.Sprintf("download framework/control <framework-name>/<control-name> [flags]\nSupported frameworks: %s", clihandler.ValidFrameworks),
Short: "Download framework/control",
Long: ``,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 2 {
return fmt.Errorf("requires two arguments : framework/control <framework-name>/<control-name>")
}
if !strings.EqualFold(args[0], "framework") && !strings.EqualFold(args[0], "control") {
return fmt.Errorf("invalid parameter '%s'. Supported parameters: framework, control", args[0])
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
if strings.EqualFold(args[0], "framework") {
downloadInfo.FrameworkName = strings.ToLower(args[1])
g := getter.NewDownloadReleasedPolicy()
if downloadInfo.Path == "" {
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.FrameworkName + ".json")
}
frameworks, err := g.GetFramework(downloadInfo.FrameworkName)
if err != nil {
return err
}
err = getter.SaveFrameworkInFile(frameworks, downloadInfo.Path)
if err != nil {
return err
}
} else if strings.EqualFold(args[0], "control") {
downloadInfo.ControlName = strings.ToLower(args[1])
g := getter.NewDownloadReleasedPolicy()
if downloadInfo.Path == "" {
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.ControlName + ".json")
}
controls, err := g.GetControl(downloadInfo.ControlName)
if err != nil {
return err
}
err = getter.SaveControlInFile(controls, downloadInfo.Path)
if err != nil {
return err
}
}
return nil
},
}
func init() {
rootCmd.AddCommand(downloadCmd)
downloadInfo = cautils.DownloadInfo{}
downloadCmd.Flags().StringVarP(&downloadInfo.Path, "output", "o", "", "Output file. If specified, will store save to `~/.kubescape/<framework name>.json`")
}

View File

@@ -0,0 +1,90 @@
package cmd
import (
"fmt"
"io"
"os"
"strings"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/clihandler"
"github.com/armosec/opa-utils/reporthandling"
"github.com/spf13/cobra"
)
var frameworkCmd = &cobra.Command{
Use: fmt.Sprintf("framework <framework name> [`<glob pattern>`/`-`] [flags]\nSupported frameworks: %s", clihandler.ValidFrameworks),
Short: fmt.Sprintf("The framework you wish to use. Supported frameworks: %s", strings.Join(clihandler.SupportedFrameworks, ", ")),
Long: "Execute a scan on a running Kubernetes cluster or `yaml`/`json` files (use glob) or `-` for stdin",
ValidArgs: clihandler.SupportedFrameworks,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 && !(cmd.Flags().Lookup("use-from").Changed) {
return fmt.Errorf("requires at least one argument")
} else if len(args) > 0 {
if !isValidFramework(strings.ToLower(args[0])) {
return fmt.Errorf(fmt.Sprintf("supported frameworks: %s", strings.Join(clihandler.SupportedFrameworks, ", ")))
}
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
scanInfo.PolicyIdentifier = reporthandling.PolicyIdentifier{}
scanInfo.PolicyIdentifier.Kind = reporthandling.KindFramework
flagValidationFramework()
if !(cmd.Flags().Lookup("use-from").Changed) {
scanInfo.PolicyIdentifier.Name = strings.ToLower(args[0])
}
if len(args) > 0 {
if len(args[1:]) == 0 || args[1] != "-" {
scanInfo.InputPatterns = args[1:]
} else { // store stout to file
tempFile, err := os.CreateTemp(".", "tmp-kubescape*.yaml")
if err != nil {
return err
}
defer os.Remove(tempFile.Name())
if _, err := io.Copy(tempFile, os.Stdin); err != nil {
return err
}
scanInfo.InputPatterns = []string{tempFile.Name()}
}
}
scanInfo.Init()
cautils.SetSilentMode(scanInfo.Silent)
err := clihandler.CliSetup(&scanInfo)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
return nil
},
}
func isValidFramework(framework string) bool {
return cautils.StringInSlice(clihandler.SupportedFrameworks, framework) != cautils.ValueNotFound
}
func init() {
scanCmd.AddCommand(frameworkCmd)
scanInfo = cautils.ScanInfo{}
scanInfo.FrameworkScan = true
frameworkCmd.Flags().BoolVarP(&scanInfo.Submit, "submit", "", false, "Send the scan results to Armo management portal where 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 default the results are not submitted")
frameworkCmd.Flags().BoolVarP(&scanInfo.Local, "keep-local", "", false, "If you do not want your Kubescape results reported to Armo backend. Use this flag if you ran with the '--submit' flag in the past and you do not want to submit your current scan results")
frameworkCmd.Flags().StringVarP(&scanInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file")
}
func flagValidationFramework() {
if scanInfo.Submit && scanInfo.Local {
fmt.Println("You can use `keep-local` or `submit`, but not both")
os.Exit(1)
}
if 100 < scanInfo.FailThreshold {
fmt.Println("bad argument: out of range threshold")
os.Exit(1)
}
}

41
clihandler/cmd/scan.go Normal file
View File

@@ -0,0 +1,41 @@
package cmd
import (
"fmt"
"strings"
"github.com/armosec/kubescape/cautils"
"github.com/spf13/cobra"
)
var scanInfo cautils.ScanInfo
// scanCmd represents the scan command
var scanCmd = &cobra.Command{
Use: "scan <command>",
Short: "Scan the current running cluster or yaml files",
Long: `The action you want to perform`,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("requires one argument: framework/control")
}
if !strings.EqualFold(args[0], "framework") && !strings.EqualFold(args[0], "control") {
return fmt.Errorf("invalid parameter '%s'. Supported parameters: framework, control", args[0])
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
},
}
func init() {
rootCmd.AddCommand(scanCmd)
scanCmd.PersistentFlags().StringVarP(&scanInfo.ExcludedNamespaces, "exclude-namespaces", "e", "", "Namespaces to exclude from scanning. Recommended: kube-system, kube-public")
scanCmd.PersistentFlags().StringVarP(&scanInfo.Format, "format", "f", "pretty-printer", `Output format. Supported formats: "pretty-printer"/"json"/"junit"`)
scanCmd.PersistentFlags().StringVarP(&scanInfo.Output, "output", "o", "", "Output file. Print output to file and not stdout")
scanCmd.PersistentFlags().BoolVarP(&scanInfo.Silent, "silent", "s", false, "Silent progress messages")
scanCmd.PersistentFlags().Uint16VarP(&scanInfo.FailThreshold, "fail-threshold", "t", 0, "Failure threshold is the percent bellow which the command fails and returns exit code 1")
scanCmd.PersistentFlags().StringVar(&scanInfo.UseFrom, "use-from", "", "Load local framework object from specified path. If not used will download latest")
scanCmd.PersistentFlags().BoolVar(&scanInfo.UseDefault, "use-default", false, "Load local framework object from default path. If not used will download latest")
scanCmd.PersistentFlags().StringVar(&scanInfo.UseExceptions, "exceptions", "", "Path to an exceptions obj. If not set will download exceptions from Armo management portal")
}

View File

@@ -25,21 +25,21 @@ func GetLatestVersion() (string, error) {
latestVersion := "https://api.github.com/repos/armosec/kubescape/releases/latest"
resp, err := http.Get(latestVersion)
if err != nil {
return "", fmt.Errorf("failed to get latest releases from '%s', reason: %s", latestVersion, err.Error())
return "unknown", fmt.Errorf("failed to get latest releases from '%s', reason: %s", latestVersion, err.Error())
}
defer resp.Body.Close()
if resp.StatusCode < 200 || 301 < resp.StatusCode {
return "", fmt.Errorf("failed to download file, status code: %s", resp.Status)
return "unknown", fmt.Errorf("failed to download file, status code: %s", resp.Status)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("failed to read response body from '%s', reason: %s", latestVersion, err.Error())
return "unknown", fmt.Errorf("failed to read response body from '%s', reason: %s", latestVersion, err.Error())
}
var data map[string]interface{}
err = json.Unmarshal(body, &data)
if err != nil {
return "", fmt.Errorf("failed to unmarshal response body from '%s', reason: %s", latestVersion, err.Error())
return "unknown", fmt.Errorf("failed to unmarshal response body from '%s', reason: %s", latestVersion, err.Error())
}
return fmt.Sprintf("%v", data["tag_name"]), nil
}

153
clihandler/initcli.go Normal file
View File

@@ -0,0 +1,153 @@
package clihandler
import (
"fmt"
"os"
"strings"
"github.com/armosec/armoapi-go/armotypes"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/opaprocessor"
"github.com/armosec/kubescape/policyhandler"
"github.com/armosec/kubescape/resourcehandler"
"github.com/armosec/kubescape/resultshandling"
"github.com/armosec/kubescape/resultshandling/printer"
"github.com/armosec/kubescape/resultshandling/reporter"
"github.com/armosec/opa-utils/reporthandling"
)
type CLIHandler struct {
policyHandler *policyhandler.PolicyHandler
scanInfo *cautils.ScanInfo
}
var SupportedFrameworks = []string{"nsa", "mitre"}
var ValidFrameworks = strings.Join(SupportedFrameworks, ", ")
type componentInterfaces struct {
clusterConfig cautils.IClusterConfig
resourceHandler resourcehandler.IResourceHandler
report reporter.IReport
printerHandler printer.IPrinter
}
func getReporter(scanInfo *cautils.ScanInfo) reporter.IReport {
if !scanInfo.Submit {
return reporter.NewReportMock()
}
if !scanInfo.FrameworkScan {
return reporter.NewReportMock()
}
return reporter.NewReportEventReceiver()
}
func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
var resourceHandler resourcehandler.IResourceHandler
var clusterConfig cautils.IClusterConfig
var reportHandler reporter.IReport
if !scanInfo.ScanRunningCluster() {
k8sinterface.ConnectedToCluster = false
clusterConfig = cautils.NewEmptyConfig()
// load fom file
resourceHandler = resourcehandler.NewFileResourceHandler(scanInfo.InputPatterns)
// set mock report (do not send report)
reportHandler = reporter.NewReportMock()
} else {
k8s := k8sinterface.NewKubernetesApi()
resourceHandler = resourcehandler.NewK8sResourceHandler(k8s, scanInfo.ExcludedNamespaces)
clusterConfig = cautils.ClusterConfigSetup(scanInfo, k8s, getter.GetArmoAPIConnector())
// setup reporter
reportHandler = getReporter(scanInfo)
}
// setup printer
printerHandler := printer.GetPrinter(scanInfo.Format)
printerHandler.SetWriter(scanInfo.Output)
return componentInterfaces{
clusterConfig: clusterConfig,
resourceHandler: resourceHandler,
report: reportHandler,
printerHandler: printerHandler,
}
}
func CliSetup(scanInfo *cautils.ScanInfo) error {
interfaces := getInterfaces(scanInfo)
processNotification := make(chan *cautils.OPASessionObj)
reportResults := make(chan *cautils.OPASessionObj)
if err := interfaces.clusterConfig.SetConfig(scanInfo.Account); err != nil {
fmt.Println(err)
}
cautils.ClusterName = interfaces.clusterConfig.GetClusterName() // TODO - Deprecated
cautils.CustomerGUID = interfaces.clusterConfig.GetCustomerGUID() // TODO - Deprecated
interfaces.report.SetClusterName(interfaces.clusterConfig.GetClusterName())
interfaces.report.SetCustomerGUID(interfaces.clusterConfig.GetCustomerGUID())
// cli handler setup
go func() {
// policy handler setup
policyHandler := policyhandler.NewPolicyHandler(&processNotification, interfaces.resourceHandler)
cli := NewCLIHandler(policyHandler, scanInfo)
if err := cli.Scan(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}()
// processor setup - rego run
go func() {
opaprocessorObj := opaprocessor.NewOPAProcessorHandler(&processNotification, &reportResults)
opaprocessorObj.ProcessRulesListenner()
}()
resultsHandling := resultshandling.NewResultsHandler(&reportResults, interfaces.report, interfaces.printerHandler)
score := resultsHandling.HandleResults(scanInfo)
// print report url
interfaces.clusterConfig.GenerateURL()
adjustedFailThreshold := float32(scanInfo.FailThreshold) / 100
if score < adjustedFailThreshold {
return fmt.Errorf("Scan score is bellow threshold")
}
return nil
}
func NewCLIHandler(policyHandler *policyhandler.PolicyHandler, scanInfo *cautils.ScanInfo) *CLIHandler {
return &CLIHandler{
scanInfo: scanInfo,
policyHandler: policyHandler,
}
}
func (clihandler *CLIHandler) Scan() error {
cautils.ScanStartDisplay()
policyNotification := &reporthandling.PolicyNotification{
NotificationType: reporthandling.TypeExecPostureScan,
Rules: []reporthandling.PolicyIdentifier{
clihandler.scanInfo.PolicyIdentifier,
},
Designators: armotypes.PortalDesignator{},
}
switch policyNotification.NotificationType {
case reporthandling.TypeExecPostureScan:
if err := clihandler.policyHandler.HandleNotificationRequest(policyNotification, clihandler.scanInfo); err != nil {
return err
}
default:
return fmt.Errorf("notification type '%s' Unknown", policyNotification.NotificationType)
}
return nil
}

View File

@@ -1,45 +0,0 @@
package cmd
import (
"fmt"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/getter"
"github.com/spf13/cobra"
)
var downloadInfo cautils.DownloadInfo
var downloadCmd = &cobra.Command{
Use: fmt.Sprintf("download framework <framework-name> [flags]\nSupported frameworks: %s", validFrameworks),
Short: "Download framework controls",
Long: ``,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 2 {
return fmt.Errorf("requires two arguments : framework <framework-name>")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
downloadInfo.FrameworkName = args[1]
g := getter.NewDownloadReleasedPolicy()
if downloadInfo.Path == "" {
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.FrameworkName + ".json")
}
frameworks, err := g.GetFramework(downloadInfo.FrameworkName)
if err != nil {
return err
}
err = getter.SaveFrameworkInFile(frameworks, downloadInfo.Path)
if err != nil {
return err
}
return nil
},
}
func init() {
rootCmd.AddCommand(downloadCmd)
downloadInfo = cautils.DownloadInfo{}
downloadCmd.Flags().StringVarP(&downloadInfo.Path, "output", "o", "", "Output file. If specified, will store save to `~/.kubescape/<framework name>.json`")
}

View File

@@ -1,201 +0,0 @@
package cmd
import (
"fmt"
"io"
"os"
"strings"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/cautils/armotypes"
"github.com/armosec/kubescape/cautils/getter"
"github.com/armosec/kubescape/cautils/k8sinterface"
"github.com/armosec/kubescape/cautils/opapolicy"
"github.com/armosec/kubescape/opaprocessor"
"github.com/armosec/kubescape/policyhandler"
"github.com/armosec/kubescape/resultshandling"
"github.com/armosec/kubescape/resultshandling/printer"
"github.com/armosec/kubescape/resultshandling/reporter"
"github.com/spf13/cobra"
)
var scanInfo cautils.ScanInfo
var supportedFrameworks = []string{"nsa", "mitre"}
var validFrameworks = strings.Join(supportedFrameworks, ", ")
type CLIHandler struct {
policyHandler *policyhandler.PolicyHandler
scanInfo *cautils.ScanInfo
}
var frameworkCmd = &cobra.Command{
Use: fmt.Sprintf("framework <framework name> [`<glob pattern>`/`-`] [flags]\nSupported frameworks: %s", validFrameworks),
Short: fmt.Sprintf("The framework you wish to use. Supported frameworks: %s", strings.Join(supportedFrameworks, ", ")),
Long: "Execute a scan on a running Kubernetes cluster or `yaml`/`json` files (use glob) or `-` for stdin",
ValidArgs: supportedFrameworks,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 && !(cmd.Flags().Lookup("use-from").Changed) {
return fmt.Errorf("requires at least one argument")
} else if len(args) > 0 {
if !isValidFramework(strings.ToLower(args[0])) {
return fmt.Errorf(fmt.Sprintf("supported frameworks: %s", strings.Join(supportedFrameworks, ", ")))
}
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
scanInfo.PolicyIdentifier = opapolicy.PolicyIdentifier{}
scanInfo.PolicyIdentifier.Kind = opapolicy.KindFramework
if !(cmd.Flags().Lookup("use-from").Changed) {
scanInfo.PolicyIdentifier.Name = strings.ToLower(args[0])
}
if len(args) > 0 {
if len(args[1:]) == 0 || args[1] != "-" {
scanInfo.InputPatterns = args[1:]
} else { // store stout to file
tempFile, err := os.CreateTemp(".", "tmp-kubescape*.yaml")
if err != nil {
return err
}
defer os.Remove(tempFile.Name())
if _, err := io.Copy(tempFile, os.Stdin); err != nil {
return err
}
scanInfo.InputPatterns = []string{tempFile.Name()}
}
}
scanInfo.Init()
cautils.SetSilentMode(scanInfo.Silent)
err := CliSetup()
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
return nil
},
}
func isValidFramework(framework string) bool {
return cautils.StringInSlice(supportedFrameworks, framework) != cautils.ValueNotFound
}
func init() {
scanCmd.AddCommand(frameworkCmd)
scanInfo = cautils.ScanInfo{}
frameworkCmd.Flags().StringVar(&scanInfo.UseFrom, "use-from", "", "Load local framework object from specified path. If not used will download latest")
frameworkCmd.Flags().BoolVar(&scanInfo.UseDefault, "use-default", false, "Load local framework object from default path. If not used will download latest")
frameworkCmd.Flags().StringVar(&scanInfo.UseExceptions, "exceptions", "", "Path to an exceptions obj. If not set will download exceptions from Armo management portal")
frameworkCmd.Flags().StringVarP(&scanInfo.ExcludedNamespaces, "exclude-namespaces", "e", "", "Namespaces to exclude from scanning. Recommended: kube-system, kube-public")
frameworkCmd.Flags().StringVarP(&scanInfo.Format, "format", "f", "pretty-printer", `Output format. Supported formats: "pretty-printer"/"json"/"junit"`)
frameworkCmd.Flags().StringVarP(&scanInfo.Output, "output", "o", "", "Output file. Print output to file and not stdout")
frameworkCmd.Flags().BoolVarP(&scanInfo.Silent, "silent", "s", false, "Silent progress messages")
frameworkCmd.Flags().Uint16VarP(&scanInfo.FailThreshold, "fail-threshold", "t", 0, "Failure threshold is the percent bellow which the command fails and returns exit code 1")
frameworkCmd.Flags().BoolVarP(&scanInfo.DoNotSendResults, "results-locally", "", false, "Deprecated. Please use `--keep-local` instead")
frameworkCmd.Flags().BoolVarP(&scanInfo.Submit, "submit", "", false, "Send the scan results to Armo management portal where 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 default the results are not submitted")
frameworkCmd.Flags().BoolVarP(&scanInfo.Local, "keep-local", "", false, "If you do not want your Kubescape results reported to Armo backend. Use this flag if you ran with the `--submit` flag in the past and you do not want to submit your current scan results")
frameworkCmd.Flags().StringVarP(&scanInfo.Account, "account", "", "", "Armo portal account ID. Default will load account ID from configMap or config file")
}
func CliSetup() error {
flagValidation()
var k8s *k8sinterface.KubernetesApi
var clusterConfig cautils.IClusterConfig
if !scanInfo.ScanRunningCluster() {
k8sinterface.ConnectedToCluster = false
clusterConfig = cautils.NewEmptyConfig()
} else {
k8s = k8sinterface.NewKubernetesApi()
// setup cluster config
clusterConfig = cautils.ClusterConfigSetup(&scanInfo, k8s, getter.GetArmoAPIConnector())
}
processNotification := make(chan *cautils.OPASessionObj)
reportResults := make(chan *cautils.OPASessionObj)
// policy handler setup
policyHandler := policyhandler.NewPolicyHandler(&processNotification, k8s)
if err := clusterConfig.SetCustomerGUID(scanInfo.Account); err != nil {
fmt.Println(err)
}
cautils.CustomerGUID = clusterConfig.GetCustomerGUID()
cautils.ClusterName = k8sinterface.GetClusterName()
// cli handler setup
go func() {
cli := NewCLIHandler(policyHandler)
if err := cli.Scan(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}()
// processor setup - rego run
go func() {
opaprocessorObj := opaprocessor.NewOPAProcessorHandler(&processNotification, &reportResults)
opaprocessorObj.ProcessRulesListenner()
}()
resultsHandling := resultshandling.NewResultsHandler(&reportResults, reporter.NewReportEventReceiver(), printer.NewPrinter(scanInfo.Format, scanInfo.Output))
score := resultsHandling.HandleResults()
// print report url
clusterConfig.GenerateURL()
adjustedFailThreshold := float32(scanInfo.FailThreshold) / 100
if score < adjustedFailThreshold {
return fmt.Errorf("Scan score is bellow threshold")
}
return nil
}
func NewCLIHandler(policyHandler *policyhandler.PolicyHandler) *CLIHandler {
return &CLIHandler{
scanInfo: &scanInfo,
policyHandler: policyHandler,
}
}
func (clihandler *CLIHandler) Scan() error {
cautils.ScanStartDisplay()
policyNotification := &opapolicy.PolicyNotification{
NotificationType: opapolicy.TypeExecPostureScan,
Rules: []opapolicy.PolicyIdentifier{
clihandler.scanInfo.PolicyIdentifier,
},
Designators: armotypes.PortalDesignator{},
}
switch policyNotification.NotificationType {
case opapolicy.TypeExecPostureScan:
//
if err := clihandler.policyHandler.HandleNotificationRequest(policyNotification, clihandler.scanInfo); err != nil {
return err
}
default:
return fmt.Errorf("notification type '%s' Unknown", policyNotification.NotificationType)
}
return nil
}
func flagValidation() {
if scanInfo.DoNotSendResults {
fmt.Println("Deprecated. Please use `--keep-local` instead")
}
if scanInfo.Submit && scanInfo.Local {
fmt.Println("You can use `keep-local` or `submit`, but not both")
os.Exit(1)
}
if 100 < scanInfo.FailThreshold {
fmt.Println("bad argument: out of range threshold")
os.Exit(1)
}
}

View File

@@ -1,18 +0,0 @@
package cmd
import (
"github.com/spf13/cobra"
)
// scanCmd represents the scan command
var scanCmd = &cobra.Command{
Use: "scan",
Short: "Scan the current running cluster or yaml files",
Long: `The action you want to perform`,
Run: func(cmd *cobra.Command, args []string) {
},
}
func init() {
rootCmd.AddCommand(scanCmd)
}

View File

@@ -51,7 +51,7 @@ kubescape scan framework nsa --exclude-namespaces kube-system,kube-public --form
kubescape scan framework nsa --exclude-namespaces kube-system,kube-public --format junit --output results.xml
```
* Scan with exceptions, objects with exceptions will be presented as `warning` and not `fail` <img src="docs/new-feature.svg">
* Scan with exceptions, objects with exceptions will be presented as `warning` and not `fail`
```
kubescape scan framework nsa --exceptions examples/exceptions.json
```

View File

@@ -0,0 +1,123 @@
---
# ------------------- Kubescape Service Account ------------------- #
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app: kubescape
name: kubescape-discovery
namespace: kubescape
---
# ------------------- Kubescape Role & Role Binding ------------------- #
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: kubescape-discovery-role
namespace: kubescape
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["get", "list", "describe"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: kubescape-discovery-binding
namespace: kubescape
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: kubescape-discovery-role
subjects:
- kind: ServiceAccount
name: kubescape-discovery
---
# ------------------- Kubescape Cluster Role & Cluster Role Binding ------------------- #
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: kubescape-discovery-clusterroles
# "namespace" omitted since ClusterRoles are not namespaced
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["get", "list", "describe"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: kubescape-discovery-role-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: kubescape-discovery-clusterroles
subjects:
- kind: ServiceAccount
name: kubescape-discovery
namespace: kubescape
---
# ------------------- Kubescape User/Customer GUID ------------------- #
kind: ConfigMap
apiVersion: v1
metadata:
name: kubescape-configmap
labels:
app: kubescape
namespace: kubescape
data:
config.json: |
{
"customerGUID": <MyGUID>,
"clusterName": <MyK8sClusterName>
}
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: kubescape
labels:
app: kubescape
namespace: kubescape
spec:
# ┌────────────────── timezone (optional)
# | ┌───────────── minute (0 - 59)
# | │ ┌───────────── hour (0 - 23)
# | │ │ ┌───────────── day of the month (1 - 31)
# | │ │ │ ┌───────────── month (1 - 12)
# | │ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday;
# | │ │ │ │ │ 7 is also Sunday on some systems)
# | │ │ │ │ │
# | │ │ │ │ │
# CRON_TZ=UTC * * * * *
schedule: "0 0 1 * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: kubescape
image: quay.io/armosec/kubescape:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c"]
args:
- kubescape scan framework nsa --submit
volumeMounts:
- name: kubescape-config-volume
mountPath: /root/.kubescape/config.json
subPath: config.json
restartPolicy: OnFailure
serviceAccountName: kubescape-discovery
volumes:
- name: kubescape-config-volume
configMap:
name: kubescape-configmap
---

61
go.mod
View File

@@ -3,30 +3,24 @@ module github.com/armosec/kubescape
go 1.17
require (
github.com/aws/aws-sdk-go v1.40.30
github.com/armosec/armoapi-go v0.0.8
github.com/armosec/k8s-interface v0.0.8
github.com/armosec/opa-utils v0.0.18
github.com/armosec/utils-go v0.0.3
github.com/briandowns/spinner v1.16.0
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/docker/docker v20.10.8+incompatible
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/enescakir/emoji v1.0.0
github.com/fatih/color v1.12.0
github.com/francoispqt/gojay v1.2.13
github.com/gofrs/uuid v4.0.0+incompatible
github.com/fatih/color v1.13.0
github.com/gofrs/uuid v4.1.0+incompatible
github.com/golang/glog v1.0.0
github.com/mattn/go-isatty v0.0.13
github.com/mattn/go-isatty v0.0.14
github.com/olekukonko/tablewriter v0.0.5
github.com/open-policy-agent/opa v0.31.0
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/open-policy-agent/opa v0.33.1
github.com/satori/go.uuid v1.2.0
github.com/spf13/cobra v1.2.1
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.22.1
k8s.io/apimachinery v0.22.1
k8s.io/client-go v0.22.1
sigs.k8s.io/controller-runtime v0.9.6
k8s.io/api v0.22.2
k8s.io/apimachinery v0.22.2
k8s.io/client-go v0.22.2
)
require (
@@ -38,11 +32,16 @@ require (
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/bytecodealliance/wasmtime-go v0.28.0 // indirect
github.com/armosec/utils-k8s-go v0.0.1 // indirect
github.com/coreos/go-oidc v2.2.1+incompatible // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/evanphx/json-patch v4.11.0+incompatible // indirect
github.com/docker/docker v20.10.9+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-gota/gota v0.12.0 // indirect
github.com/go-logr/logr v0.4.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
@@ -52,33 +51,39 @@ require (
github.com/googleapis/gnostic v0.5.5 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.11 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 // indirect
github.com/pquerna/cachecontrol v0.1.0 // indirect
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.19.1 // indirect
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 // indirect
golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
golang.org/x/net v0.0.0-20210825183410-e898025ed96a // indirect
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 // indirect
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf // indirect
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d // indirect
golang.org/x/text v0.3.6 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
gonum.org/v1/gonum v0.9.1 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.26.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/square/go-jose.v2 v2.2.2 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
k8s.io/klog/v2 v2.9.0 // indirect
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect
k8s.io/utils v0.0.0-20210722164352-7f3ee0f31471 // indirect
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a // indirect
sigs.k8s.io/controller-runtime v0.10.2 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect
sigs.k8s.io/yaml v1.2.0 // indirect
)

295
go.sum
View File

@@ -44,21 +44,20 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw=
github.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM=
github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
@@ -72,6 +71,7 @@ github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -80,11 +80,27 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armosec/armoapi-go v0.0.2/go.mod h1:vIK17yoKbJRQyZXWWLe3AqfqCRITxW8qmSkApyq5xFs=
github.com/armosec/armoapi-go v0.0.7/go.mod h1:iaVVGyc23QGGzAdv4n+szGQg3Rbpixn9yQTU3qWRpaw=
github.com/armosec/armoapi-go v0.0.8 h1:JPa9rZynuE2RucamDh6dsy/sjCScmWDsyt1zagJFCDo=
github.com/armosec/armoapi-go v0.0.8/go.mod h1:iaVVGyc23QGGzAdv4n+szGQg3Rbpixn9yQTU3qWRpaw=
github.com/armosec/k8s-interface v0.0.5/go.mod h1:xxS+V5QT3gVQTwZyAMMDrYLWGrfKOpiJ7Jfhfa0w9sM=
github.com/armosec/k8s-interface v0.0.8 h1:Eo3Qen4yFXxzVem49FNeij2ckyzHSAJ0w6PZMaSEIm8=
github.com/armosec/k8s-interface v0.0.8/go.mod h1:xxS+V5QT3gVQTwZyAMMDrYLWGrfKOpiJ7Jfhfa0w9sM=
github.com/armosec/opa-utils v0.0.18 h1:1hL5v2KCD8yStuwzul+gq1zg9+RCV9N3kHoRepKnrg0=
github.com/armosec/opa-utils v0.0.18/go.mod h1:E0mFTVx+4BYAVvO2hxWnIniv/IZIogRCak8BkKd7KK4=
github.com/armosec/utils-go v0.0.2/go.mod h1:itWmRLzRdsnwjpEOomL0mBWGnVNNIxSjDAdyc+b0iUo=
github.com/armosec/utils-go v0.0.3 h1:uyQI676yRciQM0sSN9uPoqHkbspTxHO0kmzXhBeE/xU=
github.com/armosec/utils-go v0.0.3/go.mod h1:itWmRLzRdsnwjpEOomL0mBWGnVNNIxSjDAdyc+b0iUo=
github.com/armosec/utils-k8s-go v0.0.1 h1:Ay3y7fW+4+FjVc0+obOWm8YsnEvM31vPAVoKTyTAFRk=
github.com/armosec/utils-k8s-go v0.0.1/go.mod h1:qrU4pmY2iZsOb39Eltpm0sTTNM3E4pmeyWx4dgDUC2U=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws/aws-sdk-go v1.40.30 h1:Zq1YYIr002ymVe3QjLFgIaPefi/ia+tXs1mXNAn2RN8=
github.com/aws/aws-sdk-go v1.40.30/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/aws/aws-sdk-go v1.41.1/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -93,15 +109,21 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/briandowns/spinner v1.16.0 h1:DFmp6hEaIx2QXXuqSJmtfSBSAjRmpGiKG6ip2Wm/yOs=
github.com/briandowns/spinner v1.16.0/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX3FScO+3/ZPQ=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/bytecodealliance/wasmtime-go v0.28.0 h1:JTWP482wkmR79O9T0JiIAllPqmNW5oP0v56v/FwCpaQ=
github.com/bytecodealliance/wasmtime-go v0.28.0/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI=
github.com/bytecodealliance/wasmtime-go v0.30.0 h1:WfYpr4WdqInt8m5/HvYinf+HrSEAIhItKIcth+qb1h4=
github.com/bytecodealliance/wasmtime-go v0.30.0/go.mod h1:q320gUxqyI8yB+ZqRuaJOEnGkAnHh6WtJjMaT2CW4wI=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -109,37 +131,45 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk=
github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgraph-io/badger/v3 v3.2103.1 h1:zaX53IRg7ycxVlkd5pYdCeFp1FynD6qBGQoQql3R3Hk=
github.com/dgraph-io/badger/v3 v3.2103.1/go.mod h1:dULbq6ehJ5K0cGW/1TQ9iSfUk0gbSiToDWmWmTsJ53E=
github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docker/docker v20.10.8+incompatible h1:RVqD337BgQicVCzYrrlhLDWhq6OAD2PJDUg2LsEUvKM=
github.com/docker/docker v20.10.8+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.9+incompatible h1:JlsVnETOjM2RLQa0Cc1XCIspUdXW3Zenq9P54uXBm6k=
github.com/docker/docker v20.10.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
@@ -154,13 +184,15 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs=
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c=
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
@@ -169,18 +201,27 @@ github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHqu
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g=
github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks=
github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY=
github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gota/gota v0.12.0 h1:T5BDg1hTf5fZ/CO+T/N0E+DDqUhvoKBl+UVckgcAAQg=
github.com/go-gota/gota v0.12.0/go.mod h1:UT+NsWpZC/FhaOyWb9Hui0jXg0Iq8e/YugZHTbyW/34=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
@@ -190,33 +231,34 @@ github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/zapr v0.4.0 h1:uc1uML3hRYL9/ZZPdgHS/n8Nzo+eaYL/Efxkkamf7OM=
github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
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=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
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/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.1.0+incompatible h1:sIa2eCvUTwgjbqXrPLfNwUf9S3i3mpH1O1atV+iL/Wk=
github.com/gofrs/uuid v4.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@@ -245,9 +287,15 @@ 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.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=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/flatbuffers v1.12.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw=
github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -280,28 +328,24 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
@@ -318,7 +362,6 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
@@ -334,14 +377,13 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
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/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -350,9 +392,15 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.13.5 h1:9O69jUPDcsT9fEm74W92rZL9FQY7rCdaXVneq+yyzl4=
github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
@@ -361,28 +409,26 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.13 h1:qdl+GuBjcsKKDco5BsxPJlId98mSWNKqYA+Co0SC1yA=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@@ -399,7 +445,7 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -419,34 +465,34 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI
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/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=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.14.0 h1:ep6kpPVwmr/nTbklSx2nrLNSIO62DoYAhnPNIMhK8gI=
github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
github.com/open-policy-agent/opa v0.31.0 h1:sVxgdUZz426hpPfeIP+XGIwy8yfVkETerRojY3nQTc4=
github.com/open-policy-agent/opa v0.31.0/go.mod h1:aeLYiWaZe9ikcX67qLzmtRTOxj7psNYh6YGTbTW6V+s=
github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU=
github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
github.com/open-policy-agent/opa v0.33.1 h1:EJe00U5H82iMsemgxcNm9RFwjW8zPyRMvL+0upg8+Yo=
github.com/open-policy-agent/opa v0.33.1/go.mod h1:Zb+IdRe0s7M++Rv/KgyuB0qvxO3CUpQ+ZW5v+w/cRUo=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
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.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -455,8 +501,9 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 h1:0XM1XL/OFFJjXsYXlG30spTkV/E9+gmd5GD1w2HE8xM=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc=
github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
@@ -479,7 +526,6 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
@@ -489,6 +535,7 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
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=
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
@@ -525,31 +572,32 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -559,9 +607,9 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
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/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
@@ -569,6 +617,7 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b h1:vVRagRXf67ESqAb72hG2C/ZwI8NtJF2u2V76EsuOHGY=
github.com/yashtewari/glob-intersection v0.0.0-20180916065949-5c77d914dd0b/go.mod h1:HptNXiXVDcJjXe9SqMd0v2FsL9f8dz4GnXgltU6q/co=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -577,12 +626,14 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
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.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@@ -590,48 +641,75 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4=
go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM=
go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE=
go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE=
go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/automaxprocs v1.4.0/go.mod h1:/mTEdr7LvHhs0v7mjdxDreTz1OG5zdZGqgOnhWiR/+Q=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.18.1 h1:CSUJ2mjFszzEWt4CdKISEuChVIXGBn3lAPwkRGyVrc4=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/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-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=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@@ -653,7 +731,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -678,7 +755,6 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -698,17 +774,19 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
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-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw=
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -724,8 +802,8 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 h1:B333XXssMuKQeBwiNODx4TupZy7bf4sxFZnN2ZOcvUE=
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -746,6 +824,7 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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=
@@ -755,10 +834,8 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
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-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -786,6 +863,7 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -794,18 +872,20 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
@@ -827,10 +907,13 @@ golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@@ -840,12 +923,12 @@ golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBn
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-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=
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -885,12 +968,21 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
gonum.org/v1/gonum v0.9.1 h1:HCWmqqNoELL0RAQeKBXWtkp04mGk8koafcB4He6+uhc=
gonum.org/v1/gonum v0.9.1/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc=
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
@@ -951,6 +1043,7 @@ google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfG
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
@@ -963,7 +1056,6 @@ google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
@@ -995,6 +1087,7 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -1007,15 +1100,15 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
@@ -1024,8 +1117,9 @@ gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
@@ -1054,43 +1148,36 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.21.3/go.mod h1:hUgeYHUbBp23Ue4qdX9tR8/ANi/g3ehylAqDn9NWVOg=
k8s.io/api v0.22.1 h1:ISu3tD/jRhYfSW8jI/Q1e+lRxkR7w9UwQEZ7FgslrwY=
k8s.io/api v0.22.1/go.mod h1:bh13rkTp3F1XEaLGykbyRD2QaTTzPm0e/BMd8ptFONY=
k8s.io/apiextensions-apiserver v0.21.3/go.mod h1:kl6dap3Gd45+21Jnh6utCx8Z2xxLm8LGDkprcd+KbsE=
k8s.io/apimachinery v0.21.3/go.mod h1:H/IM+5vH9kZRNJ4l3x/fXP/5bOPJaVP/guptnZPeCFI=
k8s.io/apimachinery v0.22.1 h1:DTARnyzmdHMz7bFWFDDm22AM4pLWTQECMpRTFu2d2OM=
k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
k8s.io/apiserver v0.21.3/go.mod h1:eDPWlZG6/cCCMj/JBcEpDoK+I+6i3r9GsChYBHSbAzU=
k8s.io/client-go v0.21.3/go.mod h1:+VPhCgTsaFmGILxR/7E1N0S+ryO010QBeNCv5JwRGYU=
k8s.io/client-go v0.22.1 h1:jW0ZSHi8wW260FvcXHkIa0NLxFBQszTlhiAVsU5mopw=
k8s.io/client-go v0.22.1/go.mod h1:BquC5A4UOo4qVDUtoc04/+Nxp1MeHcVc1HJm1KmG8kk=
k8s.io/code-generator v0.21.3/go.mod h1:K3y0Bv9Cz2cOW2vXUrNZlFbflhuPvuadW6JdnN6gGKo=
k8s.io/component-base v0.21.3/go.mod h1:kkuhtfEHeZM6LkX0saqSK8PbdO7A0HigUngmhhrwfGQ=
k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw=
k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8=
k8s.io/apiextensions-apiserver v0.22.2/go.mod h1:2E0Ve/isxNl7tWLSUDgi6+cmwHi5fQRdwGVCxbC+KFA=
k8s.io/apimachinery v0.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk=
k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
k8s.io/apiserver v0.22.2/go.mod h1:vrpMmbyjWrgdyOvZTSpsusQq5iigKNWv9o9KlDAbBHI=
k8s.io/client-go v0.22.2 h1:DaSQgs02aCC1QcwUdkKZWOeaVsQjYvWv8ZazcZ6JcHc=
k8s.io/client-go v0.22.2/go.mod h1:sAlhrkVDf50ZHx6z4K0S40wISNTarf1r800F+RlCF6U=
k8s.io/code-generator v0.22.2/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o=
k8s.io/component-base v0.22.2/go.mod h1:5Br2QhI9OTe79p+TzPe9JKNQYvEKbq9rTJDWllunGug=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM=
k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210722164352-7f3ee0f31471 h1:DnzUXII7sVg1FJ/4JX6YDRJfLNAC7idRatPwe07suiI=
k8s.io/utils v0.0.0-20210722164352-7f3ee0f31471/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a h1:8dYfu/Fc9Gz2rNJKB9IQRGgQOh2clmRzNIPPY1xLY5g=
k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.19/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
sigs.k8s.io/controller-runtime v0.9.6 h1:EevVMlgUj4fC1NVM4+DB3iPkWkmGRNarA66neqv9Qew=
sigs.k8s.io/controller-runtime v0.9.6/go.mod h1:q6PpkM5vqQubEKUKOM6qr06oXGzOBcCby1DA9FbyZeA=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
sigs.k8s.io/controller-runtime v0.10.2 h1:jW8qiY+yMnnPx6O9hu63tgcwaKzd1yLYui+mpvClOOc=
sigs.k8s.io/controller-runtime v0.10.2/go.mod h1:CQp8eyUQZ/Q7PJvnIrB6/hgfTC1kBkGylwsLgOQi1WY=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=

View File

@@ -54,6 +54,6 @@ echo -e "\033[0m"
$KUBESCAPE_EXEC version
echo
echo -e "\033[35mUsage: $ $KUBESCAPE_EXEC scan framework nsa --exclude-namespaces kube-system,kube-public"
echo -e "\033[35mUsage: $ $KUBESCAPE_EXEC scan framework nsa"
echo -e "\033[0m"

View File

@@ -4,7 +4,7 @@ import (
"fmt"
"os"
"github.com/armosec/kubescape/cmd"
"github.com/armosec/kubescape/clihandler/cmd"
)
func main() {

View File

@@ -6,13 +6,12 @@ import (
"time"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/kubescape/scapepkg/exceptions"
"github.com/armosec/kubescape/scapepkg/score"
"github.com/armosec/opa-utils/exceptions"
"github.com/armosec/opa-utils/reporthandling"
"github.com/armosec/kubescape/cautils/k8sinterface"
"github.com/armosec/k8s-interface/k8sinterface"
"github.com/armosec/kubescape/cautils/opapolicy"
"github.com/armosec/kubescape/cautils/opapolicy/resources"
"github.com/armosec/opa-utils/resources"
"github.com/golang/glog"
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/rego"
@@ -42,7 +41,7 @@ func NewOPAProcessor(sessionObj *cautils.OPASessionObj) *OPAProcessor {
func NewOPAProcessorHandler(processedPolicy, reportResults *chan *cautils.OPASessionObj) *OPAProcessorHandler {
regoDependenciesData := resources.NewRegoDependenciesData(k8sinterface.GetK8sConfig())
regoDependenciesData := resources.NewRegoDependenciesData(k8sinterface.GetK8sConfig(), cautils.ClusterName)
store, err := regoDependenciesData.TOStorage()
if err != nil {
panic(err)
@@ -81,7 +80,7 @@ func (opap *OPAProcessor) Process() error {
// glog.Infof(fmt.Sprintf("Starting 'Process'. reportID: %s", opap.PostureReport.ReportID))
cautils.ProgressTextDisplay(fmt.Sprintf("Scanning cluster %s", cautils.ClusterName))
cautils.StartSpinner()
frameworkReports := []opapolicy.FrameworkReport{}
frameworkReports := []reporthandling.FrameworkReport{}
var errs error
for i := range opap.Frameworks {
frameworkReport, err := opap.processFramework(&opap.Frameworks[i])
@@ -100,13 +99,13 @@ func (opap *OPAProcessor) Process() error {
return errs
}
func (opap *OPAProcessor) processFramework(framework *opapolicy.Framework) (*opapolicy.FrameworkReport, error) {
func (opap *OPAProcessor) processFramework(framework *reporthandling.Framework) (*reporthandling.FrameworkReport, error) {
var errs error
frameworkReport := opapolicy.FrameworkReport{}
frameworkReport := reporthandling.FrameworkReport{}
frameworkReport.Name = framework.Name
controlReports := []opapolicy.ControlReport{}
controlReports := []reporthandling.ControlReport{}
for i := range framework.Controls {
controlReport, err := opap.processControl(&framework.Controls[i])
if err != nil {
@@ -120,10 +119,10 @@ func (opap *OPAProcessor) processFramework(framework *opapolicy.Framework) (*opa
return &frameworkReport, errs
}
func (opap *OPAProcessor) processControl(control *opapolicy.Control) (*opapolicy.ControlReport, error) {
func (opap *OPAProcessor) processControl(control *reporthandling.Control) (*reporthandling.ControlReport, error) {
var errs error
controlReport := opapolicy.ControlReport{}
controlReport := reporthandling.ControlReport{}
controlReport.PortalBase = control.PortalBase
controlReport.ControlID = control.ControlID
controlReport.Control_ID = control.Control_ID // TODO: delete when 'id' is deprecated
@@ -132,7 +131,7 @@ func (opap *OPAProcessor) processControl(control *opapolicy.Control) (*opapolicy
controlReport.Description = control.Description
controlReport.Remediation = control.Remediation
ruleReports := []opapolicy.RuleReport{}
ruleReports := []reporthandling.RuleReport{}
for i := range control.Rules {
ruleReport, err := opap.processRule(&control.Rules[i])
if err != nil {
@@ -149,7 +148,7 @@ func (opap *OPAProcessor) processControl(control *opapolicy.Control) (*opapolicy
return &controlReport, errs
}
func (opap *OPAProcessor) processRule(rule *opapolicy.PolicyRule) (*opapolicy.RuleReport, error) {
func (opap *OPAProcessor) processRule(rule *reporthandling.PolicyRule) (*reporthandling.RuleReport, error) {
if ruleWithArmoOpaDependency(rule.Attributes) {
return nil, nil
}
@@ -166,17 +165,17 @@ func (opap *OPAProcessor) processRule(rule *opapolicy.PolicyRule) (*opapolicy.Ru
return &ruleReport, err
}
func (opap *OPAProcessor) runOPAOnSingleRule(rule *opapolicy.PolicyRule, k8sObjects []map[string]interface{}) (opapolicy.RuleReport, error) {
func (opap *OPAProcessor) runOPAOnSingleRule(rule *reporthandling.PolicyRule, k8sObjects []map[string]interface{}) (reporthandling.RuleReport, error) {
switch rule.RuleLanguage {
case opapolicy.RegoLanguage, opapolicy.RegoLanguage2:
case reporthandling.RegoLanguage, reporthandling.RegoLanguage2:
return opap.runRegoOnK8s(rule, k8sObjects)
default:
return opapolicy.RuleReport{}, fmt.Errorf("rule: '%s', language '%v' not supported", rule.Name, rule.RuleLanguage)
return reporthandling.RuleReport{}, fmt.Errorf("rule: '%s', language '%v' not supported", rule.Name, rule.RuleLanguage)
}
}
func (opap *OPAProcessor) runRegoOnK8s(rule *opapolicy.PolicyRule, k8sObjects []map[string]interface{}) (opapolicy.RuleReport, error) {
func (opap *OPAProcessor) runRegoOnK8s(rule *reporthandling.PolicyRule, k8sObjects []map[string]interface{}) (reporthandling.RuleReport, error) {
var errs error
ruleReport := opapolicy.RuleReport{
ruleReport := reporthandling.RuleReport{
Name: rule.Name,
}
@@ -203,7 +202,7 @@ func (opap *OPAProcessor) runRegoOnK8s(rule *opapolicy.PolicyRule, k8sObjects []
return ruleReport, errs
}
func (opap *OPAProcessor) regoEval(inputObj []map[string]interface{}, compiledRego *ast.Compiler) ([]opapolicy.RuleResponse, error) {
func (opap *OPAProcessor) regoEval(inputObj []map[string]interface{}, compiledRego *ast.Compiler) ([]reporthandling.RuleResponse, error) {
rego := rego.New(
rego.Query("data.armo_builtins"), // get package name from rule
rego.Compiler(compiledRego),
@@ -216,7 +215,7 @@ func (opap *OPAProcessor) regoEval(inputObj []map[string]interface{}, compiledRe
if err != nil {
return nil, fmt.Errorf("in 'regoEval', failed to evaluate rule, reason: %s", err.Error())
}
results, err := parseRegoResult(&resultSet)
results, err := reporthandling.ParseRegoResult(&resultSet)
// results, err := ParseRegoResult(&resultSet)
if err != nil {
@@ -226,34 +225,27 @@ func (opap *OPAProcessor) regoEval(inputObj []map[string]interface{}, compiledRe
return results, nil
}
func (opap *OPAProcessor) updateScore() {
if !k8sinterface.ConnectedToCluster {
return
}
// calculate score
s := score.NewScore(k8sinterface.NewKubernetesApi(), ScoreConfigPath)
s.Calculate(opap.PostureReport.FrameworkReports)
}
func (opap *OPAProcessor) updateResults() {
for f, frameworkReport := range opap.PostureReport.FrameworkReports {
sumFailed := 0
sumTotal := 0
for c, controlReport := range opap.PostureReport.FrameworkReports[f].ControlReports {
for f := range opap.PostureReport.FrameworkReports {
// set exceptions
exceptions.SetFrameworkExceptions(&opap.PostureReport.FrameworkReports[f], opap.Exceptions, cautils.ClusterName)
// set counters
reporthandling.SetUniqueResourcesCounter(&opap.PostureReport.FrameworkReports[f])
// set default score
reporthandling.SetDefaultScore(&opap.PostureReport.FrameworkReports[f])
// edit results - remove data
// TODO - move function to pkg - use RemoveData
for c := range opap.PostureReport.FrameworkReports[f].ControlReports {
for r, ruleReport := range opap.PostureReport.FrameworkReports[f].ControlReports[c].RuleReports {
// editing the responses -> removing duplications, clearing secret data, etc.
opap.PostureReport.FrameworkReports[f].ControlReports[c].RuleReports[r].RuleResponses = editRuleResponses(ruleReport.RuleResponses)
// adding exceptions to the rules
ruleExceptions := exceptions.ListRuleExceptions(opap.Exceptions, frameworkReport.Name, controlReport.Name, ruleReport.Name)
exceptions.AddExceptionsToRuleResponses(opap.PostureReport.FrameworkReports[f].ControlReports[c].RuleReports[r].RuleResponses, ruleExceptions)
}
sumFailed += controlReport.GetNumberOfFailedResources()
sumTotal += controlReport.GetNumberOfResources()
opap.PostureReport.FrameworkReports[f].ControlReports[c].Score = float32(percentage(controlReport.GetNumberOfResources(), controlReport.GetNumberOfFailedResources()))
}
opap.PostureReport.FrameworkReports[f].Score = float32(percentage(sumTotal, sumTotal-sumFailed))
}
}

View File

@@ -4,11 +4,10 @@ import (
"testing"
"github.com/armosec/kubescape/cautils"
"github.com/armosec/opa-utils/reporthandling"
"github.com/armosec/kubescape/cautils/k8sinterface"
"github.com/armosec/k8s-interface/k8sinterface"
// _ "k8s.io/client-go/plugin/pkg/client/auth"
"github.com/armosec/kubescape/cautils/opapolicy"
)
func NewOPAProcessorMock() *OPAProcessor {
@@ -22,7 +21,7 @@ func TestProcess(t *testing.T) {
// set opaSessionObj
opaSessionObj := cautils.NewOPASessionObjMock()
opaSessionObj.Frameworks = []opapolicy.Framework{*opapolicy.MockFrameworkA()}
opaSessionObj.Frameworks = []reporthandling.Framework{*reporthandling.MockFrameworkA()}
opaSessionObj.K8SResources = &k8sResources
opap := NewOPAProcessor(opaSessionObj)

Some files were not shown because too many files have changed in this diff Show More