mirror of
https://github.com/kubescape/kubescape.git
synced 2026-02-18 20:09:59 +00:00
Compare commits
139 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1cfcc6d930 | ||
|
|
8c6f618743 | ||
|
|
6adf1c3162 | ||
|
|
9f5f4f1832 | ||
|
|
9f49cc83e9 | ||
|
|
67972199ce | ||
|
|
9d5db86bf3 | ||
|
|
39efed5fc1 | ||
|
|
21c2bf22dd | ||
|
|
6c94b3a423 | ||
|
|
683248db0b | ||
|
|
9032400528 | ||
|
|
1a925b1acf | ||
|
|
dadc8c2c60 | ||
|
|
01c1b44bfc | ||
|
|
a394a99d8f | ||
|
|
4213707b7f | ||
|
|
d53b8272ee | ||
|
|
fdd688ac68 | ||
|
|
5bb961bdc6 | ||
|
|
9e7cc06f97 | ||
|
|
1d184d9000 | ||
|
|
a5e2ebf647 | ||
|
|
29eb573de5 | ||
|
|
ec6c3da5ec | ||
|
|
e2d4f8961e | ||
|
|
a48c680201 | ||
|
|
c869f2c962 | ||
|
|
f77fc9a06d | ||
|
|
e12eae93b9 | ||
|
|
d92fb32574 | ||
|
|
541dba3d79 | ||
|
|
033ed17125 | ||
|
|
aaeb663d15 | ||
|
|
c337005985 | ||
|
|
192eeee348 | ||
|
|
27c97684b9 | ||
|
|
41d5fa70ed | ||
|
|
4206e9c175 | ||
|
|
8658bb05dd | ||
|
|
9b707016a9 | ||
|
|
4b02826883 | ||
|
|
b29774ea71 | ||
|
|
bf68e90a8e | ||
|
|
cc5cdcd831 | ||
|
|
07f23ff7d9 | ||
|
|
2985da6dc9 | ||
|
|
1523973749 | ||
|
|
ccafd78a14 | ||
|
|
4f71fe0d55 | ||
|
|
7bd6b6b4d1 | ||
|
|
2b976489a2 | ||
|
|
1440f20f95 | ||
|
|
941e7e27c0 | ||
|
|
5428c6ab2f | ||
|
|
851bb65d17 | ||
|
|
a3ce04b7e8 | ||
|
|
4d68ca6aa2 | ||
|
|
29c6767d3c | ||
|
|
d8f5f7975c | ||
|
|
4cd8476837 | ||
|
|
112257449f | ||
|
|
ff9cf4adf0 | ||
|
|
f2d387bc9c | ||
|
|
2ceb5150e2 | ||
|
|
00006ec721 | ||
|
|
00aa6948ab | ||
|
|
aad32ec965 | ||
|
|
bd24ed3af7 | ||
|
|
9fc455bcec | ||
|
|
0f8ba1e7e8 | ||
|
|
d3137af3d7 | ||
|
|
775dd037d6 | ||
|
|
49cbfe130c | ||
|
|
9cd61dd996 | ||
|
|
292c4aa060 | ||
|
|
56a265930d | ||
|
|
4171d110a4 | ||
|
|
2568241ef8 | ||
|
|
d0775565e9 | ||
|
|
3c6b2db919 | ||
|
|
ca4d4a096c | ||
|
|
ff27db6b83 | ||
|
|
c9ecb6c563 | ||
|
|
b9e5782264 | ||
|
|
d852f81cb0 | ||
|
|
6137aa5d8e | ||
|
|
131b67ee83 | ||
|
|
db6f00be08 | ||
|
|
2ecc80985a | ||
|
|
93dbfd5110 | ||
|
|
ae0c384c85 | ||
|
|
f60ff1fb26 | ||
|
|
08a81696a1 | ||
|
|
8375a8ae63 | ||
|
|
31d8cf5118 | ||
|
|
597b967e55 | ||
|
|
679238ec13 | ||
|
|
94884ac3d7 | ||
|
|
0ef8f20c50 | ||
|
|
82f3d62de5 | ||
|
|
46f1e6a83b | ||
|
|
77a9956d91 | ||
|
|
a1e639453d | ||
|
|
3f84ee3fcc | ||
|
|
38103ac90b | ||
|
|
13d27697e1 | ||
|
|
942f356d19 | ||
|
|
b87b687e2f | ||
|
|
2e313719bb | ||
|
|
0c5eb48fdb | ||
|
|
2ae2c81e0b | ||
|
|
222b154505 | ||
|
|
67c2de74f1 | ||
|
|
4a9b36807a | ||
|
|
c6241fab38 | ||
|
|
afbc69c6d2 | ||
|
|
2779cb4e25 | ||
|
|
f46ee93539 | ||
|
|
3eb087e5c1 | ||
|
|
59c935e723 | ||
|
|
bae45d277f | ||
|
|
0b6dfa9cd0 | ||
|
|
1ff3a6c92c | ||
|
|
f75cee0d78 | ||
|
|
229f16cb01 | ||
|
|
2c6b1a440f | ||
|
|
37afc1352f | ||
|
|
9943119033 | ||
|
|
41457ff551 | ||
|
|
82b64b5828 | ||
|
|
229e8acc74 | ||
|
|
30324e1c01 | ||
|
|
8ca356eae7 | ||
|
|
29f4ae368d | ||
|
|
409080f51b | ||
|
|
0b24c46279 | ||
|
|
49596c5ac1 | ||
|
|
9bf79db8f8 |
21
.github/workflows/build.yaml
vendored
21
.github/workflows/build.yaml
vendored
@@ -6,7 +6,6 @@ on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
types: [ closed ]
|
||||
|
||||
jobs:
|
||||
once:
|
||||
name: Create release
|
||||
@@ -38,9 +37,14 @@ jobs:
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
|
||||
- name: Build
|
||||
run: mkdir -p build/${{ matrix.os }} && go mod tidy && go build -ldflags "-w -s" -o build/${{ matrix.os }}/kubescape # && md5sum build/${{ matrix.os }}/kubescape > build/${{ matrix.os }}/kubescape.md5
|
||||
env:
|
||||
RELEASE: v1.0.${{ github.run_number }}
|
||||
ArmoBEServer: api.armo.cloud
|
||||
ArmoERServer: report.euprod1.cyberarmorsoft.com
|
||||
ArmoWebsite: portal.armo.cloud
|
||||
CGO_ENABLED: 0
|
||||
run: mkdir -p build/${{ matrix.os }} && go mod tidy && go build -ldflags "-w -s -X github.com/armosec/kubescape/cmd.BuildNumber=$RELEASE -X github.com/armosec/kubescape/cautils/getter.ArmoBEURL=$ArmoBEServer -X github.com/armosec/kubescape/cautils/getter.ArmoERURL=$ArmoERServer -X github.com/armosec/kubescape/cautils/getter.ArmoFEURL=$ArmoWebsite" -o build/${{ matrix.os }}/kubescape # && md5sum build/${{ matrix.os }}/kubescape > build/${{ matrix.os }}/kubescape.md5
|
||||
|
||||
- name: Upload Release binaries
|
||||
id: upload-release-asset
|
||||
@@ -52,14 +56,3 @@ jobs:
|
||||
asset_path: build/${{ matrix.os }}/kubescape
|
||||
asset_name: kubescape-${{ matrix.os }}
|
||||
asset_content_type: application/octet-stream
|
||||
|
||||
# - name: Upload Release md5 digest
|
||||
# id: upload-release-asset
|
||||
# uses: actions/upload-release-asset@v1
|
||||
# env:
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# with:
|
||||
# upload_url: ${{ needs.once.outputs.upload_url }}
|
||||
# asset_path: build/${{ matrix.os }}/kubescape.md5
|
||||
# asset_name: kubescape-${{ matrix.os }}
|
||||
# asset_content_type: application/octet-stream
|
||||
|
||||
36
.github/workflows/build_dev.yaml
vendored
Normal file
36
.github/workflows/build_dev.yaml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: build-dev
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ dev ]
|
||||
pull_request:
|
||||
branches: [ dev ]
|
||||
types: [ closed ]
|
||||
jobs:
|
||||
build:
|
||||
name: Create cross-platform dev build
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.16
|
||||
- name: Build
|
||||
env:
|
||||
RELEASE: v1.0.${{ github.run_number }}
|
||||
ArmoBEServer: api.armo.cloud
|
||||
ArmoERServer: report.euprod1.cyberarmorsoft.com
|
||||
ArmoWebsite: portal.armo.cloud
|
||||
CGO_ENABLED: 0
|
||||
run: mkdir -p build/${{ matrix.os }} && go mod tidy && go build -ldflags "-w -s -X github.com/armosec/kubescape/cmd.BuildNumber=$RELEASE -X github.com/armosec/kubescape/cautils/getter.ArmoBEURL=$ArmoBEServer -X github.com/armosec/kubescape/cautils/getter.ArmoERURL=$ArmoERServer -X github.com/armosec/kubescape/cautils/getter.ArmoFEURL=$ArmoWebsite" -o build/${{ matrix.os }}/kubescape # && md5sum build/${{ matrix.os }}/kubescape > build/${{ matrix.os }}/kubescape.md5
|
||||
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: kubescape-${{ matrix.os }}
|
||||
path: build/${{ matrix.os }}/kubescape
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
*.vs*
|
||||
*go.sum*
|
||||
*kubescape*
|
||||
*debug*
|
||||
*debug*
|
||||
.idea
|
||||
16
README.md
16
README.md
@@ -38,7 +38,8 @@ If you wish to scan all namespaces in your cluster, remove the `--exclude-namesp
|
||||
| `-o`/`--output` | print to stdout | Save scan result in file |
|
||||
| `--use-from` | | Load local framework object from specified path. If not used will download latest |
|
||||
| `--use-default` | `false` | Load local framework object from default path. If not used will download latest | `true`/`false` |
|
||||
| `--exceptions` | | Path to an [exceptions obj](examples/exceptions.json) |
|
||||
| `--exceptions` | | Path to an [exceptions obj](examples/exceptions.json). If not set will download exceptions from Armo management portal |
|
||||
| `--results-locally` | `false` | Kubescape sends scan results to Armo management portal to allow users to control exceptions and maintain chronological scan results. Use this flag if you do not wish to use these features | `true`/`false`|
|
||||
|
||||
## Usage & Examples
|
||||
|
||||
@@ -125,6 +126,18 @@ go mod tidy && go build -o kubescape .
|
||||
|
||||
4. Enjoy :zany_face:
|
||||
|
||||
# How to build in Docker
|
||||
|
||||
1. Clone Project
|
||||
```
|
||||
git clone git@github.com:armosec/kubescape.git kubescape && cd "$_"
|
||||
```
|
||||
|
||||
2. Build
|
||||
```
|
||||
docker build -t kubescape -f build/Dockerfile .
|
||||
```
|
||||
|
||||
# Under the hood
|
||||
|
||||
## Tests
|
||||
@@ -149,6 +162,7 @@ Kubescape is running the following tests according to what is defined by [Kubern
|
||||
* Ingress and Egress blocked
|
||||
* Container hostPort
|
||||
* Network policies
|
||||
* Symlink Exchange Can Allow Host Filesystem Access (CVE-2021-25741)
|
||||
|
||||
|
||||
|
||||
|
||||
13
build/Dockerfile
Normal file
13
build/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
||||
FROM golang:1.16-alpine as builder
|
||||
ENV GOPROXY=https://goproxy.io,direct
|
||||
ENV GO111MODULE=on
|
||||
|
||||
WORKDIR /work
|
||||
ADD . .
|
||||
RUN go mod tidy
|
||||
RUN GOOS=linux CGO_ENABLED=0 go build -ldflags="-s -w " -installsuffix cgo -o kubescape .
|
||||
|
||||
FROM alpine
|
||||
COPY --from=builder /work/kubescape /usr/bin/kubescape
|
||||
|
||||
CMD ["kubescape"]
|
||||
@@ -1,5 +1,7 @@
|
||||
package armotypes
|
||||
|
||||
import "strings"
|
||||
|
||||
const (
|
||||
CostumerGuidQuery = "costumerGUID"
|
||||
ClusterNameQuery = "cluster"
|
||||
@@ -42,6 +44,10 @@ const (
|
||||
DesignatorSid DesignatorType = "Sid" // secret id
|
||||
)
|
||||
|
||||
func (dt DesignatorType) ToLower() DesignatorType {
|
||||
return DesignatorType(strings.ToLower(string(dt)))
|
||||
}
|
||||
|
||||
// attributes
|
||||
const (
|
||||
AttributeCluster = "cluster"
|
||||
|
||||
@@ -33,10 +33,10 @@ func (designator *PortalDesignator) GetLabels() map[string]string {
|
||||
|
||||
// DigestPortalDesignator - get cluster namespace and labels from designator
|
||||
func (designator *PortalDesignator) DigestPortalDesignator() (string, string, string, string, map[string]string) {
|
||||
switch designator.DesignatorType {
|
||||
case DesignatorAttributes, DesignatorAttribute:
|
||||
switch designator.DesignatorType.ToLower() {
|
||||
case DesignatorAttributes.ToLower(), DesignatorAttribute.ToLower():
|
||||
return designator.DigestAttributesDesignator()
|
||||
case DesignatorWlid, DesignatorWildWlid:
|
||||
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:
|
||||
|
||||
325
cautils/customerloader.go
Normal file
325
cautils/customerloader.go
Normal file
@@ -0,0 +1,325 @@
|
||||
package cautils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/kubescape/cautils/getter"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/armosec/kubescape/cautils/k8sinterface"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
configMapName = "kubescape"
|
||||
ConfigFileName = "config"
|
||||
)
|
||||
|
||||
type ConfigObj struct {
|
||||
CustomerGUID string `json:"customerGUID"`
|
||||
Token string `json:"invitationParam"`
|
||||
CustomerAdminEMail string `json:"adminMail"`
|
||||
}
|
||||
|
||||
func (co *ConfigObj) Json() []byte {
|
||||
if b, err := json.Marshal(co); err == nil {
|
||||
return b
|
||||
}
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
type IClusterConfig interface {
|
||||
SetCustomerGUID() error
|
||||
GetCustomerGUID() string
|
||||
GenerateURL()
|
||||
}
|
||||
|
||||
type ClusterConfig struct {
|
||||
k8s *k8sinterface.KubernetesApi
|
||||
defaultNS string
|
||||
armoAPI *getter.ArmoAPI
|
||||
configObj *ConfigObj
|
||||
}
|
||||
|
||||
type EmptyConfig struct {
|
||||
}
|
||||
|
||||
func (c *EmptyConfig) GenerateURL() {
|
||||
}
|
||||
|
||||
func (c *EmptyConfig) SetCustomerGUID() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *EmptyConfig) GetCustomerGUID() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func NewEmptyConfig() *EmptyConfig {
|
||||
return &EmptyConfig{}
|
||||
}
|
||||
|
||||
func NewClusterConfig(k8s *k8sinterface.KubernetesApi, armoAPI *getter.ArmoAPI) *ClusterConfig {
|
||||
return &ClusterConfig{
|
||||
k8s: k8s,
|
||||
armoAPI: armoAPI,
|
||||
defaultNS: k8sinterface.GetDefaultNamespace(),
|
||||
}
|
||||
}
|
||||
func createConfigJson() {
|
||||
ioutil.WriteFile(getter.GetDefaultPath(ConfigFileName+".json"), nil, 0664)
|
||||
|
||||
}
|
||||
|
||||
func update(configObj *ConfigObj) {
|
||||
ioutil.WriteFile(getter.GetDefaultPath(ConfigFileName+".json"), configObj.Json(), 0664)
|
||||
}
|
||||
func (c *ClusterConfig) GenerateURL() {
|
||||
u := url.URL{}
|
||||
u.Scheme = "https"
|
||||
u.Host = getter.ArmoFEURL
|
||||
if c.configObj == nil {
|
||||
return
|
||||
}
|
||||
if c.configObj.CustomerAdminEMail != "" {
|
||||
msgStr := fmt.Sprintf("To view all controls and get remediations ask access permissions to %s from %s", u.String(), c.configObj.CustomerAdminEMail)
|
||||
InfoTextDisplay(os.Stdout, msgStr+"\n")
|
||||
return
|
||||
}
|
||||
u.Path = "account/sign-up"
|
||||
q := u.Query()
|
||||
q.Add("invitationToken", c.configObj.Token)
|
||||
q.Add("customerGUID", c.configObj.CustomerGUID)
|
||||
|
||||
u.RawQuery = q.Encode()
|
||||
fmt.Println("To view all controls and get remediations visit:")
|
||||
InfoTextDisplay(os.Stdout, u.String()+"\n")
|
||||
|
||||
}
|
||||
|
||||
func (c *ClusterConfig) GetCustomerGUID() string {
|
||||
if c.configObj != nil {
|
||||
return c.configObj.CustomerGUID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *ClusterConfig) GetValueByKeyFromConfigMap(key string) (string, error) {
|
||||
|
||||
configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Get(context.Background(), configMapName, metav1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if val, ok := configMap.Data[key]; ok {
|
||||
return val, nil
|
||||
} else {
|
||||
return "", fmt.Errorf("value does not exist")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func GetValueFromConfigJson(key string) (string, error) {
|
||||
data, err := ioutil.ReadFile(getter.GetDefaultPath(ConfigFileName + ".json"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var obj map[string]interface{}
|
||||
err = json.Unmarshal(data, &obj)
|
||||
if val, ok := obj[key]; ok {
|
||||
return fmt.Sprint(val), nil
|
||||
} else {
|
||||
return "", fmt.Errorf("value does not exist")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func SetKeyValueInConfigJson(key string, value string) error {
|
||||
data, err := ioutil.ReadFile(getter.GetDefaultPath(ConfigFileName + ".json"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var obj map[string]interface{}
|
||||
err = json.Unmarshal(data, &obj)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
obj[key] = value
|
||||
newData, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(getter.GetDefaultPath(ConfigFileName+".json"), newData, 0664)
|
||||
|
||||
}
|
||||
|
||||
func (c *ClusterConfig) SetKeyValueInConfigmap(key string, value string) error {
|
||||
|
||||
configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Get(context.Background(), configMapName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
configMap = &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: configMapName,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if len(configMap.Data) == 0 {
|
||||
configMap.Data = make(map[string]string)
|
||||
}
|
||||
|
||||
configMap.Data[key] = value
|
||||
|
||||
if err != nil {
|
||||
_, err = c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Create(context.Background(), configMap, metav1.CreateOptions{})
|
||||
} else {
|
||||
_, err = c.k8s.KubernetesClient.CoreV1().ConfigMaps(configMap.Namespace).Update(context.Background(), configMap, metav1.UpdateOptions{})
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *ClusterConfig) SetCustomerGUID() error {
|
||||
|
||||
// get from file
|
||||
if existsConfigJson() {
|
||||
c.configObj, _ = loadConfigFromFile()
|
||||
} else if c.existsConfigMap() {
|
||||
c.configObj, _ = c.loadConfigFromConfigMap()
|
||||
} else {
|
||||
c.createConfigMap()
|
||||
createConfigJson()
|
||||
}
|
||||
|
||||
customerGUID := c.GetCustomerGUID()
|
||||
// get from armoBE
|
||||
tenantResponse, err := c.armoAPI.GetCustomerGUID(customerGUID)
|
||||
|
||||
if err == nil && tenantResponse != nil {
|
||||
if tenantResponse.AdminMail != "" { // this customer already belongs to some user
|
||||
if existsConfigJson() {
|
||||
update(&ConfigObj{CustomerGUID: customerGUID, CustomerAdminEMail: tenantResponse.AdminMail})
|
||||
}
|
||||
if c.existsConfigMap() {
|
||||
c.configObj.CustomerAdminEMail = tenantResponse.AdminMail
|
||||
c.updateConfigMap()
|
||||
}
|
||||
} else {
|
||||
if existsConfigJson() {
|
||||
update(&ConfigObj{CustomerGUID: tenantResponse.TenantID, Token: tenantResponse.Token})
|
||||
}
|
||||
if c.existsConfigMap() {
|
||||
c.configObj = &ConfigObj{CustomerGUID: tenantResponse.TenantID, Token: tenantResponse.Token}
|
||||
c.updateConfigMap()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err != nil && strings.Contains(err.Error(), "Invitation for tenant already exists") {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ClusterConfig) loadConfigFromConfigMap() (*ConfigObj, error) {
|
||||
if c.k8s == nil {
|
||||
return nil, nil
|
||||
}
|
||||
configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Get(context.Background(), configMapName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if bData, err := json.Marshal(configMap.Data); err == nil {
|
||||
return readConfig(bData)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *ClusterConfig) existsConfigMap() bool {
|
||||
_, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Get(context.Background(), configMapName, metav1.GetOptions{})
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func existsConfigJson() bool {
|
||||
_, err := ioutil.ReadFile(getter.GetDefaultPath(ConfigFileName + ".json"))
|
||||
|
||||
return err == nil
|
||||
|
||||
}
|
||||
|
||||
func (c *ClusterConfig) createConfigMap() error {
|
||||
if c.k8s == nil {
|
||||
return nil
|
||||
}
|
||||
configMap := &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: configMapName,
|
||||
},
|
||||
}
|
||||
c.updateConfigData(configMap)
|
||||
|
||||
_, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Create(context.Background(), configMap, metav1.CreateOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *ClusterConfig) updateConfigMap() error {
|
||||
if c.k8s == nil {
|
||||
return nil
|
||||
}
|
||||
configMap, err := c.k8s.KubernetesClient.CoreV1().ConfigMaps(c.defaultNS).Get(context.Background(), configMapName, metav1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.updateConfigData(configMap)
|
||||
|
||||
_, err = c.k8s.KubernetesClient.CoreV1().ConfigMaps(configMap.Namespace).Update(context.Background(), configMap, metav1.UpdateOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *ClusterConfig) updateConfigData(configMap *corev1.ConfigMap) {
|
||||
if len(configMap.Data) == 0 {
|
||||
configMap.Data = make(map[string]string)
|
||||
}
|
||||
m := c.ToMapString()
|
||||
for k, v := range m {
|
||||
if s, ok := v.(string); ok {
|
||||
configMap.Data[k] = s
|
||||
}
|
||||
}
|
||||
}
|
||||
func loadConfigFromFile() (*ConfigObj, error) {
|
||||
dat, err := ioutil.ReadFile(getter.GetDefaultPath(ConfigFileName + ".json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return readConfig(dat)
|
||||
}
|
||||
func readConfig(dat []byte) (*ConfigObj, error) {
|
||||
|
||||
if len(dat) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
configObj := &ConfigObj{}
|
||||
err := json.Unmarshal(dat, configObj)
|
||||
|
||||
return configObj, err
|
||||
}
|
||||
func (c *ClusterConfig) ToMapString() map[string]interface{} {
|
||||
m := map[string]interface{}{}
|
||||
bc, _ := json.Marshal(c.configObj)
|
||||
json.Unmarshal(bc, &m)
|
||||
return m
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package getter
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/kubescape/cautils/armotypes"
|
||||
"github.com/armosec/kubescape/cautils/opapolicy"
|
||||
@@ -13,16 +12,23 @@ import (
|
||||
// =============================================== ArmoAPI ===============================================================
|
||||
// =======================================================================================================================
|
||||
|
||||
var (
|
||||
// ATTENTION!!!
|
||||
// Changes in this URLs variable names, or in the usage is affecting the build process! BE CAREFULL
|
||||
ArmoBEURL = "eggdashbe.eudev3.cyberarmorsoft.com"
|
||||
ArmoERURL = "report.eudev3.cyberarmorsoft.com"
|
||||
ArmoFEURL = "armoui.eudev3.cyberarmorsoft.com"
|
||||
// ArmoURL = "https://dashbe.euprod1.cyberarmorsoft.com"
|
||||
)
|
||||
|
||||
// Armo API for downloading policies
|
||||
type ArmoAPI struct {
|
||||
httpClient *http.Client
|
||||
baseURL string
|
||||
}
|
||||
|
||||
func NewArmoAPI() *ArmoAPI {
|
||||
return &ArmoAPI{
|
||||
httpClient: &http.Client{},
|
||||
baseURL: "https://dashbe.euprod1.cyberarmorsoft.com",
|
||||
}
|
||||
}
|
||||
func (armoAPI *ArmoAPI) GetFramework(name string) (*opapolicy.Framework, error) {
|
||||
@@ -35,20 +41,11 @@ func (armoAPI *ArmoAPI) GetFramework(name string) (*opapolicy.Framework, error)
|
||||
if err = JSONDecoder(respStr).Decode(framework); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
SaveFrameworkInFile(framework, GetDefaultPath(name))
|
||||
SaveFrameworkInFile(framework, GetDefaultPath(name+".json"))
|
||||
|
||||
return framework, err
|
||||
}
|
||||
|
||||
func (armoAPI *ArmoAPI) getFrameworkURL(frameworkName string) string {
|
||||
requestURI := "v1/armoFrameworks"
|
||||
requestURI += fmt.Sprintf("?customerGUID=%s", "11111111-1111-1111-1111-111111111111")
|
||||
requestURI += fmt.Sprintf("&frameworkName=%s", strings.ToUpper(frameworkName))
|
||||
requestURI += "&getRules=true"
|
||||
|
||||
return urlEncoder(fmt.Sprintf("%s/%s", armoAPI.baseURL, requestURI))
|
||||
}
|
||||
|
||||
func (armoAPI *ArmoAPI) GetExceptions(customerGUID, clusterName string) ([]armotypes.PostureExceptionPolicy, error) {
|
||||
exceptions := []armotypes.PostureExceptionPolicy{}
|
||||
if customerGUID == "" {
|
||||
@@ -66,11 +63,26 @@ func (armoAPI *ArmoAPI) GetExceptions(customerGUID, clusterName string) ([]armot
|
||||
return exceptions, nil
|
||||
}
|
||||
|
||||
func (armoAPI *ArmoAPI) getExceptionsURL(customerGUID, clusterName string) string {
|
||||
requestURI := "api/v1/armoPostureExceptions"
|
||||
requestURI += fmt.Sprintf("?customerGUID=%s", customerGUID)
|
||||
if clusterName != "" {
|
||||
requestURI += fmt.Sprintf("&clusterName=%s", clusterName)
|
||||
func (armoAPI *ArmoAPI) GetCustomerGUID(customerGUID string) (*TenantResponse, error) {
|
||||
url := armoAPI.getCustomerURL()
|
||||
if customerGUID != "" {
|
||||
url = fmt.Sprintf("%s?customerGUID=%s", url, customerGUID)
|
||||
}
|
||||
return urlEncoder(fmt.Sprintf("%s/%s", armoAPI.baseURL, requestURI))
|
||||
respStr, err := HttpGetter(armoAPI.httpClient, url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tenant := &TenantResponse{}
|
||||
if err = JSONDecoder(respStr).Decode(tenant); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tenant, nil
|
||||
}
|
||||
|
||||
type TenantResponse struct {
|
||||
TenantID string `json:"tenantId"`
|
||||
Token string `json:"token"`
|
||||
Expires string `json:"expires"`
|
||||
AdminMail string `json:"adminMail,omitempty"`
|
||||
}
|
||||
|
||||
44
cautils/getter/armoapiutils.go
Normal file
44
cautils/getter/armoapiutils.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package getter
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (armoAPI *ArmoAPI) getFrameworkURL(frameworkName string) string {
|
||||
u := url.URL{}
|
||||
u.Scheme = "https"
|
||||
u.Host = ArmoBEURL
|
||||
u.Path = "v1/armoFrameworks"
|
||||
q := u.Query()
|
||||
q.Add("customerGUID", "11111111-1111-1111-1111-111111111111")
|
||||
q.Add("frameworkName", strings.ToUpper(frameworkName))
|
||||
q.Add("getRules", "true")
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func (armoAPI *ArmoAPI) getExceptionsURL(customerGUID, clusterName string) string {
|
||||
u := url.URL{}
|
||||
u.Scheme = "https"
|
||||
u.Host = ArmoBEURL
|
||||
u.Path = "api/v1/armoPostureExceptions"
|
||||
|
||||
q := u.Query()
|
||||
q.Add("customerGUID", customerGUID)
|
||||
// if clusterName != "" { // TODO - fix customer name support in Armo BE
|
||||
// q.Add("clusterName", clusterName)
|
||||
// }
|
||||
u.RawQuery = q.Encode()
|
||||
|
||||
return u.String()
|
||||
}
|
||||
|
||||
func (armoAPI *ArmoAPI) getCustomerURL() string {
|
||||
u := url.URL{}
|
||||
u.Scheme = "https"
|
||||
u.Host = ArmoBEURL
|
||||
u.Path = "api/v1/createTenant"
|
||||
return u.String()
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/armosec/kubescape/cautils/armotypes"
|
||||
"github.com/armosec/kubescape/cautils/opapolicy"
|
||||
)
|
||||
|
||||
@@ -27,12 +26,10 @@ func NewDownloadReleasedPolicy() *DownloadReleasedPolicy {
|
||||
}
|
||||
}
|
||||
|
||||
func (drp *DownloadReleasedPolicy) GetExceptions(customerGUID, clusterName string) ([]armotypes.PostureExceptionPolicy, error) {
|
||||
return []armotypes.PostureExceptionPolicy{}, nil
|
||||
}
|
||||
|
||||
func (drp *DownloadReleasedPolicy) GetFramework(name string) (*opapolicy.Framework, error) {
|
||||
drp.setURL(name)
|
||||
if err := drp.setURL(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
respStr, err := HttpGetter(drp.httpClient, drp.hostURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -43,7 +40,7 @@ func (drp *DownloadReleasedPolicy) GetFramework(name string) (*opapolicy.Framewo
|
||||
return framework, err
|
||||
}
|
||||
|
||||
SaveFrameworkInFile(framework, GetDefaultPath(name))
|
||||
SaveFrameworkInFile(framework, GetDefaultPath(name+".json"))
|
||||
return framework, err
|
||||
}
|
||||
|
||||
@@ -76,12 +73,13 @@ func (drp *DownloadReleasedPolicy) setURL(frameworkName string) error {
|
||||
if name == frameworkName {
|
||||
if url, ok := asset["browser_download_url"].(string); ok {
|
||||
drp.hostURL = url
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return fmt.Errorf("failed to download '%s' - not found", frameworkName)
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
|
||||
type IPolicyGetter interface {
|
||||
GetFramework(name string) (*opapolicy.Framework, error)
|
||||
GetExceptions(customerGUID, clusterName string) ([]armotypes.PostureExceptionPolicy, error)
|
||||
// GetScores(scope, customerName, namespace string) ([]armotypes.PostureExceptionPolicy, error)
|
||||
}
|
||||
|
||||
type IExceptionsGetter interface {
|
||||
GetExceptions(customerGUID, clusterName string) ([]armotypes.PostureExceptionPolicy, error)
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ import (
|
||||
"github.com/armosec/kubescape/cautils/opapolicy"
|
||||
)
|
||||
|
||||
func GetDefaultPath(frameworkName string) string {
|
||||
defaultfilePath := filepath.Join(DefaultLocalStore, frameworkName+".json")
|
||||
func GetDefaultPath(name string) string {
|
||||
defaultfilePath := filepath.Join(DefaultLocalStore, name)
|
||||
if homeDir, err := os.UserHomeDir(); err == nil {
|
||||
defaultfilePath = filepath.Join(homeDir, defaultfilePath)
|
||||
}
|
||||
|
||||
@@ -9,12 +9,15 @@ import (
|
||||
"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
|
||||
|
||||
@@ -27,8 +30,15 @@ type KubernetesApi struct {
|
||||
|
||||
// NewKubernetesApi -
|
||||
func NewKubernetesApi() *KubernetesApi {
|
||||
var kubernetesClient *kubernetes.Clientset
|
||||
var err error
|
||||
|
||||
kubernetesClient, err := kubernetes.NewForConfig(GetK8sConfig())
|
||||
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)
|
||||
@@ -53,23 +63,54 @@ var RunningIncluster bool
|
||||
func LoadK8sConfig() error {
|
||||
kubeconfig, err := config.GetConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load kubernetes config: %s\n", strings.ReplaceAll(err.Error(), "KUBERNETES_MASTER", "KUBECONFIG"))
|
||||
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 K8SConfig == nil {
|
||||
if err := LoadK8sConfig(); err != nil {
|
||||
// print error
|
||||
fmt.Printf("%s", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
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 {
|
||||
clientCfg, err := clientcmd.NewDefaultClientConfigLoadingRules().Load()
|
||||
if err != nil {
|
||||
return "default"
|
||||
}
|
||||
namespace := clientCfg.Contexts[clientCfg.CurrentContext].Namespace
|
||||
if namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
return namespace
|
||||
}
|
||||
|
||||
@@ -41,11 +41,12 @@ type FrameworkReport struct {
|
||||
}
|
||||
type ControlReport struct {
|
||||
armotypes.PortalBase `json:",inline"`
|
||||
ControlID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
RuleReports []RuleReport `json:"ruleReports"`
|
||||
Remediation string `json:"remediation"`
|
||||
Description string `json:"description"`
|
||||
Score float32 `json:"score,omitempty"`
|
||||
Score float32 `json:"score"`
|
||||
BaseScore float32 `json:"baseScore,omitempty"`
|
||||
ARMOImprovement float32 `json:"ARMOImprovement,omitempty"`
|
||||
}
|
||||
@@ -100,6 +101,7 @@ type PolicyRule struct {
|
||||
// Control represents a collection of rules which are combined together to single purpose
|
||||
type Control struct {
|
||||
armotypes.PortalBase `json:",inline"`
|
||||
ControlID string `json:"id"`
|
||||
CreationTime string `json:"creationTime"`
|
||||
Description string `json:"description"`
|
||||
Remediation string `json:"remediation"`
|
||||
|
||||
@@ -33,7 +33,8 @@ func MockFrameworkReportA() *FrameworkReport {
|
||||
Name: AMockFrameworkName,
|
||||
ControlReports: []ControlReport{
|
||||
{
|
||||
Name: AMockControlName,
|
||||
ControlID: "C-0010",
|
||||
Name: AMockControlName,
|
||||
RuleReports: []RuleReport{
|
||||
{
|
||||
Name: AMockRuleName,
|
||||
|
||||
@@ -3,6 +3,8 @@ package opapolicy
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/armosec/kubescape/cautils/k8sinterface"
|
||||
)
|
||||
|
||||
func (pn *PolicyNotification) ToJSONBytesBuffer() (*bytes.Buffer, error) {
|
||||
@@ -83,11 +85,11 @@ func (controlReport *ControlReport) ListControlsInputKinds() []string {
|
||||
|
||||
func (controlReport *ControlReport) Passed() bool {
|
||||
for i := range controlReport.RuleReports {
|
||||
if len(controlReport.RuleReports[i].RuleResponses) == 0 {
|
||||
return true
|
||||
if len(controlReport.RuleReports[i].RuleResponses) != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
func (controlReport *ControlReport) Warning() bool {
|
||||
@@ -120,14 +122,50 @@ func (ruleReport *RuleReport) GetNumberOfResources() int {
|
||||
|
||||
func (ruleReport *RuleReport) GetNumberOfFailedResources() int {
|
||||
sum := 0
|
||||
for i := range ruleReport.RuleResponses {
|
||||
for i := len(ruleReport.RuleResponses) - 1; i >= 0; i-- {
|
||||
if ruleReport.RuleResponses[i].GetSingleResultStatus() == "failed" {
|
||||
sum += 1
|
||||
if !ruleReport.DeleteIfRedundantResponse(&ruleReport.RuleResponses[i], i) {
|
||||
sum++
|
||||
}
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
func (ruleReport *RuleReport) DeleteIfRedundantResponse(RuleResponse *RuleResponse, index int) bool {
|
||||
if b, rr := ruleReport.IsDuplicateResponseOfResource(RuleResponse, index); b {
|
||||
rr.AddMessageToResponse(RuleResponse.AlertMessage)
|
||||
ruleReport.RuleResponses = removeResponse(ruleReport.RuleResponses, index)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (ruleResponse *RuleResponse) AddMessageToResponse(message string) {
|
||||
ruleResponse.AlertMessage += message
|
||||
}
|
||||
|
||||
func (ruleReport *RuleReport) IsDuplicateResponseOfResource(RuleResponse *RuleResponse, index int) (bool, *RuleResponse) {
|
||||
for i := range ruleReport.RuleResponses {
|
||||
if i != index {
|
||||
for j := range ruleReport.RuleResponses[i].AlertObject.K8SApiObjects {
|
||||
for k := range RuleResponse.AlertObject.K8SApiObjects {
|
||||
w1 := k8sinterface.NewWorkloadObj(ruleReport.RuleResponses[i].AlertObject.K8SApiObjects[j])
|
||||
w2 := k8sinterface.NewWorkloadObj(RuleResponse.AlertObject.K8SApiObjects[k])
|
||||
if w1.GetName() == w2.GetName() && w1.GetNamespace() == w2.GetNamespace() && w1.GetKind() != "Role" && w1.GetKind() != "ClusterRole" {
|
||||
return true, &ruleReport.RuleResponses[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func removeResponse(slice []RuleResponse, index int) []RuleResponse {
|
||||
return append(slice[:index], slice[index+1:]...)
|
||||
}
|
||||
|
||||
func (ruleReport *RuleReport) GetNumberOfWarningResources() int {
|
||||
sum := 0
|
||||
for i := range ruleReport.RuleResponses {
|
||||
@@ -137,3 +175,62 @@ func (ruleReport *RuleReport) GetNumberOfWarningResources() int {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -19,10 +19,11 @@ type ScanInfo struct {
|
||||
InputPatterns []string
|
||||
Silent bool
|
||||
FailThreshold uint16
|
||||
DoNotSendResults bool
|
||||
}
|
||||
|
||||
type Getters struct {
|
||||
ExceptionsGetter getter.IPolicyGetter
|
||||
ExceptionsGetter getter.IExceptionsGetter
|
||||
PolicyGetter getter.IPolicyGetter
|
||||
}
|
||||
|
||||
@@ -48,7 +49,7 @@ func (scanInfo *ScanInfo) setUseFrom() {
|
||||
return
|
||||
}
|
||||
if scanInfo.UseDefault {
|
||||
scanInfo.UseFrom = getter.GetDefaultPath(scanInfo.PolicyIdentifier.Name)
|
||||
scanInfo.UseFrom = getter.GetDefaultPath(scanInfo.PolicyIdentifier.Name + ".json")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -66,12 +67,12 @@ func (scanInfo *ScanInfo) setOutputFile() {
|
||||
return
|
||||
}
|
||||
if scanInfo.Format == "json" {
|
||||
if filepath.Ext(scanInfo.Output) != "json" {
|
||||
if filepath.Ext(scanInfo.Output) != ".json" {
|
||||
scanInfo.Output += ".json"
|
||||
}
|
||||
}
|
||||
if scanInfo.Format == "junit" {
|
||||
if filepath.Ext(scanInfo.Output) != "xml" {
|
||||
if filepath.Ext(scanInfo.Output) != ".xml" {
|
||||
scanInfo.Output += ".xml"
|
||||
}
|
||||
}
|
||||
@@ -80,3 +81,8 @@ 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
|
||||
// }
|
||||
|
||||
18
cmd/cluster.go
Normal file
18
cmd/cluster.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// clusterCmd represents the cluster command
|
||||
var clusterCmd = &cobra.Command{
|
||||
Use: "cluster",
|
||||
Short: "Set configuration for cluster",
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
configCmd.AddCommand(clusterCmd)
|
||||
}
|
||||
50
cmd/cluster_get.go
Normal file
50
cmd/cluster_get.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/kubescape/cautils/getter"
|
||||
"github.com/armosec/kubescape/cautils/k8sinterface"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var getCmd = &cobra.Command{
|
||||
Use: "get <key>",
|
||||
Short: "Get configuration in cluster",
|
||||
Long: ``,
|
||||
ValidArgs: supportedFrameworks,
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 || len(args) > 1 {
|
||||
return fmt.Errorf("requires one argument")
|
||||
}
|
||||
|
||||
keyValue := strings.Split(args[0], "=")
|
||||
if len(keyValue) != 1 {
|
||||
return fmt.Errorf("requires one argument")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
keyValue := strings.Split(args[0], "=")
|
||||
key := keyValue[0]
|
||||
|
||||
k8s := k8sinterface.NewKubernetesApi()
|
||||
clusterConfig := cautils.NewClusterConfig(k8s, getter.NewArmoAPI())
|
||||
val, err := clusterConfig.GetValueByKeyFromConfigMap(key)
|
||||
if err != nil {
|
||||
if err.Error() == "value does not exist." {
|
||||
fmt.Printf("Could net get value from configmap, reason: %s\n", err)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
fmt.Println(key + "=" + val)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
clusterCmd.AddCommand(getCmd)
|
||||
}
|
||||
44
cmd/cluster_set.go
Normal file
44
cmd/cluster_set.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/kubescape/cautils/getter"
|
||||
"github.com/armosec/kubescape/cautils/k8sinterface"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var setCmd = &cobra.Command{
|
||||
Use: "set <key>=<value>",
|
||||
Short: "Set configuration in cluster",
|
||||
Long: ``,
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 || len(args) > 1 {
|
||||
return fmt.Errorf("requires one argument: <key>=<value>")
|
||||
}
|
||||
keyValue := strings.Split(args[0], "=")
|
||||
if len(keyValue) != 2 {
|
||||
return fmt.Errorf("requires one argument: <key>=<value>")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
keyValue := strings.Split(args[0], "=")
|
||||
key := keyValue[0]
|
||||
data := keyValue[1]
|
||||
|
||||
k8s := k8sinterface.NewKubernetesApi()
|
||||
clusterConfig := cautils.NewClusterConfig(k8s, getter.NewArmoAPI())
|
||||
if err := clusterConfig.SetKeyValueInConfigmap(key, data); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Value added successfully.")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
clusterCmd.AddCommand(setCmd)
|
||||
}
|
||||
19
cmd/config.go
Normal file
19
cmd/config.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// configCmd represents the config command
|
||||
var configCmd = &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Set configuration",
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(configCmd)
|
||||
}
|
||||
@@ -22,9 +22,9 @@ var downloadCmd = &cobra.Command{
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
downloadInfo.FrameworkName = args[1]
|
||||
g := getter.NewArmoAPI()
|
||||
g := getter.NewDownloadReleasedPolicy()
|
||||
if downloadInfo.Path == "" {
|
||||
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.FrameworkName)
|
||||
downloadInfo.Path = getter.GetDefaultPath(downloadInfo.FrameworkName + ".json")
|
||||
}
|
||||
frameworks, err := g.GetFramework(downloadInfo.FrameworkName)
|
||||
if err != nil {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"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"
|
||||
@@ -93,7 +94,7 @@ func init() {
|
||||
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, "Kubescape sends scan results to Armosec backend to allow users to control exceptions and maintain chronological scan results. Use this flag if you do not wish to use these features")
|
||||
}
|
||||
|
||||
func CliSetup() error {
|
||||
@@ -105,7 +106,9 @@ func CliSetup() error {
|
||||
}
|
||||
|
||||
var k8s *k8sinterface.KubernetesApi
|
||||
if scanInfo.ScanRunningCluster() {
|
||||
if !scanInfo.ScanRunningCluster() {
|
||||
k8sinterface.ConnectedToCluster = false
|
||||
} else {
|
||||
k8s = k8sinterface.NewKubernetesApi()
|
||||
}
|
||||
|
||||
@@ -115,12 +118,29 @@ func CliSetup() error {
|
||||
// policy handler setup
|
||||
policyHandler := policyhandler.NewPolicyHandler(&processNotification, k8s)
|
||||
|
||||
// cli handler setup
|
||||
cli := NewCLIHandler(policyHandler)
|
||||
if err := cli.Scan(); err != nil {
|
||||
panic(err)
|
||||
// load cluster config
|
||||
var clusterConfig cautils.IClusterConfig
|
||||
if !scanInfo.DoNotSendResults && k8sinterface.ConnectedToCluster {
|
||||
clusterConfig = cautils.NewClusterConfig(k8s, getter.NewArmoAPI())
|
||||
} else {
|
||||
clusterConfig = cautils.NewEmptyConfig()
|
||||
}
|
||||
|
||||
if err := clusterConfig.SetCustomerGUID(); 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)
|
||||
@@ -130,6 +150,9 @@ func CliSetup() error {
|
||||
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")
|
||||
@@ -156,12 +179,10 @@ func (clihandler *CLIHandler) Scan() error {
|
||||
}
|
||||
switch policyNotification.NotificationType {
|
||||
case opapolicy.TypeExecPostureScan:
|
||||
go func() {
|
||||
if err := clihandler.policyHandler.HandleNotificationRequest(policyNotification, clihandler.scanInfo); err != nil {
|
||||
fmt.Printf("%v\n", err)
|
||||
os.Exit(0)
|
||||
}
|
||||
}()
|
||||
//
|
||||
if err := clihandler.policyHandler.HandleNotificationRequest(policyNotification, clihandler.scanInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("notification type '%s' Unknown", policyNotification.NotificationType)
|
||||
}
|
||||
|
||||
17
cmd/local.go
Normal file
17
cmd/local.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var localCmd = &cobra.Command{
|
||||
Use: "local",
|
||||
Short: "Set configuration locally (for config.json)",
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
configCmd.AddCommand(localCmd)
|
||||
}
|
||||
46
cmd/local_get.go
Normal file
46
cmd/local_get.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/kubescape/cautils/getter"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var localGetCmd = &cobra.Command{
|
||||
Use: "get <key>",
|
||||
Short: "Get configuration locally",
|
||||
Long: ``,
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 || len(args) > 1 {
|
||||
return fmt.Errorf("requires one argument")
|
||||
}
|
||||
|
||||
keyValue := strings.Split(args[0], "=")
|
||||
if len(keyValue) != 1 {
|
||||
return fmt.Errorf("requires one argument")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
keyValue := strings.Split(args[0], "=")
|
||||
key := keyValue[0]
|
||||
|
||||
val, err := cautils.GetValueFromConfigJson(key)
|
||||
if err != nil {
|
||||
if err.Error() == "value does not exist." {
|
||||
fmt.Printf("Could net get value from: %s, reason: %s\n", getter.GetDefaultPath(cautils.ConfigFileName+".json"), err)
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
fmt.Println(key + "=" + val)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
localCmd.AddCommand(localGetCmd)
|
||||
}
|
||||
40
cmd/local_set.go
Normal file
40
cmd/local_set.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var localSetCmd = &cobra.Command{
|
||||
Use: "set <key>=<value>",
|
||||
Short: "Set configuration locally",
|
||||
Long: ``,
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 || len(args) > 1 {
|
||||
return fmt.Errorf("requires one argument: <key>=<value>")
|
||||
}
|
||||
keyValue := strings.Split(args[0], "=")
|
||||
if len(keyValue) != 2 {
|
||||
return fmt.Errorf("requires one argument: <key>=<value>")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
keyValue := strings.Split(args[0], "=")
|
||||
key := keyValue[0]
|
||||
data := keyValue[1]
|
||||
|
||||
if err := cautils.SetKeyValueInConfigJson(key, data); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Value added successfully.")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
localCmd.AddCommand(localSetCmd)
|
||||
}
|
||||
49
cmd/version.go
Normal file
49
cmd/version.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var BuildNumber string
|
||||
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Get current version",
|
||||
Long: ``,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
fmt.Println("Your current version is: " + BuildNumber)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
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 := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", 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 fmt.Sprintf("%v", data["tag_name"]), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
}
|
||||
42
install.sh
42
install.sh
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "Installing Kubescape..."
|
||||
echo -e "\033[0;36mInstalling Kubescape..."
|
||||
echo
|
||||
|
||||
BASE_DIR=~/.kubescape
|
||||
@@ -27,18 +27,38 @@ mkdir -p $BASE_DIR
|
||||
OUTPUT=$BASE_DIR/$KUBESCAPE_EXEC
|
||||
|
||||
curl --progress-bar -L $DOWNLOAD_URL -o $OUTPUT
|
||||
echo -e "\033[32m[V] Downloaded Kubescape"
|
||||
|
||||
# Ping download counter
|
||||
curl --silent https://us-central1-elated-pottery-310110.cloudfunctions.net/kubescape-download-counter -o /dev/null
|
||||
|
||||
chmod +x $OUTPUT || sudo chmod +x $OUTPUT
|
||||
rm -f /usr/local/bin/$KUBESCAPE_EXEC || sudo rm -f /usr/local/bin/$KUBESCAPE_EXEC
|
||||
cp $OUTPUT /usr/local/bin || sudo cp $OUTPUT /usr/local/bin
|
||||
# Checking if SUDO needed/exists
|
||||
SUDO=
|
||||
if [ "$(id -u)" -ne 0 ] && [ -n "$(which sudo)" ]; then
|
||||
SUDO=sudo
|
||||
fi
|
||||
|
||||
|
||||
# Find install dir
|
||||
install_dir=/usr/local/bin #default
|
||||
for pdir in ${PATH//:/ }; do
|
||||
edir="${pdir/#\~/$HOME}"
|
||||
if [[ $edir == $HOME/* ]]; then
|
||||
install_dir=$edir
|
||||
mkdir -p $install_dir 2>/dev/null || true
|
||||
SUDO=
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
chmod +x $OUTPUT 2>/dev/null
|
||||
$SUDO rm -f /usr/local/bin/$KUBESCAPE_EXEC 2>/dev/null || true # clearning up old install
|
||||
$SUDO cp $OUTPUT $install_dir/$KUBESCAPE_EXEC
|
||||
rm -rf $OUTPUT
|
||||
|
||||
echo -e "[V] Finished Installation"
|
||||
echo
|
||||
echo -e "\033[32mFinished Installation."
|
||||
|
||||
echo -e "\033[0m"
|
||||
$KUBESCAPE_EXEC version
|
||||
echo
|
||||
|
||||
echo -e "\033[35m Usage: $ $KUBESCAPE_EXEC scan framework nsa --exclude-namespaces kube-system,kube-public"
|
||||
echo
|
||||
echo -e "\033[35mUsage: $ $KUBESCAPE_EXEC scan framework nsa --exclude-namespaces kube-system,kube-public"
|
||||
|
||||
echo -e "\033[0m"
|
||||
|
||||
18
main.go
18
main.go
@@ -1,7 +1,23 @@
|
||||
package main
|
||||
|
||||
import "github.com/armosec/kubescape/cmd"
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/armosec/kubescape/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
CheckLatestVersion()
|
||||
cmd.Execute()
|
||||
}
|
||||
|
||||
func CheckLatestVersion() {
|
||||
latest, err := cmd.GetLatestVersion()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %v\n", err)
|
||||
} else if latest != cmd.BuildNumber {
|
||||
fmt.Println("Warning: You are not updated to the latest release: " + latest)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,11 +13,11 @@ import (
|
||||
|
||||
"github.com/armosec/kubescape/cautils/opapolicy"
|
||||
"github.com/armosec/kubescape/cautils/opapolicy/resources"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/open-policy-agent/opa/ast"
|
||||
"github.com/open-policy-agent/opa/rego"
|
||||
"github.com/open-policy-agent/opa/storage"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
const ScoreConfigPath = "/resources/config"
|
||||
@@ -42,7 +42,7 @@ func NewOPAProcessor(sessionObj *cautils.OPASessionObj) *OPAProcessor {
|
||||
|
||||
func NewOPAProcessorHandler(processedPolicy, reportResults *chan *cautils.OPASessionObj) *OPAProcessorHandler {
|
||||
|
||||
regoDependenciesData := resources.NewRegoDependenciesData(k8sinterface.K8SConfig)
|
||||
regoDependenciesData := resources.NewRegoDependenciesData(k8sinterface.GetK8sConfig())
|
||||
store, err := regoDependenciesData.TOStorage()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -70,7 +70,7 @@ func (opaHandler *OPAProcessorHandler) ProcessRulesListenner() {
|
||||
opap.updateResults()
|
||||
|
||||
// update score
|
||||
opap.updateScore()
|
||||
// opap.updateScore()
|
||||
|
||||
// report
|
||||
*opaHandler.reportResults <- opaSessionObj
|
||||
@@ -92,6 +92,7 @@ func (opap *OPAProcessor) Process() error {
|
||||
}
|
||||
|
||||
opap.PostureReport.FrameworkReports = frameworkReports
|
||||
opap.PostureReport.ReportID = uuid.NewV4().String()
|
||||
opap.PostureReport.ReportGenerationTime = time.Now().UTC()
|
||||
// glog.Infof(fmt.Sprintf("Done 'Process'. reportID: %s", opap.PostureReport.ReportID))
|
||||
cautils.StopSpinner()
|
||||
@@ -104,6 +105,7 @@ func (opap *OPAProcessor) processFramework(framework *opapolicy.Framework) (*opa
|
||||
|
||||
frameworkReport := opapolicy.FrameworkReport{}
|
||||
frameworkReport.Name = framework.Name
|
||||
|
||||
controlReports := []opapolicy.ControlReport{}
|
||||
for i := range framework.Controls {
|
||||
controlReport, err := opap.processControl(&framework.Controls[i])
|
||||
@@ -121,6 +123,7 @@ func (opap *OPAProcessor) processControl(control *opapolicy.Control) (*opapolicy
|
||||
|
||||
controlReport := opapolicy.ControlReport{}
|
||||
controlReport.PortalBase = control.PortalBase
|
||||
controlReport.ControlID = control.ControlID
|
||||
|
||||
controlReport.Name = control.Name
|
||||
controlReport.Description = control.Description
|
||||
@@ -219,6 +222,10 @@ func (opap *OPAProcessor) regoEval(inputObj []map[string]interface{}, compiledRe
|
||||
|
||||
func (opap *OPAProcessor) updateScore() {
|
||||
|
||||
if !k8sinterface.ConnectedToCluster {
|
||||
return
|
||||
}
|
||||
|
||||
// calculate score
|
||||
s := score.NewScore(k8sinterface.NewKubernetesApi(), ScoreConfigPath)
|
||||
s.Calculate(opap.PostureReport.FrameworkReports)
|
||||
@@ -226,6 +233,8 @@ func (opap *OPAProcessor) updateScore() {
|
||||
|
||||
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 r, ruleReport := range opap.PostureReport.FrameworkReports[f].ControlReports[c].RuleReports {
|
||||
// editing the responses -> removing duplications, clearing secret data, etc.
|
||||
@@ -235,6 +244,10 @@ func (opap *OPAProcessor) updateResults() {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,3 +139,13 @@ func listMatchKinds(match []opapolicy.RuleMatchObjects) []string {
|
||||
}
|
||||
return matchKinds
|
||||
}
|
||||
|
||||
func percentage(big, small int) int {
|
||||
if big == 0 {
|
||||
if small == 0 {
|
||||
return 100
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return int(float64(float64(big-small)/float64(big)) * 100)
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ func (policyHandler *PolicyHandler) getPolicies(notification *opapolicy.PolicyNo
|
||||
func (policyHandler *PolicyHandler) getResources(notification *opapolicy.PolicyNotification, opaSessionObj *cautils.OPASessionObj, scanInfo *cautils.ScanInfo) (*cautils.K8SResources, error) {
|
||||
var k8sResources *cautils.K8SResources
|
||||
var err error
|
||||
if scanInfo.ScanRunningCluster() {
|
||||
if k8sinterface.ConnectedToCluster {
|
||||
k8sResources, err = policyHandler.getK8sResources(opaSessionObj.Frameworks, ¬ification.Designators, scanInfo.ExcludedNamespaces)
|
||||
} else {
|
||||
k8sResources, err = policyHandler.loadResources(opaSessionObj.Frameworks, scanInfo)
|
||||
|
||||
@@ -3,6 +3,7 @@ package policyhandler
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/kubescape/cautils/armotypes"
|
||||
"github.com/armosec/kubescape/cautils/opapolicy"
|
||||
)
|
||||
@@ -18,7 +19,7 @@ func (policyHandler *PolicyHandler) GetPoliciesFromBackend(notification *opapoli
|
||||
case opapolicy.KindFramework:
|
||||
receivedFramework, recExceptionPolicies, err := policyHandler.getFrameworkPolicies(rule.Name)
|
||||
if err != nil {
|
||||
errs = fmt.Errorf("%v\nKind: %v, Name: %s, error: %s", errs, rule.Kind, rule.Name, err.Error())
|
||||
return nil, nil, fmt.Errorf("kind: %v, name: %s, error: %s", rule.Kind, rule.Name, err.Error())
|
||||
}
|
||||
if receivedFramework != nil {
|
||||
frameworks = append(frameworks, *receivedFramework)
|
||||
@@ -41,7 +42,7 @@ func (policyHandler *PolicyHandler) getFrameworkPolicies(policyName string) (*op
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
receivedException, err := policyHandler.getters.ExceptionsGetter.GetExceptions("", "")
|
||||
receivedException, err := policyHandler.getters.ExceptionsGetter.GetExceptions(cautils.CustomerGUID, cautils.ClusterName)
|
||||
if err != nil {
|
||||
return receivedFramework, nil, err
|
||||
}
|
||||
|
||||
@@ -178,6 +178,10 @@ func (printer *Printer) printResult(controlName string, controlSummary *ControlS
|
||||
|
||||
}
|
||||
|
||||
func (printer *Printer) PrintUrl(url string) {
|
||||
cautils.InfoTextDisplay(printer.writer, url)
|
||||
}
|
||||
|
||||
func generateRow(control string, cs ControlSummary) []string {
|
||||
row := []string{control}
|
||||
row = append(row, cs.ToSlice()...)
|
||||
@@ -247,7 +251,7 @@ func (printer *Printer) getSortedControlsNames() []string {
|
||||
}
|
||||
|
||||
func getWriter(outputFile string) *os.File {
|
||||
|
||||
os.Remove(outputFile)
|
||||
if outputFile != "" {
|
||||
f, err := os.OpenFile(outputFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
|
||||
@@ -28,6 +28,8 @@ func (report *ReportEventReceiver) ActionSendReportListenner(opaSessionObj *caut
|
||||
if cautils.CustomerGUID == "" {
|
||||
return
|
||||
}
|
||||
//Add score
|
||||
opaSessionObj.PostureReport.RemoveData()
|
||||
if err := report.Send(opaSessionObj.PostureReport); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/kubescape/cautils/getter"
|
||||
"github.com/gofrs/uuid"
|
||||
)
|
||||
|
||||
@@ -36,12 +37,12 @@ func initEventReceiverURL() *url.URL {
|
||||
urlObj := url.URL{}
|
||||
|
||||
urlObj.Scheme = "https"
|
||||
urlObj.Host = "report.euprod1.cyberarmorsoft.com"
|
||||
urlObj.Host = getter.ArmoERURL
|
||||
urlObj.Path = "/k8s/postureReport"
|
||||
|
||||
q := urlObj.Query()
|
||||
q.Add("customerGUID", uuid.FromStringOrNil(cautils.CustomerGUID).String())
|
||||
q.Add("clusterName", cautils.ClusterName)
|
||||
|
||||
urlObj.RawQuery = q.Encode()
|
||||
|
||||
return &urlObj
|
||||
@@ -49,9 +50,7 @@ func initEventReceiverURL() *url.URL {
|
||||
|
||||
func hostToString(host *url.URL, reportID string) string {
|
||||
q := host.Query()
|
||||
if reportID != "" {
|
||||
q.Add("reportID", reportID) // TODO - do we add the reportID?
|
||||
}
|
||||
q.Add("reportID", reportID) // TODO - do we add the reportID?
|
||||
host.RawQuery = q.Encode()
|
||||
return host.String()
|
||||
}
|
||||
|
||||
@@ -24,9 +24,9 @@ func (resultsHandler *ResultsHandler) HandleResults() float32 {
|
||||
|
||||
opaSessionObj := <-*resultsHandler.opaSessionObj
|
||||
|
||||
resultsHandler.reporterObj.ActionSendReportListenner(opaSessionObj)
|
||||
|
||||
score := resultsHandler.printerObj.ActionPrint(opaSessionObj)
|
||||
|
||||
resultsHandler.reporterObj.ActionSendReportListenner(opaSessionObj)
|
||||
|
||||
return score
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package exceptions
|
||||
|
||||
import (
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
"github.com/armosec/kubescape/cautils/k8sinterface"
|
||||
|
||||
"github.com/armosec/kubescape/cautils/armotypes"
|
||||
@@ -68,6 +69,10 @@ func alertObjectToWorkloads(obj *opapolicy.AlertObject) []k8sinterface.IWorkload
|
||||
continue
|
||||
}
|
||||
resource = append(resource, r)
|
||||
ns := r.GetNamespace()
|
||||
if ns != "" {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return resource
|
||||
@@ -91,9 +96,9 @@ func hasException(designator *armotypes.PortalDesignator, workload k8sinterface.
|
||||
return false // if designators are empty
|
||||
}
|
||||
|
||||
// if cluster != "" && cluster != ClusterName { // TODO - where do we receive cluster name from?
|
||||
// return false // cluster name does not match
|
||||
// }
|
||||
if cluster != "" && cautils.ClusterName != "" && cluster != cautils.ClusterName { // TODO - where do we receive cluster name from?
|
||||
return false // cluster name does not match
|
||||
}
|
||||
|
||||
if namespace != "" && !compareNamespace(workload, namespace) {
|
||||
return false // namespaces do not match
|
||||
|
||||
Reference in New Issue
Block a user