mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +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"`
|
Selector *metav1.LabelSelector `json:"selector,omitempty" yaml:"selector,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Generation) Validate(path *field.Path, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
func (g *Generation) Validate(path *field.Path, namespaced bool, policyNamespace string, clusterResources sets.Set[string]) (errs field.ErrorList) {
|
||||||
if err := g.validateTargetsScope(clusterResources); err != nil {
|
if namespaced {
|
||||||
errs = append(errs, field.Forbidden(path.Child("generate").Child("namespace"), fmt.Sprintf("target resource scope mismatched: %v ", err)))
|
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()
|
generateType, _ := g.GetTypeAndSync()
|
||||||
|
@ -662,15 +668,28 @@ func (g *Generation) SetData(in apiextensions.JSON) {
|
||||||
g.RawData = ToJSON(in)
|
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
|
target := g.ResourceSpec
|
||||||
if clusterResources.Has(target.GetKind()) {
|
if clusterResources.Has(target.GetAPIVersion() + "/" + target.GetKind()) {
|
||||||
if target.GetNamespace() != "" {
|
return fmt.Errorf("the target must be a namespaced resource: %v/%v", target.GetAPIVersion(), target.GetKind())
|
||||||
return fmt.Errorf("the target namespace must not be set for cluster-wide resource: %v", 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
|
var rule *Rule
|
||||||
err := json.Unmarshal(testcase.rule, &rule)
|
err := json.Unmarshal(testcase.rule, &rule)
|
||||||
assert.NilError(t, err, testcase.name)
|
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)
|
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
|
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() {
|
if !r.HasGenerate() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.Generation.Validate(path, clusterResources)
|
return r.Generation.Validate(path, namespaced, policyNamespace, clusterResources)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate implements programmatic validation
|
// 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.ExcludeResources.Validate(path.Child("exclude"), namespaced, clusterResources)...)
|
||||||
errs = append(errs, r.ValidateMutationRuleTargetNamespace(path, namespaced, policyNamespace)...)
|
errs = append(errs, r.ValidateMutationRuleTargetNamespace(path, namespaced, policyNamespace)...)
|
||||||
errs = append(errs, r.ValidatePSaControlNames(path)...)
|
errs = append(errs, r.ValidatePSaControlNames(path)...)
|
||||||
errs = append(errs, r.ValidateGenerate(path, clusterResources)...)
|
errs = append(errs, r.ValidateGenerate(path, namespaced, policyNamespace, clusterResources)...)
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,7 @@ func (p *ClusterPolicy) IsReady() bool {
|
||||||
func (p *ClusterPolicy) Validate(clusterResources sets.Set[string]) (errs field.ErrorList) {
|
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.ValidateAutogenAnnotation(field.NewPath("metadata").Child("annotations"), p.GetAnnotations())...)
|
||||||
errs = append(errs, kyvernov1.ValidatePolicyName(field.NewPath("name"), p.Name)...)
|
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
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ func (p *Policy) IsReady() bool {
|
||||||
func (p *Policy) Validate(clusterResources sets.Set[string]) (errs field.ErrorList) {
|
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.ValidateAutogenAnnotation(field.NewPath("metadata").Child("annotations"), p.GetAnnotations())...)
|
||||||
errs = append(errs, kyvernov1.ValidatePolicyName(field.NewPath("name"), p.Name)...)
|
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
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ func Test_Validate_RuleType_EmptyRule(t *testing.T) {
|
||||||
Name: "validate-user-privilege",
|
Name: "validate-user-privilege",
|
||||||
}
|
}
|
||||||
path := field.NewPath("dummy")
|
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, len(errs), 1)
|
||||||
assert.Equal(t, errs[0].Field, "dummy")
|
assert.Equal(t, errs[0].Field, "dummy")
|
||||||
assert.Equal(t, errs[0].Type, field.ErrorTypeInvalid)
|
assert.Equal(t, errs[0].Type, field.ErrorTypeInvalid)
|
||||||
|
@ -94,7 +94,7 @@ func Test_Validate_RuleType_MultipleRule(t *testing.T) {
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
for _, rule := range policy.Spec.Rules {
|
for _, rule := range policy.Spec.Rules {
|
||||||
path := field.NewPath("dummy")
|
path := field.NewPath("dummy")
|
||||||
errs := rule.Validate(path, false, nil)
|
errs := rule.Validate(path, false, "", nil)
|
||||||
assert.Assert(t, len(errs) != 0)
|
assert.Assert(t, len(errs) != 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,7 +153,7 @@ func Test_Validate_RuleType_SingleRule(t *testing.T) {
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
for _, rule := range policy.Spec.Rules {
|
for _, rule := range policy.Spec.Rules {
|
||||||
path := field.NewPath("dummy")
|
path := field.NewPath("dummy")
|
||||||
errs := rule.Validate(path, false, nil)
|
errs := rule.Validate(path, false, "", nil)
|
||||||
assert.Assert(t, len(errs) == 0)
|
assert.Assert(t, len(errs) == 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -534,7 +534,7 @@ func Test_Validate_ClusterPolicy_Generate_Variables(t *testing.T) {
|
||||||
var rule *Rule
|
var rule *Rule
|
||||||
err := json.Unmarshal(testcase.rule, &rule)
|
err := json.Unmarshal(testcase.rule, &rule)
|
||||||
assert.NilError(t, err, testcase.name)
|
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)
|
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"))
|
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() {
|
if !r.HasGenerate() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.Generation.Validate(path, clusterResources)
|
return r.Generation.Validate(path, namespaced, policyNamespace, clusterResources)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate implements programmatic validation
|
// 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.ValidateRuleType(path)...)
|
||||||
errs = append(errs, r.ValidateMatchExcludeConflict(path)...)
|
errs = append(errs, r.ValidateMatchExcludeConflict(path)...)
|
||||||
errs = append(errs, r.MatchResources.Validate(path.Child("match"), namespaced, clusterResources)...)
|
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.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
|
return errs
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ func Test_Validate_UniqueRuleName(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
path := field.NewPath("dummy")
|
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, len(errs), 1)
|
||||||
assert.Equal(t, errs[0].Field, "dummy.rules[1].name")
|
assert.Equal(t, errs[0].Field, "dummy.rules[1].name")
|
||||||
assert.Equal(t, errs[0].Type, field.ErrorTypeInvalid)
|
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
|
// 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)...)
|
errs = append(errs, s.ValidateRuleNames(path)...)
|
||||||
for i, rule := range s.Rules {
|
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
|
return errs
|
||||||
}
|
}
|
||||||
|
@ -232,14 +232,14 @@ func (s *Spec) ValidateDeprecatedFields(path *field.Path) (errs field.ErrorList)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate implements programmatic validation
|
// 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 {
|
if err := s.ValidateDeprecatedFields(path); err != nil {
|
||||||
errs = append(errs, err...)
|
errs = append(errs, err...)
|
||||||
}
|
}
|
||||||
if s.WebhookTimeoutSeconds != nil && (*s.WebhookTimeoutSeconds < 1 || *s.WebhookTimeoutSeconds > 30) {
|
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, 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 {
|
if namespaced && len(s.ValidationFailureActionOverrides) > 0 {
|
||||||
errs = append(errs, field.Forbidden(path.Child("validationFailureActionOverrides"), "Use of validationFailureActionOverrides is supported only with ClusterPolicy"))
|
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 _, resList := range res {
|
||||||
for _, r := range resList.APIResources {
|
for _, r := range resList.APIResources {
|
||||||
if !r.Namespaced {
|
if !r.Namespaced {
|
||||||
|
clusterResources.Insert(resList.GroupVersion + "/" + r.Kind)
|
||||||
clusterResources.Insert(r.Kind)
|
clusterResources.Insert(r.Kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
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"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -48,63 +46,6 @@ func immutableGenerateFields(new, old kyvernov1.PolicyInterface) error {
|
||||||
return nil
|
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 {
|
func resetMutableFields(rule kyvernov1.Rule) *kyvernov1.Rule {
|
||||||
new := new(kyvernov1.Rule)
|
new := new(kyvernov1.Rule)
|
||||||
rule.DeepCopyInto(new)
|
rule.DeepCopyInto(new)
|
||||||
|
|
|
@ -165,6 +165,7 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
|
||||||
for _, resList := range res {
|
for _, resList := range res {
|
||||||
for _, r := range resList.APIResources {
|
for _, r := range resList.APIResources {
|
||||||
if !r.Namespaced {
|
if !r.Namespaced {
|
||||||
|
clusterResources.Insert(resList.GroupVersion + "/" + r.Kind)
|
||||||
clusterResources.Insert(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 {
|
if err := validateActions(i, &rules[i], client, mock, username); err != nil {
|
||||||
return warnings, err
|
return warnings, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import (
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||||
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1094,136 +1093,6 @@ func Test_Namespced_Policy(t *testing.T) {
|
||||||
assert.Assert(t, err != nil)
|
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) {
|
func Test_patchesJson6902_Policy(t *testing.T) {
|
||||||
rawPolicy := []byte(`
|
rawPolicy := []byte(`
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,15 +1,27 @@
|
||||||
apiVersion: kyverno.io/v1
|
apiVersion: kyverno.io/v1
|
||||||
kind: Policy
|
kind: ClusterPolicy
|
||||||
metadata:
|
metadata:
|
||||||
name: create-default-pdb
|
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:
|
spec:
|
||||||
rules:
|
rules:
|
||||||
- name: create-default-pdb
|
- name: create-default-pdb
|
||||||
match:
|
match:
|
||||||
resources:
|
any:
|
||||||
kinds:
|
- resources:
|
||||||
- Deployment
|
kinds:
|
||||||
|
- Deployment
|
||||||
generate:
|
generate:
|
||||||
apiVersion: policy/v1
|
apiVersion: policy/v1
|
||||||
kind: PodDisruptionBudget
|
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
|
- file: policy-namespaced-target.yaml
|
||||||
shouldFail: true
|
shouldFail: true
|
||||||
- file: policy-cluster-target.yaml
|
- 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
|
|
@ -10,3 +10,4 @@ The test fails if the policy creation is allowed, otherwise passes.
|
||||||
## Reference Issue(s)
|
## 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:
|
kinds:
|
||||||
- ConfigMap
|
- ConfigMap
|
||||||
generate:
|
generate:
|
||||||
namespace: "{{request.object.metadata.name}}"
|
namespace: default
|
||||||
synchronize : true
|
synchronize : true
|
||||||
cloneList:
|
cloneList:
|
||||||
namespace: default
|
namespace: default
|
||||||
|
|
|
@ -13,7 +13,7 @@ spec:
|
||||||
kinds:
|
kinds:
|
||||||
- ConfigMap
|
- ConfigMap
|
||||||
generate:
|
generate:
|
||||||
namespace: "{{request.object.metadata.name}}"
|
namespace: default
|
||||||
synchronize : true
|
synchronize : true
|
||||||
cloneList:
|
cloneList:
|
||||||
namespace: default
|
namespace: default
|
||||||
|
|
|
@ -13,7 +13,7 @@ spec:
|
||||||
kinds:
|
kinds:
|
||||||
- ConfigMap
|
- ConfigMap
|
||||||
generate:
|
generate:
|
||||||
namespace: "{{request.object.metadata.name}}"
|
namespace: default
|
||||||
synchronize : true
|
synchronize : true
|
||||||
cloneList:
|
cloneList:
|
||||||
namespace: update-clonelist-ns
|
namespace: update-clonelist-ns
|
||||||
|
|
|
@ -13,7 +13,7 @@ spec:
|
||||||
kinds:
|
kinds:
|
||||||
-
|
-
|
||||||
generate:
|
generate:
|
||||||
namespace: "{{request.object.metadata.name}}"
|
namespace: default
|
||||||
synchronize : true
|
synchronize : true
|
||||||
cloneList:
|
cloneList:
|
||||||
namespace: default
|
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