Module: AceMQ::AMQP::DevelopmentCertificates
- Defined in:
- lib/acemq/amqp/dev_certificates.rb
Overview
Generates a throwaway certificate authority and the certificates a local
broker and client need, so that running with TLS on a developer's machine
is one command rather than an afternoon with openssl.
AceMQ::AMQP::DevelopmentCertificates.generate(directory: "certs")
or from a checkout:
./scripts/acemq-certs.rb --out certs --broker localhost --days 30
The marker
Everything produced here carries ACEMQ DEVELOPMENT ONLY - DO NOT TRUST in its subject organisation, and Security refuses a certificate carrying it however trust is configured — unverified mode included — unless Security#allowing_development_certificates says otherwise. That is the point: a self-signed authority that drifts into production is worse than no encryption, because everything looks protected and nothing is verified. These fail closed instead.
The marker goes in the organisation rather than the organisational unit,
which is where Go and .NET put it and where every tool that prints a
subject shows it. The Java library puts it in OU, so a Java-generated
certificate is caught here too — the check is on the whole subject and the
whole issuer — but a certificate generated here matches what
scripts/tls-broker.sh greps for.
What it writes
The six files the other libraries' generators write, under the same names,
so this generator and Go's acemq-certs are interchangeable:
ca.crt the authority, to trust
ca.key its key, so more certificates can be signed later
server.crt the broker's certificate
server.key the broker's key
client.crt a client certificate, for EXTERNAL authentication
client.key its key
and, unless asked not to, a rabbitmq.conf that serves TLS from them.
Keys are written 0600. A development key is still a key, and one world-readable in a repository checkout is a habit worth not forming.
Certificates are short-lived by default. A development certificate that never expires is one that outlives the reason it was created.
Elliptic curve, not RSA
P-256, as the Go generator uses. RSA-4096 as the Java generator uses is equally fine and takes several seconds per key in Ruby, which is a long time to wait for something whose whole promise is that it is one command.
Defined Under Namespace
Classes: Result
Constant Summary collapse
- MARKER =
Written into every subject, and refused by Security unless explicitly allowed. The same string in all five languages.
Security::DEVELOPMENT_MARKER
- AUTHORITY_NAME =
The authority's common name, shared with Go and .NET.
"AceMQ development CA"- CLIENT_NAME =
The client certificate's common name, shared with Go and .NET.
"acemq-client"- DEFAULT_VALIDITY_DAYS =
Short on purpose.
30- DEFAULT_BROKER_CERTIFICATE_DIRECTORY =
Where rabbitmq.conf will look, which is where the certificates are mounted inside the broker's container rather than where they are here.
"/certs"- CURVE =
The curve. P-256 is what Go's generator uses and what every broker and every client in this decade supports.
"prime256v1"
Class Method Summary collapse
-
.broker_configuration(certificate_directory = DEFAULT_BROKER_CERTIFICATE_DIRECTORY) ⇒ String
A
rabbitmq.confthat serves TLS from what DevelopmentCertificates.generate wrote. -
.generate(directory: "certs", broker_host: "localhost", days: DEFAULT_VALIDITY_DAYS, broker_certificate_directory: DEFAULT_BROKER_CERTIFICATE_DIRECTORY, broker_config: true) ⇒ Result
Writes a certificate authority, a broker certificate and a client certificate.
Class Method Details
.broker_configuration(certificate_directory = DEFAULT_BROKER_CERTIFICATE_DIRECTORY) ⇒ String
A rabbitmq.conf that serves TLS from what generate wrote.
verify_peer with fail_if_no_peer_cert off means the broker will
use a client certificate if one is presented and will not insist on
it, which is what suits a development broker that is also reached by
password.
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/acemq/amqp/dev_certificates.rb', line 161 def broker_configuration(certificate_directory = DEFAULT_BROKER_CERTIFICATE_DIRECTORY) <<~CONF # Written by AceMQ::AMQP::DevelopmentCertificates. Development only. listeners.ssl.default = 5671 ssl_options.cacertfile = #{certificate_directory}/ca.crt ssl_options.certfile = #{certificate_directory}/server.crt ssl_options.keyfile = #{certificate_directory}/server.key ssl_options.verify = verify_peer ssl_options.fail_if_no_peer_cert = false # The plaintext listener stays on so a laptop can use either. listeners.tcp.default = 5672 CONF end |
.generate(directory: "certs", broker_host: "localhost", days: DEFAULT_VALIDITY_DAYS, broker_certificate_directory: DEFAULT_BROKER_CERTIFICATE_DIRECTORY, broker_config: true) ⇒ Result
Writes a certificate authority, a broker certificate and a client certificate.
Existing files are overwritten. These are development certificates with a short life, so regenerating them is the expected way to deal with expiry rather than something to guard against — but regenerating changes the authority, which invalidates anything already trusting it, including a broker somebody is still pointing at.
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/acemq/amqp/dev_certificates.rb', line 122 def generate(directory: "certs", broker_host: "localhost", days: DEFAULT_VALIDITY_DAYS, broker_certificate_directory: DEFAULT_BROKER_CERTIFICATE_DIRECTORY, broker_config: true) FileUtils.mkdir_p(directory) # Backdated an hour. A machine whose clock is a few minutes behind the # one that generated these would otherwise reject them as not yet # valid, which is a confusing way to spend a morning. life = ((Time.now - 3600)..(Time.now + (days * 24 * 60 * 60))) ca_key, = (life) server_key, server = leaf_pair(broker_host, "serverAuth", ca_key, , life, alternative_names(broker_host)) client_key, client = leaf_pair(CLIENT_NAME, "clientAuth", ca_key, , life, nil) written = write(directory, "ca" => [, ca_key], "server" => [server, server_key], "client" => [client, client_key]) config = File.join(directory, "rabbitmq.conf") if broker_config if config written << write_file(config, broker_configuration(broker_certificate_directory), 0o644) end Result.new(directory: directory, authority: , server: server, client: client, expiry: life.end, files: written, broker_config: config) end |