Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to use custom certification files? #75

Open
tonytonyjan opened this issue Sep 11, 2019 · 24 comments
Open

how to use custom certification files? #75

tonytonyjan opened this issue Sep 11, 2019 · 24 comments

Comments

@tonytonyjan
Copy link

tonytonyjan commented Sep 11, 2019

With puma, we can use our own cert files either by URL or a config file.

URL:

$ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert'

config:

ssl_bind '127.0.0.1', '9292', {
  cert: path_to_cert,
  key: path_to_key,
}

I wonder if falcon has the same feature.

@wtn
Copy link

wtn commented Feb 5, 2020

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

@ioquatix
Copy link
Member

ioquatix commented Feb 5, 2020

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:

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))}

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.

@ioquatix
Copy link
Member

ioquatix commented Feb 5, 2020

Also if possible, once you have it working can you let me know and can you help with documentation?

@wtn
Copy link

wtn commented Feb 5, 2020

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 }.

@ioquatix
Copy link
Member

ioquatix commented Feb 5, 2020

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?

@wtn
Copy link

wtn commented Feb 6, 2020

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.

@ioquatix
Copy link
Member

ioquatix commented Feb 6, 2020

Interesting, I'll probably need to take a closer look. Thanks for reporting back.

@wtn
Copy link

wtn commented Mar 9, 2020

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.

@ioquatix
Copy link
Member

ioquatix commented Mar 9, 2020

Can you let me know what version of falcon you are using?

@wtn
Copy link

wtn commented Mar 9, 2020

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.

@wtn
Copy link

wtn commented Mar 10, 2020

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:

  • ssl/certificate.pem
  • ssl/private.key

@ioquatix
Copy link
Member

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?

@wtn
Copy link

wtn commented Mar 10, 2020

I was using incorrect syntax. With corrected syntax, I am able to override the configuration. Thank you.

@nic-lan
Copy link

nic-lan commented Jan 4, 2021

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

Screen Shot 2021-01-04 at 20 59 34

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

Environment

  • falcon (0.37.1)
  • rails (6.1.0)
  • ruby 2.7.2
  • browsers affected:
    • Brave: Version 1.18.77 Chromium: 87.0.4280.101 (Official Build) (x86_64)
    • Safari Version 14.0.1 (15610.2.11.51.10, 15610)

Note

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

@ioquatix
Copy link
Member

ioquatix commented Jan 5, 2021

The tls environment should be sufficient. Can you try using curl --insecure and see what it prints out?

@nic-lan
Copy link

nic-lan commented Jan 5, 2021

❯ 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

@ioquatix
Copy link
Member

ioquatix commented Jan 6, 2021

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.

@nic-lan
Copy link

nic-lan commented Jan 6, 2021

Thank you @ioquatix.

your solution proved to be working.

now running curl --insecure -I https://localhost:3000 returns the html from the root page.

Note 1

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 curling insecure the html is returned

Note 2

if you like i could try to set up a direct_tls environment PR

@ioquatix
Copy link
Member

ioquatix commented Jan 6, 2021

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.

@nic-lan
Copy link

nic-lan commented Jan 7, 2021

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

@ioquatix
Copy link
Member

ioquatix commented Jan 8, 2021

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

@troex
Copy link

troex commented Jun 17, 2021

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

@ioquatix
Copy link
Member

Yes this is a totally acceptable way to do it. That .localhost directory is considered a place for your local development certificates.

@ioquatix
Copy link
Member

Just FYI, the directory was changed ~/.local/state/localhost.rb.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants