Skip to content

Commit 43263bc

Browse files
authored
Merge pull request #2001 from rabbitmq/mergify/bp/v5.x/pr-1999
Introduce helper for dev/test TLS setup (backport #1999)
2 parents fd34d84 + 4a05f2b commit 43263bc

10 files changed

Lines changed: 211 additions & 72 deletions

AGENTS.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Instructions for AI Agents
2+
3+
## Overview
4+
5+
This is the [RabbitMQ Java client](https://www.rabbitmq.com/client-libraries/java-api-guide), an AMQP 0-9-1 client library.
6+
7+
Consult the [API guide](https://www.rabbitmq.com/client-libraries/java-api-guide) to learn about how this
8+
library is supposed to be used.
9+
10+
## Supported Java Versions
11+
12+
This client supports Java 8+.
13+
14+
## Build and Test
15+
16+
```bash
17+
make deps
18+
./mvnw clean compile
19+
./mvnw spotless:apply
20+
```
21+
22+
### Running Tests
23+
24+
Tests require a RabbitMQ node on `localhost:5672` with the default `localhost`-scoped credentials (`guest`/`guest`):
25+
26+
```bash
27+
# start a RabbitMQ node in a container
28+
docker run -it --rm --name rabbitmq -p 5672:5672 rabbitmq
29+
```
30+
31+
```bash
32+
./mvnw verify -Drabbitmqctl.bin=DOCKER:rabbitmq
33+
```
34+
35+
To run a single test:
36+
37+
```bash
38+
./mvnw verify -Drabbitmqctl.bin=DOCKER:rabbitmq -Dit.test=DeadLetterExchange
39+
```
40+
41+
## Repository Layout
42+
43+
* `src/main/java/com/rabbitmq/client`: the public API
44+
* `src/main/java/com/rabbitmq/client/impl`: internal packages
45+
* `src/main/java/com/rabbitmq/client/impl/recovery`: automatic connection recovery implementation
46+
* `src/test/java`: tests
47+
* `deps/rabbitmq_codegen`: AMQP 0-9-1 framing code generation tooling (requires Python 3)
48+
* `target/generated-sources`: the AMQP 0-9-1 framing code generated by `rabbitmq_codegen`
49+
* `codegen.py`: a Python 3 script that drives the code generation process
50+
51+
## Key Files
52+
53+
Under `src/main/java/com/rabbitmq/client`:
54+
55+
* `ConnectionFactory.java`: entry point for creating connections
56+
* `Connection.java`, `Channel.java`: key connection and channel interfaces
57+
* `impl/AMQConnection.java`, `impl/ChannelN.java`: connection and channel implementations
58+
* `impl/recovery/AutorecoveringConnection.java`: a `Connection` implementation that supports automatic recovery
59+
60+
## Code Style
61+
62+
* Format code with `./mvnw spotless:apply` at the end of each task
63+
64+
## Comments
65+
66+
* Only add very important comments, both in tests and in the implementation
67+
68+
## Git Instructions
69+
70+
* Never add yourself to the list of commit co-authors
71+
* Never mention yourself in commit messages in any way (no "Generated by", no AI tool links, etc)
72+
73+
## Style Guide
74+
75+
* Never add full stops to Markdown list items

ci/start-broker.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ echo "Running RabbitMQ ${RABBITMQ_IMAGE}"
4444

4545
docker rm -f rabbitmq 2>/dev/null || echo "rabbitmq was not running"
4646
docker run -d --name rabbitmq \
47-
--network host \
47+
-p 5671:5671 -p 5672:5672 \
4848
-v "${PWD}"/rabbitmq-configuration:/etc/rabbitmq \
4949
"${RABBITMQ_IMAGE}"
5050

src/main/java/com/rabbitmq/client/ConnectionFactory.java

Lines changed: 74 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,16 @@
2929
import io.netty.channel.EventLoopGroup;
3030
import io.netty.handler.ssl.SslContext;
3131
import io.netty.handler.ssl.SslContextBuilder;
32+
import org.slf4j.Logger;
33+
import org.slf4j.LoggerFactory;
34+
3235
import java.io.IOException;
3336
import java.net.URI;
3437
import java.net.URISyntaxException;
3538
import java.net.URLDecoder;
3639
import java.security.KeyManagementException;
40+
import java.security.KeyStore;
41+
import java.security.KeyStoreException;
3742
import java.security.NoSuchAlgorithmException;
3843
import java.time.Duration;
3944
import java.util.*;
@@ -47,8 +52,7 @@
4752
import javax.net.ssl.SSLContext;
4853
import javax.net.ssl.SSLSocketFactory;
4954
import javax.net.ssl.TrustManager;
50-
import org.slf4j.Logger;
51-
import org.slf4j.LoggerFactory;
55+
import javax.net.ssl.TrustManagerFactory;
5256

5357
/**
5458
* Convenience factory class to facilitate opening a {@link Connection} to a RabbitMQ node.
@@ -348,8 +352,7 @@ public void setVirtualHost(String virtualHost) {
348352
*
349353
* @param uri is the AMQP URI containing the data
350354
*/
351-
public void setUri(URI uri)
352-
throws URISyntaxException, NoSuchAlgorithmException, KeyManagementException {
355+
public void setUri(URI uri) throws NoSuchAlgorithmException, KeyManagementException {
353356
if ("amqp".equals(uri.getScheme().toLowerCase())) {
354357
setPort(DEFAULT_AMQP_PORT);
355358
} else if ("amqps".equals(uri.getScheme().toLowerCase())) {
@@ -807,12 +810,11 @@ public boolean isSSL() {
807810
}
808811

809812
/**
810-
* Convenience method for configuring TLS using the default set of TLS protocols and a trusting
811-
* TrustManager. This setup is <strong>only suitable for development and QA environments</strong>.
812-
* The trust manager will <strong>trust every server certificate presented</strong> to it, this is
813-
* convenient for local development but <strong>not recommended to use in production</strong> as
814-
* it provides no protection against man-in-the-middle attacks. Prefer {@link
815-
* #useSslProtocol(SSLContext)}.
813+
* Convenience method for configuring TLS using the JVM default trust store and with hostname
814+
* verification enabled. This is the recommended method for enabling TLS in production
815+
* environments.
816+
*
817+
* <p>Use {@link #useSslProtocol(SSLContext)} for more control over the {@link SSLContext}.
816818
*
817819
* <p>Note this method has NO effect when using Netty, use {@link
818820
* com.rabbitmq.client.ConnectionFactory.NettyConfiguration#sslContext(io.netty.handler.ssl.SslContext)}
@@ -822,19 +824,16 @@ public void useSslProtocol() throws NoSuchAlgorithmException, KeyManagementExcep
822824
useSslProtocol(
823825
computeDefaultTlsProtocol(
824826
SSLContext.getDefault().getSupportedSSLParameters().getProtocols()));
827+
useSslProtocol(SSLContext.getDefault());
825828
}
826829

827830
/**
828-
* Convenience method for configuring TLS using the supplied protocol and a very trusting
829-
* TrustManager. This setup is <strong>only suitable for development and QA environments</strong>.
830-
* The trust manager <strong>will trust every server certificate presented</strong> to it, this is
831-
* convenient for local development but not recommended to use in production as it
832-
* <strong>provides no protection against man-in-the-middle attacks</strong>.
831+
* Convenience method for configuring TLS using the supplied protocol, the JVM default trust
832+
* store, and with hostname verification enabled.
833833
*
834-
* <p>Use {@link #useSslProtocol(SSLContext)} in production environments. The produced {@link
835-
* SSLContext} instance will be shared by all the connections created by this connection factory.
836-
*
837-
* <p>Use {@link #setSslContextFactory(SslContextFactory)} for more flexibility.
834+
* <p>The produced {@link SSLContext} instance will be shared by all the connections created by
835+
* this connection factory. Use {@link #setSslContextFactory(SslContextFactory)} for more
836+
* flexibility.
838837
*
839838
* <p>Note this method has NO effect when using Netty, use {@link
840839
* com.rabbitmq.client.ConnectionFactory.NettyConfiguration#sslContext(io.netty.handler.ssl.SslContext)}
@@ -844,7 +843,16 @@ public void useSslProtocol() throws NoSuchAlgorithmException, KeyManagementExcep
844843
*/
845844
public void useSslProtocol(String protocol)
846845
throws NoSuchAlgorithmException, KeyManagementException {
847-
useSslProtocol(protocol, new TrustEverythingTrustManager());
846+
try {
847+
TrustManagerFactory tmf =
848+
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
849+
tmf.init((KeyStore) null);
850+
SSLContext c = SSLContext.getInstance(protocol);
851+
c.init(null, tmf.getTrustManagers(), null);
852+
this.useSslProtocol(c);
853+
} catch (KeyStoreException e) {
854+
throw new KeyManagementException("Failed to initialize default trust manager", e);
855+
}
848856
}
849857

850858
/**
@@ -889,6 +897,26 @@ public void useSslProtocol(String protocol, TrustManager trustManager)
889897
public void useSslProtocol(SSLContext context) {
890898
this.sslContextFactory = name -> context;
891899
setSocketFactory(context.getSocketFactory());
900+
this.enableHostnameVerification();
901+
}
902+
903+
/**
904+
* Configure TLS without any certificate or hostname verification.
905+
*
906+
* <p><strong>DO NOT USE IN PRODUCTION.</strong> This disables all server authentication and
907+
* provides no protection against man-in-the-middle attacks. Use only in local development or CI
908+
* environments where the broker identity is not sensitive.
909+
*
910+
* <p>Note this method has NO effect when using Netty, use {@link
911+
* com.rabbitmq.client.ConnectionFactory.NettyConfiguration#useTlsWithNoVerification()} instead.
912+
*/
913+
public void useTlsWithNoVerification()
914+
throws NoSuchAlgorithmException, KeyManagementException {
915+
logTlsNoVerificationWarning();
916+
this.useSslProtocol(
917+
computeDefaultTlsProtocol(
918+
SSLContext.getDefault().getSupportedSSLParameters().getProtocols()),
919+
new TrustEverythingTrustManager());
892920
}
893921

894922
/**
@@ -1569,6 +1597,22 @@ public ConnectionFactory connectionFactory() {
15691597
return this.cf;
15701598
}
15711599

1600+
/**
1601+
* Configure TLS without any certificate or hostname verification.
1602+
*
1603+
* <p><strong>DO NOT USE IN PRODUCTION.</strong> This disables all server authentication and
1604+
* provides no protection against man-in-the-middle attacks. Use only in local development or CI
1605+
* environments where the broker identity is not sensitive.
1606+
*/
1607+
public NettyConfiguration useTlsWithNoVerification() throws Exception {
1608+
logTlsNoVerificationWarning();
1609+
return sslContext(
1610+
SslContextBuilder.forClient()
1611+
.trustManager(new TrustEverythingTrustManager())
1612+
.endpointIdentificationAlgorithm(null)
1613+
.build());
1614+
}
1615+
15721616
private boolean isTls() {
15731617
return this.sslContextFactory != null;
15741618
}
@@ -1874,6 +1918,7 @@ public int getChannelRpcTimeout() {
18741918
*/
18751919
public void setSslContextFactory(SslContextFactory sslContextFactory) {
18761920
this.sslContextFactory = sslContextFactory;
1921+
this.enableHostnameVerification();
18771922
}
18781923

18791924
/**
@@ -1986,4 +2031,13 @@ public static int ensureUnsignedShort(int value) {
19862031
return value;
19872032
}
19882033
}
2034+
2035+
static void logTlsNoVerificationWarning() {
2036+
LoggerFactory.getLogger("com.rabbitmq.client.security").warn(
2037+
"SECURITY ALERT: this mode trusts every certificate, effectively disabling peer verification, " +
2038+
"and disables hostname verification. " +
2039+
"This is convenient for local development but offers no protection against man-in-the-middle attacks. " +
2040+
"Please see https://www.rabbitmq.com/ssl.html to learn more about peer certificate verification."
2041+
);
2042+
}
19892043
}

src/main/java/com/rabbitmq/client/ConnectionFactoryConfigurator.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -396,9 +396,10 @@ private static void setUpBasicSsl(ConnectionFactory cf, boolean validateServerCe
396396
useDefaultTrustStore(cf, sslAlgorithm, verifyHostname);
397397
} else {
398398
if (sslAlgorithm == null) {
399-
cf.useSslProtocol();
399+
cf.useTlsWithNoVerification();
400400
} else {
401-
cf.useSslProtocol(sslAlgorithm);
401+
ConnectionFactory.logTlsNoVerificationWarning();
402+
cf.useSslProtocol(sslAlgorithm, new TrustEverythingTrustManager());
402403
}
403404
}
404405
}

src/main/java/com/rabbitmq/client/TrustEverythingTrustManager.java

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -13,51 +13,21 @@
1313
// If you have any questions regarding licensing, please contact us at
1414
// info@rabbitmq.com.
1515

16-
1716
package com.rabbitmq.client;
1817

19-
import org.slf4j.LoggerFactory;
20-
2118
import javax.net.ssl.X509TrustManager;
2219
import java.security.cert.X509Certificate;
2320

24-
/**
25-
* Convenience class providing a default implementation of {@link javax.net.ssl.X509TrustManager}.
26-
* Trusts every single certificate presented to it. This implementation does not perform peer
27-
* verification and <strong>provides no protection against Man-in-the-Middle (MITM) attacks</strong> and therefore
28-
* <strong>only suitable for some development and QA environments</strong>.
29-
*/
30-
public class TrustEverythingTrustManager implements X509TrustManager {
21+
final class TrustEverythingTrustManager implements X509TrustManager {
3122

32-
public TrustEverythingTrustManager() {
33-
LoggerFactory.getLogger(TrustEverythingTrustManager.class).warn(
34-
"SECURITY ALERT: this trust manager trusts every certificate, effectively disabling peer verification. " +
35-
"This is convenient for local development but offers no protection against man-in-the-middle attacks. " +
36-
"Please see https://www.rabbitmq.com/ssl.html to learn more about peer certificate verification."
37-
);
38-
}
23+
TrustEverythingTrustManager() {}
3924

40-
/**
41-
* Doesn't even bother looking at its arguments, simply returns,
42-
* which makes the check succeed.
43-
*/
4425
@Override
45-
public void checkClientTrusted(X509Certificate[] chain, String authType) {
46-
// Do nothing.
47-
}
26+
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
4827

49-
/**
50-
* Doesn't even bother looking at its arguments, simply returns,
51-
* which makes the check succeed.
52-
*/
5328
@Override
54-
public void checkServerTrusted(X509Certificate[] chain, String authType) {
55-
// Do nothing.
56-
}
29+
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
5730

58-
/**
59-
* Always returns an empty array of X509Certificates.
60-
*/
6131
@Override
6232
public X509Certificate[] getAcceptedIssuers() {
6333
return new X509Certificate[0];

src/main/java/com/rabbitmq/client/impl/OAuth2ClientCredentialsGrantCredentialsProvider.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@
1616
package com.rabbitmq.client.impl;
1717

1818
import com.fasterxml.jackson.databind.ObjectMapper;
19-
import com.rabbitmq.client.TrustEverythingTrustManager;
19+
import org.slf4j.LoggerFactory;
2020

2121
import java.net.*;
22+
import java.security.cert.X509Certificate;
2223
import java.util.concurrent.atomic.AtomicReference;
2324
import java.util.function.Function;
2425
import javax.net.ssl.*;
@@ -558,11 +559,16 @@ public TlsConfiguration sslContext(SSLContext sslContext) {
558559
* @return a TLS configuration that trusts all servers
559560
*/
560561
public TlsConfiguration dev() {
562+
logTlsNoVerificationWarning();
561563
try {
562564
SSLContext sslContext = SSLContext.getInstance(computeDefaultTlsProtocol(
563565
SSLContext.getDefault().getSupportedSSLParameters().getProtocols()
564566
));
565-
sslContext.init(null, new TrustManager[]{new TrustEverythingTrustManager()}, null);
567+
sslContext.init(null, new TrustManager[]{new X509TrustManager() {
568+
public void checkClientTrusted(X509Certificate[] chain, String authType) {}
569+
public void checkServerTrusted(X509Certificate[] chain, String authType) {}
570+
public java.security.cert.X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
571+
}}, null);
566572
this.sslContext = sslContext;
567573
} catch (NoSuchAlgorithmException | KeyManagementException e) {
568574
throw new OAuthTokenManagementException("Error while creating TLS context for development configuration", e);
@@ -606,4 +612,13 @@ public Token apply(String response) {
606612
}
607613
}
608614
}
615+
616+
private static void logTlsNoVerificationWarning() {
617+
LoggerFactory.getLogger("com.rabbitmq.client.security").warn(
618+
"SECURITY ALERT: this mode trusts every certificate, effectively disabling peer verification, " +
619+
"and disables hostname verification. " +
620+
"This is convenient for local development but offers no protection against man-in-the-middle attacks. " +
621+
"Please see https://www.rabbitmq.com/ssl.html to learn more about peer certificate verification."
622+
);
623+
}
609624
}

0 commit comments

Comments
 (0)