Skip to content

Commit

Permalink
Merge pull request #5440 from psafont/private/paus/split-guard
Browse files Browse the repository at this point in the history
CA-385323: Enable creating domain sockets while xapi isn't running
  • Loading branch information
robhoes authored Feb 12, 2024
2 parents e17089c + 43a392c commit 210d034
Show file tree
Hide file tree
Showing 8 changed files with 101 additions and 42 deletions.
5 changes: 4 additions & 1 deletion doc/content/toolstack/high-level/daemons.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -52,4 +55,4 @@ mpathalert
if paths fail and need repair

wsproxy
: handles access to VM consoles
: handles access to VM consoles
28 changes: 28 additions & 0 deletions doc/content/xapi-guard/_index.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 1 addition & 1 deletion ocaml/xapi-guard/lib/dorpc.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
2 changes: 1 addition & 1 deletion ocaml/xapi-guard/lib/dune
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
)
(library
(name xapi_guard)
(modules dorpc)
(modules dorpc types)
(libraries
rpclib.core
lwt
Expand Down
72 changes: 44 additions & 28 deletions ocaml/xapi-guard/lib/server_interface.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -161,27 +180,30 @@ 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")]
in
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 ()
| _, _ ->
Expand All @@ -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
Expand All @@ -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
5 changes: 5 additions & 0 deletions ocaml/xapi-guard/lib/types.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Service = struct
type t = Varstored | Swtpm [@@deriving rpcty]

let to_string = function Varstored -> "Varstored" | Swtpm -> "Swtpm"
end
7 changes: 7 additions & 0 deletions ocaml/xapi-guard/lib/types.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Service : sig
type t = Varstored | Swtpm

val typ_of : t Rpc.Types.typ

val to_string : t -> string
end
22 changes: 11 additions & 11 deletions ocaml/xapi-guard/src/main.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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]

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 =
Expand Down

0 comments on commit 210d034

Please sign in to comment.