-
Notifications
You must be signed in to change notification settings - Fork 338
docs: adding JFrog OPA policy allowing evidence checking on images, policy … #721
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| resources: | ||
| - template.yaml |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| apiVersion: constraints.gatekeeper.sh/v1beta1 | ||
| kind: K8sJfrogCheckEvidence | ||
| metadata: | ||
| name: jfrog-check-evidence | ||
| spec: | ||
| match: | ||
| kinds: | ||
| - apiGroups: [""] | ||
| kinds: ["Pod"] | ||
| parameters: | ||
| checkedRegistries: ["my-jfrog-registry.jfrog.io"] | ||
| # add whitelist for repositories, uncomment to enable | ||
| # checkedRepositories: ["docker-local"] | ||
| checkedPredicateTypes: ["https://slsa.dev/provenance/v1"] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| apiVersion: v1 | ||
| kind: Pod | ||
| metadata: | ||
| name: your-jfrog-testing-pod | ||
| spec: | ||
| containers: | ||
| - name: testing | ||
| image: my-jfrog-registry.jfrog.io/docker-local/your-valid-testing-image:1.0.0 | ||
| imagePullPolicy: Always | ||
| imagePullSecrets: | ||
| - name: jfrog-secret |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| apiVersion: v1 | ||
| kind: Pod | ||
| metadata: | ||
| name: your-jfrog-testing-pod | ||
| spec: | ||
| containers: | ||
| - name: testing | ||
| image: my-jfrog-registry.jfrog.io/docker-local/your-invalid-testing-image:1.0.0 | ||
| imagePullPolicy: Always | ||
| imagePullSecrets: | ||
| - name: jfrog-secret |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,18 @@ | ||||||
| kind: Suite | ||||||
| apiVersion: test.gatekeeper.sh/v1alpha1 | ||||||
| metadata: | ||||||
| name: replicalimits | ||||||
| tests: | ||||||
| - name: replica-limit | ||||||
|
||||||
| - name: replica-limit | |
| - name: jfrog-evidence-check |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| apiVersion: templates.gatekeeper.sh/v1 | ||
| kind: ConstraintTemplate | ||
| metadata: | ||
| name: k8sjfrogcheckevidence | ||
| annotations: | ||
| metadata.gatekeeper.sh/title: "Check JFrog Evidence" | ||
| metadata.gatekeeper.sh/version: 1.0.0 | ||
| description: >- | ||
| Checks if the images in a pod comply with regulations or corporate policies by validating they have required verified evidence. | ||
| This policy is based on the JFrog OPA provider. To deploy JFrog OPA Provider, please visit: https://github.com/jfrog/jfrog-opa-policy | ||
| spec: | ||
| crd: | ||
| spec: | ||
| names: | ||
| kind: K8sJfrogCheckEvidence | ||
| validation: | ||
| openAPIV3Schema: | ||
| type: object | ||
| properties: | ||
| checkedPredicateTypes: | ||
| type: array | ||
| description: "The predicate types to check for evidence" | ||
| items: | ||
| type: string | ||
| checkedRegistries: | ||
| type: array | ||
| description: "The registries for which to check for evidence" | ||
| items: | ||
| type: string | ||
| checkedRepositories: | ||
| type: array | ||
| description: "Optional, The repositories for which to check for evidence, remove parameter to check all repositories" | ||
| items: | ||
| type: string | ||
| targets: | ||
| - target: admission.k8s.gatekeeper.sh | ||
| rego: | | ||
| package k8sjfrogcheckevidence | ||
| import data.lib.filter_images | ||
| import future.keywords.in | ||
| import future.keywords.contains | ||
|
|
||
| # fails if input.parameters.checkedRegistries is empty | ||
| violation[{"msg": msg}] { | ||
| not input.parameters.checkedRegistries | ||
| msg := "checkedRegistries parameter is empty" | ||
| } | ||
|
|
||
| # fails if input.parameters.checkedPredicateTypes is empty | ||
| violation[{"msg": msg}] { | ||
| not input.parameters.checkedPredicateTypes | ||
| msg := "checkedPredicateTypes parameter is empty" | ||
| } | ||
|
|
||
| # get all images | ||
| all_images := [img | img = input.review.object.spec.containers[_].image] | ||
| target_images := [img | img = all_images[_] | ||
| filter_images.is_checked_registry(img) | ||
| filter_images.is_checked_repository(img) | ||
| ] | ||
| # get init images | ||
| init_images := [img | img = input.review.object.spec.initContainers[_].image] | ||
| target_init_images := [img | img = init_images[_] | ||
| filter_images.is_checked_registry(img) | ||
| filter_images.is_checked_repository(img) | ||
| ] | ||
|
|
||
| # append target_images, target_init_images | ||
| checked_images := array.concat(target_images, target_init_images) | ||
| # convert arreay input.parameters.checkedPredicateTypes to string | ||
| types := concat(",", input.parameters.checkedPredicateTypes) | ||
| typesArray := [types] | ||
|
|
||
| checked_keys := array.concat(typesArray, checked_images) | ||
| violation[{"msg": msg}] { | ||
| count(checked_images) > 0 | ||
| response := external_data({"provider": "jfrog-evidence-opa-provider", "keys": checked_keys}) | ||
| any_issues_found(response) | ||
|
|
||
| errors_count := count(response.errors) | ||
| system_error := response.system_error | ||
| invalid_images := [img | some item in response.responses | ||
| count(item) == 2 | ||
| item[1] == "_invalid" | ||
| img := item[0]] | ||
| msg := sprintf("TARGET IMAGES: %v, ERROR_COUNT: %v, SYSTEM_ERROR: %v, INVALID_IMAGES: %v", [checked_images, errors_count, system_error, invalid_images]) | ||
| } | ||
|
|
||
| any_issues_found(response) { | ||
| count(response.errors) > 0 | ||
| } else { | ||
| response.system_error != "" | ||
| } else { | ||
| response_has_invalid(response) | ||
| } | ||
|
|
||
| response_has_invalid(response) { | ||
| some item in response.responses | ||
| count(item) == 2 | ||
| item[1] == "_invalid" | ||
| } | ||
|
|
||
| libs: | ||
| - | | ||
| package lib.filter_images | ||
| import future.keywords.in | ||
| is_checked_registry(img) { | ||
| checked_registries := object.get(object.get(input, "parameters", {}), "checkedRegistries", []) | ||
| some registry in checked_registries | ||
| startswith(img, registry) | ||
| } | ||
| is_checked_repository(img) { | ||
| checked_repositories := object.get(object.get(input, "parameters", {}), "checkedRepositories", ["/"]) | ||
| some repository in checked_repositories | ||
| contains(img, repository) | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,35 @@ | ||||||||||||
| apiVersion: templates.gatekeeper.sh/v1 | ||||||||||||
| kind: ConstraintTemplate | ||||||||||||
| metadata: | ||||||||||||
| name: k8sjfrogcheckevidence | ||||||||||||
| annotations: | ||||||||||||
| metadata.gatekeeper.sh/title: "Check JFrog Evidence" | ||||||||||||
| metadata.gatekeeper.sh/version: 1.0.0 | ||||||||||||
| description: >- | ||||||||||||
| Checks if the images in a pod comply with regulations or corporate policies by validating they have required verified evidence. | ||||||||||||
| This policy is based on the JFrog OPA provider. To deploy JFrog OPA Provider, please visit: https://github.com/jfrog/jfrog-opa-policy | ||||||||||||
| spec: | ||||||||||||
| crd: | ||||||||||||
| spec: | ||||||||||||
| names: | ||||||||||||
| kind: K8sJfrogCheckEvidence | ||||||||||||
| validation: | ||||||||||||
| openAPIV3Schema: | ||||||||||||
| type: object | ||||||||||||
| properties: | ||||||||||||
| checkedPredicateTypes: | ||||||||||||
| type: array | ||||||||||||
| items: | ||||||||||||
| type: string | ||||||||||||
| checkedRegistries: | ||||||||||||
| type: array | ||||||||||||
| items: | ||||||||||||
| type: string | ||||||||||||
| checkedRepositories: | ||||||||||||
| type: array | ||||||||||||
|
carmithersh marked this conversation as resolved.
|
||||||||||||
| items: | ||||||||||||
| type: string | ||||||||||||
| targets: | ||||||||||||
| - target: admission.k8s.gatekeeper.sh | ||||||||||||
| rego: | | ||||||||||||
| {{ file.Read "src/general/jfrog-evidence/src.rego" | strings.Indent 8 | strings.TrimSuffix "\n" }} | ||||||||||||
|
||||||||||||
| {{ file.Read "src/general/jfrog-evidence/src.rego" | strings.Indent 8 | strings.TrimSuffix "\n" }} | |
| {{ file.Read "src/general/jfrog-evidence/src.rego" | strings.Indent 8 | strings.TrimSuffix "\n" }} | |
| libs: | |
| - rego: | | |
| {{ file.Read "src/general/jfrog-evidence/lib_filter_images.rego" | strings.Indent 8 | strings.TrimSuffix "\n" }} |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,79 @@ | ||||||||||||||||||||||||||||
| package k8sjfrogcheckevidence | ||||||||||||||||||||||||||||
| import data.lib.filter_images | ||||||||||||||||||||||||||||
| import future.keywords.in | ||||||||||||||||||||||||||||
| import future.keywords.contains | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| # fails if input.parameters.checkedRegistries is empty | ||||||||||||||||||||||||||||
| violation[{"msg": msg}] { | ||||||||||||||||||||||||||||
| not input.parameters.checkedRegistries | ||||||||||||||||||||||||||||
| msg := "checkedRegistries parameter is empty" | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| # fails if input.parameters.checkedPredicateTypes is empty | ||||||||||||||||||||||||||||
| violation[{"msg": msg}] { | ||||||||||||||||||||||||||||
| not input.parameters.checkedPredicateTypes | ||||||||||||||||||||||||||||
| msg := "checkedPredicateTypes parameter is empty" | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| # get all images | ||||||||||||||||||||||||||||
| all_images := [img | img = input.review.object.spec.containers[_].image] | ||||||||||||||||||||||||||||
| target_images := [img | img = all_images[_] | ||||||||||||||||||||||||||||
| filter_images.is_checked_registry(img) | ||||||||||||||||||||||||||||
| filter_images.is_checked_repository(img) | ||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||
| # get init images | ||||||||||||||||||||||||||||
| init_images := [img | img = input.review.object.spec.initContainers[_].image] | ||||||||||||||||||||||||||||
| target_init_images := [img | img = init_images[_] | ||||||||||||||||||||||||||||
| filter_images.is_checked_registry(img) | ||||||||||||||||||||||||||||
| filter_images.is_checked_repository(img) | ||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| # append target_images, target_init_images | ||||||||||||||||||||||||||||
| checked_images := array.concat(target_images, target_init_images) | ||||||||||||||||||||||||||||
| # convert arreay input.parameters.checkedPredicateTypes to string | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
| # convert arreay input.parameters.checkedPredicateTypes to string | |
| # convert array input.parameters.checkedPredicateTypes to string |
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the checkedPredicateTypes parameter is an empty array, the concat function will produce an empty string, which may not be the intended behavior when communicating with the JFrog provider. Consider adding validation to ensure checkedPredicateTypes is not empty, or handle the empty case explicitly to avoid unexpected behavior from the external provider.
| # convert arreay input.parameters.checkedPredicateTypes to string | |
| types := concat(",", input.parameters.checkedPredicateTypes) | |
| typesArray := [types] | |
| # build typesArray from input.parameters.checkedPredicateTypes only when non-empty | |
| typesArray := [types] { | |
| params := object.get(input, "parameters", {}) | |
| checked_predicate_types := object.get(params, "checkedPredicateTypes", []) | |
| count(checked_predicate_types) > 0 | |
| types := concat(",", checked_predicate_types) | |
| } | |
| default typesArray := [] |
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "libs:" section should not be in the src.rego file. Based on codebase conventions, the libs should be defined in the constraint.tmpl file. See examples: src/general/imagedigests/constraint.tmpl:39-41, src/general/containerresources/constraint.tmpl:52-54. The library code should be in a separate file and referenced in the constraint template.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The suite metadata name is incorrect. It should be "jfrogcheckevidence" (or preferably "k8sjfrogcheckevidence" to follow conventions) instead of "replicalimits", which appears to be copy-pasted from another test suite.