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

Dev/ssl cipher support #1478

Merged
merged 4 commits into from May 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 11 additions & 1 deletion README.md
Expand Up @@ -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

Expand Down
11 changes: 10 additions & 1 deletion ext/puma_http11/mini_ssl.c
Expand Up @@ -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;

Expand All @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions ext/puma_http11/org/jruby/puma/MiniSSL.java
Expand Up @@ -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());
Expand Down
3 changes: 3 additions & 0 deletions lib/puma/binder.rb
Expand Up @@ -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='"
Expand All @@ -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']
Expand Down Expand Up @@ -313,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
Expand Down
2 changes: 2 additions & 0 deletions lib/puma/minissl.rb
Expand Up @@ -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
Expand All @@ -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
Expand Down
28 changes: 28 additions & 0 deletions test/test_binder.rb
Expand Up @@ -28,4 +28,32 @@ def test_localhost_addresses_dont_alter_listeners_for_ssl_addresses

assert_equal [], @binder.listeners
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", __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)

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