Skip to content

Commit 596b991

Browse files
authored
CP-311293: Add sync between set ldaps and other query ops (#6966)
External auth configure/write ops(enable, disable, set-ldaps) should be synced with read ops(auth-username-and-password, etc). Xapi/ocaml does not prefer read/write locks, so we just use classic mutex, presuming no much concurrent ops. At the same time, serialize_auth_service is introduced to skip the lock if performance is preferred
2 parents e17315c + 0bcc46e commit 596b991

2 files changed

Lines changed: 42 additions & 10 deletions

File tree

ocaml/xapi/extauth_plugin_ADwinbind.ml

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,24 @@ let finally = Xapi_stdext_pervasives.Pervasiveext.finally
2929

3030
let krbtgt = "KRBTGT"
3131

32-
let ( let* ) = Result.bind
32+
let with_lock = Xapi_stdext_threads.Threadext.Mutex.execute
33+
34+
(* Mutex for serializing AD external auth operations.
35+
* Write ops (enable/disable/set_ldaps) modify winbind config and domain state.
36+
* Read ops (authenticate/query) query AD via wbinfo.
37+
* A plain Mutex serializes both. The [serialize_auth_service] config key
38+
* (default: true) can be set to false to skip locking under concurrent load,
39+
* but only when configure and authenticate calls are never concurrent. *)
40+
let serialize_ext_auth_lock = Mutex.create ()
41+
42+
let cond_sync_ext_auth f =
43+
match !Xapi_globs.serialize_auth_service with
44+
| true ->
45+
with_lock serialize_ext_auth_lock f
46+
| false ->
47+
f ()
3348

34-
let ( let@ ) = ( @@ )
49+
let ( let* ) = Result.bind
3550

3651
let ( <!> ) x f = Rresult.R.reword_error f x
3752

@@ -1166,6 +1181,8 @@ end
11661181

11671182
(* Enable or disable LDAPS for external authentication *)
11681183
let set_ldaps ~__context ~ldaps ~force =
1184+
Context.with_tracing ~__context __FUNCTION__ @@ fun __context ->
1185+
cond_sync_ext_auth @@ fun () ->
11691186
debug "%s:%d set_ldaps ldaps=%b force=%b" __FUNCTION__ __LINE__ ldaps force ;
11701187
let old_domain_info = DomainInfo.of_db ~__context in
11711188

@@ -1410,7 +1427,8 @@ module AuthADWinbind : Auth_signature.AUTH_MODULE = struct
14101427
Raises Not_found (*Subject_cannot_be_resolved*) if authentication is not succesful.
14111428
*)
14121429
let get_subject_identifier ~__context subject_name =
1413-
let@ __context = Context.with_tracing ~__context __FUNCTION__ in
1430+
Context.with_tracing ~__context __FUNCTION__ @@ fun __context ->
1431+
cond_sync_ext_auth @@ fun () ->
14141432
maybe_raise (get_subject_identifier' ~__context subject_name)
14151433

14161434
(* subject_id Authenticate_username_password(string username, string password)
@@ -1425,7 +1443,8 @@ module AuthADWinbind : Auth_signature.AUTH_MODULE = struct
14251443
*)
14261444

14271445
let authenticate_username_password ~__context uname password =
1428-
let@ __context = Context.with_tracing ~__context __FUNCTION__ in
1446+
Context.with_tracing ~__context __FUNCTION__ @@ fun __context ->
1447+
cond_sync_ext_auth @@ fun () ->
14291448
(* the `wbinfo --krb5auth` expects the username to be in either SAM or UPN format.
14301449
* we use wbinfo to try to convert the provided [uname] into said format.
14311450
* as a last ditch attempt, we try to auth with the provided [uname]
@@ -1564,7 +1583,8 @@ module AuthADWinbind : Auth_signature.AUTH_MODULE = struct
15641583
Raises Not_found (*Subject_cannot_be_resolved*) if subject_id cannot be resolved by external auth service
15651584
*)
15661585
let query_subject_information ~__context (sid : string) =
1567-
let@ __context = Context.with_tracing ~__context __FUNCTION__ in
1586+
Context.with_tracing ~__context __FUNCTION__ @@ fun __context ->
1587+
cond_sync_ext_auth @@ fun () ->
15681588
let res =
15691589
let* name = Wbinfo.name_of_sid sid in
15701590
match name with
@@ -1587,7 +1607,8 @@ module AuthADWinbind : Auth_signature.AUTH_MODULE = struct
15871607
supports nested groups (as AD does for example)
15881608
*)
15891609
let query_group_membership ~__context subject_identifier =
1590-
let@ __context = Context.with_tracing ~__context __FUNCTION__ in
1610+
Context.with_tracing ~__context __FUNCTION__ @@ fun __context ->
1611+
cond_sync_ext_auth @@ fun () ->
15911612
maybe_raise (Wbinfo.user_domgroups subject_identifier)
15921613

15931614
let assert_join_domain_user_format uname =
@@ -1614,7 +1635,8 @@ module AuthADWinbind : Auth_signature.AUTH_MODULE = struct
16141635
does not need long-term.]
16151636
*)
16161637
let on_enable ~__context config_params =
1617-
let@ __context = Context.with_tracing ~__context __FUNCTION__ in
1638+
Context.with_tracing ~__context __FUNCTION__ @@ fun __context ->
1639+
cond_sync_ext_auth @@ fun () ->
16181640
let user =
16191641
from_config ~name:"user" ~err_msg:"enable requires user" ~config_params
16201642
in
@@ -1725,7 +1747,8 @@ module AuthADWinbind : Auth_signature.AUTH_MODULE = struct
17251747
within the body of the on_disable method)
17261748
*)
17271749
let on_disable ~__context config_params =
1728-
let@ __context = Context.with_tracing ~__context __FUNCTION__ in
1750+
Context.with_tracing ~__context __FUNCTION__ @@ fun __context ->
1751+
cond_sync_ext_auth @@ fun () ->
17291752
let user = List.assoc_opt "user" config_params in
17301753
let pass = List.assoc_opt "pass" config_params in
17311754
let {service_name; netbios_name; _} = DomainInfo.of_db ~__context in
@@ -1753,8 +1776,8 @@ module AuthADWinbind : Auth_signature.AUTH_MODULE = struct
17531776
starting for the first time after a host boot
17541777
*)
17551778
let on_xapi_initialize ~__context _system_boot =
1756-
let@ __context = Context.with_tracing ~__context __FUNCTION__ in
1757-
1779+
Context.with_tracing ~__context __FUNCTION__ @@ fun __context ->
1780+
cond_sync_ext_auth @@ fun () ->
17581781
Winbind.start ~timeout:5. ~wait_until_success:true ;
17591782
RotateMachinePassword.trigger_rotate ~__context ~start:5. ;
17601783
Winbind.check_ready_to_serve ~timeout:300. ;

ocaml/xapi/xapi_globs.ml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,8 @@ let winbind_scan_trusted_domains = ref false
10671067

10681068
let winbind_keep_configuration = ref false
10691069

1070+
let serialize_auth_service = ref true
1071+
10701072
let winbind_ldap_query_subject_timeout = ref Mtime.Span.(20 * s)
10711073

10721074
let tdb_tool = ref "/usr/bin/tdbtool"
@@ -1672,6 +1674,13 @@ let other_options =
16721674
, "Whether to clear winbind configuration when join domain failed or leave \
16731675
domain"
16741676
)
1677+
; ( "serialize_auth_service"
1678+
, Arg.Bool (fun b -> serialize_auth_service := b)
1679+
, (fun () -> string_of_bool !serialize_auth_service)
1680+
, "Serialize AD external auth operations under a mutex (default: true). \
1681+
Set to false only if configure (enable/disable/set-ldaps) and \
1682+
authenticate calls are never concurrent to improve performance."
1683+
)
16751684
; ( "hsts_max_age"
16761685
, Arg.Set_int hsts_max_age
16771686
, (fun () -> string_of_int !hsts_max_age)

0 commit comments

Comments
 (0)