1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-01-20 18:52:16 +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" "os"
"path/filepath" "path/filepath"
"github.com/nirmata/kyverno/pkg/utils"
"github.com/nirmata/kyverno/pkg/kyverno/sanitizedError" "github.com/nirmata/kyverno/pkg/kyverno/sanitizedError"
policy2 "github.com/nirmata/kyverno/pkg/policy" policy2 "github.com/nirmata/kyverno/pkg/policy"
@ -69,7 +71,7 @@ func Command() *cobra.Command {
} }
for _, policy := range policies { for _, policy := range policies {
err := policy2.Validate(*policy) err := policy2.Validate(utils.MarshalPolicy(*policy))
if err != nil { if err != nil {
return sanitizedError.New(fmt.Sprintf("Policy %v is not valid", policy.Name)) return sanitizedError.New(fmt.Sprintf("Policy %v is not valid", policy.Name))
} }

View file

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

View file

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

View file

@ -2,8 +2,11 @@ package openapi
import ( import (
"encoding/json" "encoding/json"
"log"
"testing" "testing"
"github.com/nirmata/kyverno/pkg/utils"
v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1" 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 { func TestDummy(t *testing.T) {
description string var policy v1.ClusterPolicy
policy []byte 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"}`)
errMessage string json.Unmarshal(policyRaw, &policy)
}{
{
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)
}
}
policyRaw1, _ := json.Marshal(policy)
policyRaw2 := utils.MarshalPolicy(policy)
log.Println(policyRaw1, policyRaw2)
} }

View file

@ -1,6 +1,7 @@
package policy package policy
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"reflect" "reflect"
@ -8,6 +9,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/golang/glog"
"github.com/nirmata/kyverno/pkg/openapi" "github.com/nirmata/kyverno/pkg/openapi"
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1" kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
@ -19,7 +22,14 @@ import (
// Validate does some initial check to verify some conditions // Validate does some initial check to verify some conditions
// - One operation per rule // - One operation per rule
// - ResourceDescription mandatory checks // - 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 { if path, err := validateUniqueRuleName(p); err != nil {
return fmt.Errorf("path: spec.%s: %v", path, err) 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 return err
} }

View file

@ -1,5 +1,12 @@
package utils 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 // JoinPatches joins array of serialized JSON patches to the single JSONPatch array
func JoinPatches(patches [][]byte) []byte { func JoinPatches(patches [][]byte) []byte {
var result []byte var result []byte
@ -17,3 +24,42 @@ func JoinPatches(patches [][]byte) []byte {
result = append(result, []byte("\n]")...) result = append(result, []byte("\n]")...)
return result 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 package webhooks
import ( import (
"encoding/json"
"fmt"
policyvalidate "github.com/nirmata/kyverno/pkg/policy" 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" v1beta1 "k8s.io/api/admission/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
//HandlePolicyValidation performs the validation check on policy resource //HandlePolicyValidation performs the validation check on policy resource
func (ws *WebhookServer) handlePolicyValidation(request *v1beta1.AdmissionRequest) *v1beta1.AdmissionResponse { 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 ? //TODO: can this happen? wont this be picked by OpenAPI spec schema ?
raw := request.Object.Raw if err := policyvalidate.Validate(request.Object.Raw); err != nil {
if err := json.Unmarshal(raw, &policy); err != nil { return &v1beta1.AdmissionResponse{
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{
Allowed: false, Allowed: false,
Result: &metav1.Status{ Result: &metav1.Status{
Message: err.Error(), Message: err.Error(),
}, },
} }
} }
if admissionResp.Allowed {
// if the policy contains mutating & validation rules and it config does not exist we create one // if the policy contains mutating & validation rules and it config does not exist we create one
// queue the request // queue the request
ws.resourceWebhookWatcher.RegisterResourceWebhook() ws.resourceWebhookWatcher.RegisterResourceWebhook()
return &v1beta1.AdmissionResponse{
Allowed: true,
} }
return admissionResp
} }