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