mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
refactor & validate operations for generate rules in PolicyValidation
This commit is contained in:
parent
8480276f23
commit
b1063a95e1
17 changed files with 1332 additions and 793 deletions
108
pkg/auth/auth.go
Normal file
108
pkg/auth/auth.go
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
|
client "github.com/nirmata/kyverno/pkg/dclient"
|
||||||
|
authorizationv1 "k8s.io/api/authorization/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
//CanIOptions provides utility ti check if user has authorization for the given operation
|
||||||
|
type CanIOptions struct {
|
||||||
|
namespace string
|
||||||
|
verb string
|
||||||
|
kind string
|
||||||
|
client *client.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewCanI returns a new instance of operation access controler evaluator
|
||||||
|
func NewCanI(client *client.Client, kind, namespace, verb string) *CanIOptions {
|
||||||
|
o := CanIOptions{
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
|
||||||
|
o.namespace = namespace
|
||||||
|
o.kind = kind
|
||||||
|
o.verb = verb
|
||||||
|
|
||||||
|
return &o
|
||||||
|
}
|
||||||
|
|
||||||
|
//RunAccessCheck checks if the caller can perform the operation
|
||||||
|
// - operation is a combination of namespace, kind, verb
|
||||||
|
// - can only evaluate a single verb
|
||||||
|
// - group version resource is determined from the kind using the discovery client REST mapper
|
||||||
|
// - If disallowed, the reason and evaluationError is avialable in the logs
|
||||||
|
// - each can generates a SelfSubjectAccessReview resource and response is evaluated for permissions
|
||||||
|
func (o *CanIOptions) RunAccessCheck() (bool, error) {
|
||||||
|
// get GroupVersionResource from RESTMapper
|
||||||
|
// get GVR from kind
|
||||||
|
gvr := o.client.DiscoveryClient.GetGVRFromKind(o.kind)
|
||||||
|
if reflect.DeepEqual(gvr, schema.GroupVersionResource{}) {
|
||||||
|
// cannot find GVR
|
||||||
|
return false, fmt.Errorf("failed to get the Group Version Resource for kind %s", o.kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
var sar *authorizationv1.SelfSubjectAccessReview
|
||||||
|
|
||||||
|
sar = &authorizationv1.SelfSubjectAccessReview{
|
||||||
|
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
|
||||||
|
ResourceAttributes: &authorizationv1.ResourceAttributes{
|
||||||
|
Namespace: o.namespace,
|
||||||
|
Verb: o.verb,
|
||||||
|
Group: gvr.Group,
|
||||||
|
Resource: gvr.Resource,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// Set self subject access review
|
||||||
|
// - namespace
|
||||||
|
// - verb
|
||||||
|
// - resource
|
||||||
|
// - subresource
|
||||||
|
|
||||||
|
// Create the Resource
|
||||||
|
resp, err := o.client.CreateResource("SelfSubjectAccessReview", "", sar, false)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("failed to create resource %s/%s/%s", sar.Kind, sar.Namespace, sar.Name)
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// status.allowed
|
||||||
|
allowed, ok, err := unstructured.NestedBool(resp.Object, "status", "allowed")
|
||||||
|
if !ok {
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("unexpected error when getting status.allowed for %s/%s/%s", sar.Kind, sar.Namespace, sar.Name)
|
||||||
|
}
|
||||||
|
glog.Errorf("status.allowed not found for %s/%s/%s", sar.Kind, sar.Namespace, sar.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !allowed {
|
||||||
|
// status.reason
|
||||||
|
reason, ok, err := unstructured.NestedString(resp.Object, "status", "reason")
|
||||||
|
if !ok {
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("unexpected error when getting status.reason for %s/%s/%s", sar.Kind, sar.Namespace, sar.Name)
|
||||||
|
}
|
||||||
|
glog.Errorf("status.reason not found for %s/%s/%s", sar.Kind, sar.Namespace, sar.Name)
|
||||||
|
}
|
||||||
|
// status.evaluationError
|
||||||
|
evaluationError, ok, err := unstructured.NestedString(resp.Object, "status", "evaludationError")
|
||||||
|
if !ok {
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("unexpected error when getting status.evaluationError for %s/%s/%s", sar.Kind, sar.Namespace, sar.Name)
|
||||||
|
}
|
||||||
|
glog.Errorf("status.evaluationError not found for %s/%s/%s", sar.Kind, sar.Namespace, sar.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reporting ? (just logs)
|
||||||
|
glog.Errorf("reason to disallow operation: %s", reason)
|
||||||
|
glog.Errorf("evaluationError to disallow operation: %s", evaluationError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return allowed, nil
|
||||||
|
}
|
48
pkg/auth/auth_test.go
Normal file
48
pkg/auth/auth_test.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
// import (
|
||||||
|
// "testing"
|
||||||
|
// "time"
|
||||||
|
|
||||||
|
// "github.com/golang/glog"
|
||||||
|
// "github.com/nirmata/kyverno/pkg/config"
|
||||||
|
// dclient "github.com/nirmata/kyverno/pkg/dclient"
|
||||||
|
// "github.com/nirmata/kyverno/pkg/signal"
|
||||||
|
// )
|
||||||
|
|
||||||
|
// func Test_Auth_pass(t *testing.T) {
|
||||||
|
// // needs running cluster
|
||||||
|
// var kubeconfig string
|
||||||
|
// stopCh := signal.SetupSignalHandler()
|
||||||
|
// kubeconfig = "/Users/shivd/.kube/config"
|
||||||
|
// clientConfig, err := config.CreateClientConfig(kubeconfig)
|
||||||
|
// if err != nil {
|
||||||
|
// glog.Fatalf("Error building kubeconfig: %v\n", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // DYNAMIC CLIENT
|
||||||
|
// // - client for all registered resources
|
||||||
|
// // - invalidate local cache of registered resource every 10 seconds
|
||||||
|
// client, err := dclient.NewClient(clientConfig, 10*time.Second, stopCh)
|
||||||
|
// if err != nil {
|
||||||
|
// glog.Fatalf("Error creating client: %v\n", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Can i authenticate
|
||||||
|
|
||||||
|
// kind := "Deployment"
|
||||||
|
// namespace := "default"
|
||||||
|
// verb := "test"
|
||||||
|
// canI := NewCanI(client, kind, namespace, verb)
|
||||||
|
// ok, err := canI.RunAccessCheck()
|
||||||
|
// if err != nil {
|
||||||
|
// t.Error(err)
|
||||||
|
// }
|
||||||
|
// if ok {
|
||||||
|
// t.Log("allowed")
|
||||||
|
// } else {
|
||||||
|
// t.Log("notallowed")
|
||||||
|
// }
|
||||||
|
// t.FailNow()
|
||||||
|
|
||||||
|
// }
|
14
pkg/engine/variables/common.go
Normal file
14
pkg/engine/variables/common.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package variables
|
||||||
|
|
||||||
|
import "regexp"
|
||||||
|
|
||||||
|
//IsVariable returns true if the element contains a 'valid' variable {{}}
|
||||||
|
func IsVariable(element string) bool {
|
||||||
|
validRegex := regexp.MustCompile(variableRegex)
|
||||||
|
groups := validRegex.FindAllStringSubmatch(element, -1)
|
||||||
|
if len(groups) == 0 {
|
||||||
|
// there was no match
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
50
pkg/policy/actions.go
Normal file
50
pkg/policy/actions.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package policy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
|
dclient "github.com/nirmata/kyverno/pkg/dclient"
|
||||||
|
"github.com/nirmata/kyverno/pkg/policy/generate"
|
||||||
|
"github.com/nirmata/kyverno/pkg/policy/mutate"
|
||||||
|
"github.com/nirmata/kyverno/pkg/policy/validate"
|
||||||
|
)
|
||||||
|
|
||||||
|
//Validation provides methods to validate a rule
|
||||||
|
type Validation interface {
|
||||||
|
Validate() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
//validateAction performs validation on the rule actions
|
||||||
|
// - Mutate
|
||||||
|
// - Validation
|
||||||
|
// - Generate
|
||||||
|
func validateActions(idx int, rule kyverno.Rule, client *dclient.Client) error {
|
||||||
|
var checker Validation
|
||||||
|
|
||||||
|
// Mutate
|
||||||
|
if rule.HasMutate() {
|
||||||
|
checker = mutate.NewMutateFactory(rule.Mutation)
|
||||||
|
if path, err := checker.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("path: spec.rules[%d].mutate.%s.: %v", idx, path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
if rule.HasValidate() {
|
||||||
|
checker = validate.NewValidateFactory(rule.Validation)
|
||||||
|
if path, err := checker.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("path: spec.rules[%d].validate.%s.: %v", idx, path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate
|
||||||
|
if rule.HasGenerate() {
|
||||||
|
checker = generate.NewGenerateFactory(client, rule.Generation)
|
||||||
|
if path, err := checker.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("path: spec.rules[%d].generate.%s.: %v", idx, path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
85
pkg/policy/common/common.go
Normal file
85
pkg/policy/common/common.go
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/anchor"
|
||||||
|
)
|
||||||
|
|
||||||
|
//ValidatePattern validates the pattern
|
||||||
|
func ValidatePattern(patternElement interface{}, path string, supportedAnchors []anchor.IsAnchor) (string, error) {
|
||||||
|
switch typedPatternElement := patternElement.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
return validateMap(typedPatternElement, path, supportedAnchors)
|
||||||
|
case []interface{}:
|
||||||
|
return validateArray(typedPatternElement, path, supportedAnchors)
|
||||||
|
case string, float64, int, int64, bool, nil:
|
||||||
|
//TODO? check operator
|
||||||
|
return "", nil
|
||||||
|
default:
|
||||||
|
return path, fmt.Errorf("Validation rule failed at '%s', pattern contains unknown type", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func validateMap(patternMap map[string]interface{}, path string, supportedAnchors []anchor.IsAnchor) (string, error) {
|
||||||
|
// check if anchors are defined
|
||||||
|
for key, value := range patternMap {
|
||||||
|
// if key is anchor
|
||||||
|
// check regex () -> this is anchor
|
||||||
|
// ()
|
||||||
|
// single char ()
|
||||||
|
re, err := regexp.Compile(`^.?\(.+\)$`)
|
||||||
|
if err != nil {
|
||||||
|
return path + "/" + key, fmt.Errorf("Unable to parse the field %s: %v", key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
matched := re.MatchString(key)
|
||||||
|
// check the type of anchor
|
||||||
|
if matched {
|
||||||
|
// some type of anchor
|
||||||
|
// check if valid anchor
|
||||||
|
if !checkAnchors(key, supportedAnchors) {
|
||||||
|
return path + "/" + key, fmt.Errorf("Unsupported anchor %s", key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addition check for existence anchor
|
||||||
|
// value must be of type list
|
||||||
|
if anchor.IsExistenceAnchor(key) {
|
||||||
|
typedValue, ok := value.([]interface{})
|
||||||
|
if !ok {
|
||||||
|
return path + "/" + key, fmt.Errorf("Existence anchor should have value of type list")
|
||||||
|
}
|
||||||
|
// validate there is only one entry in the list
|
||||||
|
if len(typedValue) == 0 || len(typedValue) > 1 {
|
||||||
|
return path + "/" + key, fmt.Errorf("Existence anchor: single value expected, multiple specified")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// lets validate the values now :)
|
||||||
|
if errPath, err := ValidatePattern(value, path+"/"+key, supportedAnchors); err != nil {
|
||||||
|
return errPath, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateArray(patternArray []interface{}, path string, supportedAnchors []anchor.IsAnchor) (string, error) {
|
||||||
|
for i, patternElement := range patternArray {
|
||||||
|
currentPath := path + strconv.Itoa(i) + "/"
|
||||||
|
// lets validate the values now :)
|
||||||
|
if errPath, err := ValidatePattern(patternElement, currentPath, supportedAnchors); err != nil {
|
||||||
|
return errPath, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkAnchors(key string, supportedAnchors []anchor.IsAnchor) bool {
|
||||||
|
for _, f := range supportedAnchors {
|
||||||
|
if f(key) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
71
pkg/policy/generate/auth.go
Normal file
71
pkg/policy/generate/auth.go
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
package generate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nirmata/kyverno/pkg/auth"
|
||||||
|
dclient "github.com/nirmata/kyverno/pkg/dclient"
|
||||||
|
)
|
||||||
|
|
||||||
|
//Operations provides methods to performing operations on resource
|
||||||
|
type Operations interface {
|
||||||
|
// CanICreate returns 'true' if self can 'create' resource
|
||||||
|
CanICreate(kind, namespace string) (bool, error)
|
||||||
|
// CanIUpdate returns 'true' if self can 'update' resource
|
||||||
|
CanIUpdate(kind, namespace string) (bool, error)
|
||||||
|
// CanIDelete returns 'true' if self can 'delete' resource
|
||||||
|
CanIDelete(kind, namespace string) (bool, error)
|
||||||
|
// CanIGet returns 'true' if self can 'get' resource
|
||||||
|
CanIGet(kind, namespace string) (bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Auth provides implementation to check if caller/self/kyverno has access to perofrm operations
|
||||||
|
type Auth struct {
|
||||||
|
client *dclient.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewAuth returns a new instance of Auth for operations
|
||||||
|
func NewAuth(client *dclient.Client) *Auth {
|
||||||
|
a := Auth{
|
||||||
|
client: client,
|
||||||
|
}
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanICreate returns 'true' if self can 'create' resource
|
||||||
|
func (a *Auth) CanICreate(kind, namespace string) (bool, error) {
|
||||||
|
canI := auth.NewCanI(a.client, kind, namespace, "create")
|
||||||
|
ok, err := canI.RunAccessCheck()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return ok, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanIUpdate returns 'true' if self can 'update' resource
|
||||||
|
func (a *Auth) CanIUpdate(kind, namespace string) (bool, error) {
|
||||||
|
canI := auth.NewCanI(a.client, kind, namespace, "update")
|
||||||
|
ok, err := canI.RunAccessCheck()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return ok, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanIDelete returns 'true' if self can 'delete' resource
|
||||||
|
func (a *Auth) CanIDelete(kind, namespace string) (bool, error) {
|
||||||
|
canI := auth.NewCanI(a.client, kind, namespace, "delete")
|
||||||
|
ok, err := canI.RunAccessCheck()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return ok, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanIGet returns 'true' if self can 'get' resource
|
||||||
|
func (a *Auth) CanIGet(kind, namespace string) (bool, error) {
|
||||||
|
canI := auth.NewCanI(a.client, kind, namespace, "get")
|
||||||
|
ok, err := canI.RunAccessCheck()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return ok, nil
|
||||||
|
}
|
21
pkg/policy/generate/fake.go
Normal file
21
pkg/policy/generate/fake.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package generate
|
||||||
|
|
||||||
|
import (
|
||||||
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
|
"github.com/nirmata/kyverno/pkg/policy/generate/fake"
|
||||||
|
)
|
||||||
|
|
||||||
|
//FakeGenerate provides implementation for generate rule processing
|
||||||
|
// with mocks/fakes for cluster interactions
|
||||||
|
type FakeGenerate struct {
|
||||||
|
Generate
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewFakeGenerate returns a new instance of generatecheck that uses
|
||||||
|
// fake/mock implementation for operation access(always returns true)
|
||||||
|
func NewFakeGenerate(rule kyverno.Generation) *FakeGenerate {
|
||||||
|
g := FakeGenerate{}
|
||||||
|
g.rule = rule
|
||||||
|
g.authCheck = fake.NewFakeAuth()
|
||||||
|
return &g
|
||||||
|
}
|
31
pkg/policy/generate/fake/auth.go
Normal file
31
pkg/policy/generate/fake/auth.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package fake
|
||||||
|
|
||||||
|
//FakeAuth providers implementation for testing, retuning true for all operations
|
||||||
|
type FakeAuth struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewFakeAuth returns a new instance of Fake Auth that returns true for each operation
|
||||||
|
func NewFakeAuth() *FakeAuth {
|
||||||
|
a := FakeAuth{}
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanICreate returns 'true'
|
||||||
|
func (a *FakeAuth) CanICreate(kind, namespace string) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanIUpdate returns 'true'
|
||||||
|
func (a *FakeAuth) CanIUpdate(kind, namespace string) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanIDelete returns 'true'
|
||||||
|
func (a *FakeAuth) CanIDelete(kind, namespace string) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanIGet returns 'true'
|
||||||
|
func (a *FakeAuth) CanIGet(kind, namespace string) (bool, error) {
|
||||||
|
return true, nil
|
||||||
|
}
|
148
pkg/policy/generate/validate.go
Normal file
148
pkg/policy/generate/validate.go
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
package generate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
|
dclient "github.com/nirmata/kyverno/pkg/dclient"
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/anchor"
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/variables"
|
||||||
|
"github.com/nirmata/kyverno/pkg/policy/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Generate provides implementation to validate 'generate' rule
|
||||||
|
type Generate struct {
|
||||||
|
// rule to hold 'generate' rule specifications
|
||||||
|
rule kyverno.Generation
|
||||||
|
// authCheck to check access for operations
|
||||||
|
authCheck Operations
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewGenerateFactory returns a new instance of Generate validation checker
|
||||||
|
func NewGenerateFactory(client *dclient.Client, rule kyverno.Generation) *Generate {
|
||||||
|
g := Generate{
|
||||||
|
rule: rule,
|
||||||
|
authCheck: NewAuth(client),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &g
|
||||||
|
}
|
||||||
|
|
||||||
|
//Validate validates the generate rule in validation
|
||||||
|
func (g *Generate) Validate() (string, error) {
|
||||||
|
rule := g.rule
|
||||||
|
if rule.Data == nil && rule.Clone == (kyverno.CloneFrom{}) {
|
||||||
|
return "", fmt.Errorf("clone or data are required")
|
||||||
|
}
|
||||||
|
if rule.Data != nil && rule.Clone != (kyverno.CloneFrom{}) {
|
||||||
|
return "", fmt.Errorf("only one operation allowed per generate rule(data or clone)")
|
||||||
|
}
|
||||||
|
kind, name, namespace := rule.Kind, rule.Name, rule.Namespace
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
return "name", fmt.Errorf("name cannot be empty")
|
||||||
|
}
|
||||||
|
if kind == "" {
|
||||||
|
return "kind", fmt.Errorf("kind cannot be empty")
|
||||||
|
}
|
||||||
|
// Can I generate resource
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(rule.Clone, kyverno.CloneFrom{}) {
|
||||||
|
if path, err := g.validateClone(rule.Clone, kind); err != nil {
|
||||||
|
return fmt.Sprintf("clone.%s", path), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if rule.Data != nil {
|
||||||
|
//TODO: is this required ?? as anchors can only be on pattern and not resource
|
||||||
|
// we can add this check by not sure if its needed here
|
||||||
|
if path, err := common.ValidatePattern(rule.Data, "/", []anchor.IsAnchor{}); err != nil {
|
||||||
|
return fmt.Sprintf("data.%s", path), fmt.Errorf("anchors not supported on generate resources: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kyverno generate-controller create/update/deletes the resources specified in generate rule of policy
|
||||||
|
// kyverno uses SA 'kyverno-service-account' and has default ClusterRoles and ClusterRoleBindings
|
||||||
|
// instuctions to modify the RBAC for kyverno are mentioned at https://github.com/nirmata/kyverno/blob/master/documentation/installation.md
|
||||||
|
// - operations required: create/update/delete/get
|
||||||
|
// If kind and namespace contain variables, then we cannot resolve then so we skip the processing
|
||||||
|
if err := g.canIGenerate(kind, namespace); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generate) validateClone(c kyverno.CloneFrom, kind string) (string, error) {
|
||||||
|
if c.Name == "" {
|
||||||
|
return "name", fmt.Errorf("name cannot be empty")
|
||||||
|
}
|
||||||
|
if c.Namespace == "" {
|
||||||
|
return "namespace", fmt.Errorf("namespace cannot be empty")
|
||||||
|
}
|
||||||
|
namespace := c.Namespace
|
||||||
|
// Skip if there is variable defined
|
||||||
|
if !variables.IsVariable(kind) && !variables.IsVariable(namespace) {
|
||||||
|
// GET
|
||||||
|
ok, err := g.authCheck.CanIGet(kind, namespace)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("kyverno does not have permissions to 'get' resource %s/%s. Update permissions in ClusterRole 'kyverno:generatecontroller'", kind, namespace)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
glog.V(4).Info("name & namespace uses variables, so cannot be resolved. Skipping Auth Checks.")
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//canIGenerate returns a error if kyverno cannot perform oprations
|
||||||
|
func (g *Generate) canIGenerate(kind, namespace string) error {
|
||||||
|
// Skip if there is variable defined
|
||||||
|
authCheck := g.authCheck
|
||||||
|
if !variables.IsVariable(kind) && !variables.IsVariable(namespace) {
|
||||||
|
// CREATE
|
||||||
|
ok, err := authCheck.CanICreate(kind, namespace)
|
||||||
|
if err != nil {
|
||||||
|
// machinery error
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("kyverno does not have permissions to 'create' resource %s/%s. Update permissions in ClusterRole 'kyverno:generatecontroller'", kind, namespace)
|
||||||
|
}
|
||||||
|
// UPDATE
|
||||||
|
ok, err = authCheck.CanIUpdate(kind, namespace)
|
||||||
|
if err != nil {
|
||||||
|
// machinery error
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("kyverno does not have permissions to 'update' resource %s/%s. Update permissions in ClusterRole 'kyverno:generatecontroller'", kind, namespace)
|
||||||
|
}
|
||||||
|
// GET
|
||||||
|
ok, err = authCheck.CanIGet(kind, namespace)
|
||||||
|
if err != nil {
|
||||||
|
// machinery error
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("kyverno does not have permissions to 'get' resource %s/%s. Update permissions in ClusterRole 'kyverno:generatecontroller'", kind, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE
|
||||||
|
ok, err = authCheck.CanIDelete(kind, namespace)
|
||||||
|
if err != nil {
|
||||||
|
// machinery error
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("kyverno does not have permissions to 'delete' resource %s/%s. Update permissions in ClusterRole 'kyverno:generatecontroller'", kind, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
glog.V(4).Info("name & namespace uses variables, so cannot be resolved. Skipping Auth Checks.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
89
pkg/policy/generate/validate_test.go
Normal file
89
pkg/policy/generate/validate_test.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package generate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
|
"gotest.tools/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Validate_Generate(t *testing.T) {
|
||||||
|
rawGenerate := []byte(`
|
||||||
|
{
|
||||||
|
"kind": "NetworkPolicy",
|
||||||
|
"name": "defaultnetworkpolicy",
|
||||||
|
"data": {
|
||||||
|
"spec": {
|
||||||
|
"podSelector": {},
|
||||||
|
"policyTypes": [
|
||||||
|
"Ingress",
|
||||||
|
"Egress"
|
||||||
|
],
|
||||||
|
"ingress": [
|
||||||
|
{}
|
||||||
|
],
|
||||||
|
"egress": [
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
var genRule kyverno.Generation
|
||||||
|
err := json.Unmarshal(rawGenerate, &genRule)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
checker := NewFakeGenerate(genRule)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.Assert(t, err != nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Validate_Generate_HasAnchors(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
rawGenerate := []byte(`
|
||||||
|
{
|
||||||
|
"kind": "NetworkPolicy",
|
||||||
|
"name": "defaultnetworkpolicy",
|
||||||
|
"data": {
|
||||||
|
"spec": {
|
||||||
|
"(podSelector)": {},
|
||||||
|
"policyTypes": [
|
||||||
|
"Ingress",
|
||||||
|
"Egress"
|
||||||
|
],
|
||||||
|
"ingress": [
|
||||||
|
{}
|
||||||
|
],
|
||||||
|
"egress": [
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
var genRule kyverno.Generation
|
||||||
|
err = json.Unmarshal(rawGenerate, &genRule)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
checker := NewFakeGenerate(genRule)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.Assert(t, err != nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
rawGenerate = []byte(`
|
||||||
|
{
|
||||||
|
"kind": "ConfigMap",
|
||||||
|
"name": "copied-cm",
|
||||||
|
"clone": {
|
||||||
|
"^(namespace)": "default",
|
||||||
|
"name": "game"
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
err = json.Unmarshal(rawGenerate, &genRule)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
checker = NewFakeGenerate(genRule)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.Assert(t, err != nil)
|
||||||
|
}
|
||||||
|
}
|
63
pkg/policy/mutate/validate.go
Normal file
63
pkg/policy/mutate/validate.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package mutate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/anchor"
|
||||||
|
"github.com/nirmata/kyverno/pkg/policy/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mutate provides implementation to validate 'mutate' rule
|
||||||
|
type Mutate struct {
|
||||||
|
// rule to hold 'mutate' rule specifications
|
||||||
|
rule kyverno.Mutation
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewMutateFactory returns a new instance of Mutate validation checker
|
||||||
|
func NewMutateFactory(rule kyverno.Mutation) *Mutate {
|
||||||
|
m := Mutate{
|
||||||
|
rule: rule,
|
||||||
|
}
|
||||||
|
return &m
|
||||||
|
}
|
||||||
|
|
||||||
|
//Validate validates the 'mutate' rul
|
||||||
|
func (m *Mutate) Validate() (string, error) {
|
||||||
|
rule := m.rule
|
||||||
|
// JSON Patches
|
||||||
|
if len(rule.Patches) != 0 {
|
||||||
|
for i, patch := range rule.Patches {
|
||||||
|
if err := validatePatch(patch); err != nil {
|
||||||
|
return fmt.Sprintf("patch[%d]", i), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Overlay
|
||||||
|
if rule.Overlay != nil {
|
||||||
|
path, err := common.ValidatePattern(rule.Overlay, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsAddingAnchor})
|
||||||
|
if err != nil {
|
||||||
|
return path, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate if all mandatory PolicyPatch fields are set
|
||||||
|
func validatePatch(pp kyverno.Patch) error {
|
||||||
|
if pp.Path == "" {
|
||||||
|
return errors.New("JSONPatch field 'path' is mandatory")
|
||||||
|
}
|
||||||
|
if pp.Operation == "add" || pp.Operation == "replace" {
|
||||||
|
if pp.Value == nil {
|
||||||
|
return fmt.Errorf("JSONPatch field 'value' is mandatory for operation '%s'", pp.Operation)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
} else if pp.Operation == "remove" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Unsupported JSONPatch operation '%s'", pp.Operation)
|
||||||
|
}
|
151
pkg/policy/mutate/validate_test.go
Normal file
151
pkg/policy/mutate/validate_test.go
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
package mutate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
|
"gotest.tools/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Validate_Mutate_ConditionAnchor(t *testing.T) {
|
||||||
|
rawMutate := []byte(`
|
||||||
|
{
|
||||||
|
"overlay": {
|
||||||
|
"spec": {
|
||||||
|
"(serviceAccountName)": "*",
|
||||||
|
"automountServiceAccountToken": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
var mutate kyverno.Mutation
|
||||||
|
err := json.Unmarshal(rawMutate, &mutate)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
checker := NewMutateFactory(mutate)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.NilError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Validate_Mutate_PlusAnchor(t *testing.T) {
|
||||||
|
rawMutate := []byte(`
|
||||||
|
{
|
||||||
|
"overlay": {
|
||||||
|
"spec": {
|
||||||
|
"+(serviceAccountName)": "*",
|
||||||
|
"automountServiceAccountToken": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
var mutate kyverno.Mutation
|
||||||
|
err := json.Unmarshal(rawMutate, &mutate)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
checker := NewMutateFactory(mutate)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.NilError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Validate_Mutate_Mismatched(t *testing.T) {
|
||||||
|
rawMutate := []byte(`
|
||||||
|
{
|
||||||
|
"overlay": {
|
||||||
|
"spec": {
|
||||||
|
"^(serviceAccountName)": "*",
|
||||||
|
"automountServiceAccountToken": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
var mutateExistence kyverno.Mutation
|
||||||
|
err := json.Unmarshal(rawMutate, &mutateExistence)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
checker := NewMutateFactory(mutateExistence)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.Assert(t, err != nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var mutateEqual kyverno.Mutation
|
||||||
|
rawMutate = []byte(`
|
||||||
|
{
|
||||||
|
"overlay": {
|
||||||
|
"spec": {
|
||||||
|
"=(serviceAccountName)": "*",
|
||||||
|
"automountServiceAccountToken": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
err = json.Unmarshal(rawMutate, &mutateEqual)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
checker = NewMutateFactory(mutateEqual)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.Assert(t, err != nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
var mutateNegation kyverno.Mutation
|
||||||
|
rawMutate = []byte(`
|
||||||
|
{
|
||||||
|
"overlay": {
|
||||||
|
"spec": {
|
||||||
|
"X(serviceAccountName)": "*",
|
||||||
|
"automountServiceAccountToken": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
err = json.Unmarshal(rawMutate, &mutateNegation)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
checker = NewMutateFactory(mutateEqual)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.Assert(t, err != nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Validate_Mutate_Unsupported(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
var mutate kyverno.Mutation
|
||||||
|
// case 1
|
||||||
|
rawMutate := []byte(`
|
||||||
|
{
|
||||||
|
"overlay": {
|
||||||
|
"spec": {
|
||||||
|
"!(serviceAccountName)": "*",
|
||||||
|
"automountServiceAccountToken": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
err = json.Unmarshal(rawMutate, &mutate)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
checker := NewMutateFactory(mutate)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.Assert(t, err != nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// case 2
|
||||||
|
rawMutate = []byte(`
|
||||||
|
{
|
||||||
|
"overlay": {
|
||||||
|
"spec": {
|
||||||
|
"~(serviceAccountName)": "*",
|
||||||
|
"automountServiceAccountToken": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
err = json.Unmarshal(rawMutate, &mutate)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
checker = NewMutateFactory(mutate)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.Assert(t, err != nil)
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,12 +4,10 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
"github.com/nirmata/kyverno/pkg/engine/anchor"
|
dclient "github.com/nirmata/kyverno/pkg/dclient"
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
@ -17,7 +15,7 @@ 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(p kyverno.ClusterPolicy, client *dclient.Client) error {
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -50,24 +48,12 @@ func Validate(p kyverno.ClusterPolicy) error {
|
||||||
// as there are more than 1 operation in rule, not need to evaluate it further
|
// as there are more than 1 operation in rule, not need to evaluate it further
|
||||||
return fmt.Errorf("path: spec.rules[%d]: %v", i, err)
|
return fmt.Errorf("path: spec.rules[%d]: %v", i, err)
|
||||||
}
|
}
|
||||||
// Operation Validation
|
// validate rule actions
|
||||||
// Mutation
|
// - Mutate
|
||||||
if rule.HasMutate() {
|
// - Validate
|
||||||
if path, err := validateMutation(rule.Mutation); err != nil {
|
// - Generate
|
||||||
return fmt.Errorf("path: spec.rules[%d].mutate.%s.: %v", i, path, err)
|
if err := validateActions(i, rule, client); err != nil {
|
||||||
}
|
return err
|
||||||
}
|
|
||||||
// Validation
|
|
||||||
if rule.HasValidate() {
|
|
||||||
if path, err := validateValidation(rule.Validation); err != nil {
|
|
||||||
return fmt.Errorf("path: spec.rules[%d].validate.%s.: %v", i, path, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Generation
|
|
||||||
if rule.HasGenerate() {
|
|
||||||
if path, err := validateGeneration(rule.Generation); err != nil {
|
|
||||||
return fmt.Errorf("path: spec.rules[%d].generate.%s.: %v", i, path, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a rules match block does not match any kind,
|
// If a rules match block does not match any kind,
|
||||||
|
@ -262,193 +248,3 @@ func validateResourceDescription(rd kyverno.ResourceDescription) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateMutation(m kyverno.Mutation) (string, error) {
|
|
||||||
// JSON Patches
|
|
||||||
if len(m.Patches) != 0 {
|
|
||||||
for i, patch := range m.Patches {
|
|
||||||
if err := validatePatch(patch); err != nil {
|
|
||||||
return fmt.Sprintf("patch[%d]", i), err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Overlay
|
|
||||||
if m.Overlay != nil {
|
|
||||||
path, err := validatePattern(m.Overlay, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsAddingAnchor})
|
|
||||||
if err != nil {
|
|
||||||
return path, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate if all mandatory PolicyPatch fields are set
|
|
||||||
func validatePatch(pp kyverno.Patch) error {
|
|
||||||
if pp.Path == "" {
|
|
||||||
return errors.New("JSONPatch field 'path' is mandatory")
|
|
||||||
}
|
|
||||||
if pp.Operation == "add" || pp.Operation == "replace" {
|
|
||||||
if pp.Value == nil {
|
|
||||||
return fmt.Errorf("JSONPatch field 'value' is mandatory for operation '%s'", pp.Operation)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
} else if pp.Operation == "remove" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Errorf("Unsupported JSONPatch operation '%s'", pp.Operation)
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateValidation(v kyverno.Validation) (string, error) {
|
|
||||||
if err := validateOverlayPattern(v); err != nil {
|
|
||||||
// no need to proceed ahead
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.Pattern != nil {
|
|
||||||
if path, err := validatePattern(v.Pattern, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsExistenceAnchor, anchor.IsEqualityAnchor, anchor.IsNegationAnchor}); err != nil {
|
|
||||||
return fmt.Sprintf("pattern.%s", path), err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(v.AnyPattern) != 0 {
|
|
||||||
for i, pattern := range v.AnyPattern {
|
|
||||||
if path, err := validatePattern(pattern, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsExistenceAnchor, anchor.IsEqualityAnchor, anchor.IsNegationAnchor}); err != nil {
|
|
||||||
return fmt.Sprintf("anyPattern[%d].%s", i, path), err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// validateOverlayPattern checks one of pattern/anyPattern must exist
|
|
||||||
func validateOverlayPattern(v kyverno.Validation) error {
|
|
||||||
if v.Pattern == nil && len(v.AnyPattern) == 0 {
|
|
||||||
return fmt.Errorf("a pattern or anyPattern must be specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.Pattern != nil && len(v.AnyPattern) != 0 {
|
|
||||||
return fmt.Errorf("only one operation allowed per validation rule(pattern or anyPattern)")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate returns error if generator is configured incompletely
|
|
||||||
func validateGeneration(gen kyverno.Generation) (string, error) {
|
|
||||||
|
|
||||||
if gen.Data == nil && gen.Clone == (kyverno.CloneFrom{}) {
|
|
||||||
return "", fmt.Errorf("clone or data are required")
|
|
||||||
}
|
|
||||||
if gen.Data != nil && gen.Clone != (kyverno.CloneFrom{}) {
|
|
||||||
return "", fmt.Errorf("only one operation allowed per generate rule(data or clone)")
|
|
||||||
}
|
|
||||||
// check kind is non empty
|
|
||||||
// check name is non empty
|
|
||||||
if gen.Name == "" {
|
|
||||||
return "name", fmt.Errorf("name cannot be empty")
|
|
||||||
}
|
|
||||||
if gen.Kind == "" {
|
|
||||||
return "kind", fmt.Errorf("kind cannot be empty")
|
|
||||||
}
|
|
||||||
if !reflect.DeepEqual(gen.Clone, kyverno.CloneFrom{}) {
|
|
||||||
if path, err := validateClone(gen.Clone); err != nil {
|
|
||||||
return fmt.Sprintf("clone.%s", path), err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if gen.Data != nil {
|
|
||||||
//TODO: is this required ?? as anchors can only be on pattern and not resource
|
|
||||||
// we can add this check by not sure if its needed here
|
|
||||||
if path, err := validatePattern(gen.Data, "/", []anchor.IsAnchor{}); err != nil {
|
|
||||||
return fmt.Sprintf("data.%s", path), fmt.Errorf("anchors not supported on generate resources: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateClone(c kyverno.CloneFrom) (string, error) {
|
|
||||||
if c.Name == "" {
|
|
||||||
return "name", fmt.Errorf("name cannot be empty")
|
|
||||||
}
|
|
||||||
if c.Namespace == "" {
|
|
||||||
return "namespace", fmt.Errorf("namespace cannot be empty")
|
|
||||||
}
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validatePattern(patternElement interface{}, path string, supportedAnchors []anchor.IsAnchor) (string, error) {
|
|
||||||
switch typedPatternElement := patternElement.(type) {
|
|
||||||
case map[string]interface{}:
|
|
||||||
return validateMap(typedPatternElement, path, supportedAnchors)
|
|
||||||
case []interface{}:
|
|
||||||
return validateArray(typedPatternElement, path, supportedAnchors)
|
|
||||||
case string, float64, int, int64, bool, nil:
|
|
||||||
//TODO? check operator
|
|
||||||
return "", nil
|
|
||||||
default:
|
|
||||||
return path, fmt.Errorf("Validation rule failed at '%s', pattern contains unknown type", path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateMap(patternMap map[string]interface{}, path string, supportedAnchors []anchor.IsAnchor) (string, error) {
|
|
||||||
// check if anchors are defined
|
|
||||||
for key, value := range patternMap {
|
|
||||||
// if key is anchor
|
|
||||||
// check regex () -> this is anchor
|
|
||||||
// ()
|
|
||||||
// single char ()
|
|
||||||
re, err := regexp.Compile(`^.?\(.+\)$`)
|
|
||||||
if err != nil {
|
|
||||||
return path + "/" + key, fmt.Errorf("Unable to parse the field %s: %v", key, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
matched := re.MatchString(key)
|
|
||||||
// check the type of anchor
|
|
||||||
if matched {
|
|
||||||
// some type of anchor
|
|
||||||
// check if valid anchor
|
|
||||||
if !checkAnchors(key, supportedAnchors) {
|
|
||||||
return path + "/" + key, fmt.Errorf("Unsupported anchor %s", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// addition check for existence anchor
|
|
||||||
// value must be of type list
|
|
||||||
if anchor.IsExistenceAnchor(key) {
|
|
||||||
typedValue, ok := value.([]interface{})
|
|
||||||
if !ok {
|
|
||||||
return path + "/" + key, fmt.Errorf("Existence anchor should have value of type list")
|
|
||||||
}
|
|
||||||
// validate there is only one entry in the list
|
|
||||||
if len(typedValue) == 0 || len(typedValue) > 1 {
|
|
||||||
return path + "/" + key, fmt.Errorf("Existence anchor: single value expected, multiple specified")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// lets validate the values now :)
|
|
||||||
if errPath, err := validatePattern(value, path+"/"+key, supportedAnchors); err != nil {
|
|
||||||
return errPath, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateArray(patternArray []interface{}, path string, supportedAnchors []anchor.IsAnchor) (string, error) {
|
|
||||||
for i, patternElement := range patternArray {
|
|
||||||
currentPath := path + strconv.Itoa(i) + "/"
|
|
||||||
// lets validate the values now :)
|
|
||||||
if errPath, err := validatePattern(patternElement, currentPath, supportedAnchors); err != nil {
|
|
||||||
return errPath, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAnchors(key string, supportedAnchors []anchor.IsAnchor) bool {
|
|
||||||
for _, f := range supportedAnchors {
|
|
||||||
if f(key) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
61
pkg/policy/validate/validate.go
Normal file
61
pkg/policy/validate/validate.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package validate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
|
"github.com/nirmata/kyverno/pkg/engine/anchor"
|
||||||
|
"github.com/nirmata/kyverno/pkg/policy/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Validate provides implementation to validate 'validate' rule
|
||||||
|
type Validate struct {
|
||||||
|
// rule to hold 'validate' rule specifications
|
||||||
|
rule kyverno.Validation
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewValidateFactory returns a new instance of Mutate validation checker
|
||||||
|
func NewValidateFactory(rule kyverno.Validation) *Validate {
|
||||||
|
m := Validate{
|
||||||
|
rule: rule,
|
||||||
|
}
|
||||||
|
return &m
|
||||||
|
}
|
||||||
|
|
||||||
|
//Validate validates the 'validate' rule
|
||||||
|
func (v *Validate) Validate() (string, error) {
|
||||||
|
rule := v.rule
|
||||||
|
if err := v.validateOverlayPattern(); err != nil {
|
||||||
|
// no need to proceed ahead
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if rule.Pattern != nil {
|
||||||
|
if path, err := common.ValidatePattern(rule.Pattern, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsExistenceAnchor, anchor.IsEqualityAnchor, anchor.IsNegationAnchor}); err != nil {
|
||||||
|
return fmt.Sprintf("pattern.%s", path), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(rule.AnyPattern) != 0 {
|
||||||
|
for i, pattern := range rule.AnyPattern {
|
||||||
|
if path, err := common.ValidatePattern(pattern, "/", []anchor.IsAnchor{anchor.IsConditionAnchor, anchor.IsExistenceAnchor, anchor.IsEqualityAnchor, anchor.IsNegationAnchor}); err != nil {
|
||||||
|
return fmt.Sprintf("anyPattern[%d].%s", i, path), err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateOverlayPattern checks one of pattern/anyPattern must exist
|
||||||
|
func (v *Validate) validateOverlayPattern() error {
|
||||||
|
rule := v.rule
|
||||||
|
if rule.Pattern == nil && len(rule.AnyPattern) == 0 {
|
||||||
|
return fmt.Errorf("a pattern or anyPattern must be specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
if rule.Pattern != nil && len(rule.AnyPattern) != 0 {
|
||||||
|
return fmt.Errorf("only one operation allowed per validation rule(pattern or anyPattern)")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
381
pkg/policy/validate/validate_test.go
Normal file
381
pkg/policy/validate/validate_test.go
Normal file
|
@ -0,0 +1,381 @@
|
||||||
|
package validate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
|
||||||
|
"gotest.tools/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Validate_OverlayPattern_Empty(t *testing.T) {
|
||||||
|
rawValidation := []byte(`
|
||||||
|
{}`)
|
||||||
|
|
||||||
|
var validation kyverno.Validation
|
||||||
|
err := json.Unmarshal(rawValidation, &validation)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
checker := NewValidateFactory(validation)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.Assert(t, err != nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func Test_Validate_OverlayPattern_Nil_PatternAnypattern(t *testing.T) {
|
||||||
|
rawValidation := []byte(`
|
||||||
|
{ "message": "Privileged mode is not allowed. Set allowPrivilegeEscalation and privileged to false"
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
var validation kyverno.Validation
|
||||||
|
err := json.Unmarshal(rawValidation, &validation)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
checker := NewValidateFactory(validation)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.Assert(t, err != nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Validate_OverlayPattern_Exist_PatternAnypattern(t *testing.T) {
|
||||||
|
rawValidation := []byte(`
|
||||||
|
{
|
||||||
|
"message": "Privileged mode is not allowed. Set allowPrivilegeEscalation and privileged to false",
|
||||||
|
"anyPattern": [
|
||||||
|
{
|
||||||
|
"spec": {
|
||||||
|
"securityContext": {
|
||||||
|
"allowPrivilegeEscalation": false,
|
||||||
|
"privileged": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pattern": {
|
||||||
|
"spec": {
|
||||||
|
"containers": [
|
||||||
|
{
|
||||||
|
"name": "*",
|
||||||
|
"securityContext": {
|
||||||
|
"allowPrivilegeEscalation": false,
|
||||||
|
"privileged": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
var validation kyverno.Validation
|
||||||
|
err := json.Unmarshal(rawValidation, &validation)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
checker := NewValidateFactory(validation)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.Assert(t, err != nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func Test_Validate_OverlayPattern_Valid(t *testing.T) {
|
||||||
|
rawValidation := []byte(`
|
||||||
|
{
|
||||||
|
"message": "Privileged mode is not allowed. Set allowPrivilegeEscalation and privileged to false",
|
||||||
|
"anyPattern": [
|
||||||
|
{
|
||||||
|
"spec": {
|
||||||
|
"securityContext": {
|
||||||
|
"allowPrivilegeEscalation": false,
|
||||||
|
"privileged": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"spec": {
|
||||||
|
"containers": [
|
||||||
|
{
|
||||||
|
"name": "*",
|
||||||
|
"securityContext": {
|
||||||
|
"allowPrivilegeEscalation": false,
|
||||||
|
"privileged": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
var validation kyverno.Validation
|
||||||
|
err := json.Unmarshal(rawValidation, &validation)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
checker := NewValidateFactory(validation)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.NilError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Validate_ExistingAnchor_AnchorOnMap(t *testing.T) {
|
||||||
|
rawValidation := []byte(`
|
||||||
|
{
|
||||||
|
"message": "validate container security contexts",
|
||||||
|
"anyPattern": [
|
||||||
|
{
|
||||||
|
"spec": {
|
||||||
|
"template": {
|
||||||
|
"spec": {
|
||||||
|
"containers": [
|
||||||
|
{
|
||||||
|
"^(securityContext)": {
|
||||||
|
"runAsNonRoot": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
var validation kyverno.Validation
|
||||||
|
err := json.Unmarshal(rawValidation, &validation)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
checker := NewValidateFactory(validation)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.Assert(t, err != nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Validate_ExistingAnchor_AnchorOnString(t *testing.T) {
|
||||||
|
rawValidation := []byte(`{
|
||||||
|
"message": "validate container security contexts",
|
||||||
|
"pattern": {
|
||||||
|
"spec": {
|
||||||
|
"template": {
|
||||||
|
"spec": {
|
||||||
|
"containers": [
|
||||||
|
{
|
||||||
|
"securityContext": {
|
||||||
|
"allowPrivilegeEscalation": "^(false)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
|
||||||
|
var validation kyverno.Validation
|
||||||
|
err := json.Unmarshal(rawValidation, &validation)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
checker := NewValidateFactory(validation)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.Assert(t, err != nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Validate_ExistingAnchor_Valid(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
var validation kyverno.Validation
|
||||||
|
rawValidation := []byte(`
|
||||||
|
{
|
||||||
|
"message": "validate container security contexts",
|
||||||
|
"anyPattern": [
|
||||||
|
{
|
||||||
|
"spec": {
|
||||||
|
"template": {
|
||||||
|
"spec": {
|
||||||
|
"^(containers)": [
|
||||||
|
{
|
||||||
|
"securityContext": {
|
||||||
|
"runAsNonRoot": "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
|
||||||
|
err = json.Unmarshal(rawValidation, &validation)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
checker := NewValidateFactory(validation)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.Assert(t, err != nil)
|
||||||
|
}
|
||||||
|
rawValidation = []byte(`
|
||||||
|
{
|
||||||
|
"message": "validate container security contexts",
|
||||||
|
"pattern": {
|
||||||
|
"spec": {
|
||||||
|
"template": {
|
||||||
|
"spec": {
|
||||||
|
"^(containers)": [
|
||||||
|
{
|
||||||
|
"securityContext": {
|
||||||
|
"allowPrivilegeEscalation": "false"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} `)
|
||||||
|
err = json.Unmarshal(rawValidation, &validation)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
checker = NewValidateFactory(validation)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.Assert(t, err != nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Validate_Validate_ValidAnchor(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
var validate kyverno.Validation
|
||||||
|
var rawValidate []byte
|
||||||
|
// case 1
|
||||||
|
rawValidate = []byte(`
|
||||||
|
{
|
||||||
|
"message": "Root user is not allowed. Set runAsNonRoot to true.",
|
||||||
|
"anyPattern": [
|
||||||
|
{
|
||||||
|
"spec": {
|
||||||
|
"securityContext": {
|
||||||
|
"(runAsNonRoot)": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"spec": {
|
||||||
|
"^(containers)": [
|
||||||
|
{
|
||||||
|
"name": "*",
|
||||||
|
"securityContext": {
|
||||||
|
"runAsNonRoot": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
|
||||||
|
err = json.Unmarshal(rawValidate, &validate)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
checker := NewValidateFactory(validate)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.NilError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// case 2
|
||||||
|
validate = kyverno.Validation{}
|
||||||
|
rawValidate = []byte(`
|
||||||
|
{
|
||||||
|
"message": "Root user is not allowed. Set runAsNonRoot to true.",
|
||||||
|
"pattern": {
|
||||||
|
"spec": {
|
||||||
|
"=(securityContext)": {
|
||||||
|
"runAsNonRoot": "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
err = json.Unmarshal(rawValidate, &validate)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
checker = NewValidateFactory(validate)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.NilError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Validate_Validate_Mismatched(t *testing.T) {
|
||||||
|
rawValidate := []byte(`
|
||||||
|
{
|
||||||
|
"message": "Root user is not allowed. Set runAsNonRoot to true.",
|
||||||
|
"pattern": {
|
||||||
|
"spec": {
|
||||||
|
"containers": [
|
||||||
|
{
|
||||||
|
"name": "*",
|
||||||
|
"securityContext": {
|
||||||
|
"+(runAsNonRoot)": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
var validate kyverno.Validation
|
||||||
|
err := json.Unmarshal(rawValidate, &validate)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
checker := NewValidateFactory(validate)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.Assert(t, err != nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Validate_Validate_Unsupported(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
var validate kyverno.Validation
|
||||||
|
|
||||||
|
// case 1
|
||||||
|
rawValidate := []byte(`
|
||||||
|
{
|
||||||
|
"message": "Root user is not allowed. Set runAsNonRoot to true.",
|
||||||
|
"pattern": {
|
||||||
|
"spec": {
|
||||||
|
"containers": [
|
||||||
|
{
|
||||||
|
"name": "*",
|
||||||
|
"securityContext": {
|
||||||
|
"!(runAsNonRoot)": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
err = json.Unmarshal(rawValidate, &validate)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
checker := NewValidateFactory(validate)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.Assert(t, err != nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// case 2
|
||||||
|
rawValidate = []byte(`
|
||||||
|
{
|
||||||
|
"message": "Root user is not allowed. Set runAsNonRoot to true.",
|
||||||
|
"pattern": {
|
||||||
|
"spec": {
|
||||||
|
"containers": [
|
||||||
|
{
|
||||||
|
"name": "*",
|
||||||
|
"securityContext": {
|
||||||
|
"~(runAsNonRoot)": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
|
err = json.Unmarshal(rawValidate, &validate)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
checker = NewValidateFactory(validate)
|
||||||
|
if _, err := checker.Validate(); err != nil {
|
||||||
|
assert.Assert(t, err != nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -287,369 +287,6 @@ func Test_Validate_ResourceDescription_InvalidSelector(t *testing.T) {
|
||||||
assert.Assert(t, err != nil)
|
assert.Assert(t, err != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Validate_OverlayPattern_Empty(t *testing.T) {
|
|
||||||
rawValidation := []byte(`
|
|
||||||
{}`)
|
|
||||||
|
|
||||||
var validation kyverno.Validation
|
|
||||||
err := json.Unmarshal(rawValidation, &validation)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
|
|
||||||
if _, err := validateValidation(validation); err != nil {
|
|
||||||
assert.Assert(t, err != nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Validate_OverlayPattern_Nil_PatternAnypattern(t *testing.T) {
|
|
||||||
rawValidation := []byte(`
|
|
||||||
{ "message": "Privileged mode is not allowed. Set allowPrivilegeEscalation and privileged to false"
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
var validation kyverno.Validation
|
|
||||||
err := json.Unmarshal(rawValidation, &validation)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
if _, err := validateValidation(validation); err != nil {
|
|
||||||
assert.Assert(t, err != nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Validate_OverlayPattern_Exist_PatternAnypattern(t *testing.T) {
|
|
||||||
rawValidation := []byte(`
|
|
||||||
{
|
|
||||||
"message": "Privileged mode is not allowed. Set allowPrivilegeEscalation and privileged to false",
|
|
||||||
"anyPattern": [
|
|
||||||
{
|
|
||||||
"spec": {
|
|
||||||
"securityContext": {
|
|
||||||
"allowPrivilegeEscalation": false,
|
|
||||||
"privileged": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"pattern": {
|
|
||||||
"spec": {
|
|
||||||
"containers": [
|
|
||||||
{
|
|
||||||
"name": "*",
|
|
||||||
"securityContext": {
|
|
||||||
"allowPrivilegeEscalation": false,
|
|
||||||
"privileged": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
var validation kyverno.Validation
|
|
||||||
err := json.Unmarshal(rawValidation, &validation)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
if _, err := validateValidation(validation); err != nil {
|
|
||||||
assert.Assert(t, err != nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Validate_OverlayPattern_Valid(t *testing.T) {
|
|
||||||
rawValidation := []byte(`
|
|
||||||
{
|
|
||||||
"message": "Privileged mode is not allowed. Set allowPrivilegeEscalation and privileged to false",
|
|
||||||
"anyPattern": [
|
|
||||||
{
|
|
||||||
"spec": {
|
|
||||||
"securityContext": {
|
|
||||||
"allowPrivilegeEscalation": false,
|
|
||||||
"privileged": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"spec": {
|
|
||||||
"containers": [
|
|
||||||
{
|
|
||||||
"name": "*",
|
|
||||||
"securityContext": {
|
|
||||||
"allowPrivilegeEscalation": false,
|
|
||||||
"privileged": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
var validation kyverno.Validation
|
|
||||||
err := json.Unmarshal(rawValidation, &validation)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
if _, err := validateValidation(validation); err != nil {
|
|
||||||
assert.NilError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Validate_ExistingAnchor_AnchorOnMap(t *testing.T) {
|
|
||||||
rawValidation := []byte(`
|
|
||||||
{
|
|
||||||
"message": "validate container security contexts",
|
|
||||||
"anyPattern": [
|
|
||||||
{
|
|
||||||
"spec": {
|
|
||||||
"template": {
|
|
||||||
"spec": {
|
|
||||||
"containers": [
|
|
||||||
{
|
|
||||||
"^(securityContext)": {
|
|
||||||
"runAsNonRoot": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
var validation kyverno.Validation
|
|
||||||
err := json.Unmarshal(rawValidation, &validation)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
if _, err := validateValidation(validation); err != nil {
|
|
||||||
assert.Assert(t, err != nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Validate_ExistingAnchor_AnchorOnString(t *testing.T) {
|
|
||||||
rawValidation := []byte(`{
|
|
||||||
"message": "validate container security contexts",
|
|
||||||
"pattern": {
|
|
||||||
"spec": {
|
|
||||||
"template": {
|
|
||||||
"spec": {
|
|
||||||
"containers": [
|
|
||||||
{
|
|
||||||
"securityContext": {
|
|
||||||
"allowPrivilegeEscalation": "^(false)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`)
|
|
||||||
|
|
||||||
var validation kyverno.Validation
|
|
||||||
err := json.Unmarshal(rawValidation, &validation)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
if _, err := validateValidation(validation); err != nil {
|
|
||||||
assert.Assert(t, err != nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Validate_ExistingAnchor_Valid(t *testing.T) {
|
|
||||||
var err error
|
|
||||||
var validation kyverno.Validation
|
|
||||||
rawValidation := []byte(`
|
|
||||||
{
|
|
||||||
"message": "validate container security contexts",
|
|
||||||
"anyPattern": [
|
|
||||||
{
|
|
||||||
"spec": {
|
|
||||||
"template": {
|
|
||||||
"spec": {
|
|
||||||
"^(containers)": [
|
|
||||||
{
|
|
||||||
"securityContext": {
|
|
||||||
"runAsNonRoot": "true"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`)
|
|
||||||
|
|
||||||
err = json.Unmarshal(rawValidation, &validation)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
if _, err := validateValidation(validation); err != nil {
|
|
||||||
assert.Assert(t, err != nil)
|
|
||||||
}
|
|
||||||
rawValidation = []byte(`
|
|
||||||
{
|
|
||||||
"message": "validate container security contexts",
|
|
||||||
"pattern": {
|
|
||||||
"spec": {
|
|
||||||
"template": {
|
|
||||||
"spec": {
|
|
||||||
"^(containers)": [
|
|
||||||
{
|
|
||||||
"securityContext": {
|
|
||||||
"allowPrivilegeEscalation": "false"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} `)
|
|
||||||
err = json.Unmarshal(rawValidation, &validation)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
if _, err := validateValidation(validation); err != nil {
|
|
||||||
assert.Assert(t, err != nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Validate_Validate_ValidAnchor(t *testing.T) {
|
|
||||||
var err error
|
|
||||||
var validate kyverno.Validation
|
|
||||||
var rawValidate []byte
|
|
||||||
// case 1
|
|
||||||
rawValidate = []byte(`
|
|
||||||
{
|
|
||||||
"message": "Root user is not allowed. Set runAsNonRoot to true.",
|
|
||||||
"anyPattern": [
|
|
||||||
{
|
|
||||||
"spec": {
|
|
||||||
"securityContext": {
|
|
||||||
"(runAsNonRoot)": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"spec": {
|
|
||||||
"^(containers)": [
|
|
||||||
{
|
|
||||||
"name": "*",
|
|
||||||
"securityContext": {
|
|
||||||
"runAsNonRoot": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`)
|
|
||||||
|
|
||||||
err = json.Unmarshal(rawValidate, &validate)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
|
|
||||||
if _, err := validateValidation(validate); err != nil {
|
|
||||||
assert.NilError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// case 2
|
|
||||||
validate = kyverno.Validation{}
|
|
||||||
rawValidate = []byte(`
|
|
||||||
{
|
|
||||||
"message": "Root user is not allowed. Set runAsNonRoot to true.",
|
|
||||||
"pattern": {
|
|
||||||
"spec": {
|
|
||||||
"=(securityContext)": {
|
|
||||||
"runAsNonRoot": "true"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
err = json.Unmarshal(rawValidate, &validate)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
|
|
||||||
if _, err := validateValidation(validate); err != nil {
|
|
||||||
assert.NilError(t, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Validate_Validate_Mismatched(t *testing.T) {
|
|
||||||
rawValidate := []byte(`
|
|
||||||
{
|
|
||||||
"message": "Root user is not allowed. Set runAsNonRoot to true.",
|
|
||||||
"pattern": {
|
|
||||||
"spec": {
|
|
||||||
"containers": [
|
|
||||||
{
|
|
||||||
"name": "*",
|
|
||||||
"securityContext": {
|
|
||||||
"+(runAsNonRoot)": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
var validate kyverno.Validation
|
|
||||||
err := json.Unmarshal(rawValidate, &validate)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
if _, err := validateValidation(validate); err != nil {
|
|
||||||
assert.Assert(t, err != nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Validate_Validate_Unsupported(t *testing.T) {
|
|
||||||
var err error
|
|
||||||
var validate kyverno.Validation
|
|
||||||
|
|
||||||
// case 1
|
|
||||||
rawValidate := []byte(`
|
|
||||||
{
|
|
||||||
"message": "Root user is not allowed. Set runAsNonRoot to true.",
|
|
||||||
"pattern": {
|
|
||||||
"spec": {
|
|
||||||
"containers": [
|
|
||||||
{
|
|
||||||
"name": "*",
|
|
||||||
"securityContext": {
|
|
||||||
"!(runAsNonRoot)": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
err = json.Unmarshal(rawValidate, &validate)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
if _, err := validateValidation(validate); err != nil {
|
|
||||||
assert.Assert(t, err != nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// case 2
|
|
||||||
rawValidate = []byte(`
|
|
||||||
{
|
|
||||||
"message": "Root user is not allowed. Set runAsNonRoot to true.",
|
|
||||||
"pattern": {
|
|
||||||
"spec": {
|
|
||||||
"containers": [
|
|
||||||
{
|
|
||||||
"name": "*",
|
|
||||||
"securityContext": {
|
|
||||||
"~(runAsNonRoot)": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
err = json.Unmarshal(rawValidate, &validate)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
|
|
||||||
if _, err := validateValidation(validate); err != nil {
|
|
||||||
assert.Assert(t, err != nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Validate_Policy(t *testing.T) {
|
func Test_Validate_Policy(t *testing.T) {
|
||||||
rawPolicy := []byte(`
|
rawPolicy := []byte(`
|
||||||
{
|
{
|
||||||
|
@ -736,225 +373,10 @@ func Test_Validate_Policy(t *testing.T) {
|
||||||
err := json.Unmarshal(rawPolicy, &policy)
|
err := json.Unmarshal(rawPolicy, &policy)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
err = Validate(policy)
|
err = Validate(policy, nil)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_Validate_Mutate_ConditionAnchor(t *testing.T) {
|
|
||||||
rawMutate := []byte(`
|
|
||||||
{
|
|
||||||
"overlay": {
|
|
||||||
"spec": {
|
|
||||||
"(serviceAccountName)": "*",
|
|
||||||
"automountServiceAccountToken": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
var mutate kyverno.Mutation
|
|
||||||
err := json.Unmarshal(rawMutate, &mutate)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
if _, err := validateMutation(mutate); err != nil {
|
|
||||||
assert.NilError(t, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Validate_Mutate_PlusAnchor(t *testing.T) {
|
|
||||||
rawMutate := []byte(`
|
|
||||||
{
|
|
||||||
"overlay": {
|
|
||||||
"spec": {
|
|
||||||
"+(serviceAccountName)": "*",
|
|
||||||
"automountServiceAccountToken": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
var mutate kyverno.Mutation
|
|
||||||
err := json.Unmarshal(rawMutate, &mutate)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
|
|
||||||
if _, err := validateMutation(mutate); err != nil {
|
|
||||||
assert.NilError(t, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Validate_Mutate_Mismatched(t *testing.T) {
|
|
||||||
rawMutate := []byte(`
|
|
||||||
{
|
|
||||||
"overlay": {
|
|
||||||
"spec": {
|
|
||||||
"^(serviceAccountName)": "*",
|
|
||||||
"automountServiceAccountToken": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
var mutateExistence kyverno.Mutation
|
|
||||||
err := json.Unmarshal(rawMutate, &mutateExistence)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
|
|
||||||
if _, err := validateMutation(mutateExistence); err != nil {
|
|
||||||
assert.Assert(t, err != nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
var mutateEqual kyverno.Mutation
|
|
||||||
rawMutate = []byte(`
|
|
||||||
{
|
|
||||||
"overlay": {
|
|
||||||
"spec": {
|
|
||||||
"=(serviceAccountName)": "*",
|
|
||||||
"automountServiceAccountToken": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
err = json.Unmarshal(rawMutate, &mutateEqual)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
|
|
||||||
if _, err := validateMutation(mutateEqual); err != nil {
|
|
||||||
assert.Assert(t, err != nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
var mutateNegation kyverno.Mutation
|
|
||||||
rawMutate = []byte(`
|
|
||||||
{
|
|
||||||
"overlay": {
|
|
||||||
"spec": {
|
|
||||||
"X(serviceAccountName)": "*",
|
|
||||||
"automountServiceAccountToken": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
err = json.Unmarshal(rawMutate, &mutateNegation)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
|
|
||||||
if _, err := validateMutation(mutateEqual); err != nil {
|
|
||||||
assert.Assert(t, err != nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Validate_Mutate_Unsupported(t *testing.T) {
|
|
||||||
var err error
|
|
||||||
var mutate kyverno.Mutation
|
|
||||||
// case 1
|
|
||||||
rawMutate := []byte(`
|
|
||||||
{
|
|
||||||
"overlay": {
|
|
||||||
"spec": {
|
|
||||||
"!(serviceAccountName)": "*",
|
|
||||||
"automountServiceAccountToken": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
err = json.Unmarshal(rawMutate, &mutate)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
|
|
||||||
if _, err := validateMutation(mutate); err != nil {
|
|
||||||
assert.Assert(t, err != nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// case 2
|
|
||||||
rawMutate = []byte(`
|
|
||||||
{
|
|
||||||
"overlay": {
|
|
||||||
"spec": {
|
|
||||||
"~(serviceAccountName)": "*",
|
|
||||||
"automountServiceAccountToken": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
err = json.Unmarshal(rawMutate, &mutate)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
|
|
||||||
if _, err := validateMutation(mutate); err != nil {
|
|
||||||
assert.Assert(t, err != nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Validate_Generate(t *testing.T) {
|
|
||||||
rawGenerate := []byte(`
|
|
||||||
{
|
|
||||||
"kind": "NetworkPolicy",
|
|
||||||
"name": "defaultnetworkpolicy",
|
|
||||||
"data": {
|
|
||||||
"spec": {
|
|
||||||
"podSelector": {},
|
|
||||||
"policyTypes": [
|
|
||||||
"Ingress",
|
|
||||||
"Egress"
|
|
||||||
],
|
|
||||||
"ingress": [
|
|
||||||
{}
|
|
||||||
],
|
|
||||||
"egress": [
|
|
||||||
{}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
var generate kyverno.Generation
|
|
||||||
err := json.Unmarshal(rawGenerate, &generate)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
|
|
||||||
if _, err := validateGeneration(generate); err != nil {
|
|
||||||
assert.Assert(t, err != nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Validate_Generate_HasAnchors(t *testing.T) {
|
|
||||||
var err error
|
|
||||||
var generate kyverno.Generation
|
|
||||||
rawGenerate := []byte(`
|
|
||||||
{
|
|
||||||
"kind": "NetworkPolicy",
|
|
||||||
"name": "defaultnetworkpolicy",
|
|
||||||
"data": {
|
|
||||||
"spec": {
|
|
||||||
"(podSelector)": {},
|
|
||||||
"policyTypes": [
|
|
||||||
"Ingress",
|
|
||||||
"Egress"
|
|
||||||
],
|
|
||||||
"ingress": [
|
|
||||||
{}
|
|
||||||
],
|
|
||||||
"egress": [
|
|
||||||
{}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
err = json.Unmarshal(rawGenerate, &generate)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
if _, err := validateGeneration(generate); err != nil {
|
|
||||||
assert.Assert(t, err != nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
rawGenerate = []byte(`
|
|
||||||
{
|
|
||||||
"kind": "ConfigMap",
|
|
||||||
"name": "copied-cm",
|
|
||||||
"clone": {
|
|
||||||
"^(namespace)": "default",
|
|
||||||
"name": "game"
|
|
||||||
}
|
|
||||||
}`)
|
|
||||||
|
|
||||||
errNew := json.Unmarshal(rawGenerate, &generate)
|
|
||||||
assert.NilError(t, errNew)
|
|
||||||
err = json.Unmarshal(rawGenerate, &generate)
|
|
||||||
assert.NilError(t, err)
|
|
||||||
if _, err := validateGeneration(generate); err != nil {
|
|
||||||
assert.Assert(t, err != nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Validate_ErrorFormat(t *testing.T) {
|
func Test_Validate_ErrorFormat(t *testing.T) {
|
||||||
rawPolicy := []byte(`
|
rawPolicy := []byte(`
|
||||||
{
|
{
|
||||||
|
@ -1097,7 +519,7 @@ func Test_Validate_ErrorFormat(t *testing.T) {
|
||||||
err := json.Unmarshal(rawPolicy, &policy)
|
err := json.Unmarshal(rawPolicy, &policy)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
err = Validate(policy)
|
err = Validate(policy, nil)
|
||||||
assert.Assert(t, err != nil)
|
assert.Assert(t, err != nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ func (ws *WebhookServer) handlePolicyValidation(request *v1beta1.AdmissionReques
|
||||||
Message: fmt.Sprintf("Failed to unmarshal policy admission request err %v", err),
|
Message: fmt.Sprintf("Failed to unmarshal policy admission request err %v", err),
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
if err := policyvalidate.Validate(*policy); err != nil {
|
if err := policyvalidate.Validate(*policy, ws.client); err != nil {
|
||||||
admissionResp = &v1beta1.AdmissionResponse{
|
admissionResp = &v1beta1.AdmissionResponse{
|
||||||
Allowed: false,
|
Allowed: false,
|
||||||
Result: &metav1.Status{
|
Result: &metav1.Status{
|
||||||
|
|
Loading…
Add table
Reference in a new issue