Skip to content

Commit 651b8ba

Browse files
authored
Add pool.sync_trusted_certificates_from API (#7056)
Add a new API pool.sync_trusted_certificates_from to facilitate the secure LDAP feature in pool.join case. It is also a general API to download trusted certificates.
2 parents b1eeff8 + fed0b33 commit 651b8ba

5 files changed

Lines changed: 135 additions & 22 deletions

File tree

doc/content/design/external-auth-ldaps.md

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -268,10 +268,10 @@ alt precheck failed
268268
client-->>user: precheck failed
269269
end
270270
271-
Note over client,coor: sync all ldaps certs
272-
client->>coor: pool.download_trusted_certificate
273-
coor-->>client:
274-
client->>join: pool.install_trusted_certificate
271+
Note over client,coor: sync trusted CA certs from coordinator to joining host
272+
client->>join: pool.sync_trusted_certificates_from
273+
join->>coor: pool.exchange_trusted_certificates_on_join
274+
coor-->>join:
275275
join-->>client:
276276
277277
user->>client: join domain username/password
@@ -289,15 +289,11 @@ client-->>user: pool.join succeed
289289

290290
**Detailed Steps:**
291291

292-
1. Client find proper `ldaps certs` from pool coordinator as `certs_pool`
293-
- a. find all certs `ldaps in purpose`
294-
- b. if no LDAPS certs, find all `general` certs
295-
2. Client find all certs in joining host as `certs_joining_host`
296-
3. Client identify the certs needs to be synced to joining host as `certs_to_sync = certs_pool - certs_joining_host` (certs in `certs_pool`, but not in `certs_joining_host`), the certs fingerprint should be used to identify the certs
297-
4. Client download all `certs_to_sync`, `pool.download_trusted_certificate` from coordinator
298-
5. Client upload all certs to joining pool, `pool.install_trusted_certificate` to joining pool, with the same purpose
299-
6. Client trigger `pool.join` again with domain username and password
300-
7. After pool.join:
292+
1. Client calls `pool.sync_trusted_certificates_from` to joiner host. The call will
293+
- a. download all trusted certificates from the pool, and
294+
- b. install the trusted certificates into the joiner host.
295+
2. Client trigger `pool.join` again with domain username and password
296+
3. After pool.join:
301297
- If pool.join failed, Client call `pool.uninstall_trusted_certificate` on joining host to revert the certs
302298
- If pool.join succeed, do nothing as pool.join would sync the certs anyway
303299

ocaml/idl/datamodel_pool.ml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1697,6 +1697,36 @@ let uninstall_trusted_certificate =
16971697
~allowed_roles:(_R_POOL_OP ++ _R_CLIENT_CERT)
16981698
~lifecycle:[] ()
16991699

1700+
let sync_trusted_certificates_from =
1701+
call ~name:"sync_trusted_certificates_from"
1702+
~doc:
1703+
"Download trusted TLS certificates from a remote pool and install them \
1704+
in this pool. Certificates already present locally (matched by \
1705+
fingerprint and purpose) are skipped."
1706+
~params:
1707+
[
1708+
(Ref _pool, "self", "The pool")
1709+
; ( String
1710+
, "remote_pool"
1711+
, "The hostname or IP address of the coordinator of the remote pool \
1712+
from which the certificates are downloaded"
1713+
)
1714+
; ( Ref _session
1715+
, "remote_session"
1716+
, "A session obtained from the remote pool, used to authenticate the \
1717+
download"
1718+
)
1719+
; ( String
1720+
, "remote_certificate"
1721+
, "The PEM-encoded TLS certificate of the remote pool's coordinator, \
1722+
used to verify the TLS connection to the remote pool."
1723+
)
1724+
; (Bool, "ca", "true for 'ca' or false for 'pinned'")
1725+
]
1726+
~result:(Set (Ref _certificate), "The references of certificates synced.")
1727+
~allowed_roles:(_R_POOL_OP ++ _R_CLIENT_CERT)
1728+
~lifecycle:[] ()
1729+
17001730
let trusted_certs = Map (String, Set String)
17011731

17021732
let exchange_trusted_certificates_on_join =
@@ -1849,6 +1879,7 @@ let t =
18491879
; set_ssh_auto_mode
18501880
; install_trusted_certificate
18511881
; uninstall_trusted_certificate
1882+
; sync_trusted_certificates_from
18521883
; exchange_trusted_certificates_on_join
18531884
; exchange_crls_on_join
18541885
]

ocaml/xapi/message_forwarding.ml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1274,6 +1274,21 @@ functor
12741274
(certificate_uuid ~__context certificate) ;
12751275
Local.Pool.uninstall_trusted_certificate ~__context ~self ~certificate
12761276

1277+
let sync_trusted_certificates_from ~__context ~self ~remote_pool
1278+
~remote_session ~remote_certificate ~ca =
1279+
Xapi_pool_helpers.with_pool_operation ~__context
1280+
~op:`copy_primary_host_certs
1281+
~doc:"Pool.sync_trusted_certificates_from"
1282+
~self:(Helpers.get_pool ~__context)
1283+
@@ fun () ->
1284+
info
1285+
"Pool.sync_trusted_certificates_from: pool=%S remote_pool=%S \
1286+
remote_certificate=%S ca=%b"
1287+
(pool_uuid ~__context self)
1288+
remote_pool remote_certificate ca ;
1289+
Local.Pool.sync_trusted_certificates_from ~__context ~self ~remote_pool
1290+
~remote_session ~remote_certificate ~ca
1291+
12771292
let exchange_trusted_certificates_on_join ~__context ~self ~ca ~import
12781293
~export =
12791294
Xapi_pool_helpers.with_pool_operation ~__context

ocaml/xapi/xapi_pool.ml

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,7 +1688,7 @@ let certificate_sync ~__context =
16881688
Certificates.sync_all_hosts ~__context (Db.Host.get_all ~__context) ;
16891689
()
16901690

1691-
let install_trusted_certificate ~__context ~self:_ ~ca ~cert ~purpose =
1691+
let install_trusted_certificate' ~__context ~self:_ ~ca ~cert ~purpose =
16921692
let open Certificates in
16931693
let certificate =
16941694
let open Api_errors in
@@ -1712,12 +1712,18 @@ let install_trusted_certificate ~__context ~self:_ ~ca ~cert ~purpose =
17121712
| false, true ->
17131713
raise Api_errors.(Server_error (certificate_lacks_purpose, []))
17141714
in
1715-
let (_ : API.ref_Certificate), uuid =
1715+
let (ref : API.ref_Certificate), uuid =
17161716
Db_util.add_cert ~__context ~type':cert_type ~purpose certificate
17171717
in
17181718
let name = Certificates.name_of_uuid uuid in
17191719
Certificates.host_install kind ~name ~cert ;
17201720
Cert_distrib.copy_certs_to_all ~__context ;
1721+
ref
1722+
1723+
let install_trusted_certificate ~__context ~self ~ca ~cert ~purpose =
1724+
let (_ : API.ref_Certificate) =
1725+
install_trusted_certificate' ~__context ~self ~ca ~cert ~purpose
1726+
in
17211727
()
17221728

17231729
let uninstall_trusted_certificate ~__context ~self:_ ~certificate =
@@ -1742,15 +1748,29 @@ let uninstall_trusted_certificate ~__context ~self:_ ~certificate =
17421748
Cert_distrib.copy_certs_to_all ~__context ;
17431749
()
17441750

1745-
let install_trusted_certificate_ignore_dup ~__context ~self ~ca ~cert ~purpose =
1746-
try install_trusted_certificate ~__context ~self ~ca ~cert ~purpose
1751+
let install_trusted_certificate_ignore_dup' ~__context ~self ~ca ~cert ~purpose
1752+
=
1753+
try
1754+
Ok (Some (install_trusted_certificate' ~__context ~self ~ca ~cert ~purpose))
17471755
with
17481756
| Api_errors.(Server_error (code, [fp]))
1749-
when code = Api_errors.trusted_certificate_already_exists
1750-
->
1751-
warn "%s: a trusted certificate (fingerprint=%s) exists already."
1752-
__FUNCTION__ fp ;
1753-
()
1757+
when code = Api_errors.trusted_certificate_already_exists ->
1758+
warn "%s: a trusted certificate (fingerprint=%s) exists already."
1759+
__FUNCTION__ fp ;
1760+
Ok None
1761+
| e ->
1762+
error "%s: failed to install certificate: %s" __FUNCTION__
1763+
(Printexc.to_string e) ;
1764+
Error e
1765+
1766+
let install_trusted_certificate_ignore_dup ~__context ~self ~ca ~cert ~purpose =
1767+
match
1768+
install_trusted_certificate_ignore_dup' ~__context ~self ~ca ~cert ~purpose
1769+
with
1770+
| Ok _ ->
1771+
()
1772+
| Error e ->
1773+
raise e
17541774

17551775
let purpose_of_string_list = List.map Record_util.certificate_purpose_of_string
17561776

@@ -1788,6 +1808,48 @@ let exchange_trusted_certificates ~__context ~rpc ~session_id ~remote ~local =
17881808
)
17891809
[`ca; `pinned]
17901810

1811+
let sync_trusted_certificates_from ~__context ~self ~remote_pool ~remote_session
1812+
~remote_certificate ~ca =
1813+
let rpc =
1814+
Helpers.make_external_host_verified_rpc ~__context remote_pool
1815+
remote_certificate
1816+
in
1817+
let session_id = remote_session in
1818+
let cert_type =
1819+
if ca then
1820+
"ca"
1821+
else
1822+
"pinned"
1823+
in
1824+
let expr =
1825+
Printf.sprintf {|field "type"="%s" and field "name"=""|} cert_type
1826+
in
1827+
let export =
1828+
Client.Certificate.get_all_records_where ~rpc ~session_id ~expr
1829+
|> List.map fst
1830+
in
1831+
Client.Pool.exchange_trusted_certificates_on_join ~rpc ~session_id
1832+
~self:(get_pool ~rpc ~session_id)
1833+
~ca ~import:[] ~export
1834+
|> Listext.List.try_map_collect (fun (cert, purpose) ->
1835+
let purpose = purpose_of_string_list purpose in
1836+
install_trusted_certificate_ignore_dup' ~__context
1837+
~self:(Helpers.get_pool ~__context)
1838+
~ca ~cert ~purpose
1839+
)
1840+
|> function
1841+
| Ok refs ->
1842+
List.filter_map Fun.id refs
1843+
| Error (refs, e) ->
1844+
List.filter_map Fun.id refs
1845+
|> List.iter (fun ref ->
1846+
try uninstall_trusted_certificate ~__context ~self ~certificate:ref
1847+
with e ->
1848+
error "Can't revert the installed certificate %s: %s"
1849+
(Ref.string_of ref) (Printexc.to_string e)
1850+
) ;
1851+
raise e
1852+
17911853
let exchange_crls_on_join ~__context ~self:_ ~import ~export =
17921854
List.iter (fun (name, crl) -> crl_install ~__context ~name ~cert:crl) import ;
17931855
Cert_distrib.collect_crls ~__context ~names:export

ocaml/xapi/xapi_pool.mli

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,3 +478,12 @@ val exchange_crls_on_join :
478478
-> import:API.string_to_string_map
479479
-> export:string list
480480
-> API.string_to_string_map
481+
482+
val sync_trusted_certificates_from :
483+
__context:Context.t
484+
-> self:API.ref_pool
485+
-> remote_pool:string
486+
-> remote_session:API.ref_session
487+
-> remote_certificate:string
488+
-> ca:bool
489+
-> API.ref_Certificate list

0 commit comments

Comments
 (0)