2024-02-02 10:04:02 +00:00
|
|
|
package validatingadmissionpolicy
|
2023-08-31 10:25:21 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
|
2024-05-07 12:19:12 +00:00
|
|
|
"github.com/kyverno/kyverno/ext/wildcard"
|
2023-08-31 10:25:21 +00:00
|
|
|
)
|
|
|
|
|
2024-12-12 07:13:09 +00:00
|
|
|
// CanGenerateVAP check if Kyverno policy can be translated to a Kubernetes ValidatingAdmissionPolicy
|
|
|
|
func CanGenerateVAP(spec *kyvernov1.Spec) (bool, string) {
|
2024-08-13 11:55:22 +00:00
|
|
|
var msg string
|
2024-12-12 07:13:09 +00:00
|
|
|
if len(spec.Rules) > 1 {
|
|
|
|
msg = "skip generating ValidatingAdmissionPolicy: multiple rules are not applicable."
|
2023-08-31 10:25:21 +00:00
|
|
|
return false, msg
|
|
|
|
}
|
|
|
|
|
|
|
|
rule := spec.Rules[0]
|
2024-12-12 07:13:09 +00:00
|
|
|
if !rule.HasValidateCEL() {
|
|
|
|
msg = "skip generating ValidatingAdmissionPolicy for non CEL rules."
|
2023-08-31 10:25:21 +00:00
|
|
|
return false, msg
|
|
|
|
}
|
|
|
|
|
2024-12-12 07:13:09 +00:00
|
|
|
if len(spec.ValidationFailureActionOverrides) > 1 {
|
|
|
|
msg = "skip generating ValidatingAdmissionPolicy: multiple validationFailureActionOverrides are not applicable."
|
2023-08-31 10:25:21 +00:00
|
|
|
return false, msg
|
|
|
|
}
|
|
|
|
|
2024-12-12 07:13:09 +00:00
|
|
|
if len(spec.ValidationFailureActionOverrides) != 0 && len(spec.ValidationFailureActionOverrides[0].Namespaces) != 0 {
|
|
|
|
msg = "skip generating ValidatingAdmissionPolicy: Namespaces in validationFailureActionOverrides is not applicable."
|
2023-08-31 10:25:21 +00:00
|
|
|
return false, msg
|
|
|
|
}
|
|
|
|
|
|
|
|
// check the matched/excluded resources of the CEL rule.
|
2024-12-12 07:13:09 +00:00
|
|
|
match, exclude := rule.MatchResources, rule.ExcludeResources
|
|
|
|
if !exclude.UserInfo.IsEmpty() || !exclude.ResourceDescription.IsEmpty() || exclude.All != nil || exclude.Any != nil {
|
|
|
|
msg = "skip generating ValidatingAdmissionPolicy: Exclude is not applicable."
|
2023-08-31 10:25:21 +00:00
|
|
|
return false, msg
|
|
|
|
}
|
2024-12-12 07:13:09 +00:00
|
|
|
if ok, msg := checkUserInfo(match.UserInfo); !ok {
|
2024-07-16 15:06:58 +00:00
|
|
|
return false, msg
|
|
|
|
}
|
2024-12-12 07:13:09 +00:00
|
|
|
if ok, msg := checkResources(match.ResourceDescription); !ok {
|
2024-07-16 15:06:58 +00:00
|
|
|
return false, msg
|
|
|
|
}
|
2024-12-12 07:13:09 +00:00
|
|
|
|
|
|
|
var (
|
|
|
|
containsNamespaceSelector = false
|
|
|
|
containsObjectSelector = false
|
|
|
|
)
|
|
|
|
|
|
|
|
// since 'any' specify resources which will be ORed, it can be converted into multiple NamedRuleWithOperations in ValidatingAdmissionPolicy
|
|
|
|
for _, value := range match.Any {
|
|
|
|
if ok, msg := checkUserInfo(value.UserInfo); !ok {
|
2024-09-10 11:14:49 +00:00
|
|
|
return false, msg
|
|
|
|
}
|
2024-12-12 07:13:09 +00:00
|
|
|
if ok, msg := checkResources(value.ResourceDescription); !ok {
|
2024-09-10 11:14:49 +00:00
|
|
|
return false, msg
|
|
|
|
}
|
2024-12-12 07:13:09 +00:00
|
|
|
|
|
|
|
if value.NamespaceSelector != nil {
|
|
|
|
containsNamespaceSelector = true
|
2024-09-10 11:14:49 +00:00
|
|
|
}
|
2024-12-12 07:13:09 +00:00
|
|
|
if value.Selector != nil {
|
|
|
|
containsObjectSelector = true
|
2024-09-10 11:14:49 +00:00
|
|
|
}
|
2024-05-06 13:52:22 +00:00
|
|
|
}
|
2024-12-12 07:13:09 +00:00
|
|
|
// since namespace/object selectors are applied to all NamedRuleWithOperations in ValidatingAdmissionPolicy, then
|
|
|
|
// we can't have more than one resource with namespace/object selectors.
|
|
|
|
if len(match.Any) > 1 && (containsNamespaceSelector || containsObjectSelector) {
|
|
|
|
msg = "skip generating ValidatingAdmissionPolicy: NamespaceSelector / ObjectSelector across multiple resources are not applicable."
|
2024-07-16 15:06:58 +00:00
|
|
|
return false, msg
|
2023-08-31 10:25:21 +00:00
|
|
|
}
|
|
|
|
|
2024-12-12 07:13:09 +00:00
|
|
|
// since 'all' specify resources which will be ANDed, we can't have more than one resource.
|
|
|
|
if match.All != nil {
|
|
|
|
if len(match.All) > 1 {
|
|
|
|
msg = "skip generating ValidatingAdmissionPolicy: multiple 'all' is not applicable."
|
|
|
|
return false, msg
|
|
|
|
} else {
|
|
|
|
if ok, msg := checkUserInfo(match.All[0].UserInfo); !ok {
|
|
|
|
return false, msg
|
|
|
|
}
|
|
|
|
if ok, msg := checkResources(match.All[0].ResourceDescription); !ok {
|
|
|
|
return false, msg
|
|
|
|
}
|
|
|
|
}
|
2024-07-16 15:06:58 +00:00
|
|
|
}
|
2024-12-12 07:13:09 +00:00
|
|
|
|
2023-08-31 10:25:21 +00:00
|
|
|
return true, msg
|
|
|
|
}
|
2024-02-02 10:04:02 +00:00
|
|
|
|
2024-12-12 07:13:09 +00:00
|
|
|
func checkResources(resource kyvernov1.ResourceDescription) (bool, string) {
|
2024-02-02 10:04:02 +00:00
|
|
|
var msg string
|
2024-12-12 07:13:09 +00:00
|
|
|
if len(resource.Namespaces) != 0 || len(resource.Annotations) != 0 {
|
|
|
|
msg = "skip generating ValidatingAdmissionPolicy: Namespaces / Annotations in resource description is not applicable."
|
2024-02-02 10:04:02 +00:00
|
|
|
return false, msg
|
|
|
|
}
|
2024-05-07 12:19:12 +00:00
|
|
|
if resource.Name != "" && wildcard.ContainsWildcard(resource.Name) {
|
|
|
|
msg = "skip generating ValidatingAdmissionPolicy: wildcards in resource name is not applicable."
|
|
|
|
return false, msg
|
|
|
|
}
|
|
|
|
for _, name := range resource.Names {
|
|
|
|
if wildcard.ContainsWildcard(name) {
|
|
|
|
msg = "skip generating ValidatingAdmissionPolicy: wildcards in resource name is not applicable."
|
|
|
|
return false, msg
|
|
|
|
}
|
|
|
|
}
|
2024-02-02 10:04:02 +00:00
|
|
|
return true, msg
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkUserInfo(info kyvernov1.UserInfo) (bool, string) {
|
|
|
|
var msg string
|
|
|
|
if !info.IsEmpty() {
|
2024-05-07 12:19:12 +00:00
|
|
|
msg = "skip generating ValidatingAdmissionPolicy: Roles / ClusterRoles / Subjects in `any/all` is not applicable."
|
2024-02-02 10:04:02 +00:00
|
|
|
return false, msg
|
|
|
|
}
|
|
|
|
return true, msg
|
|
|
|
}
|