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.
Connection Details
Section titled “Connection Details”| Property | Value |
|---|---|
| URL | https://eu.repository.roche.com |
| Auth method | Bearer token (Artifactory access token) |
| Network | Roche corporate network (VPN or direct) |
| Access task | A16 |
| GitHub issue | #165 |
| CIDM group | GLOARTI_selfservice |
Environment Variables
Section titled “Environment Variables”| Variable | Source | Description |
|---|---|---|
ARTIFACTORY_URL | Vault common/artifactory | Base URL of the Artifactory instance |
ARTIFACTORY_USER | Vault common/artifactory | Artifactory service user |
ARTIFACTORY_CARGO_REGISTRY | Vault common/artifactory | Local Cargo repo for publishing (rdtmodel-general-cargo-all-l) |
ARTIFACTORY_CARGO_REMOTE | Vault common/artifactory | Remote Cargo repo for crates.io caching (rdtmodel-general-cargo-all-r) |
ARTIFACTORY_TOKEN | Vault common/artifactory | Bearer token for authenticated API access |
ARTIFACTORY_TOKEN_EXPIRE | Vault common/artifactory | Token expiry date (2027-05-05) |
OPA_IMAGE | Vault {env}/opa | Full OPA image reference (e.g. rdtmodel-general-docker-all-l.eu.repository.roche.com/opa:1.16.1) |
Repositories
Section titled “Repositories”| Type | Name | Purpose | MR |
|---|---|---|---|
| Local (Cargo) | rdtmodel-general-cargo-all-l | Store published rdt-model crates | #2811 |
| Remote (Cargo) | rdtmodel-general-cargo-all-r | Proxy/cache crates.io dependencies | #2811 |
| Local (Docker) | rdtmodel-general-docker-all-l | Hardened 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.
OPA Docker Image
Section titled “OPA Docker Image”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.
| Property | Value |
|---|---|
| Repository | rdtmodel-general-docker-all-l |
| Image path | eu.repository.roche.com/rdtmodel-general-docker-all-l/rdt-model/opa |
| Build script | scripts/build-opa-image.sh |
| Version file | k8s/OPA_VERSION (version + digest) |
| Vault config | secret/{env}/opa → OPA_IMAGE |
| Security | Trivy 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.
CLI Modules
Section titled “CLI Modules”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.
| Consumer | Usage |
|---|---|
GitHub Actions (deploy.yml) | Publishes crates, builds and pushes OPA image |
| All CLI modules | Resolve dependencies from remote proxy |
| CaaS (Kubernetes) | Pulls OPA image for policy evaluation pods |
Access Verification
Section titled “Access Verification”Script: scripts/access/check-artifactory.sh
Required tools: curl, jq
Checks performed:
- Artifactory system ping (
/artifactory/api/system/ping) - Authenticated access with bearer token (if
ARTIFACTORY_TOKENis set) - Cargo registry sparse index (
/artifactory/api/cargo/{repo}/index/config.json) - Remote repo accessibility (crates.io proxy)
- Deploy permissions via storage API
Authentication
Section titled “Authentication”Artifactory uses bearer token authentication:
curl -H "Authorization: Bearer $ARTIFACTORY_TOKEN" \ https://eu.repository.roche.com/artifactory/api/system/pingFor 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/"Provisioning Status
Section titled “Provisioning Status”| Repository | MR | Merged | Approved by |
|---|---|---|---|
| Cargo (local + remote) | #2811 | 2026-05-07 | Malgorzata Tobiasz |
| Docker (local) | #2850 | 2026-05-07 | Malgorzata Tobiasz |
| Step | Status |
|---|---|
Subscribe to GLOARTI_selfservice CIDM group | Done |
| Submit MR for local + remote Cargo repos | Done (MR #2811) |
| Submit MR for Docker local repo | Done (MR #2850) |
| Token generated and stored in Vault | Done |
.cargo/config.toml configured | Done |
~/.cargo/credentials.toml configured | Done |
| CI/CD workflow updated | Done |
| OPA Docker image build script | Done |
| IAM / imagePullSecret for CaaS | Pending |
| First OPA image pushed | Pending |
| First crate published | Pending |
How to Provision New Repositories
Section titled “How to Provision New Repositories”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.
Prerequisites
Section titled “Prerequisites”- Join the
GLOARTI_selfserviceCIDM group via Corporate Identity Management - Log in to code.roche.com to sync permissions
- Log in to eu.repository.roche.com so your user account is created in Artifactory (required before you can be added to permission targets)
Instance and Module Version
Section titled “Instance and Module Version”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.
Naming Convention
Section titled “Naming Convention”Repository names follow: $project_code_name-$repo_name-$type-$repo_maturity-$location
| Field | Our value | Notes |
|---|---|---|
project_code_name | rdtmodel | Short identifier for the project |
repo_name | general | Use “general” unless you need multiple repos of the same type |
$type | Determined by module source path | cargo, docker, generic, etc. |
repo_maturity | all | Options: dev, tst, prd, all. Use all for single-repo setups |
$location | Determined by module source path | l = local, r = remote, v = virtual |
File names MUST match the module name (without .tf extension). Max 52 characters for the full repo name.
Module Source Paths
Section titled “Module Source Paths”Use relative paths with the correct version:
source = "./modules/1.26.3/repositories/<type>/<location>/"| Package type | Local | Remote | Virtual | Notes |
|---|---|---|---|---|
| Cargo | cargo/local | cargo/remote | Not supported | No virtual — use source replacement in .cargo/config.toml |
| Docker | docker_v2/local/ | docker/remote/ | docker/virtual | Use docker_v2 for local (current standard on eu.repository.roche.com) |
| Generic | generic/local | generic/remote | generic/virtual | For OPA bundles or arbitrary files |
Step-by-Step Process
Section titled “Step-by-Step Process”-
Create a branch with clean naming:
add-eu.repository.roche.com-repositories-<project>-<type> -
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"} -
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 inperm_repo_repositories— virtual repos inherit permissions from their included repos. -
Create a Merge Request to
main. Tag@artifactory-approversin a comment. Assign a reviewer from the Artifactory team (e.g.tobiaszm). -
Wait for review and merge. The team applies changes to production after approval.
Pitfalls and Lessons Learned
Section titled “Pitfalls and Lessons Learned”These are mistakes we made in MR #2811 that caused review pushback:
| Mistake | What happened | Fix |
|---|---|---|
| Requested virtual Cargo repo | Artifactory as Code does not support repositories/cargo/virtual | Removed virtual repo; use Cargo source replacement instead |
| Wrong remote URL | Used https://static.crates.io/crates which returns AccessDenied | Changed to https://github.com/rust-lang/crates.io-index (publicly accessible) |
| Remote URL not publicly accessible | Artifactory needs to reach the remote URL from its servers | Always 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_urlthat Artifactory’s servers can reach. - Docker local repos use
docker_v2/local/(notdocker/local/) oneu.repository.roche.comas 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.
Our Merge Requests
Section titled “Our Merge Requests”| MR | Content | Branch | Status |
|---|---|---|---|
| #2811 | Cargo local + remote repos + permission | add-eu.repository.roche.com-repositories-rdtmodel-general-cargo | Merged 2026-05-07 |
| #2850 | Docker local repo + permission | add-eu.repository.roche.com-repositories-rdtmodel-general-docker | Merged 2026-05-07 |