1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2025-03-28 02:18:15 +00:00

feat: validate immutable fields for a generate rule - 2 (#6451)

* update validation checks

Signed-off-by: ShutingZhao <shuting@nirmata.com>

* fix ns assertions

Signed-off-by: ShutingZhao <shuting@nirmata.com>

---------

Signed-off-by: ShutingZhao <shuting@nirmata.com>
This commit is contained in:
shuting 2023-03-10 22:39:44 +08:00 committed by GitHub
parent 85a83e4fae
commit 48726dcd4a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 541 additions and 106 deletions

View file

@ -2,6 +2,8 @@ package policy
import (
"context"
"crypto/md5" //nolint:gosec
"encoding/hex"
"encoding/json"
"errors"
"fmt"
@ -245,11 +247,10 @@ func Validate(policy, oldPolicy kyvernov1.PolicyInterface, client dclient.Interf
}
}
if oldPolicy != nil {
if err := immutableGenerateFields(policy, oldPolicy); err != nil {
return warnings, err
}
if err := immutableGenerateFields(policy, oldPolicy); err != nil {
return warnings, err
}
// validate Cluster Resources in namespaced policy
// For namespaced policy, ClusterResource type field and values are not allowed in match and exclude
if namespaced {
@ -1387,32 +1388,58 @@ func checkForStatusSubresource(ruleTypeJson []byte, allKinds []string, warnings
}
func immutableGenerateFields(new, old kyvernov1.PolicyInterface) error {
if new == nil || old == nil {
return nil
}
if !new.GetSpec().HasGenerate() {
return nil
}
oldRuleNames := make(map[string]kyvernov1.Generation, len(old.GetSpec().Rules))
for _, rule := range old.GetSpec().Rules {
oldRuleNames[rule.Name] = rule.Generation
oldRuleHashes, err := buildHashes(old.GetSpec().Rules)
if err != nil {
return err
}
newRuleHashes, err := buildHashes(new.GetSpec().Rules)
if err != nil {
return err
}
newRuleNames := make(map[string]kyvernov1.Generation, len(new.GetSpec().Rules))
for _, rule := range new.GetSpec().Rules {
newRuleNames[rule.Name] = rule.Generation
}
for newRuleName, newGenerate := range newRuleNames {
oldGenerate, ok := oldRuleNames[newRuleName]
if !ok {
continue
switch len(old.GetSpec().Rules) <= len(new.GetSpec().Rules) {
case true:
if newRuleHashes.IsSuperset(oldRuleHashes) {
return nil
} else {
return errors.New("change of immutable fields for a generate rule is disallowed")
}
oldGenerate.Synchronize = newGenerate.Synchronize
oldGenerate.SetData(newGenerate.GetData())
if !reflect.DeepEqual(newGenerate, oldGenerate) {
return fmt.Errorf("cannot change downstream, or clone sources for a generate rule")
case false:
if oldRuleHashes.IsSuperset(newRuleHashes) {
return nil
} else {
return errors.New("rule deletion - change of immutable fields for a generate rule is disallowed")
}
}
return nil
}
func resetMutableFields(rule kyvernov1.Rule) *kyvernov1.Rule {
new := new(kyvernov1.Rule)
rule.DeepCopyInto(new)
new.Generation.Synchronize = true
new.Generation.SetData(nil)
return new
}
func buildHashes(rules []kyvernov1.Rule) (sets.Set[string], error) {
ruleHashes := sets.New[string]()
for _, rule := range rules {
r := resetMutableFields(rule)
data, err := json.Marshal(r)
if err != nil {
return ruleHashes, fmt.Errorf("failed to create hash from the generate rule %v", err)
}
hash := md5.Sum(data) //nolint:gosec
ruleHashes.Insert(hex.EncodeToString(hash[:]))
}
return ruleHashes, nil
}

View file

@ -2307,85 +2307,6 @@ func Test_ImmutableGenerateFields(t *testing.T) {
newPolicy []byte
expectedErr bool
}{
{
name: "update-rule-name",
oldPolicy: []byte(`
{
"apiVersion": "kyverno.io/v2beta1",
"kind": "ClusterPolicy",
"metadata": {
"name": "cpol-clone-sync-modify-source"
},
"spec": {
"rules": [
{
"name": "cpol-clone-sync-modify-source-secret",
"match": {
"any": [
{
"resources": {
"kinds": [
"Namespace"
]
}
}
]
},
"generate": {
"apiVersion": "v1",
"kind": "Secret",
"name": "regcred",
"namespace": "{{request.object.metadata.name}}",
"synchronize": true,
"clone": {
"namespace": "default",
"name": "regcred"
}
}
}
]
}
}
`),
newPolicy: []byte(`
{
"apiVersion": "kyverno.io/v2beta1",
"kind": "ClusterPolicy",
"metadata": {
"name": "cpol-clone-sync-modify-source"
},
"spec": {
"rules": [
{
"name": "updated-rule-name",
"match": {
"any": [
{
"resources": {
"kinds": [
"Namespace"
]
}
}
]
},
"generate": {
"apiVersion": "v1",
"kind": "Secret",
"name": "regcred",
"namespace": "{{request.object.metadata.name}}",
"synchronize": true,
"clone": {
"namespace": "default",
"name": "regcred"
}
}
}
]
}
}`),
expectedErr: false,
},
{
name: "update-apiVersion",
oldPolicy: []byte(`

View file

@ -5,7 +5,7 @@ metadata:
spec:
generateExisting: true
rules:
- name: existing-basic-create-rule-data
- name: existing-basic-create-rule
match:
any:
- resources:

View file

@ -46,7 +46,7 @@ spec:
clone:
name: regcred
namespace: default
- name: pol-clone-nosync-delete-rule-lr
- name: pol-clone-sync-delete-rule-lr
match:
any:
- resources:
@ -57,7 +57,7 @@ spec:
kind: LimitRange
name: genlr
namespace: default
synchronize: false
synchronize: true
clone:
name: sourcelr
namespace: default

View file

@ -1,6 +1,6 @@
## Description
This test ensures that modification of the downstream rseource defined in a generate ClusterPolicy is disallowed.
This test ensures that modification of the downstream resource defined in a generate ClusterPolicy is disallowed.
## Expected Behavior

View file

@ -0,0 +1,9 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: generate-update-rule-spec
status:
conditions:
- reason: Succeeded
status: "True"
type: Ready

View file

@ -0,0 +1,35 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: generate-update-rule-spec
spec:
generateExistingOnPolicyUpdate: false
rules:
- name: k-kafka-address
match:
any:
- resources:
kinds:
- Namespace
exclude:
any:
- resources:
namespaces:
- kube-system
- default
- kube-public
- kyverno
generate:
synchronize: true
apiVersion: v1
kind: ConfigMap
name: zk-kafka-address
namespace: default
data:
kind: ConfigMap
metadata:
labels:
somekey: somevalue
data:
ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"

View file

@ -0,0 +1,13 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
apply:
- file: update-rule-name.yaml
shouldFail: true
- file: update-rule-match.yaml
shouldFail: true
- file: update-rule-exclude.yaml
shouldFail: true
- file: update-rule-preconditions.yaml
shouldFail: true
- file: update-rule-generate-synchronize.yaml
shouldFail: false

View file

@ -0,0 +1,12 @@
## Description
This test ensures that modification of the rule spec fields defined in a generate ClusterPolicy is disallowed except `spec.generate.synchronize`.
## Expected Behavior
The test fails if the modification is allowed, otherwise passes.
## Reference Issue(s)
https://github.com/kyverno/kyverno/issues/6440

View file

@ -0,0 +1,33 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: generate-update-rule-spec
spec:
generateExistingOnPolicyUpdate: false
rules:
- name: k-kafka-address
match:
any:
- resources:
kinds:
- Namespace
exclude:
any:
- resources:
namespaces:
- kube-system
- default
generate:
synchronize: true
apiVersion: v1
kind: ConfigMap
name: zk-kafka-address
namespace: default
data:
kind: ConfigMap
metadata:
labels:
somekey: somevalue
data:
ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"

View file

@ -0,0 +1,35 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: generate-update-rule-spec
spec:
generateExistingOnPolicyUpdate: false
rules:
- name: k-kafka-address
match:
any:
- resources:
kinds:
- Namespace
exclude:
any:
- resources:
namespaces:
- kube-system
- default
- kube-public
- kyverno
generate:
synchronize: false
apiVersion: v1
kind: ConfigMap
name: zk-kafka-address
namespace: default
data:
kind: ConfigMap
metadata:
labels:
somekey: somevalue
data:
ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"

View file

@ -0,0 +1,36 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: generate-update-rule-spec
spec:
generateExistingOnPolicyUpdate: false
rules:
- name: k-kafka-address
match:
any:
- resources:
kinds:
- Namespace
- Secret
exclude:
any:
- resources:
namespaces:
- kube-system
- default
- kube-public
- kyverno
generate:
synchronize: true
apiVersion: v1
kind: ConfigMap
name: zk-kafka-address
namespace: default
data:
kind: ConfigMap
metadata:
labels:
somekey: somevalue
data:
ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"

View file

@ -0,0 +1,35 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: generate-update-rule-spec
spec:
generateExistingOnPolicyUpdate: false
rules:
- name: i-changed-this
match:
any:
- resources:
kinds:
- Namespace
exclude:
any:
- resources:
namespaces:
- kube-system
- default
- kube-public
- kyverno
generate:
synchronize: true
apiVersion: v1
kind: ConfigMap
name: zk-kafka-address
namespace: default
data:
kind: ConfigMap
metadata:
labels:
somekey: somevalue
data:
ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"

View file

@ -0,0 +1,39 @@
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: generate-update-rule-spec
spec:
generateExistingOnPolicyUpdate: false
rules:
- name: k-kafka-address
match:
any:
- resources:
kinds:
- Namespace
exclude:
any:
- resources:
namespaces:
- kube-system
- default
- kube-public
- kyverno
preconditions:
- key: "{{request.operation}}"
operator: NotEquals
value: DELETE
generate:
synchronize: true
apiVersion: v1
kind: ConfigMap
name: zk-kafka-address
namespace: default
data:
kind: ConfigMap
metadata:
labels:
somekey: somevalue
data:
ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"

View file

@ -1,6 +1,6 @@
## Description
This test ensures that modification of the downstream rseource defined in a generate Policy is disallowed.
This test ensures that modification of the downstream resource defined in a generate Policy is disallowed.
## Expected Behavior

View file

@ -0,0 +1,10 @@
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: generate-update-rule-spec
namespace: default
status:
conditions:
- reason: Succeeded
status: "True"
type: Ready

View file

@ -0,0 +1,33 @@
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: generate-update-rule-spec
namespace: default
spec:
generateExistingOnPolicyUpdate: false
rules:
- name: k-kafka-address
match:
any:
- resources:
kinds:
- Secret
exclude:
any:
- resources:
kinds:
- NetworkPolicy
generate:
synchronize: true
apiVersion: v1
kind: ConfigMap
name: zk-kafka-address
namespace: default
data:
kind: ConfigMap
metadata:
labels:
somekey: somevalue
data:
ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"

View file

@ -0,0 +1,13 @@
apiVersion: kuttl.dev/v1beta1
kind: TestStep
apply:
- file: update-rule-name.yaml
shouldFail: true
- file: update-rule-match.yaml
shouldFail: true
- file: update-rule-exclude.yaml
shouldFail: true
- file: update-rule-preconditions.yaml
shouldFail: true
- file: update-rule-generate-synchronize.yaml
shouldFail: false

View file

@ -0,0 +1,12 @@
## Description
This test ensures that modification of the rule spec fields defined in a generate Policy is disallowed except `spec.generate.synchronize`.
## Expected Behavior
The test fails if the modification is allowed, otherwise passes.
## Reference Issue(s)
https://github.com/kyverno/kyverno/issues/6440

View file

@ -0,0 +1,35 @@
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: generate-update-rule-spec
namespace: default
spec:
generateExistingOnPolicyUpdate: false
rules:
- name: k-kafka-address
match:
any:
- resources:
kinds:
- Secret
exclude:
any:
- resources:
kinds:
- NetworkPolicy
names:
- test
generate:
synchronize: true
apiVersion: v1
kind: ConfigMap
name: zk-kafka-address
namespace: default
data:
kind: ConfigMap
metadata:
labels:
somekey: somevalue
data:
ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"

View file

@ -0,0 +1,33 @@
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: generate-update-rule-spec
namespace: default
spec:
generateExistingOnPolicyUpdate: false
rules:
- name: k-kafka-address
match:
any:
- resources:
kinds:
- Secret
exclude:
any:
- resources:
kinds:
- NetworkPolicy
generate:
synchronize: false
apiVersion: v1
kind: ConfigMap
name: zk-kafka-address
namespace: default
data:
kind: ConfigMap
metadata:
labels:
somekey: somevalue
data:
ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"

View file

@ -0,0 +1,34 @@
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: generate-update-rule-spec
namespace: default
spec:
generateExistingOnPolicyUpdate: false
rules:
- name: k-kafka-address
match:
any:
- resources:
kinds:
- Secret
- ServiceAccount
exclude:
any:
- resources:
kinds:
- NetworkPolicy
generate:
synchronize: true
apiVersion: v1
kind: ConfigMap
name: zk-kafka-address
namespace: default
data:
kind: ConfigMap
metadata:
labels:
somekey: somevalue
data:
ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"

View file

@ -0,0 +1,33 @@
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: generate-update-rule-spec
namespace: default
spec:
generateExistingOnPolicyUpdate: false
rules:
- name: i-changed-this
match:
any:
- resources:
kinds:
- Secret
exclude:
any:
- resources:
kinds:
- NetworkPolicy
generate:
synchronize: true
apiVersion: v1
kind: ConfigMap
name: zk-kafka-address
namespace: default
data:
kind: ConfigMap
metadata:
labels:
somekey: somevalue
data:
ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"

View file

@ -0,0 +1,37 @@
apiVersion: kyverno.io/v1
kind: Policy
metadata:
name: generate-update-rule-spec
namespace: default
spec:
generateExistingOnPolicyUpdate: false
rules:
- name: k-kafka-address
match:
any:
- resources:
kinds:
- Secret
exclude:
any:
- resources:
kinds:
- NetworkPolicy
preconditions:
- key: "{{request.operation}}"
operator: NotEquals
value: DELETE
generate:
synchronize: true
apiVersion: v1
kind: ConfigMap
name: zk-kafka-address
namespace: default
data:
kind: ConfigMap
metadata:
labels:
somekey: somevalue
data:
ZK_ADDRESS: "192.168.10.10:2181,192.168.10.11:2181,192.168.10.12:2181"
KAFKA_ADDRESS: "192.168.10.13:9092,192.168.10.14:9092,192.168.10.15:9092"