{"id":51146337,"url":"https://github.com/jfrog/jfrog-opa-policy","last_synced_at":"2026-06-26T03:01:30.762Z","repository":{"id":338958845,"uuid":"1116086351","full_name":"jfrog/jfrog-opa-policy","owner":"jfrog","description":"This repository provider a JFrog sample implementation of an OPA Gatekeeper provider with usage example. the provider, template and policies are allowing the validation of JFrog verified evidence by OPA Gatekeeper for preventing any non-approved images to be deployed into the user's cluster","archived":false,"fork":false,"pushed_at":"2026-02-17T09:22:32.000Z","size":15407,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-17T14:20:25.134Z","etag":null,"topics":["attestations","evidence","gatekeeper","jfrog","opa","policy","policy-as-code"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jfrog.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-14T06:56:06.000Z","updated_at":"2026-02-17T09:22:36.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/jfrog/jfrog-opa-policy","commit_stats":null,"previous_names":["jfrog/jfrog-opa-policy"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/jfrog/jfrog-opa-policy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jfrog%2Fjfrog-opa-policy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jfrog%2Fjfrog-opa-policy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jfrog%2Fjfrog-opa-policy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jfrog%2Fjfrog-opa-policy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jfrog","download_url":"https://codeload.github.com/jfrog/jfrog-opa-policy/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jfrog%2Fjfrog-opa-policy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34801014,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-26T02:00:06.560Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["attestations","evidence","gatekeeper","jfrog","opa","policy","policy-as-code"],"created_at":"2026-06-26T03:01:29.501Z","updated_at":"2026-06-26T03:01:30.750Z","avatar_url":"https://github.com/jfrog.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JFrog Evidence Verify OPA Gatekeeper Provider\n\nThis project offers an External Data Provider for OPA Gatekeeper that checks JFrog Evidence records for container images before they are admitted to the cluster.\n\n## 🎯 What this repo contains\n- `provider/`: Go service implementing the External Data Provider plus Kubernetes deployment, service, and OPA Gatekeeper Provider resources.\n- `templates/`: Gatekeeper `ConstraintTemplate` that calls the provider and acts according to its response.\n- `policies/`: Example constraints configuring which registries and repositories are checked, and which predicate types must be present for images evidence.\n- `configmap-examples/`: Example Kubernetes resources with a content-check ConfigMap for validating evidence predicate content.\n\n## 📋 Prerequisites\n- Kubernetes cluster with Gatekeeper v3.11+ and External Data enabled (mutual TLS required).\n- JFrog Platform with Evidence feature and an access token that can query image metadata and evidence.\n- TLS materials for the provider pod (`server.crt`/`server.key`) and Gatekeeper’s CA (`ca.crt`).\n\n## Build and publish the provider image\n1) Build the provider image from `provider/`:\n```\ncd provider\ndocker buildx build --platform linux/\u003cyour platform\u003e -t provider:\u003cversion\u003e .\n```\nmake sure to set platform (for example arm64) and image tag \n\n## Prepare Kubernetes secrets (namespace: `gatekeeper-system`)\n\nThe secrets required for the provider to work are:\n- **Provider TLS secrets** - Used by the provider Pod for secure communication\n- **JFrog token** - Allows the provider to call JFrog platform APIs (requires permissions to read and annotate docker repositories whose workloads are being validated)\n\n\u003e **Important:** Gatekeeper uses mutual TLS; ensure the provider trusts Gatekeeper's CA. The deployment mounts `gatekeeper-webhook-server-cert` as `/tmp/gatekeeper/ca.crt`.\n\n### Option 1: Automated Setup (Recommended)\n\nRun the provided script to generate TLS certificates and create Kubernetes secrets interactively:\n\n```bash\ncd provider\n./generate-tls.sh\n```\n\nThe script will:\n1. Generate a self-signed CA and server certificates\n2. Update `provider.yaml` with the base64-encoded CA bundle\n3. Prompt to create the `jfrog-provider-tls` TLS secret\n4. Prompt to create the `jfrog-token-secret` with your JFrog access token\n\n**Environment variables** (optional):\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `SERVICE_NAME` | `jfrog-evidence-opa-provider` | Kubernetes service name |\n| `SERVICE_NAMESPACE` | `gatekeeper-system` | Kubernetes namespace |\n| `CERT_VALIDITY_DAYS` | `365` | Certificate validity period |\n| `OUTPUT_DIR` | `./certs` | Output directory for certificates |\n\n### Option 2: Manual Setup\n\nFollow the [OPA Gatekeeper External Data TLS documentation](https://open-policy-agent.github.io/gatekeeper/website/docs/v3.11.x/externaldata/#tls-and-mutual-tls-support) to create TLS resources.\n\n**Step 1: Generate CA certificate**\n```bash\nopenssl genrsa -out ca.key 2048\nopenssl req -new -x509 -days 365 -key ca.key -subj \"/O=JFrog/CN=JFrog Evidence OPA Provider CA\" -out ca.crt\n```\n\n**Step 2: Generate server certificate**\n```bash\nopenssl genrsa -out server.key 2048\nopenssl req -newkey rsa:2048 -nodes -keyout server.key -subj \"/CN=jfrog-evidence-opa-provider.gatekeeper-system\" -out server.csr\nopenssl x509 -req -extfile \u003c(printf \"subjectAltName=DNS:jfrog-evidence-opa-provider.gatekeeper-system\") -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt\n```\n\n**Step 3: Update provider.yaml with CA bundle**\n```bash\n# Generate base64-encoded CA bundle and update provider.yaml\nCA_BUNDLE=$(cat ca.crt | base64 | tr -d '\\n')\n# Replace the caBundle value in provider/provider.yaml with $CA_BUNDLE\n```\n\n**Step 4: Create TLS secret**\n```bash\nkubectl create secret tls jfrog-provider-tls \\\n  --cert=./server.crt --key=./server.key \\\n  -n gatekeeper-system\n```\n\n**Step 5: Create JFrog token secret**\n\n\u003e **Note:** The key must be `token` - the pod reads it via `JFROG_TOKEN_SECRET` environment variable.\n\n```bash\nkubectl -n gatekeeper-system create secret generic jfrog-token-secret --from-literal=token=\u003cjfrog_token\u003e\n```\n\n\u003e **Tip:** On AWS EKS you can use [jfrog-registry-operator](https://github.com/jfrog/jfrog-registry-operator) for creating JFrog image pull secrets and also generic secrets that are short-lived and auto-rotated.\n\n## 🚀 Deploy the JFrog provider\nApply the manifests (edit images/tags as needed):\n```\nkubectl -n gatekeeper-system apply -f provider/deployment.yaml\nkubectl -n gatekeeper-system apply -f provider/provider.yaml\n```\nThe deployment expects:\n- secret `gatekeeper-webhook-server-cert` with OPA Gatekeeper server CA.\n- TLS cert/key from `jfrog-provider-tls` secret created in the previous step.\n\n## 🔧 Install the Gatekeeper policy\n1) Install the `ConstraintTemplate`:\n```\nkubectl apply -f templates/jfrogcheckevidence.yaml\n```\n2) Create a constraint with your settings (registries, repositories, predicate types). Examples are provided:\n```\nkubectl apply -f policies/jfrog_check_evidence.yaml\n```\nKey parameters:\n- `checkedRegistries`: registries to evaluate (images from other registeries are not checked).\n- `checkedRepositories`: optional repository allowlist, leave empty to have all repositories checked.\n- `checkedPredicateTypes`: evidence predicate types that must be present _and verified_ for each image.\n\n## 🔍 How the provider validates images (high level)\n- Gatekeeper sends keys where the first entry is a comma-separated list of predicate types and the rest are image references.\n- For each image, the provider:\n  1. Sends a `HEAD` API call to JFrog Artifactory to obtain the digest and manifest filename of the image.\n  2. Queries JFrog Evidence GraphQL (`/onemodel/api/v1/graphql`) for the evidence collection attached to the image.\n  3. Verifies that each required predicate type has at least one **verified** evidence record.\n  4. If **content checks** are available (see below), evaluates every JMESPath expression against the matching evidence node. All expressions must return truthy values.\n  5. Returns `_valid` or `_invalid` per image back to Gatekeeper; any missing or non-conforming evidence yields a violation.\n- Set `DEBUG=true` to log requests/responses inside the provider pod.\n\n## 🔎 Content Checks (optional)\n\nBy default the provider only verifies that evidence **exists** and is **signed** for each required predicate type. Content checks let you go further and validate the **content** of the evidence predicate using [JMESPath](https://jmespath.org/) expressions.\n\n### How it works\n1. At startup the provider reads a Kubernetes ConfigMap named `jfrog-provider-content-checks` in the same namespace.\n2. The ConfigMap contains a JSON object mapping each predicate type to an array of JMESPath expressions.\n3. When evidence is found and verified for a predicate type, every expression configured for that type is evaluated against the full evidence node (including fields like `predicate`, `createdBy`, `createdAt`, etc.).\n4. All expressions must evaluate to a **truthy** value (non-nil, non-false, non-empty string/array/map). If any expression fails, the evidence is treated as non-conforming and the provider continues searching for another matching evidence record.\n5. If no evidence record passes all checks, the image is marked `_invalid`.\n\n\u003e **Note:** Content checks are entirely optional. If the ConfigMap does not exist or cannot be read, the provider falls back to existence + signature verification only.\n\n### ConfigMap format\n\nCreate a ConfigMap in the `gatekeeper-system` namespace (the same namespace as the provider):\n\n```yaml\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: jfrog-provider-content-checks\ndata:\n  checks: |\n    {\n      \"https://slsa.dev/provenance/v1\": [\n        \"starts_with(predicate.buildDefinition.externalParameters.workflow.repository, 'https://github.com/my-org')\",\n        \"starts_with(createdBy, 'ci-user@example.com')\",\n        \"starts_with(predicate.runDetails.builder.id, 'https://github.com/my-org')\",\n        \"predicate.buildDefinition.resolvedDependencies[?!starts_with(uri, 'git+https://github.com/my-org')] | length(@) == `0`\"\n      ],\n      \"https://sonarsource.com/evidence/sonarqube/v1\": [\n        \"predicate.status == 'OK'\"\n      ]\n    }\n```\n\nAn example ConfigMap is provided at `examples/jfrog-provider-content-checks.yaml`.\n\n### Expression examples\n\nExpressions are evaluated against the full evidence node JSON. Useful fields include:\n\n| Field | Description |\n|-------|-------------|\n| `predicate` | The parsed evidence predicate (structure depends on the predicate type) |\n| `createdBy` | Identity of who created the evidence |\n| `createdAt` | Timestamp of evidence creation |\n| `verified` | Whether the evidence signature was verified |\n| `predicateType` | The predicate type URI |\n\nExample expressions:\n\n| Expression | Purpose |\n|------------|---------|\n| `predicate.status == 'OK'` | Assert a status field equals a specific value |\n| `starts_with(createdBy, 'ci-bot@')` | Ensure evidence was created by a CI system |\n| `predicate.scanner.name == 'Xray'` | Verify a specific scanner produced the evidence |\n| `predicate.buildDefinition.resolvedDependencies[?!starts_with(uri, 'git+https://github.com/my-org')] \\| length(@) == \\`0\\`` | Ensure all resolved dependencies come from a trusted organization |\n\n### Deploying content checks\n\n```bash\n# Apply the content checks ConfigMap\nkubectl -n gatekeeper-system apply -f examples/jfrog-provider-content-checks.yaml\n\n# Restart the provider to pick up the new checks (loaded once at startup)\nkubectl -n gatekeeper-system rollout restart deployment jfrog-evidence-opa-provider\n```\n\n\u003e **Important:** The provider reads the ConfigMap only at startup. After changing the ConfigMap, restart the provider pod for the new rules to take effect.\n\n### RBAC\n\nThe provider's deployment manifest already includes a `ServiceAccount`, `Role`, and `RoleBinding` that grant `get` access to the `jfrog-provider-content-checks` ConfigMap. No additional RBAC configuration is needed.\n\n## Try it out\nWith the constraint installed, apply a Pod to check the policy:\n```\nkubectl apply -f pod.yaml\n```\nIf the required evidence is missing or unverified, the admission request will be rejected with a message that includes the checked images and provider response.\nNotice this ConstraintTemplate is targeting Pods. If the need is to validate Deployments, then the ConstraintTemplate needs to change and collect images from \ninput.review.object.spec.template.spec.containers[_].image\nand from \ninput.review.object.spec.template.spec.initContainers[_].image\n\nIn case the pod is within the validation scope and the validation is successful, the pod will be created (validation logs can be viewed inside the provider pod log). \n\nIn case the evidence validation is not successful, a message will appear and the pod deployment will fail. see below an example for such a failure:\n\n``\nError from server (Forbidden): error when creating \"my-pod.yaml\": admission webhook \"validation.gatekeeper.sh\" denied the request: [jfrog-check-evidence] TARGET IMAGES: [\"myjfrog.jfrog.io/docker-local/my-image:1.0.0\"], RESPONSE: {\"errors\": [], \"responses\": [[\"myjfrog.jfrog.io/docker-local/my-image:1.0.0\", \"_invalid\"]], \"status_code\": 200, \"system_error\": \"\"}\n``\n\nIn case the validation fails on an error communicating with the server, or some other failure, a different message is returned:\n``\nError from server (Forbidden): error when creating \"my-image.yaml\": admission webhook \"validation.gatekeeper.sh\" denied the request: [jfrog-check-evidence] TARGET IMAGES: [\"myjfrog.jfrog.io/docker-local/my-image:1.0\"], RESPONSE: {\"errors\": [], \"responses\": [], \"status_code\": 200, \"system_error\": \"unable to get digest for myjfrog.jfrog.io/docker-local/my-image:1.0: failed to get digest, response status: 401 Unauthorized\"}\n``\n\nMore information can be viewed on the provider log.\n\n\n## Notes\n\n- The provider listens on `:8443` with TLS 1.3 and requires client certs from Gatekeeper.\n- Update the container image reference in `provider/deployment.yaml` before production use.\n- In order to simplify the setup process, especiall the TLS setup, these instructions are installing the provider under the gatekeeper-system namespace\n\n## 📚 Additional Resources\n- [OPA Gatekeeper Documentation](https://open-policy-agent.github.io/gatekeeper/website/)\n- [GraphQL Documentation](https://graphql.org/learn/)\n- [JFrog Platform Documentation](https://www.jfrog.com/confluence/)\n- [Jamespath Documentation](https://jmespath.org/)\n\n## 🤝 Contributing\n\nFeel free to submit issues and enhancement requests!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjfrog%2Fjfrog-opa-policy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjfrog%2Fjfrog-opa-policy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjfrog%2Fjfrog-opa-policy/lists"}