Fix hostname validation not skipping with `druid.client.https.validateHostnames=false` in java 8u275 and later (#11538)

* fix skip hostname validation in java 8u275 and later

* add unit test

* fix checkstyle
This commit is contained in:
Maytas Monsereenusorn 2021-08-05 15:42:55 +07:00 committed by GitHub
parent 23d7d71ea5
commit 4470ca6a92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 56 additions and 0 deletions

View File

@ -95,6 +95,15 @@ public class CustomCheckX509TrustManager extends X509ExtendedTrustManager implem
SSLParameters params = engine.getSSLParameters();
params.setEndpointIdentificationAlgorithm(null);
engine.setSSLParameters(params);
// In Java version 8u275 and later, setting EndpointIdentificationAlgorithm to null will be ignored
// and the current value will remains. As a workaround, we can set EndpointIdentificationAlgorithm to an
// empty String instead. This will still cause the hostname validation to be skipped.
if (engine.getSSLParameters().getEndpointIdentificationAlgorithm() != null) {
params = engine.getSSLParameters();
params.setEndpointIdentificationAlgorithm("");
engine.setSSLParameters(params);
}
}
certificateChecker.checkServer(chain, authType, engine, delegate);

View File

@ -48,7 +48,10 @@ import org.apache.druid.server.initialization.jetty.JettyServerModule;
import org.apache.druid.server.initialization.jetty.ServletFilterHolder;
import org.apache.druid.server.security.AuthTestUtils;
import org.apache.druid.server.security.AuthorizerMapper;
import org.apache.druid.server.security.CustomCheckX509TrustManager;
import org.apache.druid.server.security.TLSCertificateChecker;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.joda.time.Duration;
import org.junit.Assert;
@ -56,7 +59,12 @@ import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.http.HttpServletResponse;
@ -72,6 +80,7 @@ import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.cert.X509Certificate;
import java.util.EnumSet;
import java.util.Locale;
import java.util.Map;
@ -507,6 +516,44 @@ public class JettyTest extends BaseJettyTest
Assert.assertEquals(0, jsm.getActiveConnections());
}
@Test
public void testCustomCheckX509TrustManagerSetEndpointIdentificationAlgorithmToNullWithValidateServerHostnamesSetToFalse() throws Exception
{
SslContextFactory.Server server = injector.getInstance(SslContextFactory.Server.class);
server.setEndpointIdentificationAlgorithm("HTTPS");
server.start();
SSLEngine sslEngine = server.newSSLEngine();
X509ExtendedTrustManager mockX509ExtendedTrustManager = Mockito.mock(X509ExtendedTrustManager.class);
TLSCertificateChecker mockTLSCertificateChecker = Mockito.mock(TLSCertificateChecker.class);
X509Certificate mockX509Certificate = Mockito.mock(X509Certificate.class);
String authType = "testAuthType";
X509Certificate[] chain = new X509Certificate[]{mockX509Certificate};
// The EndpointIdentificationAlgorithm should not be null as we set it to HTTPS earlier
Assert.assertNotNull(sslEngine.getSSLParameters().getEndpointIdentificationAlgorithm());
CustomCheckX509TrustManager customCheckX509TrustManager = new CustomCheckX509TrustManager(
mockX509ExtendedTrustManager,
mockTLSCertificateChecker,
false
);
customCheckX509TrustManager.checkServerTrusted(chain, authType, sslEngine);
ArgumentCaptor<SSLEngine> captor = ArgumentCaptor.forClass(SSLEngine.class);
Mockito.verify(mockTLSCertificateChecker).checkServer(
ArgumentMatchers.eq(chain),
ArgumentMatchers.eq(authType),
captor.capture(),
ArgumentMatchers.eq(mockX509ExtendedTrustManager)
);
SSLEngine transformedSSLEngine = captor.getValue();
// The EndpointIdentificationAlgorithm should be null or empty Stringas the CustomCheckX509TrustManager
// has validateServerHostnames set to false
String endpointIdentificationAlgorithm = transformedSSLEngine.getSSLParameters().getEndpointIdentificationAlgorithm();
Assert.assertTrue(endpointIdentificationAlgorithm == null || endpointIdentificationAlgorithm.isEmpty());
}
private void waitForJettyServerModuleActiveConnectionsZero(JettyServerModule jsm) throws InterruptedException
{
// it can take a bit to close the connection, so maybe sleep for a while and hope it closes