diff options
-rw-r--r-- | priv/schema/rabbit.schema | 19 | ||||
-rw-r--r-- | src/rabbit_ssl.erl | 33 | ||||
-rw-r--r-- | test/config_schema_SUITE_data/rabbit.snippets | 22 |
3 files changed, 70 insertions, 4 deletions
diff --git a/priv/schema/rabbit.schema b/priv/schema/rabbit.schema index 0100fac996..daf54c2c86 100644 --- a/priv/schema/rabbit.schema +++ b/priv/schema/rabbit.schema @@ -408,12 +408,27 @@ end}. %% https://github.com/rabbitmq/rabbitmq-auth-mechanism-ssl for further %% details. %% -%% To use the SSL cert's CN instead of its DN as the username +%% To use the peer certificate's Common Name (CN) field +%% instead of its Distinguished Name (DN) for username extraction. %% %% {ssl_cert_login_from, common_name}, +%% +%% To use the first SAN value of type DNS: +%% +%% {ssl_cert_login_from, subject_alternative_name}, +%% {ssl_cert_login_san_type, dns}, +%% {ssl_cert_login_san_index, 0} {mapping, "ssl_cert_login_from", "rabbit.ssl_cert_login_from", [ - {datatype, {enum, [distinguished_name, common_name]}} + {datatype, {enum, [distinguished_name, common_name, subject_alternative_name, subject_alt_name]}} +]}. + +{mapping, "ssl_cert_login_san_type", "rabbit.ssl_cert_login_san_type", [ + {datatype, {enum, [dns, ip, email, uri, other_name]}} +]}. + +{mapping, "ssl_cert_login_san_index", "rabbit.ssl_cert_login_san_index", [ + {datatype, integer}, {validators, ["non_negative_integer"]} ]}. %% TLS handshake timeout, in milliseconds. diff --git a/src/rabbit_ssl.erl b/src/rabbit_ssl.erl index d2283647b6..84670b0a19 100644 --- a/src/rabbit_ssl.erl +++ b/src/rabbit_ssl.erl @@ -120,6 +120,11 @@ peer_cert_subject(Cert) -> peer_cert_subject_items(Cert, Type) -> rabbit_cert_info:subject_items(Cert, Type). +%% Filters certificate SAN extensions by (OTP) SAN type name. +peer_cert_subject_alternative_names(Cert, Type) -> + SANs = rabbit_cert_info:subject_alternative_names(Cert), + lists:filter(fun({Key, _}) -> Key =:= Type end, SANs). + %% Return a string describing the certificate's validity. peer_cert_validity(Cert) -> rabbit_cert_info:validity(Cert). @@ -138,6 +143,27 @@ peer_cert_auth_name(distinguished_name, Cert) -> false -> unsafe end; +peer_cert_auth_name(subject_alt_name, Cert) -> + peer_cert_auth_name(subject_alternative_name, Cert); + +peer_cert_auth_name(subject_alternative_name, Cert) -> + case auth_config_sane() of + true -> + Type = application:get_env(rabbit, ssl_cert_login_san_type, dns), + %% lists:nth/2 is 1-based + Index = application:get_env(rabbit, ssl_cert_login_san_index, 0) + 1, + OfType = peer_cert_subject_alternative_names(Cert, otp_san_type(Type)), + rabbit_log:debug("Peer certificate SANs of type ~s: ~p, index to use with lists:nth/2: ~b", [Type, OfType, Index]), + case length(OfType) of + 0 -> not_found; + N when N < Index -> not_found; + N when N >= Index -> + {_, Value} = lists:nth(Index, OfType), + rabbit_data_coercion:to_binary(Value) + end; + false -> unsafe + end; + peer_cert_auth_name(common_name, Cert) -> %% If there is more than one CN then we join them with "," in a %% vaguely DN-like way. But this is more just so we do something @@ -160,3 +186,10 @@ auth_config_sane() -> "See https://www.rabbitmq.com/ssl.html#peer-verification to learn more.", [V]), false end. + +otp_san_type(dns) -> dNSName; +otp_san_type(ip) -> iPAddress; +otp_san_type(email) -> rfc822Name; +otp_san_type(uri) -> uniformResourceIdentifier; +otp_san_type(other_name) -> otherName; +otp_san_type(Other) -> Other. diff --git a/test/config_schema_SUITE_data/rabbit.snippets b/test/config_schema_SUITE_data/rabbit.snippets index a86fd95c76..57616e13f9 100644 --- a/test/config_schema_SUITE_data/rabbit.snippets +++ b/test/config_schema_SUITE_data/rabbit.snippets @@ -452,10 +452,28 @@ tcp_listen_options.exit_on_close = false", {fail_if_no_peer_cert, false}, {honor_ecc_order, true}]}]}], []}, - {ssl_cert_login_from, + + {ssl_cert_login_from_cn, "ssl_cert_login_from = common_name", - [{rabbit,[{ssl_cert_login_from,common_name}]}], + [{rabbit,[{ssl_cert_login_from, common_name}]}], []}, + + {ssl_cert_login_from_dn, + "ssl_cert_login_from = distinguished_name", + [{rabbit,[{ssl_cert_login_from, distinguished_name}]}], + []}, + + {ssl_cert_login_from_san_dns, + "ssl_cert_login_from = subject_alternative_name + ssl_cert_login_san_type = dns + ssl_cert_login_san_index = 0", + [{rabbit,[ + {ssl_cert_login_from, subject_alternative_name}, + {ssl_cert_login_san_type, dns}, + {ssl_cert_login_san_index, 0} + ]}], + []}, + {tcp_listen_options_linger_on, "tcp_listen_options.linger.on = true tcp_listen_options.linger.timeout = 100", |