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

754 works as intended - more changes required related to locks .etc

This commit is contained in:
shravan 2020-03-24 23:12:45 +05:30
parent 2273033c6c
commit 4cf29adccd
7 changed files with 114 additions and 57 deletions

View file

@ -7,6 +7,8 @@ import (
"os"
"path/filepath"
"github.com/nirmata/kyverno/pkg/utils"
"github.com/nirmata/kyverno/pkg/kyverno/sanitizedError"
policy2 "github.com/nirmata/kyverno/pkg/policy"
@ -69,7 +71,7 @@ func Command() *cobra.Command {
}
for _, policy := range policies {
err := policy2.Validate(*policy)
err := policy2.Validate(utils.MarshalPolicy(*policy))
if err != nil {
return sanitizedError.New(fmt.Sprintf("Policy %v is not valid", policy.Name))
}

View file

@ -7,6 +7,8 @@ import (
"os"
"path/filepath"
"github.com/nirmata/kyverno/pkg/utils"
"github.com/nirmata/kyverno/pkg/kyverno/sanitizedError"
"github.com/golang/glog"
@ -43,7 +45,7 @@ func Command() *cobra.Command {
}
for _, policy := range policies {
err = policyvalidate.Validate(*policy)
err = policyvalidate.Validate(utils.MarshalPolicy(*policy))
if err != nil {
fmt.Println("Policy " + policy.Name + " is invalid")
} else {

View file

@ -48,11 +48,12 @@ func init() {
}
}
func ValidatePolicyFields(policy v1.ClusterPolicy) error {
func ValidatePolicyFields(policyRaw []byte) error {
openApiGlobalState.mutex.RLock()
defer openApiGlobalState.mutex.RUnlock()
policyRaw, err := json.Marshal(policy)
var policy v1.ClusterPolicy
err := json.Unmarshal(policyRaw, &policy)
if err != nil {
return err
}

View file

@ -2,8 +2,11 @@ package openapi
import (
"encoding/json"
"log"
"testing"
"github.com/nirmata/kyverno/pkg/utils"
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
)
@ -64,32 +67,42 @@ func Test_ValidateMutationPolicy(t *testing.T) {
}
func Test_ValidatePolicyFields(t *testing.T) {
//func Test_ValidatePolicyFields(t *testing.T) {
//
// tcs := []struct {
// description string
// policy []byte
// errMessage string
// }{
// {
// description: "Dealing with invalid fields in the policy",
// policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"disallow-root-user"},"validationFailureAction":"enforce","spec":{"background":false,"rules":[{"name":"validate-runAsNonRoot","match":{"resources":{"kinds":["Pod"]}},"exclude":{"resources":{"kinds":["Pod"]}},"validate":{"message":"Running as root user is not allowed. Set runAsNonRoot to true","anyPattern":[{"spec":{"securityContext":{"runAsNonRoot":true}}},{"spec":{"containers":[{"securityContext":{"runAsNonRoot":true}}]}}]}}]}}`),
// },
// }
//
// for i, tc := range tcs {
// policy := v1.ClusterPolicy{}
// _ = json.Unmarshal(tc.policy, &policy)
//
// var errMessage string
// err := ValidatePolicyFields(policy)
// if err != nil {
// errMessage = err.Error()
// }
//
// if errMessage != tc.errMessage {
// t.Errorf("\nTestcase [%v] failed:\nExpected Error: %v\nGot Error: %v", i+1, tc.errMessage, errMessage)
// }
// }
//
//}
tcs := []struct {
description string
policy []byte
errMessage string
}{
{
description: "Dealing with invalid fields in the policy",
policy: []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"name":"disallow-root-user"},"validationFailureAction":"enforce","spec":{"background":false,"rules":[{"name":"validate-runAsNonRoot","match":{"resources":{"kinds":["Pod"]}},"exclude":{"resources":{"kinds":["Pod"]}},"validate":{"message":"Running as root user is not allowed. Set runAsNonRoot to true","anyPattern":[{"spec":{"securityContext":{"runAsNonRoot":true}}},{"spec":{"containers":[{"securityContext":{"runAsNonRoot":true}}]}}]}}]}}`),
},
}
for i, tc := range tcs {
policy := v1.ClusterPolicy{}
_ = json.Unmarshal(tc.policy, &policy)
var errMessage string
err := ValidatePolicyFields(policy)
if err != nil {
errMessage = err.Error()
}
if errMessage != tc.errMessage {
t.Errorf("\nTestcase [%v] failed:\nExpected Error: %v\nGot Error: %v", i+1, tc.errMessage, errMessage)
}
}
func TestDummy(t *testing.T) {
var policy v1.ClusterPolicy
policyRaw := []byte(`{"apiVersion":"kyverno.io/v1","kind":"ClusterPolicy","metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"kyverno.io/v1\",\"kind\":\"ClusterPolicy\",\"metadata\":{\"annotations\":{},\"name\":\"disallow-root-user\"},\"spec\":{\"background\":false,\"rules\":[{\"exclude\":{\"resources\":{\"kinds\":[\"Pod\"]}},\"match\":{\"resources\":{\"kinds\":[\"Pod\"]}},\"name\":\"validate-runAsNonRoot\",\"validate\":{\"anyPattern\":[{\"spec\":{\"securityContext\":{\"runAsNonRoot\":true}}},{\"spec\":{\"containers\":[{\"securityContext\":{\"runAsNonRoot\":true}}]}}],\"message\":\"Running as root user is not allowed. Set runAsNonRoot to true\"}}]},\"validationFailureAction\":\"enforce\"}\n","pod-policies.kyverno.io/autogen-controllers":"all"},"creationTimestamp":"2020-03-24T16:06:22Z","generation":1,"name":"disallow-root-user","uid":"54725f78-a292-4d19-a78f-a859f7539834"},"spec":{"background":false,"rules":[{"exclude":{"resources":{"kinds":["Pod"]}},"match":{"resources":{"kinds":["Pod"]}},"name":"validate-runAsNonRoot","validate":{"anyPattern":[{"spec":{"securityContext":{"runAsNonRoot":true}}},{"spec":{"containers":[{"securityContext":{"runAsNonRoot":true}}]}}],"message":"Running as root user is not allowed. Set runAsNonRoot to true"}},{"exclude":{"resources":{"kinds":["DaemonSet","Deployment","Job","StatefulSet"]}},"match":{"resources":{"kinds":["DaemonSet","Deployment","Job","StatefulSet"]}},"name":"autogen-validate-runAsNonRoot","validate":{"anyPattern":[{"spec":{"template":{"spec":{"securityContext":{"runAsNonRoot":true}}}}},{"spec":{"template":{"spec":{"containers":[{"securityContext":{"runAsNonRoot":true}}]}}}}],"message":"Running as root user is not allowed. Set runAsNonRoot to true"}}],"validationFailureAction":"audit"},"validationFailureAction":"enforce"}`)
json.Unmarshal(policyRaw, &policy)
policyRaw1, _ := json.Marshal(policy)
policyRaw2 := utils.MarshalPolicy(policy)
log.Println(policyRaw1, policyRaw2)
}

View file

@ -1,6 +1,7 @@
package policy
import (
"encoding/json"
"errors"
"fmt"
"reflect"
@ -8,6 +9,8 @@ import (
"strconv"
"strings"
"github.com/golang/glog"
"github.com/nirmata/kyverno/pkg/openapi"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
@ -19,7 +22,14 @@ import (
// Validate does some initial check to verify some conditions
// - One operation per rule
// - ResourceDescription mandatory checks
func Validate(p kyverno.ClusterPolicy) error {
func Validate(policyRaw []byte) error {
var p kyverno.ClusterPolicy
err := json.Unmarshal(policyRaw, &p)
if err != nil {
glog.Errorf("Failed to unmarshal policy admission request, err %v\n", err)
return fmt.Errorf("failed to unmarshal policy admission request err %v", err)
}
if path, err := validateUniqueRuleName(p); err != nil {
return fmt.Errorf("path: spec.%s: %v", path, err)
}
@ -82,7 +92,7 @@ func Validate(p kyverno.ClusterPolicy) error {
}
}
if err := openapi.ValidatePolicyFields(p); err != nil {
if err := openapi.ValidatePolicyFields(policyRaw); err != nil {
return err
}

View file

@ -1,5 +1,12 @@
package utils
import (
"encoding/json"
"reflect"
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
)
// JoinPatches joins array of serialized JSON patches to the single JSONPatch array
func JoinPatches(patches [][]byte) []byte {
var result []byte
@ -17,3 +24,42 @@ func JoinPatches(patches [][]byte) []byte {
result = append(result, []byte("\n]")...)
return result
}
// MarshalPolicy accurately marshals a policy to JSON,
// normal marshal would cause empty sub structs in
// policy to be non nil.
// TODO This needs to be removed. A simpler way to encode and decode Policy is needed.
func MarshalPolicy(policy v1.ClusterPolicy) []byte {
var rules []interface{}
rulesRaw, _ := json.Marshal(policy.Spec.Rules)
_ = json.Unmarshal(rulesRaw, &rules)
for i, r := range rules {
rule, _ := r.(map[string]interface{})
if reflect.DeepEqual(policy.Spec.Rules[i].Mutation, v1.Mutation{}) {
delete(rule, "mutate")
}
if reflect.DeepEqual(policy.Spec.Rules[i].Validation, v1.Validation{}) {
delete(rule, "validate")
}
if reflect.DeepEqual(policy.Spec.Rules[i].Generation, v1.Generation{}) {
delete(rule, "generate")
}
rules[i] = rule
}
var policyRepresentation = make(map[string]interface{})
policyRaw, _ := json.Marshal(policy)
_ = json.Unmarshal(policyRaw, &policyRepresentation)
specRepresentation, _ := policyRepresentation["spec"].(map[string]interface{})
specRepresentation["rules"] = rules
policyRepresentation["spec"] = specRepresentation
policyRaw, _ = json.Marshal(policyRepresentation)
return policyRaw
}

View file

@ -1,45 +1,28 @@
package webhooks
import (
"encoding/json"
"fmt"
policyvalidate "github.com/nirmata/kyverno/pkg/policy"
"github.com/golang/glog"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
v1beta1 "k8s.io/api/admission/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
//HandlePolicyValidation performs the validation check on policy resource
func (ws *WebhookServer) handlePolicyValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse {
var policy *kyverno.ClusterPolicy
admissionResp := &v1beta1.AdmissionResponse{
Allowed: true,
}
//TODO: can this happen? wont this be picked by OpenAPI spec schema ?
raw := request.Object.Raw
if err := json.Unmarshal(raw, &policy); err != nil {
glog.Errorf("Failed to unmarshal policy admission request, err %v\n", err)
return &v1beta1.AdmissionResponse{Allowed: false,
Result: &metav1.Status{
Message: fmt.Sprintf("Failed to unmarshal policy admission request err %v", err),
}}
}
if err := policyvalidate.Validate(*policy); err != nil {
admissionResp = &v1beta1.AdmissionResponse{
if err := policyvalidate.Validate(request.Object.Raw); err != nil {
return &v1beta1.AdmissionResponse{
Allowed: false,
Result: &metav1.Status{
Message: err.Error(),
},
}
}
if admissionResp.Allowed {
// if the policy contains mutating & validation rules and it config does not exist we create one
// queue the request
ws.resourceWebhookWatcher.RegisterResourceWebhook()
// if the policy contains mutating & validation rules and it config does not exist we create one
// queue the request
ws.resourceWebhookWatcher.RegisterResourceWebhook()
return &v1beta1.AdmissionResponse{
Allowed: true,
}
return admissionResp
}