diff --git a/deps/rabbitmq_auth_backend_oauth2/README.md b/deps/rabbitmq_auth_backend_oauth2/README.md index e11b2dd358..0a0abd97cd 100644 --- a/deps/rabbitmq_auth_backend_oauth2/README.md +++ b/deps/rabbitmq_auth_backend_oauth2/README.md @@ -151,7 +151,9 @@ NOTE: `jwks_url` takes precedence over `signing_keys` if both are provided. | `auth_oauth2.https.cacertfile` | Path to a file containing PEM-encoded CA certificates. The CA certificates are used during key server authentication | `auth_oauth2.https.depth` | Maximum number of non-self-issued intermediate certificates that can follow the peer certificate in a valid certification path. Default is 10. Please see: https://www.erlang.org/doc/man/ssl.html#type-allowed_cert_chain_length for more details | `auth_oauth2.https.peer_verification` | Identify if the verification should be performed towards key server. Available values: `verify_none`, `verify_peer`. Default is `verify_none`. It is recommended to configure `verify_peer` -| `auth_oauth2.https.wildcard` | Enable wildcard-aware hostname verification for key server. Available values: `true`, `false`. Default is `false`. +| `auth_oauth2.https.fail_if_no_peer_cert` | Used together with `auth_oauth2.https.peer_verification = verify_peer`. If set to `true`, the server fails if the client does not have a certificate to send, that is, sends an empty certificate. If set to `false`, it fails only if the client sends an invalid certificate (an empty certificate is considered valid). Default is `false`. +| `auth_oauth2.https.hostname_verification`| Enable wildcard-aware hostname verification for key server. Available values: `wildcard`, `none`. Default is `none`. +| `auth_oauth2.https.crl_check` | Enable certificate check against the CA’s Certificate Revocation List (CRL). Available values: `true`, `false`, `peer`, `best_effort`. Default is `false`. Please refer: https://www.erlang.org/doc/man/ssl.html#type-crl_check for more details | `auth_oauth2.algorithms` | Restrict the usable algorithms For example: @@ -173,6 +175,7 @@ auth_oauth2.jwks_url = https://my-jwt-issuer/jwks.json auth_oauth2.https.cacertfile = test/config_schema_SUITE_data/certs/cacert.pem auth_oauth2.https.peer_verification = verify_peer auth_oauth2.https.depth = 5 +auth_oauth2.https.fail_if_no_peer_cert = true auth_oauth2.https.wildcard = true auth_oauth2.algorithms.1 = HS256 auth_oauth2.algorithms.2 = RS256 diff --git a/deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema b/deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema index 420de4f232..1c8593e434 100644 --- a/deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema +++ b/deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema @@ -99,8 +99,18 @@ [{datatype, integer}]}. {mapping, - "auth_oauth2.https.wildcard", - "rabbitmq_auth_backend_oauth2.key_config.wildcard", + "auth_oauth2.https.hostname_verification", + "rabbitmq_auth_backend_oauth2.key_config.hostname_verification", + [{datatype, {enum, [wildcard, none]}}]}. + +{mapping, + "auth_oauth2.https.crl_check", + "rabbitmq_auth_backend_oauth2.key_config.crl_check", + [{datatype, {enum, [true, false, peer, best_effort]}}]}. + +{mapping, + "auth_oauth2.https.fail_if_no_peer_cert", + "rabbitmq_auth_backend_oauth2.key_config.fail_if_no_peer_cert", [{datatype, {enum, [true, false]}}]}. {validator, "https_uri", "According to the JWT Specification, Key Server URL must be https.", diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwks.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwks.erl new file mode 100644 index 0000000000..958097049e --- /dev/null +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwks.erl @@ -0,0 +1,27 @@ +-module(uaa_jwks). +-export([get/1]). + +-spec get(string() | binary()) -> {ok, term()} | {error, term()}. +get(JwksUrl) -> + httpc:request(get, {JwksUrl, []}, [{ssl, ssl_options()}], []). + +-spec ssl_options() -> list(). +ssl_options() -> + UaaEnv = application:get_env(rabbitmq_auth_backend_oauth2, key_config, []), + PeerVerification = proplists:get_value(peer_verification, UaaEnv, verify_none), + CaCertFile = proplists:get_value(cacertfile, UaaEnv), + Depth = proplists:get_value(depth, UaaEnv, 10), + FailIfNoPeerCert = proplists:get_value(fail_if_no_peer_cert, UaaEnv, false), + CrlCheck = proplists:get_value(crl_check, UaaEnv, false), + SslOpts0 = [{verify, PeerVerification}, + {cacertfile, CaCertFile}, + {depth, Depth}, + {fail_if_no_peer_cert, FailIfNoPeerCert}, + {crl_check, CrlCheck}, + {crl_cache, {ssl_crl_cache, {internal, [{http, 10000}]}}}], + case proplists:get_value(hostname_verification, UaaEnv, none) of + wildcard -> + [{customize_hostname_check, [{match_fun, public_key:pkix_verify_hostname_match_fun(https)}]} | SslOpts0]; + none -> + SslOpts0 + end. \ No newline at end of file diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl index e2cc7aee56..6721bbf5e7 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl @@ -58,7 +58,7 @@ update_jwks_signing_keys() -> undefined -> {error, no_jwks_url}; JwksUrl -> - case httpc:request(get, {JwksUrl, []}, [{ssl, ssl_options()}], []) of + case uaa_jwks:get(JwksUrl) of {ok, {_, _, JwksBody}} -> KeyList = maps:get(<<"keys">>, jose:decode(erlang:iolist_to_binary(JwksBody)), []), Keys = maps:from_list(lists:map(fun(Key) -> {maps:get(<<"kid">>, Key, undefined), {json, Key}} end, KeyList)), @@ -68,20 +68,6 @@ update_jwks_signing_keys() -> end end. --spec ssl_options() -> list(). -ssl_options() -> - UaaEnv = application:get_env(?APP, key_config, []), - PeerVerification = proplists:get_value(peer_verification, UaaEnv, verify_none), - CaCertFile = proplists:get_value(cacertfile, UaaEnv), - Depth = proplists:get_value(depth, UaaEnv, 10), - SslOpts0 = [{verify, PeerVerification}, {cacertfile, CaCertFile}, {depth, Depth}], - case proplists:get_value(wildcard, UaaEnv, false) of - true -> - [{customize_hostname_check, [{match_fun, public_key:pkix_verify_hostname_match_fun(https)}]} | SslOpts0]; - false -> - SslOpts0 - end. - -spec decode_and_verify(binary()) -> {boolean(), map()} | {error, term()}. decode_and_verify(Token) -> case uaa_jwt_jwt:get_key_id(Token) of diff --git a/deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets b/deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets index 0e47259b99..27976d3abc 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets +++ b/deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets @@ -9,7 +9,9 @@ auth_oauth2.https.cacertfile = test/config_schema_SUITE_data/certs/cacert.pem auth_oauth2.https.peer_verification = verify_none auth_oauth2.https.depth = 5 - auth_oauth2.https.wildcard = true + auth_oauth2.https.fail_if_no_peer_cert = false + auth_oauth2.https.hostname_verification = wildcard + auth_oauth2.https.crl_check = true auth_oauth2.algorithms.1 = HS256 auth_oauth2.algorithms.2 = RS256", [ @@ -28,7 +30,9 @@ {cacertfile, "test/config_schema_SUITE_data/certs/cacert.pem"}, {peer_verification, verify_none}, {depth, 5}, - {wildcard, true}, + {fail_if_no_peer_cert, false}, + {hostname_verification, wildcard}, + {crl_check, true}, {algorithms, [<<"HS256">>, <<"RS256">>]} ] }