Open tonytonyjan opened 4 years ago
The configuration options are different depending on how you start falcon.
I was able to set the path to certificates for falcon serve
by monkey-patching the localhost gem with the following script:
#!/usr/bin/env ruby
require 'localhost/authority'
class Localhost::Authority
def self.path
File.join __dir__, 'ssl'
end
end
require 'falcon/command'
serve = Falcon::Command::Serve[
'--hostname', 'example.com',
'--bind', 'https://localhost:443',
parent: Falcon::Command::Top[]
]
serve.call
Certificates will be loaded based on the hostname specified, like:
ssl/example.com.crt
ssl/example.com.key
falcon serve
is for local development and should generally not be used in production.
falcon host
should be used in production and you use a falcon.rb
file to configure it.
There is very limited documentation at this time as it's only just been released, but take a look at the following files:
Here are the parameters to set the tls certificates:
Make your own falcon.rb
file and put the tls configuration here:
https://github.com/socketry/falcon/blob/master/examples/hello/falcon.rb#L9-L14
Then use falcon host
.
Also if possible, once you have it working can you let me know and can you help with documentation?
Note that OpenSSL::SSL::SSLContext#ssl_version=
is deprecated in favor of min_version
and max_version
.
falcon serve
works without any configuration, but falcon host
gives me TLS errors. Here's the curl output:
% curl -v "https://example.com:3000"
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to example.com (127.0.0.1) port 3000 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS alert, handshake failure (552):
* error:14004410:SSL routines:CONNECT_CR_SRVR_HELLO:sslv3 alert handshake failure
* Closing connection 0
curl: (35) error:14004410:SSL routines:CONNECT_CR_SRVR_HELLO:sslv3 alert handshake failure
Here's my config file:
#!/usr/bin/env ./bin/falcon-host
load :rack, :tls
rack 'example.com', :tls do
ssl_session_id 'falcon'
ssl_ciphers Falcon::TLS::SERVER_CIPHERS
scheme 'https'
protocol { Async::HTTP::Protocol::HTTPS }
endpoint do
Async::HTTP::Endpoint.for(scheme, 'localhost', port: 3000, protocol: protocol)
end
ssl_certificate_path { File.expand_path 'ssl/certificate.pem', root }
ssl_certificates { OpenSSL::X509.load_certificates ssl_certificate_path }
ssl_certificate { ssl_certificates[0] }
ssl_certificate_chain { ssl_certificates[1..-1] }
ssl_private_key_path { File.expand_path 'ssl/private.key', root }
ssl_private_key { OpenSSL::PKey::RSA.new File.read(ssl_private_key_path) }
ssl_context do
OpenSSL::SSL::SSLContext.new.tap do |context|
context.add_certificate ssl_certificate, ssl_private_key, ssl_certificate_chain
context.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT
context.session_id_context = ssl_session_id
context.alpn_select_cb = lambda do |protocols|
if protocols.include? 'h2'
return 'h2'
elsif protocols.include? 'http/1.1'
return 'http/1.1'
elsif protocols.include? 'http/1.0'
return 'http/1.0'
else
return nil
end
end
context.ssl_version = :TLSv1_2_server
context.set_params ciphers: ssl_ciphers, verify_mode: OpenSSL::SSL::VERIFY_NONE
context.setup
end
end
end
It works if I disable HTTPS by setting scheme 'http'
with protocol { Async::HTTP::Protocol::HTTP1 }
.
You don't need to duplicate the configuration in :tls
if you aren't changing it. You should only need to set the ssl_certificate_path
and ssl_private_key_path
.
Can you try some other tool like wget
. Are you on Darwin or Linux?
My operating system is darwin19.2
.
Here's my updated falcon host
config file, which also does not work. The endpoint
line is needed to force falcon to bind with TCP. Without it Async::IO::UNIXEndpoint
is used.
load :rack, :tls
rack 'example.com', :tls do
endpoint { Async::HTTP::Endpoint.for scheme, 'localhost' }
ssl_certificate_path { '/usr/local/project/ssl/example.com.crt' }
ssl_private_key_path { '/usr/local/project/ssl/example.com.key' }
end
I figure my syntax must be incorrect, because no matter what I try, falcon host
has bad TLS:
% openssl s_client -connect example.com:443
CONNECTED(00000003)
4636098156:error:14004410:SSL routines:CONNECT_CR_SRVR_HELLO:sslv3 alert handshake failure:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-47.11.1/libressl-2.8/ssl/ssl_pkt.c:1200:SSL alert number 40
4636098156:error:140040E5:SSL routines:CONNECT_CR_SRVR_HELLO:ssl handshake failure:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-47.11.1/libressl-2.8/ssl/ssl_pkt.c:585:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 0 bytes
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : 0000
Session-ID:
Session-ID-ctx:
Master-Key:
Start Time: 1580958321
Timeout : 7200 (sec)
Verify return code: 0 (ok)
---
curl 7.68 suggests a client certificate is required:
* Trying ::1:443...
* TCP_NODELAY set
* Connected to localhost (::1) port 443 (#0)
* ALPN, offering http/1.1
* SSL peer handshake failed, the server most likely requires a client certificate to connect
* Closing connection 0
curl: (35) SSL peer handshake failed, the server most likely requires a client certificate to connect
However, I haven't been able to override the ssl_context
and make it work.
Meanwhile, falcon serve
negotiates cipher ECDHE-RSA-AES256-GCM-SHA384
and my CA-signed certificate works correctly.
Interesting, I'll probably need to take a closer look. Thanks for reporting back.
I tried again with a clean setup on darwin18 and had the same outcome. No TLS under falcon host
, and self-signed certificates only under falcon virtual
.
I can successfully use my custom certificates using falcon serve
with the workaround I posted before in this thread. I am running this in staging (rails production
environment) as a temporary workaround.
Can you let me know what version of falcon you are using?
I'm on falcon 0.34.5, but I see that you've worked on related code since then. Sorry, I thought I had updated more recently. I'll test on the current release and report back.
I got falcon virtual
to work under version 0.34.5 using supervisor
. Under this setup, my :tls
configuration block causes Errno::EISDIR
exceptions so I removed it and use the default certificate paths:
Can you show me your current falcon.rb
configuration?
Yes, by default it will use ssl/
directory for certificates.
https://github.com/socketry/falcon/blob/master/lib/falcon/configuration/tls.rb#L31-L38
So, is it working, or you still having the same issues?
I was using incorrect syntax. With corrected syntax, I am able to override the configuration. Thank you.
i have a similar issue.
i would like to move one rails app from using Puma used with ssl_bind
to Falcon and following this thread i set up a falcon.rb.
i am now trying to make it work on localhost before deploying on staging environment
# falcon.rb
load :rack, :tls
rack 'my_app', :tls do
endpoint { Async::HTTP::Endpoint.for scheme, 'localhost', port: '3000' }
ssl_certificate_path { './certificate.pem' }
ssl_private_key_path { './private.key' }
end
the certificate.pem and private.key are generated with openssl similarly to
openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 -subj "some configurations" -keyout private.key -out certificate.pem
and are present in the root dir of the rails app as well as the falcon.rb file
when doing bundle exec falcon host
the logs at startup shows:
0.0s info: Falcon::Command::Host [oid=0x210c] [pid=73062] [2021-01-04 20:44:50 +0000]
| Falcon Host v0.37.1 taking flight!
| - Configuration: falcon.rb
| - To terminate: Ctrl-C or kill 73062
| - To reload: kill -HUP 73062
0.02s info: Falcon::Service::Application [oid=0x2120] [pid=73062] [2021-01-04 20:44:50 +0000]
| Binding to #<Async::HTTP::Endpoint https://localhost:3000 {:port=>"3000"}>...
0.03s info: Falcon::Service::Application [oid=0x2120] [pid=73085] [2021-01-04 20:44:50 +0000]
when trying to connect to https://localhost:3000
i receive
some async tasks fail with
OpenSSL::SSL::SSLError: SSL_accept returned=1 errno=0 state=error: inappropriate fallback
other with
OpenSSL::SSL::SSLError: SSL_accept returned=1 errno=0 state=error: no shared cipher
I see a self_signed_tls
and lets_encrypt_tls
files in environments but i don't understand if they could be used somehow to solve my issue and how. I would be more than happy to help with the docs because i found them not really helpful in this case
For example the same error pops up if the falcon.rb is
load :rack, :lets_encrypt_tls, :supervisor
rack 'my_app', :lets_encrypt_tls do
endpoint { Async::HTTP::Endpoint.for scheme, 'localhost', port: '3000' }
end
supervisor
The tls
environment should be sufficient. Can you try using curl --insecure
and see what it prints out?
❯ curl --insecure -I https://localhost:3000
curl: (35) error:14004410:SSL routines:CONNECT_CR_SRVR_HELLO:sslv3 alert handshake failure
❯ curl --insecure https://localhost:3000
curl: (35) error:14004410:SSL routines:CONNECT_CR_SRVR_HELLO:sslv3 alert handshake failure
I see what's going on.
The normal TLS termination is at the falcon virtual load balancer.
falcon host
by default is for the internal termination.
So, you forced it to be on the network interface by specifying the endpoint, but you didn't specify the ssl_context
.
Your configuration needs to look like this:
# falcon.rb
load :rack, :tls
rack 'my_app', :tls do
endpoint do
Async::HTTP::Endpoint.for(scheme, 'localhost', port: '3000', ssl_context: ssl_context)
end
ssl_certificate_path { './certificate.pem' }
ssl_private_key_path { './private.key' }
end
We can probably make an environment specific to this use case, e.g. direct_tls
or something.
Thank you @ioquatix.
your solution proved to be working.
now running curl --insecure -I https://localhost:3000
returns the html from the root page.
trying to visit the link https://localhost:3000
with the browser would fail because the localhost is not able to verify the browser certificate and would return
NET::ERR_CERT_INVALID
i was tented to say "this is a client problem so i guess we are good to go." but now i see the same is happening when i do
bundle exec falcon serve --port 3000
after checking i see that falcon is still serving a tls connection at https://localhost:3000.
it would guess that it is not the right behavior and i suspect that something weird is happening under the hood but it is not clear to me why this is happening because as far as i understand falcon.rb
( where tls is configured ) should not be loaded at this point
when curl
ing insecure the html is returned
if you like i could try to set up a direct_tls
environment PR
falcon serve
does not use falcon.rb
, it only looks at config.ru
. falcon.rb
is only used by falcon host
which is a more complex mechanism that supports multiple hosts, rolling restarts, etc. falcon serve
will use self-signed certificates if required, which are stored in ~/.localhost
. falcon serve
will therefore serve TLS connections by default as this is required for HTTP/2.
Thank you for the explanation
so at the end i was able to fix the issue client side by setting
brave://flags/#allow-insecure-localhost
or by doing
bundle exec falcon serve --bind http://localhost:9292
It's not well documented I guess since you didn't find it but it is explained here too: https://github.com/socketry/localhost#self-signed-localhost
I'll add my case here, we use real signed certificates in development and testing and it's a must for our project, also it means each developer has it's own certificates. So far I'm only checking how we could use falcon in our environment but in order to run our certificates when using falcon serve
you just need to place your key and cert into ~/.localhost/localhost.key
(and .crt
) and it works out of the box. I didn't find any issues if I not add --hostname mydev.doman
Yes this is a totally acceptable way to do it. That .localhost
directory is considered a place for your local development certificates.
Just FYI, the directory was changed ~/.local/state/localhost.rb
.
With puma, we can use our own cert files either by URL or a config file.
URL:
config:
I wonder if falcon has the same feature.