Enable peer verification of remote RabbitMq server
By checking the received certificate and fail to make a connection
if the verification fail.
Solves: Jira GER-1791
Change-Id: Ibb38a84b48039b62077939592425007cb23335bd
diff --git a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/config/RabbitMqConfig.java b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/config/RabbitMqConfig.java
index 353cc21..e52fcff 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/config/RabbitMqConfig.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/eventseiffel/config/RabbitMqConfig.java
@@ -23,12 +23,17 @@
import com.google.inject.Singleton;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.ConnectionFactory;
+import java.io.FileInputStream;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
+import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateFactory;
import java.util.Date;
import java.util.concurrent.TimeUnit;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManagerFactory;
import org.eclipse.jgit.lib.Config;
@Singleton
@@ -45,6 +50,7 @@
private static final String VIRTUAL_HOST = "virtualHost";
private static final String WAIT_FOR_CONFIRM = "waitForConfirm";
private static final String MAX_BATCH_SIZE = "maxBatchSize";
+ private static final String CERTIFICATE = "certificate";
private static final boolean DEFAULT_PERSISTENT_DELIVERY = true;
private static final long DEFAULT_WAIT_FOR_CONFIRM = 5000;
private static final int DEFAULT_MAX_BATCH_SIZE = 1;
@@ -64,6 +70,7 @@
cfg.getBoolean(RABBIT_MQ, null, PERSISTENT_DELIVERY, DEFAULT_PERSISTENT_DELIVERY),
cfg.getString(RABBIT_MQ, null, ROUTING_KEY_TAG),
cfg.getString(RABBIT_MQ, null, APP_ID),
+ cfg.getString(RABBIT_MQ, null, CERTIFICATE),
ConfigUtil.getTimeUnit(
cfg,
RABBIT_MQ,
@@ -105,6 +112,7 @@
boolean persistentDelivery,
String routingKey,
String appId,
+ String certificatePath,
long waitForConfirms,
int maxBatchSize) {
this.exchange = exchange;
@@ -119,7 +127,38 @@
this.connectionFactory = null;
return;
}
+
ConnectionFactory cf = new ConnectionFactory();
+
+ if (certificatePath != null) {
+ try {
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+
+ KeyStore tks = KeyStore.getInstance("JKS");
+ tks.load(null);
+ tks.setCertificateEntry(
+ "server_certificate",
+ certificateFactory.generateCertificate(new FileInputStream(certificatePath)));
+
+ TrustManagerFactory tmf =
+ TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmf.init(tks);
+
+ SSLContext c = SSLContext.getInstance("TLSv1.3");
+ c.init(null, tmf.getTrustManagers(), null);
+
+ cf.useSslProtocol(c);
+ cf.enableHostnameVerification();
+ } catch (Exception e) {
+ logger.atSevere().withCause(e).log(
+ "Failed to setup certificate for peer-verification of RabbitMQ-server");
+ this.connectionFactory = null;
+ return;
+ }
+ } else {
+ logger.atInfo().log("No certificate provided, continuing without peer verification.");
+ }
+
try {
cf.setUri(uri);
} catch (KeyManagementException | NoSuchAlgorithmException | URISyntaxException e) {
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index b3fa201..e68d8ba 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -12,6 +12,7 @@
password = secret
waitForConfirms = 7 seconds
maxBatchSize = 100
+ certificate = /path/to/certificate.crt
[EiffelRepoClient]
graphQlUrl = https://eiffel.company.com/graphql
goRestUrl = https://eiffel.company.com/rest/
@@ -76,6 +77,10 @@
If confirms are disabled this option has no real effect.
(Default: _1_.)
+certificate
+: The certificate used for peer verification of server. If no certificate is set
+we will trust anyone.
+
## Section "EiffelRepoClient" ##
Configuration for connecting to the