Skip to content

DFLOW-150 - JVM client setup (Linux)#8

Open
akushsky wants to merge 1 commit into
mainfrom
feature/DFLOW-150-jvm-linux
Open

DFLOW-150 - JVM client setup (Linux)#8
akushsky wants to merge 1 commit into
mainfrom
feature/DFLOW-150-jvm-linux

Conversation

@akushsky
Copy link
Copy Markdown
Collaborator

Summary

Adds a Linux client-side installer + validator pair that wires a corporate CA into the JVM trust path so Maven / Gradle / sbt / Apache Ivy traffic redirected through package-reroute validates correctly. JVM-only — does not configure Node/npm/Python.

Auto-detects between two lab-verified paths:

  • Path A — update-ca-trust for RHEL/Fedora/CentOS/Amazon-Linux when a JDK whose lib/security/cacerts is symlinked to the system store is on PATH (Red Hat OpenJDK).
  • Path B — JKS + JAVA_TOOL_OPTIONS for everything else (Debian/Ubuntu, Amazon Corretto, Eclipse Temurin, SDKMAN, manual JDKs).

Reporter's note in DFLOW-150 asked for a separate script; install_certs_jvm_linux.sh lives alongside the existing install_certs_debian_ubuntu.sh rather than extending it.

Based on the published research wiki: https://jfrog-int.atlassian.net/wiki/spaces/RTFACT/pages/2440101931

Files

  • install_certs_jvm_linux.sh — installer (~470 lines, mirrors install_certs_debian_ubuntu.sh patterns).
  • validate_certs_jvm_linux.sh — companion validator.
  • _jvm_linux_paths.sh — shared constants block sourced by both, so the installer and validator cannot drift.
  • testing/test_install_certs_jvm_linux.sh — Docker matrix runner.
  • .github/workflows/ci.yml — new test-linux-jvm job calls the matrix runner.
  • README.md — new "Linux (JVM)" section between Debian/Ubuntu and Windows.

Review

This PR was reviewed locally by 5 pr-review-toolkit agents (code-reviewer, silent-failure-hunter, type-design-analyzer, pr-test-analyzer, comment-analyzer) before push. All 4 Critical findings + 10 Important findings + most Suggestions were applied — see commit 3e14f60 for the consolidated fix-wave (which intentionally rewrites several functions touched by the earlier feat(jvm-linux): … commits to avoid making this PR an over-long diff to follow).

Test plan

  • Ubuntu 22.04 + default-jdk-headless — Path B detected, all 8 invariants pass
  • Debian 12 + default-jdk-headless — Path B detected, all 8 invariants pass
  • RHEL UBI 9 + java-21-openjdk-headless — Path A detected, all 8 invariants pass
  • Amazon Linux 2023 + Corretto — Path A or B depending on cacerts symlink state, all 8 invariants pass

Per-container invariants exercised by testing/test_install_certs_jvm_linux.sh:

  • positive install + validate (subject substring match against keystore)
  • subject mismatch → exit 1 (validator's core assertion not silently passing)
  • idempotent re-install (single JKS alias, single /etc/environment line, single user-rc export after N runs)
  • custom --cert-name round-trips through validator
  • path-traversal --cert-name rejected by regex [A-Za-z0-9._-]+
  • malformed PEM rejected (openssl x509 -noout)
  • expired CA rejected (openssl x509 -checkend 0)
  • leaf cert with CA:FALSE rejected (basicConstraints check)

Run locally:

./testing/test_install_certs_jvm_linux.sh

CI runs the same matrix on every push and PR via the new test-linux-jvm job.

🤖 Generated with Claude Code

@akushsky akushsky force-pushed the feature/DFLOW-150-jvm-linux branch 2 times, most recently from 8c5d152 to 6b3c960 Compare May 20, 2026 16:20
Adds a Linux client-side installer + validator that wires a corporate CA
into the JVM trust path so Maven / Gradle / sbt / Apache Ivy traffic
redirected through package-reroute validates correctly. JVM trust only —
does not configure Node/npm/Python and does not touch Docker credentials.
Pair with install_certs_debian_ubuntu.sh if you need those flows.

Based on the published research wiki (DFLOW-136):
  https://jfrog-int.atlassian.net/wiki/spaces/RTFACT/pages/2440101931

Two lab-verified paths, auto-detected:

  Path A — update-ca-trust
    RHEL/Fedora/CentOS/Amazon-Linux when a JDK whose lib/security/cacerts
    symlinks to /etc/pki/ca-trust/extracted/java/cacerts is on PATH
    (Red Hat OpenJDK). Drops the CA into /etc/pki/ca-trust/source/anchors/
    and runs `update-ca-trust extract`. No env var.

  Path B — JKS + JAVA_TOOL_OPTIONS
    Everything else (Debian/Ubuntu, Amazon Corretto, Eclipse Temurin,
    SDKMAN, manual .tar.gz installs). Builds a JKS at
    /etc/ssl/package-route-jvm/truststore.jks containing only the customer
    CA; writes JAVA_TOOL_OPTIONS to /etc/environment plus the developer
    user's .bashrc/.zshrc. JDK-version-agnostic by construction.

Detection rule (detect_mode):
  1. No update-ca-trust on PATH                 -> Path B
  2. /etc/pki/ca-trust/extracted/java/cacerts absent -> Path B
  3. No `java` on PATH                          -> Path A (assumes Red Hat
       OpenJDK will follow via dnf; emits a loud end-of-run warning)
  4. `java`'s cacerts symlinks to RHEL store    -> Path A; else Path B

--mode java-tool-options / --mode update-ca-trust overrides detection.

Files:
  install_certs_jvm_linux.sh      Installer (mirrors install_certs_debian_ubuntu.sh patterns).
  validate_certs_jvm_linux.sh     Companion validator; subject-substring match.
  _jvm_linux_paths.sh             Shared constants sourced by both scripts so they cannot drift.
  testing/test_install_certs_jvm_linux.sh   Docker matrix runner (4 distros x 10 invariants).
  .github/workflows/ci.yml        New test-linux-jvm job runs the matrix runner.
  README.md                       New "Linux (JVM)" section with the full design + caveats.

Hardening fixes folded in from two rounds of pr-review-toolkit review:

  Critical:
  - --cert-name actually propagates to the Path B JKS alias and the validator
    accepts --cert-name so Path A's anchor file can be located when a
    non-default basename was used.
  - validate_pem rejects expired certs, leaf certs (CA:FALSE), warns on
    bundles (keytool -importcert -noprompt only imports the first cert).
  - install_via_update_ca_trust and validate_keystore_contains_subject
    capture keytool output explicitly; the previous pipefail+SIGPIPE
    pattern (... | grep -qi "$subject") silently turned real keytool
    failures into "subject not found" misdiagnoses AND real positive
    matches into false negatives.
  - validator detect_mode "both paths present" branch now routes the
    whole warn block to stderr; previously the warn lines leaked into
    the command-substitution capture and broke the case dispatch,
    yielding a silent exit-0 with NO checks run.

  Important:
  - validator FAILs (not OKs) when keytool is missing on the JKS path.
  - --all-users root check moved into parse_args (fast-fail exit 1).
  - update_user_shell_rc tracks RC_UPDATED and the final summary prints
    a loud WARNING when the per-user rc step was skipped.
  - --mode jto -> --mode java-tool-options for self-documenting CLI.
  - mktemp is -p $(dirname target) so `mv` is rename(2) (atomic), and an
    empty-awk-output check refuses to clobber /etc/environment.
  - --cert-name regex-validated to [A-Za-z0-9._-]+ to prevent path
    traversal in the anchor file path.
  - chown failure on the user's rc file captured + warned.
  - get_target_user verifies the picked username exists in /etc/passwd.

Smoke matrix (testing/test_install_certs_jvm_linux.sh): Ubuntu 22.04,
Debian 12, RHEL UBI 9, Amazon Linux 2023, in parallel. Per-container
invariants: positive install+validate, subject mismatch -> exit 1,
idempotent re-install (no duplicate lines/aliases), custom --cert-name
round-trips, path-traversal rejected, malformed PEM rejected, expired
CA rejected (skipped when openssl can't produce a verifiably-expired
test cert), leaf cert (CA:FALSE) rejected, forced --mode update-ca-trust
(Path A) on hosts with update-ca-trust, dual-artifact validator
regression test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@akushsky akushsky force-pushed the feature/DFLOW-150-jvm-linux branch from 6b3c960 to 0e236f5 Compare May 20, 2026 17:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant