1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00

fix: evaluate namespaceObject for VAPs in the CLI (#9978)

Signed-off-by: Mariam Fahmy <mariam.fahmy@nirmata.com>
This commit is contained in:
Mariam Fahmy 2024-04-19 18:20:03 +08:00 committed by GitHub
parent db7a72f950
commit e91b80a600
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 287 additions and 6 deletions

View file

@ -14,6 +14,8 @@ import (
"golang.org/x/text/language"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
"k8s.io/api/admissionregistration/v1alpha1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -63,7 +65,12 @@ func GetKinds(policy v1alpha1.ValidatingAdmissionPolicy) []string {
return kindList
}
func Validate(policyData PolicyData, resource unstructured.Unstructured, namespaceSelectorMap map[string]map[string]string, client dclient.Interface) (engineapi.EngineResponse, error) {
func Validate(
policyData PolicyData,
resource unstructured.Unstructured,
namespaceSelectorMap map[string]map[string]string,
client dclient.Interface,
) (engineapi.EngineResponse, error) {
resPath := fmt.Sprintf("%s/%s/%s", resource.GetNamespace(), resource.GetKind(), resource.GetName())
policy := policyData.definition
bindings := policyData.bindings
@ -75,6 +82,24 @@ func Validate(policyData PolicyData, resource unstructured.Unstructured, namespa
Version: gvk.Version,
Resource: strings.ToLower(gvk.Kind) + "s",
}
var namespace *corev1.Namespace
namespaceName := resource.GetNamespace()
// Special case, the namespace object has the namespace of itself.
// unset it if the incoming object is a namespace
if gvk.Kind == "Namespace" && gvk.Version == "v1" && gvk.Group == "" {
namespaceName = ""
}
if namespaceName != "" {
namespace = &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: namespaceName,
Labels: namespaceSelectorMap[namespaceName],
},
}
}
a := admission.NewAttributesRecord(resource.DeepCopyObject(), nil, resource.GroupVersionKind(), resource.GetNamespace(), resource.GetName(), gvr, "", admission.Create, nil, false, nil)
if len(bindings) == 0 {
@ -86,7 +111,7 @@ func Validate(policyData PolicyData, resource unstructured.Unstructured, namespa
return engineResponse, nil
}
logger.V(3).Info("validate resource %s against policy %s", resPath, policy.GetName())
return validateResource(policy, nil, resource, a)
return validateResource(policy, nil, resource, *namespace, a)
}
if client != nil {
@ -113,6 +138,13 @@ func Validate(policyData PolicyData, resource unstructured.Unstructured, namespa
return engineResponse, nil
}
if namespaceName != "" {
namespace, err = client.GetKubeClient().CoreV1().Namespaces().Get(context.TODO(), namespaceName, metav1.GetOptions{})
if err != nil {
return engineResponse, err
}
}
for i, binding := range bindings {
// convert policy binding from v1alpha1 to v1beta1
v1beta1binding := ConvertValidatingAdmissionPolicyBinding(binding)
@ -125,7 +157,7 @@ func Validate(policyData PolicyData, resource unstructured.Unstructured, namespa
}
logger.V(3).Info("validate resource %s against policy %s with binding %s", resPath, policy.GetName(), binding.GetName())
return validateResource(policy, &bindings[i], resource, a)
return validateResource(policy, &bindings[i], resource, *namespace, a)
}
} else {
for i, binding := range bindings {
@ -137,14 +169,20 @@ func Validate(policyData PolicyData, resource unstructured.Unstructured, namespa
continue
}
logger.V(3).Info("validate resource %s against policy %s with binding %s", resPath, policy.GetName(), binding.GetName())
return validateResource(policy, &bindings[i], resource, a)
return validateResource(policy, &bindings[i], resource, *namespace, a)
}
}
return engineResponse, nil
}
func validateResource(policy v1alpha1.ValidatingAdmissionPolicy, binding *v1alpha1.ValidatingAdmissionPolicyBinding, resource unstructured.Unstructured, a admission.Attributes) (engineapi.EngineResponse, error) {
func validateResource(
policy v1alpha1.ValidatingAdmissionPolicy,
binding *v1alpha1.ValidatingAdmissionPolicyBinding,
resource unstructured.Unstructured,
namespace corev1.Namespace,
a admission.Attributes,
) (engineapi.EngineResponse, error) {
startTime := time.Now()
engineResponse := engineapi.NewEngineResponse(resource, engineapi.NewValidatingAdmissionPolicy(policy), nil)
@ -184,7 +222,7 @@ func validateResource(policy v1alpha1.ValidatingAdmissionPolicy, binding *v1alph
&failPolicy,
)
versionedAttr, _ := admission.NewVersionedAttributes(a, a.GetKind(), nil)
validateResult := validator.Validate(context.TODO(), a.GetResource(), versionedAttr, nil, nil, celconfig.RuntimeCELCostBudget, nil)
validateResult := validator.Validate(context.TODO(), a.GetResource(), versionedAttr, nil, &namespace, celconfig.RuntimeCELCostBudget, nil)
isPass := true
for _, policyDecision := range validateResult.Decisions {

View file

@ -0,0 +1,22 @@
apiVersion: cli.kyverno.io/v1alpha1
kind: Test
metadata:
name: kyverno-test.yaml
policies:
- policy.yaml
resources:
- resources.yaml
results:
- isValidatingAdmissionPolicy: true
kind: Deployment
policy: check-deployment-namespace
resources:
- bad-deployment
result: fail
- isValidatingAdmissionPolicy: true
kind: Deployment
policy: check-deployment-namespace
resources:
- good-deployment
result: pass
variables: values.yaml

View file

@ -0,0 +1,19 @@
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingAdmissionPolicy
metadata:
name: "check-deployment-namespace"
spec:
matchConstraints:
resourceRules:
- apiGroups:
- apps
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- deployments
validations:
- expression: "namespaceObject.metadata.name != 'default'"
message: "Using 'default' namespace is not allowed for pod controllers."

View file

@ -0,0 +1,37 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: bad-deployment
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox:latest
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: good-deployment
namespace: staging
spec:
replicas: 2
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox:latest

View file

@ -0,0 +1,11 @@
apiVersion: cli.kyverno.io/v1alpha1
kind: Value
metadata:
name: values
namespaceSelector:
- labels:
environment: staging
name: staging
- labels:
environment: default
name: default

View file

@ -0,0 +1,29 @@
apiVersion: cli.kyverno.io/v1alpha1
kind: Test
metadata:
name: kyverno-test.yaml
policies:
- policy.yaml
resources:
- resources.yaml
results:
- isValidatingAdmissionPolicy: true
kind: Deployment
policy: check-deployment-namespace
resources:
- bad-deployment
result: fail
- isValidatingAdmissionPolicy: true
kind: Deployment
policy: check-deployment-namespace
resources:
- good-deployment
result: pass
- isValidatingAdmissionPolicy: true
kind: Deployment
policy: check-deployment-namespace
resources:
- skipped-deployment-1
- skipped-deployment-2
result: skip
variables: values.yaml

View file

@ -0,0 +1,31 @@
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingAdmissionPolicy
metadata:
name: "check-deployment-namespace"
spec:
matchConstraints:
resourceRules:
- apiGroups:
- apps
apiVersions:
- v1
operations:
- CREATE
- UPDATE
resources:
- deployments
validations:
- expression: "namespaceObject.metadata.name != 'default'"
message: "Using 'default' namespace is not allowed for pod controllers."
---
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "check-deployment-namespace-binding"
spec:
policyName: "check-deployment-namespace"
validationActions: [Deny]
matchResources:
objectSelector:
matchLabels:
app: nginx

View file

@ -0,0 +1,83 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: skipped-deployment-1
namespace: default
labels:
app: busybox
spec:
replicas: 2
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox:latest
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: skipped-deployment-2
namespace: staging
labels:
app: busybox
spec:
replicas: 2
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
containers:
- name: busybox
image: busybox:latest
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: bad-deployment
namespace: default
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: good-deployment
namespace: staging
labels:
app: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest

View file

@ -0,0 +1,11 @@
apiVersion: cli.kyverno.io/v1alpha1
kind: Value
metadata:
name: values
namespaceSelector:
- labels:
environment: staging
name: staging
- labels:
environment: default
name: default