RabbitMQ has inbuilt support for TLS. This includes client connections and popular plugins, where applicable, such as Federation links. It is also possible to use TLS to encrypt inter-node connections in clusters.
This guide covers various topics related to TLS in RabbitMQ:
TLS can be enabled for all protocols supported by RabbitMQ, not just AMQP 0-9-1, which this guide focuses on. HTTP API can be configured to use TLS (HTTPS) as well.
For an overview of common TLS troubleshooting techniques, see Troubleshooting TLS-related issues.
For client connections, there are two common approaches:
In order to support TLS connections, RabbitMQ needs TLS and crypto-related modules to be available in the Erlang/OTP installation. The recommended Erlang/OTP version to use with TLS is the most recent supported Erlang release. Earlier versions, even if they are supported, may work for most certificates but have known limitations (see below).
The Erlang crypto, asn1, public_key, and ssl libraries (applications) must be installed and functional. On Debian and Ubuntu this is provided by the erlang-ssl package. The zero dependency Erlang RPM for RabbitMQ includes the above modules.
If Erlang/OTP is compiled from source, it is necessary to ensure that configure finds OpenSSL and builds the above libraries.
When investigating TLS connectivity issues, please keep in mind that in the vast majority of cases they are environment-specific and do not indicate a bug or limitation in Erlang/OTP's TLS implementation. Please go through the steps outlined in the Troubleshooting TLS guide to gather more information first.
If elliptic curve cryptography (ECC) cipher suites is expected to be used, a recent supported Erlang release (e.g. 19.3.x) is highly recommended. Earlier releases have known limitations around ECC.
If you face the above limitations or any other incompatibilities, use the TLS termination option (see above).
For Windows XP users running RabbitMQ as a service: It is not possible to use SSL with RabbitMQ running as a service on Windows XP in combination with OpenSSL versions 0.9.8 or later. The bug has been confirmed on Windows XP SP3 with OpenSSL v0.9.8r and v1.0.0d. Suggested workarounds if you want to run RabbitMQ as a service is to upgrade to a later Windows release or downgrade to an earlier version of OpenSSL (v0.9.7e is known to work).
This guide walks you through the process of setting up a Certificate Authority and using it to generate client and server certificate/key pairs. Keys and certificates are necessary for RabbitMQ and clients that connect to the server on a TLS-enabled port. However, the process is fairly labourious and can be error-prone. An easier way of generating all that stuff on MacOS or Linux is with tls-gen: you will need Python 3.4+, make and openssl in PATH.
Note that tls-gen and the certificate/key pairs it generates are self-signed and only suitable for development and test environments. The vast majority of production environments should use certificates and keys issued by a well known commercial CA.
tls-gen supports RSA and Elliptic Curve Cryptography algorithms for key generation.
TLS is a large and complex topic. For a thorough understanding of TLS, OpenSSL and how to get the most out of it, we would recommend the use of other resources, for example Network Security with OpenSSL.
TLS can be used simply to establish an encrypted communication channel, but can additionally exchange signed certificates between the end points of the channel, and those certificates can optionally be verified. The verification of a certificate requires establishing a chain of trust from a known, trusted root certificate, and the certificate presented. The root certificate is a self-signed certificate, made available by a Certificate Authority. These exist as commercial companies, and will, for a fee, sign TLS (x509) certificates that you have generated.
For the purposes of this guide, we will start by creating our own Certificate Authority. Once we have done this, we will generate signed certificates for the server and clients, in a number of formats. These will we then use with the Java, .Net and Erlang AMQP clients. Note that Mono has more stringent requirements on OpenSSL certificates (and a few bugs too), so we will be specifying slightly more stringent key usage constraints than is normally necessary.
mkdir testca cd testca mkdir certs private chmod 700 private echo 01 > serial touch index.txt
Now place the following in openssl.cnf within the testca directory we've just created:
[ ca ] default_ca = testca [ testca ] dir = . certificate = $dir/cacert.pem database = $dir/index.txt new_certs_dir = $dir/certs private_key = $dir/private/cakey.pem serial = $dir/serial default_crl_days = 7 default_days = 365 default_md = sha256 policy = testca_policy x509_extensions = certificate_extensions [ testca_policy ] commonName = supplied stateOrProvinceName = optional countryName = optional emailAddress = optional organizationName = optional organizationalUnitName = optional domainComponent = optional [ certificate_extensions ] basicConstraints = CA:false [ req ] default_bits = 2048 default_keyfile = ./private/cakey.pem default_md = sha256 prompt = yes distinguished_name = root_ca_distinguished_name x509_extensions = root_ca_extensions [ root_ca_distinguished_name ] commonName = hostname [ root_ca_extensions ] basicConstraints = CA:true keyUsage = keyCertSign, cRLSign [ client_ca_extensions ] basicConstraints = CA:false keyUsage = digitalSignature,keyEncipherment extendedKeyUsage = 1.3.6.1.5.5.7.3.2 [ server_ca_extensions ] basicConstraints = CA:false keyUsage = digitalSignature,keyEncipherment extendedKeyUsage = 1.3.6.1.5.5.7.3.1
Now we can generate the key and certificates that our test Certificate Authority will use. Still within the testca directory:
openssl req -x509 -config openssl.cnf -newkey rsa:2048 -days 365 \
-out cacert.pem -outform PEM -subj /CN=MyTestCA/ -nodes
openssl x509 -in cacert.pem -out cacert.cer -outform DER
This is all that is needed to generate our test Certificate Authority. The root certificate is in testca/cacert.pem and is also in testca/cacert.cer. These two files contain the same information, but in different formats. Whilst the vast majority of the world is perfectly happy to use the PEM format, Microsoft and Mono like to be different, and so use the DER format.
Having set up our Certificate Authority, we now need to generate keys and certificates for the clients and the server. The Erlang client and the RabbitMQ broker are both able to use PEM files directly. They will both be informed of three files: the root certificate, which is implicitly trusted, the private key, which is used to prove ownership of the public certificate being presented, and the public certificate itself, which identifies the peer.
For convenience, we provide to the Java and .Net clients, a PKCS #12 store, which contains both the client's certificate and key. The PKCS store is usually password protected itself, and so that password must also be provided.
The process for creating server and client certificates is very similar. First the server:
cd ..
ls
# => testca
mkdir server
cd server
openssl genrsa -out key.pem 2048
openssl req -new -key key.pem -out req.pem -outform PEM \
-subj /CN=$(hostname)/O=server/ -nodes
cd ../testca
openssl ca -config openssl.cnf -in ../server/req.pem -out \
../server/cert.pem -notext -batch -extensions server_ca_extensions
cd ../server
openssl pkcs12 -export -out keycert.p12 -in cert.pem -inkey key.pem \
-passout pass:MySecretPassword
And now the client:
cd ..
ls
# => server testca
mkdir client
cd client
openssl genrsa -out key.pem 2048
openssl req -new -key key.pem -out req.pem -outform PEM \
-subj /CN=$(hostname)/O=client/ -nodes
cd ../testca
openssl ca -config openssl.cnf -in ../client/req.pem -out \
../client/cert.pem -notext -batch -extensions client_ca_extensions
cd ../client
openssl pkcs12 -export -out keycert.p12 -in cert.pem -inkey key.pem \
-passout pass:MySecretPassword
The two examples above generate private keys that are 2048 bits in size. It is possible to use longer (and thus more secure but also slower to generate) keys by providing a different value to openssl genrsa, e.g.:
openssl genrsa -out key.pem 4096
Another option would be to generate a key using Elliptic Curve Cryptography. Instead of openssl genrsa use openssl ecparam like so:
openssl ecparam -out key.pem -genkey -name prime256v1prime256v1 in the example above is an elliptic curve name. Different versions of OpenSSL will have a different set of curves available, list them with openssl ecparam -list_curves.
To enable the SSL/TLS support in RabbitMQ, we need to provide to RabbitMQ the location of the root certificate, the server's certificate file, and the server's key. We also need to tell it to listen on a socket that is going to be used for TLS connections, and we need to tell it whether it should ask for clients to present certificates, and if the client does present a certificate, whether we should accept the certificate if we can't establish a chain of trust to it. These settings are controlled by severalconfiguration keys:
The simplest way to set these options, is to edit the configuration file. An example of the config file is below, which will start one ssl_listener on port 5671 on all interfaces on this hostname:
listeners.ssl.default = 5671 ssl_options.cacertfile = /path/to/testca/cacert.pem ssl_options.certfile = /path/to/server/cert.pem ssl_options.keyfile = /path/to/server/key.pem ssl_options.verify = verify_peer ssl_options.fail_if_no_peer_cert = falseBelow is the same example using the classic config format:
[
{rabbit, [
{ssl_listeners, [5671]},
{ssl_options, [{cacertfile,"/path/to/testca/cacert.pem"},
{certfile,"/path/to/server/cert.pem"},
{keyfile,"/path/to/server/key.pem"},
{verify,verify_peer},
{fail_if_no_peer_cert,false}]}
]}
].
Note to Windows users: Backslashes ("\") in the configuration file are interpreted as escape sequences - so for example to specify the path c:\cacert.pem for the CA certificate you would need to use "c:\\cacert.pem" or "c:/cacert.pem".
When a web browser connects to an HTTPS web server, the server presents its public certificate, the web browser attempts to establish a chain of trust between the root certificates the browser is aware of and the server's certificate, and all being well, an encrypted communication channel is established. Although not used normally by web browsers and web servers, TLS allows the server to ask the client to present a certificate. In this way the server can verify that the client is who they say they are.
This policy of whether or not the server asks for a certificate from the client, and whether or not they demand that they are able to trust the certificate, is what the verify and fail_if_no_peer_cert arguments control. By setting the fail_if_no_peer_cert option to false, we state that we're prepared to accept clients which don't have a certificate to send us, but through the verify option set to verify_peer, we state that if the client does send us a certificate, we must be able to establish a chain of trust to it.
Note that if the verify_none mode is used, no certificate exchange takes place from the client to the server and rabbitmqctl list_connections will output empty strings for the peer certificate info items.
After starting the broker, you should then see the following in the rabbit.log:
=INFO REPORT==== 9-Aug-2010::15:10:55 === started TCP Listener on 0.0.0.0:5672 =INFO REPORT==== 9-Aug-2010::15:10:55 === started SSL Listener on 0.0.0.0:5671
Also, take note of the last line, which shows that RabbitMQ server is up and running and listening for ssl connections.
Currently, we're telling RabbitMQ to look at the testca/cacert.pem file. This contains just the public certificate of our test Certificate Authority. We may have certificates being presented by clients which have been signed by several different Certificate Authorities, and we wish RabbitMQ to trust all of them. Therefore, we can simply append these certificates to one another and provide the path to this new file as the cacerts argument to RabbitMQ:
cat testca/cacert.pem >> all_cacerts.pem cat otherca/cacert.pem >> all_cacerts.pem
and so forth.
TLS offers peer verification (validation), a way for client and server to verify each other's identity based on peer's certificate information. When peer verification is enabled, typically the hostname of the server you're connecting to needs to match the CN (Common Name) field on the server's certificate, otherwise the certificate will be rejected. However, peer verification doesn't have to be limited to just CN and hostname matching in general.
This is why the commands at the start of this guide specify ...-subj /CN=$(hostname)/... which dynamically looks up your hostname. If you're generating certificates on one machine, and using them on the other then be sure to swap out the $(hostname) section, and replace it with the correct hostname for your server.
On the server end, peer verification is primarily controlled using two rabbit.ssl_options options: verify and fail_if_no_peer_cert. For example, the following config will perform peer verification and reject clients that do not provide a certificate:
[
{rabbit, [
{ssl_listeners, [5671]},
{ssl_options, [{cacertfile,"/path/to/testca/cacert.pem"},
{certfile,"/path/to/server/cert.pem"},
{keyfile,"/path/to/server/key.pem"},
{verify, verify_peer},
{fail_if_no_peer_cert, true}]}
]}
].
How exactly peer verification is configured in client libraries varies from library to library.
Peer verification is highly recommended in production environments. With careful consideration, disabling it can make sense in certain environments (e.g. development).
It is possible to provide a password for private key using the password option:
listeners.ssl.1 = 5671 ssl_options.cacertfile = /path/to/ca_certificate.pem ssl_options.certfile = /path/to/server_certificate.pem ssl_options.keyfile = /path/to/server_key.pem ssl_options.password = t0p$3kRe7The same example using the classic config format:
[
{rabbit, [
{ssl_listeners, [5671]},
{ssl_options, [{cacertfile,"/path/to/ca_certificate.pem"},
{certfile, "/path/to/server_certificate.pem"},
{keyfile, "/path/to/server_key.pem"},
{password, "t0p$3kRe7"}
]}
]}
].
POODLE is a known SSL/TLS attack that originally compromised SSLv3. Starting with version 3.4.0, RabbitMQ server refuses to accept SSLv3 connections. In December 2014, a modified version of the POODLE attack that affects TLSv1.0 was announced. It is therefore recommended to either run Erlang 18.0 or later, which eliminates TLS 1.0 implementation vulnerability to POODLE, or disable TLSv1.0 support (see below).
BEAST attack is a known vulnerability that affects TLSv1.0. To mitigate it, disable TLSv1.0 support (see below).
To limit enabled SSL/TLS protocol versions, use the versions option.
Using the standard config:listeners.ssl.1 = 5671 ssl_options.cacertfile = /path/to/ca_certificate.pem ssl_options.certfile = /path/to/server_certificate.pem ssl_options.keyfile = /path/to/server_key.pem ssl_options.versions.1 = tlsv1.2 ssl_options.versions.2 = tlsv1.1 ssl_options.versions.3 = tlsv1Using the advanced config:
%% advanced config here is only used to configure TLS versions
[{ssl, [{versions, ['tlsv1.2', 'tlsv1.1', tlsv1]}]}].
Using classic config format:
To limit enabled TLS (SSL protocol versions, use the versions option:
%% Disable SSLv3.0 support, leaves TLSv1.0 enabled.
[
{ssl, [{versions, ['tlsv1.2', 'tlsv1.1', tlsv1]}]},
{rabbit, [
{ssl_listeners, [5671]},
{ssl_options, [{cacertfile,"/path/to/ca_certificate.pem"},
{certfile, "/path/to/server_certificate.pem"},
{keyfile, "/path/to/server_key.pem"},
{versions, ['tlsv1.2', 'tlsv1.1', tlsv1]}
]}
]}
].
The examples below disable SSLv3.0 and TLSv1.0 support.
Using the standard config:
listeners.ssl.1 = 5671 ssl_options.cacertfile = /path/to/ca_certificate.pem ssl_options.certfile = /path/to/server_certificate.pem ssl_options.keyfile = /path/to/server_key.pem ssl_options.versions.1 = tlsv1.2 ssl_options.versions.2 = tlsv1.1Using the advanced config:
[{ssl, [{versions, ['tlsv1.2', 'tlsv1.1']}]}].
Using classic config format:
%% Disable SSLv3.0 and TLSv1.0 support.
[
{ssl, [{versions, ['tlsv1.2', 'tlsv1.1']}]},
{rabbit, [
{ssl_listeners, [5671]},
{ssl_options, [{cacertfile,"/path/to/ca_certificate.pem"},
{certfile, "/path/to/server_certificate.pem"},
{keyfile, "/path/to/server_key.pem"},
{versions, ['tlsv1.2', 'tlsv1.1']}
]}
]}
].
# connect using SSLv3 openssl s_client -connect 127.0.0.1:5671 -ssl3
# connect using TLSv1.0 through v1.2 openssl s_client -connect 127.0.0.1:5671 -tls1and look for the following in the output:
SSL-Session: Protocol : TLSv1
Since RabbitMQ server can be configured to support only specific TLS versions, it may be necessary to configure preferred TLS version in the Java client. This is done using the ConnectionFactory#useSslProtocol overloads that accept a protocol version name or a SSLContext:
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5671);
factory.useSslProtocol("TLSv1.2");
As of Java client 3.6.4, the library will attempt to use the latest TLS version supported by the runtime.
Disabling TLSv1.0 limits the number of client platforms supported. Below is a table that explains what TLS versions are supported by what JDK and .NET releases.
| TLS version | Minimum JDK version | Minimum .NET version |
| TLS 1.0 | JDK 5 (RabbitMQ Java client requires 8) | .NET 2.0 (RabbitMQ .NET client requires 4.5.1) |
| TLS 1.1 | JDK 7 (see Protocols, JDK 8 recommended) | .NET 4.5 |
| TLS 1.2 | JDK 7 (see Protocols, JDK 8 recommended) | .NET 4.5 |
It is possible to configure what cipher suites will be used by RabbitMQ. Note that not all suites will be available on all systems. For example, to use elliptic curve ciphers, please run the most recent supported Erlang release available. The below examples demonstrates how the ciphers TLS option is used.
Cipher suites can only be configured using the advanced config file or the classic config format:%% List allowed ciphers
[
{ssl, [{versions, ['tlsv1.2', 'tlsv1.1']}]},
{rabbit, [
{ssl_listeners, [5671]},
{ssl_options, [{cacertfile,"/path/to/ca_certificate.pem"},
{certfile, "/path/to/server_certificate.pem"},
{keyfile, "/path/to/server_key.pem"},
{versions, ['tlsv1.2', 'tlsv1.1']},
%% This list is just an example!
%% Not all cipher suites are available on all machines.
%% Cipher suite order is important: preferred suites
%% should be listed first.
%% Different suites have different security and CPU load characteristics.
{ciphers, [
{ecdhe_ecdsa,aes_256_gcm,null,sha384},
{ecdhe_rsa,aes_256_gcm,null,sha384},
{ecdhe_ecdsa,aes_256_cbc,sha384,sha384},
{ecdhe_rsa,aes_256_cbc,sha384,sha384},
{ecdh_ecdsa,aes_256_gcm,null,sha384},
{ecdh_rsa,aes_256_gcm,null,sha384},
{ecdh_ecdsa,aes_256_cbc,sha384,sha384},
{ecdh_rsa,aes_256_cbc,sha384,sha384},
{dhe_rsa,aes_256_gcm,null,sha384},
{dhe_dss,aes_256_gcm,null,sha384},
{dhe_rsa,aes_256_cbc,sha256},
{dhe_dss,aes_256_cbc,sha256},
{rsa,aes_256_gcm,null,sha384},
{rsa,aes_256_cbc,sha256},
{ecdhe_ecdsa,aes_128_gcm,null,sha256},
{ecdhe_rsa,aes_128_gcm,null,sha256},
{ecdhe_ecdsa,aes_128_cbc,sha256,sha256},
{ecdhe_rsa,aes_128_cbc,sha256,sha256},
{ecdh_ecdsa,aes_128_gcm,null,sha256},
{ecdh_rsa,aes_128_gcm,null,sha256},
{ecdh_ecdsa,aes_128_cbc,sha256,sha256},
{ecdh_rsa,aes_128_cbc,sha256,sha256},
{dhe_rsa,aes_128_gcm,null,sha256},
{dhe_dss,aes_128_gcm,null,sha256},
{dhe_rsa,aes_128_cbc,sha256},
{ecdh_rsa,aes_128_gcm,null,sha256}
]}
]}
]}
].
To list all cipher suites supported by installed Erlang runtime, use
rabbitmqctl eval 'ssl:cipher_suites().'the output uses Erlang terms and thus can be copied into RabbitMQ's classic or advanced config file.
The same suites can be listed in the OpenSSL format:
rabbitmqctl eval 'ssl:cipher_suites(openssl).'
An IANA and Erlang/OTP cipher suite correspondence table can be used to find corresponding values in both formats.
When overriding cipher suites, it is highly recommended that server-preferred cipher suite ordering is enforced (see the following section).
During TLS connection negotiation, the server and the client negotiate what cipher suite will be used. It is possible to force server's TLS implementation to dictate its preference (cipher suite order) to avoid malicious clients that intentionally negotiate weak cipher suites in preparation for running an attack on them. To do so, configure honor_cipher_order and honor_ecc_order to true:
listeners.ssl.1 = 5671 ssl_options.cacertfile = /path/to/ca_certificate.pem ssl_options.certfile = /path/to/server_certificate.pem ssl_options.keyfile = /path/to/server_key.pem ssl_options.versions.1 = tlsv1.2 ssl_options.versions.2 = tlsv1.1 ssl_options.honor_cipher_order = true ssl_options.honor_ecc_order = trueOr, in the classic config format:
%% Enforce server-provided cipher suite order (preference)
[
{ssl, [{versions, ['tlsv1.2', 'tlsv1.1']}]},
{rabbit, [
{ssl_listeners, [5671]},
{ssl_options, [{cacertfile, "/path/to/ca_certificate.pem"},
{certfile, "/path/to/server_certificate.pem"},
{keyfile, "/path/to/server_key.pem"},
{versions, ['tlsv1.2', 'tlsv1.1']},
%% ...
{honor_cipher_order, true},
{honor_ecc_order, true},
]}
]}
].
When setting up an SSL connection there are two important stages in the protocol.
The first stage is when the peers optionally exchange certificates. Having exchanged certificates, the peers can optionally attempt to establish a chain of trust between their root certificates, and the certificates presented. This acts to verify that the peer is who it claims to be (provided the private key hasn't been stolen!).
The second stage is where the peers negotiate a symmetric encryption key that will be used for the rest of the communication. If certificates were exchanged, the public/private keys will be used in the key negotiation.
Thus you can create an encrypted SSL connection without having to verify certificates. The Java client supports both modes of operation.
There are three components to be aware of in the Java security framework: Key Manager, Trust Manager and Key Store.
A Key Manager is used by a peer to manage its certificates. This means that in a session set-up, the Key Manager will control which certificates to send to the remote peer.
A Trust Manager is used by a peer to manage remote certificates. This means that in a session set-up, the Trust Manager will control which certificates are trusted from a remote peer.
A Key Store is a Java encapsulation of certificates. Java needs all certificates to either be converted into a Java specific binary format or to be in the PKCS#12 format. These formats are managed using the Key Store class. For the server certificate, we'll use the Java binary format, but for client key/certificate pair, we'll use the PKCS#12 format.
Our first example will show a simple client, connecting to a RabbitMQ server over SSL without validating the server certificate, and without presenting any client certificate.
import java.io.*;
import java.security.*;
import com.rabbitmq.client.*;
public class Example1
{
public static void main(String[] args) throws Exception
{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5671);
factory.useSslProtocol();
// Tells the library to setup the default Key and Trust managers for you
// which do not do any form of remote server trust verification
Connection conn = factory.newConnection();
Channel channel = conn.createChannel();
//non-durable, exclusive, auto-delete queue
channel.queueDeclare("rabbitmq-java-test", false, true, true, null);
channel.basicPublish("", "rabbitmq-java-test", null, "Hello, World".getBytes());
GetResponse chResponse = channel.basicGet("rabbitmq-java-test", false);
if(chResponse == null) {
System.out.println("No message retrieved");
} else {
byte[] body = chResponse.getBody();
System.out.println("Recieved: " + new String(body));
}
channel.close();
conn.close();
}
}
This simple example is an echo test. It creates a channel rabbitmq-java-test and publishes to the default direct exchange, then reads back what has been published and echoes it out. Note that we use an exclusive, non-durable, auto-delete queue so we don't have to worry about manually cleaning up after ourselves.
First, we set-up our Key Store. We'll assume that we have the certificate for the server we want to connect to, so we now need to add it to our Key Store which we will use for the Trust Manager.
keytool -import -alias server1 -file /path/to/server/cert.pem -keystore /path/to/rabbitstore
The above command will import cert.pem into the rabbitstore and will internally refer to it as server1. The alias argument is used when you have many certificates or keys, as they must all have internally distinct names.
Be sure to answer yes to the question about trusting this certificate, and to pick a passphrase. For this example I set my passphrase to rabbitstore.
We then use our client certificate and key in a PKCS#12 file as already shown above.
Our next example will be a modification of the previous one, to now use our Key Store with our Key Manager and Trust Manager
import java.io.*;
import java.security.*;
import javax.net.ssl.*;
import com.rabbitmq.client.*;
public class Example2
{
public static void main(String[] args) throws Exception
{
char[] keyPassphrase = "MySecretPassword".toCharArray();
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream("/path/to/client/keycert.p12"), keyPassphrase);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, passphrase);
char[] trustPassphrase = "rabbitstore".toCharArray();
KeyStore tks = KeyStore.getInstance("JKS");
tks.load(new FileInputStream("/path/to/trustStore"), trustPassphrase);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(tks);
SSLContext c = SSLContext.getInstance("TLSv1.1");
c.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
factory.setPort(5671);
factory.useSslProtocol(c);
Connection conn = factory.newConnection();
Channel channel = conn.createChannel();
channel.queueDeclare("rabbitmq-java-test", false, true, true, null);
channel.basicPublish("", "rabbitmq-java-test", null, "Hello, World".getBytes());
GetResponse chResponse = channel.basicGet("rabbitmq-java-test", false);
if(chResponse == null) {
System.out.println("No message retrieved");
} else {
byte[] body = chResponse.getBody();
System.out.println("Recieved: " + new String(body));
}
channel.close();
conn.close();
}
}
To ensure that the above code works in the other case, try setting up your RabbitMQ server with a certificate that has not been imported into the key store and watch the verification exceptions decorate your screen.
For a server certificate to be understood on the .Net platform, they can be in a number of formats including DER and PKCS #12, but of course, not PEM. For the DER format, .Net expects them to be stored in files with .cer extension. In the steps above, when creating the test Certificate Authority, we converted the certificate from PEM to DER format using:
openssl x509 -in /path/to/testca/cacert.pem -out /path/to/testca/cacert.cer -outform DER
PEM is a base64 encoding of this DER format, enclosed within delimiters. This encoding is usually done to make it easier to transfer the data over 7-bit limited protocols, such as email (SMTP).
As mentioned above, Mono is rather more stringent than is normal about enforcing that certificates are only used for the purposes indicated by the certificate itself.
SSL certificate and keys can be used for a variety of purposes, for example, email signing, code signing, traffic encryption, etc. (For our purposes, we're interested in TCP Traffic encryption). RFC 5280 specifies a number of different purposes, and allows a certificate to be signed for a specific set of purposes.
SSL v3 certificates can contain a number of different extensions. The extension that deals with how a certificate can be used is call the Key Usage Extension. The various usages permitted are not, in general, well supported, or even well defined, and their usage is subject to wide interpretation. Some of the key usages have been deprecated, and mainly, they're just totally ignored.
There is a further extension, which also specifies usages, but chooses to do so using O.I.Ns, such as "1.3.6.1.5.5.7.3.1". Clearly English is lacking something that apparently random numbers add. This is the Extended Key Usage extension - a sequence of object identifiers that further defines which uses of the certificate are permissible.
Mono, however, seems to think that these extensions are both important, and need to be observed. Mono chooses to invalidate the certificate if the certificate omits a Key Usage Extension. By default, OpenSSL omits the Key Usage Extension for self-signed certificates because it is expected that if no Key Usage Extension is found, the certificate is valid to be used for any purpose.
This is the reason why in the sample openssl.cnf file listed above, the extensions specified for root_ca_extensions, client_ca_extensions and server_ca_extensions all have keyUsage specified, and the latter two also have extendedKeyUsage defined. Thus the certificates generated above are valid for use by Mono; keyEncipherment specifies that the certificate can be used by an SSL Server, and digitalSignature specifies that the certificate can be used by an SSL client. The values in the extendedKeyUsage fields say the same thing.
You can use this small tool to check that the certificate presented by the RabbitMQ server is acceptable to Mono. Note that you'll need to convert the server/cert.pem to server/cert.cer using an appropriate OpenSSL command:
openssl x509 -in /path/to/server/cert.pem -out /path/to/server/cert.cer -outform DER mono certcheck.exe /path/to/server/cert. # ... # => This certificate CAN be used by Mono for Server validation
TLS offers peer verification (validation), a way for client and server to verify each other's identity based on peer's certificate information. When peer verification is enabled, typically the hostname of the server you're connecting to needs to match the CN (Common Name) field on the server's certificate, otherwise the certificate will be rejected. However, peer verification doesn't have to be limited to just CN and hostname matching in general.
This is why the commands at the start of this guide specify ...-subj /CN=$(hostname)/... which dynamically looks up your hostname. If you're generating certificates on one machine, and using them on the other then be sure to swap out the $(hostname) section, and replace it with the correct hostname for your server.
On the .NET platform, RemoteCertificateValidationCallback controls TLS verification behavior.
In RabbitMQ .NET client, RabbitMQ.Client.SslOption.CertificateValidatioNCallback can be used to provide a RemoteCertificateValidationCallback delegate. The delegate will be used to verify peer (RabbitMQ node) identity using whatever logic fits the application. If this is not specified, the default callback will be used in conjunction with the AcceptablePolicyErrors property to determine if the remote server certificate is valid. The System.Net.Security.SslPolicyErrors.RemoteCertificateNameMismatch flag in RabbitMQ.Client.SslOption.AcceptablePolicyErrors can be used to disable peer verification (not recommended in production environments!).
RabbitMQ.Client.SslOption.CertificateSelectionCallback can be used to provide a LocalCertificateSelectionCallback that will select the local certificate used for peer verification.
On the .NET platform, remote certificates are managed by putting them into any of a number of Stores. All management of these stores is done with the 'certmgr' tool which is available on both Microsoft's .Net implementation and on Mono.
NB: On some flavours of Windows there are two versions of the command - one that ships with the operating system and provides a graphical interface only, and one that ships with the Windows SDK and provides both a graphical and command line interface. Either will do the job, but the examples below are based on the latter.
For our case, because we're supplying the client certificate/key pair in a separate PKCS #12 file, all we need to do is to import the certificate of the root Certificate Authority into the Root (Windows) / Trust (Mono) store. All certificates signed by any certificate in that store are automatically trusted.
In contrast to the Java client, which is happy to use an SSL connection without verifying the server's certificate, the .NET client by default requires this verification to succeed. To suppress verification, an application can set the System.Net.Security.SslPolicyErrors.RemoteCertificateNotAvailable and System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors flags in SslOptions.AcceptablePolicyErrors.
certmgr allows us to Add, Delete, List and perform other actions on a specified Store. These stores can be per-user stores, or machine wide. Only admin users can have write access to the machine wide stores.
To add a certificate to the users Root (Windows) / Trust (Mono) store we run:
(Windows) > certmgr -add -all \path\to\cacert.cer -s Root (Mono) $ certmgr -add -c Trust /path/to/cacert.cer
To add a certificate to the machine certificate store instead we run
(Windows) > certmgr -add -all \path\to\cacert.cer -s -r localMachine Root (Mono) $ certmgr -add -c -m Trust /path/to/cacert.cer
After adding to a store, we can view the contents of that store with the -list switch:
(Windows) > certmgr -all -s Root (Mono) $ certmgr -list -c Trust Mono Certificate Manager - version 2.4.0.0 Manage X.509 certificates and CRL from stores. Copyright 2002, 2003 Motus Technologies. Copyright 2004-2008 Novell. BSD licensed. Self-signed X.509 v3 Certificate Serial Number: AC3F2B74ECDD9EEA00 Issuer Name: CN=MyTestCA Subject Name: CN=MyTestCA Valid From: 25/08/2009 14:03:01 Valid Until: 24/09/2009 14:03:01 Unique Hash: 1F04D1D2C20B97BDD5DB70B9EB2013550697A05E
As we can see, there is one Self-signed X.509 v3 Certificate in the trust store. The Unique Hash uniquely identifies this certificate in this store. To delete this certificate, use the unique hash:
(Windows) > certmgr -del -c -sha1 1F04D1D2C20B97BDD5DB70B9EB2013550697A05E -s Root (Mono) $ certmgr -del -c Trust 1F04D1D2C20B97BDD5DB70B9EB2013550697A05E Mono Certificate Manager - version 2.4.0.0 Manage X.509 certificates and CRL from stores. Copyright 2002, 2003 Motus Technologies. Copyright 2004-2008 Novell. BSD licensed. Certificate removed from store.
With these simple steps we can go ahead and add/delete/list our root certificates to the client side store.
To create an SSL connection to RabbitMQ, we need to set some new fields in the ConnectionFactory's Parameters field. To make things easier, there is a new Field Parameters.Ssl that acts like a namespace for all the other fields that we need to set. The fields are:
This is the same example as in the Java section. It creates a channel, rabbitmq-dotnet-test and publishes to the default direct exchange, then reads back what has been published and echoes it out. Note that we use an exclusive, non-durable, auto-delete queue so we don't have to worry about manually cleaning up after ourselves
using System;
using System.IO;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Util;
namespace RabbitMQ.Client.Examples {
public class TestSSL {
public static int Main(string[] args) {
ConnectionFactory cf = new ConnectionFactory();
cf.Ssl.ServerName = System.Net.Dns.GetHostName();
cf.Ssl.CertPath = "/path/to/client/keycert.p12";
cf.Ssl.CertPassphrase = "MySecretPassword";
cf.Ssl.Enabled = true;
using (IConnection conn = cf.CreateConnection()) {
using (IModel ch = conn.CreateModel()) {
ch.QueueDeclare("rabbitmq-dotnet-test", false, false, false, null);
ch.BasicPublish("", "rabbitmq-dotnet-test", null,
Encoding.UTF8.GetBytes("Hello, World"));
BasicGetResult result = ch.BasicGet("rabbitmq-dotnet-test", true);
if (result == null) {
Console.WriteLine("No message received.");
} else {
Console.WriteLine("Received:");
DebugUtil.DumpProperties(result, Console.Out, 0);
}
ch.QueueDelete("rabbitmq-dotnet-test");
}
}
return 0;
}
}
}
Note that, on Windows XP, running the example may fail with
CryptographicException: Key not valid for use in specified state.In this case, you may be successful in loading the certificate from the certificate store and setting ConnectionFactory's Ssl.Certs parameter directly.
When using a client certificate signed by an intermediate CA, it may be necessary to configure RabbitMQ server to use a higher verification depth. The depth is the maximum number of non-self-issued intermediate certificates that may follow the peer certificate in a valid certification path. So if depth is 0 the peer (e.g. client) certificate must be signed by the trusted CA directly, if 1 the path can be "peer, CA, trusted CA", if it is 2 "peer, CA, CA, trusted CA", and so on. The following example demonstrates how to configure certificate validation depth for RabbitMQ server:
[
{rabbit, [
{ssl_listeners, [5671]},
{ssl_options, [{cacertfile,"/path/to/testca/cacert.pem"},
{certfile,"/path/to/server/cert.pem"},
{keyfile,"/path/to/server/key.pem"},
{depth, 2},
{verify,verify_peer},
{fail_if_no_peer_cert,false}]}
]}
].
When using RabbitMQ plugins such as federation or shovel with TLS, it may be necessary to configure verification depth for the Erlang client, as explained below.
Because TLS has many configurable parameters and some of them have suboptimal defaults for historical reasons, TLS setup evaluation is a recommended practice. Multiple tools exist that perform various tests on TLS-enabled server endpoints, for example, testing whether it is prone to known attacks such as POODLE, BEAST, and others.
testssl.sh is a mature and extensive TLS endpoint testing tool that can be used with protocol endpoints that do not serve HTTP. Note that the tool performs many tests (for instance, on some machines it runs over 350 cipher suite tests alone) and passing every single one may or may not make sense for every environment. For example, many production deployments do not use CRLs (Certificate Revocation Lists); most development environments use self-signed certificates and don't have to worry about the most optimal set of cipher suites enabled; and so on.
The following example configuration (in the classic config format) passes key testssl tests on Erlang 19.3 or later versions:
[
{ssl, [
{versions, ['tlsv1.2']},
{secure_renegotiate, true}
]},
{rabbit, [
{ssl_listeners, [5671]},
{ssl_options, [{cacertfile, "/path/to/ca_certificate.pem"},
{certfile, "/path/to/server_certificate.pem"},
{keyfile, "/path/to/server_key.pem"},
{versions, ['tlsv1.2']},
{ciphers, [
{ecdhe_ecdsa,aes_256_gcm,null,sha384},
{ecdhe_rsa,aes_256_gcm,null,sha384},
{ecdh_ecdsa,aes_256_gcm,null,sha384},
{ecdh_rsa,aes_256_gcm,null,sha384},
{dhe_rsa,aes_256_gcm,null,sha384},
{rsa,aes_256_gcm,null,sha384},
{ecdhe_ecdsa,aes_128_gcm,null,sha256},
{ecdhe_rsa,aes_128_gcm,null,sha256},
{ecdh_ecdsa,aes_128_gcm,null,sha256},
{ecdh_rsa,aes_128_gcm,null,sha256},
{dhe_rsa,aes_128_gcm,null,sha256},
{ecdh_rsa,aes_128_gcm,null,sha256}
]},
{honor_cipher_order, true},
{honor_ecc_order, true},
{secure_renegotiate, true},
{verify, verify_peer},
{fail_if_no_peer_cert, false}]}
]}
].
For example, this setup is reported as not vulnerable to
a set of known high profile vulnerabilities:
Testing vulnerabilities
Heartbleed (CVE-2014-0160) not vulnerable (OK), no heartbeat extension
CCS (CVE-2014-0224) not vulnerable (OK)
Ticketbleed (CVE-2016-9244), experiment. -- (applicable only for HTTPS)
Secure Renegotiation (CVE-2009-3555) not vulnerable (OK)
Secure Client-Initiated Renegotiation not vulnerable (OK)
CRIME, TLS (CVE-2012-4929) not vulnerable (OK) (not using HTTP anyway)
POODLE, SSL (CVE-2014-3566) not vulnerable (OK)
TLS_FALLBACK_SCSV (RFC 7507) No fallback possible, TLS 1.2 is the only protocol (OK)
SWEET32 (CVE-2016-2183, CVE-2016-6329) not vulnerable (OK)
FREAK (CVE-2015-0204) not vulnerable (OK)
DROWN (CVE-2016-0800, CVE-2016-0703) not vulnerable on this port (OK)
make sure you don't use this certificate elsewhere with SSLv2 enabled services
https://censys.io/ipv4?q=D6D88C603E0212109EC14429DCBB4A6B73286F8B7F5C956FE53D1A94A34D88FB could help you to find out
LOGJAM (CVE-2015-4000), experimental not vulnerable (OK): no DH EXPORT ciphers, no DH key detected
BEAST (CVE-2011-3389) no SSL3 or TLS1 (OK)
LUCKY13 (CVE-2013-0169) not vulnerable (OK)
RC4 (CVE-2013-2566, CVE-2015-2808) no RC4 ciphers detected (OK)
When using a client certificate signed by an intermediate CA, it may be necessary to configure RabbitMQ server to use a higher verification depth. The depth is the maximum number of non-self-issued intermediate certificates that may follow the peer certificate in a valid certification path. So if depth is 0 the peer (e.g. client) certificate must be signed by the trusted CA directly, if 1 the path can be "peer, CA, trusted CA", if it is 2 "peer, CA, CA, trusted CA", and so on. The following example demonstrates how to configure certificate validation depth for RabbitMQ server:
listeners.ssl.1 = 5671 ssl_options.cacertfile = /path/to/testca/cacert.pem ssl_options.certfile = /path/to/server/cert.pem ssl_options.keyfile = /path/to/server/key.pem ssl_options.depth = 2 ssl_options.verify = verify_peer ssl_options.fail_if_no_peer_cert = falseUsing the classic config format:
[
{rabbit, [
{ssl_listeners, [5671]},
{ssl_options, [{cacertfile,"/path/to/testca/cacert.pem"},
{certfile,"/path/to/server/cert.pem"},
{keyfile,"/path/to/server/key.pem"},
{depth, 2},
{verify,verify_peer},
{fail_if_no_peer_cert,false}]}
]}
].
When using RabbitMQ plugins such as federation or shovel with TLS, it may be necessary to configure verification depth for the Erlang client, as explained below.
Enabling TLS in the RabbitMQ Erlang client is rather straightforward. In the #amqp_params_network record, we just need to supply values in the ssl_options field. These, you will recognise from the options we specified to RabbitMQ.
The three important options which must be supplied are:
server_name_indication - set this option to the host name of the server to which a TLS connection will be made to enable "Server Name Indication" verification of the certificate presented by the server. This ensures that the server certificate's CN= value will be verified during TLS connection establishment. You can override this behavior by setting server_name_indication to a different host name or to the special value disable to disable this verification. Note that, by default, SNI is not enabled. This default will change in a future RabbitMQ Erlang client release.
verify - set this option to verify_peer to enable X509 certificate chain verification. The depth option configures certificate verification depth. Note that, by default, verify is set to verify_none, which disables certificate chain verification. This default will change in a future RabbitMQ Erlang client release.
SslOpts = [{cacertfile, "/path/to/testca/cacert.pem"},
{certfile, "/path/to/client/cert.pem"},
{keyfile, "/path/to/client/key.pem"},
%% only necessary with intermediate CAs
%% {depth, 2},
%% Note: it is recommended to set 'verify' to
%% to 'verify_peer' to ensure that X509
%% certificate chain validation is enabled
%%
%% Do not set 'verify' or set it to verify_none
%% if x509 certificate chain validation is
%% not desired
{verify, verify_peer},
%% If Server Name Indication validation is desired,
%% set the following option to the host name to which
%% the connection is made. If necessary, this option
%% may be set to another host name to match the server
%% certificate's CN= value.
%% Do not set this option or set it to the atom 'disable'
%% to disable SNI validation
{server_name_indication, "my.rmq-server.net"}],
Params = #amqp_params_network{host = "my.rmq-server.net",
port = 5671,
ssl_options = SslOpts}
{ok, Conn} = amqp_connection:start(Params),
You can now go ahead and use Conn as a normal connection.