5
5
*/
6
6
package org .elasticsearch .xpack .core .ssl ;
7
7
8
+ import org .apache .http .HttpConnectionMetrics ;
9
+ import org .apache .http .HttpEntityEnclosingRequest ;
10
+ import org .apache .http .HttpException ;
11
+ import org .apache .http .HttpRequest ;
12
+ import org .apache .http .HttpResponse ;
8
13
import org .apache .http .client .methods .HttpGet ;
14
+ import org .apache .http .config .RegistryBuilder ;
15
+ import org .apache .http .conn .HttpConnectionFactory ;
16
+ import org .apache .http .conn .ManagedHttpClientConnection ;
17
+ import org .apache .http .conn .routing .HttpRoute ;
18
+ import org .apache .http .conn .socket .ConnectionSocketFactory ;
19
+ import org .apache .http .conn .socket .PlainConnectionSocketFactory ;
20
+ import org .apache .http .conn .ssl .DefaultHostnameVerifier ;
21
+ import org .apache .http .conn .ssl .SSLConnectionSocketFactory ;
9
22
import org .apache .http .impl .client .CloseableHttpClient ;
10
23
import org .apache .http .impl .client .HttpClients ;
24
+ import org .apache .http .impl .conn .ManagedHttpClientConnectionFactory ;
25
+ import org .apache .http .impl .conn .PoolingHttpClientConnectionManager ;
11
26
import org .apache .http .ssl .SSLContextBuilder ;
12
27
import org .elasticsearch .common .CheckedRunnable ;
13
28
import org .elasticsearch .common .settings .MockSecureSettings ;
26
41
27
42
import javax .net .ssl .SSLContext ;
28
43
import javax .net .ssl .SSLHandshakeException ;
44
+ import javax .net .ssl .SSLSession ;
45
+ import javax .net .ssl .SSLSocket ;
29
46
import java .io .IOException ;
30
47
import java .io .InputStream ;
31
48
import java .io .OutputStream ;
49
+ import java .net .InetAddress ;
50
+ import java .net .Socket ;
32
51
import java .nio .file .AtomicMoveNotSupportedException ;
33
52
import java .nio .file .Files ;
34
53
import java .nio .file .Path ;
47
66
import java .util .Collections ;
48
67
import java .util .List ;
49
68
import java .util .concurrent .CountDownLatch ;
69
+ import java .util .concurrent .TimeUnit ;
50
70
import java .util .function .Consumer ;
51
71
52
72
import static org .hamcrest .Matchers .containsString ;
@@ -91,7 +111,6 @@ public void testReloadingKeyStore() throws Exception {
91
111
final Settings settings = Settings .builder ()
92
112
.put ("path.home" , createTempDir ())
93
113
.put ("xpack.security.transport.ssl.keystore.path" , keystorePath )
94
- .put ("xpack.security.transport.ssl.supported_protocols" , "TLSv1.2" )
95
114
.setSecureSettings (secureSettings )
96
115
.build ();
97
116
final Environment env = randomBoolean () ? null : TestEnvironment .newEnvironment (settings );
@@ -150,7 +169,6 @@ public void testPEMKeyConfigReloading() throws Exception {
150
169
.put ("xpack.security.transport.ssl.key" , keyPath )
151
170
.put ("xpack.security.transport.ssl.certificate" , certPath )
152
171
.putList ("xpack.security.transport.ssl.certificate_authorities" , certPath .toString ())
153
- .put ("xpack.security.transport.ssl.supported_protocols" , "TLSv1.2" )
154
172
.setSecureSettings (secureSettings )
155
173
.build ();
156
174
final Environment env = randomBoolean () ? null :
@@ -207,15 +225,14 @@ public void testReloadingTrustStore() throws Exception {
207
225
secureSettings .setString ("xpack.security.transport.ssl.truststore.secure_password" , "testnode" );
208
226
Settings settings = Settings .builder ()
209
227
.put ("xpack.security.transport.ssl.truststore.path" , trustStorePath )
210
- .put ("xpack.security.transport.ssl.supported_protocols" , "TLSv1.2" )
211
228
.put ("path.home" , createTempDir ())
212
229
.setSecureSettings (secureSettings )
213
230
.build ();
214
231
Environment env = randomBoolean () ? null : TestEnvironment .newEnvironment (settings );
215
232
// Create the MockWebServer once for both pre and post checks
216
233
try (MockWebServer server = getSslServer (trustStorePath , "testnode" )) {
217
234
final Consumer <SSLContext > trustMaterialPreChecks = (context ) -> {
218
- try (CloseableHttpClient client = HttpClients . custom (). setSSLContext ( context ). build ( )) {
235
+ try (CloseableHttpClient client = createHttpClient ( context )) {
219
236
privilegedConnect (() -> client .execute (new HttpGet ("https://localhost:" + server .getPort ())).close ());
220
237
} catch (Exception e ) {
221
238
throw new RuntimeException ("Error connecting to the mock server" , e );
@@ -232,7 +249,7 @@ public void testReloadingTrustStore() throws Exception {
232
249
233
250
// Client's truststore doesn't contain the server's certificate anymore so SSLHandshake should fail
234
251
final Consumer <SSLContext > trustMaterialPostChecks = (updatedContext ) -> {
235
- try (CloseableHttpClient client = HttpClients . custom (). setSSLContext ( updatedContext ). build ( )) {
252
+ try (CloseableHttpClient client = createHttpClient ( updatedContext )) {
236
253
SSLHandshakeException sslException = expectThrows (SSLHandshakeException .class , () ->
237
254
privilegedConnect (() -> client .execute (new HttpGet ("https://localhost:" + server .getPort ())).close ()));
238
255
assertThat (sslException .getCause ().getMessage (), containsString ("PKIX path building failed" ));
@@ -259,14 +276,13 @@ public void testReloadingPEMTrustConfig() throws Exception {
259
276
Files .copy (getDataPath ("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testnode_updated.crt" ), updatedCert );
260
277
Settings settings = Settings .builder ()
261
278
.putList ("xpack.security.transport.ssl.certificate_authorities" , serverCertPath .toString ())
262
- .put ("xpack.security.transport.ssl.supported_protocols" , "TLSv1.2" )
263
279
.put ("path.home" , createTempDir ())
264
280
.build ();
265
281
Environment env = randomBoolean () ? null : TestEnvironment .newEnvironment (settings );
266
282
// Create the MockWebServer once for both pre and post checks
267
283
try (MockWebServer server = getSslServer (serverKeyPath , serverCertPath , "testnode" )) {
268
284
final Consumer <SSLContext > trustMaterialPreChecks = (context ) -> {
269
- try (CloseableHttpClient client = HttpClients . custom (). setSSLContext ( context ). build ( )) {
285
+ try (CloseableHttpClient client = createHttpClient ( context )) {
270
286
privilegedConnect (() -> client .execute (new HttpGet ("https://localhost:" + server .getPort ())));//.close());
271
287
} catch (Exception e ) {
272
288
throw new RuntimeException ("Exception connecting to the mock server" , e );
@@ -283,7 +299,7 @@ public void testReloadingPEMTrustConfig() throws Exception {
283
299
284
300
// Client doesn't trust the Server certificate anymore so SSLHandshake should fail
285
301
final Consumer <SSLContext > trustMaterialPostChecks = (updatedContext ) -> {
286
- try (CloseableHttpClient client = HttpClients . custom (). setSSLContext ( updatedContext ). build ( )) {
302
+ try (CloseableHttpClient client = createHttpClient ( updatedContext )) {
287
303
SSLHandshakeException sslException = expectThrows (SSLHandshakeException .class , () ->
288
304
privilegedConnect (() -> client .execute (new HttpGet ("https://localhost:" + server .getPort ())).close ()));
289
305
assertThat (sslException .getCause ().getMessage (), containsString ("PKIX path validation failed" ));
@@ -308,7 +324,6 @@ public void testReloadingKeyStoreException() throws Exception {
308
324
secureSettings .setString ("xpack.security.transport.ssl.keystore.secure_password" , "testnode" );
309
325
Settings settings = Settings .builder ()
310
326
.put ("xpack.security.transport.ssl.keystore.path" , keystorePath )
311
- .put ("xpack.security.transport.ssl.supported_protocols" , "TLSv1.2" )
312
327
.setSecureSettings (secureSettings )
313
328
.put ("path.home" , createTempDir ())
314
329
.build ();
@@ -350,7 +365,6 @@ public void testReloadingPEMKeyConfigException() throws Exception {
350
365
.put ("xpack.security.transport.ssl.key" , keyPath )
351
366
.put ("xpack.security.transport.ssl.certificate" , certPath )
352
367
.putList ("xpack.security.transport.ssl.certificate_authorities" , certPath .toString (), clientCertPath .toString ())
353
- .put ("xpack.security.transport.ssl.supported_protocols" , "TLSv1.2" )
354
368
.put ("path.home" , createTempDir ())
355
369
.setSecureSettings (secureSettings )
356
370
.build ();
@@ -386,7 +400,6 @@ public void testTrustStoreReloadException() throws Exception {
386
400
secureSettings .setString ("xpack.security.transport.ssl.truststore.secure_password" , "testnode" );
387
401
Settings settings = Settings .builder ()
388
402
.put ("xpack.security.transport.ssl.truststore.path" , trustStorePath )
389
- .put ("xpack.security.transport.ssl.supported_protocols" , "TLSv1.2" )
390
403
.put ("path.home" , createTempDir ())
391
404
.setSecureSettings (secureSettings )
392
405
.build ();
@@ -420,7 +433,6 @@ public void testPEMTrustReloadException() throws Exception {
420
433
Files .copy (getDataPath ("/org/elasticsearch/xpack/security/transport/ssl/certs/simple/testclient.crt" ), clientCertPath );
421
434
Settings settings = Settings .builder ()
422
435
.putList ("xpack.security.transport.ssl.certificate_authorities" , clientCertPath .toString ())
423
- .put ("xpack.security.transport.ssl.supported_protocols" , "TLSv1.2" )
424
436
.put ("path.home" , createTempDir ())
425
437
.build ();
426
438
Environment env = randomBoolean () ? null : TestEnvironment .newEnvironment (settings );
@@ -490,7 +502,6 @@ private static MockWebServer getSslServer(Path keyStorePath, String keyStorePass
490
502
}
491
503
final SSLContext sslContext = new SSLContextBuilder ()
492
504
.loadKeyMaterial (keyStore , keyStorePass .toCharArray ())
493
- .setProtocol ("TLSv1.2" )
494
505
.build ();
495
506
MockWebServer server = new MockWebServer (sslContext , false );
496
507
server .enqueue (new MockResponse ().setResponseCode (200 ).setBody ("body" ));
@@ -506,7 +517,6 @@ private static MockWebServer getSslServer(Path keyPath, Path certPath, String pa
506
517
CertParsingUtils .readCertificates (Collections .singletonList (certPath )));
507
518
final SSLContext sslContext = new SSLContextBuilder ()
508
519
.loadKeyMaterial (keyStore , password .toCharArray ())
509
- .setProtocol ("TLSv1.2" )
510
520
.build ();
511
521
MockWebServer server = new MockWebServer (sslContext , false );
512
522
server .enqueue (new MockResponse ().setResponseCode (200 ).setBody ("body" ));
@@ -523,9 +533,8 @@ private static CloseableHttpClient getSSLClient(Path trustStorePath, String trus
523
533
}
524
534
final SSLContext sslContext = new SSLContextBuilder ()
525
535
.loadTrustMaterial (trustStore , null )
526
- .setProtocol ("TLSv1.2" )
527
536
.build ();
528
- return HttpClients . custom (). setSSLContext ( sslContext ). build ( );
537
+ return createHttpClient ( sslContext );
529
538
}
530
539
531
540
/**
@@ -543,9 +552,138 @@ private static CloseableHttpClient getSSLClient(List<Path> trustedCertificatePat
543
552
}
544
553
final SSLContext sslContext = new SSLContextBuilder ()
545
554
.loadTrustMaterial (trustStore , null )
546
- .setProtocol ("TLSv1.2" )
547
555
.build ();
548
- return HttpClients .custom ().setSSLContext (sslContext ).build ();
556
+ return createHttpClient (sslContext );
557
+ }
558
+
559
+ private static CloseableHttpClient createHttpClient (SSLContext sslContext ) {
560
+ return HttpClients .custom ()
561
+ .setConnectionManager (new PoolingHttpClientConnectionManager (
562
+ RegistryBuilder .<ConnectionSocketFactory >create ()
563
+ .register ("http" , PlainConnectionSocketFactory .getSocketFactory ())
564
+ .register ("https" , new SSLConnectionSocketFactory (sslContext , null , null , new DefaultHostnameVerifier ()))
565
+ .build (), getHttpClientConnectionFactory (), null , null , -1 , TimeUnit .MILLISECONDS ))
566
+ .build ();
567
+ }
568
+
569
+ /**
570
+ * Creates our own HttpConnectionFactory that changes how the connection is closed to prevent issues with
571
+ * the MockWebServer going into an endless loop based on the way that HttpClient closes its connection.
572
+ */
573
+ private static HttpConnectionFactory <HttpRoute , ManagedHttpClientConnection > getHttpClientConnectionFactory () {
574
+ return (route , config ) -> {
575
+ ManagedHttpClientConnection delegate = ManagedHttpClientConnectionFactory .INSTANCE .create (route , config );
576
+ return new ManagedHttpClientConnection () {
577
+ @ Override
578
+ public String getId () {
579
+ return delegate .getId ();
580
+ }
581
+
582
+ @ Override
583
+ public void bind (Socket socket ) throws IOException {
584
+ delegate .bind (socket );
585
+ }
586
+
587
+ @ Override
588
+ public Socket getSocket () {
589
+ return delegate .getSocket ();
590
+ }
591
+
592
+ @ Override
593
+ public SSLSession getSSLSession () {
594
+ return delegate .getSSLSession ();
595
+ }
596
+
597
+ @ Override
598
+ public boolean isResponseAvailable (int timeout ) throws IOException {
599
+ return delegate .isResponseAvailable (timeout );
600
+ }
601
+
602
+ @ Override
603
+ public void sendRequestHeader (HttpRequest request ) throws HttpException , IOException {
604
+ delegate .sendRequestHeader (request );
605
+ }
606
+
607
+ @ Override
608
+ public void sendRequestEntity (HttpEntityEnclosingRequest request ) throws HttpException , IOException {
609
+ delegate .sendRequestEntity (request );
610
+ }
611
+
612
+ @ Override
613
+ public HttpResponse receiveResponseHeader () throws HttpException , IOException {
614
+ return delegate .receiveResponseHeader ();
615
+ }
616
+
617
+ @ Override
618
+ public void receiveResponseEntity (HttpResponse response ) throws HttpException , IOException {
619
+ delegate .receiveResponseEntity (response );
620
+ }
621
+
622
+ @ Override
623
+ public void flush () throws IOException {
624
+ delegate .flush ();
625
+ }
626
+
627
+ @ Override
628
+ public InetAddress getLocalAddress () {
629
+ return delegate .getLocalAddress ();
630
+ }
631
+
632
+ @ Override
633
+ public int getLocalPort () {
634
+ return delegate .getLocalPort ();
635
+ }
636
+
637
+ @ Override
638
+ public InetAddress getRemoteAddress () {
639
+ return delegate .getRemoteAddress ();
640
+ }
641
+
642
+ @ Override
643
+ public int getRemotePort () {
644
+ return delegate .getRemotePort ();
645
+ }
646
+
647
+ @ Override
648
+ public void close () throws IOException {
649
+ if (delegate .getSocket () instanceof SSLSocket ) {
650
+ try (SSLSocket socket = (SSLSocket ) delegate .getSocket ()) {
651
+ }
652
+ }
653
+ delegate .close ();
654
+ }
655
+
656
+ @ Override
657
+ public boolean isOpen () {
658
+ return delegate .isOpen ();
659
+ }
660
+
661
+ @ Override
662
+ public boolean isStale () {
663
+ return delegate .isStale ();
664
+ }
665
+
666
+ @ Override
667
+ public void setSocketTimeout (int timeout ) {
668
+ delegate .setSocketTimeout (timeout );
669
+ }
670
+
671
+ @ Override
672
+ public int getSocketTimeout () {
673
+ return delegate .getSocketTimeout ();
674
+ }
675
+
676
+ @ Override
677
+ public void shutdown () throws IOException {
678
+ delegate .shutdown ();
679
+ }
680
+
681
+ @ Override
682
+ public HttpConnectionMetrics getMetrics () {
683
+ return delegate .getMetrics ();
684
+ }
685
+ };
686
+ };
549
687
}
550
688
551
689
private static void privilegedConnect (CheckedRunnable <Exception > runnable ) throws Exception {
0 commit comments