Skip to content

JFrog Artifactory — governing a binary, not just a container

Every compliance pipeline in this platform gates on a container image digest. Most enterprise software is not a container image. It is a jar, a wheel, a tarball — published to an artifact repository, consumed by other teams, and wrapped in a container only at the very end, if at all.

This integration closes that gap. SARC publishes a jar to JFrog Artifactory, records it in Fides against its sha256, builds the container from that published jar, and refuses to deploy unless the binary is compliant.

Why this is not just “another registry”

Section titled “Why this is not just “another registry””

A container image is built by us, from our Dockerfile, and never leaves our pipeline until it is pushed. A jar is different: it is the unit other teams depend on, it is resolved transitively into builds we do not own, and it frequently outlives the container that first shipped it.

So the interesting question is not “can we scan the image”, it is:

Can you prove that the exact bytes sitting in your artifact repository were built from a known commit, scanned, SBOM’d, and approved — and that the thing running in production came from those bytes and no others?

That is what this lane demonstrates.

build jar → test → scan → publish to Artifactory → attest to Fides
→ build the image FROM the published jar → deploy, gated on the JAR

Each step, and what makes it non-obvious:

A small Java service (apps/arc-binary-demo) builds to a shaded (fat) jar. Shading is deliberate: scanners read what is inside the artifact, so a plain jar containing only our own classes reports zero vulnerabilities whether or not the build declares a vulnerable dependency. A scan that cannot fail is not evidence.

trivy rootfs scans the jar and syft generates a CycloneDX SBOM directly from it. Both produce exactly the reports Fides already parses — the evidence pipeline never needed to learn what a jar is.

Publishing goes through the JFrog CLI, not mvn deploy. Plain mvn deploy produces no build-info, and on OSS build-info is the only native provenance carrier available (see the limitations below).

Two things are written alongside the jar:

  • build-info — JFrog’s own record of the build: name, number, VCS revision, and every artifact with its checksums. Retrievable with GET /api/build/{name}/{number}.
  • provenance.json — a companion file in the same GAV directory carrying the commit, pipeline URL, Fides trail and the jar’s own sha256. Anyone who can resolve the jar can resolve its provenance, with no licensed features.

The jar is registered against its bare 64-character sha256 with type: "binary", then the trivy report and the SBOM are attached to the same trail.

This is the step that makes the claim true rather than rhetorical. The Dockerfile does not copy a locally-built jar. It downloads the jar from Artifactory and verifies its sha256 against the digest the pipeline attested:

FATAL: fetched jar does not match the attested digest

Without that check the image could ship a jar no evidence covers, and Artifactory would have returned 200 either way.

envs/<env>/artifact-digest.yaml pins the published jar’s digest beside the usual image tag — both written in a single commit, so ArgoCD can never see a new image alongside a stale digest.

An ArgoCD PreSync hook then asks Fides about that digest before the sync is allowed to proceed:

Asserting Fides compliance for JAR sha256=3e645230…
Fides compliance response: {"compliant":true,…}
Fides assert passed — the published binary holds the door open.

The gate fails closed. If Fides is unreachable, unconfigured, or returns anything the parser does not recognise, the sync is refused. An unconfigured gate that lets everything through is worse than no gate, because the pipeline still reports that a gate ran.

Verified against a live Artifactory OSS 7.161.20, not read from a datasheet. Several of these contradict JFrog’s own documentation, and all of them shaped the design above.

Capability OSS Consequence for us
Maven local / remote / virtual repos Full customer-shaped topology works
Build-info publish + retrieve The provenance carrier
Checksum-verified upload X-Checksum-Sha256 enforced server-side
Access tokens The CI credential
Artifact properties ❌ Pro Provenance cannot hang on the jar as metadata
AQL / search API ❌ JCR+ Cannot ask “which artifact has this checksum”
OIDC Enterprise X+ CI must hold a long-lived token
User management REST ❌ Pro Default admin password cannot be rotated post-boot
Repository creation REST ❌ Pro …but see the workaround below
Docker / OCI registry ❌ (JCR is free) Irrelevant here — we publish jars
Xray, build promotion, replication, webhooks ❌ Pro/Enterprise Not used

No OIDC — and it is Enterprise X, not Pro. This is the one worth raising with any customer planning a migration. Every other integration in this platform uses short-lived credentials: cosign keyless via Sigstore, GCP Workload Identity Federation, AWS role assumption. Artifactory is the sole exception, and it is not a choice we made — OSS and Pro both lack the feature. If a customer has been told to move to OIDC token exchange, that migration requires an Enterprise licence. Better they hear it from us now than discover it mid-rollout.

No properties, and no search to query them with. The instinctive design — tag the jar with commit SHA, build id, scan verdict and evidence URL — is a Pro feature, and matrix-parameter uploads (path;key=value) are the same feature under another name. Provenance therefore rides in build-info plus the provenance.json companion. Both survive a Pro upgrade unchanged, so nothing has to be redesigned if a licence arrives later.

No user management REST. POST /api/security/users/{name} answers “This REST API is available only in Artifactory Pro”, and the Access API rejects basic auth. The default admin password therefore cannot be changed after boot — it must be set through the Helm chart’s bootstrap. That matters a great deal if the instance is reachable from the internet.

One thing that is possible, contrary to appearances

Section titled “One thing that is possible, contrary to appearances”

Repository creation via PUT /api/repositories/{key} is Pro and returns 400. It would be reasonable to conclude that OSS setup cannot be automated at all and to write a page of click-here instructions instead.

It can. The config descriptor APIPATCH /api/system/configuration with a YAML body — is available on OSS and merges repository definitions happily. That is the difference between a reproducible environment and a runbook. See scripts/ci/artifactory-bootstrap.sh, which is idempotent.

Artifactory OSS is installed on the demo cluster from the official Helm chart (jfrog/artifactory-oss), with values in apps/artifactory/values.yaml:

  • PostgreSQL enabled (mandatory, above)
  • Bundled nginx disabled — the cluster’s ingress-nginx plus cert-manager handles TLS, matching how Fides and the portal are exposed
  • Admin password set through the chart’s bootstrap, because it cannot be changed afterwards on OSS
  • Three repositories: libs-release-local (deploy target), maven-central-remote (proxy), and libs-release (virtual, for resolution)

Settings → Artifactory connects a tenant: platform URL, a scoped access token, and the repository key. Test connection distinguishes connected from connected but that repository is not visible to this token — a valid token against the wrong repo key otherwise produces an empty view that looks exactly like “nothing has been published yet”.

Once configured, a Binaries entry appears in the sidebar listing each published artifact with its full, untruncated sha256 — because that is the value you paste into a Fides compliance query, and a shortened one would be useless for exactly that.

The portal lists binaries by walking a repository folder, not by searching, because OSS has neither AQL nor the search API. That is why the view takes a GAV path rather than a filter.

Run it with just demo-artifactory (just demo-artifactory-dry rehearses safely), with the companion slide deck on just demo-artifactory-slides.

It is deliberately separate from just demo, the podtato-head container walkthrough: its own child pipeline, its own Fides flow, its own ArgoCD app, its own deck on a different port. Either can be shown alone, and neither can disturb the other’s trail, change request or environments.

Eight beats:

  1. What is published today — start from the repository, not the pipeline
  2. An ordinary change — a small Java service, deliberately uninteresting
  3. One push, one pipeline — the container demo does not run and cannot break
  4. The same evidence, over a jar — build, trivy, syft, publish, attest
  5. The jar in Artifactory — checksums, build-info, provenance.json
  6. The same digest, in Fides — registered type: "binary"
  7. The image comes from the jar — and refuses a mismatched digest
  8. The gate asks about the binary — a non-compliant jar stops the deploy

A gate nobody has watched refuse is not a demonstration of a gate. Re-run the pipeline with DEMO_VULNERABLE=true and the build pulls in log4j-core 2.14.1CVE-2021-44228, which an audience recognises on sight. trivy then reports 8 vulnerabilities, 2 of them CRITICAL, and the PreSync gate stops the deployment on the jar’s digest while the container itself is perfectly fine.

The controls are org-scoped in Fides, not per-flow. That was a constraint, and it turned into the strongest thing this lane demonstrates:

The same controls pass over a jar and over a container, without being rewritten.

Nothing was special-cased for binaries. The evidence spine never knew or cared that the other demo’s artifacts happened to be OCI images — which is exactly the property you want when the next artifact type arrives.