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