Skip to content

Commit 580a111

Browse files
authored
Merge pull request #4595 from esl/tomasz/mim-2512-tls-cert-validation
Add TLS keyfile validation
2 parents b7dec22 + b89bdb9 commit 580a111

File tree

5 files changed

+36
-9
lines changed

5 files changed

+36
-9
lines changed

src/config/mongoose_config_spec.erl

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,8 @@ tls(common) ->
655655
},
656656
defaults = #{<<"verify_mode">> => peer,
657657
<<"disconnect_on_failure">> => true,
658-
<<"crl_files">> => []}};
658+
<<"crl_files">> => []},
659+
process = fun validate_tls_options/1};
659660
tls(server) ->
660661
#section{items = #{<<"dhfile">> => #option{type = string, validate = filename},
661662
<<"early_data">> => #option{type = boolean},
@@ -1096,3 +1097,17 @@ ip_version(T) when tuple_size(T) =:= 8 -> inet6.
10961097

10971098
process_infinity_as_zero(infinity) -> 0;
10981099
process_infinity_as_zero(Num) -> Num.
1100+
1101+
%% Validate that certfile and keyfile are separate files
1102+
%% OTP 28.1+ doesn't allow concatenated certificate with private key
1103+
validate_tls_options(TLSConfig) ->
1104+
case maps:is_key(certfile, TLSConfig) andalso not maps:is_key(keyfile, TLSConfig) of
1105+
true ->
1106+
error(#{what => tls_certfile_without_keyfile,
1107+
text => <<"TLS certfile provided without a separate keyfile. "
1108+
"Since OTP 28.1, concatenated certificate and private key files "
1109+
"are not supported. Please provide a separate keyfile option.">>,
1110+
certfile => maps:get(certfile, TLSConfig)});
1111+
false ->
1112+
TLSConfig
1113+
end.

test/common/config_parser_helper.erl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ options("mongooseim-pgsql") ->
245245
max_stanza_size => 131072,
246246
tls => #{cacertfile => "priv/ca.pem",
247247
certfile => "priv/cert.pem",
248+
keyfile => "priv/dc1.pem",
248249
dhfile => "priv/dh.pem"}
249250
})
250251
]},
@@ -477,7 +478,8 @@ all_modules() ->
477478
connections_per_endpoint => 30,
478479
tls => config([modules, mod_global_distrib, connections, tls],
479480
#{cacertfile => "priv/ca.pem",
480-
certfile => "priv/dc1.pem"})
481+
certfile => "priv/cert.pem",
482+
keyfile => "priv/dc1.pem"})
481483
})
482484
}),
483485
mod_pubsub =>

test/config_parser_SUITE.erl

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,20 +1173,25 @@ test_just_tls_common(P, T) ->
11731173
?cfg(P ++ [verify_mode], none, T(#{<<"verify_mode">> => <<"none">>})),
11741174
M = tls_ca_raw(),
11751175
?cfg(P ++ [cacertfile], "priv/ca.pem", T(M)),
1176-
?cfg(P ++ [certfile], "priv/cert.pem", T(M#{<<"certfile">> => <<"priv/cert.pem">>})),
1176+
%% Certfile with keyfile should work
1177+
?cfg(P ++ [certfile], "priv/cert.pem", T(M#{<<"certfile">> => <<"priv/cert.pem">>,
1178+
<<"keyfile">> => <<"priv/dc1.pem">>})),
11771179
?cfg(P ++ [ciphers], "TLS_AES_256_GCM_SHA384",
11781180
T(M#{<<"ciphers">> => <<"TLS_AES_256_GCM_SHA384">>})),
11791181
?cfg(P ++ [keyfile], "priv/dc1.pem", T(M#{<<"keyfile">> => <<"priv/dc1.pem">>})),
11801182
?cfg(P ++ [password], "secret", T(M#{<<"password">> => <<"secret">>})),
11811183
?cfg(P ++ [versions], ['tlsv1.2', 'tlsv1.3'],
11821184
T(M#{<<"versions">> => [<<"tlsv1.2">>, <<"tlsv1.3">>]})),
11831185
?err(T(#{<<"verify_mode">> => <<"whatever">>})),
1184-
?err(T(M#{<<"certfile">> => <<"no_such_file.pem">>})),
11851186
?err(T(M#{<<"cacertfile">> => <<"no_such_file.pem">>})),
11861187
?err(T(M#{<<"ciphers">> => [<<"TLS_AES_256_GCM_SHA384">>]})),
11871188
?err(T(M#{<<"keyfile">> => <<"no_such_file.pem">>})),
11881189
?err(T(M#{<<"password">> => false})),
1189-
?err(T(M#{<<"versions">> => <<"tlsv1.2">>})).
1190+
?err(T(M#{<<"versions">> => <<"tlsv1.2">>})),
1191+
%% Certfile without keyfile should fail (OTP 28.1+ requirement)
1192+
?err(T(#{<<"certfile">> => <<"priv/cert.pem">>})),
1193+
?err(T(M#{<<"certfile">> => <<"priv/cert.pem">>})),
1194+
?err(T(M#{<<"certfile">> => <<"no_such_file.pem">>})).
11901195

11911196
test_just_tls_client_sni(ParentP, ParentT) ->
11921197
P = ParentP ++ [server_name_indication],
@@ -1447,20 +1452,23 @@ s2s_outgoing_tls(_Config) ->
14471452
?cfgh(P, default_config(P), T(#{})), % default options if tls section is present
14481453
?cfgh(P ++ [verify_mode], none, T(#{<<"verify_mode">> => <<"none">>})),
14491454
?cfgh(P ++ [cacertfile], "priv/ca.pem", T(tls_ca_raw())),
1450-
?cfgh(P ++ [certfile], "priv/cert.pem", T(#{<<"certfile">> => <<"priv/cert.pem">>})),
1455+
?cfgh(P ++ [certfile], "priv/cert.pem", T(#{<<"certfile">> => <<"priv/cert.pem">>,
1456+
<<"keyfile">> => <<"priv/dc1.pem">>})),
14511457
?cfgh(P ++ [ciphers], "TLS_AES_256_GCM_SHA384",
14521458
T(#{<<"ciphers">> => <<"TLS_AES_256_GCM_SHA384">>})),
14531459
?cfgh(P ++ [keyfile], "priv/dc1.pem", T(#{<<"keyfile">> => <<"priv/dc1.pem">>})),
14541460
?cfgh(P ++ [password], "secret", T(#{<<"password">> => <<"secret">>})),
14551461
?cfgh(P ++ [versions], ['tlsv1.2', 'tlsv1.3'],
14561462
T(#{<<"versions">> => [<<"tlsv1.2">>, <<"tlsv1.3">>]})),
14571463
?err(T(#{<<"verify_mode">> => <<"whatever">>})),
1458-
?err(T(#{<<"certfile">> => <<"no_such_file.pem">>})),
14591464
?err(T(#{<<"cacertfile">> => <<"no_such_file.pem">>})),
14601465
?err(T(#{<<"ciphers">> => [<<"TLS_AES_256_GCM_SHA384">>]})),
14611466
?err(T(#{<<"keyfile">> => <<"no_such_file.pem">>})),
14621467
?err(T(#{<<"password">> => false})),
1463-
?err(T(#{<<"versions">> => <<"tlsv1.2">>})).
1468+
?err(T(#{<<"versions">> => <<"tlsv1.2">>})),
1469+
%% Certfile without keyfile should fail (OTP 28.1+ requirement)
1470+
?err(T(#{<<"certfile">> => <<"priv/cert.pem">>})),
1471+
?err(T(#{<<"certfile">> => <<"no_such_file.pem">>})).
14641472

14651473
%% modules
14661474

test/config_parser_SUITE_data/modules.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@
120120
local_host = "datacenter1.example.com"
121121
connections.endpoints = [{host = "172.16.0.2", port = 5555}]
122122
connections.advertised_endpoints = [{host = "172.16.0.2", port = 5555}]
123-
connections.tls.certfile = "priv/dc1.pem"
123+
connections.tls.certfile = "priv/cert.pem"
124+
connections.tls.keyfile = "priv/dc1.pem"
124125
connections.tls.cacertfile = "priv/ca.pem"
125126
connections.connections_per_endpoint = 30
126127
cache.domain_lifetime_seconds = 60

test/config_parser_SUITE_data/mongooseim-pgsql.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
max_stanza_size = 131072
100100
tls.dhfile = "priv/dh.pem"
101101
tls.certfile = "priv/cert.pem"
102+
tls.keyfile = "priv/dc1.pem"
102103
tls.cacertfile = "priv/ca.pem"
103104

104105
[[listen.component]]

0 commit comments

Comments
 (0)