Issue
-
I am receiving the following error message:
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
You may encounter this error message in the following scenarios:
-
Agents connecting to a controller or operations center which uses HTTPS
-
Enabling TLS on a controller causes agents or other tools that connect to the controller to fail with this error message
-
When using a plugin that connects to an external service using HTTPS and the certificate is not trusted by the JVM
-
During a Pipeline, an agent is connecting to an external service using HTTPS and the certificate is not trusted by the JVM
-
When using a self-signed certificate for TLS
-
If the Java version running on your machine is not up to date, hence the
cacerts
that is included with your old Java version does not trust the latest Certificate Authorities. Ensure you are running the latest fix pack of Java (for example, for openjdk 1.8, a fix pack level isjava-1.8.0-openjdk-1.8.0.191.b12
). -
Running Jenkins with the option
-Djavax.net.ssl.trustStore=
and pointing to a trust store that was copied from thecacerts
of an out-of-date Java fix pack level that does not trust the latest Certificate Authorities. In this case, you may have to rebuild your custom trustStore using the latestcacerts
file from the latest Java fixpack version
Resolution
If we know that the certificate is valid, we can import this certificate into the JVM trust store. In doing so, we tell the JVM that this is is a trusted certificate and TLS connections to the destination server should be allowed.
Steps to resolve the issue:
1 . Export the certificate in pem
format, which can be done via the command line (1a), or a web browser (1b):
1a . Exporting a certificate using the command line (change www.example.com
in this command to the site that is not trusted by the JVM):
openssl s_client -showcerts -connect www.example.com:443 </dev/null 2>/dev/null|openssl x509 -outform PEM > cert.pem
1b . If you have troubles using openssl
from step 1a, you could instead export the certificate using a web browser:
Note In this example I will be using firefox. Similar steps are available for all other browsers.
To begin we first need to navigate via the browser to the URL where the certificate is located. Clicking on the green lock will show us information about the certificate. After clicking on the green lock click on More Information:
Once you click on the green lock, then a new box will appear with more information about the certificate. Click "View Certificate":
Click the "Details" tab which will provide detailed information about the certificate:
Click on the "Export…" to export this certificate to local disk. Also make sure to maintain the PEM
format. (Note: In Windows, the PEM
format is called "Base-64 encoded X.509 (.CER)"):
2 . Optionally, make a copy of your JVM’s cacerts
file to a new directory $CACERT_DIRECTORY
(the benefit of this is when the JVM is upgraded or new versions are installed, the cacerts
file stays the same):
mkdir $CACERT_DIRECTORY cp $JAVA_HOME/lib/security/cacerts $CACERT_DIRECTORY/
If you follow this step 2, you then need to configure the JVM to use that cacerts
file. To do this add the following to your Java startup arguments:
-Djavax.net.ssl.trustStore=$CACERT_DIRECTORY/cacerts -Djavax.net.ssl.trustStorePassword=changeit
3 . Import the certificate into the JVM using the following command:
keytool -import -alias $ALIAS -keystore $CACERT_DIRECTORY/cacerts -file $PATH_TO_PEM_FILE
Please replace the following:
-
$ALIAS
- This can be any value. It is a value to distinguish this certificate from others. Example would be "git-mycompany-example-com", or "artifact-mycompany-example-com". -
$CACERT_DIRECTORY
- This should be the location of where your cacerts file is, either the chosen path from step 2, or in$JAVA_HOME/jre/lib/security
if you skipped step 2. Note: Windows users should verify that they are importing the certificate into the JRE Jenkins runs in. When set up as a Windows service, Jenkins uses the version of Java defined in$JENKINS_HOME\jenkins.xml
. This can be a different version of Java than the one which gets invoked by runningjava
orkeytool
from the command line. -
$PATH_TO_PEM_FILE
- This should be the location of thePEM
file we downloaded from step 1, for example./cert.pem
.
After restarting the JVM, it should recognize that the certificate has been added to the "trusted" list and you will no longer encounter the issue.
If the issue persists, test the cacerts
file using jrunscript
as documented in: Validate the truststore outside Jenkins.
Example
In this simple example, we will import the certificate of www.example.com
(which is using a self-signed certificate that is not trusted) into the JVM default cacert
file, then restart the JVM, using the following commands:
#!/bin/sh set -eu DOMAIN=www.example.com PORT=443 ALIAS=example_com JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 TEMP_FILE=./cert.pem openssl s_client -showcerts -connect "${DOMAIN}:${PORT}" </dev/null 2>/dev/null | openssl x509 -outform PEM > "${TEMP_FILE}" keytool -import -alias "${ALIAS}" -keystore "${JAVA_HOME}/lib/security/cacerts" -file "${TEMP_FILE}" -storepass changeit -noprompt # restart the JVM (command depends on how the JVM is started)
If successful, the script will emit Certificate was added to keystore
, and after restarting the JVM, it will no longer report the error message.
Appendix
Import certificates in Linux OS
In some cases we also need to import the certificate in the OS to use it with tools like curl
, git
, etc. To import the certificate, follow the steps below based on your Linux distribution.
Debian/Ubuntu
-
Obtain the server certificate and the certificates chain need to import (in PEM format)
-
Copy your certificates in
/usr/share/ca-certificates
directory -
Update your certificates running the command
sudo update-ca-certificates --fresh
sudo openssl s_client -showcerts -connect server.example.com:443 </dev/null 2>/dev/null|openssl x509 -outform PEM > /tmp/server_example_com.pem sudo cp /tmp/server_example_com.pem /usr/share/ca-certificates/server_example_com.pem sudo update-ca-certificates
Fedora/RHEL/CentOS
-
Obtain the server certificate and the certificates chain need to import (in PEM format)
-
Copy your certificates in
/etc/pki/ca-trust/source/anchors/
-
Update your certificates running the command
sudo update-ca-trust extract
-
On RHEL6 you also have to enable the CAs
sudo update-ca-trust enable
sudo openssl s_client -showcerts -connect server.example.com:443 </dev/null 2>/dev/null|openssl x509 -outform PEM > /tmp/server_example_com.pem sudo cp /tmp/server_example_com.pem /etc/pki/ca-trust/source/anchors/server_example_com.pem sudo update-ca-trust extract sudo update-ca-trust enable