feat: gssapi authentication
This commit is contained in:
parent
4250d01363
commit
1270e6a64d
|
@ -2,6 +2,7 @@ version: '3.9'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
erlang:
|
erlang:
|
||||||
|
hostname: erlang.emqx.net
|
||||||
container_name: erlang
|
container_name: erlang
|
||||||
image: ${DOCKER_CT_RUNNER_IMAGE:-ghcr.io/emqx/emqx-builder/5.3-9:1.15.7-26.2.5-3-ubuntu22.04}
|
image: ${DOCKER_CT_RUNNER_IMAGE:-ghcr.io/emqx/emqx-builder/5.3-9:1.15.7-26.2.5-3-ubuntu22.04}
|
||||||
env_file:
|
env_file:
|
||||||
|
|
|
@ -394,6 +394,16 @@ handle_in(
|
||||||
case ConnState of
|
case ConnState of
|
||||||
connecting ->
|
connecting ->
|
||||||
process_connect(NProperties, NChannel);
|
process_connect(NProperties, NChannel);
|
||||||
|
reauthenticating ->
|
||||||
|
{ok, Auth, NChannel1} =
|
||||||
|
handle_out(
|
||||||
|
auth,
|
||||||
|
{?RC_SUCCESS, NProperties},
|
||||||
|
NChannel#channel{conn_state = connected}
|
||||||
|
),
|
||||||
|
{ok, Replies, NChannel2} =
|
||||||
|
process_connect(NProperties, NChannel1),
|
||||||
|
{ok, [?REPLY_OUTGOING(Auth) | Replies], NChannel2};
|
||||||
_ ->
|
_ ->
|
||||||
handle_out(
|
handle_out(
|
||||||
auth,
|
auth,
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
Business Source License 1.1
|
||||||
|
|
||||||
|
Licensor: Hangzhou EMQ Technologies Co., Ltd.
|
||||||
|
Licensed Work: EMQX Enterprise Edition
|
||||||
|
The Licensed Work is (c) 2023
|
||||||
|
Hangzhou EMQ Technologies Co., Ltd.
|
||||||
|
Additional Use Grant: Students and educators are granted right to copy,
|
||||||
|
modify, and create derivative work for research
|
||||||
|
or education.
|
||||||
|
Change Date: 2028-01-26
|
||||||
|
Change License: Apache License, Version 2.0
|
||||||
|
|
||||||
|
For information about alternative licensing arrangements for the Software,
|
||||||
|
please contact Licensor: https://www.emqx.com/en/contact
|
||||||
|
|
||||||
|
Notice
|
||||||
|
|
||||||
|
The Business Source License (this document, or the “License”) is not an Open
|
||||||
|
Source license. However, the Licensed Work will eventually be made available
|
||||||
|
under an Open Source License, as stated in this License.
|
||||||
|
|
||||||
|
License text copyright (c) 2017 MariaDB Corporation Ab, All Rights Reserved.
|
||||||
|
“Business Source License” is a trademark of MariaDB Corporation Ab.
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Business Source License 1.1
|
||||||
|
|
||||||
|
Terms
|
||||||
|
|
||||||
|
The Licensor hereby grants you the right to copy, modify, create derivative
|
||||||
|
works, redistribute, and make non-production use of the Licensed Work. The
|
||||||
|
Licensor may make an Additional Use Grant, above, permitting limited
|
||||||
|
production use.
|
||||||
|
|
||||||
|
Effective on the Change Date, or the fourth anniversary of the first publicly
|
||||||
|
available distribution of a specific version of the Licensed Work under this
|
||||||
|
License, whichever comes first, the Licensor hereby grants you rights under
|
||||||
|
the terms of the Change License, and the rights granted in the paragraph
|
||||||
|
above terminate.
|
||||||
|
|
||||||
|
If your use of the Licensed Work does not comply with the requirements
|
||||||
|
currently in effect as described in this License, you must purchase a
|
||||||
|
commercial license from the Licensor, its affiliated entities, or authorized
|
||||||
|
resellers, or you must refrain from using the Licensed Work.
|
||||||
|
|
||||||
|
All copies of the original and modified Licensed Work, and derivative works
|
||||||
|
of the Licensed Work, are subject to this License. This License applies
|
||||||
|
separately for each version of the Licensed Work and the Change Date may vary
|
||||||
|
for each version of the Licensed Work released by Licensor.
|
||||||
|
|
||||||
|
You must conspicuously display this License on each original or modified copy
|
||||||
|
of the Licensed Work. If you receive the Licensed Work in original or
|
||||||
|
modified form from a third party, the terms and conditions set forth in this
|
||||||
|
License apply to your use of that work.
|
||||||
|
|
||||||
|
Any use of the Licensed Work in violation of this License will automatically
|
||||||
|
terminate your rights under this License for the current and all other
|
||||||
|
versions of the Licensed Work.
|
||||||
|
|
||||||
|
This License does not grant you any right in any trademark or logo of
|
||||||
|
Licensor or its affiliates (provided that you may use a trademark or logo of
|
||||||
|
Licensor as expressly required by this License).
|
||||||
|
|
||||||
|
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
|
||||||
|
AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
|
||||||
|
TITLE.
|
||||||
|
|
||||||
|
MariaDB hereby grants you permission to use this License’s text to license
|
||||||
|
your works, and to refer to it using the trademark “Business Source License”,
|
||||||
|
as long as you comply with the Covenants of Licensor below.
|
||||||
|
|
||||||
|
Covenants of Licensor
|
||||||
|
|
||||||
|
In consideration of the right to use this License’s text and the “Business
|
||||||
|
Source License” name and trademark, Licensor covenants to MariaDB, and to all
|
||||||
|
other recipients of the licensed work to be provided by Licensor:
|
||||||
|
|
||||||
|
1. To specify as the Change License the GPL Version 2.0 or any later version,
|
||||||
|
or a license that is compatible with GPL Version 2.0 or a later version,
|
||||||
|
where “compatible” means that software provided under the Change License can
|
||||||
|
be included in a program with software provided under GPL Version 2.0 or a
|
||||||
|
later version. Licensor may specify additional Change Licenses without
|
||||||
|
limitation.
|
||||||
|
|
||||||
|
2. To either: (a) specify an additional grant of rights to use that does not
|
||||||
|
impose any additional restriction on the right granted in this License, as
|
||||||
|
the Additional Use Grant; or (b) insert the text “None”.
|
||||||
|
|
||||||
|
3. To specify a Change Date.
|
||||||
|
|
||||||
|
4. Not to modify this License in any other way.
|
|
@ -0,0 +1,16 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2024 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-ifndef(EMQX_AUTH_GSSAPI_HRL).
|
||||||
|
-define(EMQX_AUTH_GSSAPI_HRL, true).
|
||||||
|
|
||||||
|
-define(AUTHN_MECHANISM_GSSAPI, gssapi).
|
||||||
|
-define(AUTHN_MECHANISM_GSSAPI_BIN, <<"gssapi">>).
|
||||||
|
|
||||||
|
-define(AUTHN_BACKEND, gssapi).
|
||||||
|
-define(AUTHN_BACKEND_BIN, <<"gssapi">>).
|
||||||
|
|
||||||
|
-define(AUTHN_TYPE_GSSAPI, {?AUTHN_MECHANISM_GSSAPI, ?AUTHN_BACKEND}).
|
||||||
|
|
||||||
|
-endif.
|
|
@ -0,0 +1,7 @@
|
||||||
|
%% -*- mode: erlang -*-
|
||||||
|
|
||||||
|
{deps, [
|
||||||
|
{emqx, {path, "../emqx"}},
|
||||||
|
{emqx_utils, {path, "../emqx_utils"}},
|
||||||
|
{sasl_auth, {git, "https://github.com/kafka4beam/sasl_auth.git", {tag, "v2.1.0"}}}
|
||||||
|
]}.
|
|
@ -0,0 +1,18 @@
|
||||||
|
%% -*- mode: erlang -*-
|
||||||
|
{application, emqx_auth_gssapi, [
|
||||||
|
{description, "EMQX gssapi Authentication"},
|
||||||
|
{vsn, "0.1.0"},
|
||||||
|
{registered, []},
|
||||||
|
{mod, {emqx_auth_gssapi_app, []}},
|
||||||
|
{applications, [
|
||||||
|
kernel,
|
||||||
|
stdlib,
|
||||||
|
emqx_auth,
|
||||||
|
sasl_auth
|
||||||
|
]},
|
||||||
|
{env, []},
|
||||||
|
{modules, []},
|
||||||
|
|
||||||
|
{licenses, ["Apache 2.0"]},
|
||||||
|
{links, []}
|
||||||
|
]}.
|
|
@ -0,0 +1,20 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2024 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(emqx_auth_gssapi_app).
|
||||||
|
|
||||||
|
-include("emqx_auth_gssapi.hrl").
|
||||||
|
|
||||||
|
-behaviour(application).
|
||||||
|
|
||||||
|
-export([start/2, stop/1]).
|
||||||
|
|
||||||
|
start(_StartType, _StartArgs) ->
|
||||||
|
ok = emqx_authn:register_provider(?AUTHN_TYPE_GSSAPI, emqx_authn_gssapi),
|
||||||
|
{ok, Sup} = emqx_auth_gssapi_sup:start_link(),
|
||||||
|
{ok, Sup}.
|
||||||
|
|
||||||
|
stop(_State) ->
|
||||||
|
ok = emqx_authn:deregister_provider(?AUTHN_TYPE_GSSAPI),
|
||||||
|
ok.
|
|
@ -0,0 +1,25 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2024 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(emqx_auth_gssapi_sup).
|
||||||
|
|
||||||
|
-behaviour(supervisor).
|
||||||
|
|
||||||
|
-export([start_link/0]).
|
||||||
|
|
||||||
|
-export([init/1]).
|
||||||
|
|
||||||
|
-define(SERVER, ?MODULE).
|
||||||
|
|
||||||
|
start_link() ->
|
||||||
|
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
|
||||||
|
|
||||||
|
init([]) ->
|
||||||
|
SupFlags = #{
|
||||||
|
strategy => one_for_all,
|
||||||
|
intensity => 0,
|
||||||
|
period => 1
|
||||||
|
},
|
||||||
|
ChildSpecs = [],
|
||||||
|
{ok, {SupFlags, ChildSpecs}}.
|
|
@ -0,0 +1,111 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2024 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(emqx_authn_gssapi).
|
||||||
|
|
||||||
|
-include("emqx_auth_gssapi.hrl").
|
||||||
|
-include_lib("emqx_auth/include/emqx_authn.hrl").
|
||||||
|
-include_lib("typerefl/include/types.hrl").
|
||||||
|
|
||||||
|
-behaviour(emqx_authn_provider).
|
||||||
|
|
||||||
|
-export([
|
||||||
|
create/2,
|
||||||
|
update/2,
|
||||||
|
destroy/1,
|
||||||
|
authenticate/2
|
||||||
|
]).
|
||||||
|
|
||||||
|
create(
|
||||||
|
AuthenticatorID,
|
||||||
|
#{
|
||||||
|
principal := Principal,
|
||||||
|
keytab_file := KeyTabFile
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
KeyTabPath = emqx_schema:naive_env_interpolation(KeyTabFile),
|
||||||
|
case sasl_auth:kinit(KeyTabPath, Principal) of
|
||||||
|
ok ->
|
||||||
|
{ok, #{
|
||||||
|
id => AuthenticatorID,
|
||||||
|
principal => Principal,
|
||||||
|
keytab_file => KeyTabFile
|
||||||
|
}};
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end.
|
||||||
|
|
||||||
|
update(Config, #{id := ID}) ->
|
||||||
|
create(ID, Config).
|
||||||
|
|
||||||
|
destroy(_) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
authenticate(
|
||||||
|
#{
|
||||||
|
auth_method := <<"GSSAPI">>,
|
||||||
|
auth_data := AuthData,
|
||||||
|
auth_cache := AuthCache
|
||||||
|
},
|
||||||
|
#{principal := Principal}
|
||||||
|
) when AuthData =/= undefined ->
|
||||||
|
case AuthCache of
|
||||||
|
#{sasl_conn := SaslConn} ->
|
||||||
|
auth_continue(SaslConn, AuthData);
|
||||||
|
_ ->
|
||||||
|
case auth_new(Principal) of
|
||||||
|
{ok, SaslConn} -> auth_begin(SaslConn, AuthData);
|
||||||
|
Error -> Error
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
authenticate(_Credential, _State) ->
|
||||||
|
ignore.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% Internal functions
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
auth_new(Principal) ->
|
||||||
|
case sasl_auth:server_new(<<"emqx">>, Principal) of
|
||||||
|
{ok, SaslConn} ->
|
||||||
|
{ok, SaslConn};
|
||||||
|
Error ->
|
||||||
|
?TRACE_AUTHN_PROVIDER("sasl_gssapi_new_failed", #{
|
||||||
|
reason => Error,
|
||||||
|
sasl_function => "server_server_new"
|
||||||
|
}),
|
||||||
|
{error, not_authorized}
|
||||||
|
end.
|
||||||
|
|
||||||
|
auth_begin(SaslConn, ClientToken) ->
|
||||||
|
case sasl_auth:server_start(SaslConn, ClientToken) of
|
||||||
|
{ok, {sasl_continue, ServerToken}} ->
|
||||||
|
{continue, ServerToken, #{sasl_conn => SaslConn}};
|
||||||
|
{ok, {sasl_ok, ServerToken}} ->
|
||||||
|
sasl_auth:server_done(SaslConn),
|
||||||
|
{ok, #{}, ServerToken};
|
||||||
|
Reason ->
|
||||||
|
?TRACE_AUTHN_PROVIDER("sasl_gssapi_start_failed", #{
|
||||||
|
reason => Reason,
|
||||||
|
sasl_function => "server_server_start"
|
||||||
|
}),
|
||||||
|
sasl_auth:server_done(SaslConn),
|
||||||
|
{error, not_authorized}
|
||||||
|
end.
|
||||||
|
|
||||||
|
auth_continue(SaslConn, ClientToken) ->
|
||||||
|
case sasl_auth:server_step(SaslConn, ClientToken) of
|
||||||
|
{ok, {sasl_continue, ServerToken}} ->
|
||||||
|
{continue, ServerToken, #{sasl_conn => SaslConn}};
|
||||||
|
{ok, {sasl_ok, ServerToken}} ->
|
||||||
|
sasl_auth:server_done(SaslConn),
|
||||||
|
{ok, #{}, ServerToken};
|
||||||
|
Reason ->
|
||||||
|
?TRACE_AUTHN_PROVIDER("sasl_gssapi_step_failed", #{
|
||||||
|
reason => Reason,
|
||||||
|
sasl_function => "server_server_step"
|
||||||
|
}),
|
||||||
|
sasl_auth:server_done(SaslConn),
|
||||||
|
{error, not_authorized}
|
||||||
|
end.
|
|
@ -0,0 +1,57 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2024 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
|
||||||
|
-module(emqx_authn_gssapi_schema).
|
||||||
|
|
||||||
|
-include("emqx_auth_gssapi.hrl").
|
||||||
|
-include_lib("hocon/include/hoconsc.hrl").
|
||||||
|
|
||||||
|
-behaviour(emqx_authn_schema).
|
||||||
|
|
||||||
|
-export([
|
||||||
|
namespace/0,
|
||||||
|
fields/1,
|
||||||
|
desc/1,
|
||||||
|
refs/0,
|
||||||
|
select_union_member/1
|
||||||
|
]).
|
||||||
|
|
||||||
|
namespace() -> "authn".
|
||||||
|
|
||||||
|
refs() ->
|
||||||
|
[?R_REF(gssapi)].
|
||||||
|
|
||||||
|
select_union_member(#{
|
||||||
|
<<"mechanism">> := ?AUTHN_MECHANISM_GSSAPI_BIN, <<"backend">> := ?AUTHN_BACKEND_BIN
|
||||||
|
}) ->
|
||||||
|
refs();
|
||||||
|
select_union_member(#{<<"mechanism">> := ?AUTHN_MECHANISM_GSSAPI_BIN}) ->
|
||||||
|
throw(#{
|
||||||
|
reason => "unknown_backend",
|
||||||
|
expected => ?AUTHN_BACKEND
|
||||||
|
});
|
||||||
|
select_union_member(_) ->
|
||||||
|
undefined.
|
||||||
|
|
||||||
|
fields(gssapi) ->
|
||||||
|
emqx_authn_schema:common_fields() ++
|
||||||
|
[
|
||||||
|
{mechanism, emqx_authn_schema:mechanism(?AUTHN_MECHANISM_GSSAPI)},
|
||||||
|
{backend, emqx_authn_schema:backend(?AUTHN_BACKEND)},
|
||||||
|
{principal,
|
||||||
|
?HOCON(binary(), #{
|
||||||
|
required => true,
|
||||||
|
desc => ?DESC(emqx_bridge_kafka, auth_kerberos_principal)
|
||||||
|
})},
|
||||||
|
{keytab_file,
|
||||||
|
?HOCON(binary(), #{
|
||||||
|
required => true,
|
||||||
|
desc => ?DESC(emqx_bridge_kafka, auth_kerberos_keytab_file)
|
||||||
|
})}
|
||||||
|
].
|
||||||
|
|
||||||
|
desc(gssapi) ->
|
||||||
|
"Settings for GSSAPI authentication.";
|
||||||
|
desc(_) ->
|
||||||
|
undefined.
|
|
@ -9,7 +9,8 @@
|
||||||
{snappyer, "1.2.9"},
|
{snappyer, "1.2.9"},
|
||||||
{emqx_connector, {path, "../../apps/emqx_connector"}},
|
{emqx_connector, {path, "../../apps/emqx_connector"}},
|
||||||
{emqx_resource, {path, "../../apps/emqx_resource"}},
|
{emqx_resource, {path, "../../apps/emqx_resource"}},
|
||||||
{emqx_bridge, {path, "../../apps/emqx_bridge"}}
|
{emqx_bridge, {path, "../../apps/emqx_bridge"}},
|
||||||
|
{sasl_auth, {git, "https://github.com/kafka4beam/sasl_auth.git", {tag, "v2.1.0"}}}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
{shell, [
|
{shell, [
|
||||||
|
|
|
@ -55,7 +55,10 @@ authn_mods(ce) ->
|
||||||
];
|
];
|
||||||
authn_mods(ee) ->
|
authn_mods(ee) ->
|
||||||
authn_mods(ce) ++
|
authn_mods(ce) ++
|
||||||
[emqx_gcp_device_authn_schema].
|
[
|
||||||
|
emqx_gcp_device_authn_schema,
|
||||||
|
emqx_authn_gssapi_schema
|
||||||
|
].
|
||||||
|
|
||||||
authz() ->
|
authz() ->
|
||||||
[{emqx_authz_schema, authz_mods()}].
|
[{emqx_authz_schema, authz_mods()}].
|
||||||
|
|
|
@ -136,6 +136,7 @@
|
||||||
emqx_bridge_syskeeper,
|
emqx_bridge_syskeeper,
|
||||||
emqx_bridge_confluent,
|
emqx_bridge_confluent,
|
||||||
emqx_ds_shared_sub,
|
emqx_ds_shared_sub,
|
||||||
|
emqx_auth_gssapi,
|
||||||
emqx_auth_ext,
|
emqx_auth_ext,
|
||||||
emqx_cluster_link,
|
emqx_cluster_link,
|
||||||
emqx_ds_builtin_raft
|
emqx_ds_builtin_raft
|
||||||
|
|
1
mix.exs
1
mix.exs
|
@ -376,6 +376,7 @@ defmodule EMQXUmbrella.MixProject do
|
||||||
:emqx_gateway_jt808,
|
:emqx_gateway_jt808,
|
||||||
:emqx_bridge_syskeeper,
|
:emqx_bridge_syskeeper,
|
||||||
:emqx_ds_shared_sub,
|
:emqx_ds_shared_sub,
|
||||||
|
:emqx_auth_gssapi,
|
||||||
:emqx_auth_ext,
|
:emqx_auth_ext,
|
||||||
:emqx_cluster_link,
|
:emqx_cluster_link,
|
||||||
:emqx_ds_builtin_raft
|
:emqx_ds_builtin_raft
|
||||||
|
|
Loading…
Reference in New Issue