2020-03-06 03:00:18 +05:30
package apply
import (
2022-09-30 10:12:21 +02:00
"context"
2020-03-06 03:00:18 +05:30
"fmt"
2022-12-02 20:03:04 +05:30
"net/url"
2020-06-08 17:01:56 +05:30
"os"
2020-07-21 00:41:30 +05:30
"path/filepath"
2022-12-02 20:03:04 +05:30
"sort"
2020-07-21 00:41:30 +05:30
"strings"
2020-04-03 10:30:52 +05:30
"time"
2021-02-07 20:26:56 -08:00
"github.com/go-git/go-billy/v5/memfs"
2022-12-02 20:03:04 +05:30
kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1"
2022-04-25 20:20:40 +08:00
"github.com/kyverno/kyverno/api/kyverno/v1beta1"
2022-04-14 17:50:18 +05:30
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/common"
sanitizederror "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/sanitizedError"
"github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/utils/store"
2023-04-12 14:51:03 +02:00
"github.com/kyverno/kyverno/pkg/autogen"
2022-08-31 14:03:47 +08:00
"github.com/kyverno/kyverno/pkg/clients/dclient"
2022-09-20 19:05:18 +05:30
"github.com/kyverno/kyverno/pkg/config"
2023-04-12 14:51:03 +02:00
engineapi "github.com/kyverno/kyverno/pkg/engine/api"
2020-10-15 17:29:07 -07:00
"github.com/kyverno/kyverno/pkg/openapi"
2020-10-07 11:12:31 -07:00
policy2 "github.com/kyverno/kyverno/pkg/policy"
2022-12-03 19:56:09 +01:00
gitutils "github.com/kyverno/kyverno/pkg/utils/git"
2020-03-06 03:00:18 +05:30
"github.com/spf13/cobra"
2020-10-15 17:29:07 -07:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2022-11-22 14:37:27 +01:00
"k8s.io/client-go/dynamic"
2022-07-25 07:49:51 +02:00
"k8s.io/client-go/kubernetes"
2022-12-09 22:15:23 +05:30
"sigs.k8s.io/controller-runtime/pkg/log"
2020-11-10 10:49:29 +05:30
yaml1 "sigs.k8s.io/yaml"
2020-03-06 03:00:18 +05:30
)
2020-10-30 16:38:19 +05:30
type Resource struct {
Name string ` json:"name" `
Values map [ string ] string ` json:"values" `
}
type Policy struct {
Name string ` json:"name" `
Resources [ ] Resource ` json:"resources" `
}
type Values struct {
Policies [ ] Policy ` json:"policies" `
}
2021-10-14 22:44:11 +05:30
type SkippedInvalidPolicies struct {
skipped [ ] string
invalid [ ] string
}
2022-09-20 19:05:18 +05:30
type ApplyCommandConfig struct {
KubeConfig string
Context string
Namespace string
MutateLogPath string
VariablesString string
ValuesFile string
UserInfoPath string
Cluster bool
PolicyReport bool
Stdin bool
RegistryAccess bool
2022-11-21 09:21:32 -05:00
AuditWarn bool
2022-09-20 19:05:18 +05:30
ResourcePaths [ ] string
PolicyPaths [ ] string
2022-12-02 20:03:04 +05:30
GitBranch string
2022-12-05 13:15:22 -05:00
warnExitCode int
2023-03-27 11:18:44 +02:00
warnNoPassed bool
2022-09-20 19:05:18 +05:30
}
2022-12-16 14:50:15 +01:00
var (
applyHelp = `
2022-08-18 12:22:30 +05:30
2020-11-20 12:27:02 +05:30
To apply on a resource :
2022-08-18 12:22:30 +05:30
kyverno apply / path / to / policy . yaml / path / to / folderOfPolicies -- resource = / path / to / resource1 -- resource = / path / to / resource2
To apply on a folder of resources :
kyverno apply / path / to / policy . yaml / path / to / folderOfPolicies -- resource = / path / to / resources /
2020-11-20 12:27:02 +05:30
To apply on a cluster :
2022-08-18 12:22:30 +05:30
kyverno apply / path / to / policy . yaml / path / to / folderOfPolicies -- cluster
2020-11-20 12:27:02 +05:30
2022-12-02 20:03:04 +05:30
To apply policies from a gitSourceURL on a cluster :
Example : Taking github . com as a gitSourceURL here . Some other standards gitSourceURL are : gitlab . com , bitbucket . org , etc .
kyverno apply https : //github.com/kyverno/policies/openshift/ --git-branch main --cluster
2020-11-20 12:27:02 +05:30
To apply policy with variables :
1. To apply single policy with variable on single resource use flag "set" .
Example :
kyverno apply / path / to / policy . yaml -- resource / path / to / resource . yaml -- set < variable1 >= < value1 > , < variable2 >= < value2 >
2. To apply multiple policy with variable on multiple resource use flag "values_file" .
Example :
kyverno apply / path / to / policy1 . yaml / path / to / policy2 . yaml -- resource / path / to / resource1 . yaml -- resource / path / to / resource2 . yaml - f / path / to / value . yaml
Format of value . yaml :
policies :
- name : < policy1 name >
2021-08-25 09:17:47 +05:30
rules :
- name : < rule1 name >
values :
< context variable1 in policy1 rule1 > : < value >
< context variable2 in policy1 rule1 > : < value >
- name : < rule2 name >
values :
< context variable1 in policy1 rule2 > : < value >
< context variable2 in policy1 rule2 > : < value >
2020-11-20 12:27:02 +05:30
resources :
- name : < resource1 name >
values :
2021-08-25 09:17:47 +05:30
< variable1 in policy1 > : < value >
< variable2 in policy1 > : < value >
2020-11-20 12:27:02 +05:30
- name : < resource2 name >
values :
2021-08-25 09:17:47 +05:30
< variable1 in policy1 > : < value >
< variable2 in policy1 > : < value >
2020-11-20 12:27:02 +05:30
- name : < policy2 name >
resources :
- name : < resource1 name >
values :
2021-08-25 09:17:47 +05:30
< variable1 in policy2 > : < value >
< variable2 in policy2 > : < value >
2020-11-20 12:27:02 +05:30
- name : < resource2 name >
values :
2021-08-25 09:17:47 +05:30
< variable1 in policy2 > : < value >
< variable2 in policy2 > : < value >
2021-03-10 02:15:45 +05:30
namespaceSelector :
- name : < namespace1 name >
labels :
< label key > : < label value >
- name : < namespace2 name >
labels :
< label key > : < label value >
2022-12-09 22:15:23 +05:30
# If policy is matching on Kind / Subresource , then this is required
subresources :
- subresource :
name : < name of subresource >
kind : < kind of subresource >
2023-01-05 14:13:54 +05:30
group : < group of subresource >
2022-12-09 22:15:23 +05:30
version : < version of subresource >
parentResource :
name : < name of parent resource >
kind : < kind of parent resource >
2023-01-05 14:13:54 +05:30
group : < group of parent resource >
2022-12-09 22:15:23 +05:30
version : < version of parent resource >
2020-11-20 12:27:02 +05:30
2020-12-07 11:26:04 -08:00
More info : https : //kyverno.io/docs/kyverno-cli/
`
2022-12-16 14:50:15 +01:00
// allow os.exit to be overwritten during unit tests
osExit = os . Exit
)
2020-12-07 11:26:04 -08:00
func Command ( ) * cobra . Command {
var cmd * cobra . Command
2022-09-20 19:05:18 +05:30
applyCommandConfig := & ApplyCommandConfig { }
2020-12-07 11:26:04 -08:00
cmd = & cobra . Command {
Use : "apply" ,
2022-12-27 22:46:01 +08:00
Short : "Applies policies on resources." ,
2020-12-07 11:26:04 -08:00
Example : applyHelp ,
2020-03-06 03:00:18 +05:30
RunE : func ( cmd * cobra . Command , policyPaths [ ] string ) ( err error ) {
defer func ( ) {
if err != nil {
2020-12-07 11:26:04 -08:00
if ! sanitizederror . IsErrorSanitized ( err ) {
2020-03-20 11:43:21 -07:00
log . Log . Error ( err , "failed to sanitize" )
2020-08-05 23:53:27 +05:30
err = fmt . Errorf ( "internal error" )
2020-03-06 03:00:18 +05:30
}
}
} ( )
2022-09-20 19:05:18 +05:30
applyCommandConfig . PolicyPaths = policyPaths
rc , resources , skipInvalidPolicies , pvInfos , err := applyCommandConfig . applyCommandHelper ( )
2020-10-30 16:38:19 +05:30
if err != nil {
return err
2020-08-11 22:59:50 +05:30
}
2023-04-12 14:51:03 +02:00
PrintReportOrViolation ( applyCommandConfig . PolicyReport , rc , applyCommandConfig . ResourcePaths , len ( resources ) , skipInvalidPolicies , applyCommandConfig . Stdin , pvInfos , applyCommandConfig . warnExitCode , applyCommandConfig . warnNoPassed , applyCommandConfig . AuditWarn )
2020-12-20 01:21:31 +05:30
return nil
} ,
}
2022-09-20 19:05:18 +05:30
cmd . Flags ( ) . StringArrayVarP ( & applyCommandConfig . ResourcePaths , "resource" , "r" , [ ] string { } , "Path to resource files" )
cmd . Flags ( ) . BoolVarP ( & applyCommandConfig . Cluster , "cluster" , "c" , false , "Checks if policies should be applied to cluster in the current context" )
cmd . Flags ( ) . StringVarP ( & applyCommandConfig . MutateLogPath , "output" , "o" , "" , "Prints the mutated resources in provided file/directory" )
2021-08-02 16:38:43 +05:30
// currently `set` flag supports variable for single policy applied on single resource
2022-09-20 19:05:18 +05:30
cmd . Flags ( ) . StringVarP ( & applyCommandConfig . UserInfoPath , "userinfo" , "u" , "" , "Admission Info including Roles, Cluster Roles and Subjects" )
cmd . Flags ( ) . StringVarP ( & applyCommandConfig . VariablesString , "set" , "s" , "" , "Variables that are required" )
cmd . Flags ( ) . StringVarP ( & applyCommandConfig . ValuesFile , "values-file" , "f" , "" , "File containing values for policy variables" )
cmd . Flags ( ) . BoolVarP ( & applyCommandConfig . PolicyReport , "policy-report" , "p" , false , "Generates policy report when passed (default policyviolation)" )
cmd . Flags ( ) . StringVarP ( & applyCommandConfig . Namespace , "namespace" , "n" , "" , "Optional Policy parameter passed with cluster flag" )
cmd . Flags ( ) . BoolVarP ( & applyCommandConfig . Stdin , "stdin" , "i" , false , "Optional mutate policy parameter to pipe directly through to kubectl" )
cmd . Flags ( ) . BoolVarP ( & applyCommandConfig . RegistryAccess , "registry" , "" , false , "If set to true, access the image registry using local docker credentials to populate external data" )
cmd . Flags ( ) . StringVarP ( & applyCommandConfig . KubeConfig , "kubeconfig" , "" , "" , "path to kubeconfig file with authorization and master location information" )
cmd . Flags ( ) . StringVarP ( & applyCommandConfig . Context , "context" , "" , "" , "The name of the kubeconfig context to use" )
2022-12-02 20:03:04 +05:30
cmd . Flags ( ) . StringVarP ( & applyCommandConfig . GitBranch , "git-branch" , "b" , "" , "test git repository branch" )
2022-11-21 09:21:32 -05:00
cmd . Flags ( ) . BoolVarP ( & applyCommandConfig . AuditWarn , "audit-warn" , "" , false , "If set to true, will flag audit policies as warnings instead of failures" )
2022-12-05 13:15:22 -05:00
cmd . Flags ( ) . IntVar ( & applyCommandConfig . warnExitCode , "warn-exit-code" , 0 , "Set the exit code for warnings; if failures or errors are found, will exit 1" )
2023-03-27 11:18:44 +02:00
cmd . Flags ( ) . BoolVarP ( & applyCommandConfig . warnNoPassed , "warn-no-pass" , "" , false , "Specify if warning exit code should be raised if no objects satisfied a policy; can be used together with --warn-exit-code flag" )
2020-12-20 01:21:31 +05:30
return cmd
}
2020-08-05 23:53:27 +05:30
2023-04-12 14:51:03 +02:00
func ( c * ApplyCommandConfig ) applyCommandHelper ( ) ( rc * common . ResultCounts , resources [ ] * unstructured . Unstructured , skipInvalidPolicies SkippedInvalidPolicies , responses [ ] engineapi . EngineResponse , err error ) {
2021-04-29 22:39:44 +05:30
store . SetMock ( true )
2022-09-20 19:05:18 +05:30
store . SetRegistryAccess ( c . RegistryAccess )
2022-10-19 22:09:15 +05:30
if c . Cluster {
store . AllowApiCall ( true )
}
2021-02-02 18:43:19 +05:30
fs := memfs . New ( )
2020-10-16 19:56:32 +05:30
2022-09-20 19:05:18 +05:30
if c . ValuesFile != "" && c . VariablesString != "" {
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , sanitizederror . NewWithError ( "pass the values either using set flag or values_file flag" , err )
2020-12-20 01:21:31 +05:30
}
2020-12-07 11:26:04 -08:00
2022-12-09 22:15:23 +05:30
variables , globalValMap , valuesMap , namespaceSelectorMap , subresources , err := common . GetVariable ( c . VariablesString , c . ValuesFile , fs , false , "" )
2020-12-20 01:21:31 +05:30
if err != nil {
if ! sanitizederror . IsErrorSanitized ( err ) {
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , sanitizederror . NewWithError ( "failed to decode yaml" , err )
2020-12-20 01:21:31 +05:30
}
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , err
2020-12-20 01:21:31 +05:30
}
2020-10-16 19:56:32 +05:30
2023-02-09 16:15:51 +01:00
openApiManager , err := openapi . NewManager ( log . Log )
2020-12-20 01:21:31 +05:30
if err != nil {
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , sanitizederror . NewWithError ( "failed to initialize openAPIController" , err )
2020-12-20 01:21:31 +05:30
}
2020-07-21 00:41:30 +05:30
2022-05-17 16:40:51 +02:00
var dClient dclient . Interface
2022-09-20 19:05:18 +05:30
if c . Cluster {
restConfig , err := config . CreateClientConfigWithContext ( c . KubeConfig , c . Context )
2020-12-20 01:21:31 +05:30
if err != nil {
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , err
2020-12-20 01:21:31 +05:30
}
2022-07-25 07:49:51 +02:00
kubeClient , err := kubernetes . NewForConfig ( restConfig )
if err != nil {
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , err
2022-07-25 07:49:51 +02:00
}
2022-11-22 14:37:27 +01:00
dynamicClient , err := dynamic . NewForConfig ( restConfig )
if err != nil {
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , err
2022-11-22 14:37:27 +01:00
}
dClient , err = dclient . NewClient ( context . Background ( ) , dynamicClient , kubeClient , 15 * time . Minute )
2020-12-20 01:21:31 +05:30
if err != nil {
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , err
2020-12-20 01:21:31 +05:30
}
}
2020-03-06 03:00:18 +05:30
2022-09-20 19:05:18 +05:30
if len ( c . PolicyPaths ) == 0 {
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , sanitizederror . NewWithError ( "require policy" , err )
2020-12-20 01:21:31 +05:30
}
2020-11-11 17:10:38 +05:30
2022-09-20 19:05:18 +05:30
if ( len ( c . PolicyPaths ) > 0 && c . PolicyPaths [ 0 ] == "-" ) && len ( c . ResourcePaths ) > 0 && c . ResourcePaths [ 0 ] == "-" {
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , sanitizederror . NewWithError ( "a stdin pipe can be used for either policies or resources, not both" , err )
2020-12-20 01:21:31 +05:30
}
2020-11-11 11:57:23 +05:30
2022-12-02 20:03:04 +05:30
var policies [ ] kyvernov1 . PolicyInterface
2022-12-16 14:50:15 +01:00
isGit := common . IsGitSourcePath ( c . PolicyPaths )
2022-12-02 20:03:04 +05:30
if isGit {
2022-12-16 14:50:15 +01:00
gitSourceURL , err := url . Parse ( c . PolicyPaths [ 0 ] )
if err != nil {
fmt . Printf ( "Error: failed to load policies\nCause: %s\n" , err )
osExit ( 1 )
}
pathElems := strings . Split ( gitSourceURL . Path [ 1 : ] , "/" )
if len ( pathElems ) <= 1 {
err := fmt . Errorf ( "invalid URL path %s - expected https://<any_git_source_domain>/:owner/:repository/:branch (without --git-branch flag) OR https://<any_git_source_domain>/:owner/:repository/:directory (with --git-branch flag)" , gitSourceURL . Path )
fmt . Printf ( "Error: failed to parse URL \nCause: %s\n" , err )
osExit ( 1 )
}
gitSourceURL . Path = strings . Join ( [ ] string { pathElems [ 0 ] , pathElems [ 1 ] } , "/" )
repoURL := gitSourceURL . String ( )
var gitPathToYamls string
2022-12-02 20:03:04 +05:30
c . GitBranch , gitPathToYamls = common . GetGitBranchOrPolicyPaths ( c . GitBranch , repoURL , c . PolicyPaths )
2022-12-03 19:56:09 +01:00
_ , cloneErr := gitutils . Clone ( repoURL , fs , c . GitBranch )
2022-12-02 20:03:04 +05:30
if cloneErr != nil {
fmt . Printf ( "Error: failed to clone repository \nCause: %s\n" , cloneErr )
log . Log . V ( 3 ) . Info ( fmt . Sprintf ( "failed to clone repository %v as it is not valid" , repoURL ) , "error" , cloneErr )
2022-12-16 14:50:15 +01:00
osExit ( 1 )
2022-12-02 20:03:04 +05:30
}
2022-12-03 19:56:09 +01:00
policyYamls , err := gitutils . ListYamls ( fs , gitPathToYamls )
2022-12-02 20:03:04 +05:30
if err != nil {
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , sanitizederror . NewWithError ( "failed to list YAMLs in repository" , err )
2022-12-02 20:03:04 +05:30
}
sort . Strings ( policyYamls )
2022-12-16 14:50:15 +01:00
c . PolicyPaths = policyYamls
2022-12-02 20:03:04 +05:30
}
policies , err = common . GetPoliciesFromPaths ( fs , c . PolicyPaths , isGit , "" )
2020-12-20 01:21:31 +05:30
if err != nil {
fmt . Printf ( "Error: failed to load policies\nCause: %s\n" , err )
2022-12-16 14:50:15 +01:00
osExit ( 1 )
2020-12-20 01:21:31 +05:30
}
2020-07-10 14:56:07 +05:30
2022-09-20 19:05:18 +05:30
if len ( c . ResourcePaths ) == 0 && ! c . Cluster {
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , sanitizederror . NewWithError ( "resource file(s) or cluster required" , err )
2020-12-20 01:21:31 +05:30
}
2020-08-18 21:03:00 -07:00
2022-09-20 19:05:18 +05:30
mutateLogPathIsDir , err := checkMutateLogPath ( c . MutateLogPath )
2020-12-20 01:21:31 +05:30
if err != nil {
if ! sanitizederror . IsErrorSanitized ( err ) {
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , sanitizederror . NewWithError ( "failed to create file/folder" , err )
2020-12-20 01:21:31 +05:30
}
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , err
2020-12-20 01:21:31 +05:30
}
2020-08-18 21:03:00 -07:00
2021-03-12 06:00:37 +05:30
// empty the previous contents of the file just in case if the file already existed before with some content(so as to perform overwrites)
// the truncation of files for the case when mutateLogPath is dir, is handled under pkg/kyverno/apply/common.go
2022-09-20 19:05:18 +05:30
if ! mutateLogPathIsDir && c . MutateLogPath != "" {
c . MutateLogPath = filepath . Clean ( c . MutateLogPath )
2021-10-14 00:05:13 +02:00
// Necessary for us to include the file via variable as it is part of the CLI.
2022-09-20 19:05:18 +05:30
_ , err := os . OpenFile ( c . MutateLogPath , os . O_TRUNC | os . O_WRONLY , 0 o600 ) // #nosec G304
2021-03-12 06:00:37 +05:30
if err != nil {
if ! sanitizederror . IsErrorSanitized ( err ) {
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , sanitizederror . NewWithError ( "failed to truncate the existing file at " + c . MutateLogPath , err )
2021-03-12 06:00:37 +05:30
}
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , err
2021-03-12 06:00:37 +05:30
}
}
2022-10-26 01:43:46 +02:00
err = common . PrintMutatedPolicy ( policies )
2021-09-03 16:41:13 +05:30
if err != nil {
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , sanitizederror . NewWithError ( "failed to marshal mutated policy" , err )
2021-09-01 00:07:19 +05:30
}
2022-10-26 01:43:46 +02:00
resources , err = common . GetResourceAccordingToResourcePath ( fs , c . ResourcePaths , c . Cluster , policies , dClient , c . Namespace , c . PolicyReport , false , "" )
2020-12-20 01:21:31 +05:30
if err != nil {
fmt . Printf ( "Error: failed to load resources\nCause: %s\n" , err )
2022-12-16 14:50:15 +01:00
osExit ( 1 )
2020-12-20 01:21:31 +05:30
}
2020-03-06 03:00:18 +05:30
2022-10-26 01:43:46 +02:00
if ( len ( resources ) > 1 || len ( policies ) > 1 ) && c . VariablesString != "" {
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , sanitizederror . NewWithError ( "currently `set` flag supports variable for single policy applied on single resource " , nil )
2021-08-02 16:38:43 +05:30
}
2022-04-12 09:30:49 +05:30
// get the user info as request info from a different file
2022-04-25 20:20:40 +08:00
var userInfo v1beta1 . RequestInfo
2022-09-20 19:05:18 +05:30
if c . UserInfoPath != "" {
2023-02-08 14:19:56 +01:00
userInfo , err = common . GetUserInfoFromPath ( fs , c . UserInfoPath , false , "" )
2022-04-12 09:30:49 +05:30
if err != nil {
fmt . Printf ( "Error: failed to load request info\nCause: %s\n" , err )
2022-12-16 14:50:15 +01:00
osExit ( 1 )
2022-04-12 09:30:49 +05:30
}
}
2022-09-20 19:05:18 +05:30
if c . VariablesString != "" {
2022-10-26 01:43:46 +02:00
variables = common . SetInStoreContext ( policies , variables )
2021-08-02 16:38:43 +05:30
}
2022-07-19 16:28:09 +05:30
var policyRulesCount , mutatedPolicyRulesCount int
for _ , policy := range policies {
policyRulesCount += len ( policy . GetSpec ( ) . Rules )
}
2022-10-26 01:43:46 +02:00
for _ , policy := range policies {
2022-07-19 16:28:09 +05:30
mutatedPolicyRulesCount += len ( policy . GetSpec ( ) . Rules )
}
msgPolicyRules := "1 policy rule"
if policyRulesCount > 1 {
msgPolicyRules = fmt . Sprintf ( "%d policy rules" , policyRulesCount )
}
if mutatedPolicyRulesCount > policyRulesCount {
msgPolicyRules = fmt . Sprintf ( "%d policy rules" , mutatedPolicyRulesCount )
2020-12-20 01:21:31 +05:30
}
2020-11-19 15:03:15 +05:30
2020-12-20 01:21:31 +05:30
msgResources := "1 resource"
if len ( resources ) > 1 {
msgResources = fmt . Sprintf ( "%d resources" , len ( resources ) )
}
2020-08-22 01:21:29 +05:30
2022-10-26 01:43:46 +02:00
if len ( policies ) > 0 && len ( resources ) > 0 {
2022-09-20 19:05:18 +05:30
if ! c . Stdin {
2022-07-19 16:28:09 +05:30
if mutatedPolicyRulesCount > policyRulesCount {
fmt . Printf ( "\nauto-generated pod policies\nApplying %s to %s...\n" , msgPolicyRules , msgResources )
} else {
fmt . Printf ( "\nApplying %s to %s...\n" , msgPolicyRules , msgResources )
}
2021-03-26 23:33:45 +05:30
}
2020-12-20 01:21:31 +05:30
}
2020-08-11 22:59:50 +05:30
2021-08-31 21:18:54 +05:30
rc = & common . ResultCounts { }
2021-10-14 22:44:11 +05:30
skipInvalidPolicies . skipped = make ( [ ] string , 0 )
skipInvalidPolicies . invalid = make ( [ ] string , 0 )
2020-08-11 22:59:50 +05:30
2022-10-26 01:43:46 +02:00
for _ , policy := range policies {
2023-04-24 18:31:42 +08:00
_ , err := policy2 . Validate ( policy , nil , nil , true , openApiManager , config . KyvernoUserName ( config . KyvernoServiceAccountName ( ) ) )
2020-12-20 01:21:31 +05:30
if err != nil {
2022-01-04 17:36:33 -08:00
log . Log . Error ( err , "policy validation error" )
2021-10-14 22:44:11 +05:30
if strings . HasPrefix ( err . Error ( ) , "variable 'element.name'" ) {
2022-03-31 08:44:00 +02:00
skipInvalidPolicies . invalid = append ( skipInvalidPolicies . invalid , policy . GetName ( ) )
2021-10-14 22:44:11 +05:30
} else {
2022-03-31 08:44:00 +02:00
skipInvalidPolicies . skipped = append ( skipInvalidPolicies . skipped , policy . GetName ( ) )
2021-10-14 22:44:11 +05:30
}
2022-01-04 17:36:33 -08:00
2020-12-20 01:21:31 +05:30
continue
}
2020-08-12 13:39:06 +05:30
2021-11-03 11:16:55 -07:00
matches := common . HasVariables ( policy )
2021-07-24 00:02:48 +05:30
variable := common . RemoveDuplicateAndObjectVariables ( matches )
2021-08-31 16:24:31 +05:30
if len ( variable ) > 0 {
if len ( variables ) == 0 {
2021-08-31 17:59:18 +05:30
// check policy in variable file
2022-09-20 19:05:18 +05:30
if c . ValuesFile == "" || valuesMap [ policy . GetName ( ) ] == nil {
2022-03-31 08:44:00 +02:00
skipInvalidPolicies . skipped = append ( skipInvalidPolicies . skipped , policy . GetName ( ) )
2021-08-31 16:24:31 +05:30
continue
}
2020-03-06 03:00:18 +05:30
}
2020-12-20 01:21:31 +05:30
}
2020-03-06 03:00:18 +05:30
2022-12-09 22:15:23 +05:30
kindOnwhichPolicyIsApplied := common . GetKindsFromPolicy ( policy , subresources , dClient )
2021-09-01 18:51:31 +05:30
2020-12-20 01:21:31 +05:30
for _ , resource := range resources {
2021-09-20 22:16:57 +05:30
thisPolicyResourceValues , err := common . CheckVariableForPolicy ( valuesMap , globalValMap , policy . GetName ( ) , resource . GetName ( ) , resource . GetKind ( ) , variables , kindOnwhichPolicyIsApplied , variable )
2021-09-03 16:41:13 +05:30
if err != nil {
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , sanitizederror . NewWithError ( fmt . Sprintf ( "policy `%s` have variables. pass the values for the variables for resource `%s` using set/values_file flag" , policy . GetName ( ) , resource . GetName ( ) ) , err )
2020-12-20 01:21:31 +05:30
}
2022-10-19 22:09:15 +05:30
applyPolicyConfig := common . ApplyPolicyConfig {
Policy : policy ,
Resource : resource ,
MutateLogPath : c . MutateLogPath ,
MutateLogPathIsDir : mutateLogPathIsDir ,
Variables : thisPolicyResourceValues ,
UserInfo : userInfo ,
PolicyReport : c . PolicyReport ,
NamespaceSelectorMap : namespaceSelectorMap ,
Stdin : c . Stdin ,
Rc : rc ,
PrintPatchResource : true ,
Client : dClient ,
2022-11-21 09:21:32 -05:00
AuditWarn : c . AuditWarn ,
2022-12-09 22:15:23 +05:30
Subresources : subresources ,
2022-10-19 22:09:15 +05:30
}
2023-04-12 14:51:03 +02:00
ers , err := common . ApplyPolicyOnResource ( applyPolicyConfig )
2020-12-20 01:21:31 +05:30
if err != nil {
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , sanitizederror . NewWithError ( fmt . Errorf ( "failed to apply policy %v on resource %v" , policy . GetName ( ) , resource . GetName ( ) ) . Error ( ) , err )
}
for _ , response := range ers {
if ! response . IsEmpty ( ) {
for _ , rule := range autogen . ComputeRules ( response . Policy ) {
if rule . HasValidate ( ) || rule . HasVerifyImageChecks ( ) || rule . HasVerifyImages ( ) {
ruleFoundInEngineResponse := false
for _ , valResponseRule := range response . PolicyResponse . Rules {
if rule . Name == valResponseRule . Name ( ) {
ruleFoundInEngineResponse = true
switch valResponseRule . Status ( ) {
case engineapi . RuleStatusPass :
rc . Pass ++
case engineapi . RuleStatusFail :
ann := policy . GetAnnotations ( )
if scored , ok := ann [ kyvernov1 . AnnotationPolicyScored ] ; ok && scored == "false" {
rc . Warn ++
break
} else if applyPolicyConfig . AuditWarn && response . GetValidationFailureAction ( ) . Audit ( ) {
rc . Warn ++
} else {
rc . Fail ++
}
case engineapi . RuleStatusError :
rc . Error ++
case engineapi . RuleStatusWarn :
rc . Warn ++
case engineapi . RuleStatusSkip :
rc . Skip ++
}
continue
}
}
if ! ruleFoundInEngineResponse {
rc . Skip ++
response . PolicyResponse . Rules = append ( response . PolicyResponse . Rules ,
* engineapi . RuleSkip (
rule . Name ,
engineapi . Validation ,
rule . Validation . Message ,
) ,
)
}
}
}
}
responses = append ( responses , response )
2020-12-20 01:21:31 +05:30
}
}
2020-03-06 03:00:18 +05:30
}
2023-04-12 14:51:03 +02:00
return rc , resources , skipInvalidPolicies , responses , nil
2020-03-06 03:00:18 +05:30
}
2020-10-30 16:38:19 +05:30
// checkMutateLogPath - checking path for printing mutated resource (-o flag)
2020-11-03 01:25:32 +05:30
func checkMutateLogPath ( mutateLogPath string ) ( mutateLogPathIsDir bool , err error ) {
2020-10-30 16:38:19 +05:30
if mutateLogPath != "" {
spath := strings . Split ( mutateLogPath , "/" )
sfileName := strings . Split ( spath [ len ( spath ) - 1 ] , "." )
if sfileName [ len ( sfileName ) - 1 ] == "yml" || sfileName [ len ( sfileName ) - 1 ] == "yaml" {
mutateLogPathIsDir = false
} else {
mutateLogPathIsDir = true
}
err := createFileOrFolder ( mutateLogPath , mutateLogPathIsDir )
if err != nil {
2020-12-07 11:26:04 -08:00
if ! sanitizederror . IsErrorSanitized ( err ) {
return mutateLogPathIsDir , sanitizederror . NewWithError ( "failed to create file/folder." , err )
2020-10-30 16:38:19 +05:30
}
return mutateLogPathIsDir , err
}
}
return mutateLogPathIsDir , err
}
2022-07-14 02:07:51 +05:30
// PrintReportOrViolation - printing policy report/violations
2023-04-12 14:51:03 +02:00
func PrintReportOrViolation ( policyReport bool , rc * common . ResultCounts , resourcePaths [ ] string , resourcesLen int , skipInvalidPolicies SkippedInvalidPolicies , stdin bool , engineResponses [ ] engineapi . EngineResponse , warnExitCode int , warnNoPassed bool , auditWarn bool ) {
2021-10-29 11:16:13 +01:00
divider := "----------------------------------------------------------------------"
2021-10-14 22:44:11 +05:30
if len ( skipInvalidPolicies . skipped ) > 0 {
2021-10-29 11:16:13 +01:00
fmt . Println ( divider )
fmt . Println ( "Policies Skipped (as required variables are not provided by the user):" )
2021-10-14 22:44:11 +05:30
for i , policyName := range skipInvalidPolicies . skipped {
2021-10-29 11:16:13 +01:00
fmt . Printf ( "%d. %s\n" , i + 1 , policyName )
2021-10-14 22:44:11 +05:30
}
2021-10-29 11:16:13 +01:00
fmt . Println ( divider )
2021-10-14 22:44:11 +05:30
}
if len ( skipInvalidPolicies . invalid ) > 0 {
2021-10-29 11:16:13 +01:00
fmt . Println ( divider )
fmt . Println ( "Invalid Policies:" )
2021-10-14 22:44:11 +05:30
for i , policyName := range skipInvalidPolicies . invalid {
2021-10-29 11:16:13 +01:00
fmt . Printf ( "%d. %s\n" , i + 1 , policyName )
2021-08-31 16:24:31 +05:30
}
2021-10-29 11:16:13 +01:00
fmt . Println ( divider )
2021-08-31 16:24:31 +05:30
}
2020-10-30 16:38:19 +05:30
if policyReport {
2023-04-12 14:51:03 +02:00
resps := buildPolicyReports ( auditWarn , engineResponses ... )
2020-11-19 11:52:31 +05:30
if len ( resps ) > 0 || resourcesLen == 0 {
2021-10-29 11:16:13 +01:00
fmt . Println ( divider )
fmt . Println ( "POLICY REPORT:" )
fmt . Println ( divider )
2021-07-09 18:01:46 -07:00
report , _ := generateCLIRaw ( resps )
2020-11-01 22:32:12 +05:30
yamlReport , _ := yaml1 . Marshal ( report )
fmt . Println ( string ( yamlReport ) )
2020-10-30 16:38:19 +05:30
} else {
2021-10-29 11:16:13 +01:00
fmt . Println ( divider )
fmt . Println ( "POLICY REPORT: skip generating policy report (no validate policy found/resource skipped)" )
2020-10-30 16:38:19 +05:30
}
} else {
2021-03-26 23:33:45 +05:30
if ! stdin {
2023-04-12 14:51:03 +02:00
fmt . Printf ( "\npass: %d, fail: %d, warn: %d, error: %d, skip: %d \n" , rc . Pass , rc . Fail , rc . Warn , rc . Error , rc . Skip )
2021-03-26 23:33:45 +05:30
}
2021-09-03 17:17:22 +05:30
}
2020-10-30 16:38:19 +05:30
2021-09-03 17:17:22 +05:30
if rc . Fail > 0 || rc . Error > 0 {
2022-12-16 14:50:15 +01:00
osExit ( 1 )
2022-12-05 13:15:22 -05:00
} else if rc . Warn > 0 && warnExitCode != 0 {
2022-12-16 14:50:15 +01:00
osExit ( warnExitCode )
2023-03-27 11:18:44 +02:00
} else if rc . Pass == 0 && warnNoPassed {
osExit ( warnExitCode )
2020-10-30 16:38:19 +05:30
}
}
2020-08-18 21:03:00 -07:00
// createFileOrFolder - creating file or folder according to path provided
2020-08-22 01:21:29 +05:30
func createFileOrFolder ( mutateLogPath string , mutateLogPathIsDir bool ) error {
mutateLogPath = filepath . Clean ( mutateLogPath )
_ , err := os . Stat ( mutateLogPath )
2020-07-21 00:41:30 +05:30
if err != nil {
if os . IsNotExist ( err ) {
2020-08-22 01:21:29 +05:30
if ! mutateLogPathIsDir {
2020-11-17 12:01:01 -08:00
// check the folder existence, then create the file
2020-07-21 10:33:38 +05:30
var folderPath string
2020-08-22 01:21:29 +05:30
s := strings . Split ( mutateLogPath , "/" )
2020-07-21 10:33:38 +05:30
if len ( s ) > 1 {
2020-08-22 01:21:29 +05:30
folderPath = mutateLogPath [ : len ( mutateLogPath ) - len ( s [ len ( s ) - 1 ] ) - 1 ]
2020-07-21 10:33:38 +05:30
_ , err := os . Stat ( folderPath )
if os . IsNotExist ( err ) {
2022-05-17 08:19:03 +02:00
errDir := os . MkdirAll ( folderPath , 0 o750 )
2020-07-21 10:33:38 +05:30
if errDir != nil {
2021-10-14 22:44:11 +05:30
return sanitizederror . NewWithError ( "failed to create directory" , err )
2020-07-21 10:33:38 +05:30
}
2020-07-21 00:41:30 +05:30
}
}
2021-10-13 10:48:45 -07:00
mutateLogPath = filepath . Clean ( mutateLogPath )
2021-10-14 00:05:13 +02:00
// Necessary for us to create the file via variable as it is part of the CLI.
2022-05-17 08:19:03 +02:00
file , err := os . OpenFile ( mutateLogPath , os . O_RDONLY | os . O_CREATE , 0 o600 ) // #nosec G304
2020-07-21 00:41:30 +05:30
if err != nil {
2021-10-14 22:44:11 +05:30
return sanitizederror . NewWithError ( "failed to create file" , err )
2020-07-21 00:41:30 +05:30
}
err = file . Close ( )
if err != nil {
2021-10-14 22:44:11 +05:30
return sanitizederror . NewWithError ( "failed to close file" , err )
2020-07-21 00:41:30 +05:30
}
} else {
2022-05-17 08:19:03 +02:00
errDir := os . MkdirAll ( mutateLogPath , 0 o750 )
2020-07-21 00:41:30 +05:30
if errDir != nil {
2021-10-14 22:44:11 +05:30
return sanitizederror . NewWithError ( "failed to create directory" , err )
2020-07-21 00:41:30 +05:30
}
}
} else {
2021-10-14 22:44:11 +05:30
return sanitizederror . NewWithError ( "failed to describe file" , err )
2020-07-21 00:41:30 +05:30
}
}
return nil
}