Skip to content

Commit

Permalink
Merge pull request #4184 from esl/change-domain-validation
Browse files Browse the repository at this point in the history
Change domain validation logic
  • Loading branch information
chrzaszcz authored Dec 6, 2023
2 parents c5ff950 + e0d1fd1 commit f44b5b0
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 34 deletions.
54 changes: 34 additions & 20 deletions src/config/mongoose_config_validator.erl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

-spec validate(mongoose_config_parser_toml:option_value(),
mongoose_config_spec:option_type(), validator()) -> any().
validate(V, binary, domain) -> validate_binary_domain(V);
validate(V, binary, domain) -> validate_domain(V);
validate(V, binary, url) -> validate_non_empty_binary(V);
validate(V, binary, non_empty) -> validate_non_empty_binary(V);
validate(V, binary, subdomain_template) -> validate_subdomain_template(V);
Expand Down Expand Up @@ -122,36 +122,50 @@ validate_jid(Jid) ->
validate_ldap_filter(Value) ->
{ok, _} = eldap_filter:parse(Value).

validate_domain(Domain) when is_list(Domain) ->
#jid{luser = <<>>, lresource = <<>>} = jid:from_binary(list_to_binary(Domain)),
validate_domain_res(Domain).
validate_subdomain_template(SubdomainTemplate) ->
case mongoose_subdomain_utils:make_subdomain_pattern(SubdomainTemplate) of
{fqdn, Domain} ->
validate_domain(Domain);
Pattern ->
Domain = binary_to_list(mongoose_subdomain_utils:get_fqdn(Pattern, <<"example.com">>)),
case inet_parse:domain(Domain) of
true ->
ok;
false ->
error(#{what => validate_subdomain_template_failed,
text => <<"Invalid subdomain template">>,
subdomain_template => SubdomainTemplate})
end
end.

validate_domain(Domain) when is_binary(Domain) ->
validate_domain(binary_to_list(Domain));
validate_domain(Domain) ->
validate_domain_name(Domain),
resolve_domain(Domain).

validate_domain_name(Domain) ->
case inet_parse:domain(Domain) of
true ->
ok;
false ->
error(#{what => validate_domain_failed,
text => <<"Invalid domain name">>,
domain => Domain})
end.

validate_domain_res(Domain) ->
resolve_domain(Domain) ->
case inet_res:gethostbyname(Domain) of
{ok, _} ->
ok;
{error,formerr} ->
error(#{what => cfg_validate_domain_failed,
reason => formerr, text => <<"Invalid domain name">>,
domain => Domain});
{error,Reason} -> %% timeout, nxdomain
{error, Reason} -> %% timeout, nxdomain
?LOG_WARNING(#{what => cfg_validate_domain,
reason => Reason, domain => Domain,
text => <<"Couldn't resolve domain. "
"It could cause issues with production installations">>}),
ignore
end.

validate_binary_domain(Domain) when is_binary(Domain) ->
#jid{luser = <<>>, lresource = <<>>} = jid:from_binary(Domain),
validate_domain_res(binary_to_list(Domain)).

validate_subdomain_template(SubdomainTemplate) ->
Pattern = mongoose_subdomain_utils:make_subdomain_pattern(SubdomainTemplate),
Domain = mongoose_subdomain_utils:get_fqdn(Pattern, <<"example.com">>),
%% TODO: do we want warning printed by validate_domain_res, especially when
%% validating modules.mod_event_pusher_push.virtual_pubsub_hosts option
validate_binary_domain(Domain).

validate_url(Url) ->
validate_non_empty_string(Url).
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
103 changes: 89 additions & 14 deletions test/config_parser_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ all() ->
{group, shaper_acl_access},
{group, s2s},
{group, modules},
{group, services}].
{group, services},
{group, logs}].

groups() ->
[{file, [parallel], [sample_pgsql,
Expand Down Expand Up @@ -230,7 +231,9 @@ groups() ->
modules_without_config,
incorrect_module]},
{services, [parallel], [service_domain_db,
service_mongoose_system_metrics]}
service_mongoose_system_metrics]},
{logs, [], [no_warning_about_subdomain_patterns,
no_warning_for_resolvable_domain]}
].

init_per_suite(Config) ->
Expand Down Expand Up @@ -1862,8 +1865,11 @@ mod_http_upload(_Config) ->
?errh(T(RequiredOpts#{<<"token_bytes">> => 0})),
?errh(T(RequiredOpts#{<<"max_file_size">> => 0})),
?errh(T(RequiredOpts#{<<"host">> => <<"is this a host? no.">>})),
?errh(T(RequiredOpts#{<<"host">> => [<<"invalid.sub@HOST@">>]})),
?errh(T(RequiredOpts#{<<"host">> => [<<"invalid.sub.@HOST@.as.well">>]})),
?errh(T(RequiredOpts#{<<"host">> => <<"invalid-.com">>})),
?errh(T(RequiredOpts#{<<"host">> => <<"-invalid.com">>})),
?errh(T(RequiredOpts#{<<"host">> => [<<"valid.@HOST@">>]})),
?errh(T(RequiredOpts#{<<"host">> => <<"invalid.sub@HOST@">>})),
?errh(T(RequiredOpts#{<<"host">> => <<"invalid.sub.@HOST@.as.well">>})),
?errh(T(RequiredOpts#{<<"host">> => [<<"not.supported.any.more.@HOSTS@">>]})),
check_iqdisc(mod_http_upload, RequiredOpts).

Expand Down Expand Up @@ -1988,8 +1994,9 @@ mod_mam_muc(_Config) ->
?cfgh(P ++ [host], {prefix, <<"muc.">>}, T(#{<<"host">> => <<"muc.@HOST@">>})),
?cfgh(P ++ [host], {fqdn, <<"muc.test">>}, T(#{<<"host">> => <<"muc.test">>})),
?errh(T(#{<<"host">> => <<"is this a host? no.">>})),
?errh(T(#{<<"host">> => [<<"invalid.sub@HOST@">>]})),
?errh(T(#{<<"host">> => [<<"invalid.sub.@HOST@.as.well">>]})),
?errh(T(#{<<"host">> => [<<"valid.@HOST@">>]})),
?errh(T(#{<<"host">> => <<"invalid.sub@HOST@">>})),
?errh(T(#{<<"host">> => <<"invalid.sub.@HOST@.as.well">>})),
?errh(T(#{<<"archive_groupchats">> => true})), % pm-only
?errh(T(#{<<"same_mam_id_for_peers">> => true})). % pm-only

Expand Down Expand Up @@ -2110,8 +2117,9 @@ mod_muc(_Config) ->
T(<<"hibernated_room_timeout">>, 0)),
?errh(T(<<"host">>, <<>>)),
?errh(T(<<"host">>, <<"is this a host? no.">>)),
?errh(T(<<"host">>, [<<"invalid.sub@HOST@">>])),
?errh(T(<<"host">>, [<<"invalid.sub.@HOST@.as.well">>])),
?errh(T(<<"host">>, [<<"valid.@HOST@">>])),
?errh(T(<<"host">>, <<"invalid.sub@HOST@">>)),
?errh(T(<<"host">>, <<"invalid.sub.@HOST@.as.well">>)),
?errh(T(<<"backend">>, <<"amnesia">>)),
?errh(T(<<"access">>, <<>>)),
?errh(T(<<"access_create">>, 1)),
Expand Down Expand Up @@ -2281,8 +2289,11 @@ mod_muc_light(_Config) ->
T(#{<<"rooms_in_rosters">> => true})),
?errh(T(#{<<"backend">> => <<"frontend">>})),
?errh(T(#{<<"host">> => <<"what is a domain?!">>})),
?errh(T(#{<<"host">> => [<<"invalid.sub@HOST@">>]})),
?errh(T(#{<<"host">> => [<<"invalid.sub.@HOST@.as.well">>]})),
?errh(T(#{<<"host">> => <<"invalid..com">>})),
?errh(T(#{<<"host">> => [<<"valid.@HOST@">>]})),
?errh(T(#{<<"host">> => <<"invalid.sub@HOST@">>})),
?errh(T(#{<<"host">> => <<"invalid.sub.@HOST@.as.well">>})),
?errh(T(#{<<"host">> => <<"inv@lidsub.@HOST@">>})),
?errh(T(#{<<"equal_occupants">> => <<"true">>})),
?errh(T(#{<<"legacy_mode">> => 1234})),
?errh(T(#{<<"rooms_per_user">> => 0})),
Expand Down Expand Up @@ -2418,8 +2429,11 @@ mod_pubsub(_Config) ->
test_wpool(P ++ [wpool], fun(Opts) -> T(#{<<"wpool">> => Opts}) end),
?errh(T(#{<<"host">> => <<"">>})),
?errh(T(#{<<"host">> => <<"is this a host? no.">>})),
?errh(T(#{<<"host">> => [<<"invalid.sub@HOST@">>]})),
?errh(T(#{<<"host">> => [<<"invalid.sub.@HOST@.as.well">>]})),
?errh(T(#{<<"host">> => <<"invalid domain.com">>})),
?errh(T(#{<<"host">> => <<"inv@lid.com">>})),
?errh(T(#{<<"host">> => [<<"valid.@HOST@">>]})),
?errh(T(#{<<"host">> => <<"invalid.sub@HOST@">>})),
?errh(T(#{<<"host">> => <<"invalid.sub.@HOST@.as.well">>})),
?errh(T(#{<<"backend">> => <<"amnesia">>})),
?errh(T(#{<<"access_createnode">> => <<"">>})),
?errh(T(#{<<"max_items_node">> => -1})),
Expand Down Expand Up @@ -2750,9 +2764,11 @@ mod_vcard(_Config) ->
?cfgh(P ++ [ldap, binary_search_fields], [<<"PHOTO">>],
T(#{<<"backend">> => <<"ldap">>, <<"ldap">> => #{<<"binary_search_fields">> => [<<"PHOTO">>]}})),
?errh(T(#{<<"host">> => 1})),
?errh(T(#{<<"host">> => <<" ">>})),
?errh(T(#{<<"host">> => <<"is this a host? no.">>})),
?errh(T(#{<<"host">> => [<<"invalid.sub@HOST@">>]})),
?errh(T(#{<<"host">> => [<<"invalid.sub.@HOST@.as.well">>]})),
?errh(T(#{<<"host">> => [<<"valid.@HOST@">>]})),
?errh(T(#{<<"host">> => <<"invalid.sub@HOST@">>})),
?errh(T(#{<<"host">> => <<"invalid.sub.@HOST@.as.well">>})),
?errh(T(#{<<"search">> => 1})),
?errh(T(#{<<"backend">> => <<"mememesia">>})),
?errh(T(#{<<"matches">> => -1})),
Expand Down Expand Up @@ -2871,6 +2887,65 @@ service_mongoose_system_metrics(_Config) ->
?err(T(#{<<"tracking_id">> => #{<<"secret">> => 666, <<"id">> => 666}})),
?err(T(#{<<"report">> => <<"maybe">>})).

no_warning_about_subdomain_patterns(_Config) ->
check_module_defaults(mod_vcard),
check_iqdisc(mod_vcard),
P = [modules, mod_vcard],
T = fun(Opts) -> #{<<"modules">> => #{<<"mod_vcard">> => Opts}} end,

Node = #{node => mongooseim@localhost},
logger_ct_backend:start(Node),
logger_ct_backend:capture(warning, Node),

?cfgh(P ++ [host], {prefix, <<"vjud.">>},
T(#{<<"host">> => <<"vjud.@HOST@">>})),
?cfgh(P ++ [host], {fqdn, <<"vjud.test">>},
T(#{<<"host">> => <<"vjud.test">>})),

logger_ct_backend:stop_capture(Node),
logger_ct_backend:stop(Node),

FilterFun = fun(_, Msg) ->
re:run(Msg, "test") /= nomatch orelse
re:run(Msg, "example.com") /= nomatch
end,
Logs = logger_ct_backend:recv(FilterFun),

?assertNotEqual(0, length(Logs)),
AnyContainsExampleCom = lists:any(fun({_, Msg}) ->
re:run(Msg, "example.com") /= nomatch
end, Logs),
?eq(false, AnyContainsExampleCom).

no_warning_for_resolvable_domain(_Config) ->
T = fun(Opts) -> #{<<"modules">> => #{<<"mod_http_upload">> => Opts}} end,
P = [modules, mod_http_upload],
RequiredOpts = #{<<"s3">> => http_upload_s3_required_opts()},

Node = #{node => mongooseim@localhost},
logger_ct_backend:start(Node),
logger_ct_backend:capture(warning, Node),

?cfgh(P ++ [host], {fqdn, <<"example.org">>},
T(RequiredOpts#{<<"host">> => <<"example.org">>})),
?cfgh(P ++ [host], {fqdn, <<"something.invalid">>},
T(RequiredOpts#{<<"host">> => <<"something.invalid">>})),

logger_ct_backend:stop_capture(Node),
logger_ct_backend:stop(Node),

FilterFun = fun(_, Msg) ->
re:run(Msg, "example.org") /= nomatch orelse
re:run(Msg, "something.invalid") /= nomatch
end,
Logs = logger_ct_backend:recv(FilterFun),

?assertNotEqual(0, length(Logs)),
ResolvableDomainInLogs = lists:any(fun({_, Msg}) ->
re:run(Msg, "example.org") /= nomatch
end, Logs),
?eq(false, ResolvableDomainInLogs).

%% Helpers for module tests

check_iqdisc(Module) ->
Expand Down

0 comments on commit f44b5b0

Please sign in to comment.