Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #269 from gabemontero/no-proxy
[JENKINS-51223] catch more domain suffix based no_proxy matches (add …
  • Loading branch information
jeffret-b committed May 21, 2018
2 parents 3cc5305 + 0272a43 commit 510dd2b
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 55 deletions.
29 changes: 25 additions & 4 deletions src/main/java/hudson/remoting/Util.java
Expand Up @@ -30,7 +30,8 @@
* *
* @author Kohsuke Kawaguchi * @author Kohsuke Kawaguchi
*/ */
class Util { @Restricted(NoExternalUse.class)
public class Util {
/** /**
* Gets the file name portion from a qualified '/'-separate resource path name. * Gets the file name portion from a qualified '/'-separate resource path name.
* *
Expand Down Expand Up @@ -108,7 +109,7 @@ static String indent(String s) {
* *
* Warning: this method won't match shortened representation of IPV6 address * Warning: this method won't match shortened representation of IPV6 address
*/ */
static boolean inNoProxyEnvVar(@Nonnull String host) { public static boolean inNoProxyEnvVar(@Nonnull String host) {
String noProxy = System.getenv("no_proxy"); String noProxy = System.getenv("no_proxy");
if (noProxy != null) { if (noProxy != null) {
noProxy = noProxy.trim() noProxy = noProxy.trim()
Expand All @@ -124,6 +125,7 @@ static boolean inNoProxyEnvVar(@Nonnull String host) {
} }
else { else {
int depth = 0; int depth = 0;
String originalHost = host;
// Loop while we have a valid domain name: acme.com // Loop while we have a valid domain name: acme.com
// We add a safeguard to avoid a case where the host would always be valid because the regex would // We add a safeguard to avoid a case where the host would always be valid because the regex would
// for example fail to remove subdomains. // for example fail to remove subdomains.
Expand All @@ -135,8 +137,13 @@ static boolean inNoProxyEnvVar(@Nonnull String host) {
if (noProxy.matches(".*(^|,)\\Q" + host + "\\E($|,).*")) if (noProxy.matches(".*(^|,)\\Q" + host + "\\E($|,).*"))
return true; return true;
// Remove first subdomain: master.jenkins.acme.com -> jenkins.acme.com // Remove first subdomain: master.jenkins.acme.com -> jenkins.acme.com
else host = host.replaceFirst("^[a-z0-9]+(-[a-z0-9]+)*\\.", "");
host = host.replaceFirst("^[a-z0-9]+(-[a-z0-9]+)*\\.", ""); }

String[] noProxyArray = noProxy.split(",");
// fix for https://issues.jenkins-ci.org/browse/JENKINS-51223, basic suffix match
if (depth > 0 && suffixMatch(originalHost, noProxyArray)) {
return true;
} }
} }
} }
Expand All @@ -145,6 +152,20 @@ static boolean inNoProxyEnvVar(@Nonnull String host) {
return false; return false;
} }


// fix for https://issues.jenkins-ci.org/browse/JENKINS-51223
// adds curl-like algorithm for matching to existing regexp used in
// inNoProxyEnvVars
static boolean suffixMatch(String host, String[] noProxyArray) {
for (String proxy : noProxyArray) {
// still needs to capture some form of subdomain, like ".svc"
if (!proxy.contains("."))
continue;
if (host.endsWith(proxy))
return true;
}
return false;
}

/** /**
* Gets URL connection. * Gets URL connection.
* If http_proxy environment variable exists, the connection uses the proxy. * If http_proxy environment variable exists, the connection uses the proxy.
Expand Down
Expand Up @@ -24,6 +24,8 @@
package org.jenkinsci.remoting.engine; package org.jenkinsci.remoting.engine;


import hudson.remoting.Base64; import hudson.remoting.Base64;
import hudson.remoting.Util;

import org.jenkinsci.remoting.util.https.NoCheckHostnameVerifier; import org.jenkinsci.remoting.util.https.NoCheckHostnameVerifier;
import org.jenkinsci.remoting.util.https.NoCheckTrustManager; import org.jenkinsci.remoting.util.https.NoCheckTrustManager;


Expand Down Expand Up @@ -498,58 +500,8 @@ static URLConnection openURLConnection(URL url, String credentials, String proxy
return con; return con;
} }


/**
* Check if given URL is in the exclusion list defined by the no_proxy environment variable.
* On most *NIX system wildcards are not supported but if one top domain is added, all related subdomains will also
* be ignored. Both "mit.edu" and ".mit.edu" are valid syntax.
* http://www.gnu.org/software/wget/manual/html_node/Proxies.html
*
* Regexp:
* - \Q and \E: https://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html
* - To match IPV4/IPV/FQDN: Regular Expressions Cookbook, 2nd Edition (ISBN: 9781449327453)
*
* Warning: this method won't match shortened representation of IPV6 address
*
* FIXME: duplicate of hudson.remoting.Util.inNoProxyEnvVar
*/
static boolean inNoProxyEnvVar(String host) { static boolean inNoProxyEnvVar(String host) {
String noProxy = System.getenv("no_proxy"); return Util.inNoProxyEnvVar(host);
if (noProxy != null) {
noProxy = noProxy.trim()
// Remove spaces
.replaceAll("\\s+", "")
// Convert .foobar.com to foobar.com
.replaceAll("((?<=^|,)\\.)*(([a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,})(?=($|,))", "$2");

if (!noProxy.isEmpty()) {
// IPV4 and IPV6
if (host.matches("^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$") || host
.matches("^(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$")) {
return noProxy.matches(".*(^|,)\\Q" + host + "\\E($|,).*");
} else {
int depth = 0;
// Loop while we have a valid domain name: acme.com
// We add a safeguard to avoid a case where the host would always be valid because the regex would
// for example fail to remove subdomains.
// According to Wikipedia (no RFC defines it), 128 is the max number of subdivision for a valid
// FQDN:
// https://en.wikipedia.org/wiki/Subdomain#Overview
while (host.matches("^([a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,}$") && depth < 128) {
++depth;
// Check if the no_proxy contains the host
if (noProxy.matches(".*(^|,)\\Q" + host + "\\E($|,).*")) {
return true;
}
// Remove first subdomain: master.jenkins.acme.com -> jenkins.acme.com
else {
host = host.replaceFirst("^[a-z0-9]+(-[a-z0-9]+)*\\.", "");
}
}
}
}
}

return false;
} }


@CheckForNull @CheckForNull
Expand Down
41 changes: 41 additions & 0 deletions src/test/java/hudson/remoting/UtilTest.java
Expand Up @@ -121,6 +121,47 @@ public void testSubFQDNWithDot() {


assertEquals(false, Util.inNoProxyEnvVar("foobar.com")); assertEquals(false, Util.inNoProxyEnvVar("foobar.com"));
} }

@Test
public void testSubFWDNWithDotMinimalSuffix() {
PowerMockito.when(System.getenv("no_proxy")).thenReturn(".svc");

assertEquals(true, Util.inNoProxyEnvVar("bn-myproj.svc"));
}

@Test
public void testSubFWDNWithDotMinimalSuffixMixedCase() {
PowerMockito.when(System.getenv("no_proxy")).thenReturn(".svc,.default,.local,localhost,.boehringer.com,10.250.0.0/16,10.251.0.0/16,10.183.195.106,10.183.195.107,10.183.195.108,10.183.195.109,10.183.195.11,10.183.195.111,10.183.195.112,10.183.195.113,10.183.195.13,10.250.127.");

assertEquals(true, Util.inNoProxyEnvVar("bn-myproj.svc"));
}

@Test
public void testNoProxyWithInvalidChars() {
PowerMockito.when(System.getenv("no_proxy")).thenReturn("foo+.co=m");

assertEquals(false, Util.inNoProxyEnvVar("foo+.co=m"));
}

@Test
public void testNoProxyWithInvalidChar() {
PowerMockito.when(System.getenv("no_proxy")).thenReturn("foo.co=m");

assertEquals(false, Util.inNoProxyEnvVar("foo.co=m"));
}

@Test
public void testNoProxyWithInvalidCharInMinimalSuffix() {
PowerMockito.when(System.getenv("no_proxy")).thenReturn(".sv=c");

assertEquals(false, Util.inNoProxyEnvVar("foo.sv=c"));
}
@Test
public void testSubFWDNWithoutDotMinimalSuffix() {
PowerMockito.when(System.getenv("no_proxy")).thenReturn("svc");

assertEquals(false, Util.inNoProxyEnvVar("bn-myproj.svc"));
}


@Test @Test
public void testMixed() { public void testMixed() {
Expand Down

0 comments on commit 510dd2b

Please sign in to comment.