Documentation: Usage of HTTPS certificates for local development
Hi! I've been working on Mastodon which uses http.rb, and in local development, I'm using localcan to route traffic to various development servers all with auto-generated SSL certificates from a root certificate that localcan created. This means they're all self-signed.
Whilst reading the HTTPS documentation, I noticed that it was using ctx.set_params on the OpenSSL::SSL::SSLContext, I noticed a line in the documentation of:
The cert, key, and extra_chain_cert attributes are deprecated. It is recommended to use add_certificate instead.
Source: https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#attribute-i-cert
I'm wondering if the documentation should be updated to use that add_certificate method instead?
As I also have a Root CA certificate, as well as individual .crt and .key files per domain, I'm wondering if there's a way to tell http.rb to just add that Root CA certificate to it's trust chain, without overriding any existing certificates?
I'm wondering if the documentation should be updated to use that add_certificate method instead?
Yes, updating the documentation sounds good. If you can get me a working replacement code example, I can do the update.
As I also have a Root CA certificate, as well as individual .crt and .key files per domain, I'm wondering if there's a way to tell http.rb to just add that Root CA certificate to it's trust chain, without overriding any existing certificates?
I believe that's handled by OpenSSL::X509::Store which you can access using OpenSSL::X509::SSLContext#cert_store
Okay, so, yeah, from what I can tell this snippet:
HTTP.get("https://example.com", ssl_context: OpenSSL::SSL::SSLContext.new.tap do |ctx|
ctx.set_params(
cert: OpenSSL::X509::Certificate.new(File.read("client.crt")),
key: OpenSSL::PKey::RSA.new(File.read("client.key"))
)
end)
becomes:
HTTP.get("https://example.com", ssl_context: OpenSSL::SSL::SSLContext.new.tap do |ctx|
ctx.add_certificate(
OpenSSL::X509::Certificate.new(File.read("client.crt")),
OpenSSL::PKey::RSA.new(File.read("client.key"))
)
end)
and the second snippet:
ssl_context = OpenSSL::SSL::SSLContext.new.tap do |ctx|
ctx.set_params(
cert: certs.shift, # The root certificate
key: OpenSSL::PKey::RSA.new(File.read("path_to_your_private_key.pem")),
extra_chain_cert: certs # The intermediate certificates
)
end
becomes:
bundle = File.read("path_to_your_fullchain.pem")
certificate_content_regex = /-----BEGIN CERTIFICATE-----(?:.|\n)+?-----END CERTIFICATE-----/
certs = bundle.scan(certificate_content_regex).map { OpenSSL::X509::Certificate.new(_1) }
ssl_context = OpenSSL::SSL::SSLContext.new.tap do |ctx|
ctx.add_certificate(
certs.shift, # The root certificate
OpenSSL::PKey::RSA.new(File.read("path_to_your_private_key.pem")),
certs # The intermediate certificates
)
end
Hope that helps. n.b., I haven't tested exactly this code, but I did do similar the other day.