2023-03-27 17:11:27 +02:00
package validation
import (
"context"
"fmt"
2024-01-23 17:57:39 +05:30
"strings"
2023-03-27 17:11:27 +02:00
"github.com/go-logr/logr"
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
2024-06-24 23:36:55 +07:00
kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2"
2023-03-27 17:11:27 +02:00
"github.com/kyverno/kyverno/pkg/config"
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
"github.com/kyverno/kyverno/pkg/engine/handlers"
engineutils "github.com/kyverno/kyverno/pkg/engine/utils"
apiutils "github.com/kyverno/kyverno/pkg/utils/api"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2023-11-13 17:43:25 +02:00
"k8s.io/client-go/tools/cache"
2023-03-27 17:11:27 +02:00
)
2023-04-03 21:58:58 +02:00
type validateImageHandler struct { }
2023-03-27 17:11:27 +02:00
func NewValidateImageHandler (
2023-04-03 21:58:58 +02:00
policyContext engineapi . PolicyContext ,
resource unstructured . Unstructured ,
rule kyvernov1 . Rule ,
2023-03-27 17:11:27 +02:00
configuration config . Configuration ,
2023-04-03 21:58:58 +02:00
) ( handlers . Handler , error ) {
if engineutils . IsDeleteRequest ( policyContext ) {
return nil , nil
2023-03-31 08:41:48 +02:00
}
2023-04-03 21:58:58 +02:00
ruleImages , _ , err := engineutils . ExtractMatchingImages ( resource , policyContext . JSONContext ( ) , rule , configuration )
if err != nil {
return nil , err
}
if len ( ruleImages ) == 0 {
return nil , nil
}
return validateImageHandler { } , nil
2023-03-27 17:11:27 +02:00
}
func ( h validateImageHandler ) Process (
ctx context . Context ,
logger logr . Logger ,
policyContext engineapi . PolicyContext ,
resource unstructured . Unstructured ,
rule kyvernov1 . Rule ,
2023-04-03 21:58:58 +02:00
_ engineapi . EngineContextLoader ,
2024-06-24 23:36:55 +07:00
exceptions [ ] * kyvernov2 . PolicyException ,
2023-03-27 17:11:27 +02:00
) ( unstructured . Unstructured , [ ] engineapi . RuleResponse ) {
2024-07-25 20:36:19 +03:00
// check if there are policy exceptions that match the incoming resource
matchedExceptions := engineutils . MatchesException ( exceptions , policyContext , logger )
if len ( matchedExceptions ) > 0 {
var keys [ ] string
for i , exception := range matchedExceptions {
key , err := cache . MetaNamespaceKeyFunc ( & matchedExceptions [ i ] )
if err != nil {
logger . Error ( err , "failed to compute policy exception key" , "namespace" , exception . GetNamespace ( ) , "name" , exception . GetName ( ) )
return resource , handlers . WithError ( rule , engineapi . Validation , "failed to compute exception key" , err )
}
keys = append ( keys , key )
2023-11-13 17:43:25 +02:00
}
2024-07-25 20:36:19 +03:00
logger . V ( 3 ) . Info ( "policy rule is skipped due to policy exceptions" , "exceptions" , keys )
return resource , handlers . WithResponses (
2024-09-03 23:06:07 +05:30
engineapi . RuleSkip ( rule . Name , engineapi . Validation , "rule is skipped due to policy exceptions" + strings . Join ( keys , ", " ) , rule . ReportProperties ) . WithExceptions ( matchedExceptions ) ,
2024-07-25 20:36:19 +03:00
)
2023-11-13 17:43:25 +02:00
}
2024-01-23 17:57:39 +05:30
skippedImages := make ( [ ] string , 0 )
passedImages := make ( [ ] string , 0 )
2023-03-27 17:11:27 +02:00
for _ , v := range rule . VerifyImages {
imageVerify := v . Convert ( )
for _ , infoMap := range policyContext . JSONContext ( ) . ImageInfo ( ) {
2024-03-11 02:32:05 -07:00
for _ , imageInfo := range infoMap {
2023-03-27 17:11:27 +02:00
image := imageInfo . String ( )
if ! engineutils . ImageMatches ( image , imageVerify . ImageReferences ) {
logger . V ( 4 ) . Info ( "image does not match" , "imageReferences" , imageVerify . ImageReferences )
return resource , nil
}
logger . V ( 4 ) . Info ( "validating image" , "image" , image )
2024-03-11 02:32:05 -07:00
if v , err := validateImage ( policyContext , imageVerify , imageInfo , logger ) ; err != nil {
2023-04-05 12:35:38 +02:00
return resource , handlers . WithFail ( rule , engineapi . ImageVerify , err . Error ( ) )
2024-01-23 17:57:39 +05:30
} else if v == engineapi . ImageVerificationSkip {
skippedImages = append ( skippedImages , image )
} else if v == engineapi . ImageVerificationPass {
passedImages = append ( passedImages , image )
2023-03-27 17:11:27 +02:00
}
}
}
}
2024-01-23 17:57:39 +05:30
2023-03-27 17:11:27 +02:00
logger . V ( 4 ) . Info ( "validated image" , "rule" , rule . Name )
2024-01-23 17:57:39 +05:30
if len ( passedImages ) > 0 || len ( passedImages ) + len ( skippedImages ) == 0 {
if len ( skippedImages ) > 0 {
2024-05-24 15:11:04 +05:30
return resource , handlers . WithPass ( rule , engineapi . ImageVerify , strings . Join ( append ( [ ] string { "image verified, skipped images:" } , skippedImages ... ) , " " ) )
2024-01-23 17:57:39 +05:30
}
2024-05-24 15:11:04 +05:30
return resource , handlers . WithPass ( rule , engineapi . ImageVerify , "image verified" )
2024-01-23 17:57:39 +05:30
} else {
2024-05-24 15:11:04 +05:30
return resource , handlers . WithSkip ( rule , engineapi . ImageVerify , strings . Join ( append ( [ ] string { "image skipped, skipped images:" } , skippedImages ... ) , " " ) )
2024-01-23 17:57:39 +05:30
}
2023-03-27 17:11:27 +02:00
}
2024-03-11 02:32:05 -07:00
func validateImage ( ctx engineapi . PolicyContext , imageVerify * kyvernov1 . ImageVerification , imageInfo apiutils . ImageInfo , log logr . Logger ) ( engineapi . ImageVerificationMetadataStatus , error ) {
2024-01-23 17:57:39 +05:30
var verified engineapi . ImageVerificationMetadataStatus
var err error
2023-03-27 17:11:27 +02:00
image := imageInfo . String ( )
if imageVerify . VerifyDigest && imageInfo . Digest == "" {
log . V ( 2 ) . Info ( "missing digest" , "image" , imageInfo . String ( ) )
2024-01-23 17:57:39 +05:30
return engineapi . ImageVerificationFail , fmt . Errorf ( "missing digest for %s" , image )
2023-03-27 17:11:27 +02:00
}
newResource := ctx . NewResource ( )
if imageVerify . Required && newResource . Object != nil {
2024-01-23 17:57:39 +05:30
verified , err = engineutils . IsImageVerified ( newResource , image , log )
2023-03-27 17:11:27 +02:00
if err != nil {
2024-01-23 17:57:39 +05:30
return engineapi . ImageVerificationFail , err
2023-03-27 17:11:27 +02:00
}
2024-01-23 17:57:39 +05:30
if verified == engineapi . ImageVerificationFail {
return engineapi . ImageVerificationFail , fmt . Errorf ( "unverified image %s" , image )
2023-03-27 17:11:27 +02:00
}
}
2024-01-23 17:57:39 +05:30
return verified , nil
2023-03-27 17:11:27 +02:00
}