Jenkins using JNLP behind nginx SSL reverse proxy

certificatejavaJenkinsreverse-proxyssl

I'd like to use JNLP to connect jenkins slaves to jenkins master. The master is running behind an SSL nginx proxy set up according to official documentation. Beyond this documentation I run into certificate related issues.

Currently, I can get JNLP headless slave to master connection to work only with unsecure HTTP connection, but not with HTTPS (although my jenkins dashboard does in general).
I use an self-signed certificate signed by my own custom CA certificate (openssl based x509).

So how do I tell my slave's java binary to trust my SSL CA certificate? I've tried this.

# add CA certificate to key store
$ keytool -import -file /usr/local/share/ca-certificates/my_ca.crt -alias my_ca -storepass mypassword

# try to reference keystore to JNLP headless call
$ java -Djavax.net.ssl.keyStorePassword=mypassword -Djavax.net.ssl.keyStore=/home/myuser/.keystore -jar slave.jar -jnlpUrl https://proxied-jenkins.example.com/computer/testslave/slave-agent.jnlp

Actually JAVA seems not to look for the certificate within the provided keystore. What is my mistake here?

EDIT

Similar/same issue occurs when using jenkins cli. According to this, now I assume, it has nothing to do with trust of my certificate as I can't see a javax.net.ssl.SSLHandshakeException

I just receive a connection reset

Failing to obtain https://proxied-jenkins.example.com/computer/testslave/slave-agent.jnlp
java.net.SocketException: Connection reset
    at java.net.SocketInputStream.read(SocketInputStream.java:196)
    at java.net.SocketInputStream.read(SocketInputStream.java:122)
    at sun.security.ssl.InputRecord.readFully(InputRecord.java:442)
    at sun.security.ssl.InputRecord.read(InputRecord.java:480)
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:934)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1332)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1359)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1343)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:153)
    at hudson.remoting.Launcher.parseJnlpArguments(Launcher.java:269)
    at hudson.remoting.Launcher.run(Launcher.java:219)
    at hudson.remoting.Launcher.main(Launcher.java:192)

.. so I assume a configuration issue with my proxy. I took configuration from reverse proxy configuration and added following configuration to force SSL:

upstream jenkins-upstream {
  server unproxied-jenkins.example.com:8080 fail_timeout=0;
}

server {
  listen 80;
  server_name proxied-jenkins.example.com;
  return 301 https://$host$request_uri;
}
server {
  listen 443;
  server_name proxied-jenkins.example.com;

  #this is the jenkins web root directory (mentioned in the /etc/default/jenkins file)
  root            /var/run/jenkins/war/;

  ssl on;
  ssl_certificate /etc/nginx/conf.d/proxied-jenkins.example.com.crt;
  ssl_certificate_key /etc/nginx/conf.d/proxied-jenkins.example.com.key;
  ssl_protocols TLSv1.2;
  ssl_ciphers EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH;

  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;

  [...] # continuing according to jenkins documentation
  location @jenkins {
      proxy_pass                  http://jenkins-upstream
      [....]
  }
  [....]
}

Again when changing to unsecure HTTP, JNLP and jenkins-cli work as expected. So what is the mistake?
Maybe I need to pass additional header information? Maybe I need additional SSL configuration in my proxy settings?

Best Answer

(Expanded based on feedback on comments)

That server is configured to require protocol version TLSv1.2 (only), and ciphersuites using at least one of AES-GCM or 256-bit AES, with either DHE or ECDHE key exchange (for which OpenSSL and thus nginx uses variant spellings EDH and EECDH in some cases including this one).

JSSE client in Oracle or OpenJDK JDK7 does not offer TLSv1.2 or 1.1 by default (don't know for IBM, which has its own crypto providers) but they are implemented and can be enabled; for (Https)URLConnection this can be done with system property https.protocols. (Or by overlaying its socket factory, but the system property is usually easier.) But JDK7 does not implement GCM, and Oracle versions (but NOT OpenJDK ones) prohibit 256-bit symmetric encryption unless you install the "JCE Unlimited Strength Jurisdiction Policy Files" from the Oracle website; see https://stackoverflow.com/a/33712287/2868801 and possibly https://stackoverflow.com/questions/30350120/sslhandshakeexception-while-connecting-to-a-https-site

In contrast JDK8 (Oracle and OpenJDK) offers TLSv1.2 and 1.1 by default, and implements GCM, so it can connect without the Unlimited Strength policy.

Both 7 and 8 support DHE and ECDHE key exchange. But for anyone else in a similar situation with JDK6: ECDHE does not work unless you add a third-party provider for ECC primitives like bcprov from http://www.BouncyCastle.org

Related Question