2020-03-11 18:14:23 -07:00
package mutate
import (
2023-03-22 21:14:57 +08:00
"context"
2020-03-11 18:14:23 -07:00
"fmt"
2023-06-22 22:14:06 +08:00
"strings"
2020-03-11 18:14:23 -07:00
2022-05-17 13:12:43 +02:00
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
2024-10-02 17:35:05 +05:30
"github.com/kyverno/kyverno/ext/wildcard"
2024-08-20 01:55:32 -07:00
"github.com/kyverno/kyverno/pkg/clients/dclient"
2023-03-22 21:14:57 +08:00
"github.com/kyverno/kyverno/pkg/engine/variables/regex"
2024-08-20 01:55:32 -07:00
"github.com/kyverno/kyverno/pkg/logging"
2024-01-24 10:37:48 +01:00
"github.com/kyverno/kyverno/pkg/policy/auth"
2024-08-20 01:55:32 -07:00
"github.com/kyverno/kyverno/pkg/policy/auth/fake"
2023-03-22 21:14:57 +08:00
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
"go.uber.org/multierr"
2020-03-11 18:14:23 -07:00
)
// Mutate provides implementation to validate 'mutate' rule
type Mutate struct {
2024-10-02 17:35:05 +05:30
rule * kyvernov1 . Rule
authCheckerBackground auth . AuthChecks
authCheckerReports auth . AuthChecks
2020-03-11 18:14:23 -07:00
}
2022-05-17 08:19:03 +02:00
// NewMutateFactory returns a new instance of Mutate validation checker
2024-10-02 17:35:05 +05:30
func NewMutateFactory ( rule * kyvernov1 . Rule , client dclient . Interface , mock bool , backgroundSA , reportsSA string ) * Mutate {
var authCheckBackground , authCheckerReports auth . AuthChecks
2024-08-20 01:55:32 -07:00
if mock {
2024-10-02 17:35:05 +05:30
authCheckBackground = fake . NewFakeAuth ( )
authCheckerReports = fake . NewFakeAuth ( )
2024-08-20 01:55:32 -07:00
} else {
2024-10-02 17:35:05 +05:30
authCheckBackground = auth . NewAuth ( client , backgroundSA , logging . GlobalLogger ( ) )
authCheckerReports = auth . NewAuth ( client , reportsSA , logging . GlobalLogger ( ) )
2024-08-20 01:55:32 -07:00
}
2022-01-04 17:36:33 -08:00
return & Mutate {
2024-10-02 17:35:05 +05:30
rule : rule ,
authCheckerBackground : authCheckBackground ,
authCheckerReports : authCheckerReports ,
2020-03-11 18:14:23 -07:00
}
}
2022-05-17 08:19:03 +02:00
// Validate validates the 'mutate' rule
2024-09-04 19:26:24 +08:00
func ( m * Mutate ) Validate ( ctx context . Context , _ [ ] string ) ( warnings [ ] string , path string , err error ) {
2022-01-04 17:36:33 -08:00
if m . hasForEach ( ) {
2022-12-12 07:20:20 -08:00
if m . hasPatchStrategicMerge ( ) || m . hasPatchesJSON6902 ( ) {
2024-08-20 01:55:32 -07:00
return nil , "foreach" , fmt . Errorf ( "only one of `foreach`, `patchStrategicMerge`, or `patchesJson6902` is allowed" )
2022-12-12 07:20:20 -08:00
}
2024-10-02 17:35:05 +05:30
return m . validateForEach ( "" , m . rule . Mutation . ForEachMutation )
2020-03-11 18:14:23 -07:00
}
2022-01-04 17:36:33 -08:00
if m . hasPatchesJSON6902 ( ) && m . hasPatchStrategicMerge ( ) {
2024-08-20 01:55:32 -07:00
return nil , "foreach" , fmt . Errorf ( "only one of `patchStrategicMerge` or `patchesJson6902` is allowed" )
2020-03-11 18:14:23 -07:00
}
2022-01-04 17:36:33 -08:00
2024-10-02 17:35:05 +05:30
if m . rule . Mutation . Targets != nil {
if err := m . validateAuth ( ctx , m . rule . Mutation . Targets ) ; err != nil {
return nil , "targets" , fmt . Errorf ( "auth check fails, additional privileges are required for the service account '%s': %v" , m . authCheckerBackground . User ( ) , err )
2023-03-22 21:14:57 +08:00
}
}
2024-10-02 17:35:05 +05:30
if w , err := m . validateAuthReports ( ctx ) ; err != nil {
return nil , "" , err
} else if len ( w ) > 0 {
warnings = append ( warnings , w ... )
}
return warnings , "" , nil
2020-03-11 18:14:23 -07:00
}
2024-08-20 01:55:32 -07:00
func ( m * Mutate ) validateForEach ( tag string , foreach [ ] kyvernov1 . ForEachMutation ) ( warnings [ ] string , path string , err error ) {
2022-12-12 07:20:20 -08:00
for i , fe := range foreach {
tag = tag + fmt . Sprintf ( "foreach[%d]" , i )
2024-07-30 13:52:41 +03:00
fem := fe . GetForEachMutation ( )
if len ( fem ) > 0 {
2024-08-20 01:55:32 -07:00
if fe . Context != nil || fe . AnyAllConditions != nil || fe . PatchesJSON6902 != "" || fe . RawPatchStrategicMerge != nil {
return nil , tag , fmt . Errorf ( "a nested foreach cannot contain other declarations" )
2022-12-12 07:20:20 -08:00
}
2024-07-30 13:52:41 +03:00
return m . validateNestedForEach ( tag , fem )
2022-12-12 07:20:20 -08:00
}
2020-03-11 18:14:23 -07:00
2022-03-06 20:07:51 +01:00
psm := fe . GetPatchStrategicMerge ( )
if ( fe . PatchesJSON6902 == "" && psm == nil ) || ( fe . PatchesJSON6902 != "" && psm != nil ) {
2024-08-20 01:55:32 -07:00
return nil , tag , fmt . Errorf ( "only one of `patchStrategicMerge` or `patchesJson6902` is allowed" )
2022-01-04 17:36:33 -08:00
}
2020-03-11 18:14:23 -07:00
}
2024-08-20 01:55:32 -07:00
return nil , "" , nil
2022-01-04 17:36:33 -08:00
}
2024-08-20 01:55:32 -07:00
func ( m * Mutate ) validateNestedForEach ( tag string , j [ ] kyvernov1 . ForEachMutation ) ( warnings [ ] string , path string , err error ) {
2024-07-30 13:52:41 +03:00
if j != nil {
return m . validateForEach ( tag , j )
2022-12-12 07:20:20 -08:00
}
2024-08-20 01:55:32 -07:00
return nil , "" , nil
2022-12-12 07:20:20 -08:00
}
2022-01-04 17:36:33 -08:00
func ( m * Mutate ) hasForEach ( ) bool {
2024-10-02 17:35:05 +05:30
return len ( m . rule . Mutation . ForEachMutation ) > 0
2022-01-04 17:36:33 -08:00
}
func ( m * Mutate ) hasPatchStrategicMerge ( ) bool {
2024-10-02 17:35:05 +05:30
return m . rule . Mutation . GetPatchStrategicMerge ( ) != nil
2022-01-04 17:36:33 -08:00
}
func ( m * Mutate ) hasPatchesJSON6902 ( ) bool {
2024-10-02 17:35:05 +05:30
return m . rule . Mutation . PatchesJSON6902 != ""
2020-03-11 18:14:23 -07:00
}
2023-03-22 21:14:57 +08:00
2024-10-02 17:35:05 +05:30
func ( m * Mutate ) validateAuth ( ctx context . Context , targets [ ] kyvernov1 . TargetResourceSpec ) ( err error ) {
2023-03-22 21:14:57 +08:00
var errs [ ] error
for _ , target := range targets {
2024-08-20 01:55:32 -07:00
if regex . IsVariable ( target . Kind ) {
continue
}
_ , _ , k , sub := kubeutils . ParseKindSelector ( target . Kind )
gvk := strings . Join ( [ ] string { target . APIVersion , k } , "/" )
verbs := [ ] string { "get" , "update" }
2024-10-02 17:35:05 +05:30
ok , msg , err := m . authCheckerBackground . CanI ( ctx , verbs , gvk , target . Namespace , target . Name , sub )
2024-08-20 01:55:32 -07:00
if err != nil {
return err
}
if ! ok {
2024-08-28 19:09:58 +02:00
errs = append ( errs , fmt . Errorf ( msg ) ) //nolint:all
2023-03-22 21:14:57 +08:00
}
}
2024-08-20 01:55:32 -07:00
2023-03-22 21:14:57 +08:00
return multierr . Combine ( errs ... )
}
2024-10-02 17:35:05 +05:30
func ( m * Mutate ) validateAuthReports ( ctx context . Context ) ( warnings [ ] string , err error ) {
kinds := m . rule . MatchResources . GetKinds ( )
for _ , k := range kinds {
if wildcard . ContainsWildcard ( k ) {
return nil , nil
}
verbs := [ ] string { "get" , "list" , "watch" }
ok , msg , err := m . authCheckerReports . CanI ( ctx , verbs , k , "" , "" , "" )
if err != nil {
return nil , err
}
if ! ok {
warnings = append ( warnings , msg )
}
}
return warnings , nil
}