2024-02-02 12:04:02 +02:00
package validatingadmissionpolicy
2023-08-31 13:25:21 +03:00
import (
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
2024-08-13 14:55:22 +03:00
kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2"
2024-05-07 20:19:12 +08:00
"github.com/kyverno/kyverno/ext/wildcard"
2023-08-31 13:25:21 +03:00
)
2024-08-13 14:55:22 +03:00
// CanGenerateVAP check if Kyverno policy and a PolicyException can be translated to a Kubernetes ValidatingAdmissionPolicy
func CanGenerateVAP ( spec * kyvernov1 . Spec , exceptions [ ] kyvernov2 . PolicyException ) ( bool , string ) {
var msg string
if ok , msg := checkPolicy ( spec ) ; ! ok {
return false , msg
}
if ok , msg := checkExceptions ( exceptions ) ; ! ok {
return false , msg
}
return true , msg
}
func checkExceptions ( exceptions [ ] kyvernov2 . PolicyException ) ( bool , string ) {
var msg string
for _ , exception := range exceptions {
spec := exception . Spec
for _ , exception := range spec . Exceptions {
if len ( exception . RuleNames ) > 1 {
msg = "skip generating ValidatingAdmissionPolicy: multiple ruleNames in PolicyException is not applicable."
return false , msg
}
}
if spec . Conditions != nil {
msg = "skip generating ValidatingAdmissionPolicy: Conditions in PolicyException is not applicable."
return false , msg
}
exclude := spec . Match
if ok , msg := checkResourceFilter ( exclude . Any , false ) ; ! ok {
return false , msg
}
if len ( exclude . All ) > 1 {
msg = "skip generating ValidatingAdmissionPolicy: multiple 'all' in the PolicyException's match block is not applicable."
return false , msg
}
if ok , msg := checkResourceFilter ( exclude . All , false ) ; ! ok {
return false , msg
}
}
return true , msg
}
func checkPolicy ( spec * kyvernov1 . Spec ) ( bool , string ) {
2023-08-31 13:25:21 +03:00
var msg string
2024-07-16 18:06:58 +03:00
if ok , msg := checkRuleCount ( spec ) ; ! ok {
2023-08-31 13:25:21 +03:00
return false , msg
}
rule := spec . Rules [ 0 ]
2024-07-16 18:06:58 +03:00
if ok , msg := checkRuleType ( rule ) ; ! ok {
2023-08-31 13:25:21 +03:00
return false , msg
}
2024-08-06 21:24:28 +03:00
if ok , msg := checkValidationFailureActionOverrides ( spec . ValidationFailureActionOverrides ) ; ! ok {
2023-08-31 13:25:21 +03:00
return false , msg
}
2024-08-27 23:07:57 +03:00
if ok , msg := checkValidationFailureActionOverrides ( rule . Validation . FailureActionOverrides ) ; ! ok {
2023-08-31 13:25:21 +03:00
return false , msg
}
// check the matched/excluded resources of the CEL rule.
match , exclude := rule . MatchResources , rule . ExcludeResources
2024-07-16 18:06:58 +03:00
if ok , msg := checkUserInfo ( match . UserInfo ) ; ! ok {
2023-08-31 13:25:21 +03:00
return false , msg
}
2024-07-16 18:06:58 +03:00
if ok , msg := checkUserInfo ( exclude . UserInfo ) ; ! ok {
2023-08-31 13:25:21 +03:00
return false , msg
}
2024-07-16 18:06:58 +03:00
if ok , msg := checkResources ( match . ResourceDescription , true ) ; ! ok {
return false , msg
}
if ok , msg := checkResources ( exclude . ResourceDescription , false ) ; ! ok {
2023-08-31 13:25:21 +03:00
return false , msg
}
2024-07-16 18:06:58 +03:00
if ok , msg := checkResourceFilter ( match . Any , true ) ; ! ok {
return false , msg
}
2023-08-31 13:25:21 +03:00
2024-07-16 18:06:58 +03:00
if len ( match . All ) > 1 {
msg = "skip generating ValidatingAdmissionPolicy: multiple 'all' in the match block is not applicable."
return false , msg
}
if ok , msg := checkResourceFilter ( match . All , true ) ; ! ok {
return false , msg
}
2023-08-31 13:25:21 +03:00
2024-07-16 18:06:58 +03:00
if ok , msg := checkResourceFilter ( exclude . Any , false ) ; ! ok {
return false , msg
}
if len ( exclude . All ) > 1 {
msg = "skip generating ValidatingAdmissionPolicy: multiple 'all' in the exclude block is not applicable."
return false , msg
2023-08-31 13:25:21 +03:00
}
2024-07-16 18:06:58 +03:00
if ok , msg := checkResourceFilter ( exclude . All , false ) ; ! ok {
2024-05-06 21:52:22 +08:00
return false , msg
}
2024-07-16 18:06:58 +03:00
return true , msg
}
func checkRuleCount ( spec * kyvernov1 . Spec ) ( bool , string ) {
var msg string
if len ( spec . Rules ) > 1 {
msg = "skip generating ValidatingAdmissionPolicy: multiple rules are not applicable."
return false , msg
2023-08-31 13:25:21 +03:00
}
2024-07-16 18:06:58 +03:00
return true , msg
}
2023-08-31 13:25:21 +03:00
2024-07-16 18:06:58 +03:00
func checkRuleType ( rule kyvernov1 . Rule ) ( bool , string ) {
var msg string
if ! rule . HasValidateCEL ( ) {
msg = "skip generating ValidatingAdmissionPolicy for non CEL rules."
return false , msg
}
2023-08-31 13:25:21 +03:00
return true , msg
}
2024-02-02 12:04:02 +02:00
2024-07-16 18:06:58 +03:00
func checkResources ( resource kyvernov1 . ResourceDescription , isMatch bool ) ( bool , string ) {
2024-02-02 12:04:02 +02:00
var msg string
2024-07-16 18:06:58 +03:00
if ! isMatch {
if len ( resource . Kinds ) != 0 && len ( resource . Namespaces ) != 0 {
msg = "skip generating ValidatingAdmissionPolicy: excluding a resource within a namespace is not applicable."
return false , msg
}
}
2024-05-08 19:09:47 +08:00
if len ( resource . Annotations ) != 0 {
msg = "skip generating ValidatingAdmissionPolicy: Annotations in resource description is not applicable."
2024-02-02 12:04:02 +02:00
return false , msg
}
2024-05-07 20:19:12 +08: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-05-10 22:19:10 +08:00
for _ , ns := range resource . Namespaces {
if wildcard . ContainsWildcard ( ns ) {
msg = "skip generating ValidatingAdmissionPolicy: wildcards in namespace name is not applicable."
return false , msg
}
}
2024-02-02 12:04:02 +02:00
return true , msg
}
func checkUserInfo ( info kyvernov1 . UserInfo ) ( bool , string ) {
var msg string
if ! info . IsEmpty ( ) {
2024-05-07 20:19:12 +08:00
msg = "skip generating ValidatingAdmissionPolicy: Roles / ClusterRoles / Subjects in `any/all` is not applicable."
2024-02-02 12:04:02 +02:00
return false , msg
}
return true , msg
}
2024-07-16 18:06:58 +03:00
func checkResourceFilter ( resFilters kyvernov1 . ResourceFilters , isMatch bool ) ( bool , string ) {
var msg string
containsNamespaceSelector := false
containsObjectSelector := false
for _ , value := range resFilters {
if ok , msg := checkUserInfo ( value . UserInfo ) ; ! ok {
return false , msg
}
if ok , msg := checkResources ( value . ResourceDescription , isMatch ) ; ! ok {
return false , msg
}
if value . NamespaceSelector != nil {
containsNamespaceSelector = true
}
if value . Selector != nil {
containsObjectSelector = true
}
}
if ! isMatch {
if containsNamespaceSelector || containsObjectSelector {
msg = "skip generating ValidatingAdmissionPolicy: NamespaceSelector / ObjectSelector in the exclude block is not applicable."
return false , msg
}
} else {
if len ( resFilters ) > 1 && ( containsNamespaceSelector || containsObjectSelector ) {
return false , "skip generating ValidatingAdmissionPolicy: NamespaceSelector / ObjectSelector across multiple resources in the match block are not applicable."
}
}
return true , msg
}
2024-08-06 21:24:28 +03:00
func checkValidationFailureActionOverrides ( validationFailureActionOverrides [ ] kyvernov1 . ValidationFailureActionOverride ) ( bool , string ) {
var msg string
if len ( validationFailureActionOverrides ) > 1 {
msg = "skip generating ValidatingAdmissionPolicy: multiple validationFailureActionOverrides are not applicable."
return false , msg
}
if len ( validationFailureActionOverrides ) != 0 && len ( validationFailureActionOverrides [ 0 ] . Namespaces ) != 0 {
msg = "skip generating ValidatingAdmissionPolicy: Namespaces in validationFailureActionOverrides is not applicable."
return false , msg
}
return true , msg
}