From c2f6803d0097cb0501bc8f82780005dfe4babbec Mon Sep 17 00:00:00 2001 From: 284km Date: Sat, 25 Nov 2017 00:14:23 +0900 Subject: [PATCH 1/4] Add support for specifying ssl ciphers via :binds parameters --- ext/puma_http11/mini_ssl.c | 11 ++++++++++- ext/puma_http11/org/jruby/puma/MiniSSL.java | 6 ++++++ lib/puma/binder.rb | 2 ++ lib/puma/minissl.rb | 2 ++ 4 files changed, 20 insertions(+), 1 deletion(-) diff --git a/ext/puma_http11/mini_ssl.c b/ext/puma_http11/mini_ssl.c index cac1aa93e7..68c3a35050 100644 --- a/ext/puma_http11/mini_ssl.c +++ b/ext/puma_http11/mini_ssl.c @@ -161,6 +161,9 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) { ID sym_verify_mode = rb_intern("verify_mode"); VALUE verify_mode = rb_funcall(mini_ssl_ctx, sym_verify_mode, 0); + ID sym_ssl_cipher_filter = rb_intern("ssl_cipher_filter"); + VALUE ssl_cipher_filter = rb_funcall(mini_ssl_ctx, sym_ssl_cipher_filter, 0); + ctx = SSL_CTX_new(SSLv23_server_method()); conn->ctx = ctx; @@ -175,7 +178,13 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) { SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION); SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); - SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH"); + if (!NIL_P(ssl_cipher_filter)) { + StringValue(ssl_cipher_filter); + SSL_CTX_set_cipher_list(ctx, RSTRING_PTR(ssl_cipher_filter)); + } + else { + SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH"); + } DH *dh = get_dh1024(); SSL_CTX_set_tmp_dh(ctx, dh); diff --git a/ext/puma_http11/org/jruby/puma/MiniSSL.java b/ext/puma_http11/org/jruby/puma/MiniSSL.java index c3b5d63fa8..5a558f4bc3 100644 --- a/ext/puma_http11/org/jruby/puma/MiniSSL.java +++ b/ext/puma_http11/org/jruby/puma/MiniSSL.java @@ -170,6 +170,12 @@ public IRubyObject initialize(ThreadContext threadContext, IRubyObject miniSSLCo engine.setNeedClientAuth(true); } + IRubyObject sslCipherListObject = miniSSLContext.callMethod(threadContext, "ssl_cipher_list"); + if (!sslCipherListObject.isNil()) { + String[] sslCipherList = sslCipherListObject.convertToString().asJavaString().split(","); + engine.setEnabledCipherSuites(sslCipherList); + } + SSLSession session = engine.getSession(); inboundNetData = new MiniSSLBuffer(session.getPacketBufferSize()); outboundAppData = new MiniSSLBuffer(session.getApplicationBufferSize()); diff --git a/lib/puma/binder.rb b/lib/puma/binder.rb index 7c68eebe71..581d55ad08 100644 --- a/lib/puma/binder.rb +++ b/lib/puma/binder.rb @@ -162,6 +162,7 @@ def parse(binds, logger) end ctx.keystore_pass = params['keystore-pass'] + ctx.ssl_cipher_list = params['ssl_cipher_list'] if params['ssl_cipher_list'] else unless params['key'] @events.error "Please specify the SSL key via 'key='" @@ -182,6 +183,7 @@ def parse(binds, logger) end ctx.ca = params['ca'] if params['ca'] + ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter'] end if params['verify_mode'] diff --git a/lib/puma/minissl.rb b/lib/puma/minissl.rb index 9cb08a9278..a6d6df11b4 100644 --- a/lib/puma/minissl.rb +++ b/lib/puma/minissl.rb @@ -172,6 +172,7 @@ class Context # jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair attr_reader :keystore attr_accessor :keystore_pass + attr_accessor :ssl_cipher_list def keystore=(keystore) raise ArgumentError, "No such keystore file '#{keystore}'" unless File.exist? keystore @@ -187,6 +188,7 @@ def check attr_reader :key attr_reader :cert attr_reader :ca + attr_accessor :ssl_cipher_filter def key=(key) raise ArgumentError, "No such key file '#{key}'" unless File.exist? key From 0de155b62793623cbc794bed6bee5da6bbd8a236 Mon Sep 17 00:00:00 2001 From: Eleanor Allison Date: Fri, 1 Dec 2017 18:56:48 +0000 Subject: [PATCH 2/4] Add documentation and tests for change --- Gemfile | 1 + README.md | 12 +++++++++++- lib/puma/binder.rb | 1 + test/test_binder.rb | 30 +++++++++++++++++++++++++++++- 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 299e21fb0c..13d3621a16 100644 --- a/Gemfile +++ b/Gemfile @@ -8,6 +8,7 @@ gem "rake-compiler" gem "rack", "< 3.0" gem "minitest", "~> 5.9" gem "minitest-retry" +gem "pry-byebug" gem "jruby-openssl", :platform => "jruby" diff --git a/README.md b/README.md index 1d7a8e8a75..2a3c7217fa 100644 --- a/README.md +++ b/README.md @@ -157,10 +157,20 @@ $ puma -b 'unix:///var/run/puma.sock?umask=0111' ``` Need a bit of security? Use SSL sockets: - ``` $ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert' ``` +#### Controlling SSL Cipher Suites +Need to use or avoid specific SSL cipher suites? Use ssl_cipher_filter or ssl_cipher_list options. +#####Ruby: +``` +$ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&ssl_cipher_filter=!aNULL:AES+SHA' +``` +#####JRuby: +``` +$ puma -b 'ssl://127.0.0.1:9292?keystore=path_to_keystore&keystore-pass=keystore_password&ssl_cipher_list=TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA' +``` +See https://www.openssl.org/docs/man1.0.2/apps/ciphers.html for cipher filter format and full list of cipher suites. ### Control/Status Server diff --git a/lib/puma/binder.rb b/lib/puma/binder.rb index 581d55ad08..bcaeeec0c3 100644 --- a/lib/puma/binder.rb +++ b/lib/puma/binder.rb @@ -315,6 +315,7 @@ def add_ssl_listener(host, port, ctx, s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true) s.listen backlog + ssl = MiniSSL::Server.new s, ctx env = @proto_env.dup env[HTTPS_KEY] = HTTPS diff --git a/test/test_binder.rb b/test/test_binder.rb index 224442cc5c..38a394f41c 100644 --- a/test/test_binder.rb +++ b/test/test_binder.rb @@ -28,4 +28,32 @@ def test_localhost_addresses_dont_alter_listeners_for_ssl_addresses assert_equal [], @binder.listeners end -end + + def test_binder_parses_ssl_cipher_filter + skip_on_appveyor + skip_on_jruby + + key = File.expand_path "../../examples/puma/puma_keypair.pem", __FILE__ + cert = File.expand_path "../../examples/puma/cert_puma.pem", __FILE__ + ssl_cipher_filter = "AES@STRENGTH" + + @binder.parse(["ssl://0.0.0.0?key=#{key}&cert=#{cert}&ssl_cipher_filter=#{ssl_cipher_filter}"], @events) + + ssl = @binder.instance_variable_get(:@ios)[0] + ctx = ssl.instance_variable_get(:@ctx) + assert_equal(ssl_cipher_filter, ctx.ssl_cipher_filter) + end + + def test_binder_parses_jruby_ssl_options + skip unless Puma.jruby? + + keystore = File.expand_path "../../examples/puma/keystore.jks" + ssl_cipher_list = "TLS_DHE_RSA_WITH_DES_CBC_SHA,TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA" + @binder.parse(["ssl://0.0.0.0?keystore=#{keystore}&ssl_cipher_list=#{ssl_cipher_list}"], @events) + + ssl= @binder.instance_variable_get(:@ios)[0] + ctx = ssl.instance_variable_get(:@ctx) + assert_equal(keystore, ctx.keystore) + assert_equal(ssl_cipher_list, ctx.ssl_cipher_list) + end +end \ No newline at end of file From 98ab5f62ede56324562636e02d9b40d8dd0e7596 Mon Sep 17 00:00:00 2001 From: Eleanor Allison Date: Fri, 1 Dec 2017 19:06:33 +0000 Subject: [PATCH 3/4] Fix rubocop violations that don't get run as part of test target --- Gemfile | 1 - test/test_binder.rb | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 13d3621a16..299e21fb0c 100644 --- a/Gemfile +++ b/Gemfile @@ -8,7 +8,6 @@ gem "rake-compiler" gem "rack", "< 3.0" gem "minitest", "~> 5.9" gem "minitest-retry" -gem "pry-byebug" gem "jruby-openssl", :platform => "jruby" diff --git a/test/test_binder.rb b/test/test_binder.rb index 38a394f41c..4b1753e078 100644 --- a/test/test_binder.rb +++ b/test/test_binder.rb @@ -46,7 +46,7 @@ def test_binder_parses_ssl_cipher_filter def test_binder_parses_jruby_ssl_options skip unless Puma.jruby? - + keystore = File.expand_path "../../examples/puma/keystore.jks" ssl_cipher_list = "TLS_DHE_RSA_WITH_DES_CBC_SHA,TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA" @binder.parse(["ssl://0.0.0.0?keystore=#{keystore}&ssl_cipher_list=#{ssl_cipher_list}"], @events) @@ -56,4 +56,4 @@ def test_binder_parses_jruby_ssl_options assert_equal(keystore, ctx.keystore) assert_equal(ssl_cipher_list, ctx.ssl_cipher_list) end -end \ No newline at end of file +end From 6ba53b0e7c63db984baa1e0f2ca4cc45c4776f6e Mon Sep 17 00:00:00 2001 From: Eleanor Allison Date: Mon, 4 Dec 2017 19:39:17 +0000 Subject: [PATCH 4/4] Fix path of test file --- test/test_binder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_binder.rb b/test/test_binder.rb index 4b1753e078..21901dd960 100644 --- a/test/test_binder.rb +++ b/test/test_binder.rb @@ -47,7 +47,7 @@ def test_binder_parses_ssl_cipher_filter def test_binder_parses_jruby_ssl_options skip unless Puma.jruby? - keystore = File.expand_path "../../examples/puma/keystore.jks" + keystore = File.expand_path "../../examples/puma/keystore.jks", __FILE__ ssl_cipher_list = "TLS_DHE_RSA_WITH_DES_CBC_SHA,TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA" @binder.parse(["ssl://0.0.0.0?keystore=#{keystore}&ssl_cipher_list=#{ssl_cipher_list}"], @events)