1"use strict";(self.webpackChunkrabbitmq_website=self.webpackChunkrabbitmq_website||[]).push([["14175"],{44846(e,n,t){t.r(n),t.d(n,{metadata:()=>i,default:()=>u,frontMatter:()=>l,contentTitle:()=>h,toc:()=>p,assets:()=>d});var i=JSON.parse('{"id":"ssl/index","title":"TLS Support","description":"\x3c!--","source":"@site/docs/ssl/index.md","sourceDirName":"ssl","slug":"/ssl/","permalink":"/docs/next/ssl/","draft":false,"unlisted":false,"editUrl":"https://github.com/rabbitmq/rabbitmq-website/tree/main/docs/ssl/index.md","tags":[],"version":"current","frontMatter":{"title":"TLS Support"},"sidebar":"docsSidebar","previous":{"title":"Networking and RabbitMQ","permalink":"/docs/next/networking"},"next":{"title":"Client Heartbeats","permalink":"/docs/next/heartbeats"}}'),s=t(74848),r=t(28453),a=t(3432),o=t(50773),c=t(57250);let l={title:"TLS Support"},h="TLS Support",d={},p=[{value:"Table of Contents",id:"overview",level:2},{value:"Where to Learn the Basics of TLS",id:"basics",level:2},{value:"TLS and Messaging Protocols",id:"protocols",level:2},{value:"Common Approaches to TLS for client Connections with RabbitMQ",id:"tls-connectivity-options",level:2},{value:"Erlang/OTP Requirements for TLS Support",id:"erlang-otp-requirements",level:2},{value:"Known Incompatibilities and Limitations",id:"known-compatibility-issues",level:2},{value:"TLS Basics: Certificate Authorities, Certificates, Keys",id:"certificates-and-keys",level:2},{value:"Two Primary Objectives of TLS",id:"two-primary-objectives-of-tls",level:3},{value:"Certificates, Private Keys, and Certificate Authorities",id:"certificates-private-keys-and-certificate-authorities",level:3},{value:"The Short Route to Generating a CA, Certificates, and Keys",id:"automated-certificate-generation",level:2},{value:"Using tls-gen's Basic Profile",id:"automated-certificate-generation-transcript",level:3},{value:"Enabling TLS Support in RabbitMQ",id:"enabling-tls",level:2},{value:"Certificate and Private Key File Paths",id:"enabling-tls-paths",level:3},{value:"How to Verify that TLS is Enabled",id:"enabling-tls-verify-configuration",level:3},{value:"Providing Private Key Password",id:"private-key-passwords",level:3},{value:"TLS Peer Verification: Who Do You Say You Are?",id:"peer-verification",level:2},{value:"How Peer Verification Works",id:"peer-verification-how-it-works",level:3},{value:"Mutual Peer Verification (Mutual TLS Authentication or mTLS)",id:"mutual-peer-verification-mutual-tls-authentication-or-mtls",level:3},{value:"When Peer Verification Fails",id:"when-peer-verification-fails",level:3},{value:"Trusted Certificates",id:"peer-verification-trusted-certificates",level:3},{value:"Enabling Peer Verification",id:"peer-verification-configuration",level:3},{value:"Certificate Chains and Verification Depth",id:"peer-verification-depth",level:3},{value:"Using TLS in the Java Client",id:"java-client",level:2},{value:"Key Managers, Trust Managers and Stores",id:"java-client-trust-managers-and-stores",level:3},{value:"Connecting with TLS",id:"java-client-connecting",level:3},{value:"Connecting with Peer Verification Enabled",id:"java-client-connecting-with-peer-verification",level:3},{value:"Server Hostname Verification",id:"java-client-hostname-verification",level:4},{value:"Configuring TLS Version in Java Client",id:"tls-versions-java-client",level:3},{value:"Using TLS in the .NET Client",id:"dotnet-client",level:2},{value:".NET Trust Store",id:"dotnet-peer-verification",level:3},{value:"Certificate Management with Certmgr",id:"certmgr",level:3},{value:"Connection TLS Settings",id:"dotnet-connection-params",level:3},{value:"TLS Versions",id:"dotnet-tls-versions-dotnet-client",level:3},{value:"Code Example",id:"dotnet-example",level:3},{value:"TLS Peer Verification in .NET Client",id:"tls-verification-in-dotnet",level:3},{value:"Limiting TLS Versions Used by the Server",id:"tls-versions",level:2},{value:"Why Limit TLS Versions",id:"tls-versions-why-limit",level:3},{value:"Why Not Limit TLS Versions",id:"tls-versions-why-not-limit",level:3},{value:"Verifying Enabled TLS Versions",id:"verifying-tls-versions",level:3},{value:"TLSv1.3",id:"tls1.3",level:3},{value:"TLS Version Support Table for JDK and .NET",id:"tls-version-support-in-jdk-and-net",level:3},{value:"Public Extended Key Usage Options (EKUs)",id:"key-usage",level:2},{value:"Extensions and Their Effect on Accepted Cipher Suites (Cipher Suite Filtering)",id:"key-usage-effects-on-cipher-suites",level:3},{value:"Examining Certificate Extensions",id:"examining-certificate-extensions",level:3},{value:"Cipher Suites",id:"cipher-suites",level:2},{value:"Listing Cipher Suites Available on a RabbitMQ Node",id:"available-cipher-suites",level:3},{value:"Configuring Cipher Suites",id:"configuring-cipher-suites",level:3},{value:"Cipher Suite Order",id:"cipher-suite-order",level:3},{value:"Recommended Cipher Suites (TLS 1.3)",id:"recommended-cipher-suites-tls-13",level:3},{value:"Recommended Cipher Suites (TLS 1.2)",id:"recommended-cipher-suites-tls-12",level:3},{value:"Known TLS Vulnerabilities and Their Mitigation",id:"major-vulnerabilities",level:2},{value:"ROBOT",id:"robot",level:3},{value:"POODLE",id:"poodle",level:3},{value:"BEAST",id:"beast",level:3},{value:"Evaluating TLS Setup Security",id:"tls-evaluation-tools",level:2},{value:"testssl.sh",id:"testssl-sh",level:3},{value:"Evaluation of a TLS 1.3 Setup",id:"evaluation-of-a-tls-13-setup",level:3},{value:"Evaluation of a TLS 1.2 Setup with Restricted Cipher Suites",id:"evaluation-of-a-tls-12-setup-with-restricted-cipher-suites",level:3},{value:"TLS Certificate and Private Key Rotation",id:"rotation",level:2},{value:"Replacing Certificate and Private Key Files on Disk",id:"replacing-certificate-and-private-key-files-on-disk",level:3},{value:"Clearing the Certificate and Private Key Store Cache",id:"clearing-the-certificate-and-private-key-store-cache",level:3},{value:"The Trust Store Plugin",id:"trust-store",level:2},{value:"Certificate Providers",id:"trust-store-providers",level:3},{value:"Filesystem Provider",id:"trust-store-filesystem",level:4},{value:"HTTP Provider",id:"trust-store-http",level:4},{value:"Provider HTTP API",id:"provider-http-api",level:4},{value:"Using the Trust Store for Client (x509) Certificate Authentication",id:"trust-store-x509-auth",level:3},{value:"Inspecting and Refreshing the Whitelist",id:"trust-store-management",level:3},{value:"TLS Session Caching",id:"trust-store-session-caching",level:3},{value:"Using TLS in the Erlang Client",id:"erlang-client",level:2},{value:"Erlang TLS Options",id:"erlang-ssl",level:3},{value:"Code Example",id:"erlang-code-example",level:3},{value:"Manually Generating a CA, Certificates and Private Keys",id:"manual-certificate-generation",level:2}];function f(e){let n={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",img:"img",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,r.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.header,{children:(0,s.jsx)(n.h1,{id:"tls-support",children:"TLS Support"})}),"\n",(0,s.jsx)(n.h2,{id:"overview",children:"Table of Contents"}
1),"\n",(0,s.jsxs)(n.p,{children:["RabbitMQ has inbuilt support for TLS. This includes client connections and popular plugins, where applicable,\nsuch as ",(0,s.jsx)(n.a,{href:"./federation",children:"Federation links"}),". It is also possible to use TLS\nto ",(0,s.jsx)(n.a,{href:"./clustering-ssl",children:"encrypt inter-node connections in clusters"}),"."]}),"\n",(0,s.jsx)(n.p,{children:"This guide covers various topics related to TLS in RabbitMQ, with a focus on client\nconnections:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["Two ",(0,s.jsx)(n.a,{href:"#tls-connectivity-options",children:"ways of using TLS"})," for client connections: direct or via a TLS terminating proxy"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#erlang-otp-requirements",children:"Erlang/OTP requirements"})," for TLS support"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#enabling-tls",children:"Enabling TLS"})," in RabbitMQ"]}),"\n",(0,s.jsxs)(n.li,{children:["How to generate self-signed certificates for development and QA environments ",(0,s.jsx)(n.a,{href:"#automated-certificate-generation",children:"with tls-gen"})," or ",(0,s.jsx)(n.a,{href:"#manual-certificate-generation",children:"manually"})]}),"\n",(0,s.jsxs)(n.li,{children:["TLS configuration in ",(0,s.jsx)(n.a,{href:"#java-client",children:"Java"})," and ",(0,s.jsx)(n.a,{href:"#dotnet-client",children:".NET"})," clients"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#peer-verification",children:"Peer (certificate chain) verification"}),' of client connections or mutual ("mTLS")']}),"\n",(0,s.jsxs)(n.li,{children:["Public ",(0,s.jsx)(n.a,{href:"#key-usage",children:"extended key usage"})," options (EKUs) relevant to RabbitMQ server and clients"]}),"\n",(0,s.jsxs)(n.li,{children:["How to control what ",(0,s.jsx)(n.a,{href:"#tls-versions",children:"TLS version"})," and ",(0,s.jsx)(n.a,{href:"#cipher-suites",children:"cipher suite"})," are enabled"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#tls1.3",children:"TLSv1.3"})," support"]}),"\n",(0,s.jsxs)(n.li,{children:["Tools that can be used to ",(0,s.jsx)(n.a,{href:"#tls-evaluation-tools",children:"evaluate a TLS setup"})]}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"#rotation",children:"Certificate and key rotation"})}),"\n",(0,s.jsxs)(n.li,{children:["The ",(0,s.jsx)(n.a,{href:"#trust-store",children:"Trust Store plugin"})," for environments where only a set of\nleaf certificates should be used for peer verification (the chain is not really traversed)"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#trust-store-x509-auth",children:"Client Certificate-based"})," authentication"]}),"\n",(0,s.jsxs)(n.li,{children:["Known ",(0,s.jsx)(n.a,{href:"#major-vulnerabilities",children:"attacks on TLS"})," and their mitigation"]}),"\n",(0,s.jsxs)(n.li,{children:["How to use ",(0,s.jsx)(n.a,{href:"#private-key-passwords",children:"private key passwords"})]}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"and more."}),"\n",(0,s.jsx)(n.h2,{id:"basics",children:"Where to Learn the Basics of TLS"}),"\n",(0,s.jsxs)(n.p,{children:["This guide tries to ",(0,s.jsx)(n.a,{href:"#certificates-and-keys",children:"explain the basics of TLS"})," but it is not, however, a primer on TLS, encryption, ",(0,s.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/public_key_infrastructure",children:"public Key Infrastructure"})," and related topics, so the concepts are covered very briefly."]}),"\n",(0,s.jsx)(n.p,{children:"A number of beginner-oriented primers on TLS are available elsewhere on the Web:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://medium.com/zkpass/a-primer-on-transport-layer-security-tls-a7495eeff004",children:"one"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://www.cloudflare.com/en-ca/learning/ssl/why-use-tls-1.3/",children:"two"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://hpbn.co/transport-layer-security-tls/",children:"three"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://medium.com/talpor/ssl-tls-authentication-explained-86f00064280",children:"four"})}),"\n"]}),"\n",(0,s.jsx)(n.h2,{id:"protocols",children:"TLS and Messaging Protocols"}),"\n",(0,s.jsxs)(n.p,{children:["TLS can be enabled for all protocols supported by RabbitMQ.\nHowever, this guide primarily focuses on AMQP 1.0 and AMQP 0-9-1 listeners.\nSee ",(0,s.jsx)(n.a,{href:"./stream",children:"RabbitMQ Stream Protocol"}),", ",(0,s.jsx)(n.a,{href:"./mqtt",children:"MQTT"}),", ",(0,s.jsx)(n.a,{href:"./stomp",children:"STOMP"}),", ",(0,s.jsx)(n.a,{href:"./web-mqtt",children:"MQTT-over-WebSockets"}),", and ",(0,s.jsx)(n.a,{href:"./web-stomp",children:"STOMP-over-WebSockets"})," guides\nfor TLS configuration examples for those protocols."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"./management",children:"HTTP API"}),", ",(0,s.jsx)(n.a,{href:"./clustering-ssl",children:"inter-node and CLI tool traffic"})," can be configured\nto use TLS (HTTPS) as well."]}),"\n",(0,s.jsxs)(n.p,{children:["To configure TLS on Kubernetes using the RabbitMQ Cluster Operator, see the guide for ",(0,s.jsx)(n.a,{href:"/kubernetes/operator/using-operator#tls",children:"Configuring TLS"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["For an overview of common TLS troubleshooting techniques, see ",(0,s.jsx)(n.a,{href:"./troubleshooting-ssl",children:"Troubleshooting TLS-related issues"}),"\nand ",(0,s.jsx)(n.a,{href:"./troubleshooting-networking",children:"Troubleshooting Networking"}),"."]}),"\n",(0,s.jsx)(n.h2,{id:"tls-connectivity-options",children:"Common Approaches to TLS for client Connections with RabbitMQ"}),"\n",(0,s.jsx)(n.p,{children:"For client connections, there are two common approaches:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:"Configure RabbitMQ to handle TLS connections"}),"\n",(0,s.jsxs)(n.li,{children:["Use a proxy or load balancer (such as ",(0,s.jsx)(n.a,{href:"http://www.haproxy.org/",children:"HAproxy"}),")\nto perform ",(0,s.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/TLS_termination_proxy",children:"TLS termination"})," of client connections and use plain TCP connections to RabbitMQ nodes."]}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"Both approaches are valid and have pros and cons. This guide will focus on the\nfirst option. Certain parts of this guide would still be relevant for environments\nthat choose the second option."}),"\n",(0,s.jsx)(n.h2,{id:"erlang-otp-requirements",children:"Erlang/OTP Requirements for TLS Support"}
1),"\n",(0,s.jsxs)(n.p,{children:["In order to support TLS connections, RabbitMQ needs TLS and\ncrypto-related modules to be available in the Erlang/OTP\ninstallation. The recommended Erlang/OTP version to use with\nTLS is the most recent ",(0,s.jsx)(n.a,{href:"./which-erlang",children:"supported Erlang release"}),".\nEarlier versions, even if they are supported, may work for most certificates\nbut have known limitations (see below)."]}),"\n",(0,s.jsxs)(n.p,{children:["The Erlang ",(0,s.jsx)(n.code,{children:"asn1"}),", ",(0,s.jsx)(n.code,{children:"crypto"}),",\n",(0,s.jsx)(n.code,{children:"public_key"}),", and ",(0,s.jsx)(n.code,{children:"ssl"})," libraries\n(applications) must be installed and functional. On Debian and\nUbuntu this is provided by the ",(0,s.jsx)(n.a,{href:"http://packages.ubuntu.com/search?keywords=erlang-asn1",children:"erlang-asn1"}),",\n",(0,s.jsx)(n.a,{href:"http://packages.ubuntu.com/search?keywords=erlang-crypto",children:"erlang-crypto"}),", ",(0,s.jsx)(n.a,{href:"http://packages.ubuntu.com/search?keywords=erlang-public-key",children:"erlang-public-key"}),", and\n",(0,s.jsx)(n.a,{href:"http://packages.ubuntu.com/search?keywords=erlang-ssl",children:"erlang-ssl"})," packages, respectively. The ",(0,s.jsx)(n.a,{href:"https://github.com/rabbitmq/erlang-rpm",children:"zero dependency\nErlang RPM for RabbitMQ"})," includes the above modules."]}),"\n",(0,s.jsxs)(n.p,{children:["If Erlang/OTP is compiled from source, it is necessary to ensure that ",(0,s.jsx)(n.code,{children:"configure"}),"\nfinds OpenSSL and builds the above libraries."]}),"\n",(0,s.jsxs)(n.p,{children:["When investigating TLS connectivity issues, please keep in mind that in the vast majority\nof cases they are environment-specific (e.g. certificates are missing from the ",(0,s.jsx)(n.a,{href:"#peer-verification-trusted-certificates",children:"trusted certificate store"}),")\nand do not indicate a bug or limitation in Erlang/OTP's TLS implementation. Please go through the steps outlined\nin the ",(0,s.jsx)(n.a,{href:"./troubleshooting-ssl",children:"Troubleshooting TLS guide"})," to gather\nmore information first."]}),"\n",(0,s.jsx)(n.h2,{id:"known-compatibility-issues",children:"Known Incompatibilities and Limitations"}),"\n",(0,s.jsxs)(n.p,{children:["If Elliptic curve cryptography (ECC) cipher suites is\nexpected to be used, a recent ",(0,s.jsx)(n.a,{href:"./which-erlang",children:"supported Erlang release"}),"\nis highly recommended. Earlier releases have known limitations around ECC support."]}),"\n",(0,s.jsx)(n.p,{children:"If you face the above limitations or any other incompatibilities,\nuse the TLS termination option (see above)."}),"\n",(0,s.jsx)(n.h2,{id:"certificates-and-keys",children:"TLS Basics: Certificate Authorities, Certificates, Keys"}),"\n",(0,s.jsxs)(n.p,{children:["TLS is a large and fairly complex topic. Before explaining ",(0,s.jsx)(n.a,{href:"#enabling-tls",children:"how to enable TLS in RabbitMQ"}),"\nit's worth briefly cover some of the concepts used in this guide. This section is intentionally brief and oversimplifies\nsome things. Its goal is to get the reader started with enabling TLS for RabbitMQ and applications."]}),"\n",(0,s.jsx)(n.p,{children:"A number of beginner-oriented primers on TLS are available elsewhere on the Web:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://medium.com/zkpass/a-primer-on-transport-layer-security-tls-a7495eeff004",children:"one"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://www.cloudflare.com/en-ca/learning/ssl/why-use-tls-1.3/",children:"two"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://hpbn.co/transport-layer-security-tls/",children:"three"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://medium.com/talpor/ssl-tls-authentication-explained-86f00064280",children:"four"})}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:["For a thorough understanding of\nTLS and how to get the most out of it, we would recommend the use\nof other resources, for example ",(0,s.jsx)("a",{class:"extlink",href:"http://oreilly.com/catalog/9780596002701/",children:"Network Security with\nOpenSSL"}),"."]}),"\n",(0,s.jsx)(n.h3,{id:"two-primary-objectives-of-tls",children:"Two Primary Objectives of TLS"}),"\n",(0,s.jsxs)(n.p,{children:["TLS has two primary purposes: encrypting connection traffic and providing a way to authenticate (",(0,s.jsx)(n.a,{href:"#peer-verification",children:"verify"}),")\nthe peer to mitigate against ",(0,s.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Man-in-the-middle_attack",children:"Man-in-the-Middle attacks"}),".\nBoth are accomplished using a set of roles, policies and procedures known as ",(0,s.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/public_key_infrastru
1cture",children:"Public Key Infrastructure"})," (PKI)."]}),"\n",(0,s.jsxs)(n.p,{children:["A PKI is based on the concept of digital identities that can be cryptographically (mathematically) verified. Those identities are called\n",(0,s.jsx)("em",{children:"certificates"})," or more precisely, ",(0,s.jsx)("em",{children:"certificate/key pairs"}),". Every TLS-enabled server usually has its own certificate/key\npair that it uses to compute a connection-specific key that will be used to encrypt traffic sent on the connection."]}),"\n",(0,s.jsx)(n.p,{children:"Also, if asked, it can present its certificate (public key) to the connection peer. Clients may or may not have their own certificates.\nIn the context of messaging and tools such as RabbitMQ it is quite common for\nclients to also use certificate/key pairs so that servers can validate their identity."}),"\n",(0,s.jsx)(n.h3,{id:"certificates-private-keys-and-certificate-authorities",children:"Certificates, Private Keys, and Certificate Authorities"}),"\n",(0,s.jsxs)(n.p,{children:["Certificate/key pairs are generated by tools such as OpenSSL and signed by entities called ",(0,s.jsx)("em",{children:(0,s.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Certificate_authority",children:"Certificate Authorities"})})," (CA).\nCAs issue certificates that users (applications or other CAs) use. When a certificate is signed by a CA, they form a ",(0,s.jsx)("em",{children:"chain of trust"}),". Such chains can include\nmore than one CA but ultimately sign a certificate/key pair used by an application (a ",(0,s.jsx)("em",{children:"leaf"})," or ",(0,s.jsx)("em",{children:"end user"})," certificate).\nChains of CA certificates are usually distributed together in a single file. Such file is called a ",(0,s.jsx)("em",{children:"CA bundle"}),"."]}),"\n",(0,s.jsx)(n.p,{children:"Here's an example of the most basic chain with one root CA and one leaf (server or client) certificate:"}),"\n",(0,s.jsx)("figure",{children:(0,s.jsx)(n.p,{children:(0,s.jsx)(n.img,{alt:"Root CA and leaf certificates",src:t(64725).A+"",width:"248",height:"197"})})}),"\n",(0,s.jsx)(n.p,{children:"A chain with intermediate certificates might look like this:"}),"\n",(0,s.jsx)("figure",{children:(0,s.jsx)(n.p,{children:(0,s.jsx)(n.img,{alt:"Root CA, intermediate and leaf certificates",src:t(71857).A+"",width:"248",height:"318"})})}),"\n",(0,s.jsx)(n.p,{children:"There are organizations that sign and issue certificate/key pairs. Most of them are widely trusted CAs and charge a fee for their services."}),"\n",(0,s.jsx)(n.p,{children:"A TLS-enabled RabbitMQ node must have a set of Certificate Authority certificates it considers to be trusted in a file (a CA bundle),\na certificate (public key) file and a private key file. The files will be read from the local filesystem. They must be readable by the effective user\nof the RabbitMQ node process."}),"\n",(0,s.jsxs)(n.p,{children:["Both ends of a TLS-enabled connection can optionally verify\nthe other end of the connection. While doing so, they try to locate a trusted Certificate Authority in the certificate list\npresented by the peer. When both sides perform this verification process, this is known\nas ",(0,s.jsx)(n.em,{children:"mutual TLS authentication"})," or ",(0,s.jsx)(n.em,{children:"mTLS"}),".\nMore on this in the ",(0,s.jsx)(n.a,{href:"#peer-verification",children:"Peer Verification"})," section."]}),"\n",(0,s.jsxs)(n.p,{children:["This guide assumes the user has access to a Certificate Authority and two certificate/key pairs\nin a number of formats for different client libraries to use.\nThis is best done using ",(0,s.jsx)(n.a,{href:"#automated-certificate-generation",children:"existing tools"}),"\nbut those looking to get more familiar with the topic and OpenSSL command line\ntools there's a ",(0,s.jsx)(n.a,{href:"#manual-certificate-generation",children:"separate section"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["In production environments certificates are generated by a commercial Certificate Authority\nor a Certificate Authority issued by the internal security team. In those cases Certificate Authority\nbundle files very likely will contain more than one certificate. This doesn't change how the bundle file\nis used when configuration RabbitMQ as long as the same basic ",(0,s.jsx)(n.a,{href:"#enabling-tls-paths",children:"file and path requirements"})," are met.\nIn other words, whether the certificates are self-signed or issued by a ",(0,s.jsx)(n.a,{href:"#peer-verification-trusted-certificates",children:"trusted CA"}),", they are\nconfigured the same way. The section on ",(0,s.jsx)(n.a,{href:"#peer-verification",children:"peer verification"})," covers this in detail."]}),"\n",(0,s.jsx)(n.h2,{id:"automated-certificate-generation",children:"The Short Route to Generating a CA, Certificates, and Keys"}),"\n",(0,s.jsxs)(n.p,{children:["This guide assumes the user has access to a CA certificate bundle file and two ",(0,s.jsx)(n.a,{href:"#certificates-and-keys",children:"certificate/key pairs"}),".\nThe certificate/key pairs are used by RabbitMQ and clients that connect to the server on a\nTLS-enabled port. The process of generating a Certificate Authority and two key pairs is fairly labourious\nand can be error-prone. An easier way of generating all that\nstuff on MacOS or Linux is with ",(0,s.jsx)("a",{href:"https://github.com/rabbitmq/tls-gen",children:"tls-gen"}),":\nit requires ",(0,s.jsx)(n.code,{children:"Python 3.5+"}),", ",(0,s.jsx)(n.code,{children:"make"})," and ",(0,s.jsx)(n.code,{children:"openssl"})," in ",(0,s.jsx)(n.code,{children:"PATH"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["Note that ",(0,s.jsx)(n.code,{children:"tls-gen"})," and the certificate/key pairs\nit generates are self-signed and only suitable for development\nand test environments. The vast majority of production environments\nsh
1ould use certificates and keys issued by a widely trusted commercial\nCA."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"tls-gen"})," supports RSA and ",(0,s.jsx)(n.a,{href:"https://blog.cloudflare.com/a-relatively-easy-to-understand-primer-on-Elliptic-curve-cryptography/",children:"Elliptic Curve Cryptography"}),"\nalgorithms for key generation."]}),"\n",(0,s.jsx)(n.h3,{id:"automated-certificate-generation-transcript",children:"Using tls-gen's Basic Profile"}),"\n",(0,s.jsx)(n.p,{children:"Below is an example that generates a CA and uses it to produce two certificate/key pairs, one\nfor the server and another for clients. This is the setup that is expected by the rest of this guide."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"git clone https://github.com/rabbitmq/tls-gen tls-gen\ncd tls-gen/basic\n# private key password\nmake PASSWORD=bunnies\nmake verify\nmake info\nls -l ./result\n"})}),"\n",(0,s.jsx)(n.p,{children:"The certificate chain produced by this basic tls-gen profile looks like this:"}),"\n",(0,s.jsx)("figure",{children:(0,s.jsx)(n.p,{children:(0,s.jsx)(n.img,{alt:"Root CA and leaf certificates",src:t(64725).A+"",width:"248",height:"197"})})}),"\n",(0,s.jsx)(n.h2,{id:"enabling-tls",children:"Enabling TLS Support in RabbitMQ"}),"\n",(0,s.jsxs)(n.p,{children:["To enable the TLS support in RabbitMQ, the node has to be configured\nto know the location of the ",(0,s.jsx)(n.a,{href:"#certificates-and-keys",children:"Certificate Authority\nbundle"})," (a file with one more CA certificates), the server's certificate file, and the server's\nkey. A TLS listener should also be enabled to know what port to listen on for TLS-enabled client connections.\nMore TLS-related things can be configured. Those are covered in the rest of this guide."]}),"\n",(0,s.jsx)(n.p,{children:"Here are the essential configuration settings related to TLS:"}),"\n",(0,s.jsxs)("table",{className:"plain",children:[(0,s.jsx)("thead",{children:(0,s.jsxs)("tr",{children:[(0,s.jsx)("th",{children:"Configuration Key"}),(0,s.jsx)("th",{children:"Description"})]})}),(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("code",{children:"listeners.ssl"})}),(0,s.jsx)("td",{children:(0,s.jsxs)(n.p,{children:["A list of ports to listen on for TLS\nconnections. RabbitMQ can listen on a ",(0,s.jsx)("a",{href:"./networking",children:"single interface or multiple ones"}),"."]})})]}),(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("code",{children:"ssl_options.cacertfile"})}),(0,s.jsx)("td",{children:"Certificate Authority (CA) bundle file path"})]}),(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("code",{children:"ssl_options.certfile"})}),(0,s.jsx)("td",{children:"Server certificate file path"})]}),(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("code",{children:"ssl_options.keyfile"})}),(0,s.jsx)("td",{children:"Server private key file path"})]}),(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("code",{children:"ssl_options.password"})}),(0,s.jsx)("td",{children:"Password for private key file"})]}),(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("code",{children:"ssl_options.verify"})}),(0,s.jsxs)("td",{children:["Should ",(0,s.jsx)("a",{href:"#peer-verification",children:"peer verification"})," be enabled?"]})]}),(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("code",{children:"ssl_options.fail_if_no_peer_cert"})}),(0,s.jsx)("td",{children:(0,s.jsxs)(n.p,{children:["When set to ",(0,s.jsx)("code",{children:"true"}),", TLS connection\nwill be rejected if client fails to provide a certificate"]})})]})]}),"\n",(0,s.jsxs)(n.p,{children:["The options are provided in the ",(0,s.jsx)("a",{href:"./configure#configuration-files",children:"configuration\nfile"}),". An example of the config file is below, which\nwill start one TLS listener on port 5671 on all interfaces\non this hostname:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"listeners.ssl.default = 5671\n\nssl_options.cacertfile = /path/to/ca_certificate.pem\nssl_options.certfile = /path/to/server_certificate.pem\nssl_options.keyfile = /path/to/server_key.pem\nssl_options.verify = verify_peer\nssl_options.fail_if_no_peer_cert = true\n\n# If the private key file is password protected, set this value:\n# ssl_options.password = PASSWORD\n"})}),"\n",(0,s.jsxs)(n.p,{children:["This configuration will also perform ",(0,s.jsx)(n.a,{href:"#peer-verification",children:"peer certificate chain verification"}),"\nso clients without any certificates will be rejected."]}),"\n",(0,s.jsx)(n.p,{children:"It is possible to completely disable regular (non-TLS) listeners. Only TLS-enabled\nclients would be able to connect to such a node, and only if they use the correct port:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"# disables non-TLS listeners, only TLS-enabled clients will be able to connect\nlisteners.tcp = none\n\nlisteners.ssl.default = 5671\n\nssl_options.cacertfile = /path/to/ca_certificate.pem\nssl_options.certfile = /path/to/server_certificate.pem\nssl_options.keyfile = /path/to/server_key.pem\nssl_options.verify = verify_peer\nssl_options.fail_if_no_peer_cert = true\n"})}
1),"\n",(0,s.jsxs)(n.p,{children:["TLS settings can also be configured using the ",(0,s.jsx)(n.a,{href:"./configure#erlang-term-config-file",children:"classic config format"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-erlang",children:'[\n {rabbit, [\n {ssl_listeners, [5671]},\n {ssl_options, [{cacertfile, "/path/to/ca_certificate.pem"},\n {certfile, "/path/to/server_certificate.pem"},\n {keyfile, "/path/to/server_key.pem"},\n {verify, verify_peer},\n {fail_if_no_peer_cert, true}]}\n ]}\n].\n'})}),"\n",(0,s.jsx)(n.h3,{id:"enabling-tls-paths",children:"Certificate and Private Key File Paths"}),"\n",(0,s.jsx)(n.p,{children:"RabbitMQ must be able to read its configured CA certificate bundle, server certificate and private key. The files\nmust exist and have the appropriate permissions. When that's not the case the node will fail to start or fail to\naccept TLS-enabled connections."}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.strong,{children:"Note for Windows users"}),': on Windows, backslashes ("") in the\nconfiguration file are interpreted as escape sequences.\nFor example, to specify the\npath ',(0,s.jsx)(n.code,{children:"c:\\ca_certificate.pem"})," for the CA certificate on Windows,\nwould need to use ",(0,s.jsx)(n.code,{children:'"c:\\\\ca_certificate.pem"'})," or ",(0,s.jsx)(n.code,{children:'"c:/ca_certificate.pem"'}),"."]}),"\n",(0,s.jsx)(n.h3,{id:"enabling-tls-verify-configuration",children:"How to Verify that TLS is Enabled"}),"\n",(0,s.jsxs)(n.p,{children:["To verify that TLS has been enabled on the node, restart it and inspect its ",(0,s.jsx)(n.a,{href:"./logging",children:"log file"}),".\nIt should contain an entry about a TLS listener being enabled, looking like this:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{children:"2020-07-13 21:13:01.015 [info] <0.573.0> started TCP listener on [::]:5672\n2020-07-13 21:13:01.055 [info] <0.589.0> started TLS (SSL) listener on [::]:5671\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Another way is by using ",(0,s.jsx)(n.code,{children:"rabbitmq-diagnostics listeners"})," which should contain\nlines for TLS-enabled listeners:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"rabbitmq-diagnostics listeners\n#\n# ... (some output omitted for brevity)\n# => Interface: [::], port: 5671, protocol: amqp/ssl, purpose: AMQP 0-9-1 and AMQP 1.0 over TLS\n# ...\n"})}),"\n",(0,s.jsx)(n.h3,{id:"private-key-passwords",children:"Providing Private Key Password"}),"\n",(0,s.jsxs)(n.p,{children:["Private keys can be optional protected by a password.\nTo provide the password, use the ",(0,s.jsx)(n.code,{children:"password"})," option:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"listeners.ssl.1 = 5671\nssl_options.cacertfile = /path/to/ca_certificate.pem\nssl_options.certfile = /path/to/server_certificate.pem\nssl_options.keyfile = /path/to/server_key.pem\nssl_options.password = t0p$3kRe7\n"})}),"\n",(0,s.jsxs)(n.p,{children:["The same example using the ",(0,s.jsx)(n.a,{href:"./configure#erlang-term-config-file",children:"classic config format"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-erlang",children:'[\n {rabbit, [\n {ssl_listeners, [5671]},\n {ssl_options, [{cacertfile,"/path/to/ca_certificate.pem"},\n {certfile, "/path/to/server_certificate.pem"},\n {keyfile, "/path/to/server_key.pem"},\n {password, "t0p$3kRe7"}\n ]}\n ]}\n].\n'})}),"\n",(0,s.jsxs)(n.p,{children:["Classic config file format allows for ",(0,s.jsx)(n.a,{href:"./configure#configuration-encryption",children:"config value encryption"}),",\nwhich is recommended for passwords."]}),"\n",(0,s.jsx)(n.h2,{id:"peer-verification",children:"TLS Peer
1Verification: Who Do You Say You Are?"}),"\n",(0,s.jsxs)(n.p,{children:["As mentioned in the ",(0,s.jsx)(n.a,{href:"#certificates-and-keys",children:"Certificates and Keys"})," section, TLS has two\nprimary purposes: encrypting connection traffic and providing a way to verify\nthat the peer can be trusted (e.g. signed by a trusted Certificate Authority)\nto mitigate against ",(0,s.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Man-in-the-middle_attack",children:"Man-in-the-Middle attacks"}),",\na class of attacks where an attacker impersonates a legitimate trusted peer (usually a server).\nThis section will focus on the latter."]}),"\n",(0,s.jsx)(n.h3,{id:"peer-verification-how-it-works",children:"How Peer Verification Works"}),"\n",(0,s.jsxs)(n.p,{children:["When a TLS connection is established client and server perform connection negotiation that takes several steps.\nThe first step is when the peers ",(0,s.jsx)(n.em,{children:"optionally"})," exchange their ",(0,s.jsx)(n.a,{href:"#certificates-and-keys",children:"certificates"}),".\nHaving exchanged certificates, the peers can ",(0,s.jsx)(n.em,{children:"optionally"})," attempt\nto establish a chain of trust between their CA certificates and the certificates presented.\nThis acts to verify that the peer is who it claims to be (provided the private key hasn't been\nstolen)."]}),"\n",(0,s.jsxs)(n.p,{children:["The process is known as peer verification or peer validation\nand follows an algorithm known as the ",(0,s.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Certification_path_validation_algorithm",children:"Certification path validation algorithm"}),".\nUnderstanding the entire algorithm is not necessary in order to use peer verification,\nso this section provides an oversimplified explanation of the key parts."]}),"\n",(0,s.jsxs)(n.p,{children:["Each peer provides a ",(0,s.jsx)(n.em,{children:"chain of certificates"}),' that begins with a "leaf"\n(client or server) certificate and continues with at least one Certificate Authority (CA) certificate. That\nCA issued (signed) the leaf CA. If there are multiple CA certificates, they usually form a chain of signatures,\nmeaning that each CA certificate was signed by the next one. For example, if certificate B is signed by A and C is signed by B,\nthe chain is ',(0,s.jsx)(n.code,{children:"A, B, C"}),' (commas here are used for clarity). The "topmost" (first or only) CA is often referred\nto as the ',(0,s.jsx)("em",{children:"root CA"})," for the chain. Root CAs can be issued by well-known Certificate Authorities\n(commercial vendors) or any other party (",(0,s.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Self-signed_certificate",children:"self-signed"}),")."]}),"\n",(0,s.jsx)(n.p,{children:"Here's an example of the most basic chain with one root CA and one leaf (server or client) certificate:"}),"\n",(0,s.jsx)("figure",{children:(0,s.jsx)(n.p,{children:(0,s.jsx)(n.img,{alt:"Root CA and leaf certificates",src:t(64725).A+"",width:"248",height:"197"})})}),"\n",(0,s.jsx)(n.p,{children:"A chain with intermediate certificates might look like this:"}),"\n",(0,s.jsx)("figure",{children:(0,s.jsx)(n.p,{children:(0,s.jsx)(n.img,{alt:"Root CA, intermediate and leaf certificates",src:t(71857).A+"",width:"248",height:"318"})})}),"\n",(0,s.jsx)(n.p,{children:"During peer verification TLS connection client (or server) traverses\nthe chain of certificates presented by the peer\nand if a trusted certificate is found, considers the peer trusted."}),"\n",(0,s.jsx)(n.h3,{id:"mutual-peer-verification-mutual-tls-authentication-or-mtls",children:"Mutual Peer Verification (Mutual TLS Authentication or mTLS)"}),"\n",(0,s.jsxs)(n.p,{children:["When both sides perform this peer verification process, this is known\nas ",(0,s.jsx)(n.em,{children:"mutual TLS authentication"})," or ",(0,s.jsx)(n.em,{children:"mTLS"}),"."]}),"\n",(0,s.jsx)(n.p,{children:"Enabling mutual peer verification involves two things:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#peer-verification-configuration",children:"Enabling peer verification for client connections"})," on the RabbitMQ side"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.a,{href:"#peer-verification",children:"Enabling peer verification of the server"})," in application code"]}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:'In other words, mutual peer verification ("mTLS") is a joint responsibility of RabbitMQ nodes\nand client connections. Enabling peer verification on just one end is not enough.'}),"\n",(0,s.jsx)(n.h3,{id:"when-peer-verification-fails",children:"When Peer Verification Fails"}),"\n",(0,s.jsx)(n.p,{children:'If no trusted and otherwise valid certificate is found, peer verification fails and client\'s TLS (TCP) connection is\nclosed with a fatal error ("alert" in OpenSSL parlance) that says "Unknown CA" or similar. The alert\nwill be logged by the server with a message similar to this:'}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"2018-09-10 18:10:46.502 [info] <0.902.0< TLS server generated SERVER ALERT: Fatal - Unknown CA\n"})}),"\n",(0,s.jsx)(n.p,{children:"Certificate validity is also checked at every step.
1Certificates that are expired\nor aren't yet valid will be rejected. The TLS alert in that case will look something\nlike this:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"2018-09-10 18:11:05.168 [info] <0.923.0< TLS server generated SERVER ALERT: Fatal - Certificate Expired\n"})}),"\n",(0,s.jsxs)(n.p,{children:["The examples above demonstrate TLS alert messages logged by a RabbitMQ node.\nClients that perform peer verification will also raise alerts but may use different\nerror messages. ",(0,s.jsx)(n.a,{href:"https://tools.ietf.org/html/rfc8446#section-6.2",children:"RFC 8446 section 6.2"}),"\nprovides an overview of various alerts and what they mean."]}),"\n",(0,s.jsx)(n.h3,{id:"peer-verification-trusted-certificates",children:"Trusted Certificates"}),"\n",(0,s.jsx)(n.p,{children:"Every TLS-enabled tool and TLS implementation, including Erlang/OTP and\nRabbitMQ, has a way of marking a set of certificates as trusted."}),"\n",(0,s.jsx)(n.p,{children:"There are three common approaches to this:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["All trusted CA certificates must be added to a single file called the ",(0,s.jsx)(n.em,{children:"CA certificate bundle"})]}),"\n",(0,s.jsx)(n.li,{children:"All CA certificates in a directory are considered to be trusted"}),"\n",(0,s.jsx)(n.li,{children:"A dedicated tool is used to manage trusted CA certificates"}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"Different TLS implementation and tools use different options. In the context of RabbitMQ this means that\nthe trusted certificate management approach may be different for different client\nlibraries, tools and RabbitMQ server itself."}),"\n",(0,s.jsxs)(n.p,{children:["For example, OpenSSL and OpenSSL command line tools such as ",(0,s.jsx)(n.code,{children:"s_client"})," on Linux and other UNIX-like systems\nwill use a directory administered by superusers.\nCA certificates in that directory will be considered trusted,\nand so are the certificates issued by them (such as those presented by clients).\nLocations of the trusted certificate directory will ",(0,s.jsx)(n.a,{href:"https://www.happyassassin.net/2015/01/12/a-note-about-ssltls-trusted-certificate-stores-and-platforms/",children:"vary"}),"\n",(0,s.jsx)(n.a,{href:"http://gagravarr.org/writing/openssl-certs/others.shtml",children:"between distributions"}),", operating systems and releases."]}),"\n",(0,s.jsxs)(n.p,{children:["On Windows trusted certificates are managed using tools such as ",(0,s.jsx)(n.a,{href:"https://docs.microsoft.com/en-us/dotnet/framework/tools/certmgr-exe-certificate-manager-tool",children:"certmgr"}),"."]}),"\n",(0,s.jsx)(n.p,{children:"The certificates in the server's CA certificate bundle may be considered trusted.\nWe say \"may\" because it doesn't work the same way for all client libraries since this varies from TLS implementation\nto implementation. Certificates in a CA certificate bundler won't be considered to be trusted in Python,\nfor example, unless explicitly added to the trust store."}),"\n",(0,s.jsxs)(n.p,{children:["RabbitMQ relies on Erlang's TLS implementation. It assumes that\n",(0,s.jsx)(n.strong,{children:"all trusted CA certificates are added to the server certificate bundle"}),"."]}),"\n",(0,s.jsx)(n.p,{children:"When performing peer verification, RabbitMQ will only consider the root certificate (first certificate in the list) to be trusted.\nAny intermediate certificates will be ignored. If it's desired that intermediate certificates\nare also considered to be trusted they must be added to the trusted certificate list: the certificate bundle."}),"\n",(0,s.jsx)(n.p,{children:'While it is possible to place final ("leaf") certificates\nsuch as those used by servers and clients to the trusted certificate directory,\na much more common practice is to add CA certificates to the trusted certificate list.'}),"\n",(0,s.jsx)(n.p,{children:"The most common way of appending several certificates to one\nanother and use in a single Certificate Authority bundle file\nis to simply concatenate them:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cat rootca/ca_certificate.pem otherca/ca_certificate.pem > all_cacerts.pem\n"})}),"\n",(0,s.jsx)(n.h3,{id:"peer-verification-configuration",children:"Enabling Peer Verification"}),"\n",(0,s.jsxs)(n.p,{children:["On the server end, peer verification is primarily controlled using two configuration\noptions: ",(0,s.jsx)(n.code,{children:"ssl_options.verify"})," and ",(0,s.jsx)(n.code,{children:"ssl_options.fail_if_no_peer_cert"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["Setting the ",(0,s.jsx)(n.code,{children:"ssl_options.fail_if_no_peer_cert"})," option to ",(0,s.jsx)(n.code,{children:"false"})," tells\nthe node to accept clients which don't present a certificate (for example, were not configured to use one)."]}),"\n",(0,s.jsxs)(n.p,{children:["When the ",(0,s.jsx)(n.code,{children:"ssl_options.verify"})," option is set to ",(0,s.jsx)(n.code,{children:"verify_peer"}),",\nthe client does send us a certificate, the node must perform peer verification.\nWhen set to ",(0,s.jsx)(n.code,{children:"verify_none"}),", peer verification will be disabled and certificate\nexchange won't be performed."]}),"\n",(0,s.jsx)(n.p,{children:"For example, the following\nconfig will perform peer verification and reject clients that do not provide\na certificate:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"listeners.ssl.default = 5671\n\nssl_options.cacertfile = /path/to/ca_certificate.pem\nssl_options.certfile = /path/to/server_certificate.pem\nssl_options.keyfile = /path/to/server_key.pem\nssl_options.verify = verify_peer\nssl_options.fail_if_no_peer_cert = true\n"})}),"\n",(0,s.jsxs)(n.p,{children:["The same example in the ",(0,s.jsx)(n.a,{href:"./configure#config-file",children:"classic config format"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-erlang",children:'[\n{rabbit, [\n {ssl_listeners, [5671]},\n {ssl_options, [{cacertfile,"/path/to/ca_certificate.pem"},\n {certfile,"/path/to/server_certificate.pem"},\n {keyfile,"/path/to/server_key.pem"},\n {verify, verify_peer},\n {fail_if_no_peer_cert, true}]}\n ]}
1\n].\n'})}),"\n",(0,s.jsxs)(n.p,{children:["How exactly peer verification is configured in client libraries varies from library to library.\n",(0,s.jsx)(n.a,{href:"#java-client",children:"Java"})," and ",(0,s.jsx)(n.a,{href:"#dotnet-client",children:".NET"})," client sections cover peer\nverification in those clients."]}),"\n",(0,s.jsx)(n.p,{children:"Peer verification is highly recommended in production environments. With careful consideration,\ndisabling it can make sense in certain environments (e.g. development)."}),"\n",(0,s.jsx)("a",{id:"peer-verification-clients"}),"\n",(0,s.jsxs)(n.p,{children:["Thus it is possible to create an encrypted TLS connection ",(0,s.jsx)(n.em,{children:"without"})," having to\nverify certificates. Client libraries usually support both modes of operation."]}),"\n",(0,s.jsxs)(n.p,{children:["When peer verification is enabled, it is common for clients to also check whether\nthe hostname of the server\nthey are connecting to matches one of two fields\nin the server certificate: the ",(0,s.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Subject_Alternative_Name",children:"SAN (Subject Alternative Name)"}),"\nor CN (Common Name). When ",(0,s.jsx)(n.a,{href:"https://en.wikipedia.org/wiki/Wildcard_certificate",children:"wildcard certificates"})," are used,\nthe hostname is matched against a pattern. If there is no match, peer verification will also be\nfailed by the client. Hostname checks are also optional and generally orthogonal to certificate chain\nverification performed by the client."]}),"\n",(0,s.jsxs)(n.p,{children:["Because of this it is important to know what SAN (Subject Alternative Name) or CN (Common Name) values\nwere used when generating the certificate. If a certificate is generated on one host and used\non a different host then the ",(0,s.jsx)(n.code,{children:"$(hostname)"})," value should be replaced with the correct hostname of the target server."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"#automated-certificate-generation",children:"tls-gen"})," will use local machine's hostname for both values.\nLikewise, in the ",(0,s.jsx)(n.a,{href:"#manual-certificate-generation",children:"manual certificate/key pair generation section"})," local machine's hostname is specified as\n",(0,s.jsx)(n.code,{children:"...-subj /CN=$(hostname)/..."})," to some OpenSSL CLI tool commands."]}),"\n",(0,s.jsx)(n.h3,{id:"peer-verification-depth",children:"Certificate Chains and Verification Depth"}),"\n",(0,s.jsx)(n.p,{children:"When using a client certificate signed by an intermediate CA, it may be necessary\nto configure RabbitMQ server to use a higher verification depth."}),"\n",(0,s.jsx)(n.p,{children:'The depth is the maximum number of non-self-issued intermediate certificates that\nmay follow the peer certificate in a valid certification path.\nSo if depth is 0 the peer (e.g. client) certificate must be signed by the trusted CA directly,\nif 1 the path can be "peer, CA, trusted CA", if it is 2 "peer, CA, CA, trusted CA", and so on.\nThe default depth is 1.'}),"\n",(0,s.jsx)(n.p,{children:"The following example demonstrates how to configure certificate validation depth for\nRabbitMQ server:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"listeners.ssl.default = 5671\n\nssl_options.cacertfile = /path/to/ca_certificate.pem\nssl_options.certfile = /path/to/server_certificate.pem\nssl_options.keyfile = /path/to/server_key.pem\nssl_options.verify = verify_peer\nssl_options.depth = 2\nssl_options.fail_if_no_peer_cert = false\n"})}),"\n",(0,s.jsxs)(n.p,{children:["The same example in the ",(0,s.jsx)(n.a,{href:"./configure#config-file",children:"classic config format"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-erlang",children:'[\n {rabbit, [\n {ssl_listeners, [5671]},\n {ssl_options, [{cacertfile,"/path/to/ca_certificate.pem"},\n {certfile,"/path/to/server_certificate.pem"},\n {keyfile,"/path/to/server_key.pem"},\n {depth, 2},\n {verify,verify_peer},\n {fail_if_no_peer_cert,false}]}\n ]}\n].\n'})}),"\n",(0,s.jsxs)(n.p,{children:["When using RabbitMQ plugins such as ",(0,s.jsx)(n.a,{href:"./federation",children:"Federation"})," or ",(0,s.jsx)(n.a,{href:"./shovel",children:"Shovel"})," with TLS,\nit may be necessary to configure verification depth for the Erlang client that those plugins use under the hood,\nas ",(0,s.jsx)(n.a,{href:"#erlang-client",children:"explained below"}),"."]}),"\n",(0,s.jsx)(n.h2,{id:"java-client",children:"Using TLS in the Java Client"}),"\n",(0,s.jsx)(n.p,{children:"There are two main parts to enabling TLS in the RabbitMQ Java client:\nsetting up the key store with a bit of Java security framework plumbing and\nimplementing the desired peer verification strategy."}),"\n",(0,s.jsx)(n.h3,{id:"java-client-trust-managers-and-stores",children:"Key Managers, Trust Managers and Stores"}),"\n",(0,s.jsxs)(n.p,{children:["There are three main components in the Java security\nframework: ",(0,s.jsx)(n.a,{href:"https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/KeyManager.html",children:"Key Manager"}),",\n",(0,s.jsx)(n.a,{href:"https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/TrustManager.html",children:"Trust Manager"})," and ",(0,s.jsx)(n.a,{href:"https://docs.oracle.com/javase/8/docs/api/java/security/KeyStore.html",children:"Key Store"}),"."]}),"\n",(0,s.jsx)(n.p,{children:"A Key Manager is used by a peer (in this case, a client connection) to manage its certificates.\nDuring TLS connection/session negotiation, the key manager will c
1ontrol which\ncertificates to send to the remote peer."}),"\n",(0,s.jsx)(n.p,{children:"A Trust Manager is used by a peer to manage remote certificates.\nDuring TLS connection/session negotiation, the trust manager will control which\ncertificates are trusted from a remote peer. Trust managers can be used\nto implement any certificate chain verification logic."}),"\n",(0,s.jsxs)(n.p,{children:["A Key Store is a Java encapsulation of the certificate store concept. All\ncertificates must either be stored into a Java-specific binary format (JKS)\nor to be in the PKCS#12 format. These formats are managed using the\n",(0,s.jsx)(n.code,{children:"KeyStore"})," class. In the below examples the JKS format is used to add the trusted (server) certificate(s)\nto the store, while for the client key/certificate pair, the PKCS#12\nkey file generated by ",(0,s.jsx)(n.a,{href:"#automated-certificate-generation",children:"tls-gen"})," will be used."]}),"\n",(0,s.jsxs)(n.p,{children:["All TLS-related settings in the Java client\nare configured via the ",(0,s.jsx)(n.a,{href:"https://rabbitmq.github.io/rabbitmq-java-client/api/current/com/rabbitmq/client/ConnectionFactory.html",children:"ConnectionFactory"}),"."]}),"\n",(0,s.jsx)(n.h3,{id:"java-client-connecting",children:"Connecting with TLS"}),"\n",(0,s.jsx)(n.p,{children:"This very basic example will show a simple client connecting to a RabbitMQ\nserver over TLS without validating the server certificate, and\nwithout presenting any client certificate to the server."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-java",children:'import java.io.*;\nimport java.security.*;\n\nimport com.rabbitmq.client.*;\n\npublic class Example1 {\n\n public static void main(String[] args) throws Exception {\n ConnectionFactory factory = new ConnectionFactory();\n factory.setHost("localhost");\n factory.setPort(5671);\n\n factory.useSslProtocol();\n // Tells the library to setup the default Key and Trust managers for you\n // which do not do any form of remote server trust verification\n\n Connection conn = factory.newConnection();\n Channel channel = conn.createChannel();\n\n // non-durable, exclusive, auto-delete queue\n channel.queueDeclare("rabbitmq-java-test", false, true, true, null);\n channel.basicPublish("", "rabbitmq-java-test", null, "Hello, World".getBytes());\n\n GetResponse chResponse = channel.basicGet("rabbitmq-java-test", false);\n if (chResponse == null) {\n System.out.println("No message retrieved");\n } else {\n byte[] body = chResponse.getBody();\n System.out.println("Received: " + new String(body));\n }\n\n channel.close();\n conn.close();\n }\n}\n'})}),"\n",(0,s.jsxs)(n.p,{children:["This simple example is an echo client and server. It creates a channel\nand publishes to the default direct exchange, then\nfetches back what has been published and echoes it out. It uses\nan ",(0,s.jsx)(n.a,{href:"./queues",children:"exclusive, non-durable, auto-delete queue"})," that will be deleted shortly\nafter the connection is closed."]}),"\n",(0,s.jsx)(n.h3,{id:"java-client-connecting-with-peer-verification",children:"Connecting with Peer Verification Enabled"}),"\n",(0,s.jsxs)(n.p,{children:["For a Java client to trust a server, the server certificate must be added\nto a trust store which will be used to instantiate a ",(0,s.jsx)(n.a,{href:"https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/TrustManager.html",children:"Trust Manager"}),".\nThe JDK ships with a tool called ",(0,s.jsx)(n.code,{children:"keytool"})," that manages certificate stores. To import a certificate to\na store use ",(0,s.jsx)(n.code,{children:"keytool -import"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"keytool -import -alias server1 -file /path/to/server_certificate.pem -keystore /path/to/rabbitstore\n"})}),"\n",(0,s.jsxs)(n.p,{children:["The above command will import ",(0,s.jsx)(n.code,{children:"server/certificate.pem"})," into the ",(0,s.jsx)(n.code,{children:"rabbitstore"})," file\nusing the JKS format. The certificate will be referred to as ",(0,s.jsx)(n.code,{children:"server1"}
1)," in the trust store.\nAll certificates and keys must have distinct name in their store."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"keytool"})," will confirm that the certificate is trusted and ask for a password.\nThe password protects the trust store from any tampering attempt."]}),"\n",(0,s.jsxs)(n.p,{children:["The client certificate and key in a ",(0,s.jsx)(n.code,{children:"PKCS#12"})," file are then used. Note Java understands\nnatively the ",(0,s.jsx)(n.code,{children:"PKCS#12"})," format, no conversion is needed."]}),"\n",(0,s.jsxs)(n.p,{children:["The below example demonstrates how the key store and the trust store are used with a\n",(0,s.jsx)(n.a,{href:"https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/KeyManager.html",children:"Key Manager"}),"\nand ",(0,s.jsx)(n.a,{href:"https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/TrustManager.html",children:"Trust Manager"}),", respectively."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-java",children:'import java.io.*;\nimport java.security.*;\nimport javax.net.ssl.*;\n\nimport com.rabbitmq.client.*;\n\npublic class Example2 {\n\n public static void main(String[] args) throws Exception {\n char[] keyPassphrase = "MySecretPassword".toCharArray();\n KeyStore ks = KeyStore.getInstance("PKCS12");\n ks.load(new FileInputStream("/path/to/client_key.p12"), keyPassphrase);\n\n KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");\n kmf.init(ks, keyPassphrase);\n\n char[] trustPassphrase = "rabbitstore".toCharArray();\n KeyStore tks = KeyStore.getInstance("JKS");\n tks.load(new FileInputStream("/path/to/trustStore"), trustPassphrase);\n\n TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");\n tmf.init(tks);\n\n SSLContext c = SSLContext.getInstance("TLSv1.2");\n c.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);\n\n ConnectionFactory factory = new ConnectionFactory();\n factory.setHost("localhost");\n factory.setPort(5671);\n factory.useSslProtocol(c);\n factory.enableHostnameVerification();\n\n Connection conn = factory.newConnection();\n Channel channel = conn.createChannel();\n\n channel.queueDeclare("rabbitmq-java-test", false, true, true, null);\n channel.basicPublish("", "rabbitmq-java-test", null, "Hello, World".getBytes());\n\n GetResponse chResponse = channel.basicGet("rabbitmq-java-test", false);\n if (chResponse == null) {\n System.out.println("No message retrieved");\n } else {\n byte[] body = chResponse.getBody();\n System.out.println("Received: " + new String(body));\n }\n\n channel.close();\n conn.close();\n }\n}\n'})}),"\n",(0,s.jsx)(n.p,{children:"To ensure that the above code works as expected with untrusted certificates, set up\na RabbitMQ node with a certificate that has not been imported\ninto the key store and watch the connection fail."}),"\n",(0,s.jsx)(n.h4,{id:"java-client-hostname-verification",children:"Server Hostname Verification"}),"\n",(0,s.jsxs)(n.p,{children:["Hostname verification must be enabled separately using the\n",(0,s.jsx)(n.code,{children:"ConnectionFactory#enableHostnameVerification()"})," method. This is done in the example\nabove, for instance:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-java",children:'import java.io.*;\nimport java.security.*;\nimport javax.net.ssl.*;\n\nimport com.rabbitmq.client.*;\n\npublic class Example2 {\n\n public static void main(String[] args) throws Exception {\n char[] keyPassphrase = "MySecretPassword".toCharArray();\n KeyStore ks = KeyStore.getInstance("PKCS12");\n ks.load(new FileInputStream("/path/to/client_key.p12"), keyPassphrase);\n\n KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");\n kmf.init(ks, keyPassphrase);\n\n char[] trustPassphrase = "rabbitstore".toCharArray();\n KeyStore tks = KeyStore.getInstance("JKS");\n tks.load(new FileInputStream("/path/to/trustStore"), trustPassphrase);\n\n TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");\n tmf.init(tks);\n\n SSLContext c = SSLContext.getInstance("TLSv1.2");\n c.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);\n\n ConnectionFactory factory = new ConnectionFactory();\n factory.setHost("localhost");\n factory.setPort(5671);\n factory.useSslProtocol(c);\n factory.enableHostnameVerification();\n\n // this connection will both perform peer verification\n // and server hostname verification\n Connection conn = factory.newConnection();\n\n // snip ...\n }\n}\n'})}),"\n",(0,s.jsx)(n.p,{children:"This will verify\nthat the server certificate has been issued for the hostname the\nclient is connecting to. Unlike certificate chain verification, this feature\nis client-specific (not usually performed by the server)."}),"\n",(0,s.jsx)(n.h3,{id:"tls-versions-java-client",children:"Configuring TLS Version in Java Client"}),"\n",(0,s.jsxs)(n.p,{children:["Just like RabbitMQ server can be ",(0,s.jsx)(n.a,{href:"#tls-versions",children:"configured to support only specific TLS versions"}),",\nit may be necessary to configure preferred TLS version in the Java client. This is done using\nthe ",(0,s.jsx)(n.code,{children:"ConnectionFactory#useSslProtocol"})," overloads that accept a protocol version name\nor a ",(0,s.jsx)(n.code,{children:"SSLContext"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-java",children:'ConnectionFactory factory = new ConnectionFactory();\nfactory.setHost("localhost");\nfactory.setPort(5671);\n\nfactory.useSslProtocol("TLSv1.2");\n'})}),"\n",(0,s.jsx)(n.p,{children:"Modern releases of the library will attempt to use the latest TLS version\nsupported by the runtime."}),"\n",(0,s.jsx)(n.h2,{id:"dotnet-client",children:"Using TLS in the .NET Client"}),"\n",(0,s.jsxs)(n.p,{children:["For a client certificate to be understood on the .NET platform, they\ncan be in a number of formats including DER and PKCS#12 but\nnot PEM. For the DER format, .NET expects them to\nbe stored in files with ",(0,s.jsx)(n.code,{children:".cer"})," extension. ",(0,s.jsx)(n.a,{href:"#automated-certificate-generation",children:"tls-gen"}),"\ngenerates both PEM and PKCS#12 files."]}),"\n",(0,s.jsx)(n.h3,{id:"dotnet-peer-verification",children:".NET Trust Store"}),"\n",(0,s.jsxs)(n.p,{children:["On the .NET platform, ",(0,s.jsx)(n.a,{href:"#peer-verification-trusted-certificates",children:"trusted certificates"})," are managed by putting them\ninto any of a number of stores. All management of these stores is done\nwith the 'certmgr' tool."]}),"\n",(0,s.jsx)(n.p,{children:"N.B.: on some flavours of Windows there are two versions of\nthe command: one that ships with the operating system and\nprovides a graphical interface only, and one that ships\nwith the Windows SDK and provides both a graphical and command line interface.\nEither will do the job, but the examples below are based on the latter."}),"\n",(0,s.jsx)(n.p,{children:"For our case, because we're supplying the client certificate/key pair\nin a separate PKCS#12 file, all we need to do is to import the\ncertificate of the root Certificate Authority into\nthe Root (Windows) or Trust (Mono) store.\nAll certificates signed by any certificate in that store are automatically trusted."}),"\n",(0,s.jsxs)(n.p,{children:["In contrast to the Java client, which is happy to use a\nTLS connection without performing peer verification, the .NET client by default requires this\nverification to succeed. To suppress verification, an application can set\nthe ",(0,s.jsx)(n.code,{children:"System.Net.Security.SslPolicyErrors.RemoteCertificateNotAvailable"}),"\nand ",(0,s.jsx)(n.code,{children:"System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors"}),"\nflags in ",(0,s.jsx)("a",{href:(0,a.Vl)()+"/RabbitMQ.Client.SslOption.html",children:"SslOption"}),"."]}),"\n",(0,s.jsx)(n.h3,{id:"certmgr",children:"Certificate Management with Certmgr"}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"certmgr"})," is a command line tool that manages certificates in a specified store, for example,\nadds and deletes them. These stores can be per-user stores, or\nsystem-wide. Only administrative users can have write access to the system-wide stores."]}),"\n",(0,s.jsxs)(n.p,{children:["The following example adds a certificate to the store of user ",(0,s.jsx)(n.code,{children:"Root"})," (also known as ",(0,s.jsx)(n.code,{children:"Trust"})," in some .NET implementation)"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-PowerShell",children:"# Windows\ncertmgr -add -all \\path\\to\\cacert.cer -s Root\n"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Linux with Mono\ncertmgr -add -c Trust /path/to/cacert.cer\n"})}),"\n",(0,s.jsx)(n.p,{children:"To add a certificate to the system-wide (machine) certificate store instead, run"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-PowerShell",children:"# Windows\ncertmgr -add -all \\path\\to\\cacert.cer -s -r localMachine Root\n"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash=",children:"# Linux with Mono\ncertmgr -add -c -m Trust /path/to/cacert.cer\n"})}),"\n",(0,s.jsxs)(n.p,{children:["After adding to a store, we can view the contents of that store with the ",(0,s.jsx)(n.code,{children:"-all"})," (",(0,s.jsx)(n.code,{children:"-list"})," with Mono) switch:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"certmgr -all -s Root\n\n# \u2026 snip \u2026\n\nSelf-signed X.509 v3 Certificate\n Serial Number: AC3F2B74ECDD9EEA00\n Issuer Name: CN=MyTestCA\n Subject Name: CN=MyTestCA\n valid From: 25/08/2018 14:03:01\n valid
1Until: 24/09/2018 14:03:01\n Unique Hash: 1F04D1D2C20B97BDD5DB70B9EB2013550697A05E\n"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"certmgr -list -c Trust\n\n# \u2026 snip \u2026\n\nSelf-signed X.509 v3 Certificate\n Serial Number: AC3F2B74ECDD9EEA00\n Issuer Name: CN=MyTestCA\n Subject Name: CN=MyTestCA\n valid From: 25/08/2018 14:03:01\n valid Until: 24/09/2018 14:03:01\n Unique Hash: 1F04D1D2C20B97BDD5DB70B9EB2013550697A05E\n"})}),"\n",(0,s.jsx)(n.p,{children:"According to the above output there is one Self-signed X.509 v3 Certificate in the\ntrust store. The Unique Hash uniquely identifies this certificate in\nthis store. To delete this certificate, use the unique hash:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Windows\ncertmgr -del -c -sha1 1F04D1D2C20B97BDD5DB70B9EB2013550697A05E -s Root\n\n# \u2026 snip \u2026\n\nCertificate removed from store.\n"})}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# Linux with Mono\ncertmgr -del -c Trust 1F04D1D2C20B97BDD5DB70B9EB2013550697A05E\n\n# \u2026 snip \u2026\n\nCertificate removed from store.\n"})}),"\n",(0,s.jsx)(n.h3,{id:"dotnet-connection-params",children:"Connection TLS Settings"}),"\n",(0,s.jsx)(n.p,{children:"To create a TLS-enabled connection to RabbitMQ, we need to set some new\nfields in the ConnectionFactory's Parameters field.\nTo make things\neasier, there is a new Field Parameters.Ssl that acts like a namespace\nfor all the other fields that we need to set. The fields are:"}),"\n",(0,s.jsxs)("table",{className:"plain",children:[(0,s.jsx)("thead",{children:(0,s.jsxs)("tr",{children:[(0,s.jsx)("th",{children:"Property"}),(0,s.jsx)("th",{children:"Description"})]})}),(0,s.jsxs)("tbody",{children:[(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("code",{children:"Ssl.CertPath"})}),(0,s.jsx)("td",{children:(0,s.jsx)(n.p,{children:"This is the path to the client's certificate in\nPKCS#12 format if your server expects client side verification. This\nis optional."})})]}),(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("code",{children:"Ssl.CertPassphrase"})}),(0,s.jsx)("td",{children:(0,s.jsx)(n.p,{children:"If you are using a client certificate in PKCS#12\nformat then it'll probably have a password, which you specify in\nthis field."})})]}),(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("code",{children:"Ssl.Enabled"})}),(0,s.jsx)("td",{children:(0,s.jsx)(n.p,{children:"This is a boolean field that turns TLS support on or\noff. It is off by default."})})]}),(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("code",{children:"Ssl.ServerName"})}),(0,s.jsx)("td",{children:(0,s.jsx)(n.p,{children:".NET expects this to match the Subject Alternative Name (SAN) or Common Name (CN) on\nthe certificate that the server sends over."})})]})]})]}),"\n",(0,s.jsx)(n.h3,{id:"dotnet-tls-versions-dotnet-client",children:"TLS Versions"}),"\n",(0,s.jsxs)(n.p,{children:["TLS has been around since the mid-90s, and there are multiple TLS versions available,\nalthough older versions are retired by the industry ",(0,s.jsx)(n.a,{href:"https://www.cloudflare.com/en-ca/learning/ssl/why-use-tls-1.3/",children:"as newer and more secure ones are developed"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["Just like RabbitMQ server can be ",(0,s.jsx)(n.a,{href:"#tls-versions",children:"configured to support only specific TLS versions"}),",\nit may be necessary to configure preferred TLS version in the .NET client. This is done using\nthe TLS options accessible via ",(0,s.jsx)(n.code,{children:"ConnectionFactory#Ssl"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["Supported TLS version values are those of the ",(0,s.jsx)(n.a,{href:"https://docs.microsoft.com/en-us/dotnet/api/system.security.authentication.sslprotocols?view=netframework-4.8",children:"System.Security.Authentication.SslProtocols enum"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-csharp",children:'using System.Security.Authentication;\n\n// ...\n\nConnectionFactory cf = new ConnectionFactory();\n\ncf.Ssl.Enabled = true;\ncf.Ssl.ServerName = System.Net.Dns.GetHostName();\ncf.Ssl.CertPath = "/path/to/client_key.p12";\ncf.Ssl.CertPassphrase = "MySecretPassword";\n\n// Use TLSv1.2 for this connection\ncf.Ssl.Version = SslProtocols.Tls12;\n'})}),"\n",(0,s.jsx)(n.p,{children:"RabbitMQ .NET client 5.x series uses TLSv1.0 by default."}),"\n",(0,s.jsxs)(n.p,{children:["Starting with RabbitMQ .NET client 6.0\nthe default changes to ",(0,s.jsx)(n.a,{href:"https://docs.microsoft.com/en-us
1/dotnet/api/system.security.authentication.sslprotocols?view=netframework-4.8#System_Security_Authentication_SslProtocols_None",children:(0,s.jsx)(n.code,{children:"SslProtocols.None"})}),",\nwhich means the default is ",(0,s.jsx)(n.a,{href:"https://docs.microsoft.com/en-us/dotnet/framework/network-programming/tls?view=netframework-4.6.2",children:"picked by the .NET framework or the operating system"})," depending on ",(0,s.jsx)(n.a,{href:"https://docs.microsoft.com/en-us/dotnet/framework/network-programming/tls?view=netframework-4.6.2#configuring-security-via-appcontext-switches-for-net-framework-46-or-later-versions",children:"app context switches"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["If a connection that uses ",(0,s.jsx)(n.code,{children:"SslProtocols.None"})," to pick a suitable TLS version fails, the client\nwill retry with TLSv1.2 enabled explicitly. This reduces the need for explicit configuration\non the application developer's end in environments where automatic TLS version selection is\ndisabled, not available or otherwise cannot be relied on."]}),"\n",(0,s.jsxs)(n.p,{children:["Modern .NET frameworks versions ",(0,s.jsx)(n.a,{href:"https://docs.microsoft.com/en-us/dotnet/framework/network-programming/tls?view=netframework-4.6.2",children:"default to TLSv1.2"}),"."]}),"\n",(0,s.jsx)(n.h3,{id:"dotnet-example",children:"Code Example"}),"\n",(0,s.jsxs)(n.p,{children:["This is a more or less direct port of the ",(0,s.jsx)(n.a,{href:"#java-client-connecting",children:"Java client example"}),". It\ncreates a channel and publishes to\nthe default direct exchange, then reads back what has been\npublished and echoes it out. Note that we use an\n",(0,s.jsx)(n.a,{href:"./queues",children:"exclusive, non-durable, auto-delete queue"})," so we don't have\nto worry about manually cleaning up after ourselves"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-csharp",children:'using System;\nusing System.IO;\nusing System.Text;\n\nusing RabbitMQ.client;\n\nnamespace RabbitMQ.client.Examples\n{\n public class TestSSL\n {\n public static async Task<int> Main(string[] args)\n {\n ConnectionFactory cf = new ConnectionFactory();\n\n cf.Ssl.Enabled = true;\n cf.Ssl.ServerName = System.Net.Dns.GetHostName();\n cf.Ssl.CertPath = "/path/to/client_key.p12";\n cf.Ssl.CertPassphrase = "MySecretPassword";\n\n using (IConnection conn = await cf.CreateConnectionAsync())\n {\n using (IChannel ch = await conn.CreateChannelAsync())\n {\n Console.WriteLine("Successfully connected and opened a channel");\n await ch.QueueDeclareAsync("rabbitmq-dotnet-test", false, false, false, null);\n Console.WriteLine("Successfully declared a queue");\n await ch.QueueDeleteAsync("rabbitmq-dotnet-test");\n Console.WriteLine("Successfully deleted the queue");\n }\n }\n return 0;\n }\n }\n}\n'})}),"\n",(0,s.jsx)(n.h3,{id:"tls-verification-in-dotnet",children:"TLS Peer
1Verification in .NET Client"}),"\n",(0,s.jsxs)(n.p,{children:["TLS offers peer verification (validation), a way for client and server to\nverify each other's identity based on peer's certificate information.\nWhen peer verification is enabled, typically the ",(0,s.jsx)("em",{class:"",children:"hostname"})," of the server\nyou're connecting to needs to match the ",(0,s.jsx)("em",{class:"",children:"CN (Common Name)"})," field on\nthe server's certificate, otherwise the certificate will be\nrejected. However, peer verification doesn't have to be limited to just CN\nand hostname matching in general."]}),"\n",(0,s.jsxs)(n.p,{children:["This is why the commands at the start of this guide specify\n",(0,s.jsx)(n.code,{children:"...-subj /CN=$(hostname)/..."})," which dynamically looks up your\nhostname. If you're generating certificates on one machine, and using\nthem on the other then be sure to swap out the ",(0,s.jsx)(n.code,{children:"$(hostname)"}),"\nsection, and replace it with the correct hostname for your server."]}),"\n",(0,s.jsxs)(n.p,{children:["On the .NET platform, ",(0,s.jsx)(n.a,{href:"http://msdn.microsoft.com/en-us/library/system.net.security.remotecertificatevalidationcallback(v=vs.110).aspx",children:"RemoteCertificateValidationCallback"}),"\ncontrols TLS verification behavior."]}),"\n",(0,s.jsxs)(n.p,{children:["In RabbitMQ .NET client, ",(0,s.jsx)(n.code,{children:"RabbitMQ.client.SslOption.CertificatevalidationCallback"}),"\ncan be used to provide a ",(0,s.jsx)(n.a,{href:"http://msdn.microsoft.com/en-us/library/system.net.security.remotecertificatevalidationcallback(v=vs.110).aspx",children:"RemoteCertificateValidationCallback"}),"\ndelegate. The delegate will be used to verify peer (RabbitMQ node) identity using whatever logic fits\nthe applications."]}),"\n",(0,s.jsx)(n.p,{children:"If this is not specified, the default callback will be\nused in conjunction with the AcceptablePolicyErrors\nproperty to determine if the remote server certificate is\nvalid."}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"System.Net.Security.SslPolicyErrors.RemoteCertificateNameMismatch"}),"\nflag in ",(0,s.jsx)(n.code,{children:"RabbitMQ.client.SslOption.AcceptablePolicyErrors"}),"\ncan be used to disable peer verification (not recommended in production environments!)."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"RabbitMQ.client.SslOption.CertificateSelectionCallback"}),"\ncan be used to provide\na ",(0,s.jsx)(n.a,{href:"http://msdn.microsoft.com/en-us/library/system.net.security.localcertificateselectioncallback(v=vs.110).aspx",children:"LocalCertificateSelectionCallback"}),"\nthat will select the local certificate used for peer verification."]}),"\n",(0,s.jsx)(n.h2,{id:"tls-versions",children:"Limiting TLS Versions Used by the Server"}),"\n",(0,s.jsx)(n.h3,{id:"tls-versions-why-limit",children:"Why Limit TLS Versions"}),"\n",(0,s.jsxs)(n.p,{children:["TLS (n\xe9e SSL) has evolved over time and has multiple versions in use.\nEach version builds on the shortcomings of previous versions. Most of the time\nthe shortcomings resulted in ",(0,s.jsx)(n.a,{href:"#major-vulnerabilities",children:"known attacks"})," that affect specific\nversions of TLS (and SSL). Disabling older TLS versions is a way to mitigate\nmany of those attacks (another technique is to ",(0,s.jsx)(n.a,{href:"#cipher-suites",children:"disable affected cipher suites"}),")."]}),"\n",(0,s.jsx)(n.p,{children:"For the above reasons, recent release series of Erlang only enable latest supported\nTLS version by default, as demonstrated in the below table."}),"\n",(0,s.jsxs)("table",{children:[(0,s.jsx)("thead",{children:(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:(0,s.jsx)("strong",{children:"Erlang Series"})}),(0,s.jsx)("td",{children:(0,s.jsx)("strong",{children:"TLS Versions Enabled by Default"})})]})}),(0,s.jsxs)("tbody",{children:[(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:"27.x"}),(0,s.jsxs)("td",{children:[(0,s.jsx)(n.a,{href:"#tls1.3",children:"TLSv1.3"})," and TLSv1.2"]})]}),(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:"26.x"}),(0,s.jsxs)("td",{children:[(0,s.jsx)(n.a,{href:"#tls1.3",children:"TLSv1.3"})," and TLSv1.2"]}
1)]})]})]}),"\n",(0,s.jsxs)(n.p,{children:["Users of ",(0,s.jsx)(n.a,{href:"./which-erlang",children:"older supported Erlang releases"}),"\nare encouraged to limit supported TLS versions to 1.2 and later versions only, if possible.\nConsider TLSv1.0 and TLSv1.1 to be ",(0,s.jsx)(n.strong,{children:"deprecated by the industry"}),"."]}),"\n",(0,s.jsx)(n.h3,{id:"tls-versions-why-not-limit",children:"Why Not Limit TLS Versions"}),"\n",(0,s.jsxs)(n.p,{children:["Limiting TLS versions to only TLSv1.3 or even only TLSv1.2 means that clients\nthat ",(0,s.jsx)(n.a,{href:"#tls-version-support-in-jdk-and-net",children:"support older TLS versions only"})," won't be able to connect."]}),"\n",(0,s.jsx)(n.p,{children:"If support for applications that use such old runtimes is important, the server must\nbe configured to support older versions of TLS. In most cases, supporting TLSv1.2\nshould be sufficient."}),"\n",(0,s.jsx)("a",{id:"tls-versions-server"}),"\n",(0,s.jsxs)(n.p,{children:["To limit enabled TLS protocol versions, use the ",(0,s.jsx)(n.code,{children:"ssl_options.versions"})," setting."]}),"\n",(0,s.jsxs)(n.p,{children:["The example below only accepts TLSv1.3 (the most recent and secure version),\nand requires the node to be running on Erlang 26 compiled against a very recent OpenSSL.\nClients that use older runtimes (e.g. JDK, .NET, Python) without TLSv1.3 support\n",(0,s.jsx)(n.strong,{children:"will not be able to connect"})," with this setup."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"listeners.ssl.1 = 5671\n\nssl_options.cacertfile = /path/to/ca_certificate.pem\nssl_options.certfile = /path/to/server_certificate.pem\nssl_options.keyfile = /path/to/server_key.pem\n\nssl_options.versions.1 = tlsv1.3\n\n# Limits enable cipher suites to only those used by TLSv1.3.\n# There are no cipher suites supported by both TLSv1.3 and TLSv1.2.\nssl_options.ciphers.1 = TLS_AES_256_GCM_SHA384\nssl_options.ciphers.2 = TLS_AES_128_GCM_SHA256\nssl_options.ciphers.3 = TLS_CHACHA20_POLY1305_SHA256\nssl_options.ciphers.4 = TLS_AES_128_CCM_SHA256\nssl_options.ciphers.5 = TLS_AES_128_CCM_8_SHA256\n"})}),"\n",(0,s.jsx)(n.p,{children:"The example below disables versions older than TLSv1.2:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"listeners.ssl.1 = 5671\nssl_options.cacertfile = /path/to/ca_certificate.pem\nssl_options.certfile = /path/to/server_certificate.pem\nssl_options.keyfile = /path/to/server_key.pem\n\nssl_options.versions.1 = tlsv1.2\n"})}),"\n",(0,s.jsx)(n.h3,{id:"verifying-tls-versions",children:"Verifying Enabled TLS Versions"}),"\n",(0,s.jsxs)(n.p,{children:["To verify provided TLS versions, ",(0,s.jsxs)(n.a,{href:"https://www.feistyduck.com/library/openssl-cookbook/online/ch-testing-with-open./ssl",children:["use ",(0,s.jsx)(n.code,{children:"openssl s_client"})]}),"\nwith an ",(0,s.jsx)(n.a,{href:"https://www.openssl.org/docs/man1.1.1/man1/openssl-s_client.html",children:"appropriate TLS version flag"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# connect using TLSv1.3\nopenssl s_client -connect 127.0.0.1:5671 -tls1_3\n"})}),"\n",(0,s.jsx)(n.p,{children:"and look for the following in the output:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{children:"New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384\n"})}),"\n",(0,s.jsx)(n.p,{children:"In the example below, TLSv1.2 is used:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"# connect using TLSv1.2\nopenssl s_client -connect 127.0.0.1:5671 -tls1_2\n"})}),"\n",(0,s.jsx)(n.p,{children:"The protocol and negotiated cipher suite in the output would\nlook like so:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{children:"SSL-Session:\n Protocol : TLSv1.2\n Cipher : ECDHE-RSA-AES256-GCM-SHA384\n"})}),"\n",(0,s.jsx)(n.h3,{id:"tls1.3",children:"TLSv1.3"}),"\n",(0,s.jsxs)(n.admonition,{type:"important",children:[(0,s.jsx)(n.p,{children:'TLS 1.3 is a major revision of the suite of standards collectively known as "TLS".\nIt intentionally breaks backwards compatibility with all earlier versions.'}),(0,s.jsx)(n.p,{children:"While modern versions of most popular programming languages and runtimes have supports TLS 1.3 for a few years now, adopting of this\nversion requires planning and should be treated as a change that will affect\napplications that use TLS."}),(0,s.jsx)(n.p,{children:"If some applications or their runtimes cannot be easily upgraded to use this new version,\nTLS 1.3 adoption on the RabbitMQ side might be a non-starter."})]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"https://wiki.openssl.org/index.php/TLS1.3",children:"TLSv1.3"})," is a major revision to the TLS protocol. It is the most recent\nand secure option."]}),"\n",(0,s.jsxs)(n.p,{children:["TLSv1.3 support requires the node to be ",(0,s.jsx)(n.a,{href:"./which-erlang",children:"running on Erlang 27 or 26"})," compiled against a very recent OpenSSL."]}),"\n",(0,s.jsxs)(n.p,{children:["Clients that use older runtimes (e.g. JDK, .NET, Python) without TLSv1.3 support\n",(0,s.jsx)(n.strong,{children:"will not be able to connect"})," to RabbitMQ nodes that are configured to only accept TLSv1.3 connections."]}),"\n",(0,s.jsx)(n.p,{children:"Because TLSv1.3 shares no cipher suites with earlier TLS versions, when enabling TLSv1.3,\nlist a set of TLSv1.3-specific cipher suites:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"listeners.ssl.1 = 5671\n\nssl_options.cacertfile = /path/to/ca_certificate.pem\nssl_options.certfile = /path/to/server_certificate.pem\nssl_options.keyfile = /path/to/server_key.pem\n\nssl_options.versions.1 = tlsv1.3\n\n# Limits enable cipher suites to only those used by TLSv1.3.\n# There are no cipher suites supported by both TLSv1.3 and TLSv1.2.\nssl_options.ciphers.1 = TLS_AES_256_GCM_SHA384\nssl_options.ciphers.2 = TLS_AES_128_GCM_SHA256\nssl_options.ciphers.3 = TLS_CHACHA20_POLY1305_SHA256\nssl_options.ciphers.4 = TLS_AES_128_CCM_SHA256\nssl_options.ciphers.5 = TLS_AES_128_CCM_8_SHA256\n"})}),"\n",(0,s.jsx)(n.p,{children:"Explicit cipher suite configuration may also be necessary on the client side."}),"\n",(0,s.jsxs)(n.p,{children:["To verify provided TLS versions, use ",(0,s.jsx)(n.code,{children:"openssl s_client"})," as\n",(0,s.jsx)(n.a,{href:"#verifying-tls-versions",children:"explained above"}),"."]}),"\n",(0,s.jsx)(n.h3,{id:"tls-version-support-in-jdk-and-net",children:"TLS Version Support Table for JDK and .NET"}),"\n",(0,s.jsx)(n.p,{children:"Disabling TLSv1.0 limits the number of client platforms supported. Below is a table that\nexplains what TLS versions are supported by what JDK and .NET releases."}),"\n",(0,s.jsxs)("table",{children:[(0,s.jsx)("thead",{children:(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:"TLS version"}),(0,s.jsx)("td",{children:"Minimum JDK version"}),(0,s.jsx)("td",{children:"Minimum .NET version"})]})}),(0,s.jsxs)("tbody",{children:[(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:"TLS 1.3"}),(0,s.jsxs)("td",{children:["JDK 8 ",(0,s.jsx)("a",{href:"https://www.oracle.com/java/technologies/javase/8u261-relnotes.html",children:"starting with JDK8u261"}),", JDK 11+"]}),(0,s.jsxs)("td",{children:[(0,s.jsx)("a",{href:"https://github.com/dotnet/docs/issues/4675",children:".NET 4.7"}
1)," on ",(0,s.jsx)("a",{href:"https://docs.microsoft.com/en-us/dotnet/framework/network-programming/tls",children:"Windows versions that support TLSv1.3"})]})]}),(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:"TLS 1.2"}),(0,s.jsxs)("td",{children:["JDK 7 (see ",(0,s.jsx)("a",{href:"http://docs.oracle.com/javase/7/docs/technotes/guides/security/SunProviders.html#SunJSSEProvider",children:"Protocols"}),",\n",(0,s.jsx)("a",{href:"http://docs.oracle.com/javase/8/docs/technotes/guides/security/enhancements-8.html",children:"JDK 8 recommended"})]}),(0,s.jsx)("td",{children:".NET 4.5"})]}),(0,s.jsxs)("tr",{children:[(0,s.jsx)("td",{children:"TLS 1.1"}),(0,s.jsxs)("td",{children:["JDK 7 (see ",(0,s.jsx)("a",{href:"http://docs.oracle.com/javase/7/docs/technotes/guides/security/SunProviders.html#SunJSSEProvider",children:"Protocols"}),",\n",(0,s.jsx)("a",{href:"http://docs.oracle.com/javase/8/docs/technotes/guides/security/enhancements-8.html",children:"JDK 8 recommended"})]}),(0,s.jsx)("td",{children:".NET 4.5"})]})]})]}),"\n",(0,s.jsxs)(n.p,{children:["Oracle JDK has a ",(0,s.jsx)(n.a,{href:"https://java.com/en/jre-jdk-cryptoroadmap.html",children:"public roadmap on cryptography"})," and related standards\nthat outlines when certain cipher suites or TLS versions will be deprecated or removed."]}),"\n",(0,s.jsx)(n.h2,{id:"key-usage",children:"Public Extended Key Usage Options (EKUs)"}),"\n",(0,s.jsxs)(n.p,{children:["Public keys (certificates) have a number of fields that describe the intended usage scenarios for the key.\nThe fields limit how the key is allowed to be used by various tools.\nFor example, a public key can be used to verify certificate signatures (act as a ",(0,s.jsx)(n.a,{href:"#certificates-and-keys",children:"Certificate Authority"})," key)."]}),"\n",(0,s.jsxs)(n.p,{children:["These fields also have effects on what ",(0,s.jsx)(n.a,{href:"#cipher-suites",children:"cipher suites"})," will be used by RabbitMQ nodes\nand clients during connection negotiation (more specifically, the TLS handshake),\nso it is important to explain what the effects are."]}),"\n",(0,s.jsx)(n.p,{children:"This guide will cover them with some intentional oversimplification. Broadly speaking, the fields fall into one of three categories:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://tools.ietf.org/html/rfc5280#section-4.2.1.3",children:"keyUsage"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://tools.ietf.org/html/rfc5280#section-4.2.1.9",children:"Basic Constraints"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://tools.ietf.org/html/rfc5280#section-4.2.1.12",children:"extendedKeyUsage"})}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"Some fields are boolean values, others are of different types such as a set of options (bits) that can be set or unset."}),"\n",(0,s.jsx)(n.p,{children:"Data services are largely agnostic to the constraints and key usage options used. However, some are essential\nto the use cases described in this guide."}),"\n",(0,s.jsx)(n.p,{children:"For servers, they are"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"TLS Server Authentication"})," (a.k.a. ",(0,s.jsx)(n.code,{children:"id-kp-serverAuth"}),", provide RabbitMQ node's identity to clients)"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"Digital Signature"})," (verification of digital signatures)"]}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"Key Encipherment"})}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"For clients, they are"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"TLS Client Authentication"})," (a.k.a. ",(0,s.jsx)(n.code,{children:"id-kp-clientAuth"}),", provides client's identity to RabbitMQ nodes, proxies)"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"Digital Signature"})," (verification of digital signatures)"]}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.code,{children:"Key Encipherment"})}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:["Specifically ",(0,s.jsx)(n.code,{children:"TLS Server Authentication"})," and ",(0,s.jsx)(n.code,{children:"TLS Client Authentication"})," are used for ",(0,s.jsx)(n.a,{href:"#peer-verification",children:"peer verification"}),".\nThey must be set for the server and client certificates,\nrespectively, at public key generation time."]}),"\n",(0,s.jsxs)(n.admonition,{type:"tip",children:[(0,s.jsxs)(n.p,{children:["Only one of the ",(0,s.jsx)(n.code,{children:"Authentication"})," EKUs should be set in most cases: the ",(0,s.jsx)(n.code,{children:"TLS Server Authentication"})," for the certificates that RabbitMQ nodes\nwill use, and the ",(0,s.jsx)(n.code,{children:"TLS Client Authentication"})," for the certificates that clients (applications) will use."]}),(0,s.jsx)(n.p,{children:"Setting both on a single certificate is no longer considered to be a good (secure) industry practice:"}),(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://security.googleblog.com/2025/05/sustaining-digital-certificate-security-chrome-root-store-changes.html",children:"Source one"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://googlechrome.github.io/chromerootprogram/",children:"Source two"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://www.sectigo.com/blog/tls-client-authentication-public-ca-en
1d-2026",children:"Source three"})}),"\n",(0,s.jsx)(n.li,{children:(0,s.jsx)(n.a,{href:"https://knowledge.digicert.com/alerts/sunsetting-client-authentication-eku-from-digicert-public-tls-certificates",children:"Source four"})}),"\n"]})]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"#automated-certificate-generation",children:"tls-gen"})," will make sure that these constraints and extensions are correctly set.\nWhen ",(0,s.jsx)(n.a,{href:"#manual-certificate-generation",children:"generating certificates manually"}),", this is a responsibility of\nthe operator that generates the key pairs, or a key pair provider."]}),"\n",(0,s.jsx)(n.h3,{id:"key-usage-effects-on-cipher-suites",children:"Extensions and Their Effect on Accepted Cipher Suites (Cipher Suite Filtering)"}),"\n",(0,s.jsxs)(n.p,{children:["Two key extensions are critically important for two major types of ",(0,s.jsx)(n.a,{href:"#cipher-suites",children:"cipher suites"}),":"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"digitalSignature"})," for ECC (Elliptic Curve Cryptography)-based suites"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"keyEncipherment"})," for RSA-based suites"]}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:'It is highly recommended that both of the above options (bits) are set for certificates that will\nbe used by both RabbitMQ nodes and client libraries. If those bits are not set, TLS implementations\nwill leave out an entire class of cipher suites from consideration, potentially resulting in confusing\n"no suitable cipher suite found" alerts (error messages) at connection time.'}),"\n",(0,s.jsx)(n.h3,{id:"examining-certificate-extensions",children:"Examining Certificate Extensions"}),"\n",(0,s.jsxs)(n.p,{children:["To see what constraints and extensions are set for a public key, use the ",(0,s.jsx)(n.code,{children:"openssl x509"})," command:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"openssl x509 -in /path/to/certificate.pem -text -noout\n"})}),"\n",(0,s.jsx)(n.p,{children:"Its output will include a nested list of extensions and constraints that looks similar to this\n(this is a client certificate example):"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"X509v3 extensions:\n X509v3 Basic Constraints:\n CA:FALSE\n X509v3 Key Usage:\n Digital Signature, Key Encipherment\n X509v3 Extended Key Usage:\n TLS Web Client Authentication\n"})}),"\n",(0,s.jsx)(n.p,{children:"The above set of extensions says that this is a public key that can be used to authenticate\na client (provide a client identity to a RabbitMQ node), cannot be used as a Certificate Authority\ncertificate and can be used for key encipherment and digital signature."}),"\n",(0,s.jsx)(n.p,{children:"For the purpose of this guide, this is a suitable certificate (public key) to be used for client connections."}),"\n",(0,s.jsx)(n.p,{children:"Below is an example of a public key suitable certificate for server authentication (providing a RabbitMQ node's identity to clients):"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"X509v3 extensions:\n X509v3 Basic Constraints:\n CA:FALSE\n X509v3 Key Usage:\n Digital Signature, Key Encipherment\n X509v3 Extended Key Usage:\n TLS Web Server Authentication\n"})}),"\n",(0,s.jsx)(n.h2,{id:"cipher-suites",children:"Cipher Suites"}),"\n",(0,s.jsxs)(n.p,{children:["It is possible to configure what cipher suites will be used by RabbitMQ. Note that not all\nsuites will be available on all systems. For example, to use Elliptic curve ciphers,\na recent ",(0,s.jsx)(n.a,{href:"./which-erlang",children:"supported Erlang release"})," must be used."]}),"\n",(0,s.jsxs)(n.p,{children:["What cipher suites RabbitMQ nodes and clients used can also be effectively limited by the ",(0,s.jsx)(n.a,{href:"#key-usage",children:"public key usage fields"}),"\nand their values. It is important to make sure that those key usage options are acceptable before proceeding\nwith cipher suite configuration."]}),"\n",(0,s.jsx)(n.h3,{id:"available-cipher-suites",children:"Listing Cipher Suites Available on a RabbitMQ Node"}),"\n",(0,s.jsxs)(n.p,{children:["To list cipher suites supported by the Erlang runtime of a running node, use ",(0,s.jsx)(n.code,{children:"rabbitmq-diagnostics cipher_suites --format openssl"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"rabbitmq-diagnostics cipher_suites --format openssl -q\n"})}),"\n",(0,s.jsx)(n.p,{children:"This will produce a list of cipher suites in the OpenSSL format."}),"\n",(0,s.jsxs)(n.p,{children:["Note that if you use ",(0,s.jsx)(n.code,{children:"--format erlang"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"rabbitmq-diagnostics cipher_suites --format erlang -q\n"})}),"\n",(0,s.jsxs)(n.p,{children:["then ",(0,s.jsx)(n.code,{children:"rabbitmq-diagnostics cipher_suites"})," will list cipher suites in the format\nthat's only accepted in the ",(0,s.jsx)(n.a,{href:"./configure#erlang-term-config-file",children:"classic config format"}),". The OpenSSL format is accepted\nby both config formats. Note that cipher suites are not enquoted in the new style config format\nbut double quotes are required in the classic format."]}),"\n",(0,s.jsxs)(n.p,{children:["The cipher suites listed by the above command are in formats that can be used for inbound and outgoing (e.g. ",(0,s.jsx)(n.a,{href:"./shovel",children:"Shovel"}),", ",(0,s.jsx)(n.a,{href:"./federation",children:"Federation"}),")\nclient TLS connections. They are different from those used by ",(0,s.jsx)(n.a,{href:"./configure#configuration-encryption",children:"configuration value encryption"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["When overriding cipher suites, it is highly recommended\nthat server-preferred ",(0,s.jsx)(n.a,{href:"#cipher-suite-order",children:"cipher suite ordering is enforced"}),"."]}),"\n",(0,s.jsx)(n.h3,{id:"configuring-cipher-suites",children:"Configuring Cipher Suites"}),"\n",(0,s.jsxs)(n.p,{children:["Cipher suites are configured using the ",(0,s.jsx)(n.code,{children:"ssl_options.ciphers"})," config option (",(0,s.jsx)(n.code,{children:"rabbit.ssl_options.ciphers"}),"\nin the classic config format)."]}),"\n",(0,s.jsx)(n.p,{children:"The below example demonstrates how the option is used."}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"listeners.ssl.1 = 5671\n\nssl_options.cacertfile = /path/to/ca_certificate.pem\nssl_options.certfile = /path/to/server_certificate.pem\nssl_options.keyfile = /path/to/server_key.pem\nssl_options.versions.1 = tlsv1.2\n\nssl_options.verify = verify_peer\nssl_options.fail_if_no_peer_cert = false\n\nssl_options.ciphers.1 = ECDHE-ECDSA-AES256-GCM-SHA384\nssl_options.ciphers.2 = ECDHE-RSA-AES256-GCM-SHA384\nssl_options.ciphers.3 = ECDH-ECDSA-AES256-GCM-SHA384\nssl_options.ciphers.4 = ECDH-RSA-AES256-GCM-SHA384\nssl_options.ciphers.5 = DHE-RSA-AES256-GCM-SHA384\nssl_options.ciphers.6 = DHE-DSS-AES256-GCM-SHA384\nssl_options.ciphers.7 = ECDHE-ECDSA-AES128-GCM-SHA256\nssl_options.ciphers.8 = ECDHE-RSA-AES128-GCM-SHA256\nssl_options.ciphers.9 = ECDH-ECDSA-AES128-GCM-SHA256\nssl_options.ciphers.10 = ECDH-RSA-AES128-GCM-SHA256\nssl_options.ciphers.11 = DHE-RSA-AES128-GCM-SHA256\nssl_options.ciphers.12 = DHE-DSS-AES128-GCM-SHA256\n\n# these MUST be disabled if TLSv1.3 is used\nssl_options.honor_cipher_order = true\nssl_options.honor_ecc_order = true\n"})}),"\n",(0,s.jsxs)(n.p,{children:["In the ",(0,s.jsx)(n.a,{href:"./configure#erlang-term-config-file",children:"classic config format"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-erlang",children:'%% list allowed ciphers\n[\n {ssl, [{versions, [\'tlsv1.2\', \'tlsv1.1\']}]},\n {rabbit, [\n {ssl_listeners, [5671]},\n {ssl_options, [{cacertfile,"/path/to/ca_certificate.pem"},\n {certfile, "/path/to/server_certificate.pem"},\n {keyfile, "/path/to/server_key.pem"},\n {versions, [\'tlsv1.2\', \'tlsv1.1\']},\n %% This list is just an example!\n %% N
1ot all cipher suites are available on all machines.\n %% Cipher suite order is important: preferred suites\n %% should be listed first.\n %% Different suites have different security and CPU load characteristics.\n {ciphers, [\n "ECDHE-ECDSA-AES256-GCM-SHA384",\n "ECDHE-RSA-AES256-GCM-SHA384",\n "ECDH-ECDSA-AES256-GCM-SHA384",\n "ECDH-RSA-AES256-GCM-SHA384",\n "DHE-RSA-AES256-GCM-SHA384",\n "DHE-DSS-AES256-GCM-SHA384",\n "ECDHE-ECDSA-AES128-GCM-SHA256",\n "ECDHE-RSA-AES128-GCM-SHA256",\n "ECDH-ECDSA-AES128-GCM-SHA256",\n "ECDH-RSA-AES128-GCM-SHA256",\n "DHE-RSA-AES128-GCM-SHA256",\n "DHE-DSS-AES128-GCM-SHA256"\n ]}\n ]}\n ]}\n].\n'})}),"\n",(0,s.jsx)(n.h3,{id:"cipher-suite-order",children:"Cipher Suite Order"}),"\n",(0,s.jsxs)(n.p,{children:["During TLS connection negotiation, the server and the client negotiate\nwhat cipher suite will be used. It is possible to force server's TLS\nimplementation to dictate its preference (cipher suite order) to avoid\nmalicious clients that intentionally negotiate weak cipher suites in\npreparation for running an attack on them.\nTo do so, configure ",(0,s.jsx)(n.code,{children:"honor_cipher_order"}),"\nand ",(0,s.jsx)(n.code,{children:"honor_ecc_order"})," to ",(0,s.jsx)(n.code,{children:"true"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"listeners.ssl.1 = 5671\nssl_options.cacertfile = /path/to/ca_certificate.pem\nssl_options.certfile = /path/to/server_certificate.pem\nssl_options.keyfile = /path/to/server_key.pem\nssl_options.versions.1 = tlsv1.2\n\nssl_options.honor_cipher_order = true\nssl_options.honor_ecc_order = true\n"})}),"\n",(0,s.jsx)(n.p,{children:"Or, in the classic config format:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-erlang",children:"%% Enforce server-provided cipher suite order (preference)\n[\n {ssl, [{versions, ['tlsv1.2', 'tlsv1.1']}]},\n {rabbit, [\n {ssl_listeners, [5671]},\n {ssl_options, [{cacertfile, \"/path/to/ca_certificate.pem\"},\n {certfile, \"/path/to/server_certificate.pem\"},\n {keyfile, \"/path/to/server_key.pem\"},\n {versions, ['tlsv1.2', 'tlsv1.1']},\n\n %% ...\n\n\n {honor_cipher_order, true},\n {honor_ecc_order, true},\n ]}\n ]}\n].\n"})}),"\n",(0,s.jsx)(n.h3,{id:"recommended-cipher-suites-tls-13",children:"Recommended Cipher Suites (TLS 1.3)"}),"\n",(0,s.jsxs)(n.p,{children:["See the section on ",(0,s.jsx)(n.a,{href:"#tls1.3",children:"TLS 1.3"})," and ",(0,s.jsx)(n.a,{href:"#tls-evaluation-tools",children:"Evaluating TLS Setup Security"}),"."]}),"\n",(0,s.jsx)(n.h3,{id:"recommended-cipher-suites-tls-12",children:"Recommended Cipher Suites (TLS 1.2)"}),"\n",(0,s.jsxs)(n.p,{children:["See ",(0,s.jsx)(n.a,{href:"https://wiki.mozilla.org/Security/Server_Side_TLS",children:"Server-side TLS Security"})," recommendations from Mozilla\nand ",(0,s.jsx)(n.a,{href:"#tls-evaluation-tools",children:"Evaluating TLS Setup Security"}),"."]}),"\n",(0,s.jsx)(n.h2,{id:"major-vulnerabilities",children:"Known TLS Vulnerabilities and Their Mitigation"}),"\n",(0,s.jsx)(n.h3,{id:"robot",children:"ROBOT"}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"https://robotattack.org/",children:"ROBOT attack"})," affects RabbitMQ installations that rely on RSA\ncipher suites and run on Erlang/OTP versions prior to\n19.3.6.4 and 20.1.7. To mitigate, ",(0,s.jsx)(n.a,{href:"./which-erlang",children:"upgrade Erlang/OTP"})," to a patched version\nand consider ",(0,s.jsx)(n.a,{href:"#cipher-suites",children:"limiting the list of supported cipher suites"}),"."]}),"\n",(0,s.jsx)(n.h3,{id:"poodle",children:"POODLE"}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"https://templatelab.com/ssl-poodle/",children:"POODLE"})," is a known SSL/TLS attack that originally compromised SSLv3.\nStarting with version 3.4.0, RabbitMQ server refuses to accept SSLv3 connections. In December 2014, a modified version of\nthe POODLE attack that affects TLSv1.0 was ",(0,s.jsx)(n.a,{href:"https://www.imperialviolet.org/2014/12/08/poodleagain.html",children:"announced"}),".\nIt is therefore recommended to either run Erlang 18.0 or later, which\n",(0,s.jsx)(n.a,{href:"http://www.erlang.org/news/88",children:"eliminates TLS 1.0 implementation vulnerability to POODLE"}),",\nor ",(0,s.jsx)(n.a,{href:"#tls-versions",children:"disable TLSv1.0 support"}),"."]}),"\n",(0,s.jsx)(n.h3,{id:"beast",children:"BEAST"}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"http://en.wikipedia.org/wiki/Transport_Layer_Security#BEAST_attack",children:"BEAST attack"})," is a known vulnerability that\naffects TLSv1.0. To mitigate it, ",(0,s.jsx)(n.a,{href:"#tls-versions",children:"disable TLSv1.0 support"}),"."]}),"\n",(0,s.jsx)(n.h2,{id:"tls-evaluation-tools",children:"Evaluating TLS Setup Security"}),"\n",(0,s.jsx)(n.p,{children:"Because TLS has many configurable parameters\nand some of them have suboptimal defaults for historical\nreasons, TLS setup security evaluation is a recommended practice.\nMultiple tools exist that perform various tests on TLS-enabled\nserver endpoints, for example, testing whether it is prone\nto known attacks such as POODLE, BEAST, and others."}),"\n",(0,s.jsx)(n.h3,{id:"testssl-sh",children:"testssl.sh"}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"https://testssl.sh/",children:"testssl.sh"})," is a mature and extensive TLS endpoint testing\ntool. It can be used with protocol endpoints that do not serve HTTPS."]}),"\n",(0,s.jsx)(n.p,{children:"The tool performs many tests (for instance, on some machines it runs\nover 350 cipher suite tests alone) and passing every single one may or may not\nmake sense for every environment. For example, many production deployments\ndo not use CRLs (Certificate Revocation Lists); most development environments\nuse self-signed certificates and don't have to worry about\nthe most optimal set of cipher suites enabled; and so on."}),"\n",(0,s.jsxs)(n.p,{children:["To run ",(0,s.jsx)(n.code,{children:"testssl.sh"}),", provide an endpoint to test in the form of ",(0,s.jsx)(n.code,{children:"{hostname}:5671"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"./testssl.sh localhost:5671\n"})}),"\n",(0,s.jsx)(n.h3,{id:"evaluation-of-a-tls-13-setup",children:"Evaluation of a TLS 1.3 Setup"}),"\n",(0,s.jsxs)(n.p,{children:["The following example configuration that accepts TLSv1.3 connections passes key\n",(0,s.jsx)(n.code,{children:"testssl.sh"})," tests on Erlang 26:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"listeners.ssl.1 = 5671\n\nssl_options.cacertfile = /path/to/ca_certificate.pem\nssl_options.certfile = /path/to/server_certificate.pem\nssl_options.keyfile = /path/to/server_key.pem\n\nssl_options.versions.1 = tlsv1.3\n\nssl_options.verify = verify_peer\nssl_options.fail_if_no_peer_cert = true\n\nssl_options.ciphers.1 = TLS_AES_256_GCM_SHA384\nssl_options.ciphers.2 = TLS_AES_128_GCM_SHA256\nssl_options.ciphers.3 = TLS_CHACHA20_POLY1305_SHA256\nssl_options.ciphers.4 = TLS_AES_128_CCM_SHA256\nssl_options.ciphers.5 = TLS_AES_128_CCM_8_SHA256\n\nssl_options.honor_cipher_order = true\nssl_options.honor_ecc_order = true\n"})}),"\n",(0,s.jsx)(n.p,{children:"This TLSv1.3-exclusive setup is reported as not vulnerable:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:' Using "OpenSSL 3.3.1 4 Jun 2024 (Library: OpenSSL 3.3.1 4 Jun 2024)" [~94 ciphers]\n on [redacted]:/opt/homebrew/bin/openssl\n (built: "Jun 4 12:53:04 2024", platform: "darwin64-arm64-cc")\n\n\n
1Start 2024-08-08 11:56:02 --\x3e> 127.0.0.1:5671 (localhost) <<--\n\n A record via: /etc/hosts\n rDNS (127.0.0.1): localhost.\n Service detected: Couldn\'t determine what\'s running on port 5671, assuming no HTTP service => skipping all HTTP checks\n\n\n Testing protocols via sockets except NPN+ALPN\n\n SSLv2 not offered (OK)\n SSLv3 not offered (OK)\n TLS 1 not offered\n TLS 1.1 not offered\n TLS 1.2 not offered\n TLS 1.3 offered (OK): final\n NPN/SPDY not offered\n ALPN/HTTP2 not offered\n\n Testing cipher categories\n\n NULL ciphers (no encryption) not offered (OK)\n Anonymous NULL Ciphers (no authentication) not offered (OK)\n Export ciphers (w/o ADH+NULL) not offered (OK)\n LOW: 64 Bit + DES, RC[2,4], MD5 (w/o export) not offered (OK)\n Triple DES Ciphers / IDEA not offered\n Obsoleted CBC ciphers (AES, ARIA etc.) not offered\n Strong encryption (AEAD ciphers) with no FS not offered\n Forward Secrecy strong encryption (AEAD ciphers) offered (OK)\n\n\n Testing server\'s cipher preferences\n\nHexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Cipher Suite Name (IANA/RFC)\n-----------------------------------------------------------------------------------------------------------------------------\nSSLv2\n -\nSSLv3\n -\nTLSv1\n -\nTLSv1.1\n -\nTLSv1.2\n -\nTLSv1.3 (listed by strength)\n x1302 TLS_AES_256_GCM_SHA384 ECDH 253 AESGCM 256 TLS_AES_256_GCM_SHA384\n x1303 TLS_CHACHA20_POLY1305_SHA256 ECDH 253 ChaCha20 256 TLS_CHACHA20_POLY1305_SHA256\n x1301 TLS_AES_128_GCM_SHA256 ECDH 253 AESGCM 128 TLS_AES_128_GCM_SHA256\n x1304 TLS_AES_128_CCM_SHA256 ECDH 253 AESCCM 128 TLS_AES_128_CCM_SHA256\n x1305 TLS_AES_128_CCM_8_SHA256 ECDH 253 AESCCM8 128 TLS_AES_128_CCM_8_SHA256\n\n Has server cipher order? no (TLS 1.3 only)\n (limited sense as client will pick)\n\n Testing robust forward secrecy (FS) -- omitting Null Authentication/Encryption, 3DES, RC4\n\n FS is offered (OK) TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 TLS_AES_128_GCM_SHA256 TLS_AES_128_CCM_SHA256 TLS_AES_128_CCM_8_SHA256\n Elliptic curves offered: prime256v1 secp384r1 X25519 X448\n TLS 1.3 sig_algs offered: RSA-PSS-RSAE+SHA256 RSA-PSS-RSAE+SHA384 RSA-PSS-RSAE+SHA512\n\n Testing server defaults (Server Hello)\n\n TLS extensions (standard) "key share/#51" "supported versions/#43" "signature algorithms/#13" "certificate authorities/#47"\n Session Ticket RFC 5077 hint no -- no lifetime advertised\n SSL Session ID support no\n Session Resumption Tickets no, ID: no\n TLS clock skew Random values, no fingerprinting possible\n Certificate Compression none\n Client Authentication optional\n CA List for Client Auth L=$$$$,CN=TLSGenSelfSignedtRootCA 2022-03-22T11:27:45.010198\n Signature Algorithm SHA256 with RSA\n Server key size RSA 2048 bits (exponent is 65537)\n Server key usage Digital Signature, Key Encipherment\n Server extended key usage TLS Web Server Authentication\n Serial 01 (OK: length 1)\n Fingerprints SHA1 A4346FA6FDC61FCD4C0199EA14B8AE0F5D5121B1\n SHA256 C81025DA6F9BB646239659420D58E73F62CEB7D2AD5AC13FF12A9DE057394953\n Common Name (CN) [redacted]\n subjectAltName (SAN) [redacted] localhost\n Trust (hostname) Ok via SAN (same w/o SNI)\n Chain of trust NOT ok (self signed CA in chain)\n EV cert (experimental) no\n Certificate Validity (UTC) 2779 >= 60 days (2022-03-22 07:27 --\x3e 2032-03-19 07:27)\n >= 10 years is way too long\n ETS/"eTLS", visibility info not present\n Certificate Revocation List --\n OCSP URI --\n NOT ok -- neither CRL nor OCSP URI provided\n OCSP stapling not offered\n OCSP must staple extension --\n DNS CAA RR (experimental) not offered\n Certificate Transparency N/A\n Certificates provided 2\n Issuer TLSGenSelfSignedtRootCA 2022-03-22T11:27:45.010198\n Intermediate cert validity #1: ok > 40 days (2032-03-19 07:27). $$$$ <-- $$$$\n Intermediate Bad OCSP (exp.) Ok\n\n\n Testing vulnerabilities\n\n Heartbleed (CVE-2014-0160) not vulnerable (OK), no heartbeat extension\n CCS (CVE-2014-0224) not vulnerable (OK)\n Ticketbleed (CVE-2016-9244), experiment. (applicable only for HTTPS)\n ROBOT Server does not support any cipher suites that use RSA key transport\n Secure Renegotiation (RFC 5746) not vulnerable (OK)\n Secure Client-Initiated Renegotiation not vulnerable (OK)\n CRIME, TLS (CVE-2012-4929) not vulnerable (OK)\n POODLE, SSL (CVE-2014-3566) not vulnerable (OK), no SSLv3 support\n TLS_FALLBACK_SCSV (RFC 7507) No fallback possible (OK), TLS 1.3 is the only protocol\n SWEET32 (CVE-2016-2183, CVE-2016-6329) not vulnerable (OK)\n FREAK (CVE-2015-0204) not vulnerable (OK)\n DROWN (CVE-2016-0800, CVE-2016-0703) not vulnerable on this host and port (OK)\n make sure you don\'t use this certificate elsewhere with SSLv2 enabled services, see\n https://search.censys.io/search?resource=hosts&virtual_hosts=INCLUDE&q=C81025DA6F9BB646239659420D58E73F62CEB7D2AD5AC13FF12A9DE057394953\n LOGJAM (CVE-2015-4000), experimental not vulnerable (OK): no DH EXPORT ciphers, no DH key detected with <= TLS 1.2\n BEAST (CVE-2011-3389) not vulnerable (OK), no SSL3 or TLS1\n LUCKY13 (CVE-2013-0169), experimental not vulnerable (OK)\n Winshock (CVE-2014-6321), experimental not vulnerable (OK)\n RC4 (CVE-2013-2566, CVE-2015-2808) not vulnerable (OK)\n\nCould not determine the protocol, only simulating generic clients.\n\n Running client simulations via sockets\n\n Browser Protocol Cipher Suite Name (OpenSSL) Forward Secrecy\n------------------------------------------------------------------------------------------------\n Android 8.1 (native) No connection\n Android 9.0 (native) TLSv1.3 TLS_AES_128_GCM_SHA256 253 bit ECDH (X25519)\n Android 10.0 (native) TLSv1.3 TLS_AES_128_GCM_SHA256 253 bit ECDH (X25519)\n Android 11 (native) TLSv1.3 TLS_AES_128_GCM_SHA256 253 bit ECDH (X25519)\n Android 12 (native) TLSv1.3 TLS_AES_128_GCM_SHA256 253 bit ECDH (X25519)\n Java 7u25 No connection\n Java 8u161 No connection\n Java 11.0.2 (OpenJDK) TLSv1.3 TLS_AES_128_GCM_SHA256 256 bit ECDH (P-256)\n Java 17.0.3 (OpenJDK) TLSv1.3 TLS_AES_256_GCM_SHA384 253 bit ECDH (X25519)\n go 1.17.8 TLSv1.3 TLS_AES_128_GCM_SHA256 253 bit ECDH (X25519)\n LibreSSL 2.8.3 (Apple) No connection\n OpenSSL 1.0.2e No connection\n OpenSSL 1.1.0l (Debian) No connection\n OpenSSL 1.1.1d (Debian) TLSv1.3 TLS_AES_256_GCM_SHA384 253 bit ECDH (X25519)\n OpenSSL 3.0.3 (git) TLSv1.3 TLS_AES_256_GCM_SHA384 253 bit ECDH (X25519)\n'})}),"\n",(0,s.jsx)(n.h3,{id:"evaluation-of-a-tls-12-setup-with-restricted-cipher-suites",children:"Evaluation of a TLS 1.2 Setup with Restricted Cipher Suites"}),"\n",(0,s.jsxs)(n.p,{children:["The following example configuration that accepts TLSv1.2
1connections passes key\n",(0,s.jsx)(n.code,{children:"testssl.sh"})," tests on Erlang 26.2:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"listeners.ssl.default = 5671\nssl_options.cacertfile = /path/to/ca_certificate.pem\nssl_options.certfile = /path/to/server_certificate.pem\nssl_options.keyfile = /path/to/server_key.pem\nssl_options.versions.1 = tlsv1.2\n\nssl_options.verify = verify_peer\nssl_options.fail_if_no_peer_cert = false\n\nssl_options.honor_cipher_order = true\nssl_options.honor_ecc_order = true\n\n# These are highly recommended for TLSv1.2 but cannot be used\n# with TLSv1.3. If TLSv1.3 is enabled, these lines MUST be removed.\nssl_options.client_renegotiation = false\nssl_options.secure_renegotiate = true\n\nssl_options.ciphers.1 = ECDHE-ECDSA-AES256-GCM-SHA384\nssl_options.ciphers.2 = ECDHE-RSA-AES256-GCM-SHA384\nssl_options.ciphers.3 = ECDH-ECDSA-AES256-GCM-SHA384\nssl_options.ciphers.4 = ECDH-RSA-AES256-GCM-SHA384\nssl_options.ciphers.5 = DHE-RSA-AES256-GCM-SHA384\nssl_options.ciphers.6 = DHE-DSS-AES256-GCM-SHA384\nssl_options.ciphers.7 = ECDHE-ECDSA-AES128-GCM-SHA256\nssl_options.ciphers.8 = ECDHE-RSA-AES128-GCM-SHA256\nssl_options.ciphers.9 = ECDH-ECDSA-AES128-GCM-SHA256\nssl_options.ciphers.10 = ECDH-RSA-AES128-GCM-SHA256\nssl_options.ciphers.11 = DHE-RSA-AES128-GCM-SHA256\nssl_options.ciphers.12 = DHE-DSS-AES128-GCM-SHA256\n"})}),"\n",(0,s.jsx)(n.p,{children:"This TLSv1.2-enabled setup is reported as not vulnerable to a set of known\nhigh profile vulnerabilities:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:' Using "OpenSSL 3.3.1 4 Jun 2024 (Library: OpenSSL 3.3.1 4 Jun 2024)" [~94 ciphers]\n on [redacted]:/opt/homebrew/bin/openssl\n (built: "Jun 4 12:53:04 2024", platform: "darwin64-arm64-cc")\n\n\n Start 2024-08-08 13:42:36 --\x3e> 127.0.0.1:5671 (localhost) <<--\n\n A record via: /etc/hosts\n rDNS (127.0.0.1): localhost.\n Service detected: certificate-based authentication without providing client certificate and private key => skipping all HTTP checks\n\n\n Testing protocols via sockets except NPN+ALPN\n\n SSLv2 not offered (OK)\n SSLv3 not offered (OK)\n TLS 1 not offered\n TLS 1.1 not offered\n TLS 1.2 offered (OK)\n TLS 1.3 not offered and downgraded to a weaker protocol\n NPN/SPDY not offered\n ALPN/HTTP2 not offered\n\n Testing cipher categories\n\n NULL ciphers (no encryption) not offered (OK)\n Anonymous NULL Ciphers (no authentication) not offered (OK)\n Export ciphers (w/o ADH+NULL) not offered (OK)\n LOW: 64 Bit + DES, RC[2,4], MD5 (w/o export) not offered (OK)\n Triple DES Ciphers / IDEA not offered\n Obsoleted CBC ciphers (AES, ARIA etc.) not offered\n Strong encryption (AEAD ciphers) with no FS not offered\n Forward Secrecy strong encryption (AEAD ciphers) offered (OK)\n\n\n Testing server\'s cipher preferences\n\nHexcode Cipher Suite Name (OpenSSL) KeyExch. Encryption Bits Cipher Suite Name (IANA/RFC)\n-----------------------------------------------------------------------------------------------------------------------------\nSSLv2\n -\nSSLv3\n -\nTLSv1\n -\nTLSv1.1\n -\nTLSv1.2 (server order)\n xc030 ECDHE-RSA-AES256-GCM-SHA384 ECDH 253 AESGCM 256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384\n x9f DHE-RSA-AES256-GCM-SHA384 DH 2048 AESGCM 256 TLS_DHE_RSA_WITH_AES_256_GCM_SHA384\n xc02f ECDHE-RSA-AES128-GCM-SHA256 ECDH 253 AESGCM 128 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256\n x9e DHE-RSA-AES128-GCM-SHA256 DH 2048 AESGCM 128 TLS_DHE_RSA_WITH_AES_128_GCM_SHA256\nTLSv1.3\n -\n\n Has server cipher order? yes (OK)\n\n\n Testing robust forward secrecy (FS) -- omitting Null Authentication/Encryption, 3DES, RC4\n\n FS is offered (OK) ECDHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES128-GCM-SHA256\n Elliptic curves offered: prime256v1 secp384r1 secp521r1 brainpoolP256r1 brainpoolP384r1 brainpoolP512r1 X25519 X448\n DH group offered: RFC3526/Oakley Group 14 (2048 bits)\n TLS 1.2 sig_algs offered: RSA+SHA256 RSA+SHA384 RSA+SHA512 RSA-PSS-RSAE+SHA256\n\n Testing server defaults (Server Hello)\n\n TLS extensions (standard) "renegotiation info/#65281" "EC point formats/#11" "max fragment length/#1"\n Session Ticket RFC 5077 hint no -- no lifetime advertised\n SSL Session ID support yes\n Session Resumption Tickets no, Client Auth: ID resumption test not supported\n TLS clock skew -1 sec from localtime\n Client Authentication required\n CA List for Client Auth L=$$$$,CN=TLSGenSelfSignedtRootCA 2022-03-22T11:27:45.010198\n Signature Algorithm SHA256 with RSA\n Server key size RSA 2048 bits (exponent is 65537)\n Server key usage Digital Signature, Key Encipherment\n Server extended key usage TLS Web Server Authentication\n Serial 01 (OK: length 1)\n Fingerprints SHA1 A4346FA6FDC61FCD4C0199EA14B8AE0F5D5121B1\n SHA256 C81025DA6F9BB646239659420D58E73F62CEB7D2AD5AC13FF12A9DE057394953\n Common Name (CN) [redacted]\n subjectAltName (SAN) [redacted] localhost\n Trust (hostname) Ok via SAN (same w/o SNI)\n Chain of trust NOT ok (self signed CA in chain)\n EV cert (experimental) no\n Certificate Validity (UTC) 2779 >= 60 days (2022-03-22 07:27 --\x3e 2032-03-19 07:27)\n >= 10 years is way too long\n ETS/"eTLS", visibility info not present\n Certificate Revocation List --\n OCSP URI --\n NOT ok -- neither CRL nor OCSP URI provided\n OCSP stapling not offered\n OCSP must staple extension --\n DNS CAA RR (experimental) not offered\n Certificate Transparency --\n Certificates provided 2\n Issuer TLSGenSelfSignedtRootCA 2022-03-22T11:27:45.010198\n Intermediate cert validity #1: ok > 40 days (2032-03-19 07:27). $$$$ <-- $$$$\n Intermediate Bad OCSP (exp.) Ok\n\n\n Testing vulnerabilities\n\n Heartbleed (CVE-2014-0160) not vulnerable (OK), no heartbeat extension\n CCS (CVE-2014-0224) not vulnerable (OK)\n Ticketbleed (CVE-2016-9244), experiment. not vulnerable (OK), no session ticket extension\n ROBOT Server does not support any cipher suites that use RSA key transport\n Secure Renegotiation (RFC 5746) supported (OK)\n Secure Client-Initiated Renegotiation not having provided client certificate and private key file, the client x509-based authentication prevents this from being tested\n CRIME, TLS (CVE-2012-4929) not vulnerable (OK)\n BREACH (CVE-2013-3587) not having provided client certificate and private key file, the client x509-based authentication prevents this from being tested\n POODLE, SSL (CVE-2014-3566) not vulnerable (OK), no SSLv3 support\n TLS_FALLBACK_SCSV (RFC 7507) No fallback possible (OK), no protocol below TLS 1.2 offered\n SWEET32 (CVE-2016-2183, CVE-2016-6329) not vulnerable (OK)\n FREAK (CVE-2015-0204) not vulnerable (OK)\n DROWN (CVE-2016-0800, CVE-2016-0703) not vulnerable on this host and port (OK)\n make sure you don\'t use this certificate elsewhere with SSLv2 enabled services, see\n https://search.censys.io/search?resource=hosts&virtual_hosts=INCLUDE&q=C81025DA6F9BB646239659420D58E73F62CEB7D2AD5AC13FF12A9DE057394953\n LOGJAM (CVE-2015-4000), experimental common prime with 2048 bits detected: RFC3526/Oakley Group 14 (2048 bits),\n but no DH EXPORT ciphers\n BEAST (CVE-2011-3389) not vulnerable (OK), no SSL3 or TLS1\n LUCKY13 (CVE-2013-0169), experimental not vulnerable (OK)\n Winshock (CVE-2014-6321), experimental not vulnerable (OK) - CAMELLIA or ECDHE_RSA GCM ciphers found\n RC4 (CVE-2013-2566, CVE-2015-2808) no RC4 ciphers detected (OK)\n\nCould not determine the protocol, only simulating generic clients.\n\n Running client simulations via sockets\n\n Browser Protocol Cipher Suite Name (OpenSSL) Forward Secrecy\n------------------------------------------------------------------------------------------------\n Android 8.1 (native) TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 253 bit ECDH (X25519)\n Android 9.0 (native) TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 253 bit ECDH (X25519)\n Android 10.0 (native) TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 253 bit ECDH (X25519)\n Android 11 (native) TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 253 bit ECDH (X25519)\n Android 12 (native) TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 253 bit ECDH (X25519)\n Java 7u25 No connection\n Java 8u161 TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 521 bit ECDH (P-521)\n Java 11.0.2 (OpenJDK) TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 521 bit ECDH (P-521)\n Java 17.0.3 (OpenJDK) TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 253 bit ECDH (X25519)\n go 1.17.8 TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 253 bit ECDH (X25519)\n LibreSSL 2.8.3 (Apple) TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 253 bit ECDH (X25519)\n OpenSSL 1.0.2e TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 521 bit ECDH (P-521)\n OpenSSL 1.1.0l (Debian) TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 253 bit ECDH (X25519)\n OpenSSL 1.1.1d (Debian) TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 253 bit ECDH (X25519)\n OpenSSL 3.0.3 (git) TLSv1.2 ECDHE-RSA-AES256-GCM-SHA384 253 bit ECDH (X25519)\n'})}),"\n",(0,s.jsx)(n.h2,{id:"rotation",children:"TLS Certificate and Private Key Rotation"}),"\n",(0,s.jsx)(n.p,{children:"Server TLS certificates (public keys) and private keys have expiration dates and will need to be replaced\n(rotated) every so often."}),"\n",(0,s.jsx)(n.p,{children:"The replacement process involves the following steps:"}),"\n",(0,s.jsxs)(n.ol,{children:["\n",(0,s.jsx)(n.li,{children:"Replace the files on disk"}),"\n",(0,s.jsx)(n.li,{children:"Clear the certificate and private key store cache on the node"}),"\n"]}),"\n",(0,s.jsx)(n.p,{children:"Without the second step, the new certificate/key pair will be used by the node after a period of time,\nas the TLS implementation in the runtimes purges its certificate store cache."}),"\n",(0,s.jsx)(n.h3,{id:"replacing-certificate-and-private-key-files-on-disk",children:"Replacing Certificate and Private Key Files on Disk"}),"\n",(0,s.jsxs)(n.p,{children:["Simply replace the server certificate, server private key and (if needed) the ",(0,s.jsx)(n.a,{href:"#peer-verification",children:"certificate authority"}),"\nbundle files with their new versions."]}),"\n",(0,s.jsx)(n.h3,{id:"clearing-the-certificate-and-private-key-store-cache",children:"Clearing the Certificate and Private Key Store Cache"}),"\n",(0,s.jsxs)(o.A,{groupId:"examples",children:[(0,s.jsx)(c.A,{value:"bash",label:"bash",default:!0,children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"rabbitmqctl eval -n [target-node@hostname] 'ssl:clear_pem_cache().'\n"})})}),(0,s.jsx)(c.A,{value:"PowerShell",label:"PowerShell",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-PowerShell",children:"rabbitmqctl.bat eval -n [target-node@hostname] 'ssl:clear_pem_cache().'\n"})})}),(0,s.jsx)(c.A,{value:"cmd",label:"cmd",children:(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-batch",children:'rabbitmqctl.bat eval -n [target-node@hostname] "ssl:clear_pem_cache()."\n'})})})]}),"\n",(0,s.jsx)(n.h2,{id:"trust-store",children:"The Trust Store Plugin"}),"\n",(0,s.jsxs)(n.p,{children:["In some environments, there's a set of leaf (client) ",(0,s.jsx)(n.a,{href:"#peer-verification",children:"trusted certificates"})," that must be trusted\nand that's it, the chain is not traversed and the traditional trust roots (CAs) are not used."]}),"\n",(0,s.jsxs)(n.p,{children:["This approach is often used in environments where client certificates are also used ",(0,s.jsx)(n.a,{href:"#trust-store-x509-auth",children:"as identities"}),",\nmust be unique, and churn (change: some are added, others removed) often."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.a,{href:"https://github.com/rabbitmq/rabbitmq-server/tree/main/deps/rabbitmq_trust_store",children:(0,s.jsx)(n.code,{children:"rabbitmq_trust_store"})})," is a plugin that\noverrides the peer verification algorithm to do just that: verify that the connected client's certificate\nbelongs to a set (a whitelist)."]}),"\n",(0,s.jsx)(n.p,{children:"The whitelist comes from either a local filesystem directory or an HTTP API endpoint that follows\na small set of conventions. The list is refreshed at a configurable interval,\nallowing certificates to be added or removed without restarting the node\nor affecting existing connections."}),"\n",(0,s.jsx)(n.h3,{id:"trust-store-providers",children:"Certificate Providers"}),"\n",(0,s.jsx)(n.p,{children:"The trust store supports two sources (providers) of trusted leaf client certificates:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"Filesystem provider"})," (default): loads certificates from a local directory"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.strong,{children:"HTTPS provider"}),": retrieves certificates from a remote HTTPS endpoint"]}),"\n"]}),"\n",(0,s.jsx)(n.h4,{id:"trust-store-filesystem",children:"Filesystem Provider"}),"\n",(0,s.jsx)(n.p,{children:"To use the filesystem provider, configure a directory that contains the trusted client certificates\nin the PEM format:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"# will monitor `/path/to/trust-store/whitelist` for files additions and deletion\ntrust_store.directory = /path/to/trust-store/whitelist\n# scan the above directory every 30 seconds\ntrust_store.refresh_interval = 30\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Setting ",(0,s.jsx)(n.code,{children:"refresh_interval"})," to ",(0,s.jsx)(n.code,{children:"0"})," will disable automatic reloading."]}),"\n",(0,s.jsx)(n.h4,{id:"trust-store-http",children:"HTTP Provider"}),"\n",(0,s.jsx)(n.p,{children:"To retrieve certificates from a remote HTTPS endpoint that follows\ncertain convention:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"trust_store.providers.1 = http\ntrust_store.url = https://certs.example.com/trusted\ntrust_store.refresh_interval = 30\n"})}),"\n",(0,s.jsx)(n.h4,{id:"provider-http-api",children:"Provider HTTP API"}),"\n",(0,s.jsx)(n.p,{children:"The remote server must expose an HTTP API that lists certificates\nand allows for fetching individual certificates."}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"GET <url>"})," must respond with a ",(0,s.jsx)(n.code,{children:"200 OK"})," and the following body:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-json",children:'{\n "certificates": [\n {"id": "certificate-1", "path": "/certificates/1.pem"},\n {"id": "certificate-2", "path": "/certificates/2.pem"}\n // , ...\n ]\n}\n'})}),"\n",(0,s.jsx)(n.p,{children:"Certificate IDs must be valid filenames and be unique."}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"GET <url>/<relative-url>"}),", such as ",(0,s.jsx)(n.code,{children:'"/certificates/1.pem"'})," in the example above,\nmust respond with a ",(0,s.jsx)(n.code,{children:"200 OK"})," and return a PEM-encoded certificate in its body."]}),"\n",(0,s.jsxs)(n.p,{children:["The provider uses the ",(0,s.jsx)(n.code,{children:"If-Modified-Since"})," header to\navoid unnecessary re-downloads."]}),"\n",(0,s.jsx)(n.p,{children:"If the HTTPS endpoint itself authenticates clients using a x.509 certificate,\nconfigure
1 its CA certificate bundle, public and private keys:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"trust_store.ssl_options.certfile = /path/to/client/client_certificate.pem\ntrust_store.ssl_options.keyfile = /path/to/client/client_key.pem\ntrust_store.ssl_options.cacertfile = /path/to/ca/trusted_ca_bundle.pem\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Finally, a request timeout can be set with ",(0,s.jsx)(n.code,{children:"trust_store.https_request_timeout"}),"."]}),"\n",(0,s.jsx)(n.h3,{id:"trust-store-x509-auth",children:"Using the Trust Store for Client (x509) Certificate Authentication"}),"\n",(0,s.jsxs)(n.p,{children:["The aforementioned Trust Store plugin and the\n",(0,s.jsx)(n.a,{href:"https://github.com/rabbitmq/rabbitmq-server/tree/main/deps/rabbitmq_auth_mechanism_ssl",children:(0,s.jsx)(n.code,{children:"rabbitmq_auth_mechanism_ssl"})})," plugin\nserve two complementary roles:"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"rabbitmq_trust_store"})," handles ",(0,s.jsx)(n.strong,{children:"certificate validation"}),": verifies if the client certificate is on the trusted whitelist"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"rabbitmq_auth_mechanism_ssl"})," handles ",(0,s.jsx)(n.strong,{children:"client authentication"}),": extracts an identity from the certificate and maps it to a RabbitMQ user"]}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:["Combined with the ",(0,s.jsx)(n.a,{href:"./access-control#certificate-authentication",children:"EXTERNAL authentication mechanism"}),",\nthey allow clients to authenticate with just their x.509 certificate."]}),"\n",(0,s.jsx)(n.p,{children:"This setup requires two plugins to be enabled:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"rabbitmq-plugins enable rabbitmq_trust_store\nrabbitmq-plugins enable rabbitmq_auth_mechanism_ssl\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Then configure TLS, the trust store, and the authentication mechanism in ",(0,s.jsx)(n.code,{children:"rabbitmq.conf"}),":"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"# TLS listener\nlisteners.ssl.default = 5671\n\nssl_options.cacertfile = /path/to/ca_certificate.pem\nssl_options.certfile = /path/to/server_certificate.pem\nssl_options.keyfile = /path/to/server_key.pem\nssl_options.verify = verify_peer\nssl_options.fail_if_no_peer_cert = true\n\n# Trust store: directory containing whitelisted client certificates\ntrust_store.directory = /path/to/trust-store/whitelist\ntrust_store.refresh_interval = 30\n\n# Enable the EXTERNAL mechanism so clients can authenticate with a certificate\nauth_mechanisms.1 = EXTERNAL\nauth_mechanisms.2 = PLAIN\n\n# Extract the username from the certificate's Common Name (CN) field.\n# Other supported options: distinguished_name (default), subject_alternative_name\nssl_cert_login_from = common_name\n"})}),"\n",(0,s.jsxs)(n.p,{children:["The ",(0,s.jsx)(n.code,{children:"ssl_cert_login_from"})," setting controls which field of the client certificate is used as the RabbitMQ username:"]}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"distinguished_name"})," (default): uses the full subject DN in RFC 4514 format (for example, ",(0,s.jsx)(n.code,{children:"CN=guest,O=client,L=Paris,ST=France,C=FR"}),")"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"common_name"}),": uses only the CN field (for example, ",(0,s.jsx)(n.code,{children:"guest"}),")"]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"subject_alternative_name"}),": uses a SAN entry, configured with ",(0,s.jsx)(n.code,{children:"ssl_cert_login_san_type"})," (for example, ",(0,s.jsx)(n.code,{children:"dns"}),", ",(0,s.jsx)(n.code,{children:"email"}),") and an ",(0,s.jsx)(n.code,{children:"ssl_cert_login_san_index"}),"\nto pick a specific entry from a list"]}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:["A RabbitMQ user matching the extracted name must exist in the configured\n",(0,s.jsx)(n.a,{href:"./access-control#backends",children:"authentication and authorisation backend(s)"}),"."]}),"\n",(0,s.jsxs)(n.p,{children:["When the ",(0,s.jsx)(n.code,{children:"EXTERNAL"})," mechanism is used, the client-provided password is ignored."]}),"\n",(0,s.jsx)(n.h3,{id:"trust-store-management",children:"Inspecting and Refreshing the Whitelist"}),"\n",(0,s.jsx)(n.p,{children:"To list currently loaded certificates:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"rabbitmq-diagnostics list_trust_store_certificates\n"})}),"\n",(0,s.jsx)(n.p,{children:"To manually trigger a whitelist refresh:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"rabbitmqctl refresh_trust_store\n"})}),"\n",(0,s.jsx)(n.h3,{id:"trust-store-session-caching",children:"TLS Session Caching"}),"\n",(0,s.jsx)(n.p,{children:"TLS session caching bypasses trust store certificate validation. When a client resumes\na cached TLS session, the trust store is not consulted. To ensure that removed certificates\nare immediately rejected, disable TLS session caching:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"ssl_options.reuse_sessions = false\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Please refer to the ",(0,s.jsx)(n.a,{href:"https://github.com/rabbitmq/rabbitmq-server/tree/main/deps/rabbitmq_trust_store",children:"plugin README"})," for\nadditional details and advanced configuration options."]}),"\n",(0,s.jsx)(n.h2,{id:"erlang-client",children:"Using TLS in the Erlang Client"}),"\n",(0,s.jsxs)(n.p,{children:["Enabling TLS in the RabbitMQ Erlang client is similar to configuring other\nsett
1ings related to networking. The ",(0,s.jsx)(n.code,{children:"#amqp_params_network"})," record\nprovides a field, ",(0,s.jsx)(n.code,{children:"ssl_options"}),", for all the ",(0,s.jsx)(n.a,{href:"http://erlang.org/doc/man/./ssl",children:"standard Erlang TLS options"}),"."]}),"\n",(0,s.jsx)(n.h3,{id:"erlang-ssl",children:"Erlang TLS Options"}),"\n",(0,s.jsx)(n.p,{children:"The three important options which must be supplied are:"}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["The ",(0,s.jsx)(n.code,{children:"cacertfile"})," option specifies the certificates of the root\nCertificate Authorities that we wish to implicitly trust."]}),"\n",(0,s.jsxs)(n.li,{children:["The ",(0,s.jsx)(n.code,{children:"certfile"})," is the client's own certificate in PEM format"]}),"\n",(0,s.jsxs)(n.li,{children:["The ",(0,s.jsx)(n.code,{children:"keyfile"})," is the client's private key file in PEM format"]}),"\n"]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"server_name_indication"}),' - set this option to the host name of the server\nto which a TLS connection will be made to enable "Server Name Indication" verification\nof the certificate presented by the server. This ensures that the server certificate\'s\n',(0,s.jsx)(n.code,{children:"CN="})," value will be verified during TLS connection establishment. You can\noverride this behavior by setting ",(0,s.jsx)(n.code,{children:"server_name_indication"})," to a different\nhost name or to the special value ",(0,s.jsx)(n.code,{children:"disable"})," to disable this\nverification. Note that, by default, SNI is ",(0,s.jsx)("b",{children:"not"})," enabled. This default\nwill change in a future RabbitMQ Erlang client release."]}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"verify"})," - set this option to ",(0,s.jsx)(n.code,{children:"verify_peer"})," to enable X509\ncertificate chain verification. The ",(0,s.jsx)(n.code,{children:"depth"})," option configures certificate\nverification depth. Note that, by default, ",(0,s.jsx)(n.code,{children:"verify"})," is set to\n",(0,s.jsx)(n.code,{children:"verify_none"}),", which disables certificate chain verification. This default\nwill change in a future RabbitMQ Erlang client release."]}),"\n",(0,s.jsx)(n.h3,{id:"erlang-code-example",children:"Code Example"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-erlang",children:'SslOpts = [{cacertfile, "/path/to/ca_certificate.pem"},\n {certfile, "/path/to/client/certificate.pem"},\n {keyfile, "/path/to/client/private_key.pem"},\n\n %% only necessary with intermediate CAs\n %% {depth, 2},\n\n %% Note: it is recommended to set \'verify\' to\n %% to \'verify_peer\' to ensure that X509\n %% certificate chain validation is enabled\n %%\n %% Do not set \'verify\' or set it to verify_none\n %% if x509 certificate chain validation is\n %% not desired\n {verify, verify_peer},\n\n %% If Server Name Indication validation is desired,\n %% set the following option to the host name to which\n %% the connection is made. If necessary, this option\n %% may be set to another host name to match the server\n %% certificate\'s CN= value.\n %% Do not set this option or set it to the atom \'disable\'\n %% to disable SNI validation\n {server_name_indication, "my.rmq-server.net"}],\n\nParams = #amqp_params_network{host = "my.rmq-server.net",\n port = 5671,\n ssl_options = SslOpts}\n\n{ok, Conn} = amqp_connection:start(Params),\n'})}),"\n",(0,s.jsx)(n.p,{children:"You can now go ahead and use Conn as a normal connection."}),"\n",(0,s.jsx)(n.h2,{id:"manual-certificate-generation",children:"Manually Generating a CA, Certificates and Private Keys"}),"\n",(0,s.jsxs)(n.p,{children:["This section of the guide explains how to generate a Certificate Authority and\nuse it to generate and sign two certificate/key pairs, one for the server and one for\nclient libraries. Note that the process can be ",(0,s.jsx)(n.a,{href:"#automated-certificate-generation",children:"automated using\nexisting tools"}),", which is recommended. This section is intended for those who would like to improve their understanding\nof the process, OpenSSL command line tools and some important aspects of OpenSSL configuration."]}),"\n",(0,s.jsxs)(n.p,{children:["This guide assumes a UNIX-like operating system (Linux, MacOS, a BSD variant and so on)\nand a recent version of OpenSSL available in ",(0,s.jsx)(n.code,{children:"PATH"}),"."]}),"\n",(0,s.jsx)(n.p,{children:"First let's create a directory for our test Certificate Authority:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"mkdir testca\ncd testca\nmkdir certs private\nchmod 700 private\necho 01 > serial\ntouch index.txt\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Now add the following OpenSSL configuration file, ",(0,s.jsx)(n.code,{children:"openssl.cnf"}),", within the newly created ",(0,s.jsx)(n.code,{children:"testca"}),"\ndirectory:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-ini",children:"[ ca ]\ndefault_ca = testca\n\n[ testca ]\ndir = .\ncertificate = $dir/ca_certificate.pem\ndatabase = $dir/index.txt\nnew_certs_dir = $dir/certs\nprivate_key = $dir/private/ca_private_key.pem\nserial = $dir/serial\n\ndefault_crl_days = 7\ndefault_days = 365\ndefault_md = sha256\n\npolicy = testca_policy\nx509_extensions = certificate_extensions\n\n[ testca_policy ]\ncommonName = supplied\nstateOrProvinceName = optional\ncountryName = optional\nemailAddress = optional\norganizationName = optional\norganizationalUnitName = optional\ndomainComponent = optional\n\n[ certificate_extensions ]\nbasicConstraints = CA:false\n\n[ req ]\ndefault_bits = 2048\ndefault_keyfile = ./private/ca_private_key.pem\ndefault_md = sha256\nprompt = yes\ndistinguished_name = root_ca_distinguished_name\nx509_extensions = root_ca_extensions\n\n[ root_ca_distinguished_name ]\ncommonName = hostname\n\n[ root_ca_extensions ]\nbasicConstraints = CA:true\nkeyUsage = keyCertSign, cRLSign\n\n[ client_ca_extensions ]\nbasicConstraints = CA:false\nkeyUsage = digitalSignature,keyEncipherment\nextendedKeyUsage = 1.3.6.1.5.5.7.3.2\n\n[ server_ca_extensions ]\nbasicConstraints = CA:false\nkeyUsage = digitalSignature,keyEncipherment\nextendedKeyUsage = 1.3.6.1.5.5.7.3.1\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Next we need to generate the key and certificates that our test\nCertificate Authority will use. Still within the ",(0,s.jsx)(n.code,{children:"testca"}),"\ndirectory:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"openssl req -x509 -config openssl.cnf -newkey rsa:2048 -days 365 \\\n -out ca_certificate.pem -outform PEM -subj /CN=MyTestCA/ -nodes\nopenssl x509 -in ca_certificate.pem -out ca_certificate.cer -outform DER\n"})}),"\n",(0,s.jsxs)(n.p,{children:["This is all that is needed to generate a test Certificate\nAuthority. The root certificate is in ",(0,s.jsx)(n.code,{children:"ca_certificate.pem"}),"\nand is also in ",(0,s.jsx)(n.code,{children:"testca/ca_certificate.cer"}),". These two files contain the\nsame information, but in different formats, PEM and DER.\nMost software uses the former but some tools require the latter."]}),"\n",(0,s.jsx)(n.p,{children:"Having set up our Certificate Authority, we now need to generate\nprivate keys and certificates for the clients and the server.\nRabbitMQ broker uses certificates and private keys in the PEM format.\nSome client libraries use the PEM format, others will require conversion\nto a different format (e.g. PKCS#12)."}),"\n",(0,s.jsx)(n.p,{children:"Java and .NET clients use a certificate format called PKCS#12 and custom certificate stores.\
1nCertificate store contains both the client's certificate and key. The PKCS store is usually password protected, and so that\na password must be provided."}),"\n",(0,s.jsx)(n.p,{children:"The process for creating server and client certificates is very\nsimilar. First the server:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cd ..\nls\n# => testca\nmkdir server\ncd server\nopenssl genrsa -out private_key.pem 2048\nopenssl req -new -key private_key.pem -out req.pem -outform PEM \\\n -subj /CN=$(hostname)/O=server/ -nodes\ncd ../testca\nopenssl ca -config openssl.cnf -in ../server/req.pem -out \\\n ../server/server_certificate.pem -notext -batch -extensions server_ca_extensions\ncd ../server\nopenssl pkcs12 -export -out server_certificate.p12 -in server_certificate.pem -inkey private_key.pem \\\n -passout pass:MySecretPassword\n"})}),"\n",(0,s.jsx)(n.p,{children:"And now the client:"}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"cd ..\nls\n# => server testca\nmkdir client\ncd client\nopenssl genrsa -out private_key.pem 2048\nopenssl req -new -key private_key.pem -out req.pem -outform PEM \\\n -subj /CN=$(hostname)/O=client/ -nodes\ncd ../testca\nopenssl ca -config openssl.cnf -in ../client/req.pem -out \\\n ../client/client_certificate.pem -notext -batch -extensions client_ca_extensions\ncd ../client\nopenssl pkcs12 -export -out client_certificate.p12 -in client_certificate.pem -inkey private_key.pem \\\n -passout pass:MySecretPassword\n"})}),"\n",(0,s.jsxs)(n.p,{children:["The two examples above generate private keys that are 2048 bits in size.\nIt is possible to use longer (and thus more secure but also slower to generate)\nkeys by providing a different value to ",(0,s.jsx)(n.code,{children:"openssl genrsa"}),", e.g.:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"openssl genrsa -out private_key.pem 4096\n"})}),"\n",(0,s.jsxs)(n.p,{children:["Another option would be to generate a key using ",(0,s.jsx)(n.a,{href:"https://blog.cloudflare.com/a-relatively-easy-to-understand-primer-on-Elliptic-curve-cryptography/",children:"Elliptic Curve Cryptography"}),". Instead of ",(0,s.jsx)(n.code,{children:"openssl genrsa"})," use\n",(0,s.jsx)(n.code,{children:"openssl ecparam"})," like so:"]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-bash",children:"openssl ecparam -out private_key.pem -genkey -name prime256v1\n"})}),"\n",(0,s.jsxs)(n.p,{children:[(0,s.jsx)(n.code,{children:"prime256v1"})," in the example above is an Elliptic curve name.\nDifferent versions of OpenSSL will have a different set of curves available,\nlist them with ",(0,s.jsx)(n.code,{children:"openssl ecparam -list_curves"}),"."]})]})}function u(e={}){let{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(f,{...e})}):f(e)}},64725(e,n,t){t.d(n,{A:()=>i});let i=t.p+"assets/images/root_ca_and_leaf-8bc604562329d97549515682cb232d74.png"},71857(e,n,t){t.d(n,{A:()=>i});let i=t.p+"assets/images/root_intermediate_ca_and_leaf-0db0418e7a066229fe9e758463916de8.png"},57250(e,n,t){t.d(n,{A:()=>o});var i=t(74848);t(96540);var s=t(34164),r=t(57663);function a({children:e,className:n,hidden:t}){return(0,i.jsx)("div",{role:"tabpanel",className:(0,s.A)("tabItem_Ymn6",n),hidden:t,children:e})}function o({children:e,className:n,value:t}){let{selectedValue:s,lazy:c}=(0,r.uc)(),l=t===s;return!l&&c?null:(0,i.jsx)(a,{className:n,hidden:!l,children:e})}},50773(e,n,t){t.d(n,{A:()=>p});var i=t(74848);t(96540);var s=t(34164),r=t(88287),a=t(57663),o=t(28584),c=t(19863);function l({className:e}){let{selectedValue:n,selectValue:t,tabValues:r,block:c}=(0,a.uc)(),h=[],{blockElementScrollPositionUntilNextRender:d}=(0,o.a_)(),p=e=>{let i=e.currentTarget,s=r[h.indexOf(i)].value;s!==n&&(d(i),t(s))},f=e=>{let n=null;switch(e.key){case"Enter":p(e);break;case"ArrowRight":{let t=h.indexOf(e.currentTarget)+1;n=h[t]??h[0];break}case"ArrowLeft":{let t=h.indexOf(e.currentTarget)-1;n=h[t]??h[h.length-1]}}n?.focus()};return(0,i.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,s.A)("tabs",{"tabs--block":c},e),children:r.map(({value:e,label:t,attributes:r})=>(0,i.jsx)("li",{role:"tab",tabIndex:n===e?0:-1,"aria-selected":n===e,ref:e=>{h.push(e)},onKeyDown:f,onClick:p,...r,className:(0,s.A)("tabs__item","tabItem_LNqP",r?.className,{"tabs__item--active":n===e}),children:t??e},e))})}function h({children:e}){return(0,i.jsx)("div",{className:"margin-top--md",children:e})}function d({className:e,children:n}){return(0,i.jsxs)("div",{className:(0,s.A)(r.G.tabs.container,"tabs-container","tabList__CuJ"),children:[(0,i.jsx)(l,{className:e}),(0,i.jsx)(h,{children:n})]})}function p(e){let n=(0,c.A)(),t=(0,a.OC)(e);return(0,i.jsx)(a.O_,{value:t,children:(0,i.jsx)(d,{className:e.className,children:(0,a.vT)(e.children)})},String(n))}},57663(e,n,t){t.d(n,{OC:()=>p,O_:()=>m,uc:()=>u,vT:()=>h});var i=t(74848),s=t(96540),r=t(56347),a=t(99989),o=t(96629),c=t(80618),l=t(41367);function h(e){return s.Children.toArray(e).filter(e=>"\n"!==e)}function d({value:e,tabValues:n}){return n.some(n=>n.value===e)}function p(e){let n,{defaultValue:t,queryString:i=!1,groupId:h}=e,p=function(e){let{values:n,children:t}=e;return(0,s.useMemo)(()=>{let e=n??s.Children.toArray(t).flatMap(e=>{if(!e)return[];if((0,s.isValidElement)(e)&&function(e){let{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return[e];
1let n="string"==typeof e.type?e.type:e.type.name;throw Error(`Docusaurus error: Bad <Tabs> child <${n}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop. 2If you do not want to pass on a "value" prop to the direct children of <Tabs>, you can also pass an explicit <Tabs values={...}> prop.`)}).map(({props:{value:e,label:n,attributes:t,default:i}})=>({value:e,label:n,attributes:t,default:i})),i=(0,c.XI)(e,(e,n)=>e.value===n.value);if(i.length>0)throw Error(`Docusaurus error: Duplicate values "${i.map(e=>`'${e.value}'`).join(", ")}" found in <Tabs>. Every value needs to be unique.`);return e},[n,t])}(e),[f,u]=(0,s.useState)(()=>(function({defaultValue:e,tabValues:n}){if(0===n.length)throw Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(e){if(!d({value:e,tabValues:n}))throw Error(`Docusaurus error: The <Tabs> has a defaultValue "${e}" but none of its children has the corresponding value. Available values are: ${n.map(e=>e.value).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return e}let t=n.find(e=>e.default)??n[0];if(!t)throw Error("Unexpected error: 0 tabValues");return t.value})({defaultValue:t,tabValues:p})),[m,v]=function({queryString:e=!1,groupId:n}){let t=(0,r.W6)(),i=function({queryString:e=!1,groupId:n}){if("string"==typeof e)return e;if(!1===e)return null;if(!0===e&&!n)throw Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:e,groupId:n});return[(0,o.aZ)(i),(0,s.useCallback)(e=>{if(!i)return;let n=new URLSearchParams(t.location.search);n.set(i,e),t.replace({...t.location,search:n.toString()})},[i,t])]}({queryString:i,groupId:h}),[x,S]=function({groupId:e}){let n=e?`docusaurus.tab.${e}`:null,[t,i]=(0,l.Dv)(n);return[t,(0,s.useCallback)(e=>{n&&i.set(e)},[n,i])]}({groupId:h}),g=d({value:n=m??x,tabValues:p})?n:null;return(0,a.A)(()=>{g&&u(g)},[g]),{selectedValue:f,selectValue:(0,s.useCallback)(e=>{if(!d({value:e,tabValues:p}))throw Error(`Can't select invalid tab value=${e}`);u(e),v(e),S(e)},[v,S,p]),tabValues:p,lazy:e.lazy??!1,block:e.block??!1}}let f=(0,s.createContext)(null);function u(){let e=s.useContext(f);if(!e)throw Error("useTabsContext() must be used within a Tabs component");return e}function m(e){return(0,i.jsx)(f.Provider,{value:e.value,children:e.children})}},3432(e,n,t){t.d(n,{Vl:()=>i}),t(96540),t(10898);function i(){return"https://rabbitmq.github.io/rabbitmq-dotnet-client/api"}},28453(e,n,t){t.d(n,{R:()=>a,x:()=>o});var i=t(96540);let s={},r=i.createContext(s);function a(e){let n=i.useContext(r);return i.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:a(e.components),i.createElement(r.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.