Skip to content

Commit f80bb06

Browse files
authored
Merge pull request #6265 from michael-simons/feature/make_neo4j_encryption_configurable
Make Neo4j transport encryption configurable
2 parents c474979 + 67f6073 commit f80bb06

File tree

4 files changed

+186
-8
lines changed

4 files changed

+186
-8
lines changed

extensions/neo4j/runtime/pom.xml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,17 @@
2626
<artifactId>quarkus-smallrye-health</artifactId>
2727
<optional>true</optional>
2828
</dependency>
29-
<dependency>
30-
<groupId>org.graalvm.nativeimage</groupId>
31-
<artifactId>svm</artifactId>
32-
</dependency>
3329

3430
<dependency>
3531
<groupId>org.neo4j.driver</groupId>
3632
<artifactId>neo4j-java-driver</artifactId>
3733
</dependency>
34+
35+
<dependency>
36+
<groupId>io.quarkus</groupId>
37+
<artifactId>quarkus-junit5-internal</artifactId>
38+
<scope>test</scope>
39+
</dependency>
3840
</dependencies>
3941

4042
<build>

extensions/neo4j/runtime/src/main/java/io/quarkus/neo4j/runtime/Neo4jConfiguration.java

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package io.quarkus.neo4j.runtime;
22

3+
import java.io.File;
4+
import java.nio.file.Path;
35
import java.time.Duration;
6+
import java.util.Optional;
7+
8+
import org.neo4j.driver.Config;
49

510
import io.quarkus.runtime.annotations.ConfigDocSection;
611
import io.quarkus.runtime.annotations.ConfigGroup;
@@ -28,6 +33,19 @@ public class Neo4jConfiguration {
2833
@ConfigDocSection
2934
public Authentication authentication;
3035

36+
/**
37+
* If the driver should use encrypted traffic.
38+
*/
39+
@ConfigItem
40+
public boolean encrypted;
41+
42+
/**
43+
* Configure trust settings for encrypted traffic.
44+
*/
45+
@ConfigItem
46+
@ConfigDocSection
47+
public TrustSettings trustSettings;
48+
3149
/**
3250
* Connection pool.
3351
*/
@@ -54,7 +72,67 @@ static class Authentication {
5472
* Set this to true to disable authentication.
5573
*/
5674
@ConfigItem
57-
public boolean disabled = false;
75+
public boolean disabled;
76+
}
77+
78+
@ConfigGroup
79+
static class TrustSettings {
80+
81+
public enum Strategy {
82+
83+
TRUST_ALL_CERTIFICATES,
84+
85+
TRUST_CUSTOM_CA_SIGNED_CERTIFICATES,
86+
87+
TRUST_SYSTEM_CA_SIGNED_CERTIFICATES
88+
}
89+
90+
/**
91+
* Configures which trust strategy to apply when using encrypted traffic.
92+
*/
93+
@ConfigItem(defaultValue = "TRUST_SYSTEM_CA_SIGNED_CERTIFICATES")
94+
public Strategy strategy;
95+
96+
/**
97+
* The file of the certificate to use.
98+
*/
99+
@ConfigItem
100+
public Optional<Path> certFile = Optional.empty();
101+
102+
/**
103+
* If hostname verification is used.
104+
*/
105+
@ConfigItem
106+
public boolean hostnameVerificationEnabled;
107+
108+
Config.TrustStrategy toInternalRepresentation() {
109+
110+
Config.TrustStrategy internalRepresentation;
111+
Strategy nonNullStrategy = strategy == null ? Strategy.TRUST_SYSTEM_CA_SIGNED_CERTIFICATES : strategy;
112+
switch (nonNullStrategy) {
113+
case TRUST_ALL_CERTIFICATES:
114+
internalRepresentation = Config.TrustStrategy.trustAllCertificates();
115+
break;
116+
case TRUST_SYSTEM_CA_SIGNED_CERTIFICATES:
117+
internalRepresentation = Config.TrustStrategy.trustSystemCertificates();
118+
break;
119+
case TRUST_CUSTOM_CA_SIGNED_CERTIFICATES:
120+
121+
File certFile = this.certFile.map(Path::toFile).filter(File::isFile)
122+
.orElseThrow(() -> new RuntimeException("Configured trust strategy requires a certificate file."));
123+
internalRepresentation = Config.TrustStrategy.trustCustomCertificateSignedBy(certFile);
124+
break;
125+
default:
126+
throw new RuntimeException("Unknown trust strategy: " + this.strategy.name());
127+
}
128+
129+
if (hostnameVerificationEnabled) {
130+
internalRepresentation.withHostnameVerification();
131+
} else {
132+
internalRepresentation.withoutHostnameVerification();
133+
}
134+
return internalRepresentation;
135+
}
58136
}
59137

60138
@ConfigGroup

extensions/neo4j/runtime/src/main/java/io/quarkus/neo4j/runtime/Neo4jDriverRecorder.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ private Driver initializeDriver(Neo4jConfiguration configuration,
4242
}
4343

4444
Config.ConfigBuilder configBuilder = createBaseConfig();
45-
configureSsl(configBuilder);
45+
configureSsl(configBuilder, configuration);
4646
configurePoolSettings(configBuilder, configuration.pool);
4747

4848
Driver driver = GraphDatabase.driver(uri, authToken, configBuilder.build());
@@ -62,13 +62,21 @@ private static Config.ConfigBuilder createBaseConfig() {
6262
return configBuilder;
6363
}
6464

65-
private static void configureSsl(Config.ConfigBuilder configBuilder) {
65+
private static void configureSsl(Config.ConfigBuilder configBuilder,
66+
Neo4jConfiguration configuration) {
6667

6768
// Disable encryption regardless of user configuration when ssl is not natively enabled.
6869
if (ImageInfo.inImageRuntimeCode() && !SslContextConfiguration.isSslNativeEnabled()) {
6970
log.warn(
70-
"Native SSL is disabled, communication between this client and the Neo4j server won't be encrypted.");
71+
"Native SSL is disabled, communication between this client and the Neo4j server cannot be encrypted.");
7172
configBuilder.withoutEncryption();
73+
} else {
74+
if (configuration.encrypted) {
75+
configBuilder.withEncryption();
76+
configBuilder.withTrustStrategy(configuration.trustSettings.toInternalRepresentation());
77+
} else {
78+
configBuilder.withoutEncryption();
79+
}
7280
}
7381
}
7482

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package io.quarkus.neo4j.runtime;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertThrows;
6+
import static org.junit.jupiter.api.Assertions.assertTrue;
7+
8+
import java.io.IOException;
9+
import java.nio.file.Files;
10+
import java.nio.file.Paths;
11+
import java.util.Optional;
12+
13+
import org.junit.jupiter.api.Nested;
14+
import org.junit.jupiter.api.Test;
15+
import org.neo4j.driver.Config;
16+
17+
import io.quarkus.neo4j.runtime.Neo4jConfiguration.TrustSettings;
18+
19+
class Neo4jConfigurationTest {
20+
21+
@Nested
22+
class TrustSettingsTest {
23+
24+
@Test
25+
void defaultsShouldWork() {
26+
27+
TrustSettings trustSettings = new TrustSettings();
28+
29+
Config.TrustStrategy internalRepresentation = trustSettings.toInternalRepresentation();
30+
assertFalse(internalRepresentation.isHostnameVerificationEnabled());
31+
assertEquals(Config.TrustStrategy.Strategy.TRUST_SYSTEM_CA_SIGNED_CERTIFICATES,
32+
internalRepresentation.strategy());
33+
}
34+
35+
@Test
36+
void trustAllSettingShouldWork() {
37+
38+
TrustSettings trustSettings = new TrustSettings();
39+
trustSettings.strategy = TrustSettings.Strategy.TRUST_ALL_CERTIFICATES;
40+
41+
Config.TrustStrategy internalRepresentation = trustSettings.toInternalRepresentation();
42+
assertEquals(Config.TrustStrategy.Strategy.TRUST_ALL_CERTIFICATES, internalRepresentation.strategy());
43+
}
44+
45+
@Test
46+
void hostnameVerificationSettingShouldWork() {
47+
48+
TrustSettings trustSettings = new TrustSettings();
49+
trustSettings.hostnameVerificationEnabled = true;
50+
51+
Config.TrustStrategy internalRepresentation = trustSettings.toInternalRepresentation();
52+
assertTrue(internalRepresentation.isHostnameVerificationEnabled());
53+
}
54+
55+
@Test
56+
void customCaShouldRequireCertFile() {
57+
58+
TrustSettings trustSettings = new TrustSettings();
59+
trustSettings.strategy = TrustSettings.Strategy.TRUST_CUSTOM_CA_SIGNED_CERTIFICATES;
60+
61+
String msg = assertThrows(RuntimeException.class, () -> trustSettings.toInternalRepresentation())
62+
.getMessage();
63+
assertEquals("Configured trust strategy requires a certificate file.", msg);
64+
}
65+
66+
@Test
67+
void customCaShouldRequireExistingCertFile() {
68+
69+
TrustSettings trustSettings = new TrustSettings();
70+
trustSettings.strategy = TrustSettings.Strategy.TRUST_CUSTOM_CA_SIGNED_CERTIFICATES;
71+
trustSettings.certFile = Optional.of(Paths.get("na"));
72+
73+
String msg = assertThrows(RuntimeException.class, () -> trustSettings.toInternalRepresentation())
74+
.getMessage();
75+
assertEquals("Configured trust strategy requires a certificate file.", msg);
76+
}
77+
78+
@Test
79+
void trustCustomCaSettingShouldWork() throws IOException {
80+
81+
TrustSettings trustSettings = new TrustSettings();
82+
trustSettings.strategy = TrustSettings.Strategy.TRUST_CUSTOM_CA_SIGNED_CERTIFICATES;
83+
trustSettings.certFile = Optional.of(Files.createTempFile("quarkus-neo4j-test", ".cert"));
84+
85+
Config.TrustStrategy internalRepresentation = trustSettings.toInternalRepresentation();
86+
assertEquals(Config.TrustStrategy.Strategy.TRUST_CUSTOM_CA_SIGNED_CERTIFICATES,
87+
internalRepresentation.strategy());
88+
}
89+
}
90+
}

0 commit comments

Comments
 (0)