Skip to content

Commit

Permalink
[SSL] Add ability to set verification flags
Browse files Browse the repository at this point in the history
Any number of verification flags supported by OpenSSL can be set (
https://www.openssl.org/docs/manmaster/man3/X509_VERIFY_PARAM_set_hostflags.html#VERIFICATION-FLAGS
  • Loading branch information
Piotr Boniecki committed Nov 23, 2020
1 parent ea81fba commit b32249b
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 5 deletions.
1 change: 1 addition & 0 deletions History.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* You can now fork workers from worker 0 using SIGURG w/o fork_worker enabled [#2449]
* Add option to bind to systemd activated sockets ([#2362])
* Add compile option to change the `QUERY_STRING` max length ([#2485])
* Add ability to set OpenSSL verification flags (MRI only)

* Bugfixes
* Your bugfix goes here <Most recent on the top, like GitHub> (#Github Number)
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,23 @@ Disable TLS v1 with the `no_tlsv1` option:
$ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&no_tlsv1=true'
```

#### Controlling Open SSL Verification Flags

To enable verification flags offered by OpenSSL, use `verification_flags` (not available for JRuby):

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

You can also set multiple verification flags:

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

List of available flags: `USE_CHECK_TIME`, `CRL_CHECK`, `CRL_CHECK_ALL`, `IGNORE_CRITICAL`, `X509_STRICT`, `ALLOW_PROXY_CERTS`, `POLICY_CHECK`, `EXPLICIT_POLICY`, `INHIBIT_ANY`, `INHIBIT_MAP`, `NOTIFY_POLICY`, `EXTENDED_CRL_SUPPORT`, `USE_DELTAS`, `CHECK_SS_SIGNATURE`, `TRUSTED_FIRST`, `SUITEB_128_LOS_ONLY`, `SUITEB_192_LOS`, `SUITEB_128_LOS`, `PARTIAL_CHAIN`, `NO_ALT_CHAINS`, `NO_CHECK_TIME`
(see https://www.openssl.org/docs/manmaster/man3/X509_VERIFY_PARAM_set_hostflags.html#VERIFICATION-FLAGS).

### Control/Status Server

Puma has a built-in status and control app that can be used to query and control Puma.
Expand Down
11 changes: 11 additions & 0 deletions ext/puma_http11/mini_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,17 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
SSL_CTX_use_certificate_chain_file(ctx, RSTRING_PTR(cert));
SSL_CTX_use_PrivateKey_file(ctx, RSTRING_PTR(key), SSL_FILETYPE_PEM);

ID sym_verification_flags = rb_intern("verification_flags");
VALUE verification_flags = rb_funcall(mini_ssl_ctx, sym_verification_flags, 0);

if (!NIL_P(verification_flags)) {
X509_VERIFY_PARAM *param;
param = X509_VERIFY_PARAM_new();
X509_VERIFY_PARAM_set_flags(param, NUM2INT(verification_flags));
SSL_CTX_set1_param(ctx, param);
X509_VERIFY_PARAM_free(param);
}

if (!NIL_P(ca)) {
StringValue(ca);
SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL);
Expand Down
14 changes: 9 additions & 5 deletions lib/puma/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -408,22 +408,26 @@ def threads(min, max)
# key: path_to_key,
# ssl_cipher_filter: cipher_filter, # optional
# verify_mode: verify_mode, # default 'none'
# verification_flags: flags, # optional, not supported by JRuby
# keystore: path_to_keystore,
# keystore_pass: password
# }
def ssl_bind(host, port, opts)
verify = opts.fetch(:verify_mode, 'none').to_s
no_tlsv1 = opts.fetch(:no_tlsv1, 'false')
no_tlsv1_1 = opts.fetch(:no_tlsv1_1, 'false')
ca_additions = "&ca=#{opts[:ca]}" if ['peer', 'force_peer'].include?(verify)

additions = +''
additions << "&ca=#{opts[:ca]}" if ['peer', 'force_peer'].include?(verify)

if defined?(JRUBY_VERSION)
keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&#{keystore_additions}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}&no_tlsv1_1=#{no_tlsv1_1}#{ca_additions}"
additions << "&keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
else
ssl_cipher_filter = "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" if opts[:ssl_cipher_filter]
bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}#{ssl_cipher_filter}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}&no_tlsv1_1=#{no_tlsv1_1}#{ca_additions}"
additions << "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" if opts[:ssl_cipher_filter]
Array(opts[:verification_flags]).each { |flag| additions << "&verification_flags=#{flag}" }
end

bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}&no_tlsv1_1=#{no_tlsv1_1}#{additions}"
end

# Use +path+ as the file to store the server info state. This is
Expand Down
25 changes: 25 additions & 0 deletions lib/puma/minissl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ def check
attr_reader :cert
attr_reader :ca
attr_accessor :ssl_cipher_filter
attr_accessor :verification_flags

def key=(key)
raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
Expand Down Expand Up @@ -287,6 +288,30 @@ def no_tlsv1_1=(tlsv1_1)
VERIFY_PEER = 1
VERIFY_FAIL_IF_NO_PEER_CERT = 2

VERIFICATION_FLAGS = {
"USE_CHECK_TIME" => 0x2,
"CRL_CHECK" => 0x4,
"CRL_CHECK_ALL" => 0x8,
"IGNORE_CRITICAL" => 0x10,
"X509_STRICT" => 0x20,
"ALLOW_PROXY_CERTS" => 0x40,
"POLICY_CHECK" => 0x80,
"EXPLICIT_POLICY" => 0x100,
"INHIBIT_ANY" => 0x200,
"INHIBIT_MAP" => 0x400,
"NOTIFY_POLICY" => 0x800,
"EXTENDED_CRL_SUPPORT" => 0x1000,
"USE_DELTAS" => 0x2000,
"CHECK_SS_SIGNATURE" => 0x4000,
"TRUSTED_FIRST" => 0x8000,
"SUITEB_128_LOS_ONLY" => 0x10000,
"SUITEB_192_LOS" => 0x20000,
"SUITEB_128_LOS" => 0x30000,
"PARTIAL_CHAIN" => 0x80000,
"NO_ALT_CHAINS" => 0x100000,
"NO_CHECK_TIME" => 0x200000
}.freeze

class Server
def initialize(socket, ctx)
@socket = socket
Expand Down
6 changes: 6 additions & 0 deletions lib/puma/minissl/context_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ def context
end
end

if params['verification_flags']
ctx.verification_flags = Array(params['verification_flags']).
map { |flag| MiniSSL::VERIFICATION_FLAGS.fetch(flag) }.
inject { |sum, flag| sum ? sum | flag : flag }
end

ctx
end

Expand Down
20 changes: 20 additions & 0 deletions test/test_binder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -453,4 +453,24 @@ def test_binder_parses_ssl_cipher_filter

assert_equal ssl_cipher_filter, ssl_context_for_binder.ssl_cipher_filter
end

def test_binder_parses_ssl_verification_flags_one
skip 'No ssl support' unless ::Puma::HAS_SSL

input = "&verification_flags=TRUSTED_FIRST"

@binder.parse ["ssl://0.0.0.0?#{ssl_query}#{input}"], @events

assert_equal 0x8000, ssl_context_for_binder.verification_flags
end

def test_binder_parses_ssl_verification_flags_multiple
skip 'No ssl support' unless ::Puma::HAS_SSL

input = "&verification_flags=TRUSTED_FIRST&verification_flags=NO_CHECK_TIME"

@binder.parse ["ssl://0.0.0.0?#{ssl_query}#{input}"], @events

assert_equal 0x8000 | 0x200000, ssl_context_for_binder.verification_flags
end
end unless ::Puma::IS_JRUBY
19 changes: 19 additions & 0 deletions test/test_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,25 @@ def test_ssl_bind_with_cipher_filter
assert ssl_binding.include?("&ssl_cipher_filter=#{cipher_filter}")
end

def test_ssl_bind_with_verification_flags
skip_on :jruby
skip 'No ssl support' unless ::Puma::HAS_SSL

conf = Puma::Configuration.new do |c|
c.ssl_bind "0.0.0.0", "9292", {
cert: "cert",
key: "key",
verification_flags: ["TRUSTED_FIRST", "NO_CHECK_TIME"]
}
end

conf.load

ssl_binding = conf.options[:binds].first
assert ssl_binding.include?("&verification_flags=TRUSTED_FIRST")
assert ssl_binding.include?("&verification_flags=NO_CHECK_TIME")
end

def test_ssl_bind_with_ca
skip 'No ssl support' unless ::Puma::HAS_SSL
conf = Puma::Configuration.new do |c|
Expand Down

0 comments on commit b32249b

Please sign in to comment.