Skip to content

JFrog Artifactory

JFrog Artifactory hosts the private Cargo registry for all rdt-model-* crates and the hardened OPA Docker image used by CaaS deployments. It provides a local Cargo repository for publishing, a remote proxy of crates.io for dependency caching, and a Docker image path for the OPA policy engine. Cargo virtual repos are not supported by Roche’s Artifactory as Code terraform modules, so .cargo/config.toml uses source replacement to route crates.io traffic through the remote proxy.

PropertyValue
URLhttps://eu.repository.roche.com
Auth methodBearer token (Artifactory access token)
NetworkRoche corporate network (VPN or direct)
Access taskA16
GitHub issue#165
CIDM groupGLOARTI_selfservice
VariableSourceDescription
ARTIFACTORY_URLVault common/artifactoryBase URL of the Artifactory instance
ARTIFACTORY_USERVault common/artifactoryArtifactory service user
ARTIFACTORY_CARGO_REGISTRYVault common/artifactoryLocal Cargo repo for publishing (rdtmodel-general-cargo-all-l)
ARTIFACTORY_CARGO_REMOTEVault common/artifactoryRemote Cargo repo for crates.io caching (rdtmodel-general-cargo-all-r)
ARTIFACTORY_TOKENVault common/artifactoryBearer token for authenticated API access
ARTIFACTORY_TOKEN_EXPIREVault common/artifactoryToken expiry date (2027-05-05)
OPA_IMAGEVault {env}/opaFull OPA image reference (e.g. rdtmodel-general-docker-all-l.eu.repository.roche.com/opa:1.16.1)
TypeNamePurposeMR
Local (Cargo)rdtmodel-general-cargo-all-lStore published rdt-model crates#2811
Remote (Cargo)rdtmodel-general-cargo-all-rProxy/cache crates.io dependencies#2811
Local (Docker)rdtmodel-general-docker-all-lHardened OPA container images for CaaS#2850

Cargo virtual repos are not available in Roche’s Artifactory as Code terraform modules. Instead, .cargo/config.toml uses [source.crates-io] replace-with to route dependency resolution through the remote proxy, and [registries.roche] for publishing to the local repo.

The hardened OPA image is built by scripts/build-opa-image.sh and pushed to eu.repository.roche.com/rdtmodel-general-docker-all-l/rdt-model/opa. This image is pulled by CaaS Kubernetes clusters for runtime policy evaluation.

PropertyValue
Repositoryrdtmodel-general-docker-all-l
Image patheu.repository.roche.com/rdtmodel-general-docker-all-l/rdt-model/opa
Build scriptscripts/build-opa-image.sh
Version filek8s/OPA_VERSION (version + digest)
Vault configsecret/{env}/opaOPA_IMAGE
SecurityTrivy scan, Dockle CIS hardening, Cosign signing

Build flow: build-opa-image.sh --push --scan --harden → builds from digest-pinned base, scans for vulnerabilities, verifies CIS compliance, pushes to Artifactory.

IAM requirement: CaaS clusters need imagePullSecrets configured to pull from this registry. This is pending IAM setup.

No CLI module directly publishes to Artifactory — it is used by the CI/CD pipeline (cargo publish) and as a dependency source during cargo build.

ConsumerUsage
GitHub Actions (deploy.yml)Publishes crates, builds and pushes OPA image
All CLI modulesResolve dependencies from remote proxy
CaaS (Kubernetes)Pulls OPA image for policy evaluation pods

Script: scripts/access/check-artifactory.sh

Required tools: curl, jq

Checks performed:

  1. Artifactory system ping (/artifactory/api/system/ping)
  2. Authenticated access with bearer token (if ARTIFACTORY_TOKEN is set)
  3. Cargo registry sparse index (/artifactory/api/cargo/{repo}/index/config.json)
  4. Remote repo accessibility (crates.io proxy)
  5. Deploy permissions via storage API

Artifactory uses bearer token authentication:

Terminal window
curl -H "Authorization: Bearer $ARTIFACTORY_TOKEN" \
https://eu.repository.roche.com/artifactory/api/system/ping

For Cargo integration, configure ~/.cargo/credentials.toml:

[registries.roche]
token = "Bearer <ARTIFACTORY_TOKEN>"

And .cargo/config.toml (already in repo):

[registries.roche]
index = "sparse+https://eu.repository.roche.com/artifactory/api/cargo/rdtmodel-general-cargo-all-l/index/"
[source.crates-io]
replace-with = "roche-remote"
[source.roche-remote]
registry = "sparse+https://eu.repository.roche.com/artifactory/api/cargo/rdtmodel-general-cargo-all-r/index/"
RepositoryMRMergedApproved by
Cargo (local + remote)#28112026-05-07Malgorzata Tobiasz
Docker (local)#28502026-05-07Malgorzata Tobiasz
StepStatus
Subscribe to GLOARTI_selfservice CIDM groupDone
Submit MR for local + remote Cargo reposDone (MR #2811)
Submit MR for Docker local repoDone (MR #2850)
Token generated and stored in VaultDone
.cargo/config.toml configuredDone
~/.cargo/credentials.toml configuredDone
CI/CD workflow updatedDone
OPA Docker image build scriptDone
IAM / imagePullSecret for CaaSPending
First OPA image pushedPending
First crate publishedPending

All Artifactory repositories are managed via GitOps in the Artifactory as Code project. This section documents the exact process, including lessons learned from our Cargo and Docker provisioning.

  1. Join the GLOARTI_selfservice CIDM group via Corporate Identity Management
  2. Log in to code.roche.com to sync permissions
  3. Log in to eu.repository.roche.com so your user account is created in Artifactory (required before you can be added to permission targets)

Our instance is eu.repository.roche.com. Always check the README module version table for the current version. As of 2026-05-07, it is 1.26.3.

Repository names follow: $project_code_name-$repo_name-$type-$repo_maturity-$location

FieldOur valueNotes
project_code_namerdtmodelShort identifier for the project
repo_namegeneralUse “general” unless you need multiple repos of the same type
$typeDetermined by module source pathcargo, docker, generic, etc.
repo_maturityallOptions: dev, tst, prd, all. Use all for single-repo setups
$locationDetermined by module source pathl = local, r = remote, v = virtual

File names MUST match the module name (without .tf extension). Max 52 characters for the full repo name.

Use relative paths with the correct version:

source = "./modules/1.26.3/repositories/<type>/<location>/"
Package typeLocalRemoteVirtualNotes
Cargocargo/localcargo/remoteNot supportedNo virtual — use source replacement in .cargo/config.toml
Dockerdocker_v2/local/docker/remote/docker/virtualUse docker_v2 for local (current standard on eu.repository.roche.com)
Genericgeneric/localgeneric/remotegeneric/virtualFor OPA bundles or arbitrary files
  1. Create a branch with clean naming: add-eu.repository.roche.com-repositories-<project>-<type>

  2. Add repository terraform files under instances/eu.repository.roche.com/repositories/:

    Cargo local (rdtmodel-general-cargo-all-l.tf):

    module "rdtmodel-general-cargo-all-l" {
    source = "./modules/1.26.3/repositories/cargo/local/"
    project_code_name = "rdtmodel"
    repo_name = "general"
    repo_maturity = "all"
    repo_description = "Roche Data Platform CLI — published Rust crates"
    repo_enable_sparse_index = true
    }

    Cargo remote (rdtmodel-general-cargo-all-r.tf):

    module "rdtmodel-general-cargo-all-r" {
    source = "./modules/1.26.3/repositories/cargo/remote/"
    project_code_name = "rdtmodel"
    repo_name = "general"
    repo_maturity = "all"
    repo_description = "Proxy and cache for crates.io dependencies"
    repo_url = "https://github.com/rust-lang/crates.io-index"
    repo_git_registry_url = "https://github.com/rust-lang/crates.io-index"
    repo_enable_sparse_index = true
    }

    Docker local (rdtmodel-general-docker-all-l.tf):

    module "rdtmodel-general-docker-all-l" {
    source = "./modules/1.26.3/repositories/docker_v2/local/"
    project_code_name = "rdtmodel"
    repo_name = "general"
    repo_maturity = "all"
    repo_description = "Roche Data Platform — OPA policy engine container images"
    }
  3. Add a permission target under instances/eu.repository.roche.com/accesses/:

    Permission (rdtmodel-docker-perm.tf):

    module "rdtmodel-docker-perm" {
    source = "./modules/1.26.3/permissions"
    perm_name = "rdtmodel-docker-perm"
    perm_repo_users_actions = [
    { username = "rxpmode1", permissions = ["read", "annotate", "write", "delete"] },
    { username = "rxpmode2", permissions = ["read", "annotate", "write", "delete"] },
    { username = "streits", permissions = ["read", "annotate", "write", "delete"] },
    ]
    perm_repo_repositories = [
    module.rdtmodel-general-docker-all-l.output_repo_key,
    ]
    }

    Permission targets reference repos via module.<name>.output_repo_key. Only local and remote repos can be in perm_repo_repositories — virtual repos inherit permissions from their included repos.

  4. Create a Merge Request to main. Tag @artifactory-approvers in a comment. Assign a reviewer from the Artifactory team (e.g. tobiaszm).

  5. Wait for review and merge. The team applies changes to production after approval.

These are mistakes we made in MR #2811 that caused review pushback:

MistakeWhat happenedFix
Requested virtual Cargo repoArtifactory as Code does not support repositories/cargo/virtualRemoved virtual repo; use Cargo source replacement instead
Wrong remote URLUsed https://static.crates.io/crates which returns AccessDeniedChanged to https://github.com/rust-lang/crates.io-index (publicly accessible)
Remote URL not publicly accessibleArtifactory needs to reach the remote URL from its serversAlways verify the URL is publicly reachable before submitting

General rules:

  • Not all package types support all repo types (local/remote/virtual). Check existing repos on the instance for precedent.
  • Remote repos require a publicly accessible repo_url that Artifactory’s servers can reach.
  • Docker local repos use docker_v2/local/ (not docker/local/) on eu.repository.roche.com as of 2026-05.
  • Keep the MR minimal — one concern per MR. Our Docker MR (#2850) passed review on the first attempt with just 2 files.
  • Local copies of the terraform files are stored in platforms/artifactory/terraform/ for reference.
MRContentBranchStatus
#2811Cargo local + remote repos + permissionadd-eu.repository.roche.com-repositories-rdtmodel-general-cargoMerged 2026-05-07
#2850Docker local repo + permissionadd-eu.repository.roche.com-repositories-rdtmodel-general-dockerMerged 2026-05-07