1
0
Fork 0
mirror of https://github.com/puma/puma.git synced 2022-11-09 13:48:40 -05:00

[SSL] Add ability to set verification flags (#2490)

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
This commit is contained in:
Piotr Boniecki 2020-12-08 00:21:23 +01:00 committed by GitHub
parent b364a5da34
commit d2f9d6de7b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 108 additions and 3 deletions

View file

@ -2,6 +2,7 @@
* Features
* Your feature goes here <Most recent on the top, like GitHub> (#Github Number)
* Add ability to set OpenSSL verification flags (MRI only) ([#2490])
* Uses `flush` after writing messages to avoid mutating $stdout and $stderr using `sync=true` ([#2486])
* Fail build if compiling extensions raises warnings on GH Actions ([#1953])
* Add MAKE_WARNINGS_INTO_ERRORS environment variable to toggle whether a build should treat all warnings into errors or not ([#1953])

View file

@ -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 OpenSSL 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 (by separating them with coma):
```
$ puma -b 'ssl://127.0.0.1:9292?key=path_to_key&cert=path_to_cert&verification_flags=PARTIAL_CHAIN,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.

View file

@ -152,8 +152,8 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
int min;
#endif
int ssl_options;
ID sym_key, sym_cert, sym_ca, sym_verify_mode, sym_ssl_cipher_filter, sym_no_tlsv1, sym_no_tlsv1_1;
VALUE key, cert, ca, verify_mode, ssl_cipher_filter, no_tlsv1, no_tlsv1_1;
ID sym_key, sym_cert, sym_ca, sym_verify_mode, sym_ssl_cipher_filter, sym_no_tlsv1, sym_no_tlsv1_1, sym_verification_flags;
VALUE key, cert, ca, verify_mode, ssl_cipher_filter, no_tlsv1, no_tlsv1_1, verification_flags;
DH *dh;
#if OPENSSL_VERSION_NUMBER < 0x10002000L
@ -197,6 +197,15 @@ 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);
sym_verification_flags = rb_intern("verification_flags");
verification_flags = rb_funcall(mini_ssl_ctx, sym_verification_flags, 0);
if (!NIL_P(verification_flags)) {
X509_VERIFY_PARAM *param = SSL_CTX_get0_param(ctx);
X509_VERIFY_PARAM_set_flags(param, NUM2INT(verification_flags));
SSL_CTX_set1_param(ctx, param);
}
if (!NIL_P(ca)) {
StringValue(ca);
SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL);

View file

@ -51,14 +51,20 @@ module Puma
if defined?(JRUBY_VERSION)
ssl_cipher_list = opts[:ssl_cipher_list] ?
"&ssl_cipher_list=#{opts[:ssl_cipher_list]}" : nil
keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
"ssl://#{host}:#{port}?#{keystore_additions}#{ssl_cipher_list}" \
"&verify_mode=#{verify}#{tls_str}#{ca_additions}"
else
ssl_cipher_filter = opts[:ssl_cipher_filter] ?
"&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil
v_flags = (ary = opts[:verification_flags]) ?
"&verification_flags=#{Array(ary).join ','}" : nil
"ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}" \
"#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}"
"#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}"
end
end
@ -429,6 +435,7 @@ module Puma
# key: path_to_key,
# ssl_cipher_filter: cipher_filter, # optional
# verify_mode: verify_mode, # default 'none'
# verification_flags: flags, # optional, not supported by JRuby
# }
# @example For JRuby, two keys are required: keystore & keystore_pass.
# ssl_bind '127.0.0.1', '9292', {

View file

@ -245,6 +245,7 @@ module Puma
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
@ -287,6 +288,32 @@ module Puma
VERIFY_PEER = 1
VERIFY_FAIL_IF_NO_PEER_CERT = 2
# https://github.com/openssl/openssl/blob/master/include/openssl/x509_vfy.h.in
# /* Certificate verify flags */
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

View file

@ -62,6 +62,12 @@ module Puma
end
end
if params['verification_flags']
ctx.verification_flags = params['verification_flags'].split(',').
map { |flag| MiniSSL::VERIFICATION_FLAGS.fetch(flag) }.
inject { |sum, flag| sum ? sum | flag : flag }
end
ctx
end

View file

@ -453,4 +453,24 @@ class TestBinderMRI < TestBinderBase
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,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

View file

@ -141,6 +141,24 @@ class TestConfigFile < TestConfigFileBase
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,NO_CHECK_TIME")
end
def test_ssl_bind_with_ca
skip 'No ssl support' unless ::Puma::HAS_SSL
conf = Puma::Configuration.new do |c|