From 1a8bd06193631b31ee59459b212e0e441429964c Mon Sep 17 00:00:00 2001 From: Patrik Ragnarsson Date: Tue, 18 Jan 2022 19:37:28 +0100 Subject: [PATCH] Support `localhost` integration in `ssl_bind` (#2764) Close #2708 Close #2711 Co-authored-by: Marcin Olichwirowicz Co-authored-by: Marcin Olichwirowicz --- lib/puma/binder.rb | 4 +++- lib/puma/dsl.rb | 23 ++++++++++++++++------- test/config/ssl_self_signed_config.rb | 7 +++++++ test/test_config.rb | 16 ++++++++++++++++ test/test_integration_ssl.rb | 24 ++++++++++++++++++++++++ 5 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 test/config/ssl_self_signed_config.rb diff --git a/lib/puma/binder.rb b/lib/puma/binder.rb index a9d2bda303..300ece14da 100644 --- a/lib/puma/binder.rb +++ b/lib/puma/binder.rb @@ -233,7 +233,9 @@ def parse(binds, logger, log_msg = 'Listening') # If key and certs are not defined and localhost gem is required. # localhost gem will be used for self signed # Load localhost authority if not loaded. - ctx = localhost_authority && localhost_authority_context if params.empty? + if params.values_at('cert', 'key').all? { |v| v.to_s.empty? } + ctx = localhost_authority && localhost_authority_context + end ctx ||= begin diff --git a/lib/puma/dsl.rb b/lib/puma/dsl.rb index 9a0573a72c..3140e54e6d 100644 --- a/lib/puma/dsl.rb +++ b/lib/puma/dsl.rb @@ -193,7 +193,7 @@ def load(file) end # Bind the server to +url+. "tcp://", "unix://" and "ssl://" are the only - # accepted protocols. Multiple urls can be bound to, calling `bind` does + # accepted protocols. Multiple urls can be bound to, calling +bind+ does # not overwrite previous bindings. # # The default is "tcp://0.0.0.0:9292". @@ -438,8 +438,15 @@ def threads(min, max) @options[:max_threads] = max end - # Instead of `bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path'` you - # can also use the this method. + # Instead of using +bind+ and manually constructing a URI like: + # + # bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path' + # + # you can use the this method. + # + # When binding on localhost you don't need to specify +cert+ and +key+, + # Puma will assume you are using the +localhost+ gem and try to load the + # appropriate files. # # @example # ssl_bind '127.0.0.1', '9292', { @@ -450,21 +457,23 @@ def threads(min, max) # verification_flags: flags, # optional, not supported by JRuby # } # - # Alternatively, you can provide the cert_pem and key_pem: - # @example + # @example Using self-signed certificate with the +localhost+ gem: + # ssl_bind '127.0.0.1', '9292' + # + # @example Alternatively, you can provide +cert_pem+ and +key_pem+: # ssl_bind '127.0.0.1', '9292', { # cert_pem: File.read(path_to_cert), # key_pem: File.read(path_to_key), # } # - # @example For JRuby, two keys are required: keystore & keystore_pass. + # @example For JRuby, two keys are required: +keystore+ & +keystore_pass+ # ssl_bind '127.0.0.1', '9292', { # keystore: path_to_keystore, # keystore_pass: password, # ssl_cipher_list: cipher_list, # optional # verify_mode: verify_mode # default 'none' # } - def ssl_bind(host, port, opts) + def ssl_bind(host, port, opts = {}) add_pem_values_to_options_store(opts) bind self.class.ssl_bind_str(host, port, opts) end diff --git a/test/config/ssl_self_signed_config.rb b/test/config/ssl_self_signed_config.rb new file mode 100644 index 0000000000..d70b51d169 --- /dev/null +++ b/test/config/ssl_self_signed_config.rb @@ -0,0 +1,7 @@ +require "localhost" + +ssl_bind "0.0.0.0", 9292 + +app do |env| + [200, {}, ["self-signed certificate app"]] +end diff --git a/test/test_config.rb b/test/test_config.rb index 90ace92c72..d2ced63f91 100644 --- a/test/test_config.rb +++ b/test/test_config.rb @@ -58,6 +58,22 @@ def test_ssl_configuration_from_DSL assert_equal [200, {}, ["embedded app"]], app.call({}) end + def test_ssl_self_signed_configuration_from_DSL + skip_if :jruby + skip_unless :ssl + conf = Puma::Configuration.new do |config| + config.load "test/config/ssl_self_signed_config.rb" + end + + conf.load + + bind_configuration = conf.options.file_options[:binds].first + app = conf.app + + ssl_binding = "ssl://0.0.0.0:9292?cert=&key=&verify_mode=none" + assert_equal [ssl_binding], conf.options[:binds] + end + def test_ssl_bind skip_if :jruby skip_unless :ssl diff --git a/test/test_integration_ssl.rb b/test/test_integration_ssl.rb index 9c3409a7b5..07dc0972e8 100644 --- a/test/test_integration_ssl.rb +++ b/test/test_integration_ssl.rb @@ -108,6 +108,30 @@ def test_ssl_run_with_pem activate_control_app 'tcp://#{HOST}:#{control_tcp_port}', { auth_token: '#{TOKEN}' } +app do |env| + [200, {}, [env['rack.url_scheme']]] +end +RUBY + + with_server(config) do |http| + body = nil + http.start do + req = Net::HTTP::Get.new '/', {} + http.request(req) { |resp| body = resp.body } + end + assert_equal 'https', body + end + end + + def test_ssl_run_with_localhost_authority + skip_if :jruby + + config = <