remove vendor

This commit is contained in:
dwertent
2021-08-12 16:10:50 +03:00
parent d6e7344f4e
commit 7ecb8891cb
112 changed files with 1 additions and 13969 deletions

View File

@@ -41,7 +41,7 @@ func (flagHandler *FlagHandler) ParseFlag() {
}
func (flagHandler *FlagHandler) Help() {
fmt.Println("Run: ./kube-escape scan framework nsa")
fmt.Println("Run: kube-escape scan framework nsa")
}
func (flagHandler *FlagHandler) Version() {

View File

@@ -1,34 +0,0 @@
# ARMO Golang Utilities Repository
This is ARMO Golang repository for common data structures, functions and etc.
**Please keep everything organized**
Guideline: If you KNOW a datastructure/function will appear in two components or more this is where it belongs!
Each subfolder contains it's own readme
### Clone `capacketsgo` to you repository
```
git submodule add git@github.com:armosec/capacketsgo.git ./vendor/github.com/armosec/capacketsgo
```
Update your project `go.mod`:
```
replace github.com/armosec/capacketsgo => ./vendor/github.com/armosec/capacketsgo
require (
github.com/armosec/capacketsgo v0.0.0
)
```
When vendor is angry on u run build with the following command:
```
go build -mod=mod .
```
every project must do:
git config --global url."ssh://git@github.com/armosec/".insteadOf "https://github.com/armosec/"
go env -w GOPRIVATE=github.com/armosec

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,41 +0,0 @@
package apis
// func TestLogin2BE(t *testing.T) {
// loginDetails := CustomerLoginDetails{Email: "lalafi@cyberarmor.io", Password: "***", CustomerName: "CyberArmorTests"}
// res, err := BELogin(loginDetails, "login")
// if err != nil {
// t.Errorf("failed to get raw audit is different ")
// }
// k := res.ToLoginObject()
// fmt.Printf("%v\n", k)
// }
// func TestGetMicroserviceOverview(t *testing.T) {
// // client := &http.Client{}
// loginDetails := CustomerLoginDetails{Email: "lalafi@cyberarmor.io", Password: "***", CustomerName: "CyberArmorTests"}
// loginobj, err := BELogin(loginDetails, "login")
// if err != nil {
// t.Errorf("failed to get raw audit is different ")
// }
// k := loginobj.ToLoginObject()
// beURL := GetBEInfo("")
// res, err := BEHttpRequest(k, beURL,
// "GET",
// "v1/microservicesOverview",
// nil,
// BasicBEQuery,
// k)
// if err != nil {
// t.Errorf("failed to get raw audit is different ")
// }
// s := string(res)
// fmt.Printf("%v\n", s)
// }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,39 +0,0 @@
package armotypes
import "github.com/golang/glog"
var IgnoreLabels = []string{AttributeCluster, AttributeNamespace}
// DigestPortalDesignator - get cluster namespace and labels from designator
func DigestPortalDesignator(designator *PortalDesignator) (string, string, map[string]string) {
switch designator.DesignatorType {
case DesignatorAttributes:
return DigestAttributesDesignator(designator.Attributes)
// case DesignatorWlid: TODO
// case DesignatorWildWlid: TODO
default:
glog.Warningf("in 'digestPortalDesignator' designator type: '%v' not yet supported. please contact Armo team", designator.DesignatorType)
}
return "", "", nil
}
func DigestAttributesDesignator(attributes map[string]string) (string, string, map[string]string) {
cluster := ""
namespace := ""
labels := map[string]string{}
if attributes == nil || len(attributes) == 0 {
return cluster, namespace, labels
}
for k, v := range attributes {
labels[k] = v
}
if v, ok := attributes[AttributeNamespace]; ok {
namespace = v
delete(labels, AttributeNamespace)
}
if v, ok := attributes[AttributeCluster]; ok {
cluster = v
delete(labels, AttributeCluster)
}
return cluster, namespace, labels
}

View File

@@ -1,16 +0,0 @@
# Audit-logs connector
## Example
===
Define this *ELASTICSEARCH_URL*
Or use pre-defined elastic client by calling ReinitElastic function
```
AuditReportAction(&AuditReport{
Source: AuditSourceTest,
Details: "here is some test detail",
Subject: "the go compiler",
Action: "ran in test mode",
User: "ben",
Customer: "35d5509a-e81a-492b-a4c6-55264de33e0b",
})
```

View File

@@ -1,69 +0,0 @@
package auditconnector
import (
"context"
"encoding/json"
"fmt"
"strings"
"time"
"github.com/elastic/go-elasticsearch/v7/esapi"
"github.com/elastic/go-elasticsearch/v7/esutil"
"go.uber.org/zap"
)
func (audit *AuditReport) getIndexName() string {
return "v1-audit-" + audit.CustomerGUID
}
func (audit *AuditReport) doReportAuditReport() error {
indexName := audit.getIndexName()
esRequest := esapi.IndexRequest{
Index: indexName,
Body: esutil.NewJSONReader(*audit),
}
err := validateResponse(esRequest.Do(context.Background(), elasticClient))
if err != nil {
if strings.Contains(err.Error(), "index_not_found_exception") {
if err = validateResponse(elasticClient.Indices.Create(indexName, elasticClient.API.Indices.Create.WithBody(strings.NewReader(indexMapping)))); err == nil {
esRequest := esapi.IndexRequest{
Index: indexName,
Body: esutil.NewJSONReader(*audit),
}
err = validateResponse(esRequest.Do(context.Background(), elasticClient))
}
}
return err
}
return err
}
func validateResponse(res *esapi.Response, err error) error {
if err != nil {
return fmt.Errorf("In validateRespons. Primary error. Error: '%v', ", err)
}
defer res.Body.Close()
dec := json.NewDecoder(res.Body)
elErr := make(map[string]interface{})
if err := dec.Decode(&elErr); err != nil {
return fmt.Errorf("In validateResponse failed to decode error body: %v", err)
}
if res.IsError() {
return fmt.Errorf("In validateResponse error returned (%s): %v, ", res.Status(), elErr)
}
zap.L().Info("In validateResponse", zap.Any("result", elErr))
return nil
}
// AuditReportAction stores the audit report in elastic
func AuditReportAction(action *AuditReport) {
action.TimeStamp = time.Now()
if elasticClient != nil {
go func() {
if err := action.doReportAuditReport(); err != nil {
zap.L().Error("In AuditReportAction, failed to doReportAuditReport",
zap.Any("report", action), zap.Error(err))
}
}()
}
}

View File

@@ -1,48 +0,0 @@
package auditconnector
import (
"fmt"
"io/ioutil"
"testing"
)
func TestAuditReportBasic(t *testing.T) {
report := AuditReport{
Source: AuditSourceTest,
Details: "here is some test detail",
Subject: "the go compiler",
Action: "ran in test mode",
User: "ben",
CustomerGUID: "35d5509a-e81a-492b-a4c6-55264de33e0b",
}
err := report.doReportAuditReport()
if err != nil {
t.Errorf("error reporting %s", err)
return
}
res, err := elasticClient.Search(elasticClient.Search.WithIndex(report.getIndexName()))
if err != nil {
t.Errorf("error retrieving results %s", err)
return
}
defer res.Body.Close()
if res.IsError() {
t.Errorf("error retrieving results at ES level %s", res.Status())
return
}
if b, err := ioutil.ReadAll(res.Body); err == nil {
fmt.Print(string(b))
}
}
func TestAuditReportGoRutined(t *testing.T) {
AuditReportAction(&AuditReport{
Source: AuditSourceTest,
Details: "here is some test detail",
Subject: "the go compiler",
Action: "ran in test mode",
User: "ben",
CustomerGUID: "35d5509a-e81a-492b-a4c6-55264de33e0b",
})
}

View File

@@ -1,26 +0,0 @@
package auditconnector
import (
"log"
elasticsearch "github.com/elastic/go-elasticsearch/v7"
)
var elasticClient *elasticsearch.Client = nil
func init() {
var err error
elasticClient, err = elasticsearch.NewDefaultClient()
if err != nil {
log.Print(err)
log.Print("Error: audit elasticsearch client could not be created")
elasticClient = nil
}
}
// ReinitElastic inits the underlying elastic client with well-configured one instead of the default one
func ReinitElastic(client *elasticsearch.Client) {
if client != nil {
elasticClient = client
}
}

View File

@@ -1,83 +0,0 @@
package auditconnector
import (
"time"
)
// available sources for audit logs
const (
AuditSourceControlPanel = "ControlPanel"
AuditSourceAggregator = "Aggregator"
AuditSourceEventReceiver = "EventReceiver"
AuditSourceTest = "Test"
)
// type Marshaler interface {
// MarshalJSON() ([]byte, error)
// }
// AuditTime wraps the golang time object
type AuditTime time.Time
// AuditReport represents single audit log entry
type AuditReport struct {
Source string `json:"source"`
TimeStamp time.Time `json:"time"`
Action string `json:"action"`
Subject string `json:"subject"`
Details string `json:"details"`
User string `json:"user"`
CustomerGUID string `json:"-"`
}
// func (t AuditTime) MarshalJSON() ([]byte, error) {
// stamp := fmt.Sprintf("\"%s\"", time.Time(t).String())
// return []byte(stamp), nil
// }
const indexMapping = `
{
"mappings": {
"properties": {
"source": {
"type": "keyword",
"ignore_above": 256
},
"time": {
"type": "date",
"ignore_malformed": true,
"format": "strict_date_optional_time_nanos"
},
"action": {
"type": "keyword",
"ignore_above": 256
},
"subject": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 8000
}
}
},
"details": {
"type": "text"
},
"user": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"customerGUID": {
"type": "keyword",
"ignore_above": 64
}
}
}
}
`

View File

@@ -1,83 +0,0 @@
package cacli
import (
"github.com/armosec/capacketsgo/opapolicy"
"github.com/armosec/capacketsgo/secrethandling"
)
/*
Please follow the convention:
cacli <group1> <group2> <command>
The function name should look like:
GROUP1GROUP2Command (groups should be upper-case. command - first leeter upper case)
Examples:
cacli wt get -> WTGet
cacli wt Triplet -> WTTriplet
cacli secp list -> SECPList
cacli secp encrypt -> SECPEncrypt
cacli k8s attach -> K8SAttach
cacli opa framework get -> OPAFRAMEWORKGet
*/
type ICacli interface {
// basic commands
Login() error
Status() (*Status, error)
Sign(wlid, user, password, ociImageURL string) error
// wt
WTCreate(*WorkloadTemplate, string) (string, error)
WTApply(*WorkloadTemplate, string) (string, error)
WTUpdate(*WorkloadTemplate, string) (string, error)
WTDelete(string) error
WTTriplet(string) (*GUIDTriplet, error)
WTGet(string) (*WorkloadTemplate, error)
WTDownload(wlid, containerName, output string) error
WTSign(wlid, user, password, ociImageURL string) error
// sp
// SPGet(name string) (*SigningProfile, error)
// SPCreate(sp *SigningProfile) (string, error)
// SPDelete(name string) error
// SPGenarate(name string) (*SigningProfile, error)
// k8s
K8SAttach(_, _, _ string, injectLabel bool) error
// OPA FRAMEWORK
OPAFRAMEWORKCreate(*opapolicy.Framework, string) (*opapolicy.Framework, error)
OPAFRAMEWORKUpdate(*opapolicy.Framework, string) (*opapolicy.Framework, error)
OPAFRAMEWORKGet(string, bool) ([]opapolicy.Framework, error)
OPAFRAMEWORKList(bool) ([]string, error)
OPAFRAMEWORKDelete(string) error
// OPA CONTROL
OPACONTROLCreate(*opapolicy.Control, string) (*opapolicy.Control, error)
OPACONTROLUpdate(*opapolicy.Control, string) (*opapolicy.Control, error)
OPACONTROLGet(string) ([]opapolicy.Control, error)
OPACONTROLList() ([]string, error)
OPACONTROLDelete(string) error
// OPA RULE
OPARULECreate(*opapolicy.PolicyRule, string) (*opapolicy.PolicyRule, error)
OPARULEUpdate(*opapolicy.PolicyRule, string) (*opapolicy.PolicyRule, error)
OPARULEGet(string) ([]opapolicy.PolicyRule, error)
OPARULEList() ([]string, error)
OPARULEDelete(string) error
// // key
// KEYGet(string) (*Key, error)
// secret policy
SECPGet(sid, name, cluster, namespace string) ([]secrethandling.SecretAccessPolicy, error)
SECPEncrypt(message, inputFile, outputFile, keyID string, base64Enc bool) ([]byte, error)
SECPDecrypt(message, inputFile, outputFile string, base64Enc bool) ([]byte, error)
// SECPMetadata(string, bool) (*SecretMetadata, error)
// SECPCreate(*secrethandling.SecretAccessPolicy) (*secrethandling.SecretAccessPolicy, error)
// SECPUpdate(*secrethandling.SecretAccessPolicy) (*secrethandling.SecretAccessPolicy, error)
// SECPList() ([]string, error)
// Utils
UTILSCleanup(string, bool) error
}

View File

@@ -1,183 +0,0 @@
package cacli
import (
"encoding/json"
"fmt"
)
const (
DefaultCredentialsPath = "/etc/credentials"
DefaultCredentialsPathEnv = "CACLI_CREDENTAILS"
)
//WorkloadTemplate sent
type WorkloadTemplate struct {
Kind string `json:"kind"`
Name string `json:"name"`
Cluster string `json:"cluster,omitempty"`
Datacenter string `json:"datacenter,omitempty"`
Namespace string `json:"namespace,omitempty"`
Project string `json:"project,omitempty"`
GroupingLevel0 string `json:"groupingLevel0"`
GroupingLevel1 string `json:"groupingLevel1"`
Wlid string `json:"wlid"`
MetaInfo WorkloadTemplateMetaInfo `json:"metainfo,omitempty"`
AutoAccessTokenUpdate bool `json:"autoAccessTokenUpdate"`
Containers []DockerContainers `json:"containers"`
WorkloadTemplateAttributes map[string]string `json:"attributes,omitempty"`
}
// WorkloadTemplateMetaInfo attributes in workload
type WorkloadTemplateMetaInfo struct {
CreationDate string `json:"creationDate"`
LastEdited string `json:"lastEdited"`
WorkloadKind string `json:"workloadKind"`
Instances WorkloadTemplateInstances `json:"instances"`
Categories []string `json:"categories"`
}
//WorkloadTemplateInstances list of active and inactive
type WorkloadTemplateInstances struct {
Active []string `json:"active"`
Inactive []string `json:"inactive"`
}
// DockerContainers -
type DockerContainers struct {
Name string `json:"name"`
Os string `json:"os,omitempty"`
Architecture string `json:"architecture,omitempty"`
ImageHash string `json:"imageHash,omitempty"`
ImageTag string `json:"imageTag,omitempty"`
EnableVisiblity []map[string]bool `json:"enableVisiblity,omitempty"`
SigningProfileName string `json:"signingProfileName,omitempty"`
}
// ModulesInformation holds data of specific module in signing profile
type ModulesInformation struct {
FullPath string `json:"fullPath"`
Name string `json:"name"`
Mandatory int `json:"mandatory"`
Version string `json:"version,omitempty"`
SignatureMismatchAction int `json:"signatureMismatchAction,omitempty"`
Type int `json:"type,omitempty"`
}
// GUIDTriplet CyberArmor IDs of given microservice
type GUIDTriplet struct {
CustomerGUID string `json:"customerGUID"`
SolutionGUID string `json:"solutionGUID"`
ComponentGUID string `json:"componentGUID"`
ContainersComponentGUIDs []ContainerCAGUIDs `json:"containers"`
}
// ContainerCAGUIDs CyberArmor component IDs of given container
type ContainerCAGUIDs struct {
ContainerName string `json:"containerName"`
ComponentGUID string `json:"componentGUID"`
ProcessesComponentGUIDs []ProcessGUIDs `json:"processes"`
}
// ProcessGUIDs CyberArmor component ID of single process
type ProcessGUIDs struct {
ProcessName string `json:"name"`
ComponentGUID string `json:"componentGUID"`
}
// CredStruct holds the various credentials needed to do login into CA BE
type CredStruct struct {
User string `json:"user"`
Password string `json:"password"`
Customer string `json:"customer"`
}
// Key portal key structure
type Key struct {
GUID string `json:"guid"`
Name string `json:"name"`
CustomID string `json:"custom_id"`
Key string `json:"key"`
Algorithm string `json:"algorithm"`
Description string `json:"description"`
DliveryFlags string `json:"delivery_flags"`
BackupInDB bool `json:"backup_in_ca_db"`
BusinessRulePackage interface{} `json:"business_rule_package"`
Attributes map[string]string `json:"attributes"`
}
// SecretPolicy portal SecretPolicy structure
type SecretPolicy struct {
AccessPolicy int `json:"guid"`
AccessSet string `json:"name"`
EncryptionStatus string `json:"custom_id"`
KeyID string `json:"key"`
Type string `json:"algorithm"`
}
// SecretAccessSetPolicy portal SecretPolicy structure
type SecretAccessSetPolicy struct {
Wlids []string `json:"wlids"`
Attributes map[string]string `json:"attributes"`
}
// Status cacli status
type Status struct {
CacliVersion string `json:"cacli-version"`
CacsignerVersion string `json:"casigner-version"`
Server string `json:"server"`
Customer string `json:"customer"`
UserName string `json:"user-name"`
LoggedIn bool `json:"logged-in"`
}
// SecretMetadata cacli secret metadata
type SecretMetadata struct {
Version int `json:"version"`
Algorithm string `json:"algorithm"`
KeyID string `json:"keyID"`
}
// SigningProfile signingProfile configuration
type SigningProfile struct {
Name string `json:"name"`
GUID string `json:"guid"`
Platform int64 `json:"platform"`
Architecture int64 `json:"architecture"`
CreationTime string `json:"creation_time"`
LastEditTime string `json:"last_edit_time"`
Attributes SignigProfileAttributes `json:"attributes"`
ExecutableList []ExecutablesList `json:"executablesList"` // Use structs from catypes
FullPathMap map[string]bool `json:"-"`
}
// SignigProfileAttributes -
type SignigProfileAttributes struct {
IsStockProfile bool `json:"isStockProfile,omitempty"`
ContainerName string `json:"containerName,omitempty"`
DockerImageTag string `json:"dockerImageTag,omitempty"`
DockerImageSHA256 string `json:"dockerImageSHA256,omitempty"`
GeneratedFor string `json:"generatedFor,omitempty"`
GeneratedFrom string `json:"generatedFrom,omitempty"`
}
// ExecutablesList holds the list of executables in this signing profile
type ExecutablesList struct {
MainProcess string `json:"mainProcess"`
FullProcessCommandLine string `json:"fullProcessCommandLine,omitempty"`
FullProcessEnvironmentVariables map[string]string `json:"fullProcessEnvironmentVariables,omitempty"`
ModulesInfo []ModulesInformation `json:"modulesInfo"`
Filters FiltersSection `json:"filter,omitempty"`
}
// FiltersSection holds the filter section of ExecutablesList
type FiltersSection struct {
IncludePaths []string `json:"includePaths,omitempty"`
IncludeExtensions []string `json:"includeExtensions,omitempty"`
}
func (wt *WorkloadTemplate) Json() string {
if b, err := json.Marshal(*wt); err == nil {
return fmt.Sprintf("%s", b)
}
return ""
}

View File

@@ -1,171 +0,0 @@
package cacli
import (
"bytes"
"context"
"encoding/json"
"fmt"
"os/exec"
"strings"
"time"
"github.com/golang/glog"
)
// RunCommand -
func runCacliCommand(arg []string, display bool) ([]byte, error) {
cmd := &exec.Cmd{}
command := "cacli"
displayCommand := ""
if display {
displayCommand = fmt.Sprintf("command: %s %v", command, arg)
}
if display {
glog.Infof("Running: %s", displayCommand)
}
var outb, errb bytes.Buffer
cmd = exec.Command(command, arg...)
cmd.Stdout = &outb
cmd.Stderr = &errb
err := cmd.Run()
if err != nil {
e := fmt.Sprintf("error: %v, exit code: %s. %s", cmd.Stdout, err.Error(), displayCommand)
glog.Errorf(e)
return nil, fmt.Errorf(e)
}
glog.Infof("command executed successfully. %s", displayCommand)
return cmd.Stdout.(*bytes.Buffer).Bytes(), err
}
// runCacliCommandWithTimeout -
func runCacliCommandWithTimeout(arg []string, display bool, timeout time.Duration) ([]byte, error) {
var outb, errb bytes.Buffer
var cancel context.CancelFunc
// adding timeout
ctx := context.Background()
ctx, cancel = context.WithTimeout(context.Background(), timeout)
defer cancel()
command := "cacli"
if display {
glog.Infof("Running: %s %v", command, arg)
}
cmd := exec.CommandContext(ctx, command, arg...)
cmd.Stdout = &outb
cmd.Stderr = &errb
err := cmd.Run()
if err != nil {
err = fmt.Errorf(fmt.Sprintf("stdout: %v. stderr:%v. err: %v", cmd.Stdout, cmd.Stderr, err))
glog.Errorf("error running command, reason: %v", err.Error())
return nil, err
}
return cmd.Stdout.(*bytes.Buffer).Bytes(), err
}
// RunCommand -
func RunCommand(command string, arg []string, display bool, timeout time.Duration) ([]byte, error) {
var outb, errb bytes.Buffer
var cancel context.CancelFunc
// adding timeout
ctx := context.Background()
ctx, cancel = context.WithTimeout(context.Background(), timeout)
defer cancel()
if display {
glog.Infof("Running: %s %v", command, arg)
}
cmd := exec.CommandContext(ctx, command, arg...)
cmd.Stdout = &outb
cmd.Stderr = &errb
err := cmd.Run()
if err != nil {
err = fmt.Errorf(fmt.Sprintf("stdout: %v. stderr:%v. err: %v", cmd.Stdout, cmd.Stderr, err))
glog.Errorf("error running command, reason: %v", err.Error())
return nil, err
}
return cmd.Stdout.(*bytes.Buffer).Bytes(), err
}
func (cacli *Cacli) runCacliCommandRepeat(arg []string, display bool, timeout time.Duration) ([]byte, error) {
rep, err := runCacliCommandWithTimeout(arg, display, timeout)
if err != nil {
if strings.Contains(err.Error(), "Name or service not known") {
return nil, fmt.Errorf("failed to connect to Armo backend, please restart network. error: %s", err.Error())
}
status, _ := cacli.Status()
if !status.LoggedIn {
glog.Infof("logging in again and retrying %d times", 3)
if err := cacli.cacliLogin(0); err != nil {
return nil, err
}
}
i := 0
for i < 3 { // retry
rep, err = runCacliCommandWithTimeout(arg, display, timeout)
if err == nil {
return rep, nil
}
i++
time.Sleep(3 * time.Second)
}
// glog.Errorf("stdout: %v. stderr:%v. err: %v", cmd.Stdout, cmd.Stderr, err)
return nil, err
}
return rep, nil
}
// LoginCacli -
func (cacli *Cacli) cacliLogin(retries int) error {
if cacli.credentials.User == "" || cacli.credentials.Password == "" {
return fmt.Errorf("Missing cacli username or password")
}
if err := cacli.cacliLoginRetry(retries); err != nil {
return fmt.Errorf("failed to login, url: '%s', reason: %s", cacli.backendURL, err.Error())
}
status, err := cacli.Status()
if err != nil {
return err
}
s, err := json.Marshal(status)
if err != nil {
return err
}
if !status.LoggedIn {
return fmt.Errorf("Status logged-in is false, please check your credentials")
}
glog.Infof("%s", string(s))
return nil
}
// LoginCacli -
func (cacli *Cacli) cacliLoginRetry(retries int) error {
if retries == 0 {
retries = 1
}
var err error
for i := 0; i < retries; i++ {
if err = cacli.Login(); err == nil {
return nil
}
if i != retries-1 {
time.Sleep(3 * time.Second)
}
}
return err
}
// IsLoggedIn -
func (cacli *Cacli) IsLoggedIn() (bool, error) {
status, err := cacli.Status()
if err != nil {
return false, err
}
return status.LoggedIn, nil
}

View File

@@ -1,769 +0,0 @@
package cacli
import (
"encoding/json"
"fmt"
"os"
"time"
"github.com/armosec/capacketsgo/opapolicy"
"github.com/armosec/capacketsgo/secrethandling"
"github.com/golang/glog"
)
// Cacli commands
type Cacli struct {
backendURL string
credentials CredStruct
}
// NewCacli -
func NewCacli(backendURL string, setCredInEnv bool) *Cacli {
// Load credentials from mounted secret
credentials, err := LoadCredentials()
if err != nil {
glog.Error(err)
os.Exit(1)
}
cacliObj := &Cacli{
backendURL: backendURL,
credentials: *credentials,
}
// login cacli
if err := cacliObj.cacliLogin(3); err != nil {
glog.Error(err)
os.Exit(1)
}
if setCredInEnv {
if err := cacliObj.setCredentialsInEnv(); err != nil {
glog.Error(err)
os.Exit(1)
}
}
return cacliObj
}
// NewCacliWithoutLogin -
func NewCacliWithoutLogin() *Cacli {
cacliObj := &Cacli{}
// loggedin, err := cacliObj.IsLoggedIn()
// if err != nil || !loggedin {
// glog.Errorf("Please run `cacli login`\n")
// os.Exit(1)
// }
return cacliObj
}
// ================================================================================================
// ================================ BASIC =========================================================
// ================================================================================================
// Login command
func (cacli *Cacli) Login() error {
args := []string{}
args = append(args, "login")
args = append(args, "-u")
args = append(args, cacli.credentials.User)
if cacli.credentials.Customer != "" {
args = append(args, "-c")
args = append(args, cacli.credentials.Customer)
}
args = append(args, "--dashboard")
args = append(args, cacli.backendURL)
// must be last argument
args = append(args, "-p")
args = append(args, cacli.credentials.Password)
glog.Infof("Running: cacli %v", args[:len(args)-1])
_, err := runCacliCommandWithTimeout(args, false, time.Duration(2)*time.Minute)
return err
}
// Status -
func (cacli *Cacli) Status() (*Status, error) {
status := &Status{}
args := []string{}
args = append(args, "--status")
statusReceive, err := runCacliCommand(args, true)
if err == nil {
err = json.Unmarshal(statusReceive, status)
}
return status, err
}
// Sign command
func (cacli *Cacli) Sign(wlid, user, password, ociImageURL string) error {
args := []string{}
display := true
args = append(args, "--debug")
args = append(args, "sign")
args = append(args, "-wlid")
args = append(args, wlid)
if ociImageURL != "" {
args = append(args, "--dockerless-service-url")
args = append(args, ociImageURL)
}
if user != "" && password != "" {
display = false
args = append(args, "--docker-registry-user")
args = append(args, user)
args = append(args, "--docker-registry-password")
args = append(args, password)
}
_, err := runCacliCommandWithTimeout(args, display, time.Duration(8)*time.Minute)
return err
}
// ================================================================================================
// ================================== vulnscan ==========================================================
// ================================================================================================
func (cacli *Cacli) VulnerabilityScan(cluster, namespace, wlid string, attributes map[string]interface{}) error {
args := []string{}
args = append(args, "k8s")
args = append(args, "scan")
if wlid != "" {
args = append(args, "-wlid")
args = append(args, wlid)
} else if attributes == nil {
if cluster == "" {
return fmt.Errorf("invalid vulnerability scan request- missing cluster")
}
args = append(args, "--cluster")
args = append(args, cluster)
if namespace != "" {
args = append(args, "--namespace")
args = append(args, namespace)
}
}
b, err := cacli.runCacliCommandRepeat(args, true, time.Duration(5)*time.Minute)
if err != nil {
return err
}
glog.Infof("%v", string(b))
return nil
}
// ================================================================================================
// ================================== WT ==========================================================
// ================================================================================================
// Create command
func (cacli *Cacli) WTCreate(wt *WorkloadTemplate, fileName string) (string, error) {
if fileName == "" {
var err error
if fileName, err = ConvertObjectTOFile(*wt); err != nil {
return "", err
}
}
args := []string{}
args = append(args, "wt")
args = append(args, "create")
args = append(args, "-i")
args = append(args, fileName)
wlid, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
if err != nil {
return "", err
}
DeleteObjTmpFile(fileName)
wlidMap := make(map[string]string)
json.Unmarshal(wlid, &wlidMap)
return wlidMap["wlid"], err
}
// Apply command
func (cacli *Cacli) WTApply(wt *WorkloadTemplate, fileName string) (string, error) {
if fileName == "" {
if wt == nil {
return "", fmt.Errorf("missing wt and fileName, you must provide one of them")
}
f, err := StoreObjTmpFile(wt)
if err != nil {
return "", err
}
fileName = f
}
args := []string{}
args = append(args, "wt")
args = append(args, "apply")
args = append(args, "-i")
args = append(args, fileName)
wlid, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
if err != nil {
return "", err
}
DeleteObjTmpFile(fileName)
wlidMap := make(map[string]string)
json.Unmarshal(wlid, &wlidMap)
return wlidMap["wlid"], err
}
// Update command
func (cacli *Cacli) WTUpdate(wt *WorkloadTemplate, fileName string) (string, error) {
if fileName == "" {
if wt == nil {
return "", fmt.Errorf("missing wt and fileName, you must provide one of them")
}
f, err := StoreObjTmpFile(wt)
if err != nil {
return "", err
}
fileName = f
}
args := []string{}
args = append(args, "wt")
args = append(args, "update")
args = append(args, "-i")
args = append(args, fileName)
wlid, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
if err != nil {
return "", err
}
DeleteObjTmpFile(fileName)
wlidMap := make(map[string]string)
json.Unmarshal(wlid, &wlidMap)
return wlidMap["wlid"], err
}
// Triplet command
func (cacli *Cacli) WTTriplet(wlid string) (*GUIDTriplet, error) {
triplet := GUIDTriplet{}
args := []string{}
args = append(args, "wt")
args = append(args, "triplet")
args = append(args, "-wlid")
args = append(args, wlid)
tripletReceive, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
if err == nil {
json.Unmarshal(tripletReceive, &triplet)
}
return &triplet, err
}
// Get command
// func (cacli *Cacli) Get(wlid string) error {
func (cacli *Cacli) WTGet(wlid string) (*WorkloadTemplate, error) {
wt := WorkloadTemplate{}
args := []string{}
args = append(args, "wt")
args = append(args, "get")
args = append(args, "-wlid")
args = append(args, wlid)
wtReceive, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
if err == nil {
json.Unmarshal(wtReceive, &wt)
}
return &wt, err
}
// Get command
func (cacli *Cacli) WTDelete(wlid string) error {
args := []string{}
args = append(args, "wt")
args = append(args, "delete")
args = append(args, "-wlid")
args = append(args, wlid)
_, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
return err
}
// Download command
func (cacli *Cacli) WTDownload(wlid, containerName, output string) error {
args := []string{}
args = append(args, "wt")
args = append(args, "download")
args = append(args, "-wlid")
args = append(args, wlid)
args = append(args, "-o")
args = append(args, output)
if containerName != "" {
args = append(args, "-n")
args = append(args, containerName)
}
_, err := cacli.runCacliCommandRepeat(args, true, time.Duration(6)*time.Minute)
return err
}
// Sign command
func (cacli *Cacli) WTSign(wlid, user, password, ociImageURL string) error {
args := []string{}
display := true
args = append(args, "--debug")
args = append(args, "wt")
args = append(args, "sign")
args = append(args, "-wlid")
args = append(args, wlid)
if ociImageURL != "" {
args = append(args, "--dockerless-service-url")
args = append(args, ociImageURL)
}
if user != "" && password != "" {
display = false
args = append(args, "--docker-registry-user")
args = append(args, user)
args = append(args, "--docker-registry-password")
args = append(args, password)
}
_, err := runCacliCommandWithTimeout(args, display, time.Duration(8)*time.Minute)
return err
}
// ================================================================================================
// ================================= K8S ==========================================================
// ================================================================================================
// AttachNameSpace command attach workloads
func (cacli *Cacli) K8SAttach(cluster, ns, wlid string, injectLabel bool) error {
args := []string{}
args = append(args, "attach")
args = append(args, SetArgs(cluster, ns, wlid, nil)...)
if injectLabel {
args = append(args, "--attach-future")
}
_, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
return err
}
func (cacli *Cacli) RunPostureScan(framework, cluster string) error {
args := []string{}
// cacli k8s posture create --framework "MITRE" --cluster childrenofbodom
args = append(args, "k8s")
args = append(args, "posture")
args = append(args, "create")
args = append(args, "--cluster")
args = append(args, cluster)
args = append(args, "--framework")
args = append(args, framework)
res, err := cacli.runCacliCommandRepeat(args, false, time.Duration(3)*time.Minute)
if err != nil {
return err
}
glog.Infof("%v", string(res))
return nil
}
// ================================================================================================
// ============================ OPA FRAMEWORK =====================================================
// ================================================================================================
// OPAFRAMEWORKGet cacli opa get
func (cacli *Cacli) OPAFRAMEWORKGet(name string, public bool) ([]opapolicy.Framework, error) {
args := []string{}
opaList := []opapolicy.Framework{}
args = append(args, "opa")
args = append(args, "framework")
args = append(args, "get")
if name != "" {
args = append(args, "--name")
args = append(args, name)
}
if public {
args = append(args, "--public")
}
args = append(args, "--expand")
opaReceive, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
if err == nil {
if name == "" {
err = json.Unmarshal(opaReceive, &opaList)
} else {
opaSingle := opapolicy.Framework{}
err = json.Unmarshal(opaReceive, &opaSingle)
opaList = append(opaList, opaSingle)
}
}
return opaList, err
}
// OPAFRAMEWORKList - cacli opa list
func (cacli *Cacli) OPAFRAMEWORKList(public bool) ([]string, error) {
args := []string{}
opaList := []string{}
args = append(args, "opa")
args = append(args, "framework")
args = append(args, "list")
if public {
args = append(args, "--public")
}
opaReceive, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
if err == nil {
json.Unmarshal(opaReceive, &opaList)
}
return opaList, err
}
// OPAFRAMEWORKCreate - cacli opa create
func (cacli *Cacli) OPAFRAMEWORKCreate(framework *opapolicy.Framework, fileName string) (*opapolicy.Framework, error) {
if fileName == "" {
var err error
if fileName, err = ConvertObjectTOFile(*framework); err != nil {
return nil, err
}
}
args := []string{}
args = append(args, "opa")
args = append(args, "framework")
args = append(args, "create")
args = append(args, "--input")
args = append(args, fileName)
_, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
// if err == nil {
// json.Unmarshal(opaReceive, &opaList)
// }
return nil, err
}
// OPAFRAMEWORKUpdate - cacli opa update
func (cacli *Cacli) OPAFRAMEWORKUpdate(framework *opapolicy.Framework, fileName string) (*opapolicy.Framework, error) {
if fileName == "" {
var err error
if fileName, err = ConvertObjectTOFile(*framework); err != nil {
return nil, err
}
}
args := []string{}
args = append(args, "opa")
args = append(args, "framework")
args = append(args, "update")
args = append(args, "--input")
args = append(args, fileName)
_, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
// if err == nil {
// json.Unmarshal(opaReceive, &opaList)
// }
return nil, err
}
// OPAFRAMEWORKDelete cacli opa delete
func (cacli *Cacli) OPAFRAMEWORKDelete(name string) error {
args := []string{}
args = append(args, "opa")
args = append(args, "framework")
args = append(args, "delete")
args = append(args, "--name")
args = append(args, name)
_, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
return err
}
// ================================================================================================
// ============================ OPA CONTROL =======================================================
// ================================================================================================
// OPACONTROLGet cacli opa get
func (cacli *Cacli) OPACONTROLGet(name string) ([]opapolicy.Control, error) {
args := []string{}
opaList := []opapolicy.Control{}
args = append(args, "opa")
args = append(args, "control")
args = append(args, "get")
if name != "" {
args = append(args, "--name")
args = append(args, name)
}
args = append(args, "--expand")
opaReceive, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
if err == nil {
if name == "" {
err = json.Unmarshal(opaReceive, &opaList)
} else {
opaSingle := opapolicy.Control{}
err = json.Unmarshal(opaReceive, &opaSingle)
opaList = append(opaList, opaSingle)
}
}
return opaList, err
}
// OPAFRAMEWORKList - cacli opa list
func (cacli *Cacli) OPACONTROLList() ([]string, error) {
args := []string{}
opaList := []string{}
args = append(args, "opa")
args = append(args, "control")
args = append(args, "list")
opaReceive, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
if err == nil {
json.Unmarshal(opaReceive, &opaList)
}
return opaList, err
}
// OPAFRAMEWORKCreate - cacli opa create
func (cacli *Cacli) OPACONTROLCreate(control *opapolicy.Control, fileName string) (*opapolicy.Control, error) {
if fileName == "" {
var err error
if fileName, err = ConvertObjectTOFile(*control); err != nil {
return nil, err
}
}
args := []string{}
args = append(args, "opa")
args = append(args, "control")
args = append(args, "create")
args = append(args, "--input")
args = append(args, fileName)
_, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
// if err == nil {
// json.Unmarshal(opaReceive, &opaList)
// }
return nil, err
}
// OPAFRAMEWORKUpdate - cacli opa update
func (cacli *Cacli) OPACONTROLUpdate(control *opapolicy.Control, fileName string) (*opapolicy.Control, error) {
if fileName == "" {
var err error
if fileName, err = ConvertObjectTOFile(*control); err != nil {
return nil, err
}
}
args := []string{}
args = append(args, "opa")
args = append(args, "control")
args = append(args, "update")
args = append(args, "--input")
args = append(args, fileName)
_, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
// if err == nil {
// json.Unmarshal(opaReceive, &opaList)
// }
return nil, err
}
// OPACONTROLDelete cacli opa delete
func (cacli *Cacli) OPACONTROLDelete(name string) error {
args := []string{}
args = append(args, "opa")
args = append(args, "control")
args = append(args, "delete")
args = append(args, "--name")
args = append(args, name)
_, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
return err
}
// ================================================================================================
// ============================== OPA RULE ========================================================
// ================================================================================================
// OPARULEGet cacli opa get
func (cacli *Cacli) OPARULEGet(name string) ([]opapolicy.PolicyRule, error) {
args := []string{}
opaList := []opapolicy.PolicyRule{}
args = append(args, "opa")
args = append(args, "rule")
args = append(args, "get")
if name != "" {
args = append(args, "--name")
args = append(args, name)
}
opaReceive, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
if err == nil {
if name == "" {
err = json.Unmarshal(opaReceive, &opaList)
} else {
opaSingle := opapolicy.PolicyRule{}
err = json.Unmarshal(opaReceive, &opaSingle)
opaList = append(opaList, opaSingle)
}
}
return opaList, err
}
// OPAFRAMEWORKList - cacli opa list
func (cacli *Cacli) OPARULEList() ([]string, error) {
args := []string{}
opaList := []string{}
args = append(args, "opa")
args = append(args, "rule")
args = append(args, "list")
opaReceive, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
if err == nil {
json.Unmarshal(opaReceive, &opaList)
}
return opaList, err
}
// OPAFRAMEWORKCreate - cacli opa create
func (cacli *Cacli) OPARULECreate(rule *opapolicy.PolicyRule, fileName string) (*opapolicy.PolicyRule, error) {
if fileName == "" {
var err error
if fileName, err = ConvertObjectTOFile(*rule); err != nil {
return nil, err
}
}
args := []string{}
args = append(args, "opa")
args = append(args, "rule")
args = append(args, "create")
args = append(args, "--input")
args = append(args, fileName)
_, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
// if err == nil {
// json.Unmarshal(opaReceive, &opaList)
// }
return nil, err
}
// OPAFRAMEWORKUpdate - cacli opa update
func (cacli *Cacli) OPARULEUpdate(rule *opapolicy.PolicyRule, fileName string) (*opapolicy.PolicyRule, error) {
if fileName == "" {
var err error
if fileName, err = ConvertObjectTOFile(*rule); err != nil {
return nil, err
}
}
args := []string{}
args = append(args, "opa")
args = append(args, "rule")
args = append(args, "update")
args = append(args, "--input")
args = append(args, fileName)
_, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
// if err == nil {
// json.Unmarshal(opaReceive, &opaList)
// }
return nil, err
}
// OPARULEDelete cacli opa delete
func (cacli *Cacli) OPARULEDelete(name string) error {
args := []string{}
args = append(args, "opa")
args = append(args, "rule")
args = append(args, "delete")
args = append(args, "--name")
args = append(args, name)
_, err := cacli.runCacliCommandRepeat(args, true, time.Duration(2)*time.Minute)
return err
}
// ================================================================================================
// ================================ SECP ==========================================================
// ================================================================================================
// SecretEncrypt -
func (cacli *Cacli) SECPEncrypt(message, inputFile, outputFile, keyID string, base64Enc bool) ([]byte, error) {
args := []string{}
args = append(args, "secret-policy")
args = append(args, "encrypt")
if message != "" {
args = append(args, "--message")
args = append(args, message)
}
if inputFile != "" {
args = append(args, "--input")
args = append(args, inputFile)
}
if keyID != "" {
args = append(args, "-kid")
args = append(args, keyID)
}
if outputFile != "" {
args = append(args, "--output")
args = append(args, outputFile)
}
if base64Enc {
args = append(args, "--base64")
}
messageByte, err := runCacliCommand(args, false)
return messageByte, err
}
// SecretDecrypt -
func (cacli *Cacli) SECPDecrypt(message, inputFile, outputFile string, base64Enc bool) ([]byte, error) {
args := []string{}
args = append(args, "secret-policy")
args = append(args, "decrypt")
if message != "" {
args = append(args, "--message")
args = append(args, message)
}
if inputFile != "" {
args = append(args, "--input")
args = append(args, inputFile)
}
if outputFile != "" {
args = append(args, "--output")
args = append(args, outputFile)
}
if base64Enc {
args = append(args, "--base64")
}
messageByte, err := runCacliCommand(args, true)
return messageByte, err
}
// GetSecretAccessPolicy -
func (cacli *Cacli) SECPGet(sid, name, cluster, namespace string) ([]secrethandling.SecretAccessPolicy, error) {
secretAccessPolicy := []secrethandling.SecretAccessPolicy{}
args := []string{}
args = append(args, "secret-policy")
args = append(args, "get")
if sid != "" {
args = append(args, "-sid")
args = append(args, sid)
} else if name != "" {
args = append(args, "--name")
args = append(args, name)
} else {
if cluster != "" {
args = append(args, "--cluster")
args = append(args, cluster)
if namespace != "" {
args = append(args, "--namespace")
args = append(args, namespace)
}
}
}
sReceive, err := cacli.runCacliCommandRepeat(args, true, time.Duration(3)*time.Minute)
if err == nil {
if err = json.Unmarshal(sReceive, &secretAccessPolicy); err != nil {
tmpSecretAccessPolicy := secrethandling.SecretAccessPolicy{}
if err = json.Unmarshal(sReceive, &tmpSecretAccessPolicy); err == nil {
secretAccessPolicy = []secrethandling.SecretAccessPolicy{tmpSecretAccessPolicy}
}
}
err = nil // if received and empty list
}
return secretAccessPolicy, err
}
// ================================================================================================
// ================================ UTILS =========================================================
// ================================================================================================
func (cacli *Cacli) UTILSCleanup(wlid string, discoveryOnly bool) error {
args := []string{}
args = append(args, "utils")
args = append(args, "cleanup")
args = append(args, "--workload-id")
args = append(args, wlid)
if discoveryOnly {
args = append(args, "--discovery")
}
_, err := cacli.runCacliCommandRepeat(args, true, time.Duration(3)*time.Minute)
return err
}

View File

@@ -1,211 +0,0 @@
package cacli
import (
"github.com/armosec/capacketsgo/opapolicy"
"github.com/armosec/capacketsgo/secrethandling"
)
// Cacli commands
type CacliMock struct {
backendURL string
credentials CredStruct
}
// NewCacli -
func NewCacliMock(backendURL string) *CacliMock {
// Load credentials from mounted secret
return &CacliMock{
backendURL: backendURL,
credentials: CredStruct{},
}
}
// ================================================================================================
// ================================ BASIC =========================================================
// ================================================================================================
// Login cacli login
func (cacli *CacliMock) Login() error {
return nil
}
// Status cacli --status
func (caclim *CacliMock) Status() (*Status, error) {
return &Status{}, nil
}
// Sign command
func (caclim *CacliMock) Sign(wlid, user, password, ociImageURL string) error {
return nil
}
// ================================================================================================
// ================================== WT ==========================================================
// ================================================================================================
// Create command
func (caclim *CacliMock) WTCreate(wt *WorkloadTemplate, fileName string) (string, error) {
return "", nil
}
// Apply command
func (caclim *CacliMock) WTApply(wt *WorkloadTemplate, fileName string) (string, error) {
return "", nil
}
// Update command
func (caclim *CacliMock) WTUpdate(wt *WorkloadTemplate, fileName string) (string, error) {
return "", nil
}
// Triplet command
func (caclim *CacliMock) WTTriplet(wlid string) (*GUIDTriplet, error) {
return &GUIDTriplet{}, nil
}
// Get command
// func (caclim *CacliMock) Get(wlid string) error {
func (caclim *CacliMock) WTGet(wlid string) (*WorkloadTemplate, error) {
return &WorkloadTemplate{}, nil
}
// Get command
func (caclim *CacliMock) WTDelete(wlid string) error {
return nil
}
// Download command
func (caclim *CacliMock) WTDownload(wlid, containerName, output string) error {
return nil
}
// Sign command
func (caclim *CacliMock) WTSign(wlid, user, password, ociImageURL string) error {
return nil
}
// ================================================================================================
// ================================= K8S ==========================================================
// ================================================================================================
// AttachNameSpace command attach all workloads in namespace
func (caclim *CacliMock) K8SAttach(cluster, ns, wlid string, _ bool) error {
return nil
}
// ================================================================================================
// ============================ OPA FRAMEWORK =====================================================
// ================================================================================================
// OPAFRAMEWORKGet cacli opa get
func (caclim *CacliMock) OPAFRAMEWORKGet(name string) ([]opapolicy.Framework, error) {
return []opapolicy.Framework{}, nil
}
// OPAFRAMEWORKDelete cacli opa delete
func (caclim *CacliMock) OPAFRAMEWORKDelete(name string) error {
return nil
}
// OPAFRAMEWORKList - cacli opa list
func (caclim *CacliMock) OPAFRAMEWORKList() ([]string, error) {
return []string{}, nil
}
// OPAFRAMEWORKCreate - cacli opa create
func (caclim *CacliMock) OPAFRAMEWORKCreate(framework *opapolicy.Framework, fileName string) (*opapolicy.Framework, error) {
return nil, nil
}
// OPAFRAMEWORKUpdate - cacli opa update
func (caclim *CacliMock) OPAFRAMEWORKUpdate(framework *opapolicy.Framework, fileName string) (*opapolicy.Framework, error) {
return nil, nil
}
// ================================================================================================
// ============================ OPA CONTROL =======================================================
// ================================================================================================
// OPAFRAMEWORKGet cacli opa get
func (caclim *CacliMock) OPACONTROLGet(name string) ([]opapolicy.Control, error) {
return []opapolicy.Control{}, nil
}
// OPAFRAMEWORKGet cacli opa get
func (caclim *CacliMock) OPACONTROLDelete(name string) error {
return nil
}
// OPAFRAMEWORKList - cacli opa list
func (caclim *CacliMock) OPACONTROLList() ([]string, error) {
return []string{}, nil
}
// OPAFRAMEWORKCreate - cacli opa create
func (caclim *CacliMock) OPACONTROLCreate(control *opapolicy.Control, fileName string) (*opapolicy.Control, error) {
return nil, nil
}
// OPAFRAMEWORKUpdate - cacli opa update
func (caclim *CacliMock) OPACONTROLUpdate(control *opapolicy.Control, fileName string) (*opapolicy.Control, error) {
return nil, nil
}
// ================================================================================================
// ============================== OPA RULE ========================================================
// ================================================================================================
// OPAFRAMEWORKGet cacli opa get
func (caclim *CacliMock) OPARULEGet(name string) ([]opapolicy.PolicyRule, error) {
return []opapolicy.PolicyRule{}, nil
}
// OPAFRAMEWORKGet cacli opa get
func (caclim *CacliMock) OPARULEDelete(name string) error {
return nil
}
// OPAFRAMEWORKList - cacli opa list
func (caclim *CacliMock) OPARULEList() ([]string, error) {
return []string{}, nil
}
// OPAFRAMEWORKCreate - cacli opa create
func (caclim *CacliMock) OPARULECreate(rule *opapolicy.PolicyRule, fileName string) (*opapolicy.PolicyRule, error) {
return nil, nil
}
// OPAFRAMEWORKUpdate - cacli opa update
func (caclim *CacliMock) OPARULEUpdate(rule *opapolicy.PolicyRule, fileName string) (*opapolicy.PolicyRule, error) {
return nil, nil
}
// ================================================================================================
// ================================ SECP ==========================================================
// ================================================================================================
// SecretEncrypt -
func (caclim *CacliMock) SECPEncrypt(message, inputFile, outputFile, keyID string, base64Enc bool) ([]byte, error) {
return []byte{}, nil
}
// SecretDecrypt -
func (caclim *CacliMock) SECPDecrypt(message, inputFile, outputFile string, base64Enc bool) ([]byte, error) {
return []byte{}, nil
}
// GetSecretAccessPolicy -
func (caclim *CacliMock) SECPGet(sid, name, cluster, namespace string) ([]secrethandling.SecretAccessPolicy, error) {
return []secrethandling.SecretAccessPolicy{}, nil
}
// ================================================================================================
// ================================ UTILS =========================================================
// ================================================================================================
func (caclim *CacliMock) UTILSCleanup(wlid string, _ bool) error {
return nil
}

View File

@@ -1,106 +0,0 @@
package cacli
import (
"encoding/json"
"fmt"
"io/ioutil"
"math/rand"
"os"
"path/filepath"
"github.com/golang/glog"
)
func StoreObjTmpFile(obj interface{}) (string, error) {
bet, err := json.Marshal(obj)
if err != nil {
return "", err
}
file := fmt.Sprintf("/tmp/%d.json", rand.Int())
if err := ioutil.WriteFile(file, bet, 0644); err != nil {
return "", err
}
return file, nil
}
func DeleteObjTmpFile(path string) {
// delete file
var err = os.Remove(path)
if err != nil {
glog.Error(err)
}
}
func SetArgs(wlid, cluster, namespace string, attributes map[string]string) []string {
args := []string{}
if wlid != "" {
args = append(args, "--workload-id")
args = append(args, wlid)
}
if cluster != "" {
args = append(args, "--cluster")
args = append(args, cluster)
}
if namespace != "" {
args = append(args, "--namespace")
args = append(args, namespace)
}
return args
}
func ConvertObjectTOFile(obj interface{}) (string, error) {
if obj == nil {
return "", fmt.Errorf("missing wt and fileName, you must provide one of them")
}
f, err := StoreObjTmpFile(obj)
if err != nil {
return "", err
}
return f, nil
}
func LoadCredentials() (*CredStruct, error) {
credentials := CredStruct{}
credentialsPath := getCredentialsPath()
customer, err := ioutil.ReadFile(filepath.Join(credentialsPath, "customer"))
if err != nil || len(customer) == 0 {
glog.Warningf("'customer' not found in credentials secret. path: %s", filepath.Join(credentialsPath, "customer"))
}
credentials.Customer = string(customer)
username, err := ioutil.ReadFile(filepath.Join(credentialsPath, "username"))
if err != nil || len(username) == 0 {
return nil, fmt.Errorf("'username' not found in credentials secret. path: %s", filepath.Join(credentialsPath, "username"))
}
credentials.User = string(username)
password, err := ioutil.ReadFile(filepath.Join(credentialsPath, "password"))
if err != nil || len(password) == 0 {
return nil, fmt.Errorf("'password' not found in credentials secret. path: %s", filepath.Join(credentialsPath, "password"))
}
credentials.Password = string(password)
return &credentials, nil
}
func getCredentialsPath() string {
if credentialsPath := os.Getenv(DefaultCredentialsPathEnv); credentialsPath != "" {
return credentialsPath
}
return DefaultCredentialsPath
}
func (cacli *Cacli) setCredentialsInEnv() error {
if err := os.Setenv("CA_USERNAME", cacli.credentials.User); err != nil {
return err
}
if err := os.Setenv("CA_PASSWORD", cacli.credentials.Password); err != nil {
return err
}
if cacli.credentials.Customer != "" {
if err := os.Setenv("CA_CUSTOMER", cacli.credentials.Customer); err != nil {
return err
}
}
return nil
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,90 +0,0 @@
package containerscan
import (
"bytes"
"math/rand"
"time"
"github.com/francoispqt/gojay"
)
// GenerateContainerScanReportMock - generate a scan result
func GenerateContainerScanReportMock() ScanResultReport {
ds := ScanResultReport{
WLID: "wlid://cluster-k8s-geriatrix-k8s-demo3/namespace-whisky-app/deployment-whisky4all-shipping",
CustomerGUID: "1231bcb1-49ce-4a67-bdd3-5da7a393ae08",
ImgTag: "dreg.armo.cloud:443/demoservice:v16",
ImgHash: "docker-pullable://dreg.armo.cloud:443/demoservice@sha256:754f3cfca915a07ed10655a301dd7a8dc5526a06f9bd06e7c932f4d4108a8296",
Timestamp: time.Now().UnixNano(),
}
ds.Layers = make(LayersList, 0)
layer := ScanResultLayer{}
GenerateContainerScanLayer(&layer)
ds.Layers = append(ds.Layers, layer)
return ds
}
// GenerateContainerScanReportMock - generate a scan result
func GenerateContainerScanReportNoVulMock() ScanResultReport {
ds := ScanResultReport{
WLID: "wlid://cluster-k8s-geriatrix-k8s-demo3/namespace-whisky-app/deployment-whisky4all-shipping",
CustomerGUID: "1231bcb1-49ce-4a67-bdd3-5da7a393ae08",
ImgTag: "dreg.armo.cloud:443/demoservice:v16",
ImgHash: "docker-pullable://dreg.armo.cloud:443/demoservice@sha256:754f3cfca915a07ed10655a301dd7a8dc5526a06f9bd06e7c932f4d4108a8296",
Timestamp: time.Now().UnixNano(),
ContainerName: "shipping",
}
ds.Layers = make(LayersList, 0)
layer := ScanResultLayer{LayerHash: "aaa"}
ds.Layers = append(ds.Layers, layer)
return ds
}
var hash = []rune("abcdef0123456789")
var nums = []rune("0123456789")
func randSeq(n int, bank []rune) string {
rand.Seed(time.Now().UnixNano())
b := make([]rune, n)
for i := range b {
b[i] = bank[rand.Intn(len(bank))]
}
return string(b)
}
// GenerateContainerScanLayer - generate a layer with random vuls
func GenerateContainerScanLayer(layer *ScanResultLayer) {
layer.LayerHash = randSeq(32, hash)
layer.Vulnerabilities = make(VulnerabilitiesList, 0)
layer.Packages = make(LinuxPkgs, 0)
vuls := rand.Intn(10) + 1
for i := 0; i < vuls; i++ {
v := Vulnerability{}
GenerateVulnerability(&v)
layer.Vulnerabilities = append(layer.Vulnerabilities, v)
}
pkg := LinuxPackage{PackageName: "coreutils"}
pkg.Files = make(PkgFiles, 0)
pf := PackageFile{Filename: "aa"}
pkg.Files = append(pkg.Files, pf)
layer.Packages = append(layer.Packages, pkg)
}
// GenerateVulnerability - generate a vul (just diff "cve"'s)
func GenerateVulnerability(v *Vulnerability) error {
baseVul := " { \"name\": \"CVE-2014-9471\", \"imageTag\": \"debian:8\", \"link\": \"https://security-tracker.debian.org/tracker/CVE-2014-9471\", \"description\": \"The parse_datetime function in GNU coreutils allows remote attackers to cause a denial of service (crash) or possibly execute arbitrary code via a crafted date string, as demonstrated by the sdf\", \"severity\": \"Low\", \"metadata\": { \"NVD\": { \"CVSSv2\": { \"Score\": 7.5, \"Vectors\": \"AV:N/AC:L/Au:N/C:P/I:P\" } } }, \"fixedIn\": [ { \"name\": \"coreutils\", \"imageTag\": \"debian:8\", \"version\": \"8.23-1\" } ] }"
b := []byte(baseVul)
r := bytes.NewReader(b)
er := gojay.NewDecoder(r).DecodeObject(v)
v.RelatedPackageName = "coreutils"
v.Severity = HighSeverity
v.Relevancy = Irelevant
v.Name = "CVE-" + randSeq(4, nums) + "-" + randSeq(4, nums)
return er
}

View File

@@ -1,80 +0,0 @@
package containerscan
import (
"bytes"
"encoding/json"
"fmt"
"testing"
"github.com/francoispqt/gojay"
)
func TestUnmarshalScanReport(t *testing.T) {
ds := GenerateContainerScanReportMock()
str1 := ds.AsSha256()
rhs := &ScanResultReport{}
bolB, _ := json.Marshal(ds)
r := bytes.NewReader(bolB)
er := gojay.NewDecoder(r).DecodeObject(rhs)
if er != nil {
t.Errorf("marshalling failed due to: %v", er.Error())
}
if rhs.AsSha256() != str1 {
t.Errorf("marshalling failed different values after marshal:\nOriginal:\n%v\nParsed:\n%v\n\n===\n", string(bolB), rhs)
}
}
func TestConvScanReport2ESvul(t *testing.T) {
// ds := GenerateContainerScanReportMock()
// res := ds.ToFlatVulnerabilities()
// vulsBytes, _ := json.Marshal(res)
// summary := ds.Summerize()
// summaryBytes, _ := json.Marshal(summary)
// fmt.Printf("summary:\n%v\n\nvulnerabilities:\n%v\n\n", string(summaryBytes), string(vulsBytes))
// t.Errorf("%v\n", string(vulsBytes))
}
func TestConvScanReport2ESWithNoVul(t *testing.T) {
// ds := GenerateContainerScanReportNoVulMock()
// res := ds.ToFlatVulnerabilities()
// vulsBytes, _ := json.Marshal(res)
// summary := ds.Summerize()
// summaryBytes, _ := json.Marshal(summary)
// fmt.Printf("summary:\n%v\n\nvulnerabilities:\n%v\n\n", string(summaryBytes), string(vulsBytes))
// t.Errorf("%v\n", string(vulsBytes))
}
func TestUnmarshalScanReport1(t *testing.T) {
ds := Vulnerability{}
if err := GenerateVulnerability(&ds); err != nil {
t.Errorf("%v\n%v\n", ds, err)
}
}
func TestGetByPkgNameSuccess(t *testing.T) {
ds := GenerateContainerScanReportMock()
a := ds.Layers[0].GetFilesByPackage("coreutils")
if a != nil {
fmt.Printf("%+v\n", *a)
}
}
func TestGetByPkgNameMissing(t *testing.T) {
ds := GenerateContainerScanReportMock()
a := ds.Layers[0].GetFilesByPackage("s")
if a != nil {
t.Errorf("expected - no such package should be in that layer %v\n\n", ds)
}
}

View File

@@ -1,113 +0,0 @@
package containerscan
import (
"crypto/sha256"
"fmt"
)
//!!!!!!!!!!!!EVERY CHANGE IN THESE STRUCTURES => CHANGE gojayunmarshaller ASWELL!!!!!!!!!!!!!!!!!!!!!!!!
// ScanResultReport - the report given from scanner to event receiver
type ScanResultReport struct {
CustomerGUID string `json:"customerGUID"`
ImgTag string `json:"imageTag",omitempty`
ImgHash string `json:"imageHash"`
WLID string `json:"wlid"`
ContainerName string `json:"containerName"`
Timestamp int64 `json:"timestamp"`
Layers LayersList `json:"layers"`
ListOfDangerousArtifcats []string `json:"listOfDangerousArtifcats"`
}
// ScanResultLayer - represents a single layer from container scan result
type ScanResultLayer struct {
LayerHash string `json:"layerHash"`
ParentLayerHash string `json:"parentLayerHash"`
Vulnerabilities VulnerabilitiesList `json:"vulnerabilities"`
Packages LinuxPkgs `json:"packageToFile"`
}
// Vulnerability - a vul object
type Vulnerability struct {
Name string `json:"name"`
ImgHash string `json:"imageHash"`
ImgTag string `json:"imageTag",omitempty`
RelatedPackageName string `json:"packageName"`
PackageVersion string `json:"packageVersion"`
Link string `json:"link"`
Description string `json:"description"`
Severity string `json:"severity"`
Metadata interface{} `json:"metadata",omitempty`
Fixes VulFixes `json:"fixedIn",omitempty`
Relevancy string `json:"relevant"` // use the related enum
}
// FixedIn when and which pkg was fixed (which version as well)
type FixedIn struct {
Name string `json:"name"`
ImgTag string `json:"imageTag"`
Version string `json:"version"`
}
// LinuxPackage- Linux package representation
type LinuxPackage struct {
PackageName string `json:"packageName"`
Files PkgFiles `json:"files"`
PackageVersion string `json:"version"`
}
// PackageFile - s.e
type PackageFile struct {
Filename string `json:"name"`
}
// types to provide unmarshalling:
//VulnerabilitiesList -s.e
type LayersList []ScanResultLayer
//VulnerabilitiesList -s.e
type VulnerabilitiesList []Vulnerability
//LinuxPkgs - slice of linux pkgs
type LinuxPkgs []LinuxPackage
//VulFixes - information bout when/how this vul was fixed
type VulFixes []FixedIn
//PkgFiles - slice of files belong to specific pkg
type PkgFiles []PackageFile
func (v *ScanResultReport) AsSha256() string {
h := sha256.New()
h.Write([]byte(fmt.Sprintf("%v", *v)))
return fmt.Sprintf("%x", h.Sum(nil))
}
const (
//defines Relevancy as enum-like
Unknown = "Unknown"
Relevant = "Relevant"
Irelevant = "Irelevant"
NoSP = "No signature profile to compare"
//Clair Severities
UnknownSeverity = "Unknown"
NegligibleSeverity = "Negligible"
LowSeverity = "Low"
MediumSeverity = "Medium"
HighSeverity = "High"
CriticalSeverity = "Critical"
ContainerScanRedisPrefix = "_containerscan"
)
func CalculateFixed(Fixes []FixedIn) int {
for _, fix := range Fixes {
if fix.Version != "None" {
return 1
}
}
return 0
}

View File

@@ -1,39 +0,0 @@
package containerscan
import "strings"
func (layer *ScanResultLayer) GetFilesByPackage(pkgname string) (files *PkgFiles) {
for _, pkg := range layer.Packages {
if pkg.PackageName == pkgname {
return &pkg.Files
}
}
return &PkgFiles{}
}
func (layer *ScanResultLayer) GetPackagesNames() []string {
pkgsNames := []string{}
for _, pkg := range layer.Packages {
pkgsNames = append(pkgsNames, pkg.PackageName)
}
return pkgsNames
}
func (scanresult *ScanResultReport) Validate() bool {
if scanresult.CustomerGUID == "" || (scanresult.ImgHash == "" && scanresult.ImgTag == "") || scanresult.Timestamp <= 0 {
return false
}
//TODO validate layers & vuls
return true
}
func (v *Vulnerability) IsRCE() bool {
desc := strings.ToLower(v.Description)
isRCE := strings.Contains(v.Description, "RCE")
return isRCE || strings.Contains(desc, "remote code execution") || strings.Contains(desc, "remote command execution") || strings.Contains(desc, "arbitrary code") || strings.Contains(desc, "code execution") || strings.Contains(desc, "code injection") || strings.Contains(desc, "command injection") || strings.Contains(desc, "inject arbitrary commands")
}

View File

@@ -1,265 +0,0 @@
package containerscan
import "github.com/armosec/capacketsgo/cautils"
// ToFlatVulnerabilities - returnsgit p
func (scanresult *ScanResultReport) ToFlatVulnerabilities() []*ElasticContainerVulnerabilityResult {
vuls := make([]*ElasticContainerVulnerabilityResult, 0)
vul2indx := make(map[string]int, 0)
for _, layer := range scanresult.Layers {
for _, vul := range layer.Vulnerabilities {
esLayer := ESLayer{LayerHash: layer.LayerHash, ParentLayerHash: layer.ParentLayerHash}
if indx, isOk := vul2indx[vul.Name]; isOk {
vuls[indx].Layers = append(vuls[indx].Layers, esLayer)
continue
}
result := &ElasticContainerVulnerabilityResult{WLID: scanresult.WLID, Timestamp: scanresult.Timestamp}
result.Vulnerability = vul
result.Layers = make([]ESLayer, 0)
result.Layers = append(result.Layers, esLayer)
result.ContainerScanID = scanresult.AsSha256()
result.IsFixed = CalculateFixed(vul.Fixes)
result.RelevantLinks = append(result.RelevantLinks, "https://nvd.nist.gov/vuln/detail/"+vul.Name)
result.RelevantLinks = append(result.RelevantLinks, vul.Link)
result.Vulnerability.Link = "https://nvd.nist.gov/vuln/detail/" + vul.Name
vuls = append(vuls, result)
vul2indx[vul.Name] = len(vuls) - 1
}
}
// find first introduced
for i, v := range vuls {
earlyLayer := ""
for _, layer := range v.Layers {
if layer.ParentLayerHash == earlyLayer {
earlyLayer = layer.LayerHash
}
}
vuls[i].IntroducedInLayer = earlyLayer
}
return vuls
}
func (scanresult *ScanResultReport) Summerize() *ElasticContainerScanSummaryResult {
summary := &ElasticContainerScanSummaryResult{
CustomerGUID: scanresult.CustomerGUID,
ImgTag: scanresult.ImgTag,
ImgHash: scanresult.ImgHash,
WLID: scanresult.WLID,
Timestamp: scanresult.Timestamp,
ContainerName: scanresult.ContainerName,
ContainerScanID: scanresult.AsSha256(),
ListOfDangerousArtifcats: scanresult.ListOfDangerousArtifcats,
RCESummary: make(map[string]int64),
}
obj, e := cautils.SpiffeToSpiffeInfo(scanresult.WLID)
if e == nil {
summary.Cluster = obj.Level0
summary.Namespace = obj.Level1
}
imageInfo, e2 := cautils.ImageTagToImageInfo(scanresult.ImgTag)
if e2 == nil {
summary.Registry = imageInfo.Registry
summary.VersionImage = imageInfo.VersionImage
}
summary.PackagesName = make([]string, 0)
summary.Severity = make([]string, 0)
summary.Relevancy = make([]string, 0)
summary.FixAvailble = make([]string, 0)
summary.SeveritiesSum = make([]RelevanciesSum, 0)
summary.RelevanciesSum = make([]RelevanciesSum, 0)
summary.FixAvailbleSum = make([]RelevanciesSum, 0)
uniqueVulsMap := make(map[string]bool, 0)
for _, layer := range scanresult.Layers {
summary.PackagesName = append(summary.PackagesName, (layer.GetPackagesNames())...)
for _, vul := range layer.Vulnerabilities {
if _, isOk := uniqueVulsMap[vul.Name]; isOk {
continue
}
uniqueVulsMap[vul.Name] = true
if vul.IsRCE() {
summary.RCESummary[vul.Severity]++
}
switch vul.Relevancy {
case Relevant:
summary.NumOfRelevantIssues++
case Irelevant:
summary.NumOfIrelevantIssues++
default: //includes unknown as well
summary.NumOfUnknownIssues++
}
switch vul.Severity {
case NegligibleSeverity:
summary.NumOfNegligibleSeverity++
if vul.Relevancy == Relevant {
summary.NumOfRelevantNegligibleSeverity++
}
if CalculateFixed(vul.Fixes) > 0 {
summary.NumOfFixAvailableNegligibleSeverity++
}
case LowSeverity:
summary.NumOfLowSeverity++
if vul.Relevancy == Relevant {
summary.NumOfRelevantLowSeverity++
}
if CalculateFixed(vul.Fixes) > 0 {
summary.NumOfFixAvailableLowSeverity++
}
case MediumSeverity:
summary.NumOfMediumSeverity++
if vul.Relevancy == Relevant {
summary.NumOfRelevantMediumSeverity++
}
if CalculateFixed(vul.Fixes) > 0 {
summary.NumOfFixAvailableMediumSeverity++
}
case HighSeverity:
summary.NumOfHighSeverity++
if vul.Relevancy == Relevant {
summary.NumOfRelevantHighSeverity++
}
if CalculateFixed(vul.Fixes) > 0 {
summary.NumOfFixAvailableHighSeverity++
}
case CriticalSeverity:
summary.NumOfCriticalSeverity++
if vul.Relevancy == Relevant {
summary.NumOfRelevantCriticalSeverity++
}
if CalculateFixed(vul.Fixes) > 0 {
summary.NumOfFixAvailableCriticalSeverity++
}
default: //includes unknown as well
summary.NumOfUnknownSeverity++
if vul.Relevancy == Relevant {
summary.NumOfRelevantUnknownSeverity++
}
if CalculateFixed(vul.Fixes) > 0 {
summary.NumOfFixAvailableUnknownSeverity++
}
}
}
}
if summary.NumOfCriticalSeverity > 0 || summary.NumOfRelevantHighSeverity > 3 {
summary.Status = "Fail"
} else {
summary.Status = "Success"
}
//Negligible
if summary.NumOfNegligibleSeverity > 0 {
summary.Severity = append(summary.Severity, "Negligible")
summary.SeveritiesSum = append(summary.SeveritiesSum, RelevanciesSum{Relevancy: "Negligible", Sum: summary.NumOfNegligibleSeverity})
if summary.NumOfRelevantNegligibleSeverity > 0 {
summary.Relevancy = append(summary.Relevancy, "Negligible")
summary.RelevanciesSum = append(summary.RelevanciesSum, RelevanciesSum{Relevancy: "Negligible", Sum: summary.NumOfRelevantNegligibleSeverity})
}
if summary.NumOfFixAvailableNegligibleSeverity > 0 {
summary.FixAvailble = append(summary.FixAvailble, "Negligible")
summary.FixAvailbleSum = append(summary.FixAvailbleSum, RelevanciesSum{Relevancy: "Negligible", Sum: summary.NumOfFixAvailableNegligibleSeverity})
}
}
if summary.NumOfLowSeverity > 0 {
summary.Severity = append(summary.Severity, "Low")
summary.SeveritiesSum = append(summary.SeveritiesSum, RelevanciesSum{Relevancy: "Low", Sum: summary.NumOfLowSeverity})
if summary.NumOfRelevantLowSeverity > 0 {
summary.Relevancy = append(summary.Relevancy, "Low")
summary.RelevanciesSum = append(summary.RelevanciesSum, RelevanciesSum{Relevancy: "Low", Sum: summary.NumOfRelevantLowSeverity})
}
if summary.NumOfFixAvailableLowSeverity > 0 {
summary.FixAvailble = append(summary.FixAvailble, "Low")
summary.FixAvailbleSum = append(summary.FixAvailbleSum, RelevanciesSum{Relevancy: "Low", Sum: summary.NumOfFixAvailableLowSeverity})
}
}
if summary.NumOfMediumSeverity > 0 {
summary.Severity = append(summary.Severity, "Medium")
summary.SeveritiesSum = append(summary.SeveritiesSum, RelevanciesSum{Relevancy: "Medium", Sum: summary.NumOfMediumSeverity})
if summary.NumOfRelevantMediumSeverity > 0 {
summary.Relevancy = append(summary.Relevancy, "Medium")
summary.RelevanciesSum = append(summary.RelevanciesSum, RelevanciesSum{Relevancy: "Medium", Sum: summary.NumOfRelevantMediumSeverity})
}
if summary.NumOfFixAvailableMediumSeverity > 0 {
summary.FixAvailble = append(summary.FixAvailble, "Medium")
summary.FixAvailbleSum = append(summary.FixAvailbleSum, RelevanciesSum{Relevancy: "Medium", Sum: summary.NumOfFixAvailableMediumSeverity})
}
}
if summary.NumOfHighSeverity > 0 {
summary.Severity = append(summary.Severity, "High")
summary.SeveritiesSum = append(summary.SeveritiesSum, RelevanciesSum{Relevancy: "High", Sum: summary.NumOfHighSeverity})
if summary.NumOfRelevantHighSeverity > 0 {
summary.Relevancy = append(summary.Relevancy, "High")
summary.RelevanciesSum = append(summary.RelevanciesSum, RelevanciesSum{Relevancy: "High", Sum: summary.NumOfRelevantHighSeverity})
}
if summary.NumOfFixAvailableHighSeverity > 0 {
summary.FixAvailble = append(summary.FixAvailble, "High")
summary.FixAvailbleSum = append(summary.FixAvailbleSum, RelevanciesSum{Relevancy: "High", Sum: summary.NumOfFixAvailableHighSeverity})
}
}
if summary.NumOfCriticalSeverity > 0 {
summary.Severity = append(summary.Severity, "Critical")
summary.SeveritiesSum = append(summary.SeveritiesSum, RelevanciesSum{Relevancy: "Critical", Sum: summary.NumOfCriticalSeverity})
if summary.NumOfRelevantCriticalSeverity > 0 {
summary.Relevancy = append(summary.Relevancy, "Critical")
summary.RelevanciesSum = append(summary.RelevanciesSum, RelevanciesSum{Relevancy: "Critical", Sum: summary.NumOfRelevantCriticalSeverity})
}
if summary.NumOfFixAvailableCriticalSeverity > 0 {
summary.FixAvailble = append(summary.FixAvailble, "Critical")
summary.FixAvailbleSum = append(summary.FixAvailbleSum, RelevanciesSum{Relevancy: "Critical", Sum: summary.NumOfFixAvailableCriticalSeverity})
}
}
if summary.NumOfUnknownSeverity > 0 {
summary.Severity = append(summary.Severity, "Unknown")
summary.SeveritiesSum = append(summary.SeveritiesSum, RelevanciesSum{Relevancy: "Unknown", Sum: summary.NumOfUnknownSeverity})
if summary.NumOfRelevantUnknownSeverity > 0 {
summary.Relevancy = append(summary.Relevancy, "Unknown")
summary.RelevanciesSum = append(summary.RelevanciesSum, RelevanciesSum{Relevancy: "Unknown", Sum: summary.NumOfRelevantUnknownSeverity})
}
if summary.NumOfFixAvailableUnknownSeverity > 0 {
summary.FixAvailble = append(summary.FixAvailble, "Unknown")
summary.FixAvailbleSum = append(summary.FixAvailbleSum, RelevanciesSum{Relevancy: "Unknown", Sum: summary.NumOfFixAvailableUnknownSeverity})
}
}
return summary
}

View File

@@ -1,91 +0,0 @@
package containerscan
type ElasticContainerVulnerabilityResult struct {
WLID string `json:"wlid"`
ContainerScanID string `json:"containersScanID"`
Layers []ESLayer `json:"layers"`
Timestamp int64 `json:"timestamp"`
IsFixed int `json:"isFixed"`
IntroducedInLayer string `json:LayerHash`
RelevantLinks []string `json:"links"` // shitty SE practice
//
Vulnerability `json:",inline"`
}
type ESLayer struct {
LayerHash string `json:"layerHash"`
ParentLayerHash string `json:"parentLayerHash"`
}
type ElasticContainerScanSummaryResult struct {
CustomerGUID string `json:"customerGUID"`
ContainerScanID string `json:"containersScanID"`
Timestamp int64 `json:"timestamp"`
WLID string `json:"wlid"`
ImgTag string `json:"imageTag",omitempty`
ImgHash string `json:"imageHash"`
Cluster string `json:"cluster"`
Namespace string `json:"namespace"`
ContainerName string `json:"containerName"`
PackagesName []string `json:"packages"`
Severity []string `json:"severities"`
Relevancy []string `json:"relevancies"`
FixAvailble []string `json:"fixes"`
ListOfDangerousArtifcats []string `json:"listOfDangerousArtifcats"`
SeveritiesSum []RelevanciesSum `json:"severitiesSum"`
RelevanciesSum []RelevanciesSum `json:"relevanciesSum"`
FixAvailbleSum []RelevanciesSum `json:"fixAvailbleSum"`
Status string `json:"status"`
Registry string `json:"registry"`
VersionImage string `json:"versionImage"`
RCESummary map[string]int64 `json:"RCE,omitempty"`
NumOfUnknownSeverity int64 `json:"numOfUnknownSeverity"`
NumOfNegligibleSeverity int64 `json:"numOfNegligibleSeverity"`
NumOfLowSeverity int64 `json:"numOfLowSeverity"`
NumOfMediumSeverity int64 `json:"numOfMeduiumSeverity"`
NumOfHighSeverity int64 `json:"numOfHighSeverity"`
NumOfCriticalSeverity int64 `json:"numOfCriticalSeverity"`
NumOfRelevantUnknownSeverity int64 `json:"numOfRelevantUnknownSeverity"`
NumOfRelevantNegligibleSeverity int64 `json:"numOfRelevantNegligibleSeverity"`
NumOfRelevantLowSeverity int64 `json:"numOfRelevantLowSeverity"`
NumOfRelevantMediumSeverity int64 `json:"numOfRelevantMediumSeverity"`
NumOfRelevantHighSeverity int64 `json:"numOfHighRelevantSeverity"`
NumOfRelevantCriticalSeverity int64 `json:"numOfRelevantCriticalSeverity"`
NumOfFixAvailableUnknownSeverity int64 `json:"numOfFixAvailableUnknownSeverity"`
NumOfFixAvailableNegligibleSeverity int64 `json:"numOfFixAvailableNegligibleSeverity"`
NumOfFixAvailableLowSeverity int64 `json:"numOfFixAvailableLowSeverity"`
NumOfFixAvailableMediumSeverity int64 `json:"numOfFixAvailableMediumSeverity"`
NumOfFixAvailableHighSeverity int64 `json:"numOfFixAvailableHighSeverity"`
NumOfFixAvailableCriticalSeverity int64 `json:"numOfFixAvailableCriticalSeverity"`
NumOfRelevantIssues int64 `json:"numOfRelevantIssues"`
NumOfIrelevantIssues int64 `json:"numOfIrelevantIssues"`
NumOfUnknownIssues int64 `json:"numOfUnknownIssues"`
NumOfLeakedSecrets int64 `json:"numOfLeakedSecrets"`
Version string `json:"version"`
History []ContainerScanHistoryEntry `json:"history",omitempty`
}
type RelevanciesSum struct {
Relevancy string `json:"relevancy"`
Sum int64 `json:"sum"`
}
type ContainerScanHistoryEntry struct {
ContainerScanID string `json:"containerScanID"`
Timestamp int64 `json:"timestamp"`
}
func (summary *ElasticContainerScanSummaryResult) Validate() bool {
return summary.CustomerGUID != "" && summary.ContainerScanID != "" && (summary.ImgTag != "" || summary.ImgHash != "") && summary.Timestamp > 0
}

View File

@@ -1,271 +0,0 @@
package containerscan
import (
"github.com/francoispqt/gojay"
)
/*
responsible on fast unmarshaling of various COMMON containerscan structures and substructures
*/
// //VulnerabilitiesList -s.e
// type LayersList []ScanResultLayer
// //VulnerabilitiesList -s.e
// type VulnerabilitiesList []Vulnerability
// //LinuxPkgs - slice of linux pkgs
// type LinuxPkgs []LinuxPackage
// //VulFixes - information bout when/how this vul was fixed
// type VulFixes []FixedIn
// //PkgFiles - slice of files belong to specific pkg
// type PkgFiles []PackageFile
// UnmarshalJSONObject - File inside a pkg
func (file *PackageFile) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) {
switch key {
case "name":
err = dec.String(&(file.Filename))
}
return err
}
func (files *PkgFiles) UnmarshalJSONArray(dec *gojay.Decoder) error {
lae := PackageFile{}
if err := dec.Object(&lae); err != nil {
return err
}
*files = append(*files, lae)
return nil
}
func (file *PackageFile) NKeys() int {
return 0
}
// UnmarshalJSONObject--- Package
func (pkgnx *LinuxPackage) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) {
switch key {
case "packageName":
err = dec.String(&(pkgnx.PackageName))
case "version":
err = dec.String(&(pkgnx.PackageVersion))
case "files":
err = dec.Array(&(pkgnx.Files))
}
return err
}
func (file *LinuxPackage) NKeys() int {
return 0
}
func (pkgs *LinuxPkgs) UnmarshalJSONArray(dec *gojay.Decoder) error {
lae := LinuxPackage{}
if err := dec.Object(&lae); err != nil {
return err
}
*pkgs = append(*pkgs, lae)
return nil
}
//--------Vul fixed in----------------------------------
func (fx *FixedIn) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) {
switch key {
case "name":
err = dec.String(&(fx.Name))
case "imageTag":
err = dec.String(&(fx.ImgTag))
case "version":
err = dec.String(&(fx.Version))
}
return err
}
func (t *VulFixes) UnmarshalJSONArray(dec *gojay.Decoder) error {
lae := FixedIn{}
if err := dec.Object(&lae); err != nil {
return err
}
*t = append(*t, lae)
return nil
}
func (file *FixedIn) NKeys() int {
return 0
}
//------ VULNERABIlITy ---------------------
// Name string `json:"name"`
// ImgHash string `json:"imageHash"`
// ImgTag string `json:"imageTag",omitempty`
// RelatedPackageName string `json:"packageName"`
// PackageVersion string `json:"packageVersion"`
// Link string `json:"link"`
// Description string `json:"description"`
// Severity string `json:"severity"`
// Metadata interface{} `json:"metadata",omitempty`
// Fixes VulFixes `json:"fixedIn",omitempty`
// Relevancy string `json:"relevant"` // use the related enum
func (v *Vulnerability) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) {
switch key {
case "name":
err = dec.String(&(v.Name))
case "imageTag":
err = dec.String(&(v.ImgTag))
case "imageHash":
err = dec.String(&(v.ImgHash))
case "packageName":
err = dec.String(&(v.RelatedPackageName))
case "packageVersion":
err = dec.String(&(v.PackageVersion))
case "link":
err = dec.String(&(v.Link))
case "description":
err = dec.String(&(v.Description))
case "severity":
err = dec.String(&(v.Severity))
case "relevant":
err = dec.String(&(v.Relevancy))
case "fixedIn":
err = dec.Array(&(v.Fixes))
case "metadata":
err = dec.Interface(&(v.Metadata))
}
return err
}
func (t *VulnerabilitiesList) UnmarshalJSONArray(dec *gojay.Decoder) error {
lae := Vulnerability{}
if err := dec.Object(&lae); err != nil {
return err
}
*t = append(*t, lae)
return nil
}
func (v *Vulnerability) NKeys() int {
return 0
}
//---------Layer Object----------------------------------
// type ScanResultLayer struct {
// LayerHash string `json:layerHash`
// Vulnerabilities []Vulnerability `json:vulnerabilities`
// Packages []LinuxPackage `json:packageToFile`
// }
func (scan *ScanResultLayer) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) {
switch key {
// case "timestamp":
// err = dec.Time(&(reporter.Timestamp), time.RFC3339)
// reporter.Timestamp = reporter.Timestamp.Local()
case "layerHash":
err = dec.String(&(scan.LayerHash))
case "parentLayerHash":
err = dec.String(&(scan.ParentLayerHash))
case "vulnerabilities":
err = dec.Array(&(scan.Vulnerabilities))
case "packageToFile":
err = dec.Array(&(scan.Packages))
}
return err
}
func (t *LayersList) UnmarshalJSONArray(dec *gojay.Decoder) error {
lae := ScanResultLayer{}
if err := dec.Object(&lae); err != nil {
return err
}
*t = append(*t, lae)
return nil
}
func (scan *ScanResultLayer) NKeys() int {
return 0
}
//---------------------SCAN RESULT--------------------------------------------------------------------------
// type ScanResultReport struct {
// CustomerGUID string `json:customerGuid`
// ImgTag string `json:imageTag,omitempty`
// ImgHash string `json:imageHash`
// WLID string `json:wlid`
// Timestamp int `json:customerGuid`
// Layers []ScanResultLayer `json:layers`
// ContainerName
// }
func (scan *ScanResultReport) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) {
switch key {
// case "timestamp":
// err = dec.Time(&(reporter.Timestamp), time.RFC3339)
// reporter.Timestamp = reporter.Timestamp.Local()
case "customerGUID":
err = dec.String(&(scan.CustomerGUID))
case "imageTag":
err = dec.String(&(scan.ImgTag))
case "imageHash":
err = dec.String(&(scan.ImgHash))
case "wlid":
err = dec.String(&(scan.WLID))
case "containerName":
err = dec.String(&(scan.ContainerName))
case "timestamp":
err = dec.Int64(&(scan.Timestamp))
case "layers":
err = dec.Array(&(scan.Layers))
case "listOfDangerousArtifcats":
err = dec.SliceString(&(scan.ListOfDangerousArtifcats))
}
return err
}
// func (errors *[]string) UnmarshalJSONArray(dec *gojay.Decoder) error {
// lae := ""
// if err := dec.String(&lae); err != nil {
// return err
// }
// *t = append(*t, lae)
// return nil
// }
func (scan *ScanResultReport) NKeys() int {
return 0
}

View File

@@ -1,29 +0,0 @@
module github.com/armosec/capacketsgo
go 1.16
require (
github.com/armosec/armopa v0.0.4
github.com/aws/aws-sdk-go v1.40.18
github.com/coreos/go-oidc v2.2.1+incompatible
github.com/docker/docker v20.10.8+incompatible
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
// github.com/elastic/go-elasticsearch v0.0.0
github.com/elastic/go-elasticsearch/v7 v7.14.0
github.com/francoispqt/gojay v1.2.13
github.com/gofrs/uuid v4.0.0+incompatible
github.com/golang/glog v0.0.0-20210429001901-424d2337a529
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
//github.com/open-policy-agent/opa v0.27.1
github.com/satori/go.uuid v1.2.0
go.uber.org/zap v1.19.0
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22
k8s.io/api v0.22.0
k8s.io/apimachinery v0.22.0
k8s.io/apiserver v0.22.0
k8s.io/client-go v0.22.0
)

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,109 +0,0 @@
package k8sinterface
import (
"context"
"fmt"
"github.com/armosec/capacketsgo/secrethandling"
"github.com/docker/docker/api/types"
"github.com/golang/glog"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func listPodImagePullSecrets(pod *corev1.Pod) ([]string, error) {
if pod == nil {
return []string{}, fmt.Errorf("in listPodImagePullSecrets pod is nil")
}
secrets := []string{}
for _, i := range pod.Spec.ImagePullSecrets {
secrets = append(secrets, i.Name)
}
return secrets, nil
}
func listServiceAccountImagePullSecrets(k8sAPI *KubernetesApi, pod *corev1.Pod) ([]string, error) {
if pod == nil {
return []string{}, fmt.Errorf("in listServiceAccountImagePullSecrets pod is nil")
}
secrets := []string{}
serviceAccountName := pod.Spec.ServiceAccountName
if serviceAccountName == "" {
return secrets, nil
}
serviceAccount, err := k8sAPI.KubernetesClient.CoreV1().ServiceAccounts(pod.ObjectMeta.Namespace).Get(k8sAPI.Context, serviceAccountName, metav1.GetOptions{})
if err != nil {
return secrets, fmt.Errorf("in listServiceAccountImagePullSecrets failed to get ServiceAccounts: %v", err)
}
for i := range serviceAccount.ImagePullSecrets {
secrets = append(secrets, serviceAccount.ImagePullSecrets[i].Name)
}
return secrets, nil
}
func getImagePullSecret(k8sAPI *KubernetesApi, secrets []string, namespace string) map[string]types.AuthConfig {
secretsAuthConfig := make(map[string]types.AuthConfig)
for i := range secrets {
res, err := k8sAPI.KubernetesClient.CoreV1().Secrets(namespace).Get(context.Background(), secrets[i], metav1.GetOptions{})
if err != nil {
glog.Errorf("%s", err.Error())
continue
}
sec, err := secrethandling.ParseSecret(res, secrets[i])
if err == nil {
secretsAuthConfig[secrets[i]] = *sec
} else {
glog.Errorf("unable to get secret: %s", err.Error())
}
}
// glog.Infof("secrets array: %v", secretsAuthConfig)
return secretsAuthConfig
}
// GetImageRegistryCredentials returns various credentials for images in the pod
// imageTag empty means returns all of the credentials for all images in pod spec containers
// pod.ObjectMeta.Namespace must be well setted
func GetImageRegistryCredentials(imageTag string, pod *corev1.Pod) (map[string]types.AuthConfig, error) {
k8sAPI := NewKubernetesApi()
listSecret, _ := listPodImagePullSecrets(pod)
listServiceSecret, _ := listServiceAccountImagePullSecrets(k8sAPI, pod)
listSecret = append(listSecret, listServiceSecret...)
secrets := getImagePullSecret(k8sAPI, listSecret, pod.ObjectMeta.Namespace)
if len(secrets) == 0 {
secrets = make(map[string]types.AuthConfig)
}
if imageTag != "" {
cloudVendorSecrets, err := GetCloudVendorRegistryCredentials(imageTag)
if err != nil {
glog.Errorf("Failed to GetCloudVendorRegistryCredentials(%s): %v", imageTag, err)
} else if len(cloudVendorSecrets) > 0 {
for secName := range cloudVendorSecrets {
secrets[secName] = cloudVendorSecrets[secName]
}
}
} else {
for contIdx := range pod.Spec.Containers {
imageTag := pod.Spec.Containers[contIdx].Image
glog.Infof("GetCloudVendorRegistryCredentials for image: %v", imageTag)
cloudVendorSecrets, err := GetCloudVendorRegistryCredentials(imageTag)
if err != nil {
glog.Errorf("Failed to GetCloudVendorRegistryCredentials(%s): %v", imageTag, err)
} else if len(cloudVendorSecrets) > 0 {
for secName := range cloudVendorSecrets {
secrets[secName] = cloudVendorSecrets[secName]
}
}
}
}
return secrets, nil
}

View File

@@ -1,73 +0,0 @@
package k8sinterface
import (
"context"
"fmt"
"os"
"path/filepath"
"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"
)
// K8SConfig pointer to k8s config
var K8SConfig *restclient.Config
// KubernetesApi -
type KubernetesApi struct {
KubernetesClient kubernetes.Interface
DynamicClient dynamic.Interface
Context context.Context
}
// NewKubernetesApi -
func NewKubernetesApi() *KubernetesApi {
kubernetesClient, err := kubernetes.NewForConfig(GetK8sConfig())
if err != nil {
panic(fmt.Sprintf("kubernetes.NewForConfig - Failed to load config file, reason: %s", err.Error()))
}
dynamicClient, err := dynamic.NewForConfig(GetK8sConfig())
if err != nil {
panic(fmt.Sprintf("dynamic.NewForConfig - Failed to load config file, reason: %s", err.Error()))
}
return &KubernetesApi{
KubernetesClient: kubernetesClient,
DynamicClient: dynamicClient,
Context: context.Background(),
}
}
var ConfigPath = filepath.Join(os.Getenv("HOME"), ".kube", "config")
var RunningIncluster bool
// LoadK8sConfig load config from local file or from cluster
func LoadK8sConfig() error {
kubeconfig, err := clientcmd.BuildConfigFromFlags("", ConfigPath)
if err != nil {
kubeconfig, err = restclient.InClusterConfig()
if err != nil {
return fmt.Errorf("Failed to load kubernetes config from file: '%s', err: %v", ConfigPath, err)
}
RunningIncluster = true
} else {
RunningIncluster = false
}
K8SConfig = kubeconfig
return nil
}
// GetK8sConfig get config. load if not loaded yer
func GetK8sConfig() *restclient.Config {
if K8SConfig == nil {
if err := LoadK8sConfig(); err != nil {
return nil
}
}
return K8SConfig
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1 +0,0 @@
hold all shared k8s wrappers or k8s related utilities that are shared across projects

View File

@@ -1,59 +0,0 @@
package k8sshared
import (
"fmt"
"github.com/francoispqt/gojay"
)
// CAClusterName string `json:"caClusterName"`
// CANamespace string `json:"caNamespace"`
// Event json.RawMessage `json:"k8sV1Event"`
// UnmarshalJSONObject - File inside a pkg
func (l *K8sAuditLog) UnmarshalJSONObject(dec *gojay.Decoder, key string) (err error) {
switch key {
case "caClusterName":
err = dec.String(&(l.CAClusterName))
case "caNamespace":
err = dec.String(&(l.CANamespace))
case "k8sV1Event":
var tmp gojay.EmbeddedJSON
if err = dec.AddEmbeddedJSON(&tmp); err != nil {
return fmt.Errorf("failed to UnmarshalJSONObject k8sV1Event, error: %v", err)
}
l.Event = []byte(tmp)
return nil
}
return err
}
func (logs *K8sAuditLogs) UnmarshalJSONArray(dec *gojay.Decoder) error {
lae := K8sAuditLog{}
if err := dec.Object(&lae); err != nil {
return err
}
*logs = append(*logs, lae)
return nil
}
// func (logs []K8sAuditLog) UnmarshalJSONArray(dec *gojay.Decoder) error {
// lae := K8sAuditLog{}
// if err := dec.Object(&lae); err != nil {
// return err
// }
// logs = append(logs, lae)
// return nil
// }
func (file *K8sAuditLog) NKeys() int {
return 0
}

View File

@@ -1,51 +0,0 @@
package k8sshared
import (
"encoding/json"
audit "k8s.io/apiserver/pkg/apis/audit"
)
func GetEventAuditMockAsString() string {
return `{
"kind": "Event",
"apiVersion": "audit.k8s.io/v1",
"level": "Metadata",
"auditID": "1847e1e1-d66b-4661-b458-4dc553cd8539",
"stage": "ResponseComplete",
"requestURI": "/apis/storage.k8s.io/v1?timeout=32s",
"verb": "get",
"user": {
"username": "system:serviceaccount:kube-system:generic-garbage-collector",
"uid": "83093a4c-3f5f-433e-8fd4-4a2cc23eead8",
"groups": [
"system:serviceaccounts",
"system:serviceaccounts:kube-system",
"system:authenticated"
]
},
"sourceIPs": [
"192.168.49.2"
],
"userAgent": "kube-controller-manager/v1.20.0 (linux/amd64) kubernetes/af46c47/system:serviceaccount:kube-system:generic-garbage-collector",
"responseStatus": {
"metadata": {},
"code": 200
},
"requestReceivedTimestamp": "2021-02-18T08:28:43.237861Z",
"stageTimestamp": "2021-02-18T08:28:43.238551Z",
"annotations": {
"authentication.k8s.io/legacy-token": "system:serviceaccount:kube-system:generic-garbage-collector",
"authorization.k8s.io/decision": "allow",
"authorization.k8s.io/reason": "RBAC: allowed by ClusterRoleBinding \"system:discovery\" of ClusterRole \"system:discovery\" to Group \"system:authenticated\""
}
}`
}
func GetK8sAuditEventMock() (audit.Event, error) {
tmp := audit.Event{}
a := []byte(GetEventAuditMockAsString())
err := json.Unmarshal(a, &tmp)
return tmp, err
}

View File

@@ -1,44 +0,0 @@
package k8sshared
import (
"encoding/json"
"fmt"
"testing"
"github.com/armosec/capacketsgo/cautils"
)
func TestAuditStructure(t *testing.T) {
auditRAW, err := GetK8sAuditEventMock()
if err != nil {
t.Errorf("failed to get unmarshald mock %v", err.Error())
}
audit, err := Newk8sAuditLog("testcluster", "", &auditRAW)
if err != nil {
t.Errorf("failed to create ca-k8s-audit object due to : %v", err.Error())
}
res, err := json.Marshal(audit)
if err != nil {
t.Errorf("failed to get marshal audit wrapper %v", err.Error())
}
fmt.Printf("\n\nres: %v\n\n", string(res))
audit2 := K8sAuditLog{}
json.Unmarshal(res, &audit2)
if cautils.AsSHA256(audit2) != cautils.AsSHA256(*audit) {
t.Errorf("failed to get umarshal(marshal audit wrapper)\n========audit2=======\n%v\n\noriginal:\n:%v", audit2, audit)
}
auditRAW2 := audit2.GetRawK8sEvent()
if cautils.AsSHA256(*auditRAW2) != cautils.AsSHA256(auditRAW) {
t.Errorf("failed to get raw audit is different from k8s original audit:\nreplacement:\n%v\n\noriginal: %v", *auditRAW2, auditRAW)
}
}

View File

@@ -1,5 +0,0 @@
package k8sshared
const (
K8sAuditLogPrefix = "_k8sAuditLogs"
)

View File

@@ -1,25 +0,0 @@
package probes
import (
"fmt"
"net/http"
)
// server initialization
const (
ReadinessPath = "readiness"
ReadinessPort = "8000"
)
// InitReadinessV1 initialize readiness handler
func InitReadinessV1(isReadinessReady *bool) {
http.HandleFunc(fmt.Sprintf("/v1/%s", ReadinessPath), func(w http.ResponseWriter, _ *http.Request) {
if *isReadinessReady {
w.WriteHeader(http.StatusOK)
} else {
w.WriteHeader(http.StatusServiceUnavailable)
}
},
)
http.ListenAndServe(":8000", nil)
}

View File

@@ -1,41 +0,0 @@
package k8sshared
import (
"encoding/json"
"fmt"
k8saudit "k8s.io/apiserver/pkg/apis/audit"
)
// K8sAuditLog - ARMO audit event wrapper
type K8sAuditLog struct {
CAClusterName string `json:"caClusterName"`
CANamespace string `json:"caNamespace"`
Event json.RawMessage `json:"k8sV1Event"`
}
//K8sAuditLogs - slice of K8sAuditLog
type K8sAuditLogs []K8sAuditLog
func (v *K8sAuditLog) Validate() bool {
return len(v.CAClusterName) > 0
}
func (v *K8sAuditLog) GetRawK8sEvent() *k8saudit.Event {
tmp := &k8saudit.Event{}
json.Unmarshal(v.Event, &tmp)
return tmp
}
func Newk8sAuditLog(cluster, namespace string, auditRAW *k8saudit.Event) (*K8sAuditLog, error) {
audit := &K8sAuditLog{CAClusterName: cluster, CANamespace: namespace}
b, err := json.Marshal(*auditRAW)
if err != nil {
return nil, fmt.Errorf("failed to marshal audit event, reason: %s", err.Error())
}
audit.Event = b
return audit, nil
}

View File

@@ -1,18 +0,0 @@
package notificationserver
// server paths
const (
PathWebsocketV1 = "/v1/waitfornotification"
PathRESTV1 = "/v1/sendnotification"
)
const (
TargetCustomer = "customerGUID"
TargetCluster = "clusterName"
TargetComponent = "clusterComponent"
)
const (
TargetComponentPostureValue = "PolicyValidator"
TargetComponentLoggerValue = "Logger"
)

View File

@@ -1,8 +0,0 @@
package notificationserver
// Notification passed between servers
type Notification struct {
Target map[string]string `json:"target"`
SendSynchronicity bool `json:"sendSynchronicity"`
Notification interface{} `json:"notification"`
}

View File

@@ -1,12 +0,0 @@
package notificationserver
func MockNotificationA() *Notification {
return &Notification{
Target: map[string]string{
TargetCluster: "",
TargetCustomer: "",
TargetComponent: TargetComponentPostureValue,
},
Notification: nil,
}
}

View File

@@ -1,105 +0,0 @@
package notificationserver
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/golang/glog"
"gopkg.in/mgo.v2/bson"
)
// PushNotificationServer push notification to rest api server. if jsonFormat is set to false, will Marshal useing bson
func PushNotificationServer(edgeURL string, targetMap map[string]string, message interface{}, jsonFormat bool) error {
var err error
glog.Infof("Pushing notification to: '%s'", edgeURL)
// setup notification
notf, err := setNotification(targetMap, message, jsonFormat)
if err != nil {
return err
}
// push notification
client := http.Client{}
for i := 0; i < 3; i++ {
if err = sendCommandToEdge(&client, edgeURL, notf); err == nil {
return nil
}
time.Sleep(1 * time.Second)
err = fmt.Errorf("error sending url: '%s', reason: %s", edgeURL, err.Error())
}
return err
}
// sendCommandToEdge sends the HTTP request
func sendCommandToEdge(client *http.Client, edgeURL string, message []byte) error {
defer func() {
if err := recover(); err != nil {
glog.Errorf("In sendCommandToEdge, recover, reason: %v", err)
}
}()
req, err := http.NewRequest("POST", edgeURL, bytes.NewReader(message))
req.Close = true
if err != nil {
return fmt.Errorf("failed to SendCommandToCluster, url: %s, data: %s, reason: %s", edgeURL, string(message), err.Error())
}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to SendCommandToCluster, url: %s, data: %s, reason: %s", edgeURL, string(message), err.Error())
}
defer resp.Body.Close()
respStr, err := httpRespToString(resp)
if err != nil {
return fmt.Errorf("failed to SendCommandToCluster, url: %s, data: %s, reason: %s, response: %s", edgeURL, string(message), err.Error(), respStr)
}
return nil
}
func setNotification(targetMap map[string]string, message interface{}, jsonFormat bool) ([]byte, error) {
notification := Notification{
Target: targetMap,
Notification: message,
}
var err error
var m []byte
if jsonFormat {
if m, err = json.Marshal(notification); err != nil {
err = fmt.Errorf("failed marshling message to bson. message: '%v', reason: '%s'", notification, err.Error())
}
} else {
if m, err = bson.Marshal(notification); err != nil {
err = fmt.Errorf("failed marshling message to bson. message: '%v', reason: '%s'", notification, err.Error())
}
}
return m, err
}
// HTTPRespToString parses the body as string and checks the HTTP status code
func httpRespToString(resp *http.Response) (string, error) {
if resp == nil {
return "", fmt.Errorf("empty response")
}
strBuilder := strings.Builder{}
if resp.ContentLength > 0 {
strBuilder.Grow(int(resp.ContentLength))
}
_, err := io.Copy(&strBuilder, resp.Body)
if err != nil {
return strBuilder.String(), err
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
err = fmt.Errorf("Response status: %d. content: %s", resp.StatusCode, strBuilder.String())
}
return strBuilder.String(), err
}

View File

@@ -1,58 +0,0 @@
package ocimage
import (
"fmt"
"net/url"
"strings"
"github.com/docker/docker/api/types"
"github.com/golang/glog"
)
// URLEncoder encode url
func URLEncoder(oldURL string) string {
fullURL := strings.Split(oldURL, "?")
baseURL, err := url.Parse(fullURL[0])
if err != nil {
return ""
}
// Prepare Query Parameters
if len(fullURL) > 1 {
params := url.Values{}
queryParams := strings.Split(fullURL[1], "&")
for _, i := range queryParams {
queryParam := strings.Split(i, "=")
val := ""
if len(queryParam) > 1 {
val = queryParam[1]
}
params.Add(queryParam[0], val)
}
baseURL.RawQuery = params.Encode()
}
return baseURL.String()
}
// GetSecuredImageID - gets imagename+tag or with full repo, secrets map and returns the imageid
func (ocimg *OCImage) GetSecuredImageID(imageName string, secrets map[string]types.AuthConfig) (string, error) {
glog.Infof("trying to get Img: %v using secrets", imageName)
for secretName, regAuth := range secrets {
// If server address is known, then try pulling image based on sever address, otherwise try using all secretes
if regAuth.ServerAddress == "" || strings.HasPrefix(imageName, regAuth.ServerAddress) {
glog.Infof("Pulling image '%s' using '%s' secret", imageName, secretName)
// Pulling image with credentials
imageid, err := ocimg.GetImage(imageName, regAuth.Username, regAuth.Password)
if err == nil {
glog.Infof("Pulling image '%s' using secret succeeded, image id: %s", imageName, imageid)
return imageid, nil
}
}
}
return "", fmt.Errorf("failed to pull image '%s' using secrets, secrets: '%v'", imageName, secrets)
}

View File

@@ -1,109 +0,0 @@
package ocimage
// import (
// "fmt"
// "io"
// "os"
// "testing"
// )
// func base(img, usr, pass string) (*OCImage, string, error) {
// baseURL := "http://10.107.26.199:8080"
// oci := MakeOCImage(baseURL)
// imgid, err := oci.GetImage(img, usr, pass)
// return oci, imgid, err
// }
// func TestGetSingleFile(t *testing.T) {
// fmt.Printf("do nothing")
// oci, imgid, err := base("nginx:latest", "", "")
// if err != nil {
// t.Errorf("can't get image ")
// }
// os, s, err := oci.GetSingleFile(imgid, "/etc/os-release", true)
// if err != nil {
// t.Errorf("couldnt get file %s", err.Error())
// }
// fmt.Printf("file content: %s\n%s\n", string(os), s)
// t.Errorf("f")
// }
// func TestManifest(t *testing.T) {
// fmt.Printf("do nothing")
// oci, imgid, err := base("nginx:latest", "", "")
// if err != nil {
// t.Errorf("can't get image ")
// }
// manifest, err := oci.GetManifest(imgid)
// if err != nil {
// t.Errorf("couldnt get file %s", err.Error())
// }
// fmt.Printf("manifest content: %v\n\n", manifest)
// t.Errorf("f")
// }
// //gets 404 when no files are found
// func TestMultipleFilesNonExisting(t *testing.T) {
// fmt.Printf("do nothing")
// oci, imgid, err := base("nginx:latest", "", "")
// if err != nil {
// t.Errorf("can't get image ")
// }
// filestar, err := oci.GetMultipleFiles(imgid, []string{"/ethhhc/os-release", "ngjjjinx"}, true, false)
// if err != nil {
// t.Errorf("couldnt get file %s", err.Error())
// return
// }
// for {
// tarHdr, err := filestar.Next()
// if err == io.EOF {
// break
// }
// if err != nil {
// t.Errorf("error: %s", err.Error())
// continue
// }
// fmt.Printf("Contents of %s:\n", tarHdr.Name)
// if _, err := io.Copy(os.Stdout, filestar); err != nil {
// t.Errorf("error: %s", err.Error())
// }
// fmt.Printf("%v\n", tarHdr)
// }
// t.Errorf("f")
// }
// //gets Symlink mapper as usual (missing files has no key)
// func TestMultipleFilesPartialExisting(t *testing.T) {
// fmt.Printf("do nothing")
// oci, imgid, err := base("nginx:latest", "", "")
// if err != nil {
// t.Errorf("can't get image ")
// }
// filestar, err := oci.GetMultipleFiles(imgid, []string{"/etc/os-release", "ngjjjinx"}, true, false)
// if err != nil {
// t.Errorf("couldnt get file %s", err.Error())
// return
// }
// for {
// tarHdr, err := filestar.Next()
// if err == io.EOF {
// break
// }
// if err != nil {
// t.Errorf("error: %s", err.Error())
// continue
// }
// fmt.Printf("Contents of %s:\n", tarHdr.Name)
// if _, err := io.Copy(os.Stdout, filestar); err != nil {
// t.Errorf("error: %s", err.Error())
// }
// fmt.Printf("%v\n", tarHdr)
// }
// t.Errorf("f")
// }

View File

@@ -1,284 +0,0 @@
package ocimage
import (
"archive/tar"
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strconv"
"strings"
"time"
"github.com/golang/glog"
)
var MAX_RETRIES int = 3
// IOCImage - ocimage interface - https://asterix.cyberarmor.io/cyberarmor/ocimage
type IOCImage interface {
GetImage(imageTag, user, password string) (string, error)
GetSingleFile(fileName string, followSymLink bool) ([]byte, string, error)
GetMultipleFiles(fileNames []string, followSymLink, doesExist bool) ([]byte, error)
GetClient() *http.Client
FileList(imageid string, dir string, from int, to int, recursive bool, noDir bool) ([]FileMetadata, error)
Describe(imageID string) (*ImageMetadata, error)
}
// OCImage - structure, holds url and api version
type OCImage struct {
url string
apiVer string
client *http.Client
}
func (oci *OCImage) GetClient() *http.Client {
return oci.client
}
// Init - init
func MakeOCImage(ociURL string) *OCImage {
oci := &OCImage{url: ociURL, apiVer: "v1", client: &http.Client{}}
return oci
}
func (oci *OCImage) GetManifest(imageid string) (*OciImageManifest, error) {
newurl := fmt.Sprintf("%s/%s/images/id/%s/manifest", oci.url, oci.apiVer, imageid)
req, _ := http.NewRequest("GET", newurl, nil)
req.Header.Set("Content-Type", "application/json")
resp, err := oci.GetClient().Do(req)
if err != nil {
return nil, fmt.Errorf("getting manifest for imageid: %s failed due to: %s", imageid, err.Error())
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("getting manifest for imageid: %s failed due to: status code %v %v", imageid, resp.StatusCode, resp.Status)
}
jsonRaw, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
manifest := OciImageManifest{}
if err := json.Unmarshal(jsonRaw, &manifest); err != nil {
return nil, err
}
return &manifest, nil
}
// GetImage -
func (oci *OCImage) GetImage(imageTag, user, password string) (string, error) {
newurl := oci.url + "/" + oci.apiVer + "/images/id"
values := map[string]string{"image": imageTag}
if len(user) != 0 && len(password) != 0 {
values["username"] = user
values["password"] = password
}
jsonValue, err := json.Marshal(values)
if err != nil {
return "", fmt.Errorf("failed to marshal getImage request, reason: %s", err.Error())
}
glog.Infof("OCI GetImage, url: '%s'", newurl)
for i := 0; i < MAX_RETRIES; i++ {
resp, err := http.Post(newurl, "application/json", bytes.NewBuffer(jsonValue))
if err != nil {
glog.Infof("In GetImage oci, url: '%s', failed. retry: %d, reason: %s", newurl, i, err.Error())
time.Sleep(1 * time.Second)
continue
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
bodyString := string(body)
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
return bodyString, nil
} else {
glog.Errorf("requesting: %v - will retry error (%s): %s", newurl, resp.Status, bodyString)
}
}
return "", fmt.Errorf("request '%s' failed, reason: max retries exceeded", newurl)
}
// FileList - ls = the containerized version
func (oci *OCImage) FileList(imageid string, dir string, from int, to int, recursive bool, noDir bool) ([]FileMetadata, error) {
newurl := oci.url + "/" + oci.apiVer + "/images/id/" + imageid + "/list"
fmt.Printf("%v %v %v %v %v %v $v", newurl, dir, from, to, recursive, noDir)
var slashwrist []FileMetadata
req, _ := http.NewRequest("GET", newurl, nil)
req.Header.Add("Accept", "application/json")
q := req.URL.Query()
if len(dir) > 0 {
q.Add("dir", dir)
}
if from < to || to == -1 {
fromstr := strconv.Itoa(from)
tostr := strconv.Itoa(to)
q.Add("from", fromstr)
q.Add("to", tostr)
}
q.Add("recursive", strconv.FormatBool(recursive))
q.Add("no_dir", strconv.FormatBool(noDir))
req.URL.RawQuery = q.Encode()
glog.Infof("OCI FileList, url: '%s'", req.URL.String())
for i := 0; i < MAX_RETRIES; i++ {
resp, err := oci.GetClient().Do(req)
if err != nil {
glog.Errorf("requesting: %v - will retry error: %v", newurl, err.Error())
}
defer resp.Body.Close()
if respBody, err := ioutil.ReadAll(resp.Body); err == nil {
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
if err := json.Unmarshal(respBody, &slashwrist); err != nil {
return slashwrist, fmt.Errorf("failed to marshal fileList response, reason: %s", err.Error())
}
return slashwrist, nil
} else {
glog.Errorf("requesting: %v - will retry error (%s): %s", newurl, resp.Status, respBody)
}
} else {
glog.Errorf("requesting: %v - will retry error: %v", newurl, err.Error())
}
}
return nil, fmt.Errorf("request '%s' failed, reason: max retries exceeded", newurl)
}
// Describe -
func (oci *OCImage) Describe(imageid string) (ImageMetadata, error) {
newurl := oci.url + "/" + oci.apiVer + "/images/id/" + imageid
glog.Infof("OCI Describe, url: '%s'", newurl)
var slashwrist ImageMetadata
req, _ := http.NewRequest("GET", newurl, nil)
req.Header.Add("Accept", "application/json")
for i := 0; i < MAX_RETRIES; i++ {
resp, err := oci.GetClient().Do(req)
if err == nil {
defer resp.Body.Close()
if respBody, err := ioutil.ReadAll(resp.Body); err == nil {
if err := json.Unmarshal(respBody, &slashwrist); err != nil {
return slashwrist, fmt.Errorf("failed to unmarshal describe response, reason: %s", err.Error())
}
return slashwrist, nil
} else {
glog.Errorf("requesting: %s - will retry error: %v", newurl, err.Error())
}
} else {
glog.Errorf("requesting: %s - will retry error: %v", newurl, err.Error())
}
}
return slashwrist, fmt.Errorf("request '%s' failed, reason: max retries exceeded", newurl)
}
func (oci *OCImage) GetMultipleFiles(imageid string, fileNames []string, followSymLink, doesExist bool) (*tar.Reader, error) {
if len(fileNames) == 0 || len(imageid) == 0 {
return nil, fmt.Errorf("bad usage: u must specify non-empty filelist and imageid ")
}
newurl := oci.url + "/" + oci.apiVer + "/images/id/" + imageid + "/files"
req, err := http.NewRequest("GET", newurl, nil)
if err != nil {
return nil, err
}
req.Header.Add("Accept", "octet-stream")
q := req.URL.Query()
q.Add("followSymLink", strconv.FormatBool(followSymLink))
q.Add("doesExist", strconv.FormatBool(doesExist))
for _, filename := range fileNames {
q.Add("file", filename)
}
req.URL.RawQuery = q.Encode()
resp, err := oci.GetClient().Do(req)
if err != nil {
err = fmt.Errorf("error requesting file '%s' from server reason: %s", fileNames, err.Error())
glog.Errorf(err.Error())
return nil, err
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("error has occurred: " + resp.Status)
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("error: failed to read imageid %s files requested %v due to %s", imageid, fileNames, err.Error())
}
reader := bytes.NewReader(data)
filestar := tar.NewReader(reader)
return filestar, nil
}
// GetFile -
func (oci *OCImage) GetSingleFile(imageid string, filepath string, followSymLink bool) ([]byte, string, error) {
newurl := oci.url + "/" + oci.apiVer + "/images/id/" + imageid + "/files/" + filepath
glog.Infof("Requesting from OCI: '%s'", newurl)
var slashwrist []byte
client := &http.Client{}
req, err := http.NewRequest("GET", newurl, nil)
if err != nil {
return slashwrist, "", err
}
req.Header.Add("Accept", "octet-stream")
q := req.URL.Query()
q.Add("followSymLink", strconv.FormatBool(followSymLink))
req.URL.RawQuery = q.Encode()
resp, err := client.Do(req)
if err != nil {
err = fmt.Errorf("error requesting file '%s' from server reason: %s", filepath, err.Error())
glog.Errorf(err.Error())
return slashwrist, "", err
}
if resp.StatusCode != http.StatusOK {
return slashwrist, "error has occurred: " + resp.Status, fmt.Errorf("error has occurred: " + resp.Status)
}
defer resp.Body.Close()
respBody, err := ioutil.ReadAll(resp.Body)
return respBody, "success", err
}
// GetFile -
func (oci *OCImage) GetFileWithRetries(imageid string, filepath string, followSymLink bool) ([]byte, string, error) {
retry := 0
for {
respBody, status, err := oci.GetSingleFile(imageid, filepath, followSymLink)
if err != nil && strings.Contains(err.Error(), "EOF") && retry < MAX_RETRIES {
glog.Warningf("Request: '%s', received 'EOF'. Retying", filepath)
retry++
time.Sleep(1 * time.Second)
} else {
return respBody, status, err
}
}
}

View File

@@ -1,45 +0,0 @@
package ocimage
import (
"encoding/json"
"strings"
)
// import "encoding/json"
// OCImageMock -
type OCImageMock struct {
}
// GetImage -
func (ocim *OCImageMock) GetImage(imageTag, user, password string) (string, error) {
return "5ac6aae02c212cafb36e853dcfe366bac4b1c1097fe8c10c923842f09c8bf7e4", nil
}
// // FileList - mock
func (ocim *OCImageMock) FileList(imageid string, dir string, from int, to int, recursive bool, noDir bool) ([]FileMetadata, error) {
listOfFiles := []FileMetadata{}
list := `[{"isSymbolicLink":false,"layer":"sha256:f010348cae17a90a12165366416cb15c9606ea63a3735f9d967b843d33865f31","link":"","name":"etc/nginx","path":"etc/nginx","permissions":"0o40755"},{"isSymbolicLink":false,"layer":"sha256:1ce95ec4847ff9d80847f0a1836135255742c2160bc4ba52c829dfbc68a93291","link":"","name":"etc/apk","path":"etc/apk","permissions":"0o40755"},{"isSymbolicLink":false,"layer":"sha256:62bed320c887a0e141341a598b3a754c288bc91a15e66c7e1d10a941f63bc0c1","link":"","name":"etc/supervisor.d","path":"etc/supervisor.d","permissions":"0o40755"}]`
json.Unmarshal([]byte(list), &listOfFiles)
return listOfFiles, nil
}
// // Describe -
func (ocim *OCImageMock) Describe(imageID string) (*ImageMetadata, error) {
imageData := &ImageMetadata{}
id := `{"architecture":"amd64","info":{"architecture":"amd64","config":{"ArgsEscaped":true,"AttachStderr":false,"AttachStdin":false,"AttachStdout":false,"Cmd":["/nginx"],"Domainname":"","Entrypoint":["/entrypoint.sh"],"Env":["PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LANG=C.UTF-8","GPG_KEY=0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D","PYTHON_VERSION=3.6.5","PYTHON_PIP_VERSION=10.0.1","NGINX_VERSION=1.13.8","UWSGI_INI=/app/uwsgi.ini","UWSGI_CHEAPER=2","UWSGI_PROCESSES=16","NGINX_MAX_UPLOAD=0","NGINX_WORKER_PROCESSES=1","LISTEN_PORT=80","STATIC_URL=/static","STATIC_PATH=/app/static","STATIC_INDEX=0","PYTHONPATH=/app"],"ExposedPorts":{"443/tcp":{},"80/tcp":{}},"Hostname":"d98c43c06009","Image":"sha256:66531c940f46ea26a1db3c63583058b56f97b2e85f83feaa2a41b8e58e702419","Labels":{"maintainer":"Sebastian Ramirez <tiangolo@gmail.com>"},"OnBuild":[],"OpenStdin":false,"StdinOnce":false,"Tty":false,"User":"","Volumes":null,"WorkingDir":"/app"},"container":"8086c88edd59db391d40b0bd6463e6521ccf6e7ec97ccb4aa236d6612cebec1c","container_config":{"ArgsEscaped":true,"AttachStderr":false,"AttachStdin":false,"AttachStdout":false,"Cmd":["/bin/sh","-c","#(nop) COPY file:3f7d33a0228dc7f9feb6b386b9cac9f2730d691a447399a8c1ae2755e8477312 in /app/. "],"Domainname":"","Entrypoint":["/entrypoint.sh"],"Env":["PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LANG=C.UTF-8","GPG_KEY=0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D","PYTHON_VERSION=3.6.5","PYTHON_PIP_VERSION=10.0.1","NGINX_VERSION=1.13.8","UWSGI_INI=/app/uwsgi.ini","UWSGI_CHEAPER=2","UWSGI_PROCESSES=16","NGINX_MAX_UPLOAD=0","NGINX_WORKER_PROCESSES=1","LISTEN_PORT=80","STATIC_URL=/static","STATIC_PATH=/app/static","STATIC_INDEX=0","PYTHONPATH=/app"],"ExposedPorts":{"443/tcp":{},"80/tcp":{}},"Hostname":"d98c43c06009","Image":"sha256:66531c940f46ea26a1db3c63583058b56f97b2e85f83feaa2a41b8e58e702419","Labels":{"maintainer":"Sebastian Ramirez <tiangolo@gmail.com>"},"OnBuild":[],"OpenStdin":false,"StdinOnce":false,"Tty":false,"User":"","Volumes":null,"WorkingDir":"/app"},"created":"2018-11-25T11:37:00.787977594Z","docker_version":"1.13.1","id":"4944f7cd6bdbabd69dfde3efe800f996a43a373db9a5a07824ee30b908f61bfb","os":"linux","parent":"080f106513ef7ac9a5375e22ae6e56584bf745e401fbb21dd54d7fcf22ef8570"},"name":"signer","schemaVersion":1,"signatures":[{"header":{"alg":"ES256","jwk":{"crv":"P-256","kid":"W5FE:S6CL:XC37:EVE5:HSZD:OODU:4KWZ:WEIM:RRNN:MKXO:IO6H:Y7N4","kty":"EC","x":"DpcaARFTpltBfJ4cAGdE9Gp9AO2dEogJRBsWC9A2My0","y":"OTD5zOUaIa1bldgfGhVSpve-Urfxcpl1QS35hSBQoXQ"}},"protected":"eyJmb3JtYXRMZW5ndGgiOjM5MTc4LCJmb3JtYXRUYWlsIjoiQ24wIiwidGltZSI6IjIwMjAtMDUtMTRUMDc6NTc6MTBaIn0","signature":"hWCo_ezuNNz4e3M1opkK4Mrh9XcQE3K69ppL9_aboFaIPDzJWCZh2yt4zX4rM3dT3-2lLYuurFqgvw4dwLaJ4g"}],"tag":"70"}`
json.Unmarshal([]byte(id), imageData)
return imageData, nil
}
func (ocim *OCImageMock) GetSingleFile(fileName string, followSymLink bool) ([]byte, string, error) {
if strings.Contains(fileName, "-release") {
os := []byte("PRETTY_NAME=\"Debian GNU/Linux 10 (buster)\"\nNAME=\"Debian GNU/Linux\"\nVERSION_ID=\"10\"\nVERSION=\"10 (buster)\"\nVERSION_CODENAME=buster\nID=debian\nHOME_URL=\"https://www.debian.org/\"\nSUPPORT_URL=\"https://www.debian.org/support\"\nBUG_REPORT_URL=\"https://bugs.debian.org/\"\n")
return os, "os", nil
}
nginx := []byte("# Defaults for nginx initscript\n# sourced by /etc/init.d/nginx\n\n# Additional options that are passed to nginx\nDAEMON_ARGS=\"\"\n")
return nginx, "nginxscript", nil
}

View File

@@ -1,116 +0,0 @@
package ocimage
import "github.com/docker/docker/api/types/container"
// FileMetadata file metatdata
type FileMetadata struct {
IsSymbolicLink bool `json:"isSymbolicLink"`
Layer string `json:"layer"`
Link string `json:"link"`
Name string `json:"name"`
Path string `json:"path"`
Permissions string `json:"permissions"`
}
// ImageMetadata image metatdata
type ImageMetadata struct {
Tag string `json:"tag"`
Name string `json:"name"`
Architecture string `json:"architecture"`
SchemaVersion int `json:"naschemaVersionme"`
Info ImageMetaInfo `json:"info"`
Signatures []ImageMetaSignature `json:"signatures"`
}
// ImageMetaInfo -
type ImageMetaInfo struct {
ID string `json:"id,omitempty"`
Os string `json:"os,omitempty"`
Parent string `json:"parent,omitempty"`
Created string `json:"created,omitempty"`
Container string `json:"container,omitempty"`
Architecture string `json:"architecture,omitempty"`
Config *container.Config `json:"config,omitempty"`
ContainerConfig *container.Config `json:"container_config,omitempty"`
}
// // ContainerInfo -
// type ContainerInfo struct {
// Tty bool `json:"Tty,omitempty"`
// ArgsEscaped bool `json:"ArgsEscaped,omitempty"`
// AttachStderr bool `json:"AttachStderr,omitempty"`
// AttachStdin bool `json:"AttachStdin,omitempty"`
// AttachStdout bool `json:"AttachStdout,omitempty"`
// OpenStdin bool `json:"OpenStdin,omitempty"`
// StdinOnce bool `json:"StdinOnce,omitempty"`
// User string `json:"User,omitempty"`
// Image string `json:"Image"`
// Hostname string `json:"Hostname,omitempty"`
// Domainname string `json:"Domainname,omitempty"`
// WorkingDir string `json:"WorkingDir,omitempty"`
// Cmd []string `json:"Cmd"`
// Env []string `json:"Env,omitempty"`
// Entrypoint []string `json:"Entrypoint"`
// Volumes interface{} `json:"Volumes,omitempty"`
// OnBuild []interface{} `json:"OnBuild,omitempty"`
// Labels map[string]string `json:"Labels,omitempty"`
// ExposedPorts map[string]interface{} `json:"ExposedPorts,omitempty"`
// }
// ImageMetaSignature -
type ImageMetaSignature struct {
Protected string `json:"protected,omitempty"`
Signature string `json:"signature,omitempty"`
Header SignatureHeader `json:"header,omitempty"`
}
// SignatureHeader -
type SignatureHeader struct {
Alg string `json:"alg,omitempty"`
Jwk HeaderJwk `json:"jwk,omitempty"`
}
// HeaderJwk -
type HeaderJwk struct {
Crv string `json:"crv,omitempty"`
Kid string `json:"kid,omitempty"`
Kty string `json:"kty,omitempty"`
X string `json:"x,omitempty"`
Y string `json:"y,omitempty"`
}
type OciImageManifestConfig struct {
Digest string `json:"digest"`
MediaType string `json:"mediaType"`
Size int `json:"size"`
}
type OciImageManifestRequestOptions struct {
AllowRedirects bool `json:"allow_redirects"`
Stream bool `json:"stream"`
Verify bool `json:"verify"`
Headers map[string]string `json:"headers"`
}
type OciImageManifestLayer struct {
Digest string `json:"digest"`
DownloadPath string `json:"dlPath"`
MediaType string `json:"mediaType"`
Size int `json:"size"`
RequestOptions OciImageManifestRequestOptions `json:"request_options"`
}
type OciImageManifest struct {
Config OciImageManifestConfig `json:"config"`
Layers []OciImageManifestLayer `json:"layers"`
}
//{"isSymbolicLink":false,"layer":"sha256:86b54f4b6a4ebee33338eb7c182a9a3d51a69cce1eb9af95a992f4da8eabe3be","link":"","name":"var/lib/dpkg/info/libdbus-1-3.list","path":"var/lib/dpkg/info/libdbus-1-3.list","permissions":"0o100644"},
type OciImageFsEntry struct {
IsSymbolicLink bool `json:"isSymbolicLink"`
Layer string `json:"layer"`
Link string `json:"link"`
Name string `json:"name"`
Path string `json:"path"`
Permissions string `json:"permissions"`
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,89 +0,0 @@
package opapolicy
import (
"bytes"
"encoding/json"
"fmt"
"github.com/armosec/armopa/rego"
"github.com/golang/glog"
)
func (pn *PolicyNotification) ToJSONBytesBuffer() (*bytes.Buffer, error) {
res, err := json.Marshal(pn)
if err != nil {
return nil, err
}
return bytes.NewBuffer(res), err
}
func (ruleReport *RuleReport) GetRuleStatus() (string, []RuleResponse, []RuleResponse) {
if len(ruleReport.RuleResponses) == 0 {
return "success", nil, nil
}
exceptions := make([]RuleResponse, 0)
failed := make([]RuleResponse, 0)
for _, rule := range ruleReport.RuleResponses {
if rule.ExceptionName != "" {
failed = append(failed, rule)
} else {
exceptions = append(exceptions, rule)
}
}
status := "failed"
if len(failed) == 0 && len(exceptions) > 0 {
status = "warning"
}
return status, failed, exceptions
}
func ParseRegoResult(regoResult *rego.ResultSet) ([]RuleResponse, error) {
var errs error
ruleResponses := []RuleResponse{}
for _, result := range *regoResult {
for desicionIdx := range result.Expressions {
if resMap, ok := result.Expressions[desicionIdx].Value.(map[string]interface{}); ok {
for objName := range resMap {
jsonBytes, err := json.Marshal(resMap[objName])
if err != nil {
err = fmt.Errorf("in parseRegoResult, json.Marshal failed. name: %s, obj: %v, reason: %s", objName, resMap[objName], err)
glog.Error(err)
errs = fmt.Errorf("%s\n%s", errs, err)
continue
}
desObj := make([]RuleResponse, 0)
if err := json.Unmarshal(jsonBytes, &desObj); err != nil {
err = fmt.Errorf("in parseRegoResult, json.Unmarshal failed. name: %s, obj: %v, reason: %s", objName, resMap[objName], err)
glog.Error(err)
errs = fmt.Errorf("%s\n%s", errs, err)
continue
}
ruleResponses = append(ruleResponses, desObj...)
}
}
}
}
return ruleResponses, errs
}
func (controlReport *ControlReport) GetNumberOfResources() int {
sum := 0
for i := range controlReport.RuleReports {
sum += controlReport.RuleReports[i].NumOfResources
}
return sum
}
func (controlReport *ControlReport) Passed() bool {
for i := range controlReport.RuleReports {
if len(controlReport.RuleReports[i].RuleResponses) > 0 {
return false
}
}
return true
}
func (controlReport *ControlReport) Failed() bool {
return !controlReport.Passed()
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,17 +0,0 @@
package resources
import (
"os"
"path/filepath"
"testing"
)
func TestLoadRegoDependenciesFromDir(t *testing.T) {
dir, _ := os.Getwd()
t.Errorf("%s", filepath.Join(dir, "rego/dependencies"))
return
// modules := LoadRegoDependenciesFromDir("")
// if len(modules) == 0 {
// t.Errorf("modules len == 0")
// }
}

View File

@@ -1,64 +0,0 @@
package secrethandling
import (
"bytes"
"encoding/binary"
"strings"
)
// API fields
var (
WlidPrefix = "wlid://"
ClusterWlidPrefix = "cluster-"
NamespaceWlidPrefix = "namespace-"
DataCenterWlidPrefix = "datacenter-"
ProjectWlidPrefix = "project-"
SecretSIDPrefix = "secret-"
SubSecretSIDPrefix = "subsecret-"
K8SKindsList = []string{"ComponentStatus", "ConfigMap", "ControllerRevision", "CronJob",
"CustomResourceDefinition", "DaemonSet", "Deployment", "Endpoints", "Event", "HorizontalPodAutoscaler",
"Ingress", "Job", "Lease", "LimitRange", "LocalSubjectAccessReview", "MutatingWebhookConfiguration",
"Namespace", "NetworkPolicy", "Node", "PersistentVolume", "PersistentVolumeClaim", "Pod",
"PodDisruptionBudget", "PodSecurityPolicy", "PodTemplate", "PriorityClass", "ReplicaSet",
"ReplicationController", "ResourceQuota", "Role", "RoleBinding", "Secret", "SelfSubjectAccessReview",
"SelfSubjectRulesReview", "Service", "ServiceAccount", "StatefulSet", "StorageClass",
"SubjectAccessReview", "TokenReview", "ValidatingWebhookConfiguration", "VolumeAttachment"}
NativeKindsList = []string{"Dockerized", "Native"}
KindReverseMap = map[string]string{}
)
// SecretTLVTag the tlv tag
var SecretTLVTag = []byte{231, 197, 24, 237}
func init() {
for _, kind := range K8SKindsList {
KindReverseMap[strings.ToLower(strings.Replace(kind, "-", "", -1))] = kind
}
for _, kind := range NativeKindsList {
KindReverseMap[strings.ToLower(strings.Replace(kind, "-", "", -1))] = kind
}
}
// IsKindK8S returns true if kind is a k8s
func IsKindK8S(k string) bool {
if val, ok := KindReverseMap[k]; ok {
k = val
}
for _, k8sKind := range K8SKindsList {
if k == k8sKind {
return true
}
}
return false
}
// HasSecretTLV is the byte slice an encrypted secret
func HasSecretTLV(secret []byte) bool {
return bytes.HasPrefix(secret, SecretTLVTag)
}
// GetSecretTLVLength return TLV length
func GetSecretTLVLength(secret []byte) uint32 {
length := secret[len(SecretTLVTag) : len(SecretTLVTag)+4]
return uint32(len(SecretTLVTag)+4) + binary.BigEndian.Uint32(length)
}

View File

@@ -1,163 +0,0 @@
package secrethandling
import (
"context"
b64 "encoding/base64"
"encoding/json"
"fmt"
"strings"
"github.com/docker/docker/api/types"
"github.com/golang/glog"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
)
// DockerConfigJsonstructure -
type DockerConfigJsonstructure map[string]map[string]types.AuthConfig
func updateSecret(authConfig *types.AuthConfig, serverAddress string) {
if authConfig.ServerAddress == "" {
authConfig.ServerAddress = serverAddress
}
if authConfig.Username == "" || authConfig.Password == "" {
glog.Infof("secret missing user name or password, using auth")
auth := authConfig.Auth
decodedAuth, err := b64.StdEncoding.DecodeString(auth)
if err != nil {
glog.Errorf("error: %s", err.Error())
return
}
splittedAuth := strings.Split(string(decodedAuth), ":")
if len(splittedAuth) == 2 {
authConfig.Username = splittedAuth[0]
authConfig.Password = splittedAuth[1]
}
}
if authConfig.Auth == "" {
auth := fmt.Sprintf("%s:%s", authConfig.Username, authConfig.Password)
authConfig.Auth = b64.StdEncoding.EncodeToString([]byte(auth))
}
}
func parseEncodedSecret(sec map[string][]byte) (string, string) {
buser := sec[corev1.BasicAuthUsernameKey]
bpsw := sec[corev1.BasicAuthPasswordKey]
duser, _ := b64.StdEncoding.DecodeString(string(buser))
dpsw, _ := b64.StdEncoding.DecodeString(string(bpsw))
return string(duser), string(dpsw)
}
func parseDecodedSecret(sec map[string]string) (string, string) {
user := sec[corev1.BasicAuthUsernameKey]
psw := sec[corev1.BasicAuthPasswordKey]
return user, psw
}
// ReadSecret -
func ReadSecret(secret interface{}, secretName string) (types.AuthConfig, error) {
// Store secret based on it's structure
var authConfig types.AuthConfig
if sec, ok := secret.(*types.AuthConfig); ok {
return *sec, nil
}
if sec, ok := secret.(map[string]string); ok {
return types.AuthConfig{Username: sec["username"]}, nil
}
if sec, ok := secret.(DockerConfigJsonstructure); ok {
if _, k := sec["auths"]; !k {
return authConfig, fmt.Errorf("cant find auths")
}
for serverAddress, authConfig := range sec["auths"] {
updateSecret(&authConfig, serverAddress)
return authConfig, nil
}
}
return authConfig, fmt.Errorf("cant find secret")
}
func GetSecret(clientset *kubernetes.Clientset, namespace, name string) (*types.AuthConfig, error) {
res, err := clientset.CoreV1().Secrets(namespace).Get(context.Background(), name, metav1.GetOptions{})
if err != nil {
glog.Errorf("%v", err)
}
// Read secret
secret, err := GetSecretContent(res)
if err != nil {
glog.Error(err)
return nil, err
}
if secret == nil {
err := fmt.Errorf("secret %s not found", name)
glog.Error(err)
return nil, err
}
sec, err := ReadSecret(secret, name)
if err != nil {
return &sec, err
}
return &sec, nil
}
// GetSecretContent -
func GetSecretContent(secret *corev1.Secret) (interface{}, error) {
// Secret types- https://github.com/kubernetes/kubernetes/blob/7693a1d5fe2a35b6e2e205f03ae9b3eddcdabc6b/pkg/apis/core/types.go#L4394-L4478
switch secret.Type {
case corev1.SecretTypeDockerConfigJson:
sec := make(DockerConfigJsonstructure)
if err := json.Unmarshal(secret.Data[corev1.DockerConfigJsonKey], &sec); err != nil {
return nil, err
}
return sec, nil
default:
user, psw := "", ""
if len(secret.Data) != 0 {
user, psw = parseEncodedSecret(secret.Data)
} else if len(secret.StringData) != 0 {
userD, pswD := parseDecodedSecret(secret.StringData)
if userD != "" {
user = userD
}
if pswD != "" {
psw = pswD
}
} else {
return nil, fmt.Errorf("data not found in secret")
}
if user == "" || psw == "" {
return nil, fmt.Errorf("username or password not found")
}
return &types.AuthConfig{Username: user, Password: psw}, nil
}
}
func ParseSecret(res *corev1.Secret, name string) (*types.AuthConfig, error) {
// Read secret
secret, err := GetSecretContent(res)
if err != nil {
glog.Error(err)
return nil, err
}
if secret == nil {
err := fmt.Errorf("secret %s not found", name)
glog.Error(err)
return nil, err
}
sec, err := ReadSecret(secret, name)
if err != nil {
return &sec, err
}
return &sec, nil
}

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