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:
parent
2273033c6c
commit
4cf29adccd
7 changed files with 114 additions and 57 deletions
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue