2019-05-15 14:25:32 +03:00
|
|
|
package engine
|
2019-03-06 13:01:17 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
2019-06-10 17:32:26 +03:00
|
|
|
"fmt"
|
|
|
|
"strconv"
|
2019-04-30 18:54:08 -07:00
|
|
|
"strings"
|
2019-03-06 13:01:17 +02:00
|
|
|
|
2019-05-14 18:10:25 +03:00
|
|
|
"github.com/minio/minio/pkg/wildcard"
|
2019-05-21 11:00:09 -07:00
|
|
|
kubepolicy "github.com/nirmata/kyverno/pkg/apis/policy/v1alpha1"
|
2019-05-15 14:25:32 +03:00
|
|
|
|
2019-05-14 18:10:25 +03:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
2019-03-06 13:01:17 +02:00
|
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
|
|
)
|
|
|
|
|
2019-05-20 14:48:38 +03:00
|
|
|
// ResourceMeetsDescription checks requests kind, name and labels to fit the policy rule
|
|
|
|
func ResourceMeetsDescription(resourceRaw []byte, description kubepolicy.ResourceDescription, gvk metav1.GroupVersionKind) bool {
|
2019-05-21 15:43:43 -07:00
|
|
|
if !findKind(description.Kinds, gvk.Kind) {
|
2019-05-20 14:48:38 +03:00
|
|
|
return false
|
2019-05-15 14:25:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if resourceRaw != nil {
|
2019-06-05 17:43:59 -07:00
|
|
|
meta := parseMetadataFromObject(resourceRaw)
|
2019-05-15 14:25:32 +03:00
|
|
|
name := ParseNameFromObject(resourceRaw)
|
|
|
|
|
|
|
|
if description.Name != nil {
|
|
|
|
|
|
|
|
if !wildcard.Match(*description.Name, name) {
|
2019-05-20 14:48:38 +03:00
|
|
|
return false
|
2019-05-15 14:25:32 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if description.Selector != nil {
|
|
|
|
selector, err := metav1.LabelSelectorAsSelector(description.Selector)
|
|
|
|
|
|
|
|
if err != nil {
|
2019-05-20 14:48:38 +03:00
|
|
|
return false
|
2019-05-15 14:25:32 +03:00
|
|
|
}
|
|
|
|
|
2019-06-05 17:43:59 -07:00
|
|
|
labelMap := parseLabelsFromMetadata(meta)
|
2019-05-15 14:25:32 +03:00
|
|
|
|
|
|
|
if !selector.Matches(labelMap) {
|
2019-05-20 14:48:38 +03:00
|
|
|
return false
|
2019-05-15 14:25:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
2019-05-20 14:48:38 +03:00
|
|
|
return true
|
2019-05-15 14:25:32 +03:00
|
|
|
}
|
|
|
|
|
2019-06-05 17:43:59 -07:00
|
|
|
func parseMetadataFromObject(bytes []byte) map[string]interface{} {
|
2019-03-06 13:01:17 +02:00
|
|
|
var objectJSON map[string]interface{}
|
|
|
|
json.Unmarshal(bytes, &objectJSON)
|
|
|
|
|
|
|
|
return objectJSON["metadata"].(map[string]interface{})
|
|
|
|
}
|
|
|
|
|
2019-06-05 17:43:59 -07:00
|
|
|
func parseKindFromObject(bytes []byte) string {
|
2019-05-01 14:48:50 -07:00
|
|
|
var objectJSON map[string]interface{}
|
|
|
|
json.Unmarshal(bytes, &objectJSON)
|
|
|
|
|
|
|
|
return objectJSON["kind"].(string)
|
|
|
|
}
|
|
|
|
|
2019-06-05 17:43:59 -07:00
|
|
|
func parseLabelsFromMetadata(meta map[string]interface{}) labels.Set {
|
2019-03-06 13:01:17 +02:00
|
|
|
if interfaceMap, ok := meta["labels"].(map[string]interface{}); ok {
|
|
|
|
labelMap := make(labels.Set, len(interfaceMap))
|
|
|
|
|
|
|
|
for key, value := range interfaceMap {
|
|
|
|
labelMap[key] = value.(string)
|
|
|
|
}
|
|
|
|
return labelMap
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-06-05 17:43:59 -07:00
|
|
|
//ParseNameFromObject extracts resource name from JSON obj
|
2019-05-07 16:50:39 -07:00
|
|
|
func ParseNameFromObject(bytes []byte) string {
|
2019-05-02 11:15:23 -07:00
|
|
|
var objectJSON map[string]interface{}
|
|
|
|
json.Unmarshal(bytes, &objectJSON)
|
|
|
|
|
|
|
|
meta := objectJSON["metadata"].(map[string]interface{})
|
|
|
|
|
2019-03-06 13:01:17 +02:00
|
|
|
if name, ok := meta["name"].(string); ok {
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
2019-03-21 18:09:58 +02:00
|
|
|
|
2019-06-05 17:43:59 -07:00
|
|
|
// ParseNamespaceFromObject extracts the namespace from the JSON obj
|
2019-05-07 16:50:39 -07:00
|
|
|
func ParseNamespaceFromObject(bytes []byte) string {
|
2019-05-02 11:15:23 -07:00
|
|
|
var objectJSON map[string]interface{}
|
|
|
|
json.Unmarshal(bytes, &objectJSON)
|
|
|
|
|
|
|
|
meta := objectJSON["metadata"].(map[string]interface{})
|
|
|
|
|
2019-03-21 18:09:58 +02:00
|
|
|
if namespace, ok := meta["namespace"].(string); ok {
|
|
|
|
return namespace
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
2019-04-30 17:26:50 -07:00
|
|
|
|
2019-06-10 17:32:26 +03:00
|
|
|
// ParseRegexPolicyResourceName returns true if policyResourceName is a regexp
|
2019-05-07 16:50:39 -07:00
|
|
|
func ParseRegexPolicyResourceName(policyResourceName string) (string, bool) {
|
2019-04-30 18:54:08 -07:00
|
|
|
regex := strings.Split(policyResourceName, "regex:")
|
|
|
|
if len(regex) == 1 {
|
|
|
|
return regex[0], false
|
|
|
|
}
|
|
|
|
return strings.Trim(regex[1], " "), true
|
2019-04-30 17:26:50 -07:00
|
|
|
}
|
2019-05-21 18:27:56 +03:00
|
|
|
|
2019-06-05 17:43:59 -07:00
|
|
|
func getAnchorsFromMap(anchorsMap map[string]interface{}) map[string]interface{} {
|
2019-05-21 18:27:56 +03:00
|
|
|
result := make(map[string]interface{})
|
|
|
|
|
|
|
|
for key, value := range anchorsMap {
|
2019-06-13 17:20:00 +03:00
|
|
|
if isConditionAnchor(key) || isExistanceAnchor(key) {
|
2019-05-21 18:27:56 +03:00
|
|
|
result[key] = value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
2019-05-22 16:17:26 -07:00
|
|
|
|
2019-06-13 17:20:00 +03:00
|
|
|
func getAnchorFromMap(anchorsMap map[string]interface{}) (string, interface{}) {
|
|
|
|
for key, value := range anchorsMap {
|
|
|
|
if isConditionAnchor(key) || isExistanceAnchor(key) {
|
|
|
|
return key, value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
2019-05-21 15:43:43 -07:00
|
|
|
func findKind(kinds []string, kindGVK string) bool {
|
|
|
|
for _, kind := range kinds {
|
|
|
|
if kind == kindGVK {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2019-06-05 13:43:07 +03:00
|
|
|
|
2019-06-13 17:20:00 +03:00
|
|
|
func isConditionAnchor(str string) bool {
|
2019-06-05 13:43:07 +03:00
|
|
|
if len(str) < 2 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return (str[0] == '(' && str[len(str)-1] == ')')
|
|
|
|
}
|
|
|
|
|
2019-06-13 17:20:00 +03:00
|
|
|
func isExistanceAnchor(str string) bool {
|
|
|
|
left := "^("
|
|
|
|
right := ")"
|
|
|
|
|
|
|
|
if len(str) < len(left)+len(right) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return (str[:len(left)] == left && str[len(str)-len(right):] == right)
|
|
|
|
}
|
|
|
|
|
2019-06-05 13:43:07 +03:00
|
|
|
// Checks if array object matches anchors. If not - skip - return true
|
|
|
|
func skipArrayObject(object, anchors map[string]interface{}) bool {
|
|
|
|
for key, pattern := range anchors {
|
|
|
|
key = key[1 : len(key)-1]
|
|
|
|
|
|
|
|
value, ok := object[key]
|
|
|
|
if !ok {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if !ValidateValueWithPattern(value, pattern) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
2019-06-10 17:06:31 +03:00
|
|
|
|
2019-06-10 17:32:26 +03:00
|
|
|
// removeAnchor remove special characters around anchored key
|
2019-06-10 17:06:31 +03:00
|
|
|
func removeAnchor(key string) string {
|
2019-06-13 17:20:00 +03:00
|
|
|
if isConditionAnchor(key) {
|
2019-06-10 17:06:31 +03:00
|
|
|
return key[1 : len(key)-1]
|
|
|
|
}
|
|
|
|
|
2019-06-13 17:20:00 +03:00
|
|
|
if isExistanceAnchor(key) {
|
|
|
|
return key[2 : len(key)-1]
|
|
|
|
}
|
|
|
|
|
2019-06-10 17:06:31 +03:00
|
|
|
// TODO: Add logic for other anchors here
|
|
|
|
|
|
|
|
return key
|
|
|
|
}
|
2019-06-10 17:32:26 +03:00
|
|
|
|
|
|
|
// convertToFloat converts string and any other value to float64
|
|
|
|
func convertToFloat(value interface{}) (float64, error) {
|
|
|
|
switch typed := value.(type) {
|
|
|
|
case string:
|
|
|
|
var err error
|
|
|
|
floatValue, err := strconv.ParseFloat(typed, 64)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return floatValue, nil
|
|
|
|
case float64:
|
|
|
|
return typed, nil
|
|
|
|
case int64:
|
|
|
|
return float64(typed), nil
|
|
|
|
case int:
|
|
|
|
return float64(typed), nil
|
|
|
|
default:
|
|
|
|
return 0, fmt.Errorf("Could not convert %T to float64", value)
|
|
|
|
}
|
|
|
|
}
|