For "--key ID:clevis" to actually work, the appliance needs network
connectivity. Some tools allow the user to control that (for example,
guestfish takes "--network"); those tools can already use "--key
ID:clevis", provided they enable inspection and networking.
However, other tools have had no use for networking thus far. Introduce a
helper function for the OCaml utils, and another for the C utils, so that
the utils can query if there's at least one clevis selector, and if so,
enable networking for the appliance, before launching the appliance.
Note that we need to implement both helpers separately from each other;
that is, the OCaml one is not based on the C one. The reason is that
"struct key_store" (from "options/options.h"), upon which we could
logically base a common helper, exists in the OCaml tools only
ephemerally, in guestfs_int_mllib_inspect_decrypt():
inspect_decrypt [mltools/tools_utils.ml]
c_inspect_decrypt [mltools/tools_utils.ml]
guestfs_int_mllib_inspect_decrypt [mltools/tools_utils-c.c]
key_store_import_key() [options/keys.c]
inspect_do_decrypt() [options/decrypt.c]
free_key_store() [options/keys.c]
At that time, it's too late for enabling networking for the appliance.
Therefore, in the OCaml tools, search the "earlier" data structure called
"cmdline_options.ks". *That* "key_store" type comes from
"mltools/tools_utils.ml".
Bugzilla:
https://bugzilla.redhat.com/show_bug.cgi?id=1809453
Signed-off-by: Laszlo Ersek <lersek(a)redhat.com>
Reviewed-by: Richard W.M. Jones <rjones(a)redhat.com>
---
Notes:
v2:
- pick up Rich's R-b
options/options.h | 1 +
mltools/tools_utils.mli | 4 ++++
mltools/tools_utils.ml | 5 +++++
options/keys.c | 15 +++++++++++++++
options/key-option.pod | 3 +++
5 files changed, 28 insertions(+)
diff --git a/options/options.h b/options/options.h
index e7a0364cc926..60d5d8064113 100644
--- a/options/options.h
+++ b/options/options.h
@@ -168,10 +168,11 @@ extern char *read_key (const char *param);
extern struct matching_key *get_keys (struct key_store *ks, const char *device,
const char *uuid, size_t *nr_matches);
extern void free_keys (struct matching_key *keys, size_t nr_matches);
extern struct key_store *key_store_add_from_selector (struct key_store *ks, const char
*selector);
extern struct key_store *key_store_import_key (struct key_store *ks, const struct
key_store_key *key);
+extern bool key_store_requires_network (const struct key_store *ks);
extern void free_key_store (struct key_store *ks);
/* in options.c */
extern void option_a (const char *arg, const char *format, int blocksize, struct drv
**drvsp);
extern void option_d (const char *arg, struct drv **drvsp);
diff --git a/mltools/tools_utils.mli b/mltools/tools_utils.mli
index 8d9af7a0a661..ec900e6389bc 100644
--- a/mltools/tools_utils.mli
+++ b/mltools/tools_utils.mli
@@ -194,10 +194,14 @@ val inspect_mount_root_ro : Guestfs.guestfs -> string -> unit
read-only. *)
val is_btrfs_subvolume : Guestfs.guestfs -> string -> bool
(** Checks if a filesystem is a btrfs subvolume. *)
+val key_store_requires_network : key_store -> bool
+(** [key_store_requires_network ks] returns [true] iff [ks] contains at least
+ one "ID:clevis" selector. *)
+
val inspect_decrypt : Guestfs.guestfs -> key_store -> unit
(** Simple implementation of decryption: look for any encrypted
partitions and decrypt them, then rescan for VGs. *)
val with_timeout : string -> int -> ?sleep:int -> (unit -> 'a option)
-> 'a
diff --git a/mltools/tools_utils.ml b/mltools/tools_utils.ml
index 1da5850340d4..562bfadc18a8 100644
--- a/mltools/tools_utils.ml
+++ b/mltools/tools_utils.ml
@@ -694,10 +694,15 @@ let is_btrfs_subvolume g fs =
ignore (g#mountable_subvolume fs); true
with Guestfs.Error msg as exn ->
if g#last_errno () = Guestfs.Errno.errno_EINVAL then false
else raise exn
+let key_store_requires_network ks =
+ List.exists (function
+ | _, KeyClevis -> true
+ | _ -> false) !(ks.keys)
+
let inspect_decrypt g ks =
(* Note we pass original 'g' even though it is not used by the
* callee. This is so that 'g' is kept as a root on the stack, and
* so cannot be garbage collected while we are in the c_inspect_decrypt
* function.
diff --git a/options/keys.c b/options/keys.c
index e7c550f4796b..d987ae56198c 100644
--- a/options/keys.c
+++ b/options/keys.c
@@ -282,10 +282,25 @@ key_store_import_key (struct key_store *ks, const struct
key_store_key *key)
++ks->nr_keys;
return ks;
}
+bool
+key_store_requires_network (const struct key_store *ks)
+{
+ size_t i;
+
+ if (ks == NULL)
+ return false;
+
+ for (i = 0; i < ks->nr_keys; ++i)
+ if (ks->keys[i].type == key_clevis)
+ return true;
+
+ return false;
+}
+
void
free_key_store (struct key_store *ks)
{
size_t i;
diff --git a/options/key-option.pod b/options/key-option.pod
index 34229ce9cbb2..6bc04df177b1 100644
--- a/options/key-option.pod
+++ b/options/key-option.pod
@@ -18,6 +18,9 @@ Read the passphrase from F<FILENAME>.
Attempt passphrase-less unlocking for C<ID> with Clevis, over the
network. Please refer to L<guestfs(3)/ENCRYPTED DISKS> for more
information on network-bound disk encryption (NBDE).
+Note that if any such option is present on the command line, QEMU user
+networking will be automatically enabled for the libguestfs appliance.
+
=back
--
2.19.1.3.g30247aa5d201