From 637f756994464ee90781189ff0d265a6329b6e7b Mon Sep 17 00:00:00 2001 From: shuting Date: Thu, 6 Mar 2025 16:48:26 +0800 Subject: [PATCH] feat: support json payload via CLI apply command (#12296) * chore: remove unused code Signed-off-by: ShutingZhao * feat: support json in CLI apply command Signed-off-by: ShutingZhao * chore: remove not used validation expressions Signed-off-by: ShutingZhao * chore: update codegen docs Signed-off-by: ShutingZhao * chore: add unit tests Signed-off-by: ShutingZhao --------- Signed-off-by: ShutingZhao --- .../kubectl-kyverno/commands/apply/command.go | 62 ++++-- .../commands/apply/command_test.go | 36 ++-- .../kubectl-kyverno/utils/common/common.go | 2 +- cmd/cli/kubectl-kyverno/utils/common/fetch.go | 11 +- docs/user/cli/commands/kyverno_apply.md | 1 + pkg/cel/engine/engine.go | 50 +++-- pkg/cel/policy/policy_test.go | 10 +- .../json-check-dockerfile/payload.json | 176 ++++++++++++++++++ .../json-check-dockerfile/policy.yaml | 40 ++++ 9 files changed, 327 insertions(+), 61 deletions(-) create mode 100644 test/cli/test-validating-policy/json-check-dockerfile/payload.json create mode 100644 test/cli/test-validating-policy/json-check-dockerfile/policy.yaml diff --git a/cmd/cli/kubectl-kyverno/commands/apply/command.go b/cmd/cli/kubectl-kyverno/commands/apply/command.go index 5730ead628..009a374fb2 100644 --- a/cmd/cli/kubectl-kyverno/commands/apply/command.go +++ b/cmd/cli/kubectl-kyverno/commands/apply/command.go @@ -11,6 +11,7 @@ import ( "time" "github.com/go-git/go-billy/v5/memfs" + "github.com/kyverno/kyverno-json/pkg/payload" kyvernov1 "github.com/kyverno/kyverno/api/kyverno/v1" kyvernov2 "github.com/kyverno/kyverno/api/kyverno/v2" policiesv1alpha1 "github.com/kyverno/kyverno/api/policies.kyverno.io/v1alpha1" @@ -79,6 +80,7 @@ type ApplyCommandConfig struct { inlineExceptions bool GenerateExceptions bool GeneratedExceptionTTL time.Duration + JSONPaths []string } func Command() *cobra.Command { @@ -110,6 +112,9 @@ func Command() *cobra.Command { for _, response := range responses { var failedRules []engineapi.RuleResponse resPath := fmt.Sprintf("%s/%s/%s", response.Resource.GetNamespace(), response.Resource.GetKind(), response.Resource.GetName()) + if resPath == "//" { + resPath = "JSON payload" + } for _, rule := range response.PolicyResponse.Rules { if rule.Status() == engineapi.RuleStatusFail { failedRules = append(failedRules, rule) @@ -143,6 +148,8 @@ func Command() *cobra.Command { return exit(out, rc, applyCommandConfig.warnExitCode, applyCommandConfig.warnNoPassed) }, } + + cmd.Flags().StringSliceVarP(&applyCommandConfig.JSONPaths, "json", "", []string{}, "Path to JSON payload files") cmd.Flags().StringSliceVarP(&applyCommandConfig.ResourcePaths, "resource", "r", []string{}, "Path to resource files") cmd.Flags().StringSliceVarP(&applyCommandConfig.ResourcePaths, "resources", "", []string{}, "Path to resource files") cmd.Flags().StringSliceVarP(&applyCommandConfig.TargetResourcePaths, "target-resource", "", []string{}, "Path to individual files containing target resources files for policies that have mutate existing") @@ -209,7 +216,7 @@ func (c *ApplyCommandConfig) applyCommandHelper(out io.Writer) (*processor.Resul } var targetResources []*unstructured.Unstructured if len(c.TargetResourcePaths) > 0 { - targetResources, err = c.loadResources(out, c.TargetResourcePaths, policies, vaps, nil) + targetResources, _, err = c.loadResources(out, c.TargetResourcePaths, policies, vaps, nil) if err != nil { return nil, nil, skippedInvalidPolicies, nil, err } @@ -218,7 +225,7 @@ func (c *ApplyCommandConfig) applyCommandHelper(out io.Writer) (*processor.Resul if err != nil { return nil, nil, skippedInvalidPolicies, nil, err } - resources, err := c.loadResources(out, c.ResourcePaths, policies, vaps, dClient) + resources, jsonPayloads, err := c.loadResources(out, c.ResourcePaths, policies, vaps, dClient) if err != nil { return nil, nil, skippedInvalidPolicies, nil, err } @@ -247,10 +254,11 @@ func (c *ApplyCommandConfig) applyCommandHelper(out io.Writer) (*processor.Resul policyRulesCount += len(vps) exceptionsCount := len(exceptions) exceptionsCount += len(celexceptions) + resourceCount := len(resources) + len(jsonPayloads) if exceptionsCount > 0 { - fmt.Fprintf(out, "\nApplying %d policy rule(s) to %d resource(s) with %d exception(s)...\n", policyRulesCount, len(resources), exceptionsCount) + fmt.Fprintf(out, "\nApplying %d policy rule(s) to %d resource(s) with %d exception(s)...\n", policyRulesCount, resourceCount, exceptionsCount) } else { - fmt.Fprintf(out, "\nApplying %d policy rule(s) to %d resource(s)...\n", policyRulesCount, len(resources)) + fmt.Fprintf(out, "\nApplying %d policy rule(s) to %d resource(s)...\n", policyRulesCount, resourceCount) } } rc, resources1, responses1, err := c.applyPolicies( @@ -272,7 +280,7 @@ func (c *ApplyCommandConfig) applyCommandHelper(out io.Writer) (*processor.Resul if err != nil { return rc, resources1, skippedInvalidPolicies, responses1, err } - responses3, err := c.applyValidatingPolicies(vps, celexceptions, resources1, variables.Namespace, rc, dClient) + responses3, err := c.applyValidatingPolicies(vps, jsonPayloads, celexceptions, resources1, variables.Namespace, rc, dClient) if err != nil { return rc, resources1, skippedInvalidPolicies, responses1, err } @@ -325,6 +333,7 @@ func (c *ApplyCommandConfig) applyValidatingAdmissionPolicies( func (c *ApplyCommandConfig) applyValidatingPolicies( vps []policiesv1alpha1.ValidatingPolicy, + jsonPayloads []*unstructured.Unstructured, exceptions []*policiesv1alpha1.CELPolicyException, resources []*unstructured.Unstructured, namespaceProvider func(string) *corev1.Namespace, @@ -355,6 +364,7 @@ func (c *ApplyCommandConfig) applyValidatingPolicies( } restMapper := restmapper.NewDiscoveryRESTMapper(apiGroupResources) responses := make([]engineapi.EngineResponse, 0) + responsesTemp := make([]engine.EngineResponse, 0) for _, resource := range resources { // get gvk from resource gvk := resource.GroupVersionKind() @@ -384,7 +394,7 @@ func (c *ApplyCommandConfig) applyValidatingPolicies( false, nil, ) - response, err := eng.Handle(ctx, request) + reps, err := eng.Handle(ctx, request) if err != nil { if c.ContinueOnFail { fmt.Printf("failed to apply validating policies on resource %s (%v)\n", resource.GetName(), err) @@ -392,7 +402,25 @@ func (c *ApplyCommandConfig) applyValidatingPolicies( } return responses, fmt.Errorf("failed to apply validating policies on resource %s (%w)", resource.GetName(), err) } - // transform response into legacy engine responses + responsesTemp = append(responsesTemp, reps) + } + + for _, json := range jsonPayloads { + eng = engine.NewEngine(provider, nil, nil) + request := engine.RequestFromJSON(contextProvider, json) + reps, err := eng.Handle(ctx, request) + if err != nil { + if c.ContinueOnFail { + fmt.Printf("failed to apply validating policies on JSON payloads (%v)\n", err) + continue + } + return responses, fmt.Errorf("failed to apply validating policies on JSON payloads (%w)", err) + } + responsesTemp = append(responsesTemp, reps) + } + + // transform response into legacy engine responses + for _, response := range responsesTemp { for _, r := range response.Policies { engineResponse := engineapi.EngineResponse{ Resource: *response.Resource, @@ -482,12 +510,24 @@ func (c *ApplyCommandConfig) applyPolicies( return &rc, resources, responses, nil } -func (c *ApplyCommandConfig) loadResources(out io.Writer, paths []string, policies []kyvernov1.PolicyInterface, vap []admissionregistrationv1.ValidatingAdmissionPolicy, dClient dclient.Interface) ([]*unstructured.Unstructured, error) { +func (c *ApplyCommandConfig) loadResources(out io.Writer, paths []string, policies []kyvernov1.PolicyInterface, vap []admissionregistrationv1.ValidatingAdmissionPolicy, dClient dclient.Interface) ([]*unstructured.Unstructured, []*unstructured.Unstructured, error) { resources, err := common.GetResourceAccordingToResourcePath(out, nil, paths, c.Cluster, policies, vap, dClient, c.Namespace, c.PolicyReport, "") if err != nil { - return resources, fmt.Errorf("failed to load resources (%w)", err) + return resources, nil, fmt.Errorf("failed to load resources (%w)", err) } - return resources, nil + + var jsonPayloads []*unstructured.Unstructured + if len(c.JSONPaths) > 0 { + for _, path := range c.JSONPaths { + payload, err := payload.Load(path) + if err != nil { + return nil, nil, fmt.Errorf("failed to load JSON payload (%w)", err) + } + + jsonPayloads = append(jsonPayloads, &unstructured.Unstructured{Object: payload.(map[string]interface{})}) + } + } + return resources, jsonPayloads, nil } func (c *ApplyCommandConfig) loadPolicies() ( @@ -632,7 +672,7 @@ func (c *ApplyCommandConfig) checkArguments() error { if (len(c.PolicyPaths) > 0 && c.PolicyPaths[0] == "-") && len(c.ResourcePaths) > 0 && c.ResourcePaths[0] == "-" { return fmt.Errorf("a stdin pipe can be used for either policies or resources, not both") } - if len(c.ResourcePaths) == 0 && !c.Cluster { + if len(c.ResourcePaths) == 0 && !c.Cluster && len(c.JSONPaths) == 0 { return fmt.Errorf("resource file(s) or cluster required") } return nil diff --git a/cmd/cli/kubectl-kyverno/commands/apply/command_test.go b/cmd/cli/kubectl-kyverno/commands/apply/command_test.go index 36377b20c8..9be483d858 100644 --- a/cmd/cli/kubectl-kyverno/commands/apply/command_test.go +++ b/cmd/cli/kubectl-kyverno/commands/apply/command_test.go @@ -145,24 +145,6 @@ func Test_Apply(t *testing.T) { }, }}, }, - // { - // // TODO - // config: ApplyCommandConfig{ - // PolicyPaths: []string{"https://github.com/kyverno/policies/openshift/team-validate-ns-name/"}, - // ResourcePaths: []string{"../../../../../test/openshift/team-validate-ns-name.yaml"}, - // GitBranch: "main", - // PolicyReport: true, - // }, - // expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{ - // Summary: policyreportv1alpha2.PolicyReportSummary{ - // Pass: 2, - // Fail: 0, - // Skip: 0, - // Error: 0, - // Warn: 0, - // }, - // }}, - // }, { config: ApplyCommandConfig{ PolicyPaths: []string{"../../../../../test/cli/apply/policies-set"}, @@ -537,6 +519,22 @@ func Test_Apply_ValidatingPolicies(t *testing.T) { }, }}, }, + { + config: ApplyCommandConfig{ + PolicyPaths: []string{"../../../../../test/cli/test-validating-policy/json-check-dockerfile/policy.yaml"}, + JSONPaths: []string{"../../../../../test/cli/test-validating-policy/json-check-dockerfile/payload.json"}, + PolicyReport: true, + }, + expectedPolicyReports: []policyreportv1alpha2.PolicyReport{{ + Summary: policyreportv1alpha2.PolicyReportSummary{ + Pass: 1, + Fail: 1, + Skip: 0, + Error: 0, + Warn: 0, + }, + }}, + }, { config: ApplyCommandConfig{ PolicyPaths: []string{"../../../../../test/cli/test-cel-exceptions/check-deployment-labels/policy.yaml"}, @@ -610,7 +608,7 @@ func Test_Apply_ValidatingPolicies(t *testing.T) { _ = input.Close() }() } - desc := fmt.Sprintf("Policies: [%s], / Resources: [%s]", strings.Join(tc.config.PolicyPaths, ","), strings.Join(tc.config.ResourcePaths, ",")) + desc := fmt.Sprintf("Policies: [%s], / Resources: [%s], JSON payload: [%s]", strings.Join(tc.config.PolicyPaths, ","), strings.Join(tc.config.ResourcePaths, ","), strings.Join(tc.config.JSONPaths, ",")) _, _, _, responses, err := tc.config.applyCommandHelper(os.Stdout) assert.NoError(t, err, desc) diff --git a/cmd/cli/kubectl-kyverno/utils/common/common.go b/cmd/cli/kubectl-kyverno/utils/common/common.go index 9650c68600..8fe6aafc90 100644 --- a/cmd/cli/kubectl-kyverno/utils/common/common.go +++ b/cmd/cli/kubectl-kyverno/utils/common/common.go @@ -37,7 +37,7 @@ func GetResourceAccordingToResourcePath( policyResourcePath string, ) (resources []*unstructured.Unstructured, err error) { if fs != nil { - resources, err = GetResourcesWithTest(out, fs, policies, resourcePaths, policyResourcePath) + resources, err = GetResourcesWithTest(out, fs, resourcePaths, policyResourcePath) if err != nil { return nil, fmt.Errorf("failed to extract the resources (%w)", err) } diff --git a/cmd/cli/kubectl-kyverno/utils/common/fetch.go b/cmd/cli/kubectl-kyverno/utils/common/fetch.go index b3ee62cca9..d00b441687 100644 --- a/cmd/cli/kubectl-kyverno/utils/common/fetch.go +++ b/cmd/cli/kubectl-kyverno/utils/common/fetch.go @@ -13,7 +13,6 @@ import ( "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/log" "github.com/kyverno/kyverno/cmd/cli/kubectl-kyverno/resource" "github.com/kyverno/kyverno/pkg/admissionpolicy" - "github.com/kyverno/kyverno/pkg/autogen" "github.com/kyverno/kyverno/pkg/clients/dclient" kubeutils "github.com/kyverno/kyverno/pkg/utils/kube" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" @@ -126,16 +125,8 @@ func whenClusterIsFalse(out io.Writer, resourcePaths []string, policyReport bool } // GetResourcesWithTest with gets matched resources by the given policies -func GetResourcesWithTest(out io.Writer, fs billy.Filesystem, policies []kyvernov1.PolicyInterface, resourcePaths []string, policyResourcePath string) ([]*unstructured.Unstructured, error) { +func GetResourcesWithTest(out io.Writer, fs billy.Filesystem, resourcePaths []string, policyResourcePath string) ([]*unstructured.Unstructured, error) { resources := make([]*unstructured.Unstructured, 0) - resourceTypesMap := make(map[string]bool) - for _, policy := range policies { - for _, rule := range autogen.Default.ComputeRules(policy, "") { - for _, kind := range rule.MatchResources.Kinds { - resourceTypesMap[kind] = true - } - } - } if len(resourcePaths) > 0 { for _, resourcePath := range resourcePaths { var resourceBytes []byte diff --git a/docs/user/cli/commands/kyverno_apply.md b/docs/user/cli/commands/kyverno_apply.md index f9bcb08b3d..0cca1507dc 100644 --- a/docs/user/cli/commands/kyverno_apply.md +++ b/docs/user/cli/commands/kyverno_apply.md @@ -49,6 +49,7 @@ kyverno apply [flags] --generated-exception-ttl duration Default TTL for generated exceptions (default 720h0m0s) -b, --git-branch string test git repository branch -h, --help help for apply + --json strings Path to JSON payload files --kubeconfig string path to kubeconfig file with authorization and master location information -n, --namespace string Optional Policy parameter passed with cluster flag -o, --output string Prints the mutated/generated resources in provided file/directory diff --git a/pkg/cel/engine/engine.go b/pkg/cel/engine/engine.go index 63fcbfb6c2..7f788c980b 100644 --- a/pkg/cel/engine/engine.go +++ b/pkg/cel/engine/engine.go @@ -8,6 +8,7 @@ import ( vpolautogen "github.com/kyverno/kyverno/pkg/cel/autogen" contextlib "github.com/kyverno/kyverno/pkg/cel/libs/context" "github.com/kyverno/kyverno/pkg/cel/matching" + celpolicy "github.com/kyverno/kyverno/pkg/cel/policy" engineapi "github.com/kyverno/kyverno/pkg/engine/api" "github.com/kyverno/kyverno/pkg/engine/handlers" admissionutils "github.com/kyverno/kyverno/pkg/utils/admission" @@ -25,8 +26,9 @@ import ( ) type EngineRequest struct { - request admissionv1.AdmissionRequest - context contextlib.ContextInterface + jsonPayload *unstructured.Unstructured + request admissionv1.AdmissionRequest + context contextlib.ContextInterface } func RequestFromAdmission(context contextlib.ContextInterface, request admissionv1.AdmissionRequest) EngineRequest { @@ -36,6 +38,13 @@ func RequestFromAdmission(context contextlib.ContextInterface, request admission } } +func RequestFromJSON(context contextlib.ContextInterface, jsonPayload *unstructured.Unstructured) EngineRequest { + return EngineRequest{ + jsonPayload: jsonPayload, + context: context, + } +} + func Request( context contextlib.ContextInterface, gvk schema.GroupVersionKind, @@ -111,6 +120,15 @@ func (e *engine) Handle(ctx context.Context, request EngineRequest) (EngineRespo if err != nil { return response, err } + + if request.jsonPayload != nil { + response.Resource = request.jsonPayload + for _, policy := range policies { + response.Policies = append(response.Policies, e.handlePolicy(ctx, policy, request.jsonPayload.Object, nil, nil, nil, request.context)) + } + return response, nil + } + // load objects object, oldObject, err := admissionutils.ExtractResources(nil, request.request) if err != nil { @@ -147,7 +165,7 @@ func (e *engine) Handle(ctx context.Context, request EngineRequest) (EngineRespo } // evaluate policies for _, policy := range policies { - response.Policies = append(response.Policies, e.handlePolicy(ctx, policy, attr, &request.request, namespace, request.context)) + response.Policies = append(response.Policies, e.handlePolicy(ctx, policy, nil, attr, &request.request, namespace, request.context)) } return response, nil } @@ -163,12 +181,14 @@ func (e *engine) matchPolicy(policy CompiledPolicy, attr admission.Attributes, n } // match against main policy constraints - matches, err := match(policy.Policy.Spec.MatchConstraints) - if err != nil { - return false, -1, err - } - if matches { - return true, -1, nil + if policy.Policy.GetSpec().MatchConstraints != nil { + matches, err := match(policy.Policy.Spec.MatchConstraints) + if err != nil { + return false, -1, err + } + if matches { + return true, -1, nil + } } // match against autogen rules @@ -185,7 +205,7 @@ func (e *engine) matchPolicy(policy CompiledPolicy, attr admission.Attributes, n return false, -1, nil } -func (e *engine) handlePolicy(ctx context.Context, policy CompiledPolicy, attr admission.Attributes, request *admissionv1.AdmissionRequest, namespace runtime.Object, context contextlib.ContextInterface) PolicyResponse { +func (e *engine) handlePolicy(ctx context.Context, policy CompiledPolicy, jsonPayload interface{}, attr admission.Attributes, request *admissionv1.AdmissionRequest, namespace runtime.Object, context contextlib.ContextInterface) PolicyResponse { response := PolicyResponse{ Actions: policy.Actions, Policy: policy.Policy, @@ -201,7 +221,15 @@ func (e *engine) handlePolicy(ctx context.Context, policy CompiledPolicy, attr a } autogenIndex = index } - result, err := policy.CompiledPolicy.Evaluate(ctx, nil, attr, request, namespace, context, autogenIndex) + + var result *celpolicy.EvaluationResult + var err error + if jsonPayload != nil { + result, err = policy.CompiledPolicy.Evaluate(ctx, jsonPayload, nil, nil, nil, context, -1) + } else { + result, err = policy.CompiledPolicy.Evaluate(ctx, nil, attr, request, namespace, context, autogenIndex) + } + // TODO: error is about match conditions here ? if err != nil { response.Rules = handlers.WithResponses(engineapi.RuleError("evaluation", engineapi.Validation, "failed to load context", err, nil)) diff --git a/pkg/cel/policy/policy_test.go b/pkg/cel/policy/policy_test.go index 33d1d3a956..9be470d1d7 100644 --- a/pkg/cel/policy/policy_test.go +++ b/pkg/cel/policy/policy_test.go @@ -25,14 +25,6 @@ func Test_evaluateJson(t *testing.T) { { "message": "HTTP calls are not allowed", "expression": "!object.Stages.exists(s, \n s.Commands.exists(c, \n c.Args.exists(a, \n a.Value.contains('http://') || a.Value.contains('https://')\n )\n )\n)" - }, - { - "message": "curl is not allowed", - "expression": "!object.Stages.exists(s, \n s.Commands.exists(c, \n c.CmdLine.contains('curl')\n )\n)" - }, - { - "message": "wget is not allowed", - "expression": "!object.Stages.exists(s, \n s.Commands.exists(c, \n c.CmdLine.contains('wget')\n )\n)" } ] } @@ -242,5 +234,5 @@ func Test_evaluateJson(t *testing.T) { assert.NilError(t, err) } - t.Log(result) + assert.Assert(t, result.Result == false) } diff --git a/test/cli/test-validating-policy/json-check-dockerfile/payload.json b/test/cli/test-validating-policy/json-check-dockerfile/payload.json new file mode 100644 index 0000000000..aac9aa00c4 --- /dev/null +++ b/test/cli/test-validating-policy/json-check-dockerfile/payload.json @@ -0,0 +1,176 @@ +{ + "MetaArgs": [ + { + "Key": "BUILD_PLATFORM", + "DefaultValue": "\"linux/amd64\"", + "ProvidedValue": null, + "Value": "\"linux/amd64\"" + }, + { + "Key": "BUILDER_IMAGE", + "DefaultValue": "\"golang:1.20.6-alpine3.18\"", + "ProvidedValue": null, + "Value": "\"golang:1.20.6-alpine3.18\"" + } + ], + "Stages": [ + { + "Name": "builder", + "BaseName": "\"golang:1.20.6-alpine3.18\"", + "Platform": "$BUILD_PLATFORM", + "Comment": "", + "SourceCode": "FROM --platform=$BUILD_PLATFORM $BUILDER_IMAGE as builder", + "Location": [ + { + "Start": { + "Line": 4, + "Character": 0 + }, + "End": { + "Line": 4, + "Character": 0 + } + } + ], + "As": "builder", + "From": { + "Image": "\"golang:1.20.6-alpine3.18\"" + }, + "Commands": [ + { + "Name": "WORKDIR", + "Path": "/" + }, + { + "Chmod": "", + "Chown": "", + "DestPath": "./", + "From": "", + "Link": false, + "Name": "COPY", + "SourceContents": null, + "SourcePaths": [ + "." + ] + }, + { + "Args": [ + { + "Comment": "", + "Key": "SIGNER_BINARY_LINK", + "Value": "\"https://d2hvyiie56hcat.cloudfront.net/linux/amd64/plugin/latest/notation-aws-signer-plugin.zip\"" + } + ], + "Name": "ARG" + }, + { + "Args": [ + { + "Comment": "", + "Key": "SIGNER_BINARY_FILE", + "Value": "\"notation-aws-signer-plugin.zip\"" + } + ], + "Name": "ARG" + }, + { + "CmdLine": [ + "wget -O ${SIGNER_BINARY_FILE} ${SIGNER_BINARY_LINK}" + ], + "Files": null, + "FlagsUsed": [], + "Name": "RUN", + "PrependShell": true + }, + { + "CmdLine": [ + "apk update && apk add unzip && unzip -o ${SIGNER_BINARY_FILE}" + ], + "Files": null, + "FlagsUsed": [], + "Name": "RUN", + "PrependShell": true + }, + { + "CmdLine": [ + "GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags=\"-w -s\" -o kyverno-notation-aws ." + ], + "Files": null, + "FlagsUsed": [], + "Name": "RUN", + "PrependShell": true + } + ] + }, + { + "Name": "", + "BaseName": "gcr.io/distroless/static:nonroot", + "Platform": "", + "Comment": "", + "SourceCode": "FROM gcr.io/distroless/static:nonroot", + "Location": [ + { + "Start": { + "Line": 20, + "Character": 0 + }, + "End": { + "Line": 20, + "Character": 0 + } + } + ], + "From": { + "Image": "gcr.io/distroless/static:nonroot" + }, + "Commands": [ + { + "Name": "WORKDIR", + "Path": "/" + }, + { + "Env": [ + { + "Key": "PLUGINS_DIR", + "Value": "/plugins" + } + ], + "Name": "ENV" + }, + { + "Chmod": "", + "Chown": "", + "DestPath": "plugins/com.amazonaws.signer.notation.plugin/notation-com.amazonaws.signer.notation.plugin", + "From": "builder", + "Link": false, + "Name": "COPY", + "SourceContents": null, + "SourcePaths": [ + "notation-com.amazonaws.signer.notation.plugin" + ] + }, + { + "Chmod": "", + "Chown": "", + "DestPath": "kyverno-notation-aws", + "From": "builder", + "Link": false, + "Name": "COPY", + "SourceContents": null, + "SourcePaths": [ + "kyverno-notation-aws" + ] + }, + { + "CmdLine": [ + "/kyverno-notation-aws" + ], + "Files": null, + "Name": "ENTRYPOINT", + "PrependShell": false + } + ] + } + ] + } + \ No newline at end of file diff --git a/test/cli/test-validating-policy/json-check-dockerfile/policy.yaml b/test/cli/test-validating-policy/json-check-dockerfile/policy.yaml new file mode 100644 index 0000000000..e02d7223ac --- /dev/null +++ b/test/cli/test-validating-policy/json-check-dockerfile/policy.yaml @@ -0,0 +1,40 @@ +apiVersion: policies.kyverno.io/v1alpha1 +kind: ValidatingPolicy +metadata: + name: check-dockerfile-disallow-curl +spec: + evaluation: + mode: JSON + validations: + - message: "curl is not allowed" + expression: >- + !object.Stages.exists(s, + s.Commands.exists(c, + has(c.CmdLine) && c.CmdLine.exists(cmd, string(cmd).contains('curl')) + ) + ) +--- +apiVersion: policies.kyverno.io/v1alpha1 +kind: ValidatingPolicy +metadata: + name: check-dockerfile-disallow-wget +spec: + evaluation: + mode: JSON + validations: + - message: "wget is not allowed" + expression: >- + !object.Stages.exists(s, + s.Commands.exists(c, + has(c.CmdLine) && c.CmdLine.exists(cmd, string(cmd).contains('wget')) + ) + ) + - message: "HTTP calls are not allowed" + expression: >- + !object.Stages.exists(s, + s.Commands.exists(c, + c.Args.exists(a, + a.Value.contains('http://') || a.Value.contains('https://') + ) + ) + ) \ No newline at end of file