Skip to content

Commit fdbf96b

Browse files
committed
added SSL configuration properties
1 parent 8994c00 commit fdbf96b

File tree

13 files changed

+238
-20
lines changed

13 files changed

+238
-20
lines changed

core/src/main/java/com/arangodb/ArangoDB.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ public interface ArangoDB extends ArangoSerdeAccessor {
356356
/**
357357
* Reset the server log levels
358358
* Revert the server's log level settings to the values they had at startup, as determined by the startup options specified on the command-line, a configuration file, and the factory defaults.
359+
*
359360
* @since ArangoDB 3.12
360361
*/
361362
LogLevelEntity resetLogLevels(LogLevelOptions options);
@@ -484,6 +485,61 @@ public Builder useSsl(final Boolean useSsl) {
484485
return this;
485486
}
486487

488+
/**
489+
* Sets the SSL certificate value as Base64 encoded String
490+
*
491+
* @param sslCertValue the SSL certificate value as Base64 encoded String
492+
* @return {@link ArangoDB.Builder}
493+
*/
494+
public Builder sslCertValue(final String sslCertValue) {
495+
config.setSslCertValue(sslCertValue);
496+
return this;
497+
}
498+
499+
/**
500+
* Sets the SSL certificate type, default: {@code X.509}
501+
*
502+
* @param sslCertType the SSL certificate type
503+
* @return {@link ArangoDB.Builder}
504+
*/
505+
public Builder sslCertType(final String sslCertType) {
506+
config.setSslCertType(sslCertType);
507+
return this;
508+
}
509+
510+
/**
511+
* Sets the SSL Trust manager algorithm
512+
*
513+
* @param sslAlgorithm the name of the SSL Trust manager algorithm
514+
* @return {@link ArangoDB.Builder}
515+
*/
516+
public Builder sslAlgorithm(final String sslAlgorithm) {
517+
config.setSslAlgorithm(sslAlgorithm);
518+
return this;
519+
}
520+
521+
/**
522+
* Sets the SSL keystore type
523+
*
524+
* @param sslKeystoreType the SSL keystore type
525+
* @return {@link ArangoDB.Builder}
526+
*/
527+
public Builder sslKeystoreType(final String sslKeystoreType) {
528+
config.setSslKeystoreType(sslKeystoreType);
529+
return this;
530+
}
531+
532+
/**
533+
* Sets the SSLContext protocol, default: {@code TLS}
534+
*
535+
* @param sslProtocol the name of the SSLContext protocol
536+
* @return {@link ArangoDB.Builder}
537+
*/
538+
public Builder sslProtocol(final String sslProtocol) {
539+
config.setSslProtocol(sslProtocol);
540+
return this;
541+
}
542+
487543
/**
488544
* Sets the SSL context to be used when {@code true} is passed through {@link #useSsl(Boolean)}.
489545
*
@@ -716,6 +772,7 @@ public Builder compressionLevel(Integer level) {
716772

717773
/**
718774
* Configuration specific for {@link com.arangodb.internal.net.ProtocolProvider}.
775+
*
719776
* @return {@link ArangoDB.Builder}
720777
*/
721778
public Builder protocolConfig(ProtocolConfig protocolConfig) {

core/src/main/java/com/arangodb/config/ArangoConfigProperties.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ public interface ArangoConfigProperties {
1919
String KEY_JWT = "jwt";
2020
String KEY_TIMEOUT = "timeout";
2121
String KEY_USE_SSL = "useSsl";
22+
String KEY_SSL_CERT_VALUE = "sslCertValue";
23+
String KEY_SSL_CERT_TYPE = "sslCertType";
24+
String KEY_SSL_ALGORITHM = "sslAlgorithm";
25+
String KEY_SSL_KEYSTORE_TYPE = "sslKeystoreType";
26+
String KEY_SSL_PROTOCOL = "sslProtocol";
2227
String KEY_VERIFY_HOST = "verifyHost";
2328
String KEY_CHUNK_SIZE = "chunkSize";
2429
String KEY_PIPELINING = "pipelining";
@@ -103,6 +108,26 @@ default Optional<Boolean> getUseSsl() {
103108
return Optional.empty();
104109
}
105110

111+
default Optional<String> getSslCertValue() {
112+
return Optional.empty();
113+
}
114+
115+
default Optional<String> getSslCertType() {
116+
return Optional.empty();
117+
}
118+
119+
default Optional<String> getSslAlgorithm() {
120+
return Optional.empty();
121+
}
122+
123+
default Optional<String> getSslKeystoreType() {
124+
return Optional.empty();
125+
}
126+
127+
default Optional<String> getSslProtocol() {
128+
return Optional.empty();
129+
}
130+
106131
default Optional<Boolean> getVerifyHost() {
107132
return Optional.empty();
108133
}

core/src/main/java/com/arangodb/internal/ArangoDefaults.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ public final class ArangoDefaults {
4848
public static final Integer DEFAULT_TIMEOUT = 0;
4949
public static final Long DEFAULT_CONNECTION_TTL_HTTP = 30_000L;
5050
public static final Boolean DEFAULT_USE_SSL = false;
51+
public static final String DEFAULT_SSL_CERT_TYPE = "X.509";
52+
public static final String DEFAULT_SSL_CERT_ALIAS = "arangodb";
53+
public static final String DEFAULT_SSL_PROTOCOL = "TLS";
5154
public static final Boolean DEFAULT_VERIFY_HOST = true;
5255
public static final Integer DEFAULT_CHUNK_SIZE = 30_000;
5356
public static final Boolean DEFAULT_PIPELINING = false;

core/src/main/java/com/arangodb/internal/config/ArangoConfig.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@
1616
import com.fasterxml.jackson.databind.Module;
1717

1818
import javax.net.ssl.SSLContext;
19+
import javax.net.ssl.TrustManagerFactory;
20+
import java.io.ByteArrayInputStream;
1921
import java.lang.reflect.InvocationTargetException;
22+
import java.security.KeyStore;
23+
import java.security.cert.Certificate;
24+
import java.security.cert.CertificateFactory;
2025
import java.util.*;
2126
import java.util.concurrent.Executor;
2227
import java.util.stream.Collectors;
@@ -30,6 +35,11 @@ public class ArangoConfig {
3035
private String password;
3136
private String jwt;
3237
private Boolean useSsl;
38+
private Optional<String> sslCertValue;
39+
private String sslCertType;
40+
private Optional<String> sslAlgorithm;
41+
private Optional<String> sslKeystoreType;
42+
private String sslProtocol;
3343
private SSLContext sslContext;
3444
private Boolean verifyHost;
3545
private Integer chunkSize;
@@ -69,6 +79,11 @@ public void loadProperties(final ArangoConfigProperties properties) {
6979
// FIXME: make jwt field Optional
7080
jwt = properties.getJwt().orElse(null);
7181
useSsl = properties.getUseSsl().orElse(ArangoDefaults.DEFAULT_USE_SSL);
82+
sslCertValue = properties.getSslCertValue();
83+
sslCertType = properties.getSslCertType().orElse(ArangoDefaults.DEFAULT_SSL_CERT_TYPE);
84+
sslAlgorithm = properties.getSslAlgorithm();
85+
sslKeystoreType = properties.getSslKeystoreType();
86+
sslProtocol = properties.getSslProtocol().orElse(ArangoDefaults.DEFAULT_SSL_PROTOCOL);
7287
verifyHost = properties.getVerifyHost().orElse(ArangoDefaults.DEFAULT_VERIFY_HOST);
7388
chunkSize = properties.getChunkSize().orElse(ArangoDefaults.DEFAULT_CHUNK_SIZE);
7489
pipelining = properties.getPipelining().orElse(ArangoDefaults.DEFAULT_PIPELINING);
@@ -151,7 +166,30 @@ public void setUseSsl(Boolean useSsl) {
151166
this.useSsl = useSsl;
152167
}
153168

169+
public void setSslCertValue(String sslCertValue) {
170+
this.sslCertValue = Optional.ofNullable(sslCertValue);
171+
}
172+
173+
public void setSslCertType(String sslCertType) {
174+
this.sslCertType = sslCertType;
175+
}
176+
177+
public void setSslAlgorithm(String sslAlgorithm) {
178+
this.sslAlgorithm = Optional.ofNullable(sslAlgorithm);
179+
}
180+
181+
public void setSslKeystoreType(String sslKeystoreType) {
182+
this.sslKeystoreType = Optional.ofNullable(sslKeystoreType);
183+
}
184+
185+
public void setSslProtocol(String sslProtocol) {
186+
this.sslProtocol = sslProtocol;
187+
}
188+
154189
public SSLContext getSslContext() {
190+
if (sslContext == null) {
191+
sslContext = createSslContext();
192+
}
155193
return sslContext;
156194
}
157195

@@ -342,4 +380,26 @@ public ProtocolConfig getProtocolConfig() {
342380
public void setProtocolConfig(ProtocolConfig protocolConfig) {
343381
this.protocolConfig = protocolConfig;
344382
}
383+
384+
private SSLContext createSslContext() {
385+
try {
386+
if (sslCertValue.isPresent()) {
387+
ByteArrayInputStream is = new ByteArrayInputStream(Base64.getDecoder().decode(sslCertValue.get()));
388+
Certificate cert = CertificateFactory.getInstance(sslCertType).generateCertificate(is);
389+
KeyStore ks = KeyStore.getInstance(sslKeystoreType.orElseGet(KeyStore::getDefaultType));
390+
ks.load(null);
391+
ks.setCertificateEntry("arangodb", cert);
392+
TrustManagerFactory tmf = TrustManagerFactory.getInstance(sslAlgorithm.orElseGet(TrustManagerFactory::getDefaultAlgorithm));
393+
tmf.init(ks);
394+
SSLContext sc = SSLContext.getInstance(sslProtocol);
395+
sc.init(null, tmf.getTrustManagers(), null);
396+
return sc;
397+
} else {
398+
return SSLContext.getDefault();
399+
}
400+
} catch (Exception e) {
401+
throw new RuntimeException(e);
402+
}
403+
}
404+
345405
}

core/src/main/java/com/arangodb/internal/config/ArangoConfigPropertiesImpl.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,31 @@ public Optional<Boolean> getUseSsl() {
109109
return Optional.ofNullable(getProperty(KEY_USE_SSL)).map(Boolean::valueOf);
110110
}
111111

112+
@Override
113+
public Optional<String> getSslCertValue() {
114+
return Optional.ofNullable(getProperty(KEY_SSL_CERT_VALUE));
115+
}
116+
117+
@Override
118+
public Optional<String> getSslCertType() {
119+
return Optional.ofNullable(getProperty(KEY_SSL_CERT_TYPE));
120+
}
121+
122+
@Override
123+
public Optional<String> getSslAlgorithm() {
124+
return Optional.ofNullable(getProperty(KEY_SSL_ALGORITHM));
125+
}
126+
127+
@Override
128+
public Optional<String> getSslKeystoreType() {
129+
return Optional.ofNullable(getProperty(KEY_SSL_KEYSTORE_TYPE));
130+
}
131+
132+
@Override
133+
public Optional<String> getSslProtocol() {
134+
return Optional.ofNullable(getProperty(KEY_SSL_PROTOCOL));
135+
}
136+
112137
@Override
113138
public Optional<Boolean> getVerifyHost() {
114139
return Optional.ofNullable(getProperty(KEY_VERIFY_HOST)).map(Boolean::valueOf);

http-protocol/src/main/java/com/arangodb/http/HttpConnection.java

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
import org.slf4j.LoggerFactory;
5656

5757
import javax.net.ssl.SSLContext;
58-
import java.security.NoSuchAlgorithmException;
5958
import java.util.Collections;
6059
import java.util.Iterator;
6160
import java.util.Map.Entry;
@@ -169,17 +168,7 @@ private static String getUserAgent() {
169168
}
170169

171170
if (Boolean.TRUE.equals(config.getUseSsl())) {
172-
SSLContext ctx;
173-
if (config.getSslContext() != null) {
174-
ctx = config.getSslContext();
175-
} else {
176-
try {
177-
ctx = SSLContext.getDefault();
178-
} catch (NoSuchAlgorithmException e) {
179-
throw ArangoDBException.of(e);
180-
}
181-
}
182-
171+
SSLContext ctx = config.getSslContext();
183172
webClientOptions
184173
.setSsl(true)
185174
.setUseAlpn(true)

test-functional/src/test-ssl/java/com/arangodb/ArangoSslTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
package com.arangodb;
2222

23+
import com.arangodb.config.ArangoConfigProperties;
2324
import com.arangodb.entity.ArangoDBVersion;
2425
import org.junit.jupiter.params.ParameterizedTest;
2526
import org.junit.jupiter.params.provider.EnumSource;
@@ -55,6 +56,36 @@ void connect(Protocol protocol) {
5556
assertThat(version).isNotNull();
5657
}
5758

59+
@ParameterizedTest
60+
@EnumSource(Protocol.class)
61+
void connectWithProperties(Protocol protocol) {
62+
assumeTrue(protocol != Protocol.VST);
63+
64+
final ArangoDB arangoDB = new ArangoDB.Builder()
65+
.protocol(protocol)
66+
.host("172.28.0.1", 8529)
67+
.password("test")
68+
.useSsl(true)
69+
.sslCertValue("MIIDezCCAmOgAwIBAgIEeDCzXzANBgkqhkiG9w0BAQsFADBuMRAwDgYDVQQGEwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAxMTAxMTg1MTE5WhcNMzAxMDMwMTg1MTE5WjBuMRAwDgYDVQQGEwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1WiDnd4+uCmMG539ZNZB8NwI0RZF3sUSQGPx3lkqaFTZVEzMZL76HYvdc9Qg7difyKyQ09RLSpMALX9euSseD7bZGnfQH52BnKcT09eQ3wh7aVQ5sN2omygdHLC7X9usntxAfv7NzmvdogNXoJQyY/hSZff7RIqWH8NnAUKkjqOe6Bf5LDbxHKESmrFBxOCOnhcpvZWetwpiRdJVPwUn5P82CAZzfiBfmBZnB7D0l+/6Cv4jMuH26uAIcixnVekBQzl1RgwczuiZf2MGO64vDMMJJWE9ClZF1uQuQrwXF6qwhuP1Hnkii6wNbTtPWlGSkqeutr004+Hzbf8KnRY4PAgMBAAGjITAfMB0GA1UdDgQWBBTBrv9Awynt3C5IbaCNyOW5v4DNkTANBgkqhkiG9w0BAQsFAAOCAQEAIm9rPvDkYpmzpSIhR3VXG9Y71gxRDrqkEeLsMoEyqGnw/zx1bDCNeGg2PncLlW6zTIipEBooixIE9U7KxHgZxBy0Et6EEWvIUmnr6F4F+dbTD050GHlcZ7eOeqYTPYeQC502G1Fo4tdNi4lDP9L9XZpf7Q1QimRH2qaLS03ZFZa2tY7ah/RQqZL8Dkxx8/zc25sgTHVpxoK853glBVBs/ENMiyGJWmAXQayewY3EPt/9wGwV4KmU3dPDleQeXSUGPUISeQxFjy+jCw21pYviWVJTNBA9l5ny3GhEmcnOT/gQHCvVRLyGLMbaMZ4JrPwb+aAtBgrgeiK4xeSMMvrbhw==")
70+
.verifyHost(false)
71+
.build();
72+
final ArangoDBVersion version = arangoDB.getVersion();
73+
assertThat(version).isNotNull();
74+
}
75+
76+
@ParameterizedTest
77+
@EnumSource(Protocol.class)
78+
void connectWithFileProperties(Protocol protocol) {
79+
assumeTrue(protocol != Protocol.VST);
80+
81+
final ArangoDB arangoDB = new ArangoDB.Builder()
82+
.loadProperties(ArangoConfigProperties.fromFile("arangodb-ssl.properties"))
83+
.protocol(protocol)
84+
.build();
85+
final ArangoDBVersion version = arangoDB.getVersion();
86+
assertThat(version).isNotNull();
87+
}
88+
5889
@ParameterizedTest
5990
@EnumSource(Protocol.class)
6091
void connectWithoutValidSslContext(Protocol protocol) {

test-functional/src/test/java/com/arangodb/ArangoConfigTest.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55
import com.arangodb.internal.config.ArangoConfig;
66
import org.junit.jupiter.api.Test;
77

8+
import javax.net.ssl.SSLContext;
9+
10+
import java.security.NoSuchAlgorithmException;
11+
812
import static org.assertj.core.api.Assertions.assertThat;
913

1014
public class ArangoConfigTest {
1115
@Test
12-
void ArangoConfigDefaultValues() {
16+
void ArangoConfigDefaultValues() throws NoSuchAlgorithmException {
1317
ArangoConfig cfg = new ArangoConfig();
1418
assertThat(cfg.getHosts()).isEqualTo(ArangoDefaults.DEFAULT_HOSTS);
1519
assertThat(cfg.getProtocol()).isEqualTo(Protocol.HTTP2_JSON);
@@ -18,7 +22,7 @@ void ArangoConfigDefaultValues() {
1822
assertThat(cfg.getPassword()).isNull();
1923
assertThat(cfg.getJwt()).isNull();
2024
assertThat(cfg.getUseSsl()).isEqualTo(ArangoDefaults.DEFAULT_USE_SSL);
21-
assertThat(cfg.getSslContext()).isNull();
25+
assertThat(cfg.getSslContext()).isEqualTo(SSLContext.getDefault());
2226
assertThat(cfg.getVerifyHost()).isEqualTo(ArangoDefaults.DEFAULT_VERIFY_HOST);
2327
assertThat(cfg.getChunkSize()).isEqualTo(ArangoDefaults.DEFAULT_CHUNK_SIZE);
2428
assertThat(cfg.getMaxConnections()).isEqualTo(ArangoDefaults.MAX_CONNECTIONS_HTTP2_DEFAULT);
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
arangodb.hosts=172.28.0.1:8529
2+
arangodb.password=test
3+
arangodb.useSsl=true
4+
arangodb.sslCertValue=MIIDezCCAmOgAwIBAgIEeDCzXzANBgkqhkiG9w0BAQsFADBuMRAwDgYDVQQGEwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAxMTAxMTg1MTE5WhcNMzAxMDMwMTg1MTE5WjBuMRAwDgYDVQQGEwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1WiDnd4+uCmMG539ZNZB8NwI0RZF3sUSQGPx3lkqaFTZVEzMZL76HYvdc9Qg7difyKyQ09RLSpMALX9euSseD7bZGnfQH52BnKcT09eQ3wh7aVQ5sN2omygdHLC7X9usntxAfv7NzmvdogNXoJQyY/hSZff7RIqWH8NnAUKkjqOe6Bf5LDbxHKESmrFBxOCOnhcpvZWetwpiRdJVPwUn5P82CAZzfiBfmBZnB7D0l+/6Cv4jMuH26uAIcixnVekBQzl1RgwczuiZf2MGO64vDMMJJWE9ClZF1uQuQrwXF6qwhuP1Hnkii6wNbTtPWlGSkqeutr004+Hzbf8KnRY4PAgMBAAGjITAfMB0GA1UdDgQWBBTBrv9Awynt3C5IbaCNyOW5v4DNkTANBgkqhkiG9w0BAQsFAAOCAQEAIm9rPvDkYpmzpSIhR3VXG9Y71gxRDrqkEeLsMoEyqGnw/zx1bDCNeGg2PncLlW6zTIipEBooixIE9U7KxHgZxBy0Et6EEWvIUmnr6F4F+dbTD050GHlcZ7eOeqYTPYeQC502G1Fo4tdNi4lDP9L9XZpf7Q1QimRH2qaLS03ZFZa2tY7ah/RQqZL8Dkxx8/zc25sgTHVpxoK853glBVBs/ENMiyGJWmAXQayewY3EPt/9wGwV4KmU3dPDleQeXSUGPUISeQxFjy+jCw21pYviWVJTNBA9l5ny3GhEmcnOT/gQHCvVRLyGLMbaMZ4JrPwb+aAtBgrgeiK4xeSMMvrbhw==
5+
arangodb.sslCertType=X.509
6+
arangodb.sslAlgorithm=SunX509
7+
arangodb.sslKeystoreType=jks
8+
arangodb.sslProtocol=TLS
9+
arangodb.verifyHost=false

test-non-functional/src/test/java/mp/ConfigMPDefaultsTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ private void checkResult(ArangoConfigProperties config) {
2323
assertThat(config.getJwt()).isNotPresent();
2424
assertThat(config.getTimeout()).isEmpty();
2525
assertThat(config.getUseSsl()).isEmpty();
26+
assertThat(config.getSslCertValue()).isEmpty();
27+
assertThat(config.getSslCertType()).isEmpty();
28+
assertThat(config.getSslAlgorithm()).isEmpty();
29+
assertThat(config.getSslKeystoreType()).isEmpty();
30+
assertThat(config.getSslProtocol()).isEmpty();
2631
assertThat(config.getVerifyHost()).isEmpty();
2732
assertThat(config.getChunkSize()).isEmpty();
2833
assertThat(config.getPipelining()).isEmpty();

0 commit comments

Comments
 (0)