From 1e00902ae0a2c20bf035e410c2b1c8bf8759da6a Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Fri, 18 Dec 2015 20:39:39 +0100 Subject: [PATCH 1/3] [JENKINS-31256] Use credentials in waitForServerToBack This address three regressions introduced with HUDSON-4071, 662b0f and JENKINS-6167 as these only modified run() resulting in no proxy and no authorization support in waitForServerToBack() --- src/main/java/hudson/remoting/Engine.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/hudson/remoting/Engine.java b/src/main/java/hudson/remoting/Engine.java index 1a164600a..4ea1ab97d 100644 --- a/src/main/java/hudson/remoting/Engine.java +++ b/src/main/java/hudson/remoting/Engine.java @@ -182,7 +182,7 @@ public void run() { // find out the TCP port HttpURLConnection con = (HttpURLConnection)Util.openURLConnection(salURL); if (credentials != null) { - // TODO /tcpSlaveAgentListener is unprotected so why do we need to pass any credentials? + // Pass credentials to support running Jenkins behind a (reverse) proxy requiring authorization String encoding = Base64.encode(credentials.getBytes("UTF-8")); con.setRequestProperty("Authorization", "Basic " + encoding); } @@ -372,7 +372,17 @@ private void waitForServerToBack() throws InterruptedException { retries++; t.setName(oldName+": trying "+url+" for "+retries+" times"); - HttpURLConnection con = (HttpURLConnection) url.openConnection(); + HttpURLConnection con = (HttpURLConnection)Util.openURLConnection(url); + if (credentials != null) { + // Pass credentials to support running Jenkins behind a (reverse) proxy requiring authorization + String encoding = Base64.encode(credentials.getBytes("UTF-8")); + con.setRequestProperty("Authorization", "Basic " + encoding); + } + + if (proxyCredentials != null) { + String encoding = Base64.encode(proxyCredentials.getBytes("UTF-8")); + con.setRequestProperty("Proxy-Authorization", "Basic " + encoding); + } con.setConnectTimeout(5000); con.setReadTimeout(5000); con.connect(); From dacee70d9964a88238610f3d7c14043a5e6c6dd7 Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Tue, 5 Jan 2016 22:10:41 +0100 Subject: [PATCH 2/3] Move authorization code to openURLConnection() --- src/main/java/hudson/remoting/Engine.java | 24 ++--------------------- src/main/java/hudson/remoting/Util.java | 19 +++++++++++++++++- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/main/java/hudson/remoting/Engine.java b/src/main/java/hudson/remoting/Engine.java index 4ea1ab97d..0f4770063 100644 --- a/src/main/java/hudson/remoting/Engine.java +++ b/src/main/java/hudson/remoting/Engine.java @@ -180,17 +180,7 @@ public void run() { URL salURL = new URL(s+"tcpSlaveAgentListener/"); // find out the TCP port - HttpURLConnection con = (HttpURLConnection)Util.openURLConnection(salURL); - if (credentials != null) { - // Pass credentials to support running Jenkins behind a (reverse) proxy requiring authorization - String encoding = Base64.encode(credentials.getBytes("UTF-8")); - con.setRequestProperty("Authorization", "Basic " + encoding); - } - - if (proxyCredentials != null) { - String encoding = Base64.encode(proxyCredentials.getBytes("UTF-8")); - con.setRequestProperty("Proxy-Authorization", "Basic " + encoding); - } + HttpURLConnection con = (HttpURLConnection)Util.openURLConnection(salURL, credentials, proxyCredentials); try { try { con.setConnectTimeout(30000); @@ -372,17 +362,7 @@ private void waitForServerToBack() throws InterruptedException { retries++; t.setName(oldName+": trying "+url+" for "+retries+" times"); - HttpURLConnection con = (HttpURLConnection)Util.openURLConnection(url); - if (credentials != null) { - // Pass credentials to support running Jenkins behind a (reverse) proxy requiring authorization - String encoding = Base64.encode(credentials.getBytes("UTF-8")); - con.setRequestProperty("Authorization", "Basic " + encoding); - } - - if (proxyCredentials != null) { - String encoding = Base64.encode(proxyCredentials.getBytes("UTF-8")); - con.setRequestProperty("Proxy-Authorization", "Basic " + encoding); - } + HttpURLConnection con = (HttpURLConnection)Util.openURLConnection(url, credentials, proxyCredentials); con.setConnectTimeout(5000); con.setReadTimeout(5000); con.connect(); diff --git a/src/main/java/hudson/remoting/Util.java b/src/main/java/hudson/remoting/Util.java index 3716ecb26..e70cd7696 100644 --- a/src/main/java/hudson/remoting/Util.java +++ b/src/main/java/hudson/remoting/Util.java @@ -110,8 +110,9 @@ static String indent(String s) { /** * Gets URL connection. * If http_proxy environment variable exists, the connection uses the proxy. + * Credentials can be passed e.g. to support running Jenkins behind a (reverse) proxy requiring authorization */ - static URLConnection openURLConnection(URL url) throws IOException { + static URLConnection openURLConnection(URL url, String credentials, String proxyCredentials) throws IOException { String httpProxy = null; // If http.proxyHost property exists, openConnection() uses it. if (System.getProperty("http.proxyHost") == null) { @@ -131,9 +132,25 @@ static URLConnection openURLConnection(URL url) throws IOException { } else { con = url.openConnection(); } + if (credentials != null) { + String encoding = Base64.encode(credentials.getBytes("UTF-8")); + con.setRequestProperty("Authorization", "Basic " + encoding); + } + if (proxyCredentials != null) { + String encoding = Base64.encode(proxyCredentials.getBytes("UTF-8")); + con.setRequestProperty("Proxy-Authorization", "Basic " + encoding); + } return con; } + /** + * Gets URL connection. + * If http_proxy environment variable exists, the connection uses the proxy. + */ + static URLConnection openURLConnection(URL url) throws IOException { + return openURLConnection(url, null, null); + } + static InetSocketAddress getResolvedHttpProxyAddress(String host, int port) throws IOException { InetSocketAddress targetAddress = null; Iterator proxies = ProxySelector.getDefault().select(URI.create(String.format("http://%s:%d", host, port))).iterator(); From 97597d6ef8c4f2c5d0a7621a6e7157d10f5c82f6 Mon Sep 17 00:00:00 2001 From: Tim Pambor Date: Thu, 18 Feb 2016 16:16:00 +0100 Subject: [PATCH 3/3] Factored out sslSocketFactory creation and use sslSocketFactory also in waitForServerToBack() --- src/main/java/hudson/remoting/Engine.java | 60 +++++++++++++---------- src/main/java/hudson/remoting/Util.java | 18 ++++++- 2 files changed, 51 insertions(+), 27 deletions(-) diff --git a/src/main/java/hudson/remoting/Engine.java b/src/main/java/hudson/remoting/Engine.java index 33e0f03d9..00f7956e5 100644 --- a/src/main/java/hudson/remoting/Engine.java +++ b/src/main/java/hudson/remoting/Engine.java @@ -29,6 +29,7 @@ import java.security.AccessController; import java.security.KeyStore; import java.security.KeyStoreException; +import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.PrivilegedActionException; @@ -42,7 +43,6 @@ import java.util.logging.Level; import javax.annotation.CheckForNull; import javax.annotation.concurrent.NotThreadSafe; -import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManagerFactory; @@ -214,25 +214,7 @@ public void run() { Throwable firstError=null; String host=null; String port=null; - SSLSocketFactory sslSocketFactory = null; - if (candidateCertificates != null && !candidateCertificates.isEmpty()) { - KeyStore keyStore = getCacertsKeyStore(); - // load the keystore - keyStore.load(null, null); - int i = 0; - for (X509Certificate c : candidateCertificates) { - keyStore.setCertificateEntry(String.format("alias-%d", i++), c); - } - // prepare the trust manager - TrustManagerFactory trustManagerFactory = - TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - trustManagerFactory.init(keyStore); - // prepare the SSL context - SSLContext ctx = SSLContext.getInstance("TLS"); - ctx.init(null, trustManagerFactory.getTrustManagers(), null); - // now we have our custom socket factory - sslSocketFactory = ctx.getSocketFactory(); - } + SSLSocketFactory sslSocketFactory = getSSLSocketFactory(); for (URL url : candidateUrls) { String s = url.toExternalForm(); @@ -240,10 +222,7 @@ public void run() { URL salURL = new URL(s+"tcpSlaveAgentListener/"); // find out the TCP port - HttpURLConnection con = (HttpURLConnection)Util.openURLConnection(salURL, credentials, proxyCredentials); - if (con instanceof HttpsURLConnection && sslSocketFactory != null) { - ((HttpsURLConnection) con).setSSLSocketFactory(sslSocketFactory); - } + HttpURLConnection con = (HttpURLConnection)Util.openURLConnection(salURL, credentials, proxyCredentials, sslSocketFactory); try { try { con.setConnectTimeout(30000); @@ -414,6 +393,12 @@ private Socket connect(String host, String port) throws IOException, Interrupted private void waitForServerToBack() throws InterruptedException { Thread t = Thread.currentThread(); String oldName = t.getName(); + SSLSocketFactory sslSocketFactory = null; + try { + sslSocketFactory = getSSLSocketFactory(); + } catch (Throwable e) { + events.error(e); + } try { int retries=0; while(true) { @@ -425,7 +410,7 @@ private void waitForServerToBack() throws InterruptedException { retries++; t.setName(oldName+": trying "+url+" for "+retries+" times"); - HttpURLConnection con = (HttpURLConnection)Util.openURLConnection(url, credentials, proxyCredentials); + HttpURLConnection con = (HttpURLConnection)Util.openURLConnection(url, credentials, proxyCredentials, sslSocketFactory); con.setConnectTimeout(5000); con.setReadTimeout(5000); con.connect(); @@ -552,6 +537,31 @@ public FileInputStream run() throws Exception { }); } + private SSLSocketFactory getSSLSocketFactory() + throws PrivilegedActionException, KeyStoreException, NoSuchProviderException, CertificateException, + NoSuchAlgorithmException, IOException, KeyManagementException { + SSLSocketFactory sslSocketFactory = null; + if (candidateCertificates != null && !candidateCertificates.isEmpty()) { + KeyStore keyStore = getCacertsKeyStore(); + // load the keystore + keyStore.load(null, null); + int i = 0; + for (X509Certificate c : candidateCertificates) { + keyStore.setCertificateEntry(String.format("alias-%d", i++), c); + } + // prepare the trust manager + TrustManagerFactory trustManagerFactory = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init(keyStore); + // prepare the SSL context + SSLContext ctx = SSLContext.getInstance("TLS"); + ctx.init(null, trustManagerFactory.getTrustManagers(), null); + // now we have our custom socket factory + sslSocketFactory = ctx.getSocketFactory(); + } + return sslSocketFactory; + } + /** * @deprecated Use {@link JnlpProtocol#GREETING_SUCCESS}. */ diff --git a/src/main/java/hudson/remoting/Util.java b/src/main/java/hudson/remoting/Util.java index e70cd7696..fbb980810 100644 --- a/src/main/java/hudson/remoting/Util.java +++ b/src/main/java/hudson/remoting/Util.java @@ -14,6 +14,8 @@ import java.net.Proxy; import java.net.SocketAddress; import java.net.URL; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSocketFactory; import java.util.Iterator; /** @@ -112,7 +114,7 @@ static String indent(String s) { * If http_proxy environment variable exists, the connection uses the proxy. * Credentials can be passed e.g. to support running Jenkins behind a (reverse) proxy requiring authorization */ - static URLConnection openURLConnection(URL url, String credentials, String proxyCredentials) throws IOException { + static URLConnection openURLConnection(URL url, String credentials, String proxyCredentials, SSLSocketFactory sslSocketFactory) throws IOException { String httpProxy = null; // If http.proxyHost property exists, openConnection() uses it. if (System.getProperty("http.proxyHost") == null) { @@ -140,15 +142,27 @@ static URLConnection openURLConnection(URL url, String credentials, String proxy String encoding = Base64.encode(proxyCredentials.getBytes("UTF-8")); con.setRequestProperty("Proxy-Authorization", "Basic " + encoding); } + if (con instanceof HttpsURLConnection && sslSocketFactory != null) { + ((HttpsURLConnection) con).setSSLSocketFactory(sslSocketFactory); + } return con; } + /** + * Gets URL connection. + * If http_proxy environment variable exists, the connection uses the proxy. + * Credentials can be passed e.g. to support running Jenkins behind a (reverse) proxy requiring authorization + */ + static URLConnection openURLConnection(URL url, String credentials, String proxyCredentials) throws IOException { + return openURLConnection(url, credentials, proxyCredentials, null); + } + /** * Gets URL connection. * If http_proxy environment variable exists, the connection uses the proxy. */ static URLConnection openURLConnection(URL url) throws IOException { - return openURLConnection(url, null, null); + return openURLConnection(url, null, null, null); } static InetSocketAddress getResolvedHttpProxyAddress(String host, int port) throws IOException {