|
1 | 1 | /*
|
2 |
| - * Copyright 2012-2023 the original author or authors. |
| 2 | + * Copyright 2012-2025 the original author or authors. |
3 | 3 | *
|
4 | 4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
5 | 5 | * you may not use this file except in compliance with the License.
|
|
16 | 16 |
|
17 | 17 | package org.springframework.boot.testcontainers.service.connection.elasticsearch;
|
18 | 18 |
|
| 19 | +import java.io.ByteArrayInputStream; |
| 20 | +import java.io.IOException; |
| 21 | +import java.security.KeyStore; |
| 22 | +import java.security.KeyStoreException; |
| 23 | +import java.security.NoSuchAlgorithmException; |
| 24 | +import java.security.cert.Certificate; |
| 25 | +import java.security.cert.CertificateException; |
| 26 | +import java.security.cert.CertificateFactory; |
19 | 27 | import java.util.List;
|
20 | 28 |
|
21 | 29 | import org.testcontainers.elasticsearch.ElasticsearchContainer;
|
22 | 30 |
|
23 | 31 | import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails;
|
24 | 32 | import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchConnectionDetails.Node.Protocol;
|
| 33 | +import org.springframework.boot.ssl.SslBundle; |
| 34 | +import org.springframework.boot.ssl.SslStoreBundle; |
25 | 35 | import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
26 | 36 | import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
27 | 37 | import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
| 38 | +import org.springframework.boot.testcontainers.service.connection.Ssl; |
28 | 39 |
|
29 | 40 | /**
|
30 | 41 | * {@link ContainerConnectionDetailsFactory} to create
|
@@ -53,15 +64,83 @@ protected ElasticsearchConnectionDetails getContainerConnectionDetails(
|
53 | 64 | private static final class ElasticsearchContainerConnectionDetails
|
54 | 65 | extends ContainerConnectionDetails<ElasticsearchContainer> implements ElasticsearchConnectionDetails {
|
55 | 66 |
|
| 67 | + private volatile SslBundle sslBundle; |
| 68 | + |
56 | 69 | private ElasticsearchContainerConnectionDetails(ContainerConnectionSource<ElasticsearchContainer> source) {
|
57 | 70 | super(source);
|
58 | 71 | }
|
59 | 72 |
|
| 73 | + @Override |
| 74 | + public String getUsername() { |
| 75 | + return "elastic"; |
| 76 | + } |
| 77 | + |
| 78 | + @Override |
| 79 | + public String getPassword() { |
| 80 | + return getContainer().getEnvMap().get("ELASTIC_PASSWORD"); |
| 81 | + } |
| 82 | + |
60 | 83 | @Override
|
61 | 84 | public List<Node> getNodes() {
|
62 | 85 | String host = getContainer().getHost();
|
63 | 86 | Integer port = getContainer().getMappedPort(DEFAULT_PORT);
|
64 |
| - return List.of(new Node(host, port, Protocol.HTTP, null, null)); |
| 87 | + return List.of(new Node(host, port, (getSslBundle() != null) ? Protocol.HTTPS : Protocol.HTTP, |
| 88 | + getUsername(), getPassword())); |
| 89 | + } |
| 90 | + |
| 91 | + @Override |
| 92 | + public SslBundle getSslBundle() { |
| 93 | + if (this.sslBundle != null) { |
| 94 | + return this.sslBundle; |
| 95 | + } |
| 96 | + SslBundle sslBundle = super.getSslBundle(); |
| 97 | + if (sslBundle != null) { |
| 98 | + this.sslBundle = sslBundle; |
| 99 | + return sslBundle; |
| 100 | + } |
| 101 | + if (hasAnnotation(Ssl.class)) { |
| 102 | + byte[] caCertificate = getContainer().caCertAsBytes().orElse(null); |
| 103 | + if (caCertificate != null) { |
| 104 | + KeyStore trustStore = createTrustStore(caCertificate); |
| 105 | + sslBundle = createSslBundleWithTrustStore(trustStore); |
| 106 | + this.sslBundle = sslBundle; |
| 107 | + return sslBundle; |
| 108 | + } |
| 109 | + } |
| 110 | + return null; |
| 111 | + } |
| 112 | + |
| 113 | + private SslBundle createSslBundleWithTrustStore(KeyStore trustStore) { |
| 114 | + return SslBundle.of(new SslStoreBundle() { |
| 115 | + @Override |
| 116 | + public KeyStore getKeyStore() { |
| 117 | + return null; |
| 118 | + } |
| 119 | + |
| 120 | + @Override |
| 121 | + public String getKeyStorePassword() { |
| 122 | + return null; |
| 123 | + } |
| 124 | + |
| 125 | + @Override |
| 126 | + public KeyStore getTrustStore() { |
| 127 | + return trustStore; |
| 128 | + } |
| 129 | + }); |
| 130 | + } |
| 131 | + |
| 132 | + private KeyStore createTrustStore(byte[] caCertificate) { |
| 133 | + try { |
| 134 | + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); |
| 135 | + keyStore.load(null, null); |
| 136 | + CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); |
| 137 | + Certificate certificate = certFactory.generateCertificate(new ByteArrayInputStream(caCertificate)); |
| 138 | + keyStore.setCertificateEntry("ca", certificate); |
| 139 | + return keyStore; |
| 140 | + } |
| 141 | + catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException ex) { |
| 142 | + throw new IllegalStateException("Failed to create keystore from CA certificate", ex); |
| 143 | + } |
65 | 144 | }
|
66 | 145 |
|
67 | 146 | }
|
|
0 commit comments