Command-Line Git vs JGit in Jenkins Git Plugin — Divergent Certificate Validation Behavior
0 reputation · 12 Jan 2021, 14:41 UTC
0 reputation · 12 Jan 2021, 14:41 UTC
The Jenkins Git Plugin supports two Git implementations: command-line git (delegating to the OS trust store and git config http.sslCAInfo) and JGit (using the Java cacerts store and JSSE system properties). A single pipeline may invoke both — checkout via JGit while submodule or LFS operations fall back to command-line git — causing inconsistent certificate validation results against the same Git server.
No unified configuration point exists to synchronize trust decisions across both stacks. The Git Client Plugin's "Skip SSL verification" option only affects command-line git; JGit ignores it and requires Java-level trust store manipulation, which broadens the attack surface to all JVM network clients. Corporate MITM proxies and self-signed internal Git servers exacerbate the split, as the Plugin Manager (updates.jenkins.io) also depends exclusively on the Java trust store.
Maintainers have not converged on whether to introduce a unified SSL verification mode or deprecate one implementation since JGit integration arrived in 2016.
34775 reputation · 12 Jan 2021, 18:32 UTC
-Djavax.net.ssl.trustStore=/path/custom.jks -Djavax.net.ssl.trustStorePassword=changeit. Configure command-line Git to use the same PEM bundle with git config --global http.sslCAInfo /path/corporate-ca.pem. This isolates the trust decision to the agent and avoids mutating the system cacerts or OS store.-Dorg.jenkinsci.plugins.gitclient.Git.useJGit=true (or false) on the agent to lock the entire job to one stack. Without this flag, Jenkins defaults to command-line Git when available and falls back to JGit only when the binary is missing, which can cause a split within a single build.Command-line Git reads the OS trust store (/etc/ssl/certs, Windows Cert Store, macOS Keychain) plus Git config http.sslCAInfo/http.sslCAPath. JGit ignores Git config entirely and uses the JVM truststore (cacerts or -Djavax.net.ssl.trustStore) and JSSE system properties. Hostname verification also differs: command-line Git honors http.sslVerify=false; JGit requires a custom HostnameVerifier or -Djdk.tls.allowUnsafeServerCertChange=true (JDK 11+). Corporate MITM proxies and self-signed internal servers therefore succeed on one stack and fail on the other.
corporate-ca.pem).keytool -importcert -alias corporate-ca -file corporate-ca.pem -keystore /var/lib/jenkins/custom-truststore.jks -storepass changeit -noprompt-Djavax.net.ssl.trustStore=/var/lib/jenkins/custom-truststore.jks -Djavax.net.ssl.trustStorePassword=changeitgit config --system http.sslCAInfo /etc/ssl/certs/corporate-ca.pem# Use command-line Git everywhere
-Dorg.jenkinsci.plugins.gitclient.Git.useJGit=false
# Or use JGit everywhere
-Dorg.jenkinsci.plugins.gitclient.Git.useJGit=trueRun these on the agent to confirm both stacks see the same CA:
# Command-line Git
GIT_CURL_VERBOSE=1 git ls-remote https://your-internal-git.example.com/repo.git
# JGit (Groovy script console or pipeline step)
org.eclipse.jgit.api.LsRemoteCommand.newInstance()
.setRemote('https://your-internal-git.example.com/repo.git')
.setCredentialsProvider(new org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider('user','token'))
.call()Check which implementation Jenkins actually uses: Manage Jenkins → System Information → git.client.GitClient shows CliGitAPIImpl (command-line) or JGitAPIImpl.
cacerts may require the custom truststore even for command-line Git if the OS store is empty.-Djdk.tls.client.protocols=TLSv1.2.Does your environment use a MITM proxy that re-signs certificates with an internal CA, or are you connecting directly to a self-signed Git server? The proxy case requires the proxy CA in both stores; the self-signed case only needs the server's CA. This changes whether you must also update the OS store for non-Jenkins tooling.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 12 Jan 2021, 20:24 UTC
When a job mixes git steps, Jenkins prints a short banner at the beginning of each step:
Using JGit for this operation
If you see Using command-line git instead, the step is delegating to the OS binary. This banner is emitted by the Git Client Plugin and is the fastest way to confirm the stack being used.
For a deeper dive, add the following JVM options to the agent that runs the build:
JAVA_OPTS="-Dorg.eclipse.jgit.logging=DEBUG -Djavax.net.debug=ssl,handshake"
With org.eclipse.jgit.logging=DEBUG, JGit will log the trust store it consulted. The javax.net.debug flag will emit the entire SSL handshake, showing whether the OS store (for CLI git) or the Java cacerts (for JGit) is being used. These logs are invaluable when a submodule or LFS step silently falls back to the other implementation.
Because the two stacks read different trust stores, a certificate that passes in one may fail in the other. By inspecting the console output and the debug logs, you can quickly pinpoint which step is responsible for the failure and adjust the configuration accordingly.