mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-28 02:18:15 +00:00
fix: target scope validation for the generate rule (#7479)
* fix target scope validation for generate Signed-off-by: ShutingZhao <shuting@nirmata.com> * add kuttl tests Signed-off-by: ShutingZhao <shuting@nirmata.com> --------- Signed-off-by: ShutingZhao <shuting@nirmata.com>
This commit is contained in:
parent
5fa6e1fa48
commit
5ce80c4e68
34 changed files with 777 additions and 243 deletions
|
@ -620,9 +620,15 @@ type CloneList struct {
|
|||
Selector *metav1.LabelSelector `json:"selector,omitempty" yaml:"selector,omitempty"`
|
||||
}
|
||||
|
||||
func (g *Generation) Validate(path *field.Path, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
if err := g.validateTargetsScope(clusterResources); err != nil {
|
||||
errs = append(errs, field.Forbidden(path.Child("generate").Child("namespace"), fmt.Sprintf("target resource scope mismatched: %v ", err)))
|
||||
func (g *Generation) Validate(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
if namespaced {
|
||||
if err := g.validateTargetsScope(clusterResources, policyNamespace); err != nil {
|
||||
errs = append(errs, field.Forbidden(path.Child("generate").Child("namespace"), fmt.Sprintf("target resource scope mismatched: %v ", err)))
|
||||
}
|
||||
} else {
|
||||
if g.GetNamespace() == "" && g.CloneList.Namespace == "" {
|
||||
errs = append(errs, field.Forbidden(path.Child("generate"), "target namespace must be set in a clusterpolicy"))
|
||||
}
|
||||
}
|
||||
|
||||
generateType, _ := g.GetTypeAndSync()
|
||||
|
@ -662,15 +668,28 @@ func (g *Generation) SetData(in apiextensions.JSON) {
|
|||
g.RawData = ToJSON(in)
|
||||
}
|
||||
|
||||
func (g *Generation) validateTargetsScope(clusterResources sets.Set[string]) error {
|
||||
func (g *Generation) validateTargetsScope(clusterResources sets.Set[string], policyNamespace string) error {
|
||||
target := g.ResourceSpec
|
||||
if clusterResources.Has(target.GetKind()) {
|
||||
if target.GetNamespace() != "" {
|
||||
return fmt.Errorf("the target namespace must not be set for cluster-wide resource: %v", target.GetKind())
|
||||
if clusterResources.Has(target.GetAPIVersion() + "/" + target.GetKind()) {
|
||||
return fmt.Errorf("the target must be a namespaced resource: %v/%v", target.GetAPIVersion(), target.GetKind())
|
||||
}
|
||||
|
||||
if g.GetNamespace() != policyNamespace {
|
||||
return fmt.Errorf("a namespaced policy cannot generate resources in other namespaces, expected: %v, received: %v", policyNamespace, g.GetNamespace())
|
||||
}
|
||||
|
||||
if g.Clone.Name != "" {
|
||||
if g.Clone.Namespace != policyNamespace {
|
||||
return fmt.Errorf("a namespaced policy cannot clone resources from other namespaces, expected: %v, received: %v", policyNamespace, g.Clone.Namespace)
|
||||
}
|
||||
} else {
|
||||
if target.GetNamespace() == "" {
|
||||
return fmt.Errorf("the target namespace must be set for namespaced resource: %v", target.GetKind())
|
||||
}
|
||||
|
||||
for _, kind := range g.CloneList.Kinds {
|
||||
if clusterResources.Has(kind) {
|
||||
return fmt.Errorf("the source in cloneList must be a namespaced resource: %v/%v", target.GetAPIVersion(), target.GetKind())
|
||||
}
|
||||
if g.CloneList.Namespace != policyNamespace {
|
||||
return fmt.Errorf("a namespaced policy cannot clone resources from other namespace, expected: %v, received: %v", policyNamespace, g.CloneList.Namespace)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1133,7 +1133,7 @@ func Test_Validate_ClusterPolicy_Generate_Variables(t *testing.T) {
|
|||
var rule *Rule
|
||||
err := json.Unmarshal(testcase.rule, &rule)
|
||||
assert.NilError(t, err, testcase.name)
|
||||
errs := rule.ValidateGenerate(path, nil)
|
||||
errs := rule.ValidateGenerate(path, false, "", nil)
|
||||
assert.Equal(t, len(errs) != 0, testcase.shouldFail, testcase.name)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -404,12 +404,12 @@ func (r *Rule) ValidatePSaControlNames(path *field.Path) (errs field.ErrorList)
|
|||
return errs
|
||||
}
|
||||
|
||||
func (r *Rule) ValidateGenerate(path *field.Path, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
func (r *Rule) ValidateGenerate(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
if !r.HasGenerate() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return r.Generation.Validate(path, clusterResources)
|
||||
return r.Generation.Validate(path, namespaced, policyNamespace, clusterResources)
|
||||
}
|
||||
|
||||
// Validate implements programmatic validation
|
||||
|
@ -420,6 +420,6 @@ func (r *Rule) Validate(path *field.Path, namespaced bool, policyNamespace strin
|
|||
errs = append(errs, r.ExcludeResources.Validate(path.Child("exclude"), namespaced, clusterResources)...)
|
||||
errs = append(errs, r.ValidateMutationRuleTargetNamespace(path, namespaced, policyNamespace)...)
|
||||
errs = append(errs, r.ValidatePSaControlNames(path)...)
|
||||
errs = append(errs, r.ValidateGenerate(path, clusterResources)...)
|
||||
errs = append(errs, r.ValidateGenerate(path, namespaced, policyNamespace, clusterResources)...)
|
||||
return errs
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ func (p *ClusterPolicy) IsReady() bool {
|
|||
func (p *ClusterPolicy) Validate(clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
errs = append(errs, kyvernov1.ValidateAutogenAnnotation(field.NewPath("metadata").Child("annotations"), p.GetAnnotations())...)
|
||||
errs = append(errs, kyvernov1.ValidatePolicyName(field.NewPath("name"), p.Name)...)
|
||||
errs = append(errs, p.Spec.Validate(field.NewPath("spec"), p.IsNamespaced(), clusterResources)...)
|
||||
errs = append(errs, p.Spec.Validate(field.NewPath("spec"), p.IsNamespaced(), p.Namespace, clusterResources)...)
|
||||
return errs
|
||||
}
|
||||
|
||||
|
|
|
@ -105,7 +105,7 @@ func (p *Policy) IsReady() bool {
|
|||
func (p *Policy) Validate(clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
errs = append(errs, kyvernov1.ValidateAutogenAnnotation(field.NewPath("metadata").Child("annotations"), p.GetAnnotations())...)
|
||||
errs = append(errs, kyvernov1.ValidatePolicyName(field.NewPath("name"), p.Name)...)
|
||||
errs = append(errs, p.Spec.Validate(field.NewPath("spec"), p.IsNamespaced(), clusterResources)...)
|
||||
errs = append(errs, p.Spec.Validate(field.NewPath("spec"), p.IsNamespaced(), p.Namespace, clusterResources)...)
|
||||
return errs
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ func Test_Validate_RuleType_EmptyRule(t *testing.T) {
|
|||
Name: "validate-user-privilege",
|
||||
}
|
||||
path := field.NewPath("dummy")
|
||||
errs := subject.Validate(path, false, nil)
|
||||
errs := subject.Validate(path, false, "", nil)
|
||||
assert.Equal(t, len(errs), 1)
|
||||
assert.Equal(t, errs[0].Field, "dummy")
|
||||
assert.Equal(t, errs[0].Type, field.ErrorTypeInvalid)
|
||||
|
@ -94,7 +94,7 @@ func Test_Validate_RuleType_MultipleRule(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
path := field.NewPath("dummy")
|
||||
errs := rule.Validate(path, false, nil)
|
||||
errs := rule.Validate(path, false, "", nil)
|
||||
assert.Assert(t, len(errs) != 0)
|
||||
}
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ func Test_Validate_RuleType_SingleRule(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
for _, rule := range policy.Spec.Rules {
|
||||
path := field.NewPath("dummy")
|
||||
errs := rule.Validate(path, false, nil)
|
||||
errs := rule.Validate(path, false, "", nil)
|
||||
assert.Assert(t, len(errs) == 0)
|
||||
}
|
||||
}
|
||||
|
@ -534,7 +534,7 @@ func Test_Validate_ClusterPolicy_Generate_Variables(t *testing.T) {
|
|||
var rule *Rule
|
||||
err := json.Unmarshal(testcase.rule, &rule)
|
||||
assert.NilError(t, err, testcase.name)
|
||||
errs := rule.ValidateGenerate(path, nil)
|
||||
errs := rule.ValidateGenerate(path, false, "", nil)
|
||||
assert.Equal(t, len(errs) != 0, testcase.shouldFail, testcase.name)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -183,20 +183,20 @@ func (r *Rule) ValidateMatchExcludeConflict(path *field.Path) (errs field.ErrorL
|
|||
return append(errs, field.Invalid(path, r, "Rule is matching an empty set"))
|
||||
}
|
||||
|
||||
func (r *Rule) ValidateGenerate(path *field.Path, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
func (r *Rule) ValidateGenerate(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
if !r.HasGenerate() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return r.Generation.Validate(path, clusterResources)
|
||||
return r.Generation.Validate(path, namespaced, policyNamespace, clusterResources)
|
||||
}
|
||||
|
||||
// Validate implements programmatic validation
|
||||
func (r *Rule) Validate(path *field.Path, namespaced bool, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
func (r *Rule) Validate(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
errs = append(errs, r.ValidateRuleType(path)...)
|
||||
errs = append(errs, r.ValidateMatchExcludeConflict(path)...)
|
||||
errs = append(errs, r.MatchResources.Validate(path.Child("match"), namespaced, clusterResources)...)
|
||||
errs = append(errs, r.ExcludeResources.Validate(path.Child("exclude"), namespaced, clusterResources)...)
|
||||
errs = append(errs, r.ValidateGenerate(path, clusterResources)...)
|
||||
errs = append(errs, r.ValidateGenerate(path, namespaced, policyNamespace, clusterResources)...)
|
||||
return errs
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ func Test_Validate_UniqueRuleName(t *testing.T) {
|
|||
}},
|
||||
}
|
||||
path := field.NewPath("dummy")
|
||||
errs := subject.Validate(path, false, nil)
|
||||
errs := subject.Validate(path, false, "", nil)
|
||||
assert.Equal(t, len(errs), 1)
|
||||
assert.Equal(t, errs[0].Field, "dummy.rules[1].name")
|
||||
assert.Equal(t, errs[0].Type, field.ErrorTypeInvalid)
|
||||
|
|
|
@ -216,10 +216,10 @@ func (s *Spec) ValidateRuleNames(path *field.Path) (errs field.ErrorList) {
|
|||
}
|
||||
|
||||
// ValidateRules implements programmatic validation of Rules
|
||||
func (s *Spec) ValidateRules(path *field.Path, namespaced bool, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
func (s *Spec) ValidateRules(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
errs = append(errs, s.ValidateRuleNames(path)...)
|
||||
for i, rule := range s.Rules {
|
||||
errs = append(errs, rule.Validate(path.Index(i), namespaced, clusterResources)...)
|
||||
errs = append(errs, rule.Validate(path.Index(i), namespaced, policyNamespace, clusterResources)...)
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
@ -232,14 +232,14 @@ func (s *Spec) ValidateDeprecatedFields(path *field.Path) (errs field.ErrorList)
|
|||
}
|
||||
|
||||
// Validate implements programmatic validation
|
||||
func (s *Spec) Validate(path *field.Path, namespaced bool, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
func (s *Spec) Validate(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||
if err := s.ValidateDeprecatedFields(path); err != nil {
|
||||
errs = append(errs, err...)
|
||||
}
|
||||
if s.WebhookTimeoutSeconds != nil && (*s.WebhookTimeoutSeconds < 1 || *s.WebhookTimeoutSeconds > 30) {
|
||||
errs = append(errs, field.Invalid(path.Child("webhookTimeoutSeconds"), s.WebhookTimeoutSeconds, "the timeout value must be between 1 and 30 seconds"))
|
||||
}
|
||||
errs = append(errs, s.ValidateRules(path.Child("rules"), namespaced, clusterResources)...)
|
||||
errs = append(errs, s.ValidateRules(path.Child("rules"), namespaced, policyNamespace, clusterResources)...)
|
||||
if namespaced && len(s.ValidationFailureActionOverrides) > 0 {
|
||||
errs = append(errs, field.Forbidden(path.Child("validationFailureActionOverrides"), "Use of validationFailureActionOverrides is supported only with ClusterPolicy"))
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ func FetchClusteredResources(logger logr.Logger, client dclient.Interface) (sets
|
|||
for _, resList := range res {
|
||||
for _, r := range resList.APIResources {
|
||||
if !r.Namespaced {
|
||||
clusterResources.Insert(resList.GroupVersion + "/" + r.Kind)
|
||||
clusterResources.Insert(r.Kind)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
||||
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
|
@ -48,63 +46,6 @@ func immutableGenerateFields(new, old kyvernov1.PolicyInterface) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// checkClusterResourceInMatchAndExclude returns false if namespaced ClusterPolicy contains cluster wide resources in
|
||||
// Match and Exclude block
|
||||
func checkClusterResourceInMatchAndExclude(rule kyvernov1.Rule, clusterResources sets.Set[string], policyNamespace string, mock bool, res []*metav1.APIResourceList) error {
|
||||
if !mock {
|
||||
// Check for generate policy
|
||||
// - if resource to be generated is namespaced resource then the namespace field
|
||||
// should be mentioned
|
||||
// - if resource to be generated is non namespaced resource then the namespace field
|
||||
// should not be mentioned
|
||||
if rule.HasGenerate() {
|
||||
generateResourceKind := rule.Generation.Kind
|
||||
for _, resList := range res {
|
||||
for _, r := range resList.APIResources {
|
||||
if r.Kind == generateResourceKind {
|
||||
if r.Namespaced {
|
||||
if rule.Generation.Namespace == "" {
|
||||
return fmt.Errorf("path: spec.rules[%v]: please mention the namespace to generate a namespaced resource", rule.Name)
|
||||
}
|
||||
if rule.Generation.Namespace != policyNamespace {
|
||||
return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot generate resources in other namespaces, expected: %v, received: %v", rule.Name, policyNamespace, rule.Generation.Namespace)
|
||||
}
|
||||
if rule.Generation.Clone.Name != "" {
|
||||
if rule.Generation.Clone.Namespace != policyNamespace {
|
||||
return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot clone resources to or from other namespaces, expected: %v, received: %v", rule.Name, policyNamespace, rule.Generation.Clone.Namespace)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if rule.Generation.Namespace != "" {
|
||||
return fmt.Errorf("path: spec.rules[%v]: do not mention the namespace to generate a non namespaced resource", rule.Name)
|
||||
}
|
||||
if policyNamespace != "" {
|
||||
return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot generate cluster-wide resources", rule.Name)
|
||||
}
|
||||
}
|
||||
} else if len(rule.Generation.CloneList.Kinds) != 0 {
|
||||
for _, kind := range rule.Generation.CloneList.Kinds {
|
||||
_, splitkind := kubeutils.GetKindFromGVK(kind)
|
||||
if r.Kind == splitkind {
|
||||
if r.Namespaced {
|
||||
if rule.Generation.CloneList.Namespace != policyNamespace {
|
||||
return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot clone resource in other namespace, expected: %v, received: %v", rule.Name, policyNamespace, rule.Generation.Namespace)
|
||||
}
|
||||
} else {
|
||||
if policyNamespace != "" {
|
||||
return fmt.Errorf("path: spec.rules[%v]: a namespaced policy cannot generate cluster-wide resources", rule.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func resetMutableFields(rule kyvernov1.Rule) *kyvernov1.Rule {
|
||||
new := new(kyvernov1.Rule)
|
||||
rule.DeepCopyInto(new)
|
||||
|
|
|
@ -165,6 +165,7 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
|
|||
for _, resList := range res {
|
||||
for _, r := range resList.APIResources {
|
||||
if !r.Namespaced {
|
||||
clusterResources.Insert(resList.GroupVersion + "/" + r.Kind)
|
||||
clusterResources.Insert(r.Kind)
|
||||
}
|
||||
}
|
||||
|
@ -277,14 +278,6 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
|
|||
}
|
||||
}
|
||||
|
||||
// validate Cluster Resources in namespaced policy
|
||||
// For namespaced policy, ClusterResource type field and values are not allowed in match and exclude
|
||||
if policy.IsNamespaced() {
|
||||
if err := checkClusterResourceInMatchAndExclude(rule, clusterResources, policy.GetNamespace(), mock, res); err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := validateActions(i, &rules[i], client, mock, username); err != nil {
|
||||
return warnings, err
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import (
|
|||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
|
@ -1094,136 +1093,6 @@ func Test_Namespced_Policy(t *testing.T) {
|
|||
assert.Assert(t, err != nil)
|
||||
}
|
||||
|
||||
func Test_Namespaced_Generate_Policy(t *testing.T) {
|
||||
testcases := []struct {
|
||||
description string
|
||||
rule []byte
|
||||
policyNamespace string
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
description: "Only generate resource where the policy exists",
|
||||
rule: []byte(`
|
||||
{"name": "gen-zk",
|
||||
"generate": {
|
||||
"synchronize": false,
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"name": "zk",
|
||||
"namespace": "default",
|
||||
"data": {
|
||||
"kind": "ConfigMap",
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"somekey": "somevalue"
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"ZK_ADDRESS": "192.168.10.10:2181",
|
||||
"KAFKA_ADDRESS": "192.168.10.13:9092"
|
||||
}
|
||||
}
|
||||
}
|
||||
}`),
|
||||
policyNamespace: "poltest",
|
||||
expectedError: errors.New("path: spec.rules[gen-zk]: a namespaced policy cannot generate resources in other namespaces, expected: poltest, received: default"),
|
||||
},
|
||||
{
|
||||
description: "Not allowed to clone resource outside the policy namespace",
|
||||
rule: []byte(`
|
||||
{
|
||||
"name": "sync-image-pull-secret",
|
||||
"generate": {
|
||||
"apiVersion": "v1",
|
||||
"kind": "Secret",
|
||||
"name": "secret-basic-auth-gen",
|
||||
"namespace": "poltest",
|
||||
"synchronize": true,
|
||||
"clone": {
|
||||
"namespace": "default",
|
||||
"name": "secret-basic-auth"
|
||||
}
|
||||
}
|
||||
}`),
|
||||
policyNamespace: "poltest",
|
||||
expectedError: errors.New("path: spec.rules[sync-image-pull-secret]: a namespaced policy cannot clone resources to or from other namespaces, expected: poltest, received: default"),
|
||||
},
|
||||
{
|
||||
description: "Do not mention the namespace to generate cluster scoped resource",
|
||||
rule: []byte(`
|
||||
{
|
||||
"name": "sync-clone",
|
||||
"generate": {
|
||||
"apiVersion": "storage.k8s.io/v1",
|
||||
"kind": "StorageClass",
|
||||
"name": "local-class",
|
||||
"namespace": "poltest",
|
||||
"synchronize": true,
|
||||
"clone": {
|
||||
"name": "pv-class"
|
||||
}
|
||||
}
|
||||
}`),
|
||||
policyNamespace: "poltest",
|
||||
expectedError: errors.New("path: spec.rules[sync-clone]: do not mention the namespace to generate a non namespaced resource"),
|
||||
},
|
||||
{
|
||||
description: "Not allowed to clone cluster scoped resource",
|
||||
rule: []byte(`
|
||||
{
|
||||
"name": "sync-clone",
|
||||
"generate": {
|
||||
"apiVersion": "storage.k8s.io/v1",
|
||||
"kind": "StorageClass",
|
||||
"name": "local-class",
|
||||
"synchronize": true,
|
||||
"clone": {
|
||||
"name": "pv-class"
|
||||
}
|
||||
}
|
||||
}`),
|
||||
policyNamespace: "poltest",
|
||||
expectedError: errors.New("path: spec.rules[sync-clone]: a namespaced policy cannot generate cluster-wide resources"),
|
||||
},
|
||||
{
|
||||
description: "Not allowed to multi clone cluster scoped resource",
|
||||
rule: []byte(`
|
||||
{
|
||||
"name": "sync-multi-clone",
|
||||
"generate": {
|
||||
"namespace": "staging",
|
||||
"synchronize": true,
|
||||
"cloneList": {
|
||||
"namespace": "staging",
|
||||
"kinds": [
|
||||
"storage.k8s.io/v1/StorageClass"
|
||||
],
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"allowedToBeCloned": "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`),
|
||||
policyNamespace: "staging",
|
||||
expectedError: errors.New("path: spec.rules[sync-multi-clone]: a namespaced policy cannot generate cluster-wide resources"),
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.description, func(t *testing.T) {
|
||||
var rule kyverno.Rule
|
||||
_ = json.Unmarshal(tc.rule, &rule)
|
||||
err := checkClusterResourceInMatchAndExclude(rule, sets.New[string](), tc.policyNamespace, false, testResourceList())
|
||||
if tc.expectedError != nil {
|
||||
assert.Error(t, err, tc.expectedError.Error())
|
||||
} else {
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_patchesJson6902_Policy(t *testing.T) {
|
||||
rawPolicy := []byte(`
|
||||
{
|
||||
|
|
|
@ -1,15 +1,27 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: Policy
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: create-default-pdb
|
||||
namespace: hello-world
|
||||
annotations:
|
||||
policies.kyverno.io/title: Add Pod Disruption Budget
|
||||
policies.kyverno.io/category: Sample
|
||||
kyverno.io/kyverno-version: 1.6.2
|
||||
policies.kyverno.io/minversion: 1.6.0
|
||||
policies.kyverno.io/subject: Deployment
|
||||
policies.kyverno.io/description: >-
|
||||
A PodDisruptionBudget limits the number of Pods of a replicated application that
|
||||
are down simultaneously from voluntary disruptions. For example, a quorum-based
|
||||
application would like to ensure that the number of replicas running is never brought
|
||||
below the number needed for a quorum. As an application owner, you can create a PodDisruptionBudget (PDB)
|
||||
for each application. This policy will create a PDB resource whenever a new Deployment is created.
|
||||
spec:
|
||||
rules:
|
||||
- name: create-default-pdb
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Deployment
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Deployment
|
||||
generate:
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: roles.iam.aws.crossplane.io
|
||||
spec:
|
||||
group: iam.aws.crossplane.io
|
||||
names:
|
||||
categories:
|
||||
- crossplane
|
||||
- managed
|
||||
- aws
|
||||
kind: Role
|
||||
listKind: RoleList
|
||||
plural: roles
|
||||
shortNames:
|
||||
- iamrole
|
||||
singular: role
|
||||
scope: Cluster
|
||||
versions:
|
||||
- additionalPrinterColumns:
|
||||
- jsonPath: .status.conditions[?(@.type=='Ready')].status
|
||||
name: READY
|
||||
type: string
|
||||
- jsonPath: .status.conditions[?(@.type=='Synced')].status
|
||||
name: SYNCED
|
||||
type: string
|
||||
- jsonPath: .metadata.creationTimestamp
|
||||
name: AGE
|
||||
type: date
|
||||
name: v1beta1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: An Role is a managed resource that represents an AWS IAM Role.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: An RoleSpec defines the desired state of an Role.
|
||||
properties:
|
||||
deletionPolicy:
|
||||
default: Delete
|
||||
description: DeletionPolicy specifies what will happen to the underlying
|
||||
external when this managed resource is deleted - either "Delete"
|
||||
or "Orphan" the external resource.
|
||||
enum:
|
||||
- Orphan
|
||||
- Delete
|
||||
type: string
|
||||
forProvider:
|
||||
description: RoleParameters define the desired state of an AWS IAM
|
||||
Role.
|
||||
properties:
|
||||
assumeRolePolicyDocument:
|
||||
description: AssumeRolePolicyDocument is the the trust relationship
|
||||
policy document that grants an entity permission to assume the
|
||||
role.
|
||||
type: string
|
||||
description:
|
||||
description: Description is a description of the role.
|
||||
type: string
|
||||
maxSessionDuration:
|
||||
description: 'MaxSessionDuration is the duration (in seconds)
|
||||
that you want to set for the specified role. The default maximum
|
||||
of one hour is applied. This setting can have a value from 1
|
||||
hour to 12 hours. Default: 3600'
|
||||
format: int32
|
||||
type: integer
|
||||
path:
|
||||
description: 'Path is the path to the role. Default: /'
|
||||
type: string
|
||||
permissionsBoundary:
|
||||
description: PermissionsBoundary is the ARN of the policy that
|
||||
is used to set the permissions boundary for the role.
|
||||
type: string
|
||||
tags:
|
||||
description: Tags. For more information about tagging, see Tagging
|
||||
IAM Identities (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html)
|
||||
in the IAM User Guide.
|
||||
items:
|
||||
description: Tag represents user-provided metadata that can
|
||||
be associated with a IAM role. For more information about
|
||||
tagging, see Tagging IAM Identities (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html)
|
||||
in the IAM User Guide.
|
||||
properties:
|
||||
key:
|
||||
description: The key name that can be used to look up or
|
||||
retrieve the associated value. For example, Department
|
||||
or Cost Center are common choices.
|
||||
type: string
|
||||
value:
|
||||
description: "The value associated with this tag. For example,
|
||||
tags with a key name of Department could have values such
|
||||
as Human Resources, Accounting, and Support. Tags with
|
||||
a key name of Cost Center might have values that consist
|
||||
of the number associated with the different cost centers
|
||||
in your company. Typically, many resources have tags with
|
||||
the same key name but with different values. \n AWS always
|
||||
interprets the tag Value as a single string. If you need
|
||||
to store an array, you can store comma-separated values
|
||||
in the string. However, you must interpret the value in
|
||||
your code."
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- assumeRolePolicyDocument
|
||||
type: object
|
||||
providerConfigRef:
|
||||
default:
|
||||
name: default
|
||||
description: ProviderConfigReference specifies how the provider that
|
||||
will be used to create, observe, update, and delete this managed
|
||||
resource should be configured.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the referenced object.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
providerRef:
|
||||
description: 'ProviderReference specifies the provider that will be
|
||||
used to create, observe, update, and delete this managed resource.
|
||||
Deprecated: Please use ProviderConfigReference, i.e. `providerConfigRef`'
|
||||
properties:
|
||||
name:
|
||||
description: Name of the referenced object.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
writeConnectionSecretToRef:
|
||||
description: WriteConnectionSecretToReference specifies the namespace
|
||||
and name of a Secret to which any connection details for this managed
|
||||
resource should be written. Connection details frequently include
|
||||
the endpoint, username, and password required to connect to the
|
||||
managed resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the secret.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace of the secret.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- namespace
|
||||
type: object
|
||||
required:
|
||||
- forProvider
|
||||
type: object
|
||||
status:
|
||||
description: An RoleStatus represents the observed state of an Role.
|
||||
properties:
|
||||
atProvider:
|
||||
description: RoleExternalStatus keeps the state for the external resource
|
||||
properties:
|
||||
arn:
|
||||
description: ARN is the Amazon Resource Name (ARN) specifying
|
||||
the role. For more information about ARNs and how to use them
|
||||
in policies, see IAM Identifiers (http://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html)
|
||||
in the IAM User Guide guide.
|
||||
type: string
|
||||
roleID:
|
||||
description: RoleID is the stable and unique string identifying
|
||||
the role. For more information about IDs, see IAM Identifiers
|
||||
(http://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html)
|
||||
in the Using IAM guide.
|
||||
type: string
|
||||
required:
|
||||
- arn
|
||||
- roleID
|
||||
type: object
|
||||
conditions:
|
||||
description: Conditions of the resource.
|
||||
items:
|
||||
description: A Condition that may apply to a resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: LastTransitionTime is the last time this condition
|
||||
transitioned from one status to another.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: A Message containing details about this condition's
|
||||
last transition from one status to another, if any.
|
||||
type: string
|
||||
reason:
|
||||
description: A Reason for this condition's last transition from
|
||||
one status to another.
|
||||
type: string
|
||||
status:
|
||||
description: Status of this condition; is it currently True,
|
||||
False, or Unknown?
|
||||
type: string
|
||||
type:
|
||||
description: Type of this condition. At most one of each condition
|
||||
type may apply to a resource at any point in time.
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions:
|
||||
- v1beta1
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kyverno:background-controller:additional
|
||||
labels:
|
||||
app.kubernetes.io/component: background-controller
|
||||
app.kubernetes.io/instance: kyverno
|
||||
app.kubernetes.io/part-of: kyverno
|
||||
rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
resources:
|
||||
- namespaces
|
||||
verbs:
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- get
|
||||
- list
|
|
@ -4,4 +4,6 @@ apply:
|
|||
- file: policy-namespaced-target.yaml
|
||||
shouldFail: true
|
||||
- file: policy-cluster-target.yaml
|
||||
shouldFail: true
|
||||
shouldFail: false
|
||||
- file: policy-pass.yaml
|
||||
shouldFail: false
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
delete:
|
||||
- apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
name: roles.iam.aws.crossplane.io
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
delete:
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
name: kyverno:background-controller:additional
|
|
@ -9,4 +9,5 @@ The test fails if the policy creation is allowed, otherwise passes.
|
|||
|
||||
## Reference Issue(s)
|
||||
|
||||
https://github.com/kyverno/kyverno/issues/7038
|
||||
https://github.com/kyverno/kyverno/issues/7038
|
||||
https://github.com/kyverno/kyverno/issues/7470
|
|
@ -0,0 +1,59 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: target-namespace-scope-pass-1
|
||||
spec:
|
||||
generateExistingOnPolicyUpdate: true
|
||||
rules:
|
||||
- generate:
|
||||
apiVersion: iam.aws.crossplane.io/v1beta1
|
||||
data:
|
||||
rules:
|
||||
- verbs:
|
||||
- "*"
|
||||
apiGroups:
|
||||
- "*"
|
||||
resources:
|
||||
- "*"
|
||||
kind: Role
|
||||
name: superuser
|
||||
namespace: "{{request.object.metadata.name}}"
|
||||
synchronize: true
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Namespace
|
||||
names:
|
||||
- dev-*
|
||||
name: role-per-namespace
|
||||
---
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: user-per-namespace-pass-2
|
||||
spec:
|
||||
generateExistingOnPolicyUpdate: true
|
||||
rules:
|
||||
- generate:
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
data:
|
||||
rules:
|
||||
- verbs:
|
||||
- "*"
|
||||
apiGroups:
|
||||
- "*"
|
||||
resources:
|
||||
- "*"
|
||||
kind: Role
|
||||
name: superuser
|
||||
namespace: "{{request.object.metadata.name}}"
|
||||
synchronize: true
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Namespace
|
||||
names:
|
||||
- dev-*
|
||||
name: role-per-namespace
|
|
@ -13,7 +13,7 @@ spec:
|
|||
kinds:
|
||||
- ConfigMap
|
||||
generate:
|
||||
namespace: "{{request.object.metadata.name}}"
|
||||
namespace: default
|
||||
synchronize : true
|
||||
cloneList:
|
||||
namespace: default
|
||||
|
|
|
@ -13,7 +13,7 @@ spec:
|
|||
kinds:
|
||||
- ConfigMap
|
||||
generate:
|
||||
namespace: "{{request.object.metadata.name}}"
|
||||
namespace: default
|
||||
synchronize : true
|
||||
cloneList:
|
||||
namespace: default
|
||||
|
|
|
@ -13,7 +13,7 @@ spec:
|
|||
kinds:
|
||||
- ConfigMap
|
||||
generate:
|
||||
namespace: "{{request.object.metadata.name}}"
|
||||
namespace: default
|
||||
synchronize : true
|
||||
cloneList:
|
||||
namespace: update-clonelist-ns
|
||||
|
|
|
@ -13,7 +13,7 @@ spec:
|
|||
kinds:
|
||||
-
|
||||
generate:
|
||||
namespace: "{{request.object.metadata.name}}"
|
||||
namespace: default
|
||||
synchronize : true
|
||||
cloneList:
|
||||
namespace: default
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: roles.iam.aws.crossplane.io
|
||||
spec:
|
||||
group: iam.aws.crossplane.io
|
||||
names:
|
||||
categories:
|
||||
- crossplane
|
||||
- managed
|
||||
- aws
|
||||
kind: Role
|
||||
listKind: RoleList
|
||||
plural: roles
|
||||
shortNames:
|
||||
- iamrole
|
||||
singular: role
|
||||
scope: Cluster
|
||||
versions:
|
||||
- additionalPrinterColumns:
|
||||
- jsonPath: .status.conditions[?(@.type=='Ready')].status
|
||||
name: READY
|
||||
type: string
|
||||
- jsonPath: .status.conditions[?(@.type=='Synced')].status
|
||||
name: SYNCED
|
||||
type: string
|
||||
- jsonPath: .metadata.creationTimestamp
|
||||
name: AGE
|
||||
type: date
|
||||
name: v1beta1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: An Role is a managed resource that represents an AWS IAM Role.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: An RoleSpec defines the desired state of an Role.
|
||||
properties:
|
||||
deletionPolicy:
|
||||
default: Delete
|
||||
description: DeletionPolicy specifies what will happen to the underlying
|
||||
external when this managed resource is deleted - either "Delete"
|
||||
or "Orphan" the external resource.
|
||||
enum:
|
||||
- Orphan
|
||||
- Delete
|
||||
type: string
|
||||
forProvider:
|
||||
description: RoleParameters define the desired state of an AWS IAM
|
||||
Role.
|
||||
properties:
|
||||
assumeRolePolicyDocument:
|
||||
description: AssumeRolePolicyDocument is the the trust relationship
|
||||
policy document that grants an entity permission to assume the
|
||||
role.
|
||||
type: string
|
||||
description:
|
||||
description: Description is a description of the role.
|
||||
type: string
|
||||
maxSessionDuration:
|
||||
description: 'MaxSessionDuration is the duration (in seconds)
|
||||
that you want to set for the specified role. The default maximum
|
||||
of one hour is applied. This setting can have a value from 1
|
||||
hour to 12 hours. Default: 3600'
|
||||
format: int32
|
||||
type: integer
|
||||
path:
|
||||
description: 'Path is the path to the role. Default: /'
|
||||
type: string
|
||||
permissionsBoundary:
|
||||
description: PermissionsBoundary is the ARN of the policy that
|
||||
is used to set the permissions boundary for the role.
|
||||
type: string
|
||||
tags:
|
||||
description: Tags. For more information about tagging, see Tagging
|
||||
IAM Identities (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html)
|
||||
in the IAM User Guide.
|
||||
items:
|
||||
description: Tag represents user-provided metadata that can
|
||||
be associated with a IAM role. For more information about
|
||||
tagging, see Tagging IAM Identities (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html)
|
||||
in the IAM User Guide.
|
||||
properties:
|
||||
key:
|
||||
description: The key name that can be used to look up or
|
||||
retrieve the associated value. For example, Department
|
||||
or Cost Center are common choices.
|
||||
type: string
|
||||
value:
|
||||
description: "The value associated with this tag. For example,
|
||||
tags with a key name of Department could have values such
|
||||
as Human Resources, Accounting, and Support. Tags with
|
||||
a key name of Cost Center might have values that consist
|
||||
of the number associated with the different cost centers
|
||||
in your company. Typically, many resources have tags with
|
||||
the same key name but with different values. \n AWS always
|
||||
interprets the tag Value as a single string. If you need
|
||||
to store an array, you can store comma-separated values
|
||||
in the string. However, you must interpret the value in
|
||||
your code."
|
||||
type: string
|
||||
required:
|
||||
- key
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- assumeRolePolicyDocument
|
||||
type: object
|
||||
providerConfigRef:
|
||||
default:
|
||||
name: default
|
||||
description: ProviderConfigReference specifies how the provider that
|
||||
will be used to create, observe, update, and delete this managed
|
||||
resource should be configured.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the referenced object.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
providerRef:
|
||||
description: 'ProviderReference specifies the provider that will be
|
||||
used to create, observe, update, and delete this managed resource.
|
||||
Deprecated: Please use ProviderConfigReference, i.e. `providerConfigRef`'
|
||||
properties:
|
||||
name:
|
||||
description: Name of the referenced object.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
writeConnectionSecretToRef:
|
||||
description: WriteConnectionSecretToReference specifies the namespace
|
||||
and name of a Secret to which any connection details for this managed
|
||||
resource should be written. Connection details frequently include
|
||||
the endpoint, username, and password required to connect to the
|
||||
managed resource.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the secret.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace of the secret.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- namespace
|
||||
type: object
|
||||
required:
|
||||
- forProvider
|
||||
type: object
|
||||
status:
|
||||
description: An RoleStatus represents the observed state of an Role.
|
||||
properties:
|
||||
atProvider:
|
||||
description: RoleExternalStatus keeps the state for the external resource
|
||||
properties:
|
||||
arn:
|
||||
description: ARN is the Amazon Resource Name (ARN) specifying
|
||||
the role. For more information about ARNs and how to use them
|
||||
in policies, see IAM Identifiers (http://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html)
|
||||
in the IAM User Guide guide.
|
||||
type: string
|
||||
roleID:
|
||||
description: RoleID is the stable and unique string identifying
|
||||
the role. For more information about IDs, see IAM Identifiers
|
||||
(http://docs.aws.amazon.com/IAM/latest/UserGuide/Using_Identifiers.html)
|
||||
in the Using IAM guide.
|
||||
type: string
|
||||
required:
|
||||
- arn
|
||||
- roleID
|
||||
type: object
|
||||
conditions:
|
||||
description: Conditions of the resource.
|
||||
items:
|
||||
description: A Condition that may apply to a resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: LastTransitionTime is the last time this condition
|
||||
transitioned from one status to another.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: A Message containing details about this condition's
|
||||
last transition from one status to another, if any.
|
||||
type: string
|
||||
reason:
|
||||
description: A Reason for this condition's last transition from
|
||||
one status to another.
|
||||
type: string
|
||||
status:
|
||||
description: Status of this condition; is it currently True,
|
||||
False, or Unknown?
|
||||
type: string
|
||||
type:
|
||||
description: Type of this condition. At most one of each condition
|
||||
type may apply to a resource at any point in time.
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions:
|
||||
- v1beta1
|
|
@ -1,5 +0,0 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- file: policy-namespaced-target.yaml
|
||||
shouldFail: true
|
|
@ -0,0 +1,13 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
apply:
|
||||
- file: policy-pass.yaml
|
||||
shouldFail: false
|
||||
- file: policy-fail-0.yaml
|
||||
shouldFail: true
|
||||
- file: policy-fail-1.yaml
|
||||
shouldFail: true
|
||||
- file: policy-fail-2.yaml
|
||||
shouldFail: true
|
||||
- file: policy-fail-3.yaml
|
||||
shouldFail: true
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kuttl.dev/v1beta1
|
||||
kind: TestStep
|
||||
delete:
|
||||
- apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
name: roles.iam.aws.crossplane.io
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: Policy
|
||||
metadata:
|
||||
name: pol-target-namespace-scope-fail-1
|
||||
namespace: default
|
||||
spec:
|
||||
generateExistingOnPolicyUpdate: true
|
||||
rules:
|
||||
- generate:
|
||||
apiVersion: iam.aws.crossplane.io/v1beta1
|
||||
data:
|
||||
rules:
|
||||
- verbs:
|
||||
- "*"
|
||||
apiGroups:
|
||||
- "*"
|
||||
resources:
|
||||
- "*"
|
||||
kind: Role
|
||||
name: superuser
|
||||
namespace: default
|
||||
synchronize: true
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Secret
|
||||
names:
|
||||
- dev-*
|
||||
name: role-per-namespace
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: Policy
|
||||
metadata:
|
||||
name: pol-target-namespace-scope-fail-2
|
||||
namespace: default
|
||||
spec:
|
||||
generateExistingOnPolicyUpdate: true
|
||||
rules:
|
||||
- generate:
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
data:
|
||||
rules:
|
||||
- verbs:
|
||||
- "*"
|
||||
apiGroups:
|
||||
- "*"
|
||||
resources:
|
||||
- "*"
|
||||
kind: Role
|
||||
name: superuser
|
||||
namespace: "{{request.object.metadata.name}}"
|
||||
synchronize: true
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Secret
|
||||
names:
|
||||
- dev-*
|
||||
name: role-per-namespace
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: Policy
|
||||
metadata:
|
||||
name: pol-target-namespace-scope-fail-3
|
||||
namespace: default
|
||||
spec:
|
||||
generateExistingOnPolicyUpdate: true
|
||||
rules:
|
||||
- generate:
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
data:
|
||||
rules:
|
||||
- verbs:
|
||||
- "*"
|
||||
apiGroups:
|
||||
- "*"
|
||||
resources:
|
||||
- "*"
|
||||
kind: Role
|
||||
name: superuser
|
||||
namespace: test
|
||||
synchronize: true
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Secret
|
||||
names:
|
||||
- dev-*
|
||||
name: role-per-namespace
|
|
@ -0,0 +1,30 @@
|
|||
apiVersion: kyverno.io/v1
|
||||
kind: Policy
|
||||
metadata:
|
||||
name: user-per-namespace-pass
|
||||
namespace: default
|
||||
spec:
|
||||
generateExistingOnPolicyUpdate: true
|
||||
rules:
|
||||
- generate:
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
data:
|
||||
rules:
|
||||
- verbs:
|
||||
- "*"
|
||||
apiGroups:
|
||||
- "*"
|
||||
resources:
|
||||
- "*"
|
||||
kind: Role
|
||||
name: superuser
|
||||
namespace: default
|
||||
synchronize: true
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Secret
|
||||
names:
|
||||
- dev-*
|
||||
name: role-per-namespace
|
Loading…
Add table
Reference in a new issue