Jenkins using JNLP behind nginx SSL reverse proxy

6,922

Solution 1

(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

Solution 2

For googlers who reach this page based on the title rather than the content of this question, and want JNLP slaves to connect to a reverse-proxied Jenkins that is running on a separate server rather than on the reverse-proxy server, please see my answer to Jenkins: How to configure Jenkins behind Nginx reverse proxy for JNLP slaves to connect.

Share:
6,922

Related videos on Youtube

ITL
Author by

ITL

Updated on September 18, 2022

Comments

  • ITL
    ITL over 1 year

    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?

    • dave_thompson_085
      dave_thompson_085 over 8 years
      You need your CAcert in the SSL truststore of the client process, not the SSL keystore. Not all SSLHandshakeException are due to cert problems, but you aren't getting the one that is so you are presumably failing before the point of validating the cert. (1) What version of java (6/7/8, Oracle/OpenJDK/IBM/?) are you using for the client? (2) Can you either (2A) get a network capture with Wireshark tcpdump or similar, filtered as closely as possible and show or link to that; or (2B) run with sysprop javax.net.debug=ssl, capture the (large!) output, and add that?
    • ITL
      ITL over 8 years
      @dave_thompson_085 the javax.net.debug=ssl did the trick. I was able to find out, that java binary (oracle jdk 7) tried to do TLSv1 handshake with 'weak' ciphers... After installing Java Cryptography Extension (JCE) I've had stronger cipher support and I was able to tell java binary to do do TLSv1.2. by using -Dhttps.protocols=TLSv1.2. However after upgrading to Oracle JDK 8, it works out-of-box. So thanks for your support and pointing me into the right direction... Maybe you formulate this as answer and I'll except and upvote it.