mirror of
https://github.com/kubescape/kubescape.git
synced 2026-04-15 06:58:11 +00:00
support vuln scan
This commit is contained in:
@@ -63,7 +63,12 @@ func getInterfaces(scanInfo *cautils.ScanInfo) componentInterfaces {
|
||||
scanInfo.ExcludedNamespaces = fmt.Sprintf("%s,%s", scanInfo.ExcludedNamespaces, hostSensorHandler.GetNamespace())
|
||||
}
|
||||
|
||||
resourceHandler := getResourceHandler(scanInfo, tenantConfig, k8s, hostSensorHandler)
|
||||
registryAdaptors, err := resourcehandler.NewRegistryAdaptors()
|
||||
if err != nil {
|
||||
// display warning
|
||||
}
|
||||
|
||||
resourceHandler := getResourceHandler(scanInfo, tenantConfig, k8s, hostSensorHandler, registryAdaptors)
|
||||
|
||||
// reporting behavior - setup reporter
|
||||
reportHandler := getReporter(tenantConfig, scanInfo.Submit)
|
||||
|
||||
@@ -55,12 +55,13 @@ func getReporter(tenantConfig cautils.ITenantConfig, submit bool) reporter.IRepo
|
||||
return reporterv1.NewReportMock()
|
||||
}
|
||||
|
||||
func getResourceHandler(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, hostSensorHandler hostsensorutils.IHostSensor) resourcehandler.IResourceHandler {
|
||||
func getResourceHandler(scanInfo *cautils.ScanInfo, tenantConfig cautils.ITenantConfig, k8s *k8sinterface.KubernetesApi, hostSensorHandler hostsensorutils.IHostSensor, registryAdaptors *resourcehandler.RegistryAdaptors) resourcehandler.IResourceHandler {
|
||||
if len(scanInfo.InputPatterns) > 0 || k8s == nil {
|
||||
return resourcehandler.NewFileResourceHandler(scanInfo.InputPatterns)
|
||||
return resourcehandler.NewFileResourceHandler(scanInfo.InputPatterns, registryAdaptors)
|
||||
}
|
||||
getter.GetArmoAPIConnector()
|
||||
rbacObjects := getRBACHandler(tenantConfig, k8s, scanInfo.Submit)
|
||||
return resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo), hostSensorHandler, rbacObjects)
|
||||
return resourcehandler.NewK8sResourceHandler(k8s, getFieldSelector(scanInfo), hostSensorHandler, rbacObjects, registryAdaptors)
|
||||
}
|
||||
|
||||
func getHostSensorHandler(scanInfo *cautils.ScanInfo, k8s *k8sinterface.KubernetesApi) hostsensorutils.IHostSensor {
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
# Container image vulnerability adaptor interface
|
||||
|
||||
## High level design of Kubescape
|
||||
|
||||
### Layers
|
||||
|
||||
* Controls and Rules: that actual control logic implementation, the "tests" themselves. Implemented in rego
|
||||
* OPA engine: the [OPA](https://github.com/open-policy-agent/opa) rego interpreter
|
||||
* Rules processor: Kubescape component, it enumerates and runs the controls while also preparing the all the input data that the controls need for running
|
||||
* Data sources: set of different modules providing data to the Rules processor so it can run the controls with them. Examples: Kubernetes objects, cloud vendor API objects and adding in this proposal the vulnerability infomration
|
||||
* Cloud Image Vulnerability adaption interface: the subject of this proposal, it gives a common interface for different registry/vulnerabilty vendors to adapt to.
|
||||
* CIV adaptors: specific implementation of the CIV interface, example Harbor adaption
|
||||
```
|
||||
-----------------------
|
||||
| Controls/Rules (rego) |
|
||||
-----------------------
|
||||
|
|
||||
-----------------------
|
||||
| OPA engine |
|
||||
-----------------------
|
||||
|
|
||||
-----------------------
|
||||
| Rules processor |
|
||||
-----------------------
|
||||
|
|
||||
-----------------------
|
||||
| Data sources |
|
||||
-----------------------
|
||||
|
|
||||
=======================
|
||||
| CIV adaption interface| <- Adding this layer in this proposal
|
||||
=======================
|
||||
|
|
||||
-----------------------
|
||||
| Specific CIV adaptors | <- Will be implemented based on this proposal
|
||||
-----------------------
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Functionalities to cover
|
||||
|
||||
The interface needs to cover the following functionalities:
|
||||
|
||||
* Authentication against the information source (abstracted login)
|
||||
* Triggering image scan (if applicable, the source might store vulnerabilities for images but cannot scan alone)
|
||||
* Reading image scan status (with last scan date and etc.)
|
||||
* Getting vulnerability information for a given image
|
||||
* Getting image information
|
||||
* Image manifests
|
||||
* Image BOMs (bill of material)
|
||||
|
||||
## Go API proposal
|
||||
|
||||
```
|
||||
|
||||
/*type ContainerImageRegistryCredentials struct {
|
||||
Password string
|
||||
Tag string
|
||||
Hash string
|
||||
}*/
|
||||
|
||||
type ContainerImageIdentifier struct {
|
||||
Registry string
|
||||
Repository string
|
||||
Tag string
|
||||
Hash string
|
||||
}
|
||||
|
||||
type ContainerImageScanStatus struct {
|
||||
ImageID ContainerImageIdentifier
|
||||
IsScanAvailable bool
|
||||
IsBomAvailable bool
|
||||
LastScanDate time.Time
|
||||
}
|
||||
|
||||
type ContainerImageVulnerabilityReport struct {
|
||||
ImageID ContainerImageIdentifier
|
||||
// TBD
|
||||
}
|
||||
|
||||
type ContainerImageInformation struct {
|
||||
ImageID ContainerImageIdentifier
|
||||
Bom []string
|
||||
ImageManifest Manifest // will use here Docker package definition
|
||||
}
|
||||
|
||||
type IContainerImageVulnerabilityAdaptor interface {
|
||||
// Credentials are coming from user input (CLI or configuration file) and they are abstracted at string to string map level
|
||||
// so and example use would be like registry: "simpledockerregistry:80" and credentials like {"username":"joedoe","password":"abcd1234"}
|
||||
Login(registry string, credentials map[string]string) error
|
||||
|
||||
// For "help" purposes
|
||||
DescribeAdaptor() string
|
||||
|
||||
GetImagesScanStatus(imageIDs []ContainerImageIdentifier) ([]ContainerImageScanStatus, error)
|
||||
|
||||
GetImagesVulnerabilties(imageIDs []ContainerImageIdentifier) ([]ContainerImageVulnerabilityReport, error)
|
||||
|
||||
GetImagesInformation(imageIDs []ContainerImageIdentifier) ([]ContainerImageInformation, error)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
# Integration
|
||||
|
||||
# Input
|
||||
|
||||
The objects received from the interface will be converted to an IMetadata compatible objects as following
|
||||
|
||||
```
|
||||
{
|
||||
"apiVersion": "image.vulnscan.com/v1",
|
||||
"kind": "ImageVulnerabilities",
|
||||
"metadata": {
|
||||
"name": "nginx:latest"
|
||||
},
|
||||
"data": {
|
||||
// list of vulnerabilities
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
# Output
|
||||
|
||||
The rego results will be a combination of the k8s artifact and the list of relevant CVEs for the control
|
||||
|
||||
```
|
||||
{
|
||||
"apiVersion": "result.vulnscan.com/v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"name": "nginx"
|
||||
"namespace": "default"
|
||||
|
||||
},
|
||||
"relatedObjects": [
|
||||
{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Pod",
|
||||
"metadata": {
|
||||
"name": "nginx"
|
||||
"namespace": "default"
|
||||
},
|
||||
"spec": {
|
||||
// podSpec
|
||||
},
|
||||
},
|
||||
{
|
||||
"apiVersion": "image.vulnscan.com/v1",
|
||||
"kind": "ImageVulnerabilities",
|
||||
"metadata": {
|
||||
"name": "nginx:latest",
|
||||
},
|
||||
"data": {
|
||||
// list of vulnerabilities
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -12,20 +12,20 @@ import (
|
||||
)
|
||||
|
||||
func NewArmoAdaptor(registry string, credentials map[string]string) (*ArmoCivAdaptor, error) {
|
||||
var accountId string
|
||||
var accountID string
|
||||
var accessKey string
|
||||
var clientId string
|
||||
var clientID string
|
||||
var ok bool
|
||||
if accountId, ok = credentials["accountId"]; !ok {
|
||||
return nil, fmt.Errorf("define accountId in credentials")
|
||||
if accountID, ok = credentials["accountID"]; !ok {
|
||||
return nil, fmt.Errorf("define accountID in credentials")
|
||||
}
|
||||
if clientId, ok = credentials["clientId"]; !ok {
|
||||
return nil, fmt.Errorf("define clientId in credentials")
|
||||
if clientID, ok = credentials["clientID"]; !ok {
|
||||
return nil, fmt.Errorf("define clientID in credentials")
|
||||
}
|
||||
if accessKey, ok = credentials["accessKey"]; !ok {
|
||||
return nil, fmt.Errorf("define accessKey in credentials")
|
||||
}
|
||||
armoCivAdaptor := ArmoCivAdaptor{registry: registry, accountId: accountId, clientId: clientId, accessKey: accessKey}
|
||||
armoCivAdaptor := ArmoCivAdaptor{registry: registry, clientID: clientID, accountID: accountID, accessKey: accessKey}
|
||||
err := armoCivAdaptor.initializeUrls()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -34,7 +34,7 @@ func NewArmoAdaptor(registry string, credentials map[string]string) (*ArmoCivAda
|
||||
}
|
||||
|
||||
func (armoCivAdaptor *ArmoCivAdaptor) Login() error {
|
||||
feLoginData := FeLoginData{ClientId: armoCivAdaptor.clientId, Secret: armoCivAdaptor.accessKey}
|
||||
feLoginData := FeLoginData{ClientId: armoCivAdaptor.clientID, Secret: armoCivAdaptor.accessKey}
|
||||
body, _ := json.Marshal(feLoginData)
|
||||
|
||||
authApiTokenEndpoint := fmt.Sprintf("%s/frontegg/identity/resources/auth/v1/api-token", armoCivAdaptor.armoUrls.AuthUrl)
|
||||
@@ -53,11 +53,12 @@ func (armoCivAdaptor *ArmoCivAdaptor) Login() error {
|
||||
return err
|
||||
}
|
||||
var feLoginResponse FeLoginResponse
|
||||
err = json.Unmarshal(responseBody, &feLoginResponse)
|
||||
armoCivAdaptor.feToken = feLoginResponse
|
||||
if err != nil {
|
||||
|
||||
if err = json.Unmarshal(responseBody, &feLoginResponse); err != nil {
|
||||
return err
|
||||
}
|
||||
armoCivAdaptor.feToken = feLoginResponse
|
||||
|
||||
/* Now we have JWT */
|
||||
|
||||
armoCivAdaptor.authCookie, err = armoCivAdaptor.getAuthCookie()
|
||||
@@ -84,12 +85,16 @@ func (armoCivAdaptor *ArmoCivAdaptor) GetImageVulnerability(imageID *registryvul
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if containerScanId == "" {
|
||||
return nil, fmt.Errorf("last scan ID is empty")
|
||||
}
|
||||
|
||||
filter := []map[string]string{{"containersScanID": containerScanId}}
|
||||
pageSize := 300
|
||||
pageNumber := 1
|
||||
request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"}
|
||||
requestBody, _ := json.Marshal(request)
|
||||
requestUrl := fmt.Sprintf("%s/api/v1/vulnerability/scanResultsDetails?customerGUID=%s", armoCivAdaptor.armoUrls.BackendUrl, armoCivAdaptor.accountId)
|
||||
requestUrl := fmt.Sprintf("%s/api/v1/vulnerability/scanResultsDetails?customerGUID=%s", armoCivAdaptor.armoUrls.BackendUrl, armoCivAdaptor.accountID)
|
||||
client := &http.Client{}
|
||||
httpRequest, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(requestBody))
|
||||
if err != nil {
|
||||
@@ -126,25 +131,7 @@ func (armoCivAdaptor *ArmoCivAdaptor) GetImageVulnerability(imageID *registryvul
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vulnerabilities := make([]registryvulnerabilities.Vulnerability, len(scanDetailsResult.Response))
|
||||
for i, vulnerabilityEntry := range scanDetailsResult.Response {
|
||||
vulnerabilities[i].Description = vulnerabilityEntry.Description
|
||||
vulnerabilities[i].Fixes = make([]registryvulnerabilities.FixedIn, len(vulnerabilityEntry.Fixes))
|
||||
for j, fix := range vulnerabilityEntry.Fixes {
|
||||
vulnerabilities[i].Fixes[j].ImgTag = fix.ImgTag
|
||||
vulnerabilities[i].Fixes[j].Name = fix.Name
|
||||
vulnerabilities[i].Fixes[j].Version = fix.Version
|
||||
}
|
||||
vulnerabilities[i].HealthStatus = vulnerabilityEntry.HealthStatus
|
||||
vulnerabilities[i].Link = vulnerabilityEntry.Link
|
||||
vulnerabilities[i].Metadata = vulnerabilityEntry.Metadata
|
||||
vulnerabilities[i].Name = vulnerabilityEntry.Name
|
||||
vulnerabilities[i].PackageVersion = vulnerabilityEntry.PackageVersion
|
||||
vulnerabilities[i].RelatedPackageName = vulnerabilityEntry.RelatedPackageName
|
||||
vulnerabilities[i].Relevancy = vulnerabilityEntry.Relevancy
|
||||
vulnerabilities[i].Severity = vulnerabilityEntry.Severity
|
||||
vulnerabilities[i].UrgentCount = vulnerabilityEntry.UrgentCount
|
||||
}
|
||||
vulnerabilities := responseObjectToVulnerabilities(scanDetailsResult.Response)
|
||||
|
||||
resultImageVulnerabilityReport := registryvulnerabilities.ContainerImageVulnerabilityReport{
|
||||
ImageID: *imageID,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/armosec/kubescape/registryadaptors/registryvulnerabilities"
|
||||
@@ -9,52 +8,16 @@ import (
|
||||
)
|
||||
|
||||
func TestSum(t *testing.T) {
|
||||
credentials := make(map[string]string)
|
||||
credentials["clientId"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
credentials["accessKey"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
credentials["accountId"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
|
||||
var err error
|
||||
var adaptor registryvulnerabilities.IContainerImageVulnerabilityAdaptor
|
||||
|
||||
adaptor, err = NewArmoAdaptorMock("armoui-dev.eudev3.cyberarmorsoft.com", credentials)
|
||||
adaptor, err = NewArmoAdaptorMock()
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.NoError(t, adaptor.Login())
|
||||
//fmt.Printf("Login successful: %s\n", adaptor.feToken.Token)
|
||||
|
||||
imageVulnerabilityReport, err := adaptor.GetImageVulnerability(®istryvulnerabilities.ContainerImageIdentifier{Tag: "gke.gcr.io/gcp-compute-persistent-disk-csi-driver:v1.3.4-gke.0"})
|
||||
assert.NoError(t, err)
|
||||
|
||||
for _, vulnerability := range imageVulnerabilityReport.Vulnerabilities {
|
||||
fmt.Printf("%s: %s\n", vulnerability.Name, vulnerability.Description)
|
||||
}
|
||||
assert.Equal(t, 25, len(imageVulnerabilityReport.Vulnerabilities))
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
func TestSum(t *testing.T) {
|
||||
credentials := make(map[string]string)
|
||||
credentials["clientId"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
credentials["accessKey"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
credentials["accountId"] = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|
||||
|
||||
var err error
|
||||
var adaptor registryvulnerabilities.IContainerImageVulnerabilityAdaptor
|
||||
|
||||
adaptor, err = NewArmoAdaptorMock("armoui-dev.eudev3.cyberarmorsoft.com", credentials)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// TODO - create mock
|
||||
assert.NoError(t, adaptor.Login())
|
||||
//fmt.Printf("Login successful: %s\n", adaptor.feToken.Token)
|
||||
|
||||
imageVulnerabilityReport, err := adaptor.GetImageVulnerability(®istryvulnerabilities.ContainerImageIdentifier{Tag: "gke.gcr.io/gcp-compute-persistent-disk-csi-driver:v1.3.4-gke.0"})
|
||||
assert.NoError(t, err)
|
||||
|
||||
for _, vulnerability := range imageVulnerabilityReport.Vulnerabilities {
|
||||
fmt.Printf("%s: %s\n", vulnerability.Name, vulnerability.Description)
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -35,7 +35,7 @@ func (armoCivAdaptor *ArmoCivAdaptor) initializeUrls() error {
|
||||
}
|
||||
|
||||
func (armoCivAdaptor *ArmoCivAdaptor) getAuthCookie() (string, error) {
|
||||
selectCustomer := ArmoSelectCustomer{SelectedCustomerGuid: armoCivAdaptor.accountId}
|
||||
selectCustomer := ArmoSelectCustomer{SelectedCustomerGuid: armoCivAdaptor.accountID}
|
||||
requestBody, _ := json.Marshal(selectCustomer)
|
||||
requestUrl := fmt.Sprintf("%s/api/v1/openid_customers", armoCivAdaptor.armoUrls.BackendUrl)
|
||||
client := &http.Client{}
|
||||
@@ -75,14 +75,18 @@ func (armoCivAdaptor *ArmoCivAdaptor) getAuthCookie() (string, error) {
|
||||
}
|
||||
|
||||
func (armoCivAdaptor *ArmoCivAdaptor) getImageLastScanId(imageID *registryvulnerabilities.ContainerImageIdentifier) (string, error) {
|
||||
filter := []map[string]string{{"imageTag": imageID.Tag}}
|
||||
filter := []map[string]string{{"imageTag": imageID.Tag, "status": "Success"}}
|
||||
pageSize := 1
|
||||
pageNumber := 1
|
||||
request := V2ListRequest{PageSize: &pageSize, PageNum: &pageNumber, InnerFilters: filter, OrderBy: "timestamp:desc"}
|
||||
requestBody, _ := json.Marshal(request)
|
||||
requestUrl := fmt.Sprintf("%s/api/v1/vulnerability/scanResultsSumSummary?customerGUID=%s", armoCivAdaptor.armoUrls.BackendUrl, armoCivAdaptor.accountId)
|
||||
requestUrl := fmt.Sprintf("%s/api/v1/vulnerability/scanResultsSumSummary?customerGUID=%s", armoCivAdaptor.armoUrls.BackendUrl, armoCivAdaptor.accountID)
|
||||
client := &http.Client{}
|
||||
httpRequest, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(requestBody))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
httpRequest.Header.Set("Content-Type", "application/json")
|
||||
httpRequest.Header.Set("Authorization", fmt.Sprintf("Bearer %s", armoCivAdaptor.feToken.Token))
|
||||
httpRequest.Header.Set("Cookie", fmt.Sprintf("auth=%s", armoCivAdaptor.authCookie))
|
||||
@@ -120,3 +124,26 @@ func (armoCivAdaptor *ArmoCivAdaptor) getImageLastScanId(imageID *registryvulner
|
||||
|
||||
return scanSummartResult.Response[0].ContainerScanID, nil
|
||||
}
|
||||
|
||||
func responseObjectToVulnerabilities(vulnerabilitiesList containerscan.VulnerabilitiesList) []registryvulnerabilities.Vulnerability {
|
||||
vulnerabilities := make([]registryvulnerabilities.Vulnerability, len(vulnerabilitiesList))
|
||||
for i, vulnerabilityEntry := range vulnerabilitiesList {
|
||||
vulnerabilities[i].Description = vulnerabilityEntry.Description
|
||||
vulnerabilities[i].Fixes = make([]registryvulnerabilities.FixedIn, len(vulnerabilityEntry.Fixes))
|
||||
for j, fix := range vulnerabilityEntry.Fixes {
|
||||
vulnerabilities[i].Fixes[j].ImgTag = fix.ImgTag
|
||||
vulnerabilities[i].Fixes[j].Name = fix.Name
|
||||
vulnerabilities[i].Fixes[j].Version = fix.Version
|
||||
}
|
||||
vulnerabilities[i].HealthStatus = vulnerabilityEntry.HealthStatus
|
||||
vulnerabilities[i].Link = vulnerabilityEntry.Link
|
||||
vulnerabilities[i].Metadata = vulnerabilityEntry.Metadata
|
||||
vulnerabilities[i].Name = vulnerabilityEntry.Name
|
||||
vulnerabilities[i].PackageVersion = vulnerabilityEntry.PackageVersion
|
||||
vulnerabilities[i].RelatedPackageName = vulnerabilityEntry.RelatedPackageName
|
||||
vulnerabilities[i].Relevancy = vulnerabilityEntry.Relevancy
|
||||
vulnerabilities[i].Severity = vulnerabilityEntry.Severity
|
||||
vulnerabilities[i].UrgentCount = vulnerabilityEntry.UrgentCount
|
||||
}
|
||||
return vulnerabilities
|
||||
}
|
||||
|
||||
@@ -49,8 +49,8 @@ type ArmoSelectCustomer struct {
|
||||
|
||||
type ArmoCivAdaptor struct {
|
||||
registry string
|
||||
accountId string
|
||||
clientId string
|
||||
accountID string
|
||||
clientID string
|
||||
accessKey string
|
||||
feToken FeLoginResponse
|
||||
armoUrls ArmoBeConfiguration
|
||||
|
||||
@@ -34,13 +34,15 @@ const (
|
||||
|
||||
// FileResourceHandler handle resources from files and URLs
|
||||
type FileResourceHandler struct {
|
||||
inputPatterns []string
|
||||
inputPatterns []string
|
||||
registryAdaptors *RegistryAdaptors
|
||||
}
|
||||
|
||||
func NewFileResourceHandler(inputPatterns []string) *FileResourceHandler {
|
||||
func NewFileResourceHandler(inputPatterns []string, registryAdaptors *RegistryAdaptors) *FileResourceHandler {
|
||||
k8sinterface.InitializeMapResourcesMock() // initialize the resource map
|
||||
return &FileResourceHandler{
|
||||
inputPatterns: inputPatterns,
|
||||
inputPatterns: inputPatterns,
|
||||
registryAdaptors: registryAdaptors,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +92,10 @@ func (fileHandler *FileResourceHandler) GetResources(frameworks []reporthandling
|
||||
}
|
||||
}
|
||||
|
||||
if err := fileHandler.registryAdaptors.collectImagesVulnerabilities(k8sResources, allResources); err != nil {
|
||||
cautils.WarningDisplay(os.Stderr, "Warning: failed to collect images vulnerabilities: %s\n", err.Error())
|
||||
}
|
||||
|
||||
return k8sResources, allResources, nil
|
||||
|
||||
}
|
||||
|
||||
@@ -30,14 +30,16 @@ type K8sResourceHandler struct {
|
||||
hostSensorHandler hostsensorutils.IHostSensor
|
||||
fieldSelector IFieldSelector
|
||||
rbacObjectsAPI *cautils.RBACObjects
|
||||
registryAdaptors *RegistryAdaptors
|
||||
}
|
||||
|
||||
func NewK8sResourceHandler(k8s *k8sinterface.KubernetesApi, fieldSelector IFieldSelector, hostSensorHandler hostsensorutils.IHostSensor, rbacObjects *cautils.RBACObjects) *K8sResourceHandler {
|
||||
func NewK8sResourceHandler(k8s *k8sinterface.KubernetesApi, fieldSelector IFieldSelector, hostSensorHandler hostsensorutils.IHostSensor, rbacObjects *cautils.RBACObjects, registryAdaptors *RegistryAdaptors) *K8sResourceHandler {
|
||||
return &K8sResourceHandler{
|
||||
k8s: k8s,
|
||||
fieldSelector: fieldSelector,
|
||||
hostSensorHandler: hostSensorHandler,
|
||||
rbacObjectsAPI: rbacObjects,
|
||||
registryAdaptors: registryAdaptors,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +62,11 @@ func (k8sHandler *K8sResourceHandler) GetResources(frameworks []reporthandling.F
|
||||
if err := k8sHandler.pullResources(k8sResourcesMap, allResources, namespace, labels); err != nil {
|
||||
return k8sResourcesMap, allResources, err
|
||||
}
|
||||
|
||||
if err := k8sHandler.registryAdaptors.collectImagesVulnerabilities(k8sResourcesMap, allResources); err != nil {
|
||||
cautils.WarningDisplay(os.Stderr, "Warning: failed to collect image vulnerabilities: %s\n", err.Error())
|
||||
}
|
||||
|
||||
if err := k8sHandler.collectHostResources(allResources, k8sResourcesMap); err != nil {
|
||||
cautils.WarningDisplay(os.Stderr, "Warning: failed to collect host sensor resources\n")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
package resourcehandler
|
||||
|
||||
import (
|
||||
"github.com/armosec/k8s-interface/k8sinterface"
|
||||
"github.com/armosec/k8s-interface/workloadinterface"
|
||||
"github.com/armosec/kubescape/cautils"
|
||||
armosecadaptorv1 "github.com/armosec/kubescape/registryadaptors/armosec/v1"
|
||||
"github.com/armosec/kubescape/registryadaptors/registryvulnerabilities"
|
||||
"github.com/armosec/opa-utils/shared"
|
||||
)
|
||||
|
||||
const (
|
||||
ImagevulnerabilitiesObjectGroup = "image.vulnscan.com"
|
||||
ImagevulnerabilitiesObjectVersion = "v1"
|
||||
ImagevulnerabilitiesObjectKind = "ImageVulnerabilities"
|
||||
)
|
||||
|
||||
type RegistryAdaptors struct {
|
||||
adaptors []registryvulnerabilities.IContainerImageVulnerabilityAdaptor
|
||||
}
|
||||
|
||||
func NewRegistryAdaptors() (*RegistryAdaptors, error) {
|
||||
// list supported adaptors
|
||||
registryAdaptors := &RegistryAdaptors{}
|
||||
adaptors, err := listAdaptores()
|
||||
if err != nil {
|
||||
return registryAdaptors, err
|
||||
}
|
||||
registryAdaptors.adaptors = adaptors
|
||||
return registryAdaptors, nil
|
||||
}
|
||||
|
||||
func (registryAdaptors *RegistryAdaptors) collectImagesVulnerabilities(k8sResourcesMap *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata) error {
|
||||
|
||||
// list cluster images
|
||||
images := listImagesTags(k8sResourcesMap, allResources)
|
||||
imagesIdentifiers := imageTagsToContainerImageIdentifier(images)
|
||||
|
||||
imagesVulnerability := map[string][]registryvulnerabilities.Vulnerability{}
|
||||
for i := range registryAdaptors.adaptors { // login and and get vulnerabilities
|
||||
|
||||
if err := registryAdaptors.adaptors[i].Login(); err != nil {
|
||||
return err
|
||||
}
|
||||
vulnerabilities, err := registryAdaptors.adaptors[i].GetImagesVulnerabilities(imagesIdentifiers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for j := range vulnerabilities {
|
||||
imagesVulnerability[vulnerabilities[j].ImageID.Tag] = vulnerabilities[j].Vulnerabilities
|
||||
}
|
||||
}
|
||||
|
||||
// convert result to IMetadata object
|
||||
metaObjs := vulnerabilitiesToIMetadata(imagesVulnerability)
|
||||
|
||||
// save in resources map
|
||||
for i := range metaObjs {
|
||||
allResources[metaObjs[i].GetID()] = metaObjs[i]
|
||||
}
|
||||
(*k8sResourcesMap)[k8sinterface.JoinResourceTriplets(ImagevulnerabilitiesObjectGroup, ImagevulnerabilitiesObjectVersion, ImagevulnerabilitiesObjectKind)] = workloadinterface.ListMetaIDs(metaObjs)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func vulnerabilitiesToIMetadata(vulnerabilities map[string][]registryvulnerabilities.Vulnerability) []workloadinterface.IMetadata {
|
||||
objs := []workloadinterface.IMetadata{}
|
||||
for i := range vulnerabilities {
|
||||
objs = append(objs, vulnerabilityToIMetadata(i, vulnerabilities[i]))
|
||||
}
|
||||
return objs
|
||||
}
|
||||
|
||||
func vulnerabilityToIMetadata(imageTag string, vulnerabilities []registryvulnerabilities.Vulnerability) workloadinterface.IMetadata {
|
||||
obj := map[string]interface{}{}
|
||||
metadata := map[string]interface{}{}
|
||||
metadata["name"] = imageTag // store image tag as object name
|
||||
obj["kind"] = ImagevulnerabilitiesObjectKind
|
||||
obj["apiVersion"] = k8sinterface.JoinGroupVersion(ImagevulnerabilitiesObjectGroup, ImagevulnerabilitiesObjectVersion)
|
||||
obj["data"] = vulnerabilities
|
||||
obj["metadata"] = metadata
|
||||
|
||||
return workloadinterface.NewWorkloadObj(obj)
|
||||
}
|
||||
|
||||
// list all images tags
|
||||
func listImagesTags(k8sResourcesMap *cautils.K8SResources, allResources map[string]workloadinterface.IMetadata) []string {
|
||||
images := []string{}
|
||||
for _, resources := range *k8sResourcesMap {
|
||||
for j := range resources {
|
||||
if resource, ok := allResources[resources[j]]; ok {
|
||||
if resource.GetObjectType() == workloadinterface.TypeWorkloadObject {
|
||||
workload := workloadinterface.NewWorkloadObj(resource.GetObject())
|
||||
if contianers, err := workload.GetContainers(); err == nil {
|
||||
for i := range contianers {
|
||||
images = append(images, contianers[i].Image)
|
||||
}
|
||||
}
|
||||
if contianers, err := workload.GetInitContainers(); err == nil {
|
||||
for i := range contianers {
|
||||
images = append(images, contianers[i].Image)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return shared.SliceStringToUnique(images)
|
||||
}
|
||||
|
||||
func imageTagsToContainerImageIdentifier(images []string) []registryvulnerabilities.ContainerImageIdentifier {
|
||||
imagesIdentifiers := make([]registryvulnerabilities.ContainerImageIdentifier, len(images))
|
||||
for i := range images {
|
||||
imageIdentifier := registryvulnerabilities.ContainerImageIdentifier{
|
||||
Tag: images[i],
|
||||
}
|
||||
// splitted := strings.Split(images[i], "/")
|
||||
// if len(splitted) == 1 {
|
||||
// imageIdentifier.Tag = splitted[0]
|
||||
// } else if len(splitted) == 2 {
|
||||
// imageIdentifier.Registry = splitted[0]
|
||||
// imageIdentifier.Tag = splitted[1]
|
||||
// } else if len(splitted) >= 3 {
|
||||
// imageIdentifier.Registry = splitted[0]
|
||||
// imageIdentifier.Repository = strings.Join(splitted[1:len(splitted)-1], "/")
|
||||
// imageIdentifier.Tag = splitted[len(splitted)-1]
|
||||
// }
|
||||
imagesIdentifiers[i] = imageIdentifier
|
||||
}
|
||||
return imagesIdentifiers
|
||||
}
|
||||
func listAdaptores() ([]registryvulnerabilities.IContainerImageVulnerabilityAdaptor, error) {
|
||||
customerGUID := " "
|
||||
clientID := " "
|
||||
accessKey := " "
|
||||
registry := "armoui-dev.eudev3.cyberarmorsoft.com"
|
||||
|
||||
adaptors := []registryvulnerabilities.IContainerImageVulnerabilityAdaptor{}
|
||||
armosecAdaptor, err := armosecadaptorv1.NewArmoAdaptor(registry, map[string]string{"accountID": customerGUID, "clientID": clientID, "accessKey": accessKey})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
adaptors = append(adaptors, armosecAdaptor)
|
||||
return adaptors, nil
|
||||
}
|
||||
Reference in New Issue
Block a user