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:
parent
db7a72f950
commit
e91b80a600
9 changed files with 287 additions and 6 deletions
|
@ -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 {
|
||||
|
|
|
@ -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
|
|
@ -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."
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in a new issue