Skip to content

Commit a24de18

Browse files
committed
* Add support for testing TLS connections to OpenLDAP
* Add support for validating TLS related configuration via `/ldap/validate/simple-bind`
1 parent d2ffd01 commit a24de18

File tree

3 files changed

+126
-28
lines changed

3 files changed

+126
-28
lines changed

deps/rabbitmq_auth_backend_ldap/src/rabbit_auth_backend_ldap_mgmt.erl

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,29 +46,34 @@ is_authorized(ReqData, Context) ->
4646
accept_content(ReqData0, Context) ->
4747
F = fun (_Values, BodyMap, ReqData1) ->
4848
Port = rabbit_mgmt_util:parse_int(maps:get(port, BodyMap, 389)),
49-
_UseSsl = rabbit_mgmt_util:parse_bool(maps:get(use_ssl, BodyMap, false)),
50-
_UseStartTls = rabbit_mgmt_util:parse_bool(maps:get(use_starttls, BodyMap, false)),
49+
UseSsl = rabbit_mgmt_util:parse_bool(maps:get(use_ssl, BodyMap, false)),
50+
UseStartTls = rabbit_mgmt_util:parse_bool(maps:get(use_starttls, BodyMap, false)),
5151
Servers = maps:get(servers, BodyMap, []),
5252
UserDN = maps:get(user_dn, BodyMap, <<"">>),
5353
Password = maps:get(password, BodyMap, <<"">>),
54-
Options = [
54+
Options0 = [
5555
{port, Port},
56-
{timeout, 5000},
57-
{ssl, false}
56+
{timeout, 5000}
5857
],
59-
?LOG_DEBUG("eldap:open Servers: ~tp Options: ~tp", [Servers, Options]),
60-
case eldap:open(Servers, Options) of
58+
{ok, Options1} = maybe_add_ssl_options(Options0, UseSsl, BodyMap),
59+
?LOG_DEBUG("eldap:open Servers: ~tp Options1: ~tp", [Servers, Options1]),
60+
case eldap:open(Servers, Options1) of
6161
{ok, LDAP} ->
62-
?LOG_DEBUG("eldap:simple_bind UserDN: ~tp Password: ~tp", [UserDN, Password]),
63-
Result = case eldap:simple_bind(LDAP, UserDN, Password) of
62+
Result = case maybe_starttls(LDAP, UseStartTls, BodyMap) of
6463
ok ->
65-
{true, ReqData1, Context};
66-
{error, invalidCredentials} ->
67-
rabbit_mgmt_util:not_authorised("invalid credentials", ReqData1, Context);
68-
{error, unwillingToPerform} ->
69-
rabbit_mgmt_util:not_authorised("invalid credentials", ReqData1, Context);
70-
{error, E} ->
71-
Reason = unicode_format(E),
64+
case eldap:simple_bind(LDAP, UserDN, Password) of
65+
ok ->
66+
{true, ReqData1, Context};
67+
{error, invalidCredentials} ->
68+
rabbit_mgmt_util:not_authorised("invalid credentials", ReqData1, Context);
69+
{error, unwillingToPerform} ->
70+
rabbit_mgmt_util:not_authorised("invalid credentials", ReqData1, Context);
71+
{error, E} ->
72+
Reason = unicode_format(E),
73+
rabbit_mgmt_util:bad_request(Reason, ReqData1, Context)
74+
end;
75+
Error ->
76+
Reason = unicode_format(Error),
7277
rabbit_mgmt_util:bad_request(Reason, ReqData1, Context)
7378
end,
7479
eldap:close(LDAP),
@@ -84,3 +89,44 @@ accept_content(ReqData0, Context) ->
8489

8590
unicode_format(Arg) ->
8691
rabbit_data_coercion:to_utf8_binary(io_lib:format("~tp", [Arg])).
92+
93+
maybe_starttls(_LDAP, false, _BodyMap) ->
94+
ok;
95+
maybe_starttls(LDAP, true, BodyMap) ->
96+
{ok, TlsOptions} = tls_options(BodyMap),
97+
?LOG_DEBUG("maybe_starttls TlsOptions ~tp", [TlsOptions]),
98+
eldap:start_tls(LDAP, TlsOptions, 5000).
99+
100+
maybe_add_ssl_options(Options0, false, _BodyMap) ->
101+
{ok, Options0};
102+
maybe_add_ssl_options(Options0, true, BodyMap) ->
103+
case maps:is_key(ssl_options, BodyMap) of
104+
false ->
105+
{ok, Options0};
106+
true ->
107+
Options1 = [{ssl, true} | Options0],
108+
{ok, TlsOptions} = tls_options(BodyMap),
109+
Options2 = [{sslopts, TlsOptions} | Options1],
110+
{ok, Options2}
111+
end.
112+
113+
tls_options(BodyMap) ->
114+
case maps:get(ssl_options, BodyMap, undefined) of
115+
undefined ->
116+
{ok, []};
117+
SslOptionsMap ->
118+
%% NB: for some reason the "cacertfile" key isn't turned into an atom
119+
TlsOpts0 = case maps:get(<<"cacertfile">>, SslOptionsMap, undefined) of
120+
undefined ->
121+
[];
122+
CaCertfile ->
123+
[{cacertfile, CaCertfile}]
124+
end,
125+
TlsOpts1 = case maps:get(<<"server_name_indication">>, SslOptionsMap, disable) of
126+
disable ->
127+
TlsOpts0;
128+
SniValue ->
129+
[{server_name_indication, SniValue} | TlsOpts0]
130+
end,
131+
{ok, TlsOpts1}
132+
end.

deps/rabbitmq_auth_backend_ldap/test/system_SUITE.erl

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,23 @@ end_per_group(_, Config) ->
162162
init_slapd(Config) ->
163163
DataDir = ?config(data_dir, Config),
164164
PrivDir = ?config(priv_dir, Config),
165+
CertsDir = ?config(rmq_certsdir, Config),
166+
CaCertfile = filename:join([CertsDir, "testca", "cacert.pem"]),
167+
ServerCertfile = filename:join([CertsDir, "server", "cert.pem"]),
168+
ServerKeyfile = filename:join([CertsDir, "server", "key.pem"]),
165169
TcpPort = 25389,
170+
TlsPort = 25689,
166171
SlapdDir = filename:join([PrivDir, "openldap"]),
167172
InitSlapd = filename:join([DataDir, "init-slapd.sh"]),
168-
Cmd = [InitSlapd, SlapdDir, {"~b", [TcpPort]}],
173+
Cmd = [
174+
InitSlapd,
175+
SlapdDir,
176+
{"~b", [TcpPort]},
177+
{"~b", [TlsPort]},
178+
CaCertfile,
179+
ServerCertfile,
180+
ServerKeyfile
181+
],
169182
case rabbit_ct_helpers:exec(Cmd) of
170183
{ok, Stdout} ->
171184
{match, [SlapdPid]} = re:run(
@@ -178,7 +191,8 @@ init_slapd(Config) ->
178191
[SlapdPid, TcpPort]),
179192
rabbit_ct_helpers:set_config(Config,
180193
[{slapd_pid, SlapdPid},
181-
{ldap_port, TcpPort}]);
194+
{ldap_port, TcpPort},
195+
{ldap_tls_port, TlsPort}]);
182196
_ ->
183197
_ = rabbit_ct_helpers:exec(["pkill", "-INT", "slapd"]),
184198
{skip, "Failed to initialize slapd(8)"}
@@ -282,13 +296,18 @@ end_per_testcase(Testcase, Config) ->
282296
%% -------------------------------------------------------------------
283297

284298
validate_ldap_configuration_via_api(Config) ->
299+
CertsDir = ?config(rmq_certsdir, Config),
300+
CaCertfile = filename:join([CertsDir, "testca", "cacert.pem"]),
301+
285302
%% {user_dn_pattern, "cn=${username},ou=People,dc=rabbitmq,dc=com"},
286303
UserDNFmt = "cn=~ts,ou=People,dc=rabbitmq,dc=com",
287304
AliceUserDN = rabbit_data_coercion:to_utf8_binary(io_lib:format(UserDNFmt, [?ALICE_NAME])),
288305
InvalidUserDN = rabbit_data_coercion:to_utf8_binary(io_lib:format(UserDNFmt, ["NOBODY"])),
289306
Password = rabbit_data_coercion:to_utf8_binary("password"),
290307

291308
LdapPort = ?config(ldap_port, Config),
309+
LdapTlsPort = ?config(ldap_tls_port, Config),
310+
292311
%% NB: bad resource name
293312
http_put(Config, "/ldap/validate/bad-bind-name",
294313
#{
@@ -310,7 +329,31 @@ validate_ldap_configuration_via_api(Config) ->
310329
'password' => Password,
311330
'servers' => ["localhost"],
312331
'port' => LdapPort
313-
}, ?NOT_AUTHORISED).
332+
}, ?NOT_AUTHORISED),
333+
http_put(Config, "/ldap/validate/simple-bind",
334+
#{
335+
'user_dn' => AliceUserDN,
336+
'password' => Password,
337+
'servers' => ["localhost"],
338+
'port' => LdapTlsPort,
339+
'use_ssl' => true,
340+
'ssl_options' => #{
341+
'cacertfile' => CaCertfile
342+
}
343+
}, ?NO_CONTENT),
344+
http_put(Config, "/ldap/validate/simple-bind",
345+
#{
346+
'user_dn' => AliceUserDN,
347+
'password' => Password,
348+
'servers' => ["localhost"],
349+
'port' => LdapPort,
350+
'use_ssl' => false,
351+
'use_starttls' => true,
352+
'ssl_options' => #{
353+
'server_name_indication' => "localhost",
354+
'cacertfile' => CaCertfile
355+
}
356+
}, ?NO_CONTENT).
314357

315358
purge_connection(Config) ->
316359
{ok, _} = rabbit_ct_broker_helpers:rpc(Config, 0,

deps/rabbitmq_auth_backend_ldap/test/system_SUITE_data/init-slapd.sh

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
#!/bin/sh
22
# vim:sw=4:et:
33

4-
set -ex
4+
set -eux
55

66
readonly slapd_data_dir="$1"
77
readonly tcp_port="$2"
8+
readonly tls_port="$3"
9+
readonly cacertfile="$4"
10+
readonly server_certfile="$5"
11+
readonly server_keyfile="$6"
812

913
readonly pidfile="$slapd_data_dir/slapd.pid"
10-
readonly uri="ldap://localhost:$tcp_port"
14+
readonly tcp_uri="ldap://localhost:$tcp_port"
15+
readonly tls_uri="ldaps://localhost:$tls_port"
1116

1217
readonly binddn="cn=config"
1318
readonly passwd=secret
@@ -68,6 +73,10 @@ loglevel 7
6873
database config
6974
rootdn "$binddn"
7075
rootpw $passwd
76+
77+
TLSCACertificateFile $cacertfile
78+
TLSCertificateFile $server_certfile
79+
TLSCertificateKeyFile $server_keyfile
7180
EOF
7281

7382
cat "$conf_file"
@@ -79,15 +88,15 @@ mkdir -p "$conf_dir"
7988
"$slapd" \
8089
-f "$conf_file" \
8190
-F "$conf_dir" \
82-
-h "$uri"
91+
-h "$tcp_uri $tls_uri"
8392

8493
readonly auth="-x -D $binddn -w $passwd"
8594

8695
# We wait for the server to start.
8796
# shellcheck disable=SC2034
8897
for seconds in 1 2 3 4 5 6 7 8 9 10; do
8998
# shellcheck disable=SC2086
90-
ldapsearch $auth -H "$uri" -LLL -b cn=config dn && break;
99+
ldapsearch $auth -H "$tcp_uri" -LLL -b cn=config dn && break;
91100
sleep 1
92101
done
93102

@@ -106,22 +115,22 @@ mkdir -p "$example_data_dir"
106115
# shellcheck disable=SC2086
107116
sed -E -e "s,^olcDbDirectory:.*,olcDbDirectory: $example_data_dir," \
108117
< "$example_ldif_dir/global.ldif" | \
109-
ldapadd $auth -H "$uri"
118+
ldapadd $auth -H "$tcp_uri"
110119

111120
# We remove the module path from the example LDIF as it was already
112121
# configured.
113122
# shellcheck disable=SC2086
114123
sed -E -e "s,^olcModulePath:.*,olcModulePath: $modulepath," \
115124
< "$example_ldif_dir/memberof_init.ldif" | \
116-
ldapadd $auth -H "$uri"
125+
ldapadd $auth -H "$tcp_uri"
117126

118127
# shellcheck disable=SC2086
119-
ldapmodify $auth -H "$uri" -f "$example_ldif_dir/refint_1.ldif"
128+
ldapmodify $auth -H "$tcp_uri" -f "$example_ldif_dir/refint_1.ldif"
120129

121130
# shellcheck disable=SC2086
122-
ldapadd $auth -H "$uri" -f "$example_ldif_dir/refint_2.ldif"
131+
ldapadd $auth -H "$tcp_uri" -f "$example_ldif_dir/refint_2.ldif"
123132

124133
# shellcheck disable=SC2086
125-
ldapsearch $auth -H "$uri" -LLL -b cn=config dn
134+
ldapsearch $auth -H "$tcp_uri" -LLL -b cn=config dn
126135

127136
echo SLAPD_PID="$(cat "$pidfile")"

0 commit comments

Comments
 (0)