فهرست منبع

chore(authn): test JWT authn with HTTPS key server

Ilya Averyanov 4 سال پیش
والد
کامیت
2b3d3ebb0a
2فایلهای تغییر یافته به همراه63 افزوده شده و 26 حذف شده
  1. 2 2
      apps/emqx_authn/src/simple_authn/emqx_authn_jwt.erl
  2. 61 24
      apps/emqx_authn/test/emqx_authn_jwt_SUITE.erl

+ 2 - 2
apps/emqx_authn/src/simple_authn/emqx_authn_jwt.erl

@@ -114,8 +114,8 @@ certfile(_) -> undefined.
 keyfile(type) -> string();
 keyfile(_) -> undefined.
 
-verify(type) -> boolean();
-verify(default) -> false;
+verify(type) -> hoconsc:enum([verify_peer, verify_none]);
+verify(default) -> verify_none;
 verify(_) -> undefined.
 
 server_name_indication(type) -> string();

+ 61 - 24
apps/emqx_authn/test/emqx_authn_jwt_SUITE.erl

@@ -23,8 +23,6 @@
 -include_lib("eunit/include/eunit.hrl").
 -include_lib("snabbkaffe/include/snabbkaffe.hrl").
 
--include("emqx_authn.hrl").
-
 -define(AUTHN_ID, <<"mechanism:jwt">>).
 
 -define(JWKS_PORT, 33333).
@@ -156,7 +154,7 @@ t_jwt_authenticator_public_key(_) ->
     ok.
 
 t_jwks_renewal(_Config) ->
-    {ok, _} = emqx_authn_http_test_server:start_link(?JWKS_PORT, ?JWKS_PATH),
+    {ok, _} = emqx_authn_http_test_server:start_link(?JWKS_PORT, ?JWKS_PATH, server_ssl_opts()),
     ok = emqx_authn_http_test_server:set_handler(fun jwks_handler/2),
 
     PrivateKey = test_rsa_key(private),
@@ -164,45 +162,63 @@ t_jwks_renewal(_Config) ->
     JWS = generate_jws('public-key', Payload, PrivateKey),
     Credential = #{username => <<"myuser">>,
 			       password => JWS},
+    
+    BadConfig0 = #{mechanism => jwt,
+                   algorithm => 'public-key',
+                   ssl => #{enable => false},
+                   verify_claims => [],
 
-    BadConfig = #{mechanism => jwt,
-                  algorithm => 'public-key',
-                  ssl => #{enable => false},
-                  verify_claims => [],
-
-                  use_jwks => true,
-                  endpoint => "http://127.0.0.1:" ++ integer_to_list(?JWKS_PORT + 1) ++ ?JWKS_PATH,
-                  refresh_interval => 1000
-                 },
+                   use_jwks => true,
+                   endpoint => "https://127.0.0.1:" ++ integer_to_list(?JWKS_PORT + 1) ++ ?JWKS_PATH,
+                   refresh_interval => 1000
+                  },
 
     ok = snabbkaffe:start_trace(),
 
     {{ok, State0}, _} = ?wait_async_action(
-                           emqx_authn_jwt:create(?AUTHN_ID, BadConfig),
+                           emqx_authn_jwt:create(?AUTHN_ID, BadConfig0),
                            #{?snk_kind := jwks_endpoint_response},
-                           1000),
+                           10000),
 
     ok = snabbkaffe:stop(),
 
     ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential, State0)),
     ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential#{password => <<"badpassword">>}, State0)),
 
-    GoodConfig = BadConfig#{endpoint =>
-                            "http://127.0.0.1:" ++ integer_to_list(?JWKS_PORT) ++ ?JWKS_PATH},
+    ClientSSLOpts = client_ssl_opts(),
+    BadClientSSLOpts = ClientSSLOpts#{server_name_indication => "authn-https-unknown-host"},
+
+    BadConfig1 = BadConfig0#{endpoint =>
+                            "https://127.0.0.1:" ++ integer_to_list(?JWKS_PORT) ++ ?JWKS_PATH,
+                            ssl => BadClientSSLOpts},
 
     ok = snabbkaffe:start_trace(),
 
     {{ok, State1}, _} = ?wait_async_action(
-                           emqx_authn_jwt:update(GoodConfig, State0),
+                           emqx_authn_jwt:create(?AUTHN_ID, BadConfig1),
                            #{?snk_kind := jwks_endpoint_response},
-                           1000),
+                           10000),
 
     ok = snabbkaffe:stop(),
 
-    ?assertEqual({ok, #{is_superuser => false}}, emqx_authn_jwt:authenticate(Credential, State1)),
-    ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential#{password => <<"badpassword">>}, State1)),
+    ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential, State1)),
+    ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential#{password => <<"badpassword">>}, State0)),
+
+    GoodConfig = BadConfig1#{ssl => ClientSSLOpts},
+
+    ok = snabbkaffe:start_trace(),
+
+    {{ok, State2}, _} = ?wait_async_action(
+                           emqx_authn_jwt:update(GoodConfig, State1),
+                           #{?snk_kind := jwks_endpoint_response},
+                           10000),
 
-    ?assertEqual(ok, emqx_authn_jwt:destroy(State1)),
+    ok = snabbkaffe:stop(),
+
+    ?assertEqual({ok, #{is_superuser => false}}, emqx_authn_jwt:authenticate(Credential, State2)),
+    ?assertEqual(ignore, emqx_authn_jwt:authenticate(Credential#{password => <<"badpassword">>}, State2)),
+
+    ?assertEqual(ok, emqx_authn_jwt:destroy(State2)),
     ok = emqx_authn_http_test_server:stop().
 
 %%------------------------------------------------------------------------------
@@ -220,12 +236,17 @@ jwks_handler(Req0, State) ->
     {ok, Req, State}.
 
 test_rsa_key(public) ->
-    Dir = code:lib_dir(emqx_authn, test),
-    list_to_binary(filename:join([Dir, "data/public_key.pem"]));
+    data_file("public_key.pem");
 
 test_rsa_key(private) ->
+    data_file("private_key.pem").
+
+data_file(Name) ->
     Dir = code:lib_dir(emqx_authn, test),
-    list_to_binary(filename:join([Dir, "data/private_key.pem"])).
+    list_to_binary(filename:join([Dir, "data", Name])).
+
+cert_file(Name) ->
+    data_file(filename:join(["certs", Name])).
 
 generate_jws('hmac-based', Payload, Secret) ->
     JWK = jose_jwk:from_oct(Secret),
@@ -243,3 +264,19 @@ generate_jws('public-key', Payload, PrivateKey) ->
     Signed = jose_jwt:sign(JWK, Header, Payload),
     {_, JWS} = jose_jws:compact(Signed),
     JWS.
+
+client_ssl_opts() ->
+    #{keyfile    => cert_file("authn-https-client.key"),
+      certfile   => cert_file("authn-https-client.crt"),
+      cacertfile => cert_file("authn-https-ca.crt"),
+      enable => true,
+      verify => verify_peer,
+      server_name_indication => "authn-https"
+    }.
+
+server_ssl_opts() ->
+    [{keyfile, cert_file("authn-https-server.key")},
+     {certfile, cert_file("authn-https-server.crt")},
+     {cacertfile, cert_file("authn-https-ca.crt")},
+     {verify, verify_none}
+    ].