From 3ebe9c16151d6bce422df3034ab6e2c7c5e0da6a Mon Sep 17 00:00:00 2001 From: Pau Ruiz Safont Date: Wed, 7 Feb 2024 14:50:09 +0000 Subject: [PATCH 1/3] CA-385323: do not try to connect to xapi when creating sockets Instead delay the connection to each request. These will still fail when xapi is offline. Currently means roundtrips are done, depending on the request. Signed-off-by: Pau Ruiz Safont --- ocaml/xapi-guard/lib/server_interface.ml | 68 +++++++++++++++--------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/ocaml/xapi-guard/lib/server_interface.ml b/ocaml/xapi-guard/lib/server_interface.ml index 5b2acd7c27c..f76118aec72 100644 --- a/ocaml/xapi-guard/lib/server_interface.ml +++ b/ocaml/xapi-guard/lib/server_interface.ml @@ -152,7 +152,26 @@ let update_key key state t = let empty = "" -let serve_forever_lwt_callback_vtpm ~cache mutex vtpm _path _ req body = +let serve_forever_lwt_callback_vtpm ~cache mutex vm_uuid _ req body = + let get_vtpm_ref () = + let* vm = + with_xapi ~cache @@ Xen_api_lwt_unix.VM.get_by_uuid ~uuid:vm_uuid + in + let* vTPMs = with_xapi ~cache @@ Xen_api_lwt_unix.VM.get_VTPMs ~self:vm in + match vTPMs with + | [] -> + D.warn + "%s: received a request from a VM that has no VTPM associated, \ + ignoring request" + __FUNCTION__ ; + let msg = + Printf.sprintf "No VTPM associated with VM %s, nothing to do" vm_uuid + in + raise (Failure msg) + | self :: _ -> + let* uuid = with_xapi ~cache @@ Xen_api_lwt_unix.VTPM.get_uuid ~self in + with_xapi ~cache @@ VTPM.get_by_uuid ~uuid + in let uri = Cohttp.Request.uri req in (* in case the connection is interrupted/etc. we may still have pending operations, so use a per vTPM mutex to ensure we really only have 1 pending operation at a time for a vTPM @@ -161,7 +180,8 @@ let serve_forever_lwt_callback_vtpm ~cache mutex vtpm _path _ req body = (* TODO: some logging *) match (Cohttp.Request.meth req, Uri.path uri) with | `GET, key when key <> "/" -> - let* contents = with_xapi ~cache @@ VTPM.get_contents ~self:vtpm in + let* self = get_vtpm_ref () in + let* contents = with_xapi ~cache @@ VTPM.get_contents ~self in let body = contents |> deserialize |> lookup_key key in let headers = Cohttp.Header.of_list [("Content-Type", "application/octet-stream")] @@ -169,19 +189,21 @@ let serve_forever_lwt_callback_vtpm ~cache mutex vtpm _path _ req body = Cohttp_lwt_unix.Server.respond_string ~headers ~status:`OK ~body () | `PUT, key when key <> "/" -> let* body = Cohttp_lwt.Body.to_string body in - let* contents = with_xapi ~cache @@ VTPM.get_contents ~self:vtpm in + let* self = get_vtpm_ref () in + let* contents = with_xapi ~cache @@ VTPM.get_contents ~self in let contents = contents |> deserialize |> update_key key body |> serialize in - let* () = with_xapi ~cache @@ VTPM.set_contents ~self:vtpm ~contents in + let* () = with_xapi ~cache @@ VTPM.set_contents ~self ~contents in Cohttp_lwt_unix.Server.respond ~status:`No_content ~body:Cohttp_lwt.Body.empty () | `DELETE, key when key <> "/" -> - let* contents = with_xapi ~cache @@ VTPM.get_contents ~self:vtpm in + let* self = get_vtpm_ref () in + let* contents = with_xapi ~cache @@ VTPM.get_contents ~self in let contents = contents |> deserialize |> update_key key empty |> serialize in - let* () = with_xapi ~cache @@ VTPM.set_contents ~self:vtpm ~contents in + let* () = with_xapi ~cache @@ VTPM.set_contents ~self ~contents in Cohttp_lwt_unix.Server.respond ~status:`No_content ~body:Cohttp_lwt.Body.empty () | _, _ -> @@ -192,14 +214,22 @@ let serve_forever_lwt_callback_vtpm ~cache mutex vtpm _path _ req body = let make_server_varstored ~cache path vm_uuid = let module Server = Xapi_idl_guard_varstored.Interface.RPC_API (Rpc_lwt.GenServer ()) in - let* vm = with_xapi ~cache @@ VM.get_by_uuid ~uuid:vm_uuid in + let get_vm_ref () = with_xapi ~cache @@ VM.get_by_uuid ~uuid:vm_uuid in let ret v = (* TODO: maybe map XAPI exceptions *) Lwt.bind v Lwt.return_ok |> Rpc_lwt.T.put in - let get_nvram _ _ = ret @@ with_xapi ~cache @@ VM.get_NVRAM ~self:vm in + let get_nvram _ _ = + (let* self = get_vm_ref () in + with_xapi ~cache @@ VM.get_NVRAM ~self + ) + |> ret + in let set_nvram _ _ nvram = - ret @@ with_xapi ~cache @@ VM.set_NVRAM_EFI_variables ~self:vm ~value:nvram + (let* self = get_vm_ref () in + with_xapi ~cache @@ VM.set_NVRAM_EFI_variables ~self ~value:nvram + ) + |> ret in let message_create _ _name priority _cls _uuid body = ret @@ -224,20 +254,6 @@ let make_server_varstored ~cache path vm_uuid = |> serve_forever_lwt path let make_server_vtpm_rest ~cache path vm_uuid = - let vtpm_server uuid = - let* vtpm = with_xapi ~cache @@ VTPM.get_by_uuid ~uuid in - let mutex = Lwt_mutex.create () in - serve_forever_lwt_callback_vtpm ~cache mutex vtpm path - |> serve_forever_lwt path - in - let* vm = with_xapi ~cache @@ Xen_api_lwt_unix.VM.get_by_uuid ~uuid:vm_uuid in - let* vTPMs = with_xapi ~cache @@ Xen_api_lwt_unix.VM.get_VTPMs ~self:vm in - match vTPMs with - | [] -> - D.warn - "%s: asked to start swtpm server in socket, but no vtpms associated!" - __FUNCTION__ ; - Lwt.return Lwt.return - | self :: _ -> - let* uuid = with_xapi ~cache @@ Xen_api_lwt_unix.VTPM.get_uuid ~self in - vtpm_server uuid + let mutex = Lwt_mutex.create () in + let callback = serve_forever_lwt_callback_vtpm ~cache mutex vm_uuid in + serve_forever_lwt path callback From 319970859c372e6b2db25f4a659ed1f8e2afafe3 Mon Sep 17 00:00:00 2001 From: Pau Ruiz Safont Date: Tue, 6 Feb 2024 15:17:17 +0000 Subject: [PATCH 2/3] xapi-guard: separate base types to its own module use xapi-guard for name in logs Signed-off-by: Pau Ruiz Safont --- ocaml/xapi-guard/lib/dorpc.ml | 2 +- ocaml/xapi-guard/lib/dune | 2 +- ocaml/xapi-guard/lib/server_interface.ml | 4 ++-- ocaml/xapi-guard/lib/types.ml | 5 +++++ ocaml/xapi-guard/lib/types.mli | 7 +++++++ ocaml/xapi-guard/src/main.ml | 22 +++++++++++----------- 6 files changed, 27 insertions(+), 15 deletions(-) create mode 100644 ocaml/xapi-guard/lib/types.ml create mode 100644 ocaml/xapi-guard/lib/types.mli diff --git a/ocaml/xapi-guard/lib/dorpc.ml b/ocaml/xapi-guard/lib/dorpc.ml index 2074f35e529..bbb1f11bb74 100644 --- a/ocaml/xapi-guard/lib/dorpc.ml +++ b/ocaml/xapi-guard/lib/dorpc.ml @@ -13,7 +13,7 @@ *) open Idl -module D = Debug.Make (struct let name = "varstored-guard rpc" end) +module D = Debug.Make (struct let name = "xapi-guard rpc" end) let wrap_rpc error f = let on_error e = diff --git a/ocaml/xapi-guard/lib/dune b/ocaml/xapi-guard/lib/dune index bfb4841ab3d..87d10e7766e 100644 --- a/ocaml/xapi-guard/lib/dune +++ b/ocaml/xapi-guard/lib/dune @@ -20,7 +20,7 @@ ) (library (name xapi_guard) - (modules dorpc) + (modules dorpc types) (libraries rpclib.core lwt diff --git a/ocaml/xapi-guard/lib/server_interface.ml b/ocaml/xapi-guard/lib/server_interface.ml index f76118aec72..4bcaae8a387 100644 --- a/ocaml/xapi-guard/lib/server_interface.ml +++ b/ocaml/xapi-guard/lib/server_interface.ml @@ -15,7 +15,7 @@ open Rpc open Lwt.Syntax -module D = Debug.Make (struct let name = "varstored_interface" end) +module D = Debug.Make (struct let name = __MODULE__ end) open D @@ -25,7 +25,7 @@ let err = Xenops_interface.err type nvram = (string * string) list [@@deriving rpcty] -let originator = "varstored-guard" +let originator = "xapi-guard" type session = [`session] Ref.t diff --git a/ocaml/xapi-guard/lib/types.ml b/ocaml/xapi-guard/lib/types.ml new file mode 100644 index 00000000000..7b705c89a01 --- /dev/null +++ b/ocaml/xapi-guard/lib/types.ml @@ -0,0 +1,5 @@ +module Service = struct + type t = Varstored | Swtpm [@@deriving rpcty] + + let to_string = function Varstored -> "Varstored" | Swtpm -> "Swtpm" +end diff --git a/ocaml/xapi-guard/lib/types.mli b/ocaml/xapi-guard/lib/types.mli new file mode 100644 index 00000000000..6bb036826d7 --- /dev/null +++ b/ocaml/xapi-guard/lib/types.mli @@ -0,0 +1,7 @@ +module Service : sig + type t = Varstored | Swtpm + + val typ_of : t Rpc.Types.typ + + val to_string : t -> string +end diff --git a/ocaml/xapi-guard/src/main.ml b/ocaml/xapi-guard/src/main.ml index 551a60372d4..b80bf354516 100644 --- a/ocaml/xapi-guard/src/main.ml +++ b/ocaml/xapi-guard/src/main.ml @@ -13,18 +13,16 @@ * GNU Lesser General Public License for more details. *) -open Xapi_guard open Lwt.Syntax open Xapi_guard_server +module Types = Xapi_guard.Types module SessionCache = Xen_api_lwt_unix.SessionCache -module D = Debug.Make (struct let name = "varstored-guard" end) +let daemon_name = "xapi-guard" -let ret v = Lwt.bind v Lwt.return_ok |> Rpc_lwt.T.put - -type ty = Varstored | Swtpm [@@deriving rpcty] +module D = Debug.Make (struct let name = daemon_name end) -let ty_to_string = function Varstored -> "Varstored" | Swtpm -> "Swtpm" +let ret v = Lwt.bind v Lwt.return_ok |> Rpc_lwt.T.put let log_fds () = let count stream = Lwt_stream.fold (fun _ n -> n + 1) stream 0 in @@ -41,7 +39,7 @@ module Persistent = struct vm_uuid: Xapi_idl_guard_privileged.Interface.Uuidm.t ; path: string ; gid: int - ; typ: ty + ; typ: Types.Service.t } [@@deriving rpcty] @@ -109,7 +107,8 @@ let listen_for_vm {Persistent.vm_uuid; path; gid; typ} = in let vm_uuid_str = Uuidm.to_string vm_uuid in D.debug "%s: listening for %s on socket %s for VM %s" __FUNCTION__ - (ty_to_string typ) path vm_uuid_str ; + (Types.Service.to_string typ) + path vm_uuid_str ; let* () = safe_unlink path in let* stop_server = make_server ~cache path vm_uuid_str in let* () = log_fds () in @@ -230,7 +229,8 @@ let rpc_fn = let process body = let+ response = - Dorpc.wrap_rpc Xapi_idl_guard_privileged.Interface.E.error (fun () -> + Xapi_guard.Dorpc.wrap_rpc Xapi_idl_guard_privileged.Interface.E.error + (fun () -> let call = Jsonrpc.call_of_string body in D.debug "Received request from message-switch, method %s" call.Rpc.name ; rpc_fn call @@ -278,12 +278,12 @@ let main log_level = old_hook exn ) ; let () = Lwt_main.run @@ make_message_switch_server () in - D.debug "Exiting varstored-guard" + D.debug "Exiting %s" daemon_name open! Cmdliner let cmd = - let info = Cmd.info "varstored-guard" in + let info = Cmd.info daemon_name in let log_level = let doc = "Syslog level. E.g. debug, info etc." in let level_conv = From 43a392c8c0d10c64f867e23bb5e19d84fd361646 Mon Sep 17 00:00:00 2001 From: Pau Ruiz Safont Date: Tue, 6 Feb 2024 14:08:59 +0000 Subject: [PATCH 3/3] doc: Add some information about xapi-guard Signed-off-by: Pau Ruiz Safont --- doc/content/toolstack/high-level/daemons.md | 5 +++- doc/content/xapi-guard/_index.md | 28 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 doc/content/xapi-guard/_index.md diff --git a/doc/content/toolstack/high-level/daemons.md b/doc/content/toolstack/high-level/daemons.md index e00c2fc287a..103798bb0d5 100644 --- a/doc/content/toolstack/high-level/daemons.md +++ b/doc/content/toolstack/high-level/daemons.md @@ -34,6 +34,9 @@ xapi-storage-script message-switch : exchanges messages between the daemons on a host +xapi-guard +: forwards uefi and vtpm persistence calls from domains to xapi + v6d : controls which features are enabled. @@ -52,4 +55,4 @@ mpathalert if paths fail and need repair wsproxy -: handles access to VM consoles \ No newline at end of file +: handles access to VM consoles diff --git a/doc/content/xapi-guard/_index.md b/doc/content/xapi-guard/_index.md new file mode 100644 index 00000000000..bcafb968b07 --- /dev/null +++ b/doc/content/xapi-guard/_index.md @@ -0,0 +1,28 @@ ++++ +title = "Xapi-guard" +weight = 50 ++++ + +The `xapi-guard` daemon is the component in the xapi toolstack that is responsible for handling persistence requests from VMs (domains). +Currently these are UEFI vars and vTPM updates. + +The code is in `ocaml/xapi-guard`. +When the daemon managed only with UEFI updates it was called `varstored-guard`. +Some files and package names still use the previous name. + +Principles +---------- +1. Calls from domains must be limited in privilege to do certain API calls, and + to read and write from their corresponding VM in xapi's database only. +2. Xenopsd is able to control xapi-guard through message switch, this access is + not limited. +3. Listening to domain socket is restored whenever the daemon restarts to minimize disruption of running domains. + + +Overview +-------- + +Xapi-guard forwards calls from domains to xapi to persist UEFI variables, and update vTPMs. +To do this, it listens to 1 socket per service (varstored, or swtpm) per domain. +To create these sockets before the domains are running, it listens to a message-switch socket. +This socket listens to calls from xenopsd, which orchestrates the domain creation.