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:
parent
fb6151c6d0
commit
61c1ea5a49
6 changed files with 35 additions and 230 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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.
|
Loading…
Reference in a new issue