Skip to content

Commit

Permalink
Fix proxy authentication (#688)
Browse files Browse the repository at this point in the history
Co-authored-by: Jesse Glick <[email protected]>
  • Loading branch information
basil and jglick authored Oct 20, 2023
1 parent e4ef25c commit 3dd999d
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 9 deletions.
2 changes: 1 addition & 1 deletion src/main/java/hudson/remoting/Engine.java
Original file line number Diff line number Diff line change
Expand Up @@ -894,7 +894,7 @@ private JnlpEndpointResolver createEndpointResolver(List<String> jenkinsUrls, St
resolver = new JnlpAgentEndpointResolver(jenkinsUrls, credentials, proxyCredentials, tunnel,
sslSocketFactory, disableHttpsCertValidation, agentName);
} else {
resolver = new JnlpAgentEndpointConfigurator(directConnection, instanceIdentity, protocols);
resolver = new JnlpAgentEndpointConfigurator(directConnection, instanceIdentity, protocols, proxyCredentials);
}
return resolver;
}
Expand Down
27 changes: 23 additions & 4 deletions src/main/java/org/jenkinsci/remoting/engine/JnlpAgentEndpoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.security.interfaces.RSAPublicKey;
import java.util.Base64;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Objects;
Expand Down Expand Up @@ -76,13 +77,25 @@ public class JnlpAgentEndpoint {
@CheckForNull
private final URL serviceUrl;

@CheckForNull
private final String proxyCredentials;

/**
* @deprecated Use {@link #JnlpAgentEndpoint(java.lang.String, int, java.security.interfaces.RSAPublicKey, java.util.Set, java.net.URL)}
* @deprecated Use {@link #JnlpAgentEndpoint(java.lang.String, int, java.security.interfaces.RSAPublicKey, java.util.Set, java.net.URL, java.lang.String)}
*/
@Deprecated
public JnlpAgentEndpoint(@NonNull String host, int port, @CheckForNull RSAPublicKey publicKey,
@CheckForNull Set<String> protocols) {
this(host, port, publicKey, protocols, null);
this(host, port, publicKey, protocols, null, null);
}

/**
* @deprecated Use {@link #JnlpAgentEndpoint(java.lang.String, int, java.security.interfaces.RSAPublicKey, java.util.Set, java.net.URL, java.lang.String)}
*/
@Deprecated
public JnlpAgentEndpoint(@NonNull String host, int port, @CheckForNull RSAPublicKey publicKey,
@CheckForNull Set<String> protocols, @CheckForNull URL serviceURL) {
this(host, port, publicKey, protocols, serviceURL, null);
}

/**
Expand All @@ -97,7 +110,7 @@ public JnlpAgentEndpoint(@NonNull String host, int port, @CheckForNull RSAPublic
* @since 3.0
*/
public JnlpAgentEndpoint(@NonNull String host, int port, @CheckForNull RSAPublicKey publicKey,
@CheckForNull Set<String> protocols, @CheckForNull URL serviceURL) {
@CheckForNull Set<String> protocols, @CheckForNull URL serviceURL, @CheckForNull String proxyCredentials) {
if (port <= 0 || 65536 <= port) {
throw new IllegalArgumentException("Port " + port + " is not in the range 1-65535");
}
Expand All @@ -106,6 +119,7 @@ public JnlpAgentEndpoint(@NonNull String host, int port, @CheckForNull RSAPublic
this.publicKey = publicKey;
this.protocols = protocols == null || protocols.isEmpty() ? null : Collections.unmodifiableSet(new LinkedHashSet<>(protocols));
this.serviceUrl = serviceURL;
this.proxyCredentials = proxyCredentials;
}

/**
Expand Down Expand Up @@ -215,7 +229,12 @@ public Socket open(int socketTimeout) throws IOException {
socket.setSoTimeout(socketTimeout);

if (isHttpProxy) {
String connectCommand = String.format("CONNECT %s:%s HTTP/1.1\r\nHost: %s\r\n\r\n", host, port, host);
String connectCommand = String.format("CONNECT %s:%s HTTP/1.1\r\nHost: %s\r\n", host, port, host);
if (proxyCredentials != null) {
String encoding = Base64.getEncoder().encodeToString(proxyCredentials.getBytes(StandardCharsets.UTF_8));
connectCommand += "Proxy-Authorization: Basic " + encoding + "\r\n";
}
connectCommand += "\r\n";
socket.getOutputStream()
.write(connectCommand.getBytes(StandardCharsets.UTF_8)); // TODO: internationalized domain names

Check warning on line 239 in src/main/java/org/jenkinsci/remoting/engine/JnlpAgentEndpoint.java

View check run for this annotation

ci.jenkins.io / Open Tasks Scanner

TODO

NORMAL: internationalized domain names

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,13 @@ public class JnlpAgentEndpointConfigurator extends JnlpEndpointResolver {
private final String instanceIdentity;
private final Set<String> protocols;
private final String directionConnection;
private final String proxyCredentials;

public JnlpAgentEndpointConfigurator(String directConnection, String instanceIdentity, Set<String> protocols) {
public JnlpAgentEndpointConfigurator(String directConnection, String instanceIdentity, Set<String> protocols, String proxyCredentials) {
this.directionConnection = directConnection;
this.instanceIdentity = instanceIdentity;
this.protocols = protocols;
this.proxyCredentials = proxyCredentials;
}

@Override
Expand All @@ -57,7 +59,7 @@ public JnlpAgentEndpoint resolve() throws IOException {
}
HostPort hostPort = new HostPort(directionConnection);

return new JnlpAgentEndpoint(hostPort.getHost(), hostPort.getPort(), identity, protocols, null);
return new JnlpAgentEndpoint(hostPort.getHost(), hostPort.getPort(), identity, protocols, null, proxyCredentials);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,13 @@
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.IOException;
import java.net.Authenticator;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.NoRouteToHostException;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.Proxy.Type;
import java.net.ProxySelector;
Expand Down Expand Up @@ -325,7 +327,7 @@ public JnlpAgentEndpoint resolve() throws IOException {
}

//TODO: all the checks above do not make much sense if tunneling is enabled (JENKINS-52246)

Check warning on line 329 in src/main/java/org/jenkinsci/remoting/engine/JnlpAgentEndpointResolver.java

View check run for this annotation

ci.jenkins.io / Open Tasks Scanner

TODO

NORMAL: all the checks above do not make much sense if tunneling is enabled (JENKINS-52246)
return new JnlpAgentEndpoint(host, port, identity, agentProtocolNames, selectedJenkinsURL);
return new JnlpAgentEndpoint(host, port, identity, agentProtocolNames, selectedJenkinsURL, proxyCredentials);
} finally {
con.disconnect();
}
Expand All @@ -337,11 +339,30 @@ public JnlpAgentEndpoint resolve() throws IOException {
}

@SuppressFBWarnings(value = "UNENCRYPTED_SOCKET", justification = "This just verifies connection to the port. No data is transmitted.")
private boolean isPortVisible(String hostname, int port) {
private synchronized boolean isPortVisible(String hostname, int port) {
boolean exitStatus = false;
Socket s = null;

Authenticator orig = null;
try {
if (proxyCredentials != null) {
final int index = proxyCredentials.indexOf(':');
if (index < 0) {
throw new IllegalArgumentException("Invalid credential");
}
orig = Authenticator.getDefault();
// Using Authenticator.setDefault is a bit ugly, but there is no other way to control the authenticator
// on the HTTPURLConnection object created internally by Socket.
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType().equals(RequestorType.PROXY)) {
return new PasswordAuthentication(proxyCredentials.substring(0, index), proxyCredentials.substring(index + 1).toCharArray());
}
return super.getPasswordAuthentication();
}
});
}
InetSocketAddress proxyToUse = getResolvedHttpProxyAddress(hostname,port);
s = proxyToUse == null ? new Socket() : new Socket(new Proxy(Type.HTTP, proxyToUse));
s.setReuseAddress(true);
Expand All @@ -360,6 +381,9 @@ private boolean isPortVisible(String hostname, int port) {
LOGGER.warning(e.getMessage());
}
}
if (orig != null) {
Authenticator.setDefault(orig);
}
}
return exitStatus;
}
Expand Down

0 comments on commit 3dd999d

Please sign in to comment.