1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

use validatepattern in generate rule to check for subset existance

This commit is contained in:
shivkumar dudhani 2019-10-31 13:04:56 -07:00
parent fb6151c6d0
commit 61c1ea5a49
6 changed files with 35 additions and 230 deletions

View file

@ -1,7 +1,6 @@
package engine
import (
"encoding/json"
"time"
"fmt"
@ -9,7 +8,6 @@ import (
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1alpha1"
client "github.com/nirmata/kyverno/pkg/dclient"
"github.com/nirmata/kyverno/pkg/utils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
@ -149,38 +147,12 @@ func applyRuleGenerator(client *client.Client, ns unstructured.Unstructured, rul
//checkResource checks if the config is present in th eresource
func checkResource(config interface{}, resource *unstructured.Unstructured) (bool, error) {
var err error
objByte, err := resource.MarshalJSON()
// we are checking if config is a subset of resource with default pattern
path, err := validateResourceWithPattern(resource.Object, config)
if err != nil {
// unable to parse the json
glog.V(4).Infof("config not a subset of resource. failed at path %s: %v", path, err)
return false, err
}
err = resource.UnmarshalJSON(objByte)
if err != nil {
// unable to parse the json
return false, err
}
// marshall and unmarshall json to verify if its right format
configByte, err := json.Marshal(config)
if err != nil {
// unable to marshall the config
return false, err
}
var configData interface{}
err = json.Unmarshal(configByte, &configData)
if err != nil {
// unable to unmarshall
return false, err
}
var objData interface{}
err = json.Unmarshal(objByte, &objData)
if err != nil {
// unable to unmarshall
return false, err
}
// Check if the config is a subset of resource
return utils.JSONsubsetValue(configData, objData), nil
return true, nil
}

View file

@ -3,6 +3,7 @@ package engine
import (
"math"
"regexp"
"strconv"
"strings"
"github.com/golang/glog"
@ -91,6 +92,14 @@ func validateValueWithIntPattern(value interface{}, pattern int64) bool {
glog.Warningf("Expected int, found float: %f\n", typedValue)
return false
case string:
// extract int64 from string
int64Num, err := strconv.ParseInt(typedValue, 10, 64)
if err != nil {
glog.Warningf("Failed to parse int64 from string: %v", err)
return false
}
return int64Num == pattern
default:
glog.Warningf("Expected int, found: %T\n", value)
return false
@ -105,11 +114,25 @@ func validateValueWithFloatPattern(value interface{}, pattern float64) bool {
if pattern == math.Trunc(pattern) {
return int(pattern) == value
}
glog.Warningf("Expected float, found int: %d\n", typedValue)
return false
case int64:
// check that float has no fraction
if pattern == math.Trunc(pattern) {
return int64(pattern) == value
}
glog.Warningf("Expected float, found int: %d\n", typedValue)
return false
case float64:
return typedValue == pattern
case string:
// extract float64 from string
float64Num, err := strconv.ParseFloat(typedValue, 64)
if err != nil {
glog.Warningf("Failed to parse float64 from string: %v", err)
return false
}
return float64Num == pattern
default:
glog.Warningf("Expected float, found: %T\n", value)
return false

View file

@ -1,193 +1,5 @@
package utils
import (
"github.com/golang/glog"
)
//JSONsubsetValue checks if JSON a is contained in JSON b
func JSONsubsetValue(a interface{}, b interface{}) bool {
switch typed := a.(type) {
case bool:
bv, ok := b.(bool)
if !ok {
glog.Errorf("expected bool found %T", b)
return false
}
av, _ := a.(bool)
if av == bv {
return true
}
case int:
bv, ok := b.(int)
if !ok {
glog.Errorf("expected int found %T", b)
return false
}
av, _ := a.(int)
if av == bv {
return true
}
case float64:
bv, ok := b.(float64)
if !ok {
glog.Errorf("expected float64 found %T", b)
return false
}
av, _ := a.(float64)
if av == bv {
return true
}
case string:
bv, ok := b.(string)
if !ok {
glog.Errorf("expected string found %T", b)
return false
}
av, _ := a.(string)
if av == bv {
return true
}
case map[string]interface{}:
bv, ok := b.(map[string]interface{})
if !ok {
glog.Errorf("expected map[string]interface{} found %T", b)
return false
}
av, _ := a.(map[string]interface{})
return subsetMap(av, bv)
case []interface{}:
// TODO: verify the logic
bv, ok := b.([]interface{})
if !ok {
glog.Errorf("expected []interface{} found %T", b)
return false
}
av, _ := a.([]interface{})
return subsetSlice(av, bv)
default:
glog.Errorf("Unspported type %s", typed)
}
return false
}
func subsetMap(a, b map[string]interface{}) bool {
// check if keys are present
for k := range a {
if _, ok := b[k]; !ok {
glog.Errorf("key %s, not present in resource", k)
return false
}
}
// check if values for the keys match
for ak, av := range a {
bv := b[ak]
if !JSONsubsetValue(av, bv) {
return false
}
}
return true
}
func containsInt(a interface{}, b []interface{}) bool {
switch typed := a.(type) {
case bool:
for _, bv := range b {
bv, ok := bv.(bool)
if !ok {
return false
}
av, _ := a.(bool)
if bv == av {
return true
}
}
case int:
for _, bv := range b {
bv, ok := bv.(int)
if !ok {
return false
}
av, _ := a.(int)
if bv == av {
return true
}
}
case float64:
for _, bv := range b {
bv, ok := bv.(float64)
if !ok {
return false
}
av, _ := a.(float64)
if bv == av {
return true
}
}
case string:
for _, bv := range b {
bv, ok := bv.(string)
if !ok {
return false
}
av, _ := a.(string)
if bv == av {
return true
}
}
case map[string]interface{}:
for _, bv := range b {
bv, ok := bv.(map[string]interface{})
if !ok {
return false
}
av, _ := a.(map[string]interface{})
if subsetMap(av, bv) {
return true
}
}
case []interface{}:
for _, bv := range b {
bv, ok := bv.([]interface{})
if !ok {
return false
}
av, _ := a.([]interface{})
if JSONsubsetValue(av, bv) {
return true
}
}
default:
glog.Errorf("Unspported type %s", typed)
}
return false
}
func subsetSlice(a, b []interface{}) bool {
// if empty
if len(a) == 0 {
return true
}
// check if len is not greater
if len(a) > len(b) {
return false
}
for _, av := range a {
if !containsInt(av, b) {
return false
}
}
return true
}
// JoinPatches joins array of serialized JSON patches to the single JSONPatch array
func JoinPatches(patches [][]byte) []byte {
var result []byte

View file

@ -90,8 +90,6 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pat
glog.V(2).Infof("Handling validation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.UID, request.Operation)
// glog.V(4).Infof("Validating resource %s/%s/%s with policy %s with %d rules\n", resource.GetKind(), resource.GetNamespace(), resource.GetName(), policy.ObjectMeta.Name, len(policy.Spec.Rules))
engineResponse := engine.Validate(*policy, *resource)
engineResponses = append(engineResponses, engineResponse)
// Gather policy application statistics

View file

@ -20,7 +20,7 @@ spec:
data:
spec:
hard:
requests.cpu: '4'
requests.memory: "16Gi"
limits.cpu: '4'
limits.memory: "16Gi"
requests.cpu: 4
requests.memory: 16Gi
limits.cpu: 4
limits.memory: 16Gi

View file

@ -20,7 +20,7 @@ spec:
pattern:
spec:
securityContext:
runAsUser: '1000'
runAsUser: 1000
- name: validate-groupid
match:
resources:
@ -31,7 +31,7 @@ spec:
pattern:
spec:
securityContext:
runAsGroup: '3000'
runAsGroup: 3000
- name: validate-fsgroup
match:
resources:
@ -42,7 +42,7 @@ spec:
pattern:
spec:
securityContext:
fsGroup: '2000'
fsGroup: 2000
# Alls processes inside the pod can be made to run with specific user and groupID by setting runAsUser and runAsGroup respectively.
# fsGroup can be specified to make sure any file created in the volume with have the specified groupID.
# The above parameters can also be used in a validate policy to restrict user & group IDs.