-
-
Save tadast/9932075 to your computer and use it in GitHub Desktop.
# 1) Create your private key (any password will do, we remove it below) | |
$ cd ~/.ssh | |
$ openssl genrsa -des3 -out server.orig.key 2048 | |
# 2) Remove the password | |
$ openssl rsa -in server.orig.key -out server.key | |
# 3) Generate the csr (Certificate signing request) (Details are important!) | |
$ openssl req -new -key server.key -out server.csr | |
# IMPORTANT | |
# MUST have localhost.ssl as the common name to keep browsers happy | |
# (has to do with non internal domain names ... which sadly can be | |
# avoided with a domain name with a "." in the middle of it somewhere) | |
Country Name (2 letter code) [AU]: | |
... | |
Common Name: localhost.ssl | |
... | |
# 4) Generate self signed ssl certificate | |
$ openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt | |
# 5) Finally Add localhost.ssl to your hosts file | |
$ echo "127.0.0.1 localhost.ssl" | sudo tee -a /private/etc/hosts | |
# 6) Boot puma | |
$ puma -b 'ssl://127.0.0.1:3000?key=/Users/tadas/.ssh/server.key&cert=/Users/tadas/.ssh/server.crt' | |
# 7) Add server.crt as trusted !!SYSTEM!! (not login) cert in the mac osx keychain | |
# Open keychain tool, drag .crt file to system, and trust everything. | |
# Notes: | |
# 1) Https traffic and http traffic can't be served from the same process. If you want | |
# both you need to start two instances on different ports. | |
# | |
# |
The first two instructions here are extraneous. If you simply eliminate the "-des3" in the first statement then there's no need to remove the passphrase later.
- openssl genrsa -out server.key 4096
- openssl req -new -key server.key -out server.csr
- openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
You can do it in a single command if you like, instructions here:
As above, to make Puma listen on 3001 for an SSL connection, add this to your config/puma.rb file:
# On development, run ssl server on port 3001
if ENV.fetch("RAILS_ENV") == 'development'
ssl_bind '127.0.0.1', '3001', {
key: ENV.fetch("SSL_KEY_PATH"),
cert: ENV.fetch("SSL_CERT_PATH"),
verify_mode: 'none'
}
end
Make sure to set up environment variables for SSL_KEY_PATH and SSL_CERT_PATH, or replace "ENV.fetch..." above with actual paths.
Okay, after much fussing about I got this working. It took two things from the original doc:
- @ericchen post got the ssl errors to go away. This should have been it, but then
- SSL was crashing puma because of: puma/puma#1214, so I ended running off master until they get this merged in.
@craineum would you mind sharing your solution I am having exact same problem on
- Mac OS Sierra 10.12.6 (16G29)
- Rails 5.0.1
I have tried running puma off of master along with @mdchaney and @ericchen steps ... but I continue to get the same error
2017-08-01 22:34:10 -0500: HTTP parse error, malformed request (): #<Puma::HttpParserError: Invalid HTTP format, parsing fails.>
Thank you!
On my elementaryOS (ubuntu 16.04) worked.
Only I replace "/private/etc/hosts" on "/etc/hosts"
and "/Users/user_name" on "/home/user_name"
@timoschilling any luck binding SSL and TCP both to the same port? I'm trying to get Rails' force_ssl
to redirect from HTTP to HTTPS, but that would mean it's on the same port. This is for local only.
Thanks!
mkdir config/certs && touch config/certs/.keep
puma.rb
if Rails.env.development?
unless File.exist?(Rails.root.join('config', 'certs', 'localhost.key'))
def generate_root_cert(root_key)
root_ca = OpenSSL::X509::Certificate.new
root_ca.version = 2 # cf. RFC 5280 - to make it a "v3" certificate
root_ca.serial = 0x0
root_ca.subject = OpenSSL::X509::Name.parse "/C=BE/O=A1/OU=A/CN=localhost"
root_ca.issuer = root_ca.subject # root CA's are "self-signed"
root_ca.public_key = root_key.public_key
root_ca.not_before = Time.now
root_ca.not_after = @root_ca.not_before + 2 * 365 * 24 * 60 * 60 # 2 years validity
root_ca.sign(root_key, OpenSSL::Digest::SHA256.new)
root_ca
end
root_key = OpenSSL::PKey::RSA.new(2048)
file = File.new( Rails.root.join('config', 'certs', 'localhost.key'), "wb")
file.write(root_key)
file.close
root_cert = generate_root_cert(root_key)
file = File.new( Rails.root.join('config','certs', 'localhost.cert'), "wb")
file.write(root_cert)
file.close
end
ssl_bind '0.0.0.0', '8443', {
key: Rails.root.join('config','certs', 'localhost.key'),
cert: Rails.root.join('config','certs', 'localhost.cert')
}
end
.gitignore
/config/certs/*
!/config/certs/.keep
@yuri-zubov Great! I think you might have won this thread! Your solution works for me.
Minor change:
root_ca.not_after = @root_ca.not_before + 2 * 365 * 24 * 60 * 60 # 2 years validity
becomes
root_ca.not_after = root_ca.not_before + 2 * 365 * 24 * 60 * 60 # 2 years validity
It's not an instance variable, it's a local variable. Thanks!
There appears to be some kind of issue with Puma 3.11 and localhost SSL. Going back to 3.10 for now doesn't display the same error.
puma/puma#1483
@yuri-zubov @Petercopter Thanks. Only solution that works for me after trying many different things.
While everything seems to work fine, puma generates this error message:
"peer cert: , #<Puma::MiniSSL::SSLError: System error: Success - 0>"
I'm using Puma 3.10.0, Rails 5.2.0.rc1 and Ruby 2.5.
@Petercopter @rajeshm15 I was getting the same error and then tried opening the whole URL displayed after starting the server:
https://localhost:8443/?cert=/Users/benjam1n/Development/testing/config/certs/localhost.cert&key=/Users/benjam1n/Development/testing/config/certs/localhost.key&verify_mode=none
...and it worked 👍
Worth noting you can definitely just run the rails server in this fashion, so you still get the rails logs without any extra configuration:
rails s -b 'ssl://127.0.0.1:3000?key=/Users/{{username}}/.ssh/server.key&cert=/Users/{{username}}/.ssh/server.crt'
Hello Guys,
I'm getting the error when I run the openssl req -new -key server.key -out server.csr
Error Loading extension section v3_ca
140274425202328:error:22075075:X509 V3 routines:v2i_GENERAL_NAME_ex:unsupported option:v3_alt.c:550:name=subjectKeyIdentifier
140274425202328:error:22098080:X509 V3 routines:X509V3_EXT_nconf:error in extension:v3_conf.c:95:name=subjectAltName, value=@alt_names
Also, getting when run rails server
2018-04-17 15:16:36 +0530: SSL error, peer: 127.0.0.1, peer cert: , #<Puma::MiniSSL::SSLError: OpenSSL error: error:1408A0C1:SSL routines:ssl3_get_client_hello:no shared cipher - 336109761>
I'm also seeing the above error reported by @djadma. This was working for me for months until today.
There's a small issue in @yuri-zubov's awesome gist. It has to be root_ca.not_after = root_ca.not_before + 2 * 365 * 24 * 60 * 60 # 2 years validity
, not root_ca.not_after = @root_ca.not_before + 2 * 365 * 24 * 60 * 60 # 2 years validity
- without the @
.
Below is a version adapted for the use with Puma, but without Rails (e.g. for Grape):
localhost_key = "#{Dir.pwd}/#{File.join('config', 'certs', 'localhost.key')}"
localhost_crt = "#{Dir.pwd}/#{File.join('config', 'certs', 'localhost.crt')}"
unless File.exist?(localhost_key)
def generate_root_cert(root_key)
root_ca = OpenSSL::X509::Certificate.new
root_ca.version = 2 # cf. RFC 5280 - to make it a "v3" certificate
root_ca.serial = 0x0
root_ca.subject = OpenSSL::X509::Name.parse "/C=BE/O=A1/OU=A/CN=localhost"
root_ca.issuer = root_ca.subject # root CA's are "self-signed"
root_ca.public_key = root_key.public_key
root_ca.not_before = Time.now
root_ca.not_after = root_ca.not_before + 2 * 365 * 24 * 60 * 60 # 2 years validity
root_ca.sign(root_key, OpenSSL::Digest::SHA256.new)
root_ca
end
root_key = OpenSSL::PKey::RSA.new(2048)
file = File.new(localhost_key, "wb")
file.write(root_key)
file.close
root_cert = generate_root_cert(root_key)
file = File.new(localhost_crt, "wb")
file.write(root_cert)
file.close
end
# To be able to use rake etc
if self.respond_to?(:ssl_bind)
ssl_bind '0.0.0.0', '8443', {
key: localhost_key,
cert: localhost_crt
}
end
This works. But when I am trying to access assets in sidekiq it throws Openssl error.
open('https://localhost:3000/uploads/messasge_attachments/sms/cabff65057e09a8f.').read
OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 state=error: certificate verify failed
from (irb):6
@stiller-leser's solution worked for me.
Thanks everyone who chimed in with their solutions and feedback.
puma (3.11.4)
rails 5.2.0
ruby 2.5
dockerized environment
My puma.rb
:
# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum; this matches the default thread size of Active Record.
#
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count
# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#
port ENV.fetch("PORT") { 3000 }
# Specifies the `environment` that Puma will run in.
#
environment ENV.fetch("RAILS_ENV") { "development" }
# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked webserver processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
# workers ENV.fetch("WEB_CONCURRENCY") { 2 }
# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory.
#
# preload_app!
# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart
if Rails.env.development?
localhost_key = "#{Dir.pwd}/#{File.join('config', 'certs', 'localhost.key')}"
localhost_cert = "#{Dir.pwd}/#{File.join('config', 'certs', 'localhost.crt')}"
unless File.exist?(localhost_key)
def generate_root_cert(root_key)
root_ca = OpenSSL::X509::Certificate.new
root_ca.version = 2 # cf. RFC 5280 - to make it a "v3" certificate
root_ca.serial = 0x0
root_ca.subject = OpenSSL::X509::Name.parse "/C=BE/O=A1/OU=A/CN=localhost"
root_ca.issuer = root_ca.subject # root CA's are "self-signed"
root_ca.public_key = root_key.public_key
root_ca.not_before = Time.now
root_ca.not_after = root_ca.not_before + 2 * 365 * 24 * 60 * 60 # 2 years validity
root_ca.sign(root_key, OpenSSL::Digest::SHA256.new)
root_ca
end
root_key = OpenSSL::PKey::RSA.new(2048)
file = File.new( localhost_key, "wb")
file.write(root_key)
file.close
root_cert = generate_root_cert(root_key)
file = File.new( localhost_cert, "wb")
file.write(root_cert)
file.close
end
ssl_bind '0.0.0.0', '8443', {
key: localhost_key,
cert: localhost_cert
}
end
There is a fantastic tool called mkcert which eliminates most of the pain of generating self signed certs and installing them as trusted certs on your machine - https://github.com/FiloSottile/mkcert. Way easier than trying wrangle OpenSSL commands and APIs.
For what it's worth, here's a simpler and more secure alternative to Linux for hosting your static sites and Rails apps on the same server: https://gist.github.com/anon987654321/4532cf8d6c59c1f43ec8973faa031103
I'm seeing this error on modern versions of Chrome.
2020-06-26 09:23:21 -0500: SSL error, peer: 127.0.0.1, peer cert: , #<Puma::MiniSSL::SSLError: OpenSSL error: error:141F7065:SSL routines:final_key_share:no suitable key share - 337604709>
According to google, this indicates that the cert generated in this guide is insecure and not supported in SSL 1.3
OpenSSL is fundamentally insecure. I'd recommend checking out my gist above which is based on OpenBSD's OpenSSL rewrite LibreSSL:
OpenBSD's acme-client is also the most secure cert generator around. Check it out!
Can such cert be generated on a mac, @anon987654321? I'd love to get away from openssl, but it seems like it's the only TLS group that figured out how to do distribution of their software (which is clearly a bit less than ideal given their quality track record).
thank you, it work for me
Thank you for this great thread!
This is my adaptation: https://gist.github.com/etozzato/0ba2140ea3c6125d4839373309fe733a
- Allows for a domain and wildcard subdomain;
- Cleans up after itself in case of error;
- Will still boot puma (no SSL) in case of error;
@TheNotary thanks for getting back at me. You'd probably have to spawn a new server using OpenBSD, check out:
https://github.com/basicfeatures/openbsd-rails
Does SSL/TLS termination before Puma as Puma isn't really suited for this. Check out https://github.com/ErwinM/acts_as_tenant for multiple domains/subdomains, or message me.
@etozzato I might be wrong, but your gist looks over-engineered.
@etozzato I might be wrong, but your gist looks over-engineered.
yes, it's plausible! 👍
You can generate a trusted localhost cert by using letsencrypt and creating a certificate like localhost.domain.com
(or *.localhost.domain.com
for wildcards), verify that with a dns challenge, which usually involves creating an _acme_challenge
TXT record. Then, once you have passed the challenges and have the cert, point localhost.domain.com to 127.0.0.1
If you have a multi-tenant app, you can create a wildcard cert also, but you'll have to go through the extra step of manually adding subdomains to localhost.domain.com
to/etc/hosts
and your config/enviroments/development.rb
(assuming this is a rails app)
In order to run with Rails (version 7),
bin/rails s -u puma -b 'ssl://127.0.0.1:3000?key=server.key&cert=server.crt&verify_mode=peer&ca=server.crt'
There is a fantastic tool called mkcert which eliminates most of the pain of generating self signed certs and installing them as trusted certs on your machine - https://github.com/FiloSottile/mkcert. Way easier than trying wrangle OpenSSL commands and APIs.
I would like to recommend this approach as well.
I am no SSL guru, so I had a long battle trying to get local SSL to work a my new computer (it works fine on my older one). At some point I even had subjectively non-deterministic results where my SSL would work for a minute or two and then stop working with no apparent change in anything.
Using the mkcert on my macOS computer via homebrew solved the problem very quickly and easily.
@fifiteen82726 You are running webrick, not puma, these instructions will not work for webrick.