diff --git a/.rubocop.yml b/.rubocop.yml index 5ae463f23..c8bb6119a 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -39,6 +39,9 @@ Lint/UnusedBlockArgument: Lint/UnusedMethodArgument: Enabled: false +Naming/FileName: + Enabled: false + Metrics: Enabled: false diff --git a/bundler.d/puma.rb b/bundler.d/puma.rb new file mode 100644 index 000000000..27197ce71 --- /dev/null +++ b/bundler.d/puma.rb @@ -0,0 +1,3 @@ +group :puma do + gem 'puma', '~> 4.1', :require => 'puma' +end diff --git a/config/settings.yml.example b/config/settings.yml.example index b1afd10f5..b8e8ab84f 100644 --- a/config/settings.yml.example +++ b/config/settings.yml.example @@ -7,15 +7,17 @@ #:ssl_ca_file: ssl/certs/ca.pem #:ssl_private_key: ssl/private_keys/fqdn.key -# Use this option only if you need to disable certain cipher suites. -# Note: we use the OpenSSL suite name, such as "RC4-MD5". -# The complete list of cipher suite names can be found at: -# https://www.openssl.org/docs/manmaster/man1/ciphers.html#CIPHER-SUITE-NAMES -#:ssl_disabled_ciphers: [CIPHER-SUITE-1, CIPHER-SUITE-2] +# Enabled ciphers for SSL. The complete list of cipher suite +# names and list format can be found at: +# https://www.openssl.org/docs/man1.0.2/man1/ciphers.html +# Accepts both array and colon-separated (OpenSSL) string. +# The default value (when commented out) is defined in: +# /usr/share/foreman-proxy/lib/proxy/settings/global.rb +#:ssl_enabled_ciphers: ['ECDHE-RSA-AES128-GCM-SHA256','ECDHE-RSA-AES256-GCM-SHA384','AES128-GCM-SHA256','AES256-GCM-SHA384','AES128-SHA256','AES256-SHA256','AES128-SHA','AES256-SHA'] +#:ssl_enabled_ciphers: "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256" -# Use this option only if you need to strictly specify TLS versions to be -# disabled. SSLv3 and TLS v1.0 are always disabled and cannot be configured. -# Specify versions like: '1.1', or '1.2' +# SSLv3 and TLS 1.0 and 1.1 are disabled in smart-proxy. To disable additional +# versions of TLS, use this setting. Specify versions in an array, e.g. ['1.2']. #:tls_disabled_versions: [] # Hosts which the proxy accepts connections from @@ -49,7 +51,7 @@ #:daemon_pid: /var/run/foreman-proxy/foreman-proxy.pid # host and ports configuration -# an array of interfaces to bind ports to (possible values: *, localhost, 0.0.0.0) +# an array of interfaces to bind ports to (possible values: *, localhost, 0.0.0.0, ::1, ::) #:bind_host: ['*'] # http is disabled by default. To enable, uncomment 'http_port' setting #:http_port: 8000 @@ -58,6 +60,11 @@ # default values for https_port is 8443 #:https_port: 8443 +# http server type configuration +# A string that indicates the server type (possible values: 'webrick', 'puma'). +# Default value is 'puma' +#:http_server_type: 'puma' + # Log configuration # Uncomment and modify if you want to change the location of the log file or use STDOUT or SYSLOG values #:log_file: /var/log/foreman-proxy/proxy.log diff --git a/lib/launcher.rb b/lib/launcher.rb index 020968e7e..516854a41 100644 --- a/lib/launcher.rb +++ b/lib/launcher.rb @@ -1,21 +1,28 @@ require 'proxy/log' +require 'proxy/util' +require 'proxy/sd_notify_all' require 'proxy/settings' require 'proxy/signal_handler' require 'proxy/log_buffer/trace_decorator' require 'sd_notify' - -CIPHERS = ['ECDHE-RSA-AES128-GCM-SHA256', 'ECDHE-RSA-AES256-GCM-SHA384', - 'AES128-GCM-SHA256', 'AES256-GCM-SHA384', 'AES128-SHA256', - 'AES256-SHA256', 'AES128-SHA', 'AES256-SHA'].freeze +require 'rack' +require 'webrick' module Proxy class Launcher include ::Proxy::Log + include ::Proxy::Util attr_reader :settings - def initialize(settings = SETTINGS) + def initialize(settings = Proxy::SETTINGS) @settings = settings + if @settings.http_server_type == "puma" + require 'puma' + require 'rack/handler/puma' + require 'puma-patch' + end + @servers = [] end def pid_path @@ -50,7 +57,7 @@ def http_app(http_port, plugins = http_plugins) { :app => app, - :server => :webrick, + :server => settings.http_server_type.to_sym, :DoNotListen => true, :Port => http_port, # only being used to correctly log http port being used :Logger => ::Proxy::LogBuffer::TraceDecorator.instance, @@ -70,41 +77,69 @@ def https_app(https_port, plugins = https_plugins) plugins.each { |p| instance_eval(p.https_rackup) } end - ssl_options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] - ssl_options |= OpenSSL::SSL::OP_CIPHER_SERVER_PREFERENCE if defined?(OpenSSL::SSL::OP_CIPHER_SERVER_PREFERENCE) - # This is required to disable SSLv3 on Ruby 1.8.7 - ssl_options |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2) - ssl_options |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3) - ssl_options |= OpenSSL::SSL::OP_NO_TLSv1 if defined?(OpenSSL::SSL::OP_NO_TLSv1) - ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_1 if defined?(OpenSSL::SSL::OP_NO_TLSv1_1) - - Proxy::SETTINGS.tls_disabled_versions&.each do |version| - constant = OpenSSL::SSL.const_get("OP_NO_TLSv#{version.to_s.tr('.', '_')}") rescue nil - - if constant - logger.info "TLSv#{version} will be disabled." - ssl_options |= constant - else - logger.warn "TLSv#{version} was not found." - end - end + ssl_enabled_ciphers = if settings.ssl_enabled_ciphers.is_a?(String) + settings.ssl_enabled_ciphers.split(':') + else + settings.ssl_enabled_ciphers + end - { + app_details = { :app => app, - :server => :webrick, + :server => settings.http_server_type, :DoNotListen => true, :Port => https_port, # only being used to correctly log https port being used :Logger => ::Proxy::LogBuffer::Decorator.instance, :ServerSoftware => "foreman-proxy/#{Proxy::VERSION}", - :SSLEnable => true, - :SSLVerifyClient => OpenSSL::SSL::VERIFY_PEER, - :SSLPrivateKey => load_ssl_private_key(settings.ssl_private_key), - :SSLCertificate => load_ssl_certificate(settings.ssl_certificate), - :SSLCACertificateFile => settings.ssl_ca_file, - :SSLOptions => ssl_options, - :SSLCiphers => CIPHERS - Proxy::SETTINGS.ssl_disabled_ciphers, + :SSLCiphers => ssl_enabled_ciphers, :daemonize => false, } + + case settings.http_server_type + when "webrick" + ssl_options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] + ssl_options |= OpenSSL::SSL::OP_CIPHER_SERVER_PREFERENCE if defined?(OpenSSL::SSL::OP_CIPHER_SERVER_PREFERENCE) + ssl_options |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2) + ssl_options |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3) + ssl_options |= OpenSSL::SSL::OP_NO_TLSv1 if defined?(OpenSSL::SSL::OP_NO_TLSv1) + ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_1 if defined?(OpenSSL::SSL::OP_NO_TLSv1_1) + + if settings.tls_disabled_versions + settings.tls_disabled_versions&.each do |version| + constant = OpenSSL::SSL.const_get("OP_NO_TLSv#{version.to_s.tr('.', '_')}") rescue nil + + if constant + logger.info "TLSv#{version} will be disabled." + ssl_options |= constant + else + logger.warn "TLSv#{version} was not found." + end + end + end + + app_details[:SSLEnable] = true + app_details[:SSLVerifyClient] = OpenSSL::SSL::VERIFY_PEER + app_details[:SSLCACertificateFile] = settings.ssl_ca_file + app_details[:SSLPrivateKey] = load_ssl_private_key(settings.ssl_private_key) + app_details[:SSLCertificate] = load_ssl_certificate(settings.ssl_certificate) + app_details[:SSLOptions] = ssl_options + when "puma" + # https://github.com/puma/puma#binding-tcp--sockets + app_details[:SSLArgs] = { + :ca => settings.ssl_ca_file, + :key => settings.ssl_private_key, + :cert => settings.ssl_certificate, + :verify_mode => 'peer', + } + app_details[:SSLArgs][:no_tlsv1] = "true" + app_details[:SSLArgs][:no_tlsv1_1] = "true" + # no additional TLS versions via tls_disabled_versions can be currently disabled for puma + if settings.ssl_enabled_ciphers + app_details[:SSLArgs][:ssl_cipher_list] = ssl_enabled_ciphers.join(':') + end + else + raise "Unknown http_server_type: #{settings.http_server_type}" + end + app_details end def load_ssl_private_key(path) @@ -152,13 +187,87 @@ def write_pid retry end - def webrick_server(app, addresses, port) + def add_puma_server_callback(sd_notify) + events = ::Puma::Events.new(::Proxy::LogBuffer::Decorator.instance, ::Proxy::LogBuffer::Decorator.instance) + events.register(:state) do |status| + if status == :running + sd_notify.ready_all { sd_notify.status("Started all #{sd_notify.total} threads, ready", logger) } + sd_notify.status("Started, #{sd_notify.pending} threads to go", logger) if sd_notify.pending > 0 + end + end + events + end + + def format_ip_for_url(address) + addr = IPAddr.new(address) + addr.ipv6? ? "[#{addr}]" : addr.to_s + rescue IPAddr::InvalidAddressError + address + end + + def add_puma_server(app, address, port, conn_type, sd_notify) + address = format_ip_for_url(address) + logger.debug "Launching Puma listener at #{address} port #{port}" + if conn_type == :ssl + host = "ssl://#{address}:#{port}/?#{hash_to_query_string(app[:SSLArgs])}" + else + host = address + end + logger.debug "Host URL: #{host}" + # the following lines are from lib/rack/handler/puma.rb#run + options = {Verbose: true, Port: port, Host: host} + conf = Rack::Handler::Puma.config(app[:app], options) + # install callback to notify systemd + events = add_puma_server_callback(sd_notify) + launcher = ::Puma::Launcher.new(conf, :events => events) + @servers << launcher + launcher.run + end + + def add_webrick_server_callback(app, sd_notify) + app[:StartCallback] = lambda do + sd_notify.ready_all { sd_notify.status("Started all #{sd_notify.total} threads, ready", logger) } + sd_notify.status("Started, #{sd_notify.pending} threads to go", logger) if sd_notify.pending > 0 + end + end + + def add_webrick_server(app, addresses, port, sd_notify) + # install callback to notify systemd + add_webrick_server_callback(app, sd_notify) + # initialize the server server = ::WEBrick::HTTPServer.new(app) - addresses.each { |a| server.listen(a, port) } - server.mount "/", Rack::Handler::WEBrick, app[:app] + addresses.each do |address| + logger.debug "Launching Webrick listener at #{address} port #{port}" + server.listen(address, port) + end + server.mount '/', Rack::Handler::WEBrick, app[:app] server end + def ipv6_enabled? + File.exist?('/proc/net/if_inet6') || (RUBY_PLATFORM =~ /cygwin|mswin|mingw|bccwin|wince|emx/) + end + + def add_threaded_server(server_name, conn_type, app, addresses, port, sd_notify) + result = [] + case server_name + when "webrick" + result << Thread.new do + @servers << add_webrick_server(app, addresses, port, sd_notify).start + end + when "puma" + addresses.flatten.each do |address| + # Puma listens both on IPv4 and IPv6 on '::', there is no way to make Puma + # to listen only on IPv6. + address = '::' if address == '*' && ipv6_enabled? + result << Thread.new do + add_puma_server(app, address, port, conn_type, sd_notify) + end + end + end + result + end + def launch raise Exception.new("Both http and https are disabled, unable to start.") unless http_enabled? || https_enabled? @@ -172,14 +281,35 @@ def launch http_app = http_app(settings.http_port) https_app = https_app(settings.https_port) - install_webrick_callback!(http_app, https_app) - - t1 = Thread.new { webrick_server(https_app, settings.bind_host, settings.https_port).start } unless https_app.nil? - t2 = Thread.new { webrick_server(http_app, settings.bind_host, settings.http_port).start } unless http_app.nil? + hosts = settings.bind_host.is_a?(Array) ? settings.bind_host.size : 1 + expected = [http_app, https_app].compact.size * hosts + logger.debug "Expected number of instances to launch: #{expected}" + sd_notify = Proxy::SdNotifyAll.new(expected) + sd_notify.status("Starting #{expected} threads", logger) + + http_server_name = settings.http_server_type + https_server_name = settings.http_server_type + threads = [] + if https_app + threads += add_threaded_server(https_server_name, + :ssl, + https_app, + settings.bind_host, + settings.https_port, + sd_notify) + end - Proxy::SignalHandler.install_traps + if http_app + threads += add_threaded_server(http_server_name, + :tcp, + http_app, + settings.bind_host, + settings.http_port, + sd_notify) + end - (t1 || t2).join + Proxy::SignalHandler.install_traps(@servers) + threads.each(&:join) rescue SignalException => e logger.debug("Caught #{e}. Exiting") raise @@ -191,28 +321,5 @@ def launch puts "Errors detected on startup, see log for details. Exiting: #{e}" exit(1) end - - def install_webrick_callback!(*apps) - apps.compact! - - # track how many webrick apps are still starting up - @pending_webrick = apps.size - @pending_webrick_lock = Mutex.new - - apps.each do |app| - # add a callback to each server, decrementing the pending counter - app[:StartCallback] = lambda do - @pending_webrick_lock.synchronize do - @pending_webrick -= 1 - launched(apps) if @pending_webrick.zero? - end - end - end - end - - def launched(apps) - logger.info("Smart proxy has launched on #{apps.size} socket(s), waiting for requests") - SdNotify.ready - end end end diff --git a/lib/proxy/helpers.rb b/lib/proxy/helpers.rb index 7f7d6038b..cc6d88d2c 100644 --- a/lib/proxy/helpers.rb +++ b/lib/proxy/helpers.rb @@ -29,8 +29,8 @@ def log_halt(code = nil, exception_or_msg = nil, custom_msg = nil) end # read the HTTPS client certificate from the environment and extract its CN - def https_cert_cn - certificate_raw = request.env['SSL_CLIENT_CERT'].to_s + def https_cert_cn(request) + certificate_raw = ssl_client_cert(request) log_halt 403, 'could not read client cert from environment' if certificate_raw.empty? begin @@ -106,4 +106,19 @@ def remote_fqdn(forward_verify = true) fqdn end end + + def ssl_client_cert(request) + if request.env.key?('SSL_CLIENT_CERT') + request.env['SSL_CLIENT_CERT'].to_s + elsif request.env.key?('puma.peercert') + request.env['puma.peercert'].to_s + else + '' + end + end + + def https?(request) + # test env variable for puma and also webrick + request.env['HTTPS'].to_s == 'https' || request.env['HTTPS'].to_s =~ /yes|on|1/ + end end diff --git a/lib/proxy/log_buffer/decorator.rb b/lib/proxy/log_buffer/decorator.rb index f0e30199e..831c034ab 100644 --- a/lib/proxy/log_buffer/decorator.rb +++ b/lib/proxy/log_buffer/decorator.rb @@ -121,6 +121,12 @@ def exception(context_message, exception, options = {}) end end + # for Puma Event compatibility + def sync=(_) + end + alias_method :log, :info + alias_method :puts, :info + def method_missing(symbol, *args) @logger.send(symbol, *args) end diff --git a/lib/proxy/request.rb b/lib/proxy/request.rb index b70259dba..4991d3ebe 100644 --- a/lib/proxy/request.rb +++ b/lib/proxy/request.rb @@ -5,17 +5,15 @@ module Proxy::HttpRequest class ForemanRequestFactory + include Proxy::Util + def initialize(base_uri) @base_uri = base_uri end - def query_string(input = {}) - input.compact.map { |k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v)}" }.join("&") - end - def create_get(path, query = {}, headers = {}) uri = uri(path) - req = Net::HTTP::Get.new("#{uri.path || '/'}?#{query_string(query)}") + req = Net::HTTP::Get.new("#{uri.path || '/'}?#{hash_to_query_string(query)}") req = add_headers(req, headers) req end @@ -35,7 +33,7 @@ def add_headers(req, headers = {}) def create_post(path, body, headers = {}, query = {}) uri = uri(path) - uri.query = query_string(query) + uri.query = hash_to_query_string(query) req = Net::HTTP::Post.new(uri) req = add_headers(req, headers) req.body = body diff --git a/lib/proxy/sd_notify_all.rb b/lib/proxy/sd_notify_all.rb new file mode 100644 index 000000000..703bdf46b --- /dev/null +++ b/lib/proxy/sd_notify_all.rb @@ -0,0 +1,44 @@ +require 'sd_notify' +require 'monitor' + +module Proxy + class SdNotifyAll + def initialize(number) + @pending = @total = number + @pending_and_total_lock = Monitor.new + end + + def status(msg, logger = nil) + logger&.info(msg) + SdNotify.status(msg) + end + + def ready_all(decrement = 1) + @pending_and_total_lock.synchronize do + @pending -= decrement + if @pending.zero? + yield if block_given? + ready + end + end + end + + def total + @pending_and_total_lock.synchronize do + @total + end + end + + def pending + @pending_and_total_lock.synchronize do + @pending + end + end + + private + + def ready + SdNotify.ready + end + end +end diff --git a/lib/proxy/settings/global.rb b/lib/proxy/settings/global.rb index f3a535dda..c5640d950 100644 --- a/lib/proxy/settings/global.rb +++ b/lib/proxy/settings/global.rb @@ -2,6 +2,7 @@ module ::Proxy::Settings class Global < ::OpenStruct DEFAULT_SETTINGS = { :settings_directory => Pathname.new(__FILE__).join("..", "..", "..", "..", "config", "settings.d").expand_path.to_s, + :http_server_type => "puma", :https_port => 8443, :log_file => "/var/log/foreman-proxy/proxy.log", :file_rolling_keep => 6, @@ -16,7 +17,7 @@ class Global < ::OpenStruct :bind_host => ["*"], :log_buffer => 2000, :log_buffer_errors => 1000, - :ssl_disabled_ciphers => [], + :ssl_enabled_ciphers => ['ECDHE-RSA-AES128-GCM-SHA256', 'ECDHE-RSA-AES256-GCM-SHA384', 'AES128-GCM-SHA256', 'AES256-GCM-SHA384', 'AES128-SHA256', 'AES256-SHA256', 'AES128-SHA', 'AES256-SHA'], :tls_disabled_versions => [], :dns_resolv_timeouts => [5, 8, 13], # Ruby default is [5, 20, 40] which is a bit too much for us } diff --git a/lib/proxy/signal_handler.rb b/lib/proxy/signal_handler.rb index cb9aedb9b..2e6314e16 100644 --- a/lib/proxy/signal_handler.rb +++ b/lib/proxy/signal_handler.rb @@ -3,11 +3,11 @@ class Proxy::SignalHandler include ::Proxy::Log - def self.install_traps + def self.install_traps(servers) handler = new handler.install_ttin_trap unless RUBY_PLATFORM =~ /mingw/ - handler.install_int_trap - handler.install_term_trap + handler.install_int_trap(servers) + handler.install_term_trap(servers) handler.install_usr1_trap unless RUBY_PLATFORM =~ /mingw/ end @@ -25,12 +25,28 @@ def install_ttin_trap end end - def install_int_trap - trap(:INT) { exit(0) } + def install_int_trap(servers) + trap(:INT) do + servers&.each do |server| + server.shutdown if + server&.respond_to?(:shutdown) + server.stop if + server&.respond_to?(:stop) + end + exit(0) + end end - def install_term_trap - trap(:TERM) { exit(0) } + def install_term_trap(servers) + trap(:TERM) do + servers&.each do |server| + server.shutdown if + server&.respond_to?(:shutdown) + server.stop if + server&.respond_to?(:stop) + end + exit(0) + end end def install_usr1_trap diff --git a/lib/proxy/util.rb b/lib/proxy/util.rb index 6d9a3c16f..6251a0659 100644 --- a/lib/proxy/util.rb +++ b/lib/proxy/util.rb @@ -103,4 +103,8 @@ def strict_encode64(str) Base64.encode64(str).delete("\n") end end + + def hash_to_query_string(input = {}) + input.compact.map { |k, v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v)}" }.join("&") + end end diff --git a/lib/puma-patch.rb b/lib/puma-patch.rb new file mode 100644 index 000000000..b6af671dc --- /dev/null +++ b/lib/puma-patch.rb @@ -0,0 +1,7 @@ +module Puma + class Launcher + # Smart Proxy has its own launching and shutdown code + def setup_signals + end + end +end diff --git a/lib/sinatra/authorization.rb b/lib/sinatra/authorization.rb index 7be503e8c..5a541e27b 100644 --- a/lib/sinatra/authorization.rb +++ b/lib/sinatra/authorization.rb @@ -32,8 +32,8 @@ def do_authorize_with_trusted_hosts if trusted_hosts logger.debug "verifying remote client #{request.env['REMOTE_ADDR']} against trusted_hosts #{trusted_hosts}" - if ['yes', 'on', 1].include? request.env['HTTPS'].to_s - fqdn = https_cert_cn + if https?(request) + fqdn = https_cert_cn(request) else fqdn = remote_fqdn(Proxy::SETTINGS.forward_verify) end @@ -46,8 +46,8 @@ def do_authorize_with_trusted_hosts end def do_authorize_with_ssl_client - if ['yes', 'on', '1'].include? request.env['HTTPS'].to_s - if request.env['SSL_CLIENT_CERT'].to_s.empty? + if https?(request) + if ssl_client_cert(request).empty? log_halt 403, "No client SSL certificate supplied" end else diff --git a/test/launcher_test.rb b/test/launcher_test.rb index ef9cc7621..e00b653d9 100644 --- a/test/launcher_test.rb +++ b/test/launcher_test.rb @@ -1,5 +1,8 @@ require 'test_helper' require 'launcher' +require 'puma' +require 'rack/handler/puma' +require 'puma-patch' class LauncherTest < Test::Unit::TestCase def setup @@ -40,19 +43,22 @@ def test_check_pid_exits_program FileUtils.rm_f @launcher.pid_path end - def test_install_webrick_callback - app1 = {app: 1} - app2 = {app: 2} - @launcher.install_webrick_callback!(app1, nil, app2) - @launcher.expects(:launched).never - app1[:StartCallback].call - @launcher.expects(:launched).with([app1, app2]) - app2[:StartCallback].call + def test_add_webrick_server_callback + an_app = {} + sdn = Proxy::SdNotifyAll.new(2) + sdn.stubs(:status) + sdn.expects(:ready) + @launcher.add_webrick_server_callback(an_app, sdn) + an_app[:StartCallback].call + an_app[:StartCallback].call end - def test_launched_with_sdnotify - @launcher.logger.expects(:info).with(includes('2 socket(s)')) - ::SdNotify.expects(:ready) - @launcher.launched([:app1, :app2]) + def test_add_puma_server_callback + sdn = Proxy::SdNotifyAll.new(2) + sdn.stubs(:status) + sdn.expects(:ready) + events = @launcher.add_puma_server_callback(sdn) + events.fire(:state, :running) + events.fire(:state, :running) end end diff --git a/test/sinatra/ssl_client_verification_integration_test.rb b/test/sinatra/ssl_client_verification_integration_test.rb index 98493195f..f1b9579e0 100644 --- a/test/sinatra/ssl_client_verification_integration_test.rb +++ b/test/sinatra/ssl_client_verification_integration_test.rb @@ -43,7 +43,7 @@ def test_https_cert_from_different_authority http.cert = OpenSSL::X509::Certificate.new(File.read(File.join(fixtures, 'certs', 'badclient.example.com.pem'))) http.key = OpenSSL::PKey::RSA.new(File.read(File.join(fixtures, 'private_keys', 'badclient.example.com.pem'))) http.verify_mode = OpenSSL::SSL::VERIFY_PEER - assert_raise OpenSSL::SSL::SSLError do + assert_raise EOFError, OpenSSL::SSL::SSLError do http.get('/') end end diff --git a/test/sinatra/ssl_client_verification_test.rb b/test/sinatra/ssl_client_verification_test.rb index ab8d5bc3e..472a046d2 100644 --- a/test/sinatra/ssl_client_verification_test.rb +++ b/test/sinatra/ssl_client_verification_test.rb @@ -16,15 +16,20 @@ def test_http assert last_response.ok? end - ['yes', 'on', '1'].each do |yes| + ['yes', 'on', '1', 'https'].each do |yes| define_method("test_https_no_cert_https_#{yes}") do get '/test', {}, 'HTTPS' => yes assert last_response.forbidden? end end - def test_https_cert - get '/test', {}, 'HTTPS' => 'on', 'SSL_CLIENT_CERT' => '...' + def test_https_cert_webrick + get '/test', {}, 'HTTPS' => 'yes', 'SSL_CLIENT_CERT' => '...' + assert last_response.ok? + end + + def test_https_cert_puma + get '/test', {}, 'HTTPS' => 'https', 'puma.peercert' => '...' assert last_response.ok? end diff --git a/test/test_helper.rb b/test/test_helper.rb index dcd5bf95e..d396cb047 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -44,7 +44,13 @@ def launch(protocol: 'https', plugins: [], settings: {}) @t = Thread.new do launcher = Proxy::Launcher.new(@settings) app = launcher.public_send("#{protocol}_app", port, plugins) - launcher.webrick_server(app.merge(AccessLog: [Logger.new('/dev/null')]), ['localhost'], port).start + case @settings.http_server_type + when "webrick" + launcher.add_webrick_server(app.merge(AccessLog: [Logger.new('/dev/null')]), "localhost", port, Proxy::SdNotifyAll.new(1)).start + when "puma" + conn_type = (protocol == "https") ? :ssl : :tcp + launcher.add_puma_server(app.merge(AccessLog: [Logger.new('/dev/null')]), "localhost", port, conn_type, Proxy::SdNotifyAll.new(1)).start + end end Timeout.timeout(2) do sleep(0.1) until can_connect?('localhost', @settings.send("#{protocol}_port"))