2020-05-29 15:32:29 +05:30
package common
import (
2020-09-01 09:38:49 -07:00
"bufio"
2022-05-11 14:04:40 +02:00
"context"
2020-05-29 15:32:29 +05:30
"encoding/json"
"fmt"
2022-09-30 15:25:19 +08:00
"io"
2021-02-08 23:38:06 +05:30
"net/http"
2020-10-19 12:36:55 -07:00
"os"
"path/filepath"
2021-02-02 18:43:19 +05:30
"strings"
2020-08-31 19:32:00 +05:30
2021-02-07 20:26:56 -08:00
"github.com/go-git/go-billy/v5"
2022-05-17 13:12:43 +02:00
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
kyvernov1beta1 "github.com/kyverno/kyverno/api/kyverno/v1beta1"
policyreportv1alpha2 "github.com/kyverno/kyverno/api/policyreport/v1alpha2"
2022-04-14 17:50:18 +05:30
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store"
2022-04-25 20:20:40 +08:00
"github.com/kyverno/kyverno/pkg/autogen"
2022-05-25 19:56:22 +05:30
"github.com/kyverno/kyverno/pkg/background/generate"
2022-08-31 14:03:47 +08:00
"github.com/kyverno/kyverno/pkg/clients/dclient"
2023-01-02 18:14:40 +01:00
"github.com/kyverno/kyverno/pkg/config"
2021-02-07 20:26:56 -08:00
"github.com/kyverno/kyverno/pkg/engine"
2023-01-30 12:41:09 +01:00
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
2022-05-11 14:04:40 +02:00
engineContext "github.com/kyverno/kyverno/pkg/engine/context"
2023-03-03 19:32:40 +08:00
"github.com/kyverno/kyverno/pkg/engine/variables/regex"
2022-12-07 16:08:37 +01:00
"github.com/kyverno/kyverno/pkg/registryclient"
2023-03-24 11:01:49 +01:00
datautils "github.com/kyverno/kyverno/pkg/utils/data"
2022-12-09 22:15:23 +05:30
kubeutils "github.com/kyverno/kyverno/pkg/utils/kube"
2022-09-06 17:16:44 +02:00
yamlutils "github.com/kyverno/kyverno/pkg/utils/yaml"
2021-02-07 20:26:56 -08:00
yamlv2 "gopkg.in/yaml.v2"
2022-12-09 22:15:23 +05:30
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2020-11-17 13:07:30 -08:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2022-05-25 19:56:22 +05:30
"k8s.io/apimachinery/pkg/runtime"
2023-03-22 11:18:11 +01:00
"k8s.io/apimachinery/pkg/runtime/schema"
2020-11-17 13:07:30 -08:00
"k8s.io/apimachinery/pkg/util/yaml"
2021-02-07 20:26:56 -08:00
"sigs.k8s.io/controller-runtime/pkg/log"
2020-05-29 15:32:29 +05:30
)
2021-08-31 21:18:54 +05:30
type ResultCounts struct {
Pass int
Fail int
Warn int
Error int
Skip int
}
2021-02-02 18:43:19 +05:30
type Policy struct {
Name string ` json:"name" `
Resources [ ] Resource ` json:"resources" `
2021-04-29 22:39:44 +05:30
Rules [ ] Rule ` json:"rules" `
}
type Rule struct {
2022-05-07 21:30:11 +01:00
Name string ` json:"name" `
Values map [ string ] interface { } ` json:"values" `
ForeachValues map [ string ] [ ] interface { } ` json:"foreachValues" `
2021-02-02 18:43:19 +05:30
}
type Values struct {
2021-03-10 02:15:45 +05:30
Policies [ ] Policy ` json:"policies" `
2021-09-20 22:16:57 +05:30
GlobalValues map [ string ] string ` json:"globalValues" `
2021-03-10 02:15:45 +05:30
NamespaceSelectors [ ] NamespaceSelector ` json:"namespaceSelector" `
2022-12-09 22:15:23 +05:30
Subresources [ ] Subresource ` json:"subresources" `
2021-03-10 02:15:45 +05:30
}
2021-04-29 22:39:44 +05:30
type Resource struct {
2022-05-07 21:30:11 +01:00
Name string ` json:"name" `
Values map [ string ] interface { } ` json:"values" `
2021-04-29 22:39:44 +05:30
}
2022-12-09 22:15:23 +05:30
type Subresource struct {
APIResource metav1 . APIResource ` json:"subresource" `
ParentResource metav1 . APIResource ` json:"parentResource" `
}
2021-03-10 02:15:45 +05:30
type NamespaceSelector struct {
Name string ` json:"name" `
Labels map [ string ] string ` json:"labels" `
2021-02-02 18:43:19 +05:30
}
2022-10-19 22:09:15 +05:30
type ApplyPolicyConfig struct {
Policy kyvernov1 . PolicyInterface
Resource * unstructured . Unstructured
MutateLogPath string
MutateLogPathIsDir bool
Variables map [ string ] interface { }
UserInfo kyvernov1beta1 . RequestInfo
PolicyReport bool
NamespaceSelectorMap map [ string ] map [ string ] string
Stdin bool
Rc * ResultCounts
PrintPatchResource bool
RuleToCloneSourceResource map [ string ] string
Client dclient . Interface
2022-11-21 09:21:32 -05:00
AuditWarn bool
2022-12-09 22:15:23 +05:30
Subresources [ ] Subresource
2022-10-19 22:09:15 +05:30
}
2021-11-03 11:16:55 -07:00
// HasVariables - check for variables in the policy
2022-05-17 13:12:43 +02:00
func HasVariables ( policy kyvernov1 . PolicyInterface ) [ ] [ ] string {
2021-11-03 11:16:55 -07:00
policyRaw , _ := json . Marshal ( policy )
2023-03-03 19:32:40 +08:00
matches := regex . RegexVariables . FindAllStringSubmatch ( string ( policyRaw ) , - 1 )
2021-11-03 11:16:55 -07:00
return matches
}
2021-08-31 21:18:54 +05:30
// GetPolicies - Extracting the policies from multiple YAML
2022-05-17 13:12:43 +02:00
func GetPolicies ( paths [ ] string ) ( policies [ ] kyvernov1 . PolicyInterface , errors [ ] error ) {
2020-11-03 01:25:32 +05:30
for _ , path := range paths {
2020-12-07 11:26:04 -08:00
log . Log . V ( 5 ) . Info ( "reading policies" , "path" , path )
2021-02-08 23:38:06 +05:30
var (
fileDesc os . FileInfo
err error
)
2021-10-29 16:06:03 +01:00
isHTTPPath := IsHTTPRegex . MatchString ( path )
2021-02-08 23:38:06 +05:30
// path clean and retrieving file info can be possible if it's not an HTTP URL
2021-10-29 16:06:03 +01:00
if ! isHTTPPath {
2021-02-08 23:38:06 +05:30
path = filepath . Clean ( path )
fileDesc , err = os . Stat ( path )
if err != nil {
err := fmt . Errorf ( "failed to process %v: %v" , path , err . Error ( ) )
errors = append ( errors , err )
continue
}
2020-10-21 20:05:05 +05:30
}
2020-12-07 11:26:04 -08:00
2021-02-08 23:38:06 +05:30
// apply file from a directory is possible only if the path is not HTTP URL
2021-10-29 16:06:03 +01:00
if ! isHTTPPath && fileDesc . IsDir ( ) {
2022-09-30 15:25:19 +08:00
files , err := os . ReadDir ( path )
2020-10-07 06:20:53 +05:30
if err != nil {
2021-02-08 23:38:06 +05:30
err := fmt . Errorf ( "failed to process %v: %v" , path , err . Error ( ) )
errors = append ( errors , err )
2020-12-07 11:26:04 -08:00
continue
2020-10-07 06:20:53 +05:30
}
2020-12-07 11:26:04 -08:00
2020-11-03 01:25:32 +05:30
listOfFiles := make ( [ ] string , 0 )
for _ , file := range files {
2020-12-07 11:26:04 -08:00
ext := filepath . Ext ( file . Name ( ) )
if ext == "" || ext == ".yaml" || ext == ".yml" {
listOfFiles = append ( listOfFiles , filepath . Join ( path , file . Name ( ) ) )
}
2020-11-01 23:18:58 +05:30
}
2020-12-07 11:26:04 -08:00
policiesFromDir , errorsFromDir := GetPolicies ( listOfFiles )
errors = append ( errors , errorsFromDir ... )
2020-11-03 01:25:32 +05:30
policies = append ( policies , policiesFromDir ... )
} else {
2021-02-08 23:38:06 +05:30
var fileBytes [ ] byte
2021-10-29 16:06:03 +01:00
if isHTTPPath {
2021-10-14 00:05:13 +02:00
// We accept here that a random URL might be called based on user provided input.
2022-05-11 14:04:40 +02:00
req , err := http . NewRequestWithContext ( context . TODO ( ) , http . MethodGet , path , nil )
if err != nil {
err := fmt . Errorf ( "failed to process %v: %v" , path , err . Error ( ) )
errors = append ( errors , err )
continue
}
resp , err := http . DefaultClient . Do ( req )
2021-02-08 23:38:06 +05:30
if err != nil {
err := fmt . Errorf ( "failed to process %v: %v" , path , err . Error ( ) )
errors = append ( errors , err )
continue
}
defer resp . Body . Close ( )
if resp . StatusCode != http . StatusOK {
err := fmt . Errorf ( "failed to process %v: %v" , path , err . Error ( ) )
errors = append ( errors , err )
continue
}
2022-09-30 15:25:19 +08:00
fileBytes , err = io . ReadAll ( resp . Body )
2021-02-08 23:38:06 +05:30
if err != nil {
err := fmt . Errorf ( "failed to process %v: %v" , path , err . Error ( ) )
errors = append ( errors , err )
continue
}
} else {
2021-10-13 10:48:45 -07:00
path = filepath . Clean ( path )
2021-10-14 00:05:13 +02:00
// We accept the risk of including a user provided file here.
2022-09-30 15:25:19 +08:00
fileBytes , err = os . ReadFile ( path ) // #nosec G304
2021-02-08 23:38:06 +05:30
if err != nil {
err := fmt . Errorf ( "failed to process %v: %v" , path , err . Error ( ) )
errors = append ( errors , err )
continue
}
2020-11-03 01:25:32 +05:30
}
2020-12-07 11:26:04 -08:00
2022-09-06 17:16:44 +02:00
policiesFromFile , errFromFile := yamlutils . GetPolicy ( fileBytes )
2020-12-07 11:26:04 -08:00
if errFromFile != nil {
err := fmt . Errorf ( "failed to process %s: %v" , path , errFromFile . Error ( ) )
errors = append ( errors , err )
continue
2020-11-01 23:18:58 +05:30
}
2020-10-21 20:05:05 +05:30
2020-12-07 11:26:04 -08:00
policies = append ( policies , policiesFromFile ... )
2020-11-01 23:18:58 +05:30
}
2020-10-21 20:05:05 +05:30
}
2020-12-07 11:26:04 -08:00
log . Log . V ( 3 ) . Info ( "read policies" , "policies" , len ( policies ) , "errors" , len ( errors ) )
return policies , errors
2020-05-29 15:32:29 +05:30
}
2020-07-07 16:20:55 +05:30
2020-10-07 06:20:53 +05:30
// IsInputFromPipe - check if input is passed using pipe
func IsInputFromPipe ( ) bool {
fileInfo , _ := os . Stdin . Stat ( )
return fileInfo . Mode ( ) & os . ModeCharDevice == 0
}
2021-02-02 18:43:19 +05:30
2021-07-24 00:02:48 +05:30
// RemoveDuplicateAndObjectVariables - remove duplicate variables
func RemoveDuplicateAndObjectVariables ( matches [ ] [ ] string ) string {
2021-02-02 18:43:19 +05:30
var variableStr string
for _ , m := range matches {
for _ , v := range m {
foundVariable := strings . Contains ( variableStr , v )
if ! foundVariable {
2022-01-04 17:36:33 -08:00
if ! strings . Contains ( v , "request.object" ) && ! strings . Contains ( v , "element" ) && v == "elementIndex" {
2021-07-24 00:02:48 +05:30
variableStr = variableStr + " " + v
}
2021-02-02 18:43:19 +05:30
}
}
}
return variableStr
}
2022-12-09 22:15:23 +05:30
func GetVariable ( variablesString , valuesFile string , fs billy . Filesystem , isGit bool , policyResourcePath string ) ( map [ string ] string , map [ string ] string , map [ string ] map [ string ] Resource , map [ string ] map [ string ] string , [ ] Subresource , error ) {
2021-04-29 22:39:44 +05:30
valuesMapResource := make ( map [ string ] map [ string ] Resource )
valuesMapRule := make ( map [ string ] map [ string ] Rule )
2021-03-10 02:15:45 +05:30
namespaceSelectorMap := make ( map [ string ] map [ string ] string )
2021-02-03 18:24:50 +05:30
variables := make ( map [ string ] string )
2022-12-09 22:15:23 +05:30
subresources := make ( [ ] Subresource , 0 )
2021-09-20 22:16:57 +05:30
globalValMap := make ( map [ string ] string )
2021-08-01 16:27:47 +05:30
reqObjVars := ""
2021-07-28 10:52:32 +05:30
2021-02-18 01:00:41 +05:30
var yamlFile [ ] byte
var err error
2021-02-02 18:43:19 +05:30
if variablesString != "" {
kvpairs := strings . Split ( strings . Trim ( variablesString , " " ) , "," )
for _ , kvpair := range kvpairs {
kvs := strings . Split ( strings . Trim ( kvpair , " " ) , "=" )
2021-07-24 00:02:48 +05:30
if strings . Contains ( kvs [ 0 ] , "request.object" ) {
2021-08-01 16:27:47 +05:30
if ! strings . Contains ( reqObjVars , kvs [ 0 ] ) {
reqObjVars = reqObjVars + "," + kvs [ 0 ]
}
continue
2021-07-24 00:02:48 +05:30
}
2021-07-28 10:52:32 +05:30
2021-02-02 18:43:19 +05:30
variables [ strings . Trim ( kvs [ 0 ] , " " ) ] = strings . Trim ( kvs [ 1 ] , " " )
}
}
2021-07-24 00:02:48 +05:30
2021-02-02 18:43:19 +05:30
if valuesFile != "" {
2021-02-18 01:00:41 +05:30
if isGit {
2021-06-22 18:56:44 +05:30
filep , err := fs . Open ( filepath . Join ( policyResourcePath , valuesFile ) )
2021-02-18 01:00:41 +05:30
if err != nil {
fmt . Printf ( "Unable to open variable file: %s. error: %s" , valuesFile , err )
}
2022-09-30 15:25:19 +08:00
yamlFile , err = io . ReadAll ( filep )
2021-10-14 00:05:13 +02:00
if err != nil {
fmt . Printf ( "Unable to read variable files: %s. error: %s \n" , filep , err )
}
2021-02-18 01:00:41 +05:30
} else {
2021-10-14 00:05:13 +02:00
// We accept the risk of including a user provided file here.
2022-09-30 15:25:19 +08:00
yamlFile , err = os . ReadFile ( filepath . Join ( policyResourcePath , valuesFile ) ) // #nosec G304
2021-09-03 18:43:11 +05:30
if err != nil {
fmt . Printf ( "\n Unable to open variable file: %s. error: %s \n" , valuesFile , err )
}
2021-02-18 01:00:41 +05:30
}
2021-02-02 18:43:19 +05:30
if err != nil {
2022-12-09 22:15:23 +05:30
return variables , globalValMap , valuesMapResource , namespaceSelectorMap , subresources , sanitizederror . NewWithError ( "unable to read yaml" , err )
2021-02-02 18:43:19 +05:30
}
valuesBytes , err := yaml . ToJSON ( yamlFile )
if err != nil {
2022-12-09 22:15:23 +05:30
return variables , globalValMap , valuesMapResource , namespaceSelectorMap , subresources , sanitizederror . NewWithError ( "failed to convert json" , err )
2021-02-02 18:43:19 +05:30
}
values := & Values { }
if err := json . Unmarshal ( valuesBytes , values ) ; err != nil {
2022-12-09 22:15:23 +05:30
return variables , globalValMap , valuesMapResource , namespaceSelectorMap , subresources , sanitizederror . NewWithError ( "failed to decode yaml" , err )
2021-02-02 18:43:19 +05:30
}
2022-05-25 19:29:53 +05:30
if values . GlobalValues == nil {
values . GlobalValues = make ( map [ string ] string )
values . GlobalValues [ "request.operation" ] = "CREATE"
log . Log . V ( 3 ) . Info ( "Defaulting request.operation to CREATE" )
} else {
if val , ok := values . GlobalValues [ "request.operation" ] ; ok {
if val == "" {
values . GlobalValues [ "request.operation" ] = "CREATE"
log . Log . V ( 3 ) . Info ( "Globally request.operation value provided by the user is empty, defaulting it to CREATE" , "request.opearation: " , values . GlobalValues )
}
}
}
2021-09-20 22:16:57 +05:30
globalValMap = values . GlobalValues
2021-02-02 18:43:19 +05:30
for _ , p := range values . Policies {
2021-04-29 22:39:44 +05:30
resourceMap := make ( map [ string ] Resource )
2021-02-02 18:43:19 +05:30
for _ , r := range p . Resources {
2021-11-18 20:39:35 +05:30
if val , ok := r . Values [ "request.operation" ] ; ok {
if val == "" {
r . Values [ "request.operation" ] = "CREATE"
log . Log . V ( 3 ) . Info ( "No request.operation found, defaulting it to CREATE" , "policy" , p . Name )
}
}
2021-07-28 10:52:32 +05:30
for variableInFile := range r . Values {
2021-07-24 00:02:48 +05:30
if strings . Contains ( variableInFile , "request.object" ) {
2021-08-01 16:27:47 +05:30
if ! strings . Contains ( reqObjVars , variableInFile ) {
reqObjVars = reqObjVars + "," + variableInFile
}
delete ( r . Values , variableInFile )
continue
2021-07-24 00:02:48 +05:30
}
}
2021-04-29 22:39:44 +05:30
resourceMap [ r . Name ] = r
}
valuesMapResource [ p . Name ] = resourceMap
if p . Rules != nil {
ruleMap := make ( map [ string ] Rule )
for _ , r := range p . Rules {
ruleMap [ r . Name ] = r
}
valuesMapRule [ p . Name ] = ruleMap
2021-02-02 18:43:19 +05:30
}
}
2021-03-10 02:15:45 +05:30
for _ , n := range values . NamespaceSelectors {
namespaceSelectorMap [ n . Name ] = n . Labels
}
2022-12-09 22:15:23 +05:30
subresources = values . Subresources
2021-02-02 18:43:19 +05:30
}
2021-08-01 16:27:47 +05:30
if reqObjVars != "" {
2022-12-09 22:15:23 +05:30
fmt . Printf ( "\nNOTICE: request.object.* variables are automatically parsed from the supplied resource. Ignoring value of variables `%v`.\n" , reqObjVars )
2021-08-01 16:27:47 +05:30
}
2022-05-25 19:29:53 +05:30
if globalValMap != nil {
2022-12-09 22:15:23 +05:30
if _ , ok := globalValMap [ "request.operation" ] ; ! ok {
2022-11-22 20:17:06 +08:00
globalValMap [ "request.operation" ] = "CREATE"
log . Log . V ( 3 ) . Info ( "Defaulting request.operation to CREATE" )
}
2022-05-25 19:29:53 +05:30
}
2022-03-02 08:29:33 +01:00
storePolicies := make ( [ ] store . Policy , 0 )
2021-04-29 22:39:44 +05:30
for policyName , ruleMap := range valuesMapRule {
storeRules := make ( [ ] store . Rule , 0 )
for _ , rule := range ruleMap {
storeRules = append ( storeRules , store . Rule {
2022-04-25 22:06:31 +05:30
Name : rule . Name ,
Values : rule . Values ,
2022-12-12 11:24:13 -08:00
ForEachValues : rule . ForeachValues ,
2021-04-29 22:39:44 +05:30
} )
}
2022-03-02 08:29:33 +01:00
storePolicies = append ( storePolicies , store . Policy {
2021-04-29 22:39:44 +05:30
Name : policyName ,
Rules : storeRules ,
} )
}
2023-01-30 16:30:47 +01:00
store . SetPolicies ( storePolicies ... )
2021-04-29 22:39:44 +05:30
2022-12-09 22:15:23 +05:30
return variables , globalValMap , valuesMapResource , namespaceSelectorMap , subresources , nil
2021-02-02 18:43:19 +05:30
}
// ApplyPolicyOnResource - function to apply policy on resource
2023-01-30 12:41:09 +01:00
func ApplyPolicyOnResource ( c ApplyPolicyConfig ) ( [ ] * engineapi . EngineResponse , Info , error ) {
var engineResponses [ ] * engineapi . EngineResponse
2021-10-01 17:52:23 +05:30
namespaceLabels := make ( map [ string ] string )
2021-07-28 10:52:32 +05:30
operationIsDelete := false
2022-10-19 22:09:15 +05:30
if c . Variables [ "request.operation" ] == "DELETE" {
2021-07-28 10:52:32 +05:30
operationIsDelete = true
}
2021-02-02 18:43:19 +05:30
2021-03-11 00:24:21 +05:30
policyWithNamespaceSelector := false
2022-01-25 03:06:17 -05:00
OuterLoop :
2022-10-19 22:09:15 +05:30
for _ , p := range autogen . ComputeRules ( c . Policy ) {
2021-03-11 10:31:53 +05:30
if p . MatchResources . ResourceDescription . NamespaceSelector != nil ||
p . ExcludeResources . ResourceDescription . NamespaceSelector != nil {
2021-03-11 00:24:21 +05:30
policyWithNamespaceSelector = true
break
}
2022-01-25 03:06:17 -05:00
for _ , m := range p . MatchResources . Any {
if m . ResourceDescription . NamespaceSelector != nil {
policyWithNamespaceSelector = true
break OuterLoop
}
}
for _ , m := range p . MatchResources . All {
if m . ResourceDescription . NamespaceSelector != nil {
policyWithNamespaceSelector = true
break OuterLoop
}
}
for _ , e := range p . ExcludeResources . Any {
if e . ResourceDescription . NamespaceSelector != nil {
policyWithNamespaceSelector = true
break OuterLoop
}
}
for _ , e := range p . ExcludeResources . All {
if e . ResourceDescription . NamespaceSelector != nil {
policyWithNamespaceSelector = true
break OuterLoop
}
}
2021-03-11 00:24:21 +05:30
}
if policyWithNamespaceSelector {
2022-10-19 22:09:15 +05:30
resourceNamespace := c . Resource . GetNamespace ( )
namespaceLabels = c . NamespaceSelectorMap [ c . Resource . GetNamespace ( ) ]
2021-03-11 00:24:21 +05:30
if resourceNamespace != "default" && len ( namespaceLabels ) < 1 {
2022-11-02 09:06:44 +00:00
return engineResponses , Info { } , sanitizederror . NewWithError ( fmt . Sprintf ( "failed to get namespace labels for resource %s. use --values-file flag to pass the namespace labels" , c . Resource . GetName ( ) ) , nil )
2021-03-11 00:24:21 +05:30
}
2021-03-10 02:15:45 +05:30
}
2021-03-11 00:24:21 +05:30
2022-10-19 22:09:15 +05:30
resPath := fmt . Sprintf ( "%s/%s/%s" , c . Resource . GetNamespace ( ) , c . Resource . GetKind ( ) , c . Resource . GetName ( ) )
log . Log . V ( 3 ) . Info ( "applying policy on resource" , "policy" , c . Policy . GetName ( ) , "resource" , resPath )
2021-02-02 18:43:19 +05:30
2022-10-19 22:09:15 +05:30
resourceRaw , err := c . Resource . MarshalJSON ( )
2021-07-24 00:02:48 +05:30
if err != nil {
log . Log . Error ( err , "failed to marshal resource" )
}
2023-01-03 13:02:15 +01:00
updatedResource , err := kubeutils . BytesToUnstructured ( resourceRaw )
2022-01-19 05:08:49 +05:30
if err != nil {
log . Log . Error ( err , "unable to convert raw resource to unstructured" )
}
2022-05-11 14:04:40 +02:00
ctx := engineContext . NewContext ( )
2022-01-19 05:08:49 +05:30
2021-07-24 00:02:48 +05:30
if operationIsDelete {
2022-05-11 14:04:40 +02:00
err = engineContext . AddOldResource ( ctx , resourceRaw )
2021-07-24 00:02:48 +05:30
} else {
2022-05-11 14:04:40 +02:00
err = engineContext . AddResource ( ctx , resourceRaw )
2021-07-24 00:02:48 +05:30
}
2021-11-03 14:06:47 -07:00
2021-07-24 00:02:48 +05:30
if err != nil {
log . Log . Error ( err , "failed to load resource in context" )
}
2022-10-19 22:09:15 +05:30
for key , value := range c . Variables {
2022-04-09 13:52:50 +02:00
err = ctx . AddVariable ( key , value )
2021-10-11 23:57:43 +02:00
if err != nil {
log . Log . Error ( err , "failed to add variable to context" )
}
2021-02-02 18:43:19 +05:30
}
2023-01-02 18:14:40 +01:00
cfg := config . NewDefaultConfiguration ( )
if err := ctx . AddImageInfos ( c . Resource , cfg ) ; err != nil {
2023-03-28 15:36:07 +02:00
log . Log . Error ( err , "failed to add image variables to context" )
2021-11-03 14:06:47 -07:00
}
2023-03-22 11:18:11 +01:00
gvk , subresource := updatedResource . GroupVersionKind ( ) , ""
// If --cluster flag is not set, then we need to find the top level resource GVK and subresource
2022-12-09 22:15:23 +05:30
if c . Client == nil {
2023-03-22 11:18:11 +01:00
for _ , s := range c . Subresources {
subgvk := schema . GroupVersionKind {
Group : s . APIResource . Group ,
Version : s . APIResource . Version ,
Kind : s . APIResource . Kind ,
}
if gvk == subgvk {
gvk = schema . GroupVersionKind {
Group : s . ParentResource . Group ,
Version : s . ParentResource . Version ,
Kind : s . ParentResource . Kind ,
}
parts := strings . Split ( s . APIResource . Name , "/" )
subresource = parts [ 1 ]
}
2022-12-09 22:15:23 +05:30
}
}
2023-02-03 06:01:11 +01:00
eng := engine . NewEngine (
cfg ,
2023-02-07 16:09:15 +01:00
c . Client ,
2023-02-08 06:55:03 +01:00
registryclient . NewOrDie ( ) ,
2023-02-08 14:19:56 +01:00
store . ContextLoaderFactory ( nil ) ,
2023-02-06 06:49:47 +01:00
nil ,
2023-02-03 06:01:11 +01:00
)
2023-03-29 06:22:21 +02:00
policyContext := engine . NewPolicyContextWithJsonContext ( kyvernov1 . Create , ctx ) .
2022-12-02 09:14:23 +01:00
WithPolicy ( c . Policy ) .
WithNewResource ( * updatedResource ) .
WithNamespaceLabels ( namespaceLabels ) .
WithAdmissionInfo ( c . UserInfo ) .
2023-03-22 11:18:11 +01:00
WithResourceKind ( gvk , subresource )
2022-04-27 08:09:52 -07:00
2023-03-22 15:55:00 +01:00
mutateResponse := eng . Mutate ( context . Background ( ) , policyContext )
engineResponses = append ( engineResponses , & mutateResponse )
2021-10-01 17:52:23 +05:30
2023-03-22 15:55:00 +01:00
err = processMutateEngineResponse ( c , & mutateResponse , resPath )
2021-09-03 12:32:12 +05:30
if err != nil {
if ! sanitizederror . IsErrorSanitized ( err ) {
2022-11-02 09:06:44 +00:00
return engineResponses , Info { } , sanitizederror . NewWithError ( "failed to print mutated result" , err )
2021-02-02 18:43:19 +05:30
}
}
2021-09-02 03:54:02 +05:30
var policyHasValidate bool
2022-10-19 22:09:15 +05:30
for _ , rule := range autogen . ComputeRules ( c . Policy ) {
2022-04-27 08:09:52 -07:00
if rule . HasValidate ( ) || rule . HasImagesValidationChecks ( ) {
2021-09-02 03:54:02 +05:30
policyHasValidate = true
}
}
2022-12-02 09:14:23 +01:00
policyContext = policyContext . WithNewResource ( mutateResponse . PatchedResource )
2022-04-27 08:09:52 -07:00
2022-11-02 09:06:44 +00:00
var info Info
2023-03-22 15:55:00 +01:00
var validateResponse engineapi . EngineResponse
2021-09-02 03:54:02 +05:30
if policyHasValidate {
2023-03-22 15:55:00 +01:00
validateResponse = eng . Validate ( context . Background ( ) , policyContext )
info = ProcessValidateEngineResponse ( c . Policy , & validateResponse , resPath , c . Rc , c . PolicyReport , c . AuditWarn )
2021-09-02 03:54:02 +05:30
}
2022-04-27 08:09:52 -07:00
2023-03-22 15:55:00 +01:00
if ! validateResponse . IsEmpty ( ) {
engineResponses = append ( engineResponses , & validateResponse )
2021-10-01 14:16:33 +05:30
}
2021-02-02 18:43:19 +05:30
2023-02-08 06:55:03 +01:00
verifyImageResponse , _ := eng . VerifyAndPatchImages ( context . TODO ( ) , policyContext )
2023-03-23 13:58:52 +01:00
if ! verifyImageResponse . IsEmpty ( ) {
engineResponses = append ( engineResponses , & verifyImageResponse )
info = ProcessValidateEngineResponse ( c . Policy , & verifyImageResponse , resPath , c . Rc , c . PolicyReport , c . AuditWarn )
2022-05-04 09:41:59 +05:30
}
2021-02-02 18:43:19 +05:30
var policyHasGenerate bool
2022-10-19 22:09:15 +05:30
for _ , rule := range autogen . ComputeRules ( c . Policy ) {
2021-02-02 18:43:19 +05:30
if rule . HasGenerate ( ) {
policyHasGenerate = true
}
}
if policyHasGenerate {
2023-02-08 06:55:03 +01:00
generateResponse := eng . ApplyBackgroundChecks ( context . TODO ( ) , policyContext )
2023-03-22 15:55:00 +01:00
if ! generateResponse . IsEmpty ( ) {
newRuleResponse , err := handleGeneratePolicy ( & generateResponse , * policyContext , c . RuleToCloneSourceResource )
2022-05-25 19:56:22 +05:30
if err != nil {
log . Log . Error ( err , "failed to apply generate policy" )
} else {
generateResponse . PolicyResponse . Rules = newRuleResponse
}
2023-03-22 15:55:00 +01:00
engineResponses = append ( engineResponses , & generateResponse )
2021-10-01 14:16:33 +05:30
}
2023-03-22 15:55:00 +01:00
updateResultCounts ( c . Policy , & generateResponse , resPath , c . Rc , c . AuditWarn )
2021-02-02 18:43:19 +05:30
}
2021-02-07 20:26:56 -08:00
2021-10-01 17:52:23 +05:30
return engineResponses , info , nil
2021-02-02 18:43:19 +05:30
}
// PrintMutatedOutput - function to print output in provided file or directory
func PrintMutatedOutput ( mutateLogPath string , mutateLogPathIsDir bool , yaml string , fileName string ) error {
var f * os . File
var err error
yaml = yaml + ( "\n---\n\n" )
2021-10-13 10:48:45 -07:00
mutateLogPath = filepath . Clean ( mutateLogPath )
2021-02-02 18:43:19 +05:30
if ! mutateLogPathIsDir {
2021-07-09 18:01:46 -07:00
// truncation for the case when mutateLogPath is a file (not a directory) is handled under pkg/kyverno/apply/test_command.go
2022-05-17 08:19:03 +02:00
f , err = os . OpenFile ( mutateLogPath , os . O_APPEND | os . O_WRONLY , 0 o600 ) // #nosec G304
2021-02-02 18:43:19 +05:30
} else {
2022-05-17 08:19:03 +02:00
f , err = os . OpenFile ( mutateLogPath + "/" + fileName + ".yaml" , os . O_CREATE | os . O_WRONLY , 0 o600 ) // #nosec G304
2021-02-02 18:43:19 +05:30
}
if err != nil {
return err
}
if _ , err := f . Write ( [ ] byte ( yaml ) ) ; err != nil {
2021-10-14 00:05:13 +02:00
closeErr := f . Close ( )
if closeErr != nil {
log . Log . Error ( closeErr , "failed to close file" )
}
2021-02-02 18:43:19 +05:30
return err
}
if err := f . Close ( ) ; err != nil {
return err
}
return nil
}
// GetPoliciesFromPaths - get policies according to the resource path
2022-05-17 13:12:43 +02:00
func GetPoliciesFromPaths ( fs billy . Filesystem , dirPath [ ] string , isGit bool , policyResourcePath string ) ( policies [ ] kyvernov1 . PolicyInterface , err error ) {
2021-02-02 18:43:19 +05:30
if isGit {
for _ , pp := range dirPath {
2021-06-22 18:56:44 +05:30
filep , err := fs . Open ( filepath . Join ( policyResourcePath , pp ) )
2021-02-18 01:00:41 +05:30
if err != nil {
fmt . Printf ( "Error: file not available with path %s: %v" , filep . Name ( ) , err . Error ( ) )
continue
}
2022-09-30 15:25:19 +08:00
bytes , err := io . ReadAll ( filep )
2021-02-02 18:43:19 +05:30
if err != nil {
fmt . Printf ( "Error: failed to read file %s: %v" , filep . Name ( ) , err . Error ( ) )
2021-02-18 01:00:41 +05:30
continue
2021-02-02 18:43:19 +05:30
}
policyBytes , err := yaml . ToJSON ( bytes )
if err != nil {
fmt . Printf ( "failed to convert to JSON: %v" , err )
continue
}
2022-09-06 17:16:44 +02:00
policiesFromFile , errFromFile := yamlutils . GetPolicy ( policyBytes )
2021-02-02 18:43:19 +05:30
if errFromFile != nil {
2022-05-09 18:55:35 +02:00
fmt . Printf ( "failed to process : %v" , errFromFile . Error ( ) )
2021-02-02 18:43:19 +05:30
continue
}
policies = append ( policies , policiesFromFile ... )
}
} else {
if len ( dirPath ) > 0 && dirPath [ 0 ] == "-" {
if IsInputFromPipe ( ) {
policyStr := ""
scanner := bufio . NewScanner ( os . Stdin )
for scanner . Scan ( ) {
policyStr = policyStr + scanner . Text ( ) + "\n"
}
yamlBytes := [ ] byte ( policyStr )
2022-09-06 17:16:44 +02:00
policies , err = yamlutils . GetPolicy ( yamlBytes )
2021-02-02 18:43:19 +05:30
if err != nil {
return nil , sanitizederror . NewWithError ( "failed to extract the resources" , err )
}
}
} else {
var errors [ ] error
policies , errors = GetPolicies ( dirPath )
if len ( policies ) == 0 {
if len ( errors ) > 0 {
return nil , sanitizederror . NewWithErrors ( "failed to read file" , errors )
}
return nil , sanitizederror . New ( fmt . Sprintf ( "no file found in paths %v" , dirPath ) )
}
if len ( errors ) > 0 && log . Log . V ( 1 ) . Enabled ( ) {
fmt . Printf ( "ignoring errors: \n" )
for _ , e := range errors {
fmt . Printf ( " %v \n" , e . Error ( ) )
}
}
}
2021-02-07 20:26:56 -08:00
}
2021-02-02 18:43:19 +05:30
return
}
// GetResourceAccordingToResourcePath - get resources according to the resource path
2021-02-07 20:26:56 -08:00
func GetResourceAccordingToResourcePath ( fs billy . Filesystem , resourcePaths [ ] string ,
2022-05-17 16:40:51 +02:00
cluster bool , policies [ ] kyvernov1 . PolicyInterface , dClient dclient . Interface , namespace string , policyReport bool , isGit bool , policyResourcePath string ,
2022-05-17 08:19:03 +02:00
) ( resources [ ] * unstructured . Unstructured , err error ) {
2021-02-02 18:43:19 +05:30
if isGit {
2021-06-22 18:56:44 +05:30
resources , err = GetResourcesWithTest ( fs , policies , resourcePaths , isGit , policyResourcePath )
2021-02-02 18:43:19 +05:30
if err != nil {
return nil , sanitizederror . NewWithError ( "failed to extract the resources" , err )
}
} else {
if len ( resourcePaths ) > 0 && resourcePaths [ 0 ] == "-" {
if IsInputFromPipe ( ) {
resourceStr := ""
scanner := bufio . NewScanner ( os . Stdin )
for scanner . Scan ( ) {
resourceStr = resourceStr + scanner . Text ( ) + "\n"
}
yamlBytes := [ ] byte ( resourceStr )
resources , err = GetResource ( yamlBytes )
if err != nil {
return nil , sanitizederror . NewWithError ( "failed to extract the resources" , err )
}
}
2022-03-21 19:31:23 +05:30
} else {
if len ( resourcePaths ) > 0 {
fileDesc , err := os . Stat ( resourcePaths [ 0 ] )
2022-03-15 13:30:59 +05:30
if err != nil {
2022-03-21 19:31:23 +05:30
return nil , err
2022-03-15 13:30:59 +05:30
}
2022-03-21 19:31:23 +05:30
if fileDesc . IsDir ( ) {
2022-09-30 15:25:19 +08:00
files , err := os . ReadDir ( resourcePaths [ 0 ] )
2022-03-21 19:31:23 +05:30
if err != nil {
return nil , sanitizederror . NewWithError ( fmt . Sprintf ( "failed to parse %v" , resourcePaths [ 0 ] ) , err )
}
listOfFiles := make ( [ ] string , 0 )
for _ , file := range files {
ext := filepath . Ext ( file . Name ( ) )
if ext == ".yaml" || ext == ".yml" {
listOfFiles = append ( listOfFiles , filepath . Join ( resourcePaths [ 0 ] , file . Name ( ) ) )
}
2022-03-15 13:30:59 +05:30
}
2022-03-21 19:31:23 +05:30
resourcePaths = listOfFiles
2022-03-15 13:30:59 +05:30
}
}
2022-03-21 19:31:23 +05:30
2021-02-02 18:43:19 +05:30
resources , err = GetResources ( policies , resourcePaths , dClient , cluster , namespace , policyReport )
if err != nil {
return resources , err
}
}
}
return resources , err
2021-02-07 20:26:56 -08:00
}
2021-09-02 00:02:55 +05:30
2023-01-30 12:41:09 +01:00
func ProcessValidateEngineResponse ( policy kyvernov1 . PolicyInterface , validateResponse * engineapi . EngineResponse , resPath string , rc * ResultCounts , policyReport bool , auditWarn bool ) Info {
2022-05-17 13:12:43 +02:00
var violatedRules [ ] kyvernov1 . ViolatedRule
2022-04-12 09:30:49 +05:30
2021-09-02 00:02:55 +05:30
printCount := 0
2022-03-28 16:01:27 +02:00
for _ , policyRule := range autogen . ComputeRules ( policy ) {
2021-09-02 00:02:55 +05:30
ruleFoundInEngineResponse := false
2022-05-04 09:41:59 +05:30
if ! policyRule . HasValidate ( ) && ! policyRule . HasImagesValidationChecks ( ) && ! policyRule . HasVerifyImages ( ) {
2022-02-09 16:03:54 +05:30
continue
}
2021-09-02 00:02:55 +05:30
for i , valResponseRule := range validateResponse . PolicyResponse . Rules {
if policyRule . Name == valResponseRule . Name {
ruleFoundInEngineResponse = true
2022-05-17 13:12:43 +02:00
vrule := kyvernov1 . ViolatedRule {
2021-09-02 00:02:55 +05:30
Name : valResponseRule . Name ,
2022-04-06 21:04:08 +02:00
Type : string ( valResponseRule . Type ) ,
2021-09-02 00:02:55 +05:30
Message : valResponseRule . Message ,
}
2021-10-03 01:31:05 -07:00
switch valResponseRule . Status {
2023-01-30 12:41:09 +01:00
case engineapi . RuleStatusPass :
2021-09-02 03:54:02 +05:30
rc . Pass ++
2022-05-17 13:12:43 +02:00
vrule . Status = policyreportv1alpha2 . StatusPass
2021-10-03 01:31:05 -07:00
2023-01-30 12:41:09 +01:00
case engineapi . RuleStatusFail :
2022-11-21 09:21:32 -05:00
auditWarning := false
2022-05-07 15:33:50 +02:00
ann := policy . GetAnnotations ( )
2022-09-28 13:45:16 +02:00
if scored , ok := ann [ kyvernov1 . AnnotationPolicyScored ] ; ok && scored == "false" {
2022-05-07 15:33:50 +02:00
rc . Warn ++
2022-05-17 13:12:43 +02:00
vrule . Status = policyreportv1alpha2 . StatusWarn
2022-05-07 15:33:50 +02:00
break
2022-11-21 09:21:32 -05:00
} else if auditWarn && validateResponse . GetValidationFailureAction ( ) . Audit ( ) {
rc . Warn ++
auditWarning = true
vrule . Status = policyreportv1alpha2 . StatusWarn
2022-05-07 15:33:50 +02:00
} else {
rc . Fail ++
2022-05-17 13:12:43 +02:00
vrule . Status = policyreportv1alpha2 . StatusFail
2022-05-07 15:33:50 +02:00
}
2021-09-02 01:06:29 +05:30
if ! policyReport {
if printCount < 1 {
2022-11-21 09:21:32 -05:00
if auditWarning {
fmt . Printf ( "\npolicy %s -> resource %s failed as audit warning: \n" , policy . GetName ( ) , resPath )
} else {
fmt . Printf ( "\npolicy %s -> resource %s failed: \n" , policy . GetName ( ) , resPath )
}
2021-09-02 01:06:29 +05:30
printCount ++
}
2021-10-03 01:31:05 -07:00
2021-09-02 01:06:29 +05:30
fmt . Printf ( "%d. %s: %s \n" , i + 1 , valResponseRule . Name , valResponseRule . Message )
}
2021-10-03 01:31:05 -07:00
2023-01-30 12:41:09 +01:00
case engineapi . RuleStatusError :
2021-10-03 01:31:05 -07:00
rc . Error ++
2022-05-17 13:12:43 +02:00
vrule . Status = policyreportv1alpha2 . StatusError
2021-10-03 01:31:05 -07:00
2023-01-30 12:41:09 +01:00
case engineapi . RuleStatusWarn :
2021-10-03 01:31:05 -07:00
rc . Warn ++
2022-05-17 13:12:43 +02:00
vrule . Status = policyreportv1alpha2 . StatusWarn
2021-10-03 01:31:05 -07:00
2023-01-30 12:41:09 +01:00
case engineapi . RuleStatusSkip :
2021-10-03 01:31:05 -07:00
rc . Skip ++
2022-05-17 13:12:43 +02:00
vrule . Status = policyreportv1alpha2 . StatusSkip
2021-09-02 00:02:55 +05:30
}
2021-10-03 01:31:05 -07:00
2021-09-02 00:02:55 +05:30
violatedRules = append ( violatedRules , vrule )
continue
}
}
if ! ruleFoundInEngineResponse {
2021-09-02 03:54:02 +05:30
rc . Skip ++
2022-05-17 13:12:43 +02:00
vruleSkip := kyvernov1 . ViolatedRule {
2021-09-02 00:02:55 +05:30
Name : policyRule . Name ,
Type : "Validation" ,
Message : policyRule . Validation . Message ,
2022-05-17 13:12:43 +02:00
Status : policyreportv1alpha2 . StatusSkip ,
2021-09-02 00:02:55 +05:30
}
violatedRules = append ( violatedRules , vruleSkip )
}
}
return buildPVInfo ( validateResponse , violatedRules )
}
2023-01-30 12:41:09 +01:00
func buildPVInfo ( er * engineapi . EngineResponse , violatedRules [ ] kyvernov1 . ViolatedRule ) Info {
2022-11-02 09:06:44 +00:00
info := Info {
2023-02-10 09:11:21 +01:00
PolicyName : er . Policy . GetName ( ) ,
2021-09-02 00:02:55 +05:30
Namespace : er . PatchedResource . GetNamespace ( ) ,
2022-11-02 09:06:44 +00:00
Results : [ ] EngineResponseResult {
2021-09-02 00:02:55 +05:30
{
Resource : er . GetResourceSpec ( ) ,
Rules : violatedRules ,
} ,
} ,
}
return info
}
2021-09-02 03:54:02 +05:30
2023-01-30 12:41:09 +01:00
func updateResultCounts ( policy kyvernov1 . PolicyInterface , engineResponse * engineapi . EngineResponse , resPath string , rc * ResultCounts , auditWarn bool ) {
2021-09-02 03:54:02 +05:30
printCount := 0
2022-03-28 16:01:27 +02:00
for _ , policyRule := range autogen . ComputeRules ( policy ) {
2021-09-02 03:54:02 +05:30
ruleFoundInEngineResponse := false
2022-04-27 08:09:52 -07:00
for i , ruleResponse := range engineResponse . PolicyResponse . Rules {
if policyRule . Name == ruleResponse . Name {
2021-09-02 03:54:02 +05:30
ruleFoundInEngineResponse = true
2022-05-04 09:41:59 +05:30
2023-01-30 12:41:09 +01:00
if ruleResponse . Status == engineapi . RuleStatusPass {
2021-09-02 03:54:02 +05:30
rc . Pass ++
} else {
if printCount < 1 {
2022-04-27 08:09:52 -07:00
fmt . Println ( "\ninvalid resource" , "policy" , policy . GetName ( ) , "resource" , resPath )
2021-09-02 03:54:02 +05:30
printCount ++
}
2022-04-27 08:09:52 -07:00
fmt . Printf ( "%d. %s - %s\n" , i + 1 , ruleResponse . Name , ruleResponse . Message )
2022-11-21 09:21:32 -05:00
if auditWarn && engineResponse . GetValidationFailureAction ( ) . Audit ( ) {
rc . Warn ++
} else {
rc . Fail ++
}
2021-09-02 03:54:02 +05:30
}
continue
}
}
2022-04-27 08:09:52 -07:00
2021-09-02 03:54:02 +05:30
if ! ruleFoundInEngineResponse {
rc . Skip ++
}
}
}
2021-09-02 23:11:35 +05:30
2022-05-17 13:12:43 +02:00
func SetInStoreContext ( mutatedPolicies [ ] kyvernov1 . PolicyInterface , variables map [ string ] string ) map [ string ] string {
2022-03-02 08:29:33 +01:00
storePolicies := make ( [ ] store . Policy , 0 )
2021-09-02 23:11:35 +05:30
for _ , policy := range mutatedPolicies {
storeRules := make ( [ ] store . Rule , 0 )
2022-03-28 16:01:27 +02:00
for _ , rule := range autogen . ComputeRules ( policy ) {
2022-05-07 21:30:11 +01:00
contextVal := make ( map [ string ] interface { } )
2021-09-02 23:11:35 +05:30
if len ( rule . Context ) != 0 {
for _ , contextVar := range rule . Context {
for k , v := range variables {
if strings . HasPrefix ( k , contextVar . Name ) {
contextVal [ k ] = v
delete ( variables , k )
}
}
}
storeRules = append ( storeRules , store . Rule {
Name : rule . Name ,
Values : contextVal ,
} )
}
}
2022-03-02 08:29:33 +01:00
storePolicies = append ( storePolicies , store . Policy {
2022-03-31 08:44:00 +02:00
Name : policy . GetName ( ) ,
2021-09-02 23:11:35 +05:30
Rules : storeRules ,
} )
}
2023-01-30 16:30:47 +01:00
store . SetPolicies ( storePolicies ... )
2021-09-02 23:11:35 +05:30
return variables
}
2021-09-03 12:32:12 +05:30
2023-01-30 12:41:09 +01:00
func processMutateEngineResponse ( c ApplyPolicyConfig , mutateResponse * engineapi . EngineResponse , resPath string ) error {
2021-09-03 12:41:05 +05:30
var policyHasMutate bool
2022-10-19 22:09:15 +05:30
for _ , rule := range autogen . ComputeRules ( c . Policy ) {
2021-09-03 12:41:05 +05:30
if rule . HasMutate ( ) {
policyHasMutate = true
}
}
2021-09-03 16:41:13 +05:30
if ! policyHasMutate {
return nil
}
2021-09-03 12:41:05 +05:30
2021-09-03 16:41:13 +05:30
printCount := 0
printMutatedRes := false
2022-10-19 22:09:15 +05:30
for _ , policyRule := range autogen . ComputeRules ( c . Policy ) {
2021-09-03 16:41:13 +05:30
ruleFoundInEngineResponse := false
for i , mutateResponseRule := range mutateResponse . PolicyResponse . Rules {
if policyRule . Name == mutateResponseRule . Name {
ruleFoundInEngineResponse = true
2023-01-30 12:41:09 +01:00
if mutateResponseRule . Status == engineapi . RuleStatusPass {
2022-10-19 22:09:15 +05:30
c . Rc . Pass ++
2021-09-03 16:41:13 +05:30
printMutatedRes = true
2023-01-30 12:41:09 +01:00
} else if mutateResponseRule . Status == engineapi . RuleStatusSkip {
2022-10-19 22:09:15 +05:30
fmt . Printf ( "\nskipped mutate policy %s -> resource %s" , c . Policy . GetName ( ) , resPath )
c . Rc . Skip ++
2023-01-30 12:41:09 +01:00
} else if mutateResponseRule . Status == engineapi . RuleStatusError {
2022-10-19 22:09:15 +05:30
fmt . Printf ( "\nerror while applying mutate policy %s -> resource %s\nerror: %s" , c . Policy . GetName ( ) , resPath , mutateResponseRule . Message )
c . Rc . Error ++
2021-09-03 16:41:13 +05:30
} else {
if printCount < 1 {
2022-10-19 22:09:15 +05:30
fmt . Printf ( "\nfailed to apply mutate policy %s -> resource %s" , c . Policy . GetName ( ) , resPath )
2021-09-03 16:41:13 +05:30
printCount ++
2021-09-03 12:32:12 +05:30
}
2021-09-03 16:41:13 +05:30
fmt . Printf ( "%d. %s - %s \n" , i + 1 , mutateResponseRule . Name , mutateResponseRule . Message )
2022-10-19 22:09:15 +05:30
c . Rc . Fail ++
2021-09-03 12:32:12 +05:30
}
2021-09-03 16:41:13 +05:30
continue
2021-09-03 12:32:12 +05:30
}
}
2021-09-03 16:41:13 +05:30
if ! ruleFoundInEngineResponse {
2022-10-19 22:09:15 +05:30
c . Rc . Skip ++
2021-09-03 16:41:13 +05:30
}
}
2021-09-03 12:32:12 +05:30
2022-10-19 22:09:15 +05:30
if printMutatedRes && c . PrintPatchResource {
2021-09-03 16:41:13 +05:30
yamlEncodedResource , err := yamlv2 . Marshal ( mutateResponse . PatchedResource . Object )
if err != nil {
return sanitizederror . NewWithError ( "failed to marshal" , err )
}
2021-09-03 12:32:12 +05:30
2022-10-19 22:09:15 +05:30
if c . MutateLogPath == "" {
2021-09-03 16:41:13 +05:30
mutatedResource := string ( yamlEncodedResource ) + string ( "\n---" )
if len ( strings . TrimSpace ( mutatedResource ) ) > 0 {
2022-10-19 22:09:15 +05:30
if ! c . Stdin {
fmt . Printf ( "\nmutate policy %s applied to %s:" , c . Policy . GetName ( ) , resPath )
2021-09-03 12:32:12 +05:30
}
2021-10-01 17:52:23 +05:30
fmt . Printf ( "\n" + mutatedResource + "\n" )
2021-09-03 12:32:12 +05:30
}
2021-09-03 16:41:13 +05:30
} else {
2022-10-19 22:09:15 +05:30
err := PrintMutatedOutput ( c . MutateLogPath , c . MutateLogPathIsDir , string ( yamlEncodedResource ) , c . Resource . GetName ( ) + "-mutated" )
2021-09-03 16:41:13 +05:30
if err != nil {
return sanitizederror . NewWithError ( "failed to print mutated result" , err )
}
fmt . Printf ( "\n\nMutation:\nMutation has been applied successfully. Check the files." )
}
}
return nil
}
2022-05-17 13:12:43 +02:00
func PrintMutatedPolicy ( mutatedPolicies [ ] kyvernov1 . PolicyInterface ) error {
2021-09-03 16:41:13 +05:30
for _ , policy := range mutatedPolicies {
p , err := json . Marshal ( policy )
if err != nil {
return sanitizederror . NewWithError ( "failed to marsal mutated policy" , err )
2021-09-03 12:32:12 +05:30
}
2021-09-03 16:41:13 +05:30
log . Log . V ( 5 ) . Info ( "mutated Policy:" , string ( p ) )
2021-09-03 12:32:12 +05:30
}
return nil
}
2021-09-03 16:41:13 +05:30
2022-05-07 21:30:11 +01:00
func CheckVariableForPolicy ( valuesMap map [ string ] map [ string ] Resource , globalValMap map [ string ] string , policyName string , resourceName string , resourceKind string , variables map [ string ] string , kindOnwhichPolicyIsApplied map [ string ] struct { } , variable string ) ( map [ string ] interface { } , error ) {
2021-09-03 16:41:13 +05:30
// get values from file for this policy resource combination
2022-05-07 21:30:11 +01:00
thisPolicyResourceValues := make ( map [ string ] interface { } )
2023-03-24 11:01:49 +01:00
if len ( valuesMap [ policyName ] ) != 0 && ! datautils . DeepEqual ( valuesMap [ policyName ] [ resourceName ] , Resource { } ) {
2021-09-03 16:41:13 +05:30
thisPolicyResourceValues = valuesMap [ policyName ] [ resourceName ] . Values
}
2021-09-20 22:16:57 +05:30
2021-09-03 16:41:13 +05:30
for k , v := range variables {
thisPolicyResourceValues [ k ] = v
}
2021-09-20 22:16:57 +05:30
if thisPolicyResourceValues == nil && len ( globalValMap ) > 0 {
2022-05-07 21:30:11 +01:00
thisPolicyResourceValues = make ( map [ string ] interface { } )
2021-09-20 22:16:57 +05:30
}
for k , v := range globalValMap {
if _ , ok := thisPolicyResourceValues [ k ] ; ! ok {
thisPolicyResourceValues [ k ] = v
}
}
2021-09-03 16:41:13 +05:30
// skipping the variable check for non matching kind
if _ , ok := kindOnwhichPolicyIsApplied [ resourceKind ] ; ok {
2023-01-30 16:30:47 +01:00
if len ( variable ) > 0 && len ( thisPolicyResourceValues ) == 0 && store . HasPolicies ( ) {
2021-09-03 16:41:13 +05:30
return thisPolicyResourceValues , sanitizederror . NewWithError ( fmt . Sprintf ( "policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag" , policyName , resourceName ) , nil )
}
}
return thisPolicyResourceValues , nil
}
2022-12-09 22:15:23 +05:30
func GetKindsFromPolicy ( policy kyvernov1 . PolicyInterface , subresources [ ] Subresource , dClient dclient . Interface ) map [ string ] struct { } {
2022-05-17 08:19:03 +02:00
kindOnwhichPolicyIsApplied := make ( map [ string ] struct { } )
2022-03-28 16:01:27 +02:00
for _ , rule := range autogen . ComputeRules ( policy ) {
2021-09-03 16:41:13 +05:30
for _ , kind := range rule . MatchResources . ResourceDescription . Kinds {
2022-12-09 22:15:23 +05:30
k , err := getKind ( kind , subresources , dClient )
if err != nil {
fmt . Printf ( "Error: %s" , err . Error ( ) )
continue
}
kindOnwhichPolicyIsApplied [ k ] = struct { } { }
2021-09-03 16:41:13 +05:30
}
for _ , kind := range rule . ExcludeResources . ResourceDescription . Kinds {
2022-12-09 22:15:23 +05:30
k , err := getKind ( kind , subresources , dClient )
if err != nil {
fmt . Printf ( "Error: %s" , err . Error ( ) )
continue
}
kindOnwhichPolicyIsApplied [ k ] = struct { } { }
2021-09-03 16:41:13 +05:30
}
}
return kindOnwhichPolicyIsApplied
}
2021-10-01 14:16:33 +05:30
2022-12-09 22:15:23 +05:30
func getKind ( kind string , subresources [ ] Subresource , dClient dclient . Interface ) ( string , error ) {
2023-03-22 17:04:32 +01:00
group , version , kind , subresource := kubeutils . ParseKindSelector ( kind )
if subresource == "" {
return kind , nil
}
if dClient == nil {
gv := schema . GroupVersion { Group : group , Version : version }
return getSubresourceKind ( gv . String ( ) , kind , subresource , subresources )
}
gvrss , err := dClient . Discovery ( ) . FindResources ( group , version , kind , subresource )
if err != nil {
return kind , err
}
if len ( gvrss ) != 1 {
return kind , fmt . Errorf ( "no unique match for kind %s" , kind )
}
for _ , api := range gvrss {
return api . Kind , nil
2022-12-09 22:15:23 +05:30
}
2023-03-22 17:04:32 +01:00
return kind , nil
2022-12-09 22:15:23 +05:30
}
func getSubresourceKind ( groupVersion , parentKind , subresourceName string , subresources [ ] Subresource ) ( string , error ) {
for _ , subresource := range subresources {
parentResourceGroupVersion := metav1 . GroupVersion {
Group : subresource . ParentResource . Group ,
Version : subresource . ParentResource . Version ,
} . String ( )
if groupVersion == "" || kubeutils . GroupVersionMatches ( groupVersion , parentResourceGroupVersion ) {
if parentKind == subresource . ParentResource . Kind {
if strings . ToLower ( subresourceName ) == strings . Split ( subresource . APIResource . Name , "/" ) [ 1 ] {
return subresource . APIResource . Kind , nil
}
}
}
}
return "" , sanitizederror . NewWithError ( fmt . Sprintf ( "subresource %s not found for parent resource %s" , subresourceName , parentKind ) , nil )
}
2022-08-24 15:08:24 +02:00
// GetResourceFromPath - get patchedResource and generatedResource from given path
2022-05-25 19:56:22 +05:30
func GetResourceFromPath ( fs billy . Filesystem , path string , isGit bool , policyResourcePath string , resourceType string ) ( unstructured . Unstructured , error ) {
var resourceBytes [ ] byte
var resource unstructured . Unstructured
2021-10-01 14:16:33 +05:30
var err error
if isGit {
if len ( path ) > 0 {
2022-05-09 18:55:35 +02:00
filep , fileErr := fs . Open ( filepath . Join ( policyResourcePath , path ) )
if fileErr != nil {
2022-05-25 19:56:22 +05:30
fmt . Printf ( "Unable to open %s file: %s. \nerror: %s" , resourceType , path , err )
2021-10-01 14:16:33 +05:30
}
2022-09-30 15:25:19 +08:00
resourceBytes , err = io . ReadAll ( filep )
2021-10-01 14:16:33 +05:30
}
} else {
2022-05-25 19:56:22 +05:30
resourceBytes , err = getFileBytes ( path )
}
if err != nil {
fmt . Printf ( "\n----------------------------------------------------------------------\nfailed to load %s: %s. \nerror: %s\n----------------------------------------------------------------------\n" , resourceType , path , err )
return resource , err
2021-10-01 14:16:33 +05:30
}
2021-10-01 17:52:23 +05:30
2022-05-25 19:56:22 +05:30
resource , err = GetPatchedAndGeneratedResource ( resourceBytes )
2021-10-01 14:16:33 +05:30
if err != nil {
2022-05-25 19:56:22 +05:30
return resource , err
2021-10-01 14:16:33 +05:30
}
2021-10-01 17:52:23 +05:30
2022-05-25 19:56:22 +05:30
return resource , nil
}
// initializeMockController initializes a basic Generate Controller with a fake dynamic client.
func initializeMockController ( objects [ ] runtime . Object ) ( * generate . GenerateController , error ) {
2022-08-02 07:54:02 -07:00
client , err := dclient . NewFakeClient ( runtime . NewScheme ( ) , nil , objects ... )
2021-10-01 14:16:33 +05:30
if err != nil {
2022-05-25 19:56:22 +05:30
fmt . Printf ( "Failed to mock dynamic client" )
return nil , err
}
client . SetDiscovery ( dclient . NewFakeDiscoveryClient ( nil ) )
2023-02-03 06:01:11 +01:00
c := generate . NewGenerateControllerWithOnlyClient ( client , engine . NewEngine (
config . NewDefaultConfiguration ( ) ,
2023-02-07 16:09:15 +01:00
client ,
2023-02-08 06:55:03 +01:00
nil ,
2023-02-08 14:19:56 +01:00
store . ContextLoaderFactory ( nil ) ,
2023-02-06 06:49:47 +01:00
nil ,
2023-02-03 06:01:11 +01:00
) )
2022-05-25 19:56:22 +05:30
return c , nil
}
// handleGeneratePolicy returns a new RuleResponse with the Kyverno generated resource configuration by applying the generate rule.
2023-01-30 12:41:09 +01:00
func handleGeneratePolicy ( generateResponse * engineapi . EngineResponse , policyContext engine . PolicyContext , ruleToCloneSourceResource map [ string ] string ) ( [ ] engineapi . RuleResponse , error ) {
2022-12-02 09:14:23 +01:00
resource := policyContext . NewResource ( )
objects := [ ] runtime . Object { & resource }
2022-08-24 15:08:24 +02:00
resources := [ ] * unstructured . Unstructured { }
2022-05-25 19:56:22 +05:30
for _ , rule := range generateResponse . PolicyResponse . Rules {
if path , ok := ruleToCloneSourceResource [ rule . Name ] ; ok {
resourceBytes , err := getFileBytes ( path )
if err != nil {
fmt . Printf ( "failed to get resource bytes\n" )
} else {
resources , err = GetResource ( resourceBytes )
if err != nil {
fmt . Printf ( "failed to convert resource bytes to unstructured format\n" )
}
}
}
}
for _ , res := range resources {
objects = append ( objects , res )
}
c , err := initializeMockController ( objects )
if err != nil {
fmt . Println ( "error at controller" )
return nil , err
}
gr := kyvernov1beta1 . UpdateRequest {
Spec : kyvernov1beta1 . UpdateRequestSpec {
Type : kyvernov1beta1 . Generate ,
2023-02-10 09:11:21 +01:00
Policy : generateResponse . Policy . GetName ( ) ,
2022-05-25 19:56:22 +05:30
Resource : kyvernov1 . ResourceSpec {
2023-02-10 15:04:41 +01:00
Kind : generateResponse . Resource . GetKind ( ) ,
Namespace : generateResponse . Resource . GetNamespace ( ) ,
Name : generateResponse . Resource . GetName ( ) ,
APIVersion : generateResponse . Resource . GetAPIVersion ( ) ,
2022-05-25 19:56:22 +05:30
} ,
} ,
}
2023-01-30 12:41:09 +01:00
var newRuleResponse [ ] engineapi . RuleResponse
2022-05-25 19:56:22 +05:30
for _ , rule := range generateResponse . PolicyResponse . Rules {
2023-03-01 23:49:05 +08:00
genResource , err := c . ApplyGeneratePolicy ( log . Log , & policyContext , gr , [ ] string { rule . Name } )
2022-05-25 19:56:22 +05:30
if err != nil {
2023-01-30 12:41:09 +01:00
rule . Status = engineapi . RuleStatusError
2022-05-25 19:56:22 +05:30
return nil , err
}
unstrGenResource , err := c . GetUnstrResource ( genResource [ 0 ] )
if err != nil {
2023-01-30 12:41:09 +01:00
rule . Status = engineapi . RuleStatusError
2022-05-25 19:56:22 +05:30
return nil , err
}
rule . GeneratedResource = * unstrGenResource
newRuleResponse = append ( newRuleResponse , rule )
2021-10-01 14:16:33 +05:30
}
2021-10-01 17:52:23 +05:30
2022-05-25 19:56:22 +05:30
return newRuleResponse , nil
2021-10-01 14:16:33 +05:30
}
2022-04-12 09:30:49 +05:30
2022-05-17 08:19:03 +02:00
// GetUserInfoFromPath - get the request info as user info from a given path
2023-02-08 14:19:56 +01:00
func GetUserInfoFromPath ( fs billy . Filesystem , path string , isGit bool , policyResourcePath string ) ( kyvernov1beta1 . RequestInfo , error ) {
2022-05-17 13:12:43 +02:00
userInfo := & kyvernov1beta1 . RequestInfo { }
2022-04-12 09:30:49 +05:30
if isGit {
filep , err := fs . Open ( filepath . Join ( policyResourcePath , path ) )
if err != nil {
fmt . Printf ( "Unable to open userInfo file: %s. \nerror: %s" , path , err )
}
2022-09-30 15:25:19 +08:00
bytes , err := io . ReadAll ( filep )
2022-04-12 09:30:49 +05:30
if err != nil {
fmt . Printf ( "Error: failed to read file %s: %v" , filep . Name ( ) , err . Error ( ) )
}
userInfoBytes , err := yaml . ToJSON ( bytes )
if err != nil {
fmt . Printf ( "failed to convert to JSON: %v" , err )
}
if err := json . Unmarshal ( userInfoBytes , userInfo ) ; err != nil {
fmt . Printf ( "failed to decode yaml: %v" , err )
}
} else {
var errors [ ] error
2022-09-30 15:25:19 +08:00
pathname := filepath . Clean ( filepath . Join ( policyResourcePath , path ) )
bytes , err := os . ReadFile ( pathname )
2022-04-12 09:30:49 +05:30
if err != nil {
errors = append ( errors , sanitizederror . NewWithError ( "unable to read yaml" , err ) )
}
userInfoBytes , err := yaml . ToJSON ( bytes )
if err != nil {
errors = append ( errors , sanitizederror . NewWithError ( "failed to convert json" , err ) )
}
if err := json . Unmarshal ( userInfoBytes , userInfo ) ; err != nil {
errors = append ( errors , sanitizederror . NewWithError ( "failed to decode yaml" , err ) )
}
if len ( errors ) > 0 && log . Log . V ( 1 ) . Enabled ( ) {
fmt . Printf ( "ignoring errors: \n" )
for _ , e := range errors {
fmt . Printf ( " %v \n" , e . Error ( ) )
}
}
}
2023-02-08 14:19:56 +01:00
return * userInfo , nil
2022-04-12 09:30:49 +05:30
}
2022-12-02 20:03:04 +05:30
func IsGitSourcePath ( policyPaths [ ] string ) bool {
return strings . Contains ( policyPaths [ 0 ] , "https://" )
}
func GetGitBranchOrPolicyPaths ( gitBranch , repoURL string , policyPaths [ ] string ) ( string , string ) {
var gitPathToYamls string
if gitBranch == "" {
gitPathToYamls = "/"
if string ( policyPaths [ 0 ] [ len ( policyPaths [ 0 ] ) - 1 ] ) == "/" {
gitBranch = strings . ReplaceAll ( policyPaths [ 0 ] , repoURL + "/" , "" )
} else {
gitBranch = strings . ReplaceAll ( policyPaths [ 0 ] , repoURL , "" )
}
if gitBranch == "" {
gitBranch = "main"
} else if string ( gitBranch [ 0 ] ) == "/" {
gitBranch = gitBranch [ 1 : ]
}
return gitBranch , gitPathToYamls
}
if string ( policyPaths [ 0 ] [ len ( policyPaths [ 0 ] ) - 1 ] ) == "/" {
gitPathToYamls = strings . ReplaceAll ( policyPaths [ 0 ] , repoURL + "/" , "/" )
} else {
gitPathToYamls = strings . ReplaceAll ( policyPaths [ 0 ] , repoURL , "/" )
}
return gitBranch , gitPathToYamls
}