Skip to content

jfrog/package-reroute

Repository files navigation

Certificate installation scripts

Scripts to install a CA certificate, configure Node/npm, Python (pip, uv, Hugging Face Hub, and related TLS clients), Ruby where supported, and clear Docker Hub credentials that can break redirected Docker Hub pulls.

This document describes the certificate installation and validation scripts for macOS, Linux (Debian/Ubuntu), and Windows.

Script Platform Purpose
install_certs_macos.sh macOS Install cert, set env vars (Node/Python), and clear Docker Hub credentials
validate_install_macos.sh macOS Validate PEM and env config
install_certs_debian_ubuntu.sh Debian/Ubuntu Install cert into system trust + profile.d + user shell rc + Docker cleanup
validate_certs_debian_ubuntu.sh Debian/Ubuntu Validate PEM and env config
install_certs_windows.ps1 Windows Install cert, set env vars (Node/Python/Ruby), and clear Docker Hub credentials
validate_install_windows.ps1 Windows Validate PEM and env config

Environment variables by platform (see each section for details):

Variable Typical use Notes
NODE_USE_SYSTEM_CA=1 Node/npm macOS, Debian, Windows when npm is configured
NODE_EXTRA_CA_CERTS=<path> Node/npm PEM path (bundle allowed)
UV_NATIVE_TLS=true / 1 Python uv macOS uses true; Windows uses 1; not set by the Debian/Ubuntu script
UV_SYSTEM_CERTS=true Python uv Set by macOS and Windows for python, huggingface, or all
REQUESTS_CA_BUNDLE=<path> Python requests / many HTTPS stacks PEM or bundle path
SSL_CERT_FILE=<path> OpenSSL-backed tools, including Ruby Set on Debian/Ubuntu for python, huggingface, or all to the system CA bundle; on Windows for python, huggingface, ruby, or all to the same PEM as other cert vars (package-route.pem or -UseCert path)
HF_HUB_DISABLE_XET=1 Python huggingface_hub Set when huggingface or all: disables XET (not supported with typical MITM / Artifactory redirect flows)
HF_HUB_ETAG_TIMEOUT=86400 Python huggingface_hub Set when huggingface or all: ETag check timeout (seconds); reduces spurious failures on slow paths
HF_HUB_DOWNLOAD_TIMEOUT=86400 Python huggingface_hub Set when huggingface or all: download timeout (seconds)

Docker Hub credential cleanup

Each install script includes a best-effort cleanup for Docker Hub credentials. This fixes environments where corporate egress redirects registry-1.docker.io to JFrog Artifactory: locally stored Docker Hub credentials can otherwise be sent to JFrog's token endpoint and produce Bad Credentials.

The cleanup attempts docker logout for these Docker Hub key forms:

https://index.docker.io/v1/
index.docker.io
docker.io
https://registry-1.docker.io/
registry-1.docker.io

If Docker CLI is unavailable, the scripts skip this step. The cleanup is idempotent, logs warnings on logout failures, and never aborts the certificate install.

The cleanup runs in the user context where possible because Docker credentials and credential stores are user-scoped:

  • macOS: uses SUDO_USER when present; under JAMF/root execution, falls back to the console user from /dev/console and runs through launchctl asuser.
  • Debian/Ubuntu: uses SUDO_USER, then logname, then a best-effort active loginctl session fallback.
  • Windows: runs for the current PowerShell user. If the script runs as SYSTEM, Docker cleanup is skipped with a warning because the user's Docker credential store is not accessible from that context.

Known limitation: Docker Desktop GUI sign-in can recreate CLI credentials later. If a user signed in through Docker Desktop, sign out in the Docker Desktop UI as well.


macOS: install_certs_macos.sh

Overview

install_certs_macos.sh configures Node/npm and/or Python on macOS to use a custom CA certificate (e.g. for corporate proxy or package routing). It:

  • Runs only as root (e.g. sudo).
  • Either exports a full PEM bundle from macOS system Keychains, or uses an existing PEM file you provide.
  • For each user in /Users/*, writes or updates the certificate file and sets environment variables in that user’s ~/.zshrc so Node and Python use the certificate.
  • Clears Docker Hub credentials for the target non-root user, when Docker is installed.

With Keychain export, each user gets package-route.pem under ~/<extract-path>/. The bundle includes Apple's system roots and enterprise CAs from /Library/Keychains/System.keychain. With --use-cert, the install uses your PEM path as-is for every user.

Requirements

  • macOS (script uses security for Keychain export when --extract-path is used).
  • Root (script exits with an error and suggests sudo if not root).
  • openssl on PATH. Optional: use --install-dependencies to install it via Homebrew in the same run if missing.
  • When using --extract-path: the security (Keychain) tool must be available (system tool; /usr/bin is prepended to PATH by the script).

How to use

Basic usage

sudo ./install_certs_macos.sh [OPTIONS]

Options

Option Required Description
--package <npm|python|huggingface|all> No (default: all) npm (Node only), python (Python TLS: uv, requests—no Hugging Face Hub vars), huggingface (Python TLS + HF_HUB_*), all (npm + python + Hugging Face Hub).
--extract-path <path> Yes* Directory under each user’s home where package-route.pem is written: ~/<path with leading / stripped>/package-route.pem (e.g. opt/certs~/opt/certs/..., certs~/certs/...).
--use-cert <path> Yes* Use this existing PEM file instead of exporting from Keychain. Cannot be used with --extract-path.
--install-dependencies No If openssl is missing, install it via Homebrew and continue in the same run.
-h, --help Print usage and exit.

* You must use either --extract-path or --use-cert, not both and not neither.

Examples

1. Export Keychain bundle and configure npm + Python for all users

PEM is written under each user’s home (e.g. ~/opt/certs/package-route.pem for --extract-path /opt/certs) and each user’s .zshrc is updated:

sudo ./install_certs_macos.sh \
  --package all \
  --extract-path /opt/certs

2. Use an existing PEM file (e.g. from IT)

No Keychain access; same PEM path is set for every user:

sudo ./install_certs_macos.sh \
  --package all \
  --use-cert /opt/certs/company-ca.pem

3. Only configure Python TLS (UV_NATIVE_TLS, REQUESTS_CA_BUNDLE; no HF_HUB_*)

sudo ./install_certs_macos.sh \
  --package python \
  --extract-path certs

With a relative --extract-path, each user gets their own file, e.g. /Users/jane/certs/package-route.pem.

4. Python TLS + Hugging Face Hub (huggingface; same TLS vars plus HF_HUB_*)

sudo ./install_certs_macos.sh \
  --package huggingface \
  --extract-path certs

5. Install openssl if missing, then run (single run)

sudo ./install_certs_macos.sh \
  --install-dependencies \
  --package all \
  --extract-path /opt/certs

Install validation

validate_install_macos.sh checks that the certificate installation succeeded: PEM file(s) exist and are valid (via openssl x509). --expected-subject is required for every invocation. It does not require root unless you use --all-users.

Option Description
--expected-subject <pattern> Required. At least one cert in each PEM file (bundle) must have a subject matching <pattern> (case-insensitive).
(default scope) Read NODE_EXTRA_CA_CERTS and REQUESTS_CA_BUNDLE from the current user’s ~/.zshrc (with ~ expanded), then validate each referenced PEM file. UV_NATIVE_TLS is not validated.
--all-users (Root only.) For each user in /Users/*, read their ~/.zshrc, resolve cert paths, and validate each PEM. Use: sudo ./validate_install_macos.sh --expected-subject <pattern> --all-users.

Requirements: openssl on PATH (same paths as the install script are prepended).

Exit code: 0 if all checks pass, 1 if any check fails.

Use a substring from your CA subject as <ca-subject-pattern> (find it with openssl x509 -in <pem> -noout -subject).

# After install: validate current user’s config and cert path(s)
./validate_install_macos.sh --expected-subject "<ca-subject-pattern>"

# Validate every user’s config (run as root)
sudo ./validate_install_macos.sh --expected-subject "<ca-subject-pattern>" --all-users

Testing

Tests live in testing/. Automated tests cover macOS and Windows only (not Debian/Ubuntu).

test_install_certs_macos.sh runs automated tests for install_certs_macos.sh (CLI and argument validation) and validate_install_macos.sh (validation with a temp PEM and mock home). No root required for the default test run.

Requirements: openssl on PATH (for generating a temporary cert in tests).

# From repo root
./testing/test_install_certs_macos.sh

# Or from repo root with testing as current dir
cd testing && ./test_install_certs_macos.sh

Exit code 0 if all tests pass, 1 otherwise.

Test coverage

Area Covered Not covered
install_certs_macos.sh CLI and pre-root: --help; unknown option; invalid --package; no cert source; --use-cert + --extract-path conflict; --use-cert with missing file; non-root exit and message. --use-cert: valid PEM path and --package npm/python/huggingface (non-root → run as root); invalid PEM content rejected with "Invalid or missing PEM" when run as root (tested when passwordless sudo available). Post-root: PATH/openssl, --install-dependencies (Homebrew); Keychain export; per-user loop; Docker credential cleanup; writing PEM and updating .zshrc. Requires root and/or Keychain; not run in CI.
validate_install_macos.sh CLI: unknown option (exit 1); missing --expected-subject (exit 1). Main paths: default with mock HOME and .zshrc; missing PEM in .zshrc (exit 1); --all-users without root (exit 1). Covers validate_pem, get_export_path, validate_user_config. Multi-cert bundle in validate_pem; --all-users as root.

Tests are black-box (exit codes and stderr).

Windows tests

test_install_certs_windows.ps1 runs automated tests for install_certs_windows.ps1 (CLI and parameter validation; -ExtractPath full trust-store export; -UseCert -Package python sets TLS Machine vars to the supplied PEM and leaves existing HF_HUB_* unchanged; -Package ruby sets SSL_CERT_FILE; -Package huggingface adds HF_HUB_*; -Package all sets npm + TLS + Ruby + HF; when run as admin) and validate_install_windows.ps1 (-ExpectedSubject required, env-based validation: valid PEM, missing file, invalid PEM, subject match and no-match, and system-level env when run as admin). Run the test script as Administrator so install script tests and system-level validate tests execute; the script uses a temp directory and an embedded PEM.

Requirements: Windows with PowerShell. The install and validate scripts must be in the parent of testing/ (repo root).

# From repo root (PowerShell on Windows, as Administrator)
powershell -NoProfile -ExecutionPolicy Bypass -File testing/test_install_certs_windows.ps1

From a non-Windows host you can run the tests on a Windows VM via SSH (e.g. copy the scripts and invoke the same command over ssh jump-windows).

Exit code 0 if all tests pass, 1 otherwise. Output shows pass/fail per test and a final count.

Area Covered
install_certs_windows.ps1 When run as admin: script passes admin check (no "must run as Administrator" error). No cert source; invalid -Package; -UseCert and -ExtractPath together; -ExtractPath full trust-store export; -UseCert with nonexistent file; -UseCert with invalid PEM; -UseCert with valid PEM (no "not a file" or "Invalid PEM" error). Packages: -UseCert -Package python sets TLS-only Machine vars to the supplied PEM and does not remove pre-existing HF_HUB_*; -Package ruby sets SSL_CERT_FILE to the supplied PEM; -Package huggingface adds HF_HUB_*; -Package all sets npm + TLS + Ruby + HF, all pointing at the supplied PEM.
validate_install_windows.ps1 -ExpectedSubject required (exit 1 if missing); current user env (no paths → exit 0); env path to valid PEM (exit 0), missing file (exit 1), invalid PEM (exit 1); subject mismatch (exit 1, FAIL message); system-level (Machine) env when run as admin. Reads NODE_EXTRA_CA_CERTS, REQUESTS_CA_BUNDLE, and SSL_CERT_FILE.

Tests are black-box (exit codes and stdout/stderr). Paths are passed to the validate script via a temp file when invoking as a child process to avoid command-line parsing issues with backslashes.


Logic in detail

1. Argument handling

  • --package defaults to all if omitted; must be npm, python, huggingface, or all (all = npm + Python TLS + Hugging Face Hub).
  • Cert source is one of:
    • Keychain export: --extract-path set; --use-cert must not be set.
    • Use file: --use-cert set; --extract-path must not be set.
  • Script exits with an error if:
    • Both --extract-path and --use-cert are used, or
    • Neither cert source is provided.

2. Root and dependencies

  • Script must run as root; otherwise it prints an error and suggests sudo $0 [options].
  • Prepends /usr/bin and common Homebrew paths to PATH so openssl and security are found.
  • If --install-dependencies is set and openssl is not on PATH:
    • Tries Homebrew (/opt/homebrew/bin/brew or /usr/local/bin/brew).
    • Runs brew install openssl, then adds the new openssl to PATH and continues in the same run.
  • If openssl is still missing after that (or without the flag), script exits with an error.
  • If cert source is Keychain export (--extract-path), script checks that security is available; if not, it exits (system tool, cannot be installed).

3. Certificate source

  • --use-cert: Validates the file with openssl x509 -noout and uses it as the certificate for all users. No Keychain access.
  • --extract-path:
    • Exports all trusted root CAs from SystemRootCertificates.keychain and System.keychain.
    • Writes the full bundle to each user's package-route.pem.

4. Per-user loop

For each directory in /Users/* (skipping Shared and non-directories):

  • Cert file path:
    • If --use-cert: use that path for every user.
    • If --extract-path: use <homedir>/<extract-path with leading / stripped>/package-route.pem (e.g. /opt/certs~/opt/certs/package-route.pem, certs~/certs/package-route.pem). Script creates the directory, writes the PEM, and chowns to that user.
  • For each user, the script creates ~/.zshrc if needed, then calls add_exports_to_file with that file and the user’s cert path. If ~/.zshrc is a directory, it skips that user with a warning.

5. Docker Hub credential cleanup

After cert and shell config updates, the script runs Docker Hub credential cleanup as the target non-root user (SUDO_USER, or the console user under JAMF). It uses docker logout when Docker is available; otherwise it skips the cleanup.

6. add_exports_to_file (per user, per shell file)

For npm (if --package is npm or all):

  • Ensure NODE_USE_SYSTEM_CA=1.
  • Add or replace NODE_EXTRA_CA_CERTS so it points at the selected cert path.

For Python TLS (if --package is python, huggingface, or all):

  • Ensure UV_NATIVE_TLS=true and UV_SYSTEM_CERTS=true.
  • Add or replace REQUESTS_CA_BUNDLE so it points at the selected cert path.

For Hugging Face Hub (if --package is huggingface or all):

  • Ensure HF_HUB_DISABLE_XET=1, HF_HUB_ETAG_TIMEOUT=86400, HF_HUB_DOWNLOAD_TIMEOUT=86400 (same add/replace/leave-as-is behavior via ensure_export). With python only, those lines are not added or updated; any existing exports in .zshrc are left unchanged (so user or prior-run values are not stripped).

Flowchart

install_certs_macos.sh flowchart


Summary (macOS)

  • One run as root (optionally with --install-dependencies to install openssl).
  • One cert source: either Keychain export (--extract-path) or existing file (--use-cert).
  • Per user: PEM at ~/<extract-path>/package-route.pem for each user (leading / on --extract-path is stripped); env vars in ~/.zshrc point to that path. With --use-cert, the same PEM path is used for every user.
  • If user already had a different env path: script replaces it with the selected cert path.
  • Docker: clears Docker Hub credentials for the target user if Docker is installed.

Users must open a new terminal (or source ~/.zshrc) for the new environment variables to take effect.


Linux (Debian/Ubuntu): install_certs_debian_ubuntu.sh

Overview

install_certs_debian_ubuntu.sh installs a PEM/CRT into the Debian/Ubuntu system trust store (update-ca-certificates), writes a managed file under /etc/profile.d/package-route-certs.sh, and updates the target non-root user’s shell rc (~/.zshrc or ~/.bashrc, depending on their login shell). It only supports an existing certificate file (--use-cert); there is no Keychain or cert-store extraction on Linux in this repo.

  • npm: NODE_USE_SYSTEM_CA=1 and NODE_EXTRA_CA_CERTS pointing at the installed cert under /usr/local/share/ca-certificates/ (default basename package-route-custom-ca.crt, overridable with --cert-name).
  • Python TLS: REQUESTS_CA_BUNDLE and SSL_CERT_FILE point at the system CA bundle (/etc/ssl/certs/ca-certificates.crt), which includes your CA after update-ca-certificates. UV_NATIVE_TLS is not set (unlike macOS/Windows Python flows). HF_HUB_* are set only for huggingface or all; with python only, existing HF_HUB_* lines in the user’s ~/.bashrc / ~/.zshrc are not removed.
  • Docker: best-effort Docker Hub credential cleanup runs for the target non-root user.

Requirements

  • Debian or Ubuntu (script checks /etc/os-release).
  • Root (sudo).
  • openssl and update-ca-certificates on PATH.
  • Optional: Docker CLI for Docker Hub credential-store cleanup. If Docker CLI is missing, the cleanup step is skipped.

Options

Option Required Description
--use-cert <path> Yes Path to an existing PEM/CRT file.
--package npm|python|huggingface|all No (default: all) What to configure.
--cert-name <name> No (default: package-route-custom-ca) Base name for the file installed under /usr/local/share/ca-certificates/<name>.crt (not a Keychain/subject pattern).
-h, --help Usage.

Examples

sudo ./install_certs_debian_ubuntu.sh --use-cert /tmp/company-ca.pem
sudo ./install_certs_debian_ubuntu.sh --use-cert /tmp/company-ca.pem --package npm
sudo ./install_certs_debian_ubuntu.sh --use-cert /tmp/company-ca.pem --package huggingface
sudo ./install_certs_debian_ubuntu.sh --use-cert /tmp/company-ca.pem --cert-name my-org-ca

Validation: validate_certs_debian_ubuntu.sh

--expected-subject is required. Checks PEM paths from the current user’s ~/.bashrc / ~/.zshrc and, when present, /etc/profile.d/package-route-certs.sh (NODE_EXTRA_CA_CERTS, REQUESTS_CA_BUNDLE, SSL_CERT_FILE). With --all-users (root only), validates /home/* users’ rc files.

./validate_certs_debian_ubuntu.sh --expected-subject "O=Example"
sudo ./validate_certs_debian_ubuntu.sh --all-users --expected-subject "O=Example"

Windows: install_certs_windows.ps1

Overview

install_certs_windows.ps1 configures Node/npm, Python, and/or Ruby on Windows to use trusted CAs for redirect-proxy / corporate TLS flows. It must be run as Administrator (or SYSTEM); the script exits with an error otherwise.

Behavior matches install_certs_macos.sh (full trust-store PEM export, or use an existing PEM):

  • With -ExtractPath: exports all certificates from Windows trust stores into package-route.pem per user under each profile (~/<ExtractPath>/package-route.pem). Stores exported: Trusted Root (LocalMachine\Root, CurrentUser\Root), Third-Party Root (LocalMachine\AuthRoot), and Intermediate (LocalMachine\CA, CurrentUser\CA). Includes enterprise roots/intermediates (e.g. NetSkope) plus public CAs. Sets User-level env vars in the registry for each user. Does not modify System (Machine) cert env vars.
  • With -UseCert: points all cert env vars at your existing PEM file (no store export, no separate bundle). Sets Machine-level env vars when run as admin and removes overlapping User-level cert vars so Machine settings apply (User wins over Machine on Windows).

Env vars set (same PEM path for all packages in each mode):

Package Variables
npm NODE_USE_SYSTEM_CA, NODE_EXTRA_CA_CERTS
python / huggingface / all UV_NATIVE_TLS, UV_SYSTEM_CERTS, REQUESTS_CA_BUNDLE, SSL_CERT_FILE
ruby / all SSL_CERT_FILE
huggingface / all HF_HUB_DISABLE_XET, HF_HUB_ETAG_TIMEOUT, HF_HUB_DOWNLOAD_TIMEOUT

Clears Docker Hub credentials for the current PowerShell user. When running as SYSTEM, this cleanup is skipped with a warning because it must run in the user's Windows session.

Requirements

  • Windows with PowerShell.
  • Run as Administrator (or SYSTEM). The script checks and exits with an error if not elevated.
  • When using -ExtractPath: at least one certificate must exist in the exported trust stores.
  • Optional: Docker Desktop / Docker CLI for Docker Hub credential-store cleanup. If Docker CLI is missing, the cleanup step is skipped.

How to use

Run from a directory that contains the script (or use full path):

powershell -ExecutionPolicy Bypass -File install_certs_windows.ps1 -Package all -ExtractPath certs\jfrog
# Or use an existing PEM:
powershell -ExecutionPolicy Bypass -File install_certs_windows.ps1 -Package all -UseCert C:\path\to\ca.pem
Parameter Required Description
-Package No (default: all) npm, python, huggingface, ruby, or all (all = npm + Python TLS + Ruby + Hugging Face Hub).
-ExtractPath Yes* Directory under each user's profile for package-route.pem (rooted paths are normalized to a folder under the profile, same idea as macOS). Full trust-store export. Cannot be used with -UseCert.
-UseCert Yes* Path to an existing PEM file. Cannot be used with -ExtractPath.

* Use either -ExtractPath or -UseCert (exactly one).

Examples

Extract trust store and configure all users (run as admin):

.\install_certs_windows.ps1 -Package all -ExtractPath certs\jfrog

Use an existing PEM (Machine-level env; User-level cert vars are removed):

.\install_certs_windows.ps1 -Package all -UseCert C:\Users\Administrator\other-ca\company-ca.pem

Only npm:

.\install_certs_windows.ps1 -Package npm -ExtractPath certs\jfrog

Summary (Windows)

  • Admin required. The script must be run as Administrator (or SYSTEM).
  • Cert source: either full store export (-ExtractPath) or file (-UseCert).
  • Extract path: per-user package-route.pem (multi-cert bundle) and User-level env per -Package. System (Machine) cert env vars are not set or cleared.
  • UseCert: all cert env vars point at the supplied PEM; Machine-level env when run as admin; python does not set HF_HUB_* and does not clear pre-existing HF_HUB_* on the target scope. User-level cert vars are removed so Machine applies.
  • Docker: clears Docker Hub credentials for the current PowerShell user. Run the script in the user's session for this step; SYSTEM cannot clean up the user's Docker credential store.

Users must start a new terminal for env changes to take effect.

After install, verify with validate_install_windows.ps1 -ExpectedSubject (see below) to confirm your enterprise CA subject appears in the bundle.


Windows: validate_install_windows.ps1

validate_install_windows.ps1 checks that the certificate installation is valid: PEM file(s) exist and are valid (same validation as the install script). -ExpectedSubject is required for every invocation. It does not require admin unless you use -AllUsers.

Parameter Description
-ExpectedSubject <pattern> Required. At least one cert in each PEM file (bundle) must have a subject matching <pattern> (case-insensitive).
(default scope) Read NODE_EXTRA_CA_CERTS, REQUESTS_CA_BUNDLE, and SSL_CERT_FILE from the current user's environment (User then Machine), then validate each referenced PEM file.
-AllUsers (Admin only.) For each user in C:\Users\*, read their User registry env, resolve cert paths, and validate each PEM.

Exit code: 0 if all checks passed, 1 if any check failed.

Use a substring from your CA subject as <ca-subject-pattern> (find it with openssl x509 -in <pem> -noout -subject).

# After install: validate current user's env and cert path(s)
.\validate_install_windows.ps1 -ExpectedSubject "<ca-subject-pattern>"

# Validate every user's config (run as Administrator)
.\validate_install_windows.ps1 -ExpectedSubject "<ca-subject-pattern>" -AllUsers

Continuous integration

On push and pull request to main or master, GitHub Actions runs:

Job Runner Command
Test (macOS) macos-latest sudo ./testing/test_install_certs_macos.sh
Test (Windows) windows-latest ./testing/test_install_certs_windows.ps1 (PowerShell)

There is no CI job for the Debian/Ubuntu scripts in this workflow.

About

No description, website, or topics provided.

Resources

License

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors