Skip to content

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

License

Notifications You must be signed in to change notification settings

jfrog/jfrog-opa-policy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

31 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

JFrog Evidence Verify OPA Gatekeeper Provider

This project offers an External Data Provider for OPA Gatekeeper that checks JFrog Evidence records for container images before they are admitted to the cluster.

🎯 What this repo contains

  • provider/: Go service implementing the External Data Provider plus Kubernetes deployment, service, and OPA Gatekeeper Provider resources.
  • templates/: Gatekeeper ConstraintTemplate that calls the provider and acts according to its response.
  • policies/: Example constraints configuring which registries and repositories are checked, and which predicate types must be present for images evidence.
  • configmap-examples/: Example Kubernetes resources with a content-check ConfigMap for validating evidence predicate content.

πŸ“‹ Prerequisites

  • Kubernetes cluster with Gatekeeper v3.11+ and External Data enabled (mutual TLS required).
  • JFrog Platform with Evidence feature and an access token that can query image metadata and evidence.
  • TLS materials for the provider pod (server.crt/server.key) and Gatekeeper’s CA (ca.crt).

Build and publish the provider image

  1. Build the provider image from provider/:
cd provider
docker buildx build --platform linux/<your platform> -t provider:<version> .

make sure to set platform (for example arm64) and image tag

Prepare Kubernetes secrets (namespace: gatekeeper-system)

The secrets required for the provider to work are:

  • Provider TLS secrets - Used by the provider Pod for secure communication
  • JFrog token - Allows the provider to call JFrog platform APIs (requires permissions to read and annotate docker repositories whose workloads are being validated)

Important: Gatekeeper uses mutual TLS; ensure the provider trusts Gatekeeper's CA. The deployment mounts gatekeeper-webhook-server-cert as /tmp/gatekeeper/ca.crt.

Option 1: Automated Setup (Recommended)

Run the provided script to generate TLS certificates and create Kubernetes secrets interactively:

cd provider
./generate-tls.sh

The script will:

  1. Generate a self-signed CA and server certificates
  2. Update provider.yaml with the base64-encoded CA bundle
  3. Prompt to create the jfrog-provider-tls TLS secret
  4. Prompt to create the jfrog-token-secret with your JFrog access token

Environment variables (optional):

Variable Default Description
SERVICE_NAME jfrog-evidence-opa-provider Kubernetes service name
SERVICE_NAMESPACE gatekeeper-system Kubernetes namespace
CERT_VALIDITY_DAYS 365 Certificate validity period
OUTPUT_DIR ./certs Output directory for certificates

Option 2: Manual Setup

Follow the OPA Gatekeeper External Data TLS documentation to create TLS resources.

Step 1: Generate CA certificate

openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 365 -key ca.key -subj "/O=JFrog/CN=JFrog Evidence OPA Provider CA" -out ca.crt

Step 2: Generate server certificate

openssl genrsa -out server.key 2048
openssl req -newkey rsa:2048 -nodes -keyout server.key -subj "/CN=jfrog-evidence-opa-provider.gatekeeper-system" -out server.csr
openssl x509 -req -extfile <(printf "subjectAltName=DNS:jfrog-evidence-opa-provider.gatekeeper-system") -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt

Step 3: Update provider.yaml with CA bundle

# Generate base64-encoded CA bundle and update provider.yaml
CA_BUNDLE=$(cat ca.crt | base64 | tr -d '\n')
# Replace the caBundle value in provider/provider.yaml with $CA_BUNDLE

Step 4: Create TLS secret

kubectl create secret tls jfrog-provider-tls \
  --cert=./server.crt --key=./server.key \
  -n gatekeeper-system

Step 5: Create JFrog token secret

Note: The key must be token - the pod reads it via JFROG_TOKEN_SECRET environment variable.

kubectl -n gatekeeper-system create secret generic jfrog-token-secret --from-literal=token=<jfrog_token>

Tip: On AWS EKS you can use jfrog-registry-operator for creating JFrog image pull secrets and also generic secrets that are short-lived and auto-rotated.

πŸš€ Deploy the JFrog provider

Apply the manifests (edit images/tags as needed):

kubectl -n gatekeeper-system apply -f provider/deployment.yaml
kubectl -n gatekeeper-system apply -f provider/provider.yaml

The deployment expects:

  • secret gatekeeper-webhook-server-cert with OPA Gatekeeper server CA.
  • TLS cert/key from jfrog-provider-tls secret created in the previous step.

πŸ”§ Install the Gatekeeper policy

  1. Install the ConstraintTemplate:
kubectl apply -f templates/jfrogcheckevidence.yaml
  1. Create a constraint with your settings (registries, repositories, predicate types). Examples are provided:
kubectl apply -f policies/jfrog_check_evidence.yaml

Key parameters:

  • checkedRegistries: registries to evaluate (images from other registeries are not checked).
  • checkedRepositories: optional repository allowlist, leave empty to have all repositories checked.
  • checkedPredicateTypes: evidence predicate types that must be present and verified for each image.

πŸ” How the provider validates images (high level)

  • Gatekeeper sends keys where the first entry is a comma-separated list of predicate types and the rest are image references.
  • For each image, the provider:
    1. Sends a HEAD API call to JFrog Artifactory to obtain the digest and manifest filename of the image.
    2. Queries JFrog Evidence GraphQL (/onemodel/api/v1/graphql) for the evidence collection attached to the image.
    3. Verifies that each required predicate type has at least one verified evidence record.
    4. If content checks are available (see below), evaluates every JMESPath expression against the matching evidence node. All expressions must return truthy values.
    5. Returns _valid or _invalid per image back to Gatekeeper; any missing or non-conforming evidence yields a violation.
  • Set DEBUG=true to log requests/responses inside the provider pod.

πŸ”Ž Content Checks (optional)

By 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 expressions.

How it works

  1. At startup the provider reads a Kubernetes ConfigMap named jfrog-provider-content-checks in the same namespace.
  2. The ConfigMap contains a JSON object mapping each predicate type to an array of JMESPath expressions.
  3. 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.).
  4. 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.
  5. If no evidence record passes all checks, the image is marked _invalid.

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.

ConfigMap format

Create a ConfigMap in the gatekeeper-system namespace (the same namespace as the provider):

apiVersion: v1
kind: ConfigMap
metadata:
  name: jfrog-provider-content-checks
data:
  checks: |
    {
      "https://slsa.dev/provenance/v1": [
        "starts_with(predicate.buildDefinition.externalParameters.workflow.repository, 'https://github.com/my-org')",
        "starts_with(createdBy, '[email protected]')",
        "starts_with(predicate.runDetails.builder.id, 'https://github.com/my-org')",
        "predicate.buildDefinition.resolvedDependencies[?!starts_with(uri, 'git+https://github.com/my-org')] | length(@) == `0`"
      ],
      "https://sonarsource.com/evidence/sonarqube/v1": [
        "predicate.status == 'OK'"
      ]
    }

An example ConfigMap is provided at examples/jfrog-provider-content-checks.yaml.

Expression examples

Expressions are evaluated against the full evidence node JSON. Useful fields include:

Field Description
predicate The parsed evidence predicate (structure depends on the predicate type)
createdBy Identity of who created the evidence
createdAt Timestamp of evidence creation
verified Whether the evidence signature was verified
predicateType The predicate type URI

Example expressions:

Expression Purpose
predicate.status == 'OK' Assert a status field equals a specific value
starts_with(createdBy, 'ci-bot@') Ensure evidence was created by a CI system
predicate.scanner.name == 'Xray' Verify a specific scanner produced the evidence
predicate.buildDefinition.resolvedDependencies[?!starts_with(uri, 'git+https://github.com/my-org')] | length(@) == \0`` Ensure all resolved dependencies come from a trusted organization

Deploying content checks

# Apply the content checks ConfigMap
kubectl -n gatekeeper-system apply -f examples/jfrog-provider-content-checks.yaml

# Restart the provider to pick up the new checks (loaded once at startup)
kubectl -n gatekeeper-system rollout restart deployment jfrog-evidence-opa-provider

Important: The provider reads the ConfigMap only at startup. After changing the ConfigMap, restart the provider pod for the new rules to take effect.

RBAC

The 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.

Try it out

With the constraint installed, apply a Pod to check the policy:

kubectl apply -f pod.yaml

If the required evidence is missing or unverified, the admission request will be rejected with a message that includes the checked images and provider response. Notice this ConstraintTemplate is targeting Pods. If the need is to validate Deployments, then the ConstraintTemplate needs to change and collect images from input.review.object.spec.template.spec.containers[].image and from input.review.object.spec.template.spec.initContainers[].image

In 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).

In 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:

Error 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": ""}

In case the validation fails on an error communicating with the server, or some other failure, a different message is returned: Error 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"}

More information can be viewed on the provider log.

Notes

  • The provider listens on :8443 with TLS 1.3 and requires client certs from Gatekeeper.
  • Update the container image reference in provider/deployment.yaml before production use.
  • In order to simplify the setup process, especiall the TLS setup, these instructions are installing the provider under the gatekeeper-system namespace

πŸ“š Additional Resources

🀝 Contributing

Feel free to submit issues and enhancement requests!

About

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

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Releases

No releases published

Contributors 2

  •  
  •