We have a Rails app running in Windows that uses Puma. So far we've stored our SSL/TLS certificates on the filesystem, which seems to be the standard in general, and the way Puma is designed to take in that data when starting up.
We would like to instead keep only an encrypted PKCS#12 file (.12) on disk, that holds all certificate data (one or more certificates and the private key), pulls out the specific certs during puma start up into variables, and then feeds that directly into the Puma ssl_bind command.
So I'm trying to figure out if Puma can accept variables that hold certificate data, as opposed to providing the expected cert_path and key_path that point at plaintext files.
I've tried a few different ways of replacing the file paths with variables, but I only get errors so far (see below for example). I've output the cert key along side the same from the file system, and they look identical to me. I've read other somewhat related SO threads that suggest maybe I need to add newlines or otherwise slightly manipulate the data in my variables, but that line of thinking has confused me so far and I'm not sure if it really pertains to my scenario. I think it comes down to ssl_bind expecting a file path, and likely running "file open" logic under the hood. Does it simply not support taking it directly?
Here is an example of what works today:
# tls.key and tls.crt are already sitting on filesystem
ssl_bind '0.0.0.0', '443', {
key: 'certs/tls.key',
cert: 'certs/tls.crt',
no_tlsv1: true,
no_tlsv1_1: true,
verify_mode: 'none'
}
Here is an example of what we want to do
require 'openssl'
# get p12 password out of secrets at runtime
p12_password = Rails.application.credentials.p12[:password].to_s
# open encrypted p12 file
p12 = OpenSSL::PKCS12.new(File.binread('certs/tls.p12'), p12_password)
# pull out certificate and key from p12
leafkey = p12.key.to_pem
leafcertificate = p12.certificate.to_pem
ssl_bind '0.0.0.0', '443', {
key: leafkey,
cert: leafcertificate,
no_tlsv1: true,
no_tlsv1_1: true,
verify_mode: 'none'
}
The error we receive from the above is:
C:/Ruby27-x64/lib/ruby/gems/2.7.0/gems/puma-4.3.8/lib/puma/minissl.rb:209:in `key=': No such key file '-----BEGIN EC PRIVATE KEY-----MDECAQEEIBccaYhSLodf 4TRzzWkOE5rr8t Ul0oQHcjYmmoiuvloAoGCCqGSM4jdu73-----END EC PRIVATE KEY-----' (ArgumentError)
This is the valid EC key data, but Puma/ssl_bind appears confused (not surprisingly) that it's not the expected path to a file on disk that contains this data. Can we trick Puma into accepting it directly this way?
Thank you for reading and taking the time to express any thoughts you may have!
This requirement was added as a enhancement in this PR
So far it looks like I was able to update Puma from 4.3.8 directly to 5.6.2 without any fuss. We did need to update 2 options to the *_pem versions, i.e.,
cert becomes cert_pem and key becomes key_pem
With this in place, it JUST WORKED.
Example running with puma 5.6.2:
require 'openssl'
# using cleartext password for testing/simplicity
p12 = OpenSSL::PKCS12.new(File.binread('certs/tls.p12'), "1234")
leafcertificate = p12.certificate.to_pem
leafkey = p12.key.to_pem
ssl_bind '0.0.0.0', '443', {
cert_pem: leafcertificate,
key_pem: leafkey,
no_tlsv1: true,
no_tlsv1_1: true
}
Personal lessons learned: I should have prioritized digging into the Puma repo: pull requests, issues, etc.