Richard W.M. Jones
2022-Jun-28 14:57 UTC
[Libguestfs] [libguestfs PATCH 2/3] introduce the "clevis_luks_unlock" API
On Tue, Jun 28, 2022 at 01:54:17PM +0200, Laszlo Ersek wrote:> Introduce a new guestfs API called "clevis_luks_unlock". At the libguestfs > level, it is quite simple; it wraps the "clevis luks unlock" guest command > (implemented by the "clevis-luks-unlock" executable, which is in fact a > shell script). > > The complexity is instead in the network-based disk encryption > (Clevis/Tang) scheme. Useful documentation: > > - https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html-single/security_hardening/index#configuring-automated-unlocking-of-encrypted-volumes-using-policy-based-decryption_security-hardening > - https://github.com/latchset/clevis#clevis > - https://github.com/latchset/tang#tang > > The package providing "clevis-luks-unlock" is usually called > "clevis-luks", occasionally "clevis". Some distros don't package clevis at > all. Add the new API under a new option group (which may not be available) > called "clevisluks". > > Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1809453 > Signed-off-by: Laszlo Ersek <lersek at redhat.com> > --- > daemon/Makefile.am | 1 + > generator/actions_core.ml | 38 +++++++++++++ > generator/proc_nr.ml | 1 + > daemon/clevis-luks.c | 58 ++++++++++++++++++++ > appliance/packagelist.in | 4 ++ > lib/MAX_PROC_NR | 2 +- > lib/guestfs.pod | 19 +++++-- > 7 files changed, 118 insertions(+), 5 deletions(-) > > diff --git a/daemon/Makefile.am b/daemon/Makefile.am > index bbd49f9ea1e0..f50faecd6930 100644 > --- a/daemon/Makefile.am > +++ b/daemon/Makefile.am > @@ -96,10 +96,11 @@ guestfsd_SOURCES = \ > btrfs.c \ > caml-stubs.c \ > cap.c \ > checksum.c \ > cleanups.c \ > + clevis-luks.c \ > cmp.c \ > command.c \ > command.h \ > compress.c \ > copy.c \ > diff --git a/generator/actions_core.ml b/generator/actions_core.ml > index 807150615426..1b1fdcf77209 100644 > --- a/generator/actions_core.ml > +++ b/generator/actions_core.ml > @@ -9718,6 +9718,44 @@ and I<not> the name of the underlying block device." }; > style = RErr, [String (Pathname, "dir"); String (FileOut, "filename")], []; > visibility = VInternal; > shortdesc = "read directories entries"; > longdesc = "Internal function for readdir." }; > > + { defaults with > + name = "clevis_luks_unlock"; added = (1, 49, 3); > + style = RErr, > + [String (Device, "device"); String (PlainString, "mapname")],PlainString because we create the device from the mapname, we're not passing (this) device in as a parameter, so this is correct. However do we never need any optional parameters? cryptsetup_open has [OBool "readonly"; OString "crypttype"].> + []; > + optional = Some "clevisluks"; > + test_excuse = "needs networking and a configured Tang server"; > + shortdesc = "open an encrypted LUKS block device with Clevis and Tang"; > + longdesc = "\ > +This command opens a block device that has been encrypted according to the Linux > +Unified Key Setup (LUKS) standard, using network-bound disk encryption (NBDE). > + > +C<device> is the encrypted block device. > + > +The appliance will connect to the Tang servers noted in the tree of Clevis pins > +that is bound to a keyslot of the LUKS header. The Clevis pin tree may comprise > +C<sss> (redudancy) pins as internal nodes (optionally), and C<tang> pins as > +leaves. C<tpm2> pins are not supported. The appliance unlocks the encrypted > +block device by combining responses from the Tang servers with metadata from the > +LUKS header; there is no C<key> parameter. > + > +This command will fail if networking has not been enabled for the appliance. > +Refer to C<guestfs_set_network>. > + > +The command creates a new block device called F</dev/mapper/mapname>. Reads and > +writes to this block device are decrypted from and encrypted to the underlying > +C<device> respectively. Close the decrypted block device with > +C<guestfs_cryptsetup_close>. > + > +C<mapname> cannot be C<\"control\"> because that name is reserved by > +device-mapper. > + > +If this block device contains LVM volume groups, then calling > +C<guestfs_lvm_scan> with the C<activate> parameter C<true> will make them > +visible. > + > +Use C<guestfs_list_dm_devices> to list all device mapper devices." };Some lines here are longer than 80 characters, which might cause problems. Be better to stick to a bit less than 80 chars.> ] > diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml > index bdced51c9acd..edd9bd99d2da 100644 > --- a/generator/proc_nr.ml > +++ b/generator/proc_nr.ml > @@ -512,10 +512,11 @@ let proc_nr = [ > 507, "luks_uuid"; > 508, "cryptsetup_open"; > 509, "cryptsetup_close"; > 510, "internal_list_rpm_applications"; > 511, "internal_readdir"; > +512, "clevis_luks_unlock" > ] > > (* End of list. If adding a new entry, add it at the end of the list > * above. Do not modify anything below this line. > *---------------------------------------------------------------------- > diff --git a/daemon/clevis-luks.c b/daemon/clevis-luks.c > new file mode 100644 > index 000000000000..d3d970d78098 > --- /dev/null > +++ b/daemon/clevis-luks.c > @@ -0,0 +1,58 @@ > +/* libguestfs - the guestfsd daemon > + * Copyright (C) 2009-2022 Red Hat Inc. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. > + */ > + > +#include <config.h> > + > +#include "daemon.h" > +#include "actions.h" > +#include "optgroups.h" > + > +#define MAX_ARGS 8 > + > +int > +optgroup_clevisluks_available (void) > +{ > + return prog_exists ("clevis-luks-unlock"); > +} > + > +int > +do_clevis_luks_unlock (const char *device, const char *mapname) > +{ > + const char *argv[MAX_ARGS]; > + size_t i = 0; > + int r; > + CLEANUP_FREE char *err = NULL; > + > + ADD_ARG (argv, i, "clevis"); > + ADD_ARG (argv, i, "luks"); > + ADD_ARG (argv, i, "unlock"); > + ADD_ARG (argv, i, "-d"); > + ADD_ARG (argv, i, device); > + ADD_ARG (argv, i, "-n"); > + ADD_ARG (argv, i, mapname); > + ADD_ARG (argv, i, NULL); > + > + r = commandv (NULL, &err, argv); > + if (r == -1) { > + reply_with_error ("%s: %s: %s", device, mapname, err); > + return -1; > + } > + > + udev_settle (); > + return 0; > +} > diff --git a/appliance/packagelist.in b/appliance/packagelist.in > index 77a07acc6ad0..0b79edcdd55d 100644 > --- a/appliance/packagelist.in > +++ b/appliance/packagelist.in > @@ -21,10 +21,11 @@ dnl ./configure --with-extra-packages="gdb valgrind [etc]" > > dnl Basically the same with a few minor tweaks. > ifelse(UBUNTU,1,`define(`DEBIAN',1)') > > ifelse(REDHAT,1, > + clevis-luks > cryptsetup > cryptsetup-luks dnl old name used before Fedora 17 > dhclient > gfs-utils > gfs2-utils > @@ -51,10 +52,11 @@ ifelse(REDHAT,1, > > ifelse(DEBIAN,1, > bsdmainutils > dnl old name used in Jessie and earlier > btrfs-tools > + clevis-luks > cryptsetup > dash > extlinux > dnl gfs-tools, gfs2-tools have been renamed to gfs2-utils > gfs-tools > @@ -90,10 +92,11 @@ dnl iproute has been renamed to iproute2 > ) > > ifelse(ARCHLINUX,1, > cdrkit > cdrtools > + clevis > cryptsetup > dhclient > dhcpcd > gptfdisk > grub > @@ -117,10 +120,11 @@ ifelse(ARCHLINUX,1, > ifelse(SUSE,1, > dnl It seems no other augeas package depends on it. > augeas-lenses > btrfsprogs > cdrkit-cdrtools-compat > + clevis > cryptsetup > dhcpcd > dhcp-client > glibc-locale > gptfdisk > diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR > index c0556fb20f37..4d0e90cbcbaf 100644 > --- a/lib/MAX_PROC_NR > +++ b/lib/MAX_PROC_NR > @@ -1 +1 @@ > -511 > +512 > diff --git a/lib/guestfs.pod b/lib/guestfs.pod > index 1ad44e7c2878..e10bf1fbec30 100644 > --- a/lib/guestfs.pod > +++ b/lib/guestfs.pod > @@ -589,15 +589,26 @@ devices. For LUKS it returns the string C<crypto_LUKS>. > For Windows BitLocker it returns C<BitLocker>. > > Then open these devices by calling L</guestfs_cryptsetup_open>. > Obviously you will require the passphrase! > > +Passphrase-less unlocking is supported for LUKS (not BitLocker) > +block devices that have been encrypted with network-bound disk > +encryption (NBDE), using Clevis on the Linux guest side, and > +Tang on a separate Linux server. Open such devices with > +L</guestfs_clevis_luks_unlock>. The appliance will need > +networking enabled (refer to L</guestfs_set_network>) and actual > +connectivity to the Tang servers noted in the C<tang> Clevis > +pins that are bound to the LUKS header. (This includes the > +ability to resolve the names of the Tang servers.) > + > Opening an encrypted device creates a new device mapper device > -called F</dev/mapper/mapname> (where C<mapname> is the > -string you supply to L</guestfs_cryptsetup_open>). > -Reads and writes to this mapper device are decrypted from and > -encrypted to the underlying block device respectively. > +called F</dev/mapper/mapname> (where C<mapname> is the string > +you supply to L</guestfs_cryptsetup_open> or > +L</guestfs_clevis_luks_unlock>). Reads and writes to this mapper > +device are decrypted from and encrypted to the underlying block > +device respectively. > > LVM volume groups on the device can be made visible by calling > L</guestfs_vgscan> followed by L</guestfs_vg_activate_all>. > The logical volume(s) can now be mounted in the usual way.Looks generally OK. Rich. -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-builder quickly builds VMs from scratch http://libguestfs.org/virt-builder.1.html
Laszlo Ersek
2022-Jun-29 12:52 UTC
[Libguestfs] [libguestfs PATCH 2/3] introduce the "clevis_luks_unlock" API
On 06/28/22 16:57, Richard W.M. Jones wrote:> On Tue, Jun 28, 2022 at 01:54:17PM +0200, Laszlo Ersek wrote: >> Introduce a new guestfs API called "clevis_luks_unlock". At the libguestfs >> level, it is quite simple; it wraps the "clevis luks unlock" guest command >> (implemented by the "clevis-luks-unlock" executable, which is in fact a >> shell script). >> >> The complexity is instead in the network-based disk encryption >> (Clevis/Tang) scheme. Useful documentation: >> >> - https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html-single/security_hardening/index#configuring-automated-unlocking-of-encrypted-volumes-using-policy-based-decryption_security-hardening >> - https://github.com/latchset/clevis#clevis >> - https://github.com/latchset/tang#tang >> >> The package providing "clevis-luks-unlock" is usually called >> "clevis-luks", occasionally "clevis". Some distros don't package clevis at >> all. Add the new API under a new option group (which may not be available) >> called "clevisluks". >> >> Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1809453 >> Signed-off-by: Laszlo Ersek <lersek at redhat.com> >> --- >> daemon/Makefile.am | 1 + >> generator/actions_core.ml | 38 +++++++++++++ >> generator/proc_nr.ml | 1 + >> daemon/clevis-luks.c | 58 ++++++++++++++++++++ >> appliance/packagelist.in | 4 ++ >> lib/MAX_PROC_NR | 2 +- >> lib/guestfs.pod | 19 +++++-- >> 7 files changed, 118 insertions(+), 5 deletions(-) >> >> diff --git a/daemon/Makefile.am b/daemon/Makefile.am >> index bbd49f9ea1e0..f50faecd6930 100644 >> --- a/daemon/Makefile.am >> +++ b/daemon/Makefile.am >> @@ -96,10 +96,11 @@ guestfsd_SOURCES = \ >> btrfs.c \ >> caml-stubs.c \ >> cap.c \ >> checksum.c \ >> cleanups.c \ >> + clevis-luks.c \ >> cmp.c \ >> command.c \ >> command.h \ >> compress.c \ >> copy.c \ >> diff --git a/generator/actions_core.ml b/generator/actions_core.ml >> index 807150615426..1b1fdcf77209 100644 >> --- a/generator/actions_core.ml >> +++ b/generator/actions_core.ml >> @@ -9718,6 +9718,44 @@ and I<not> the name of the underlying block device." }; >> style = RErr, [String (Pathname, "dir"); String (FileOut, "filename")], []; >> visibility = VInternal; >> shortdesc = "read directories entries"; >> longdesc = "Internal function for readdir." }; >> >> + { defaults with >> + name = "clevis_luks_unlock"; added = (1, 49, 3); >> + style = RErr, >> + [String (Device, "device"); String (PlainString, "mapname")], > > PlainString because we create the device from the mapname, we're not > passing (this) device in as a parameter, so this is correct. > > However do we never need any optional parameters? cryptsetup_open has > [OBool "readonly"; OString "crypttype"].Right, I evaluated those (and some more) questions in <https://bugzilla.redhat.com/show_bug.cgi?id=1809453#c13>: - guestfs_cryptsetup_open() permits "crypttype" = bitlk, but that makes no sense with clevis (no BitLocker, just LUKS, with Clevis -- I read this in the Clevis documentation) - guestfs_cryptsetup_open() permits "readonly" = 1, but read-only mappings are not supported by clevis-luks-unlock(1).> >> + []; >> + optional = Some "clevisluks"; >> + test_excuse = "needs networking and a configured Tang server"; >> + shortdesc = "open an encrypted LUKS block device with Clevis and Tang"; >> + longdesc = "\ >> +This command opens a block device that has been encrypted according to the Linux >> +Unified Key Setup (LUKS) standard, using network-bound disk encryption (NBDE). >> + >> +C<device> is the encrypted block device. >> + >> +The appliance will connect to the Tang servers noted in the tree of Clevis pins >> +that is bound to a keyslot of the LUKS header. The Clevis pin tree may comprise >> +C<sss> (redudancy) pins as internal nodes (optionally), and C<tang> pins as >> +leaves. C<tpm2> pins are not supported. The appliance unlocks the encrypted >> +block device by combining responses from the Tang servers with metadata from the >> +LUKS header; there is no C<key> parameter. >> + >> +This command will fail if networking has not been enabled for the appliance. >> +Refer to C<guestfs_set_network>. >> + >> +The command creates a new block device called F</dev/mapper/mapname>. Reads and >> +writes to this block device are decrypted from and encrypted to the underlying >> +C<device> respectively. Close the decrypted block device with >> +C<guestfs_cryptsetup_close>. >> + >> +C<mapname> cannot be C<\"control\"> because that name is reserved by >> +device-mapper. >> + >> +If this block device contains LVM volume groups, then calling >> +C<guestfs_lvm_scan> with the C<activate> parameter C<true> will make them >> +visible. >> + >> +Use C<guestfs_list_dm_devices> to list all device mapper devices." }; > > Some lines here are longer than 80 characters,No, the longest line (excluding the \n in the source code) is exactly 80 characters long. Is that a problem as well?> which might cause > problems. Be better to stick to a bit less than 80 chars.I've noticed that the pod files use shorter lines, but couldn't figure out if it was 72 chars, 64 chars, or something else. They are not all consistent. Thanks! Laszlo> >> ] >> diff --git a/generator/proc_nr.ml b/generator/proc_nr.ml >> index bdced51c9acd..edd9bd99d2da 100644 >> --- a/generator/proc_nr.ml >> +++ b/generator/proc_nr.ml >> @@ -512,10 +512,11 @@ let proc_nr = [ >> 507, "luks_uuid"; >> 508, "cryptsetup_open"; >> 509, "cryptsetup_close"; >> 510, "internal_list_rpm_applications"; >> 511, "internal_readdir"; >> +512, "clevis_luks_unlock" >> ] >> >> (* End of list. If adding a new entry, add it at the end of the list >> * above. Do not modify anything below this line. >> *---------------------------------------------------------------------- >> diff --git a/daemon/clevis-luks.c b/daemon/clevis-luks.c >> new file mode 100644 >> index 000000000000..d3d970d78098 >> --- /dev/null >> +++ b/daemon/clevis-luks.c >> @@ -0,0 +1,58 @@ >> +/* libguestfs - the guestfsd daemon >> + * Copyright (C) 2009-2022 Red Hat Inc. >> + * >> + * This program is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License as published by >> + * the Free Software Foundation; either version 2 of the License, or >> + * (at your option) any later version. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + * >> + * You should have received a copy of the GNU General Public License >> + * along with this program; if not, write to the Free Software >> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >> + */ >> + >> +#include <config.h> >> + >> +#include "daemon.h" >> +#include "actions.h" >> +#include "optgroups.h" >> + >> +#define MAX_ARGS 8 >> + >> +int >> +optgroup_clevisluks_available (void) >> +{ >> + return prog_exists ("clevis-luks-unlock"); >> +} >> + >> +int >> +do_clevis_luks_unlock (const char *device, const char *mapname) >> +{ >> + const char *argv[MAX_ARGS]; >> + size_t i = 0; >> + int r; >> + CLEANUP_FREE char *err = NULL; >> + >> + ADD_ARG (argv, i, "clevis"); >> + ADD_ARG (argv, i, "luks"); >> + ADD_ARG (argv, i, "unlock"); >> + ADD_ARG (argv, i, "-d"); >> + ADD_ARG (argv, i, device); >> + ADD_ARG (argv, i, "-n"); >> + ADD_ARG (argv, i, mapname); >> + ADD_ARG (argv, i, NULL); >> + >> + r = commandv (NULL, &err, argv); >> + if (r == -1) { >> + reply_with_error ("%s: %s: %s", device, mapname, err); >> + return -1; >> + } >> + >> + udev_settle (); >> + return 0; >> +} >> diff --git a/appliance/packagelist.in b/appliance/packagelist.in >> index 77a07acc6ad0..0b79edcdd55d 100644 >> --- a/appliance/packagelist.in >> +++ b/appliance/packagelist.in >> @@ -21,10 +21,11 @@ dnl ./configure --with-extra-packages="gdb valgrind [etc]" >> >> dnl Basically the same with a few minor tweaks. >> ifelse(UBUNTU,1,`define(`DEBIAN',1)') >> >> ifelse(REDHAT,1, >> + clevis-luks >> cryptsetup >> cryptsetup-luks dnl old name used before Fedora 17 >> dhclient >> gfs-utils >> gfs2-utils >> @@ -51,10 +52,11 @@ ifelse(REDHAT,1, >> >> ifelse(DEBIAN,1, >> bsdmainutils >> dnl old name used in Jessie and earlier >> btrfs-tools >> + clevis-luks >> cryptsetup >> dash >> extlinux >> dnl gfs-tools, gfs2-tools have been renamed to gfs2-utils >> gfs-tools >> @@ -90,10 +92,11 @@ dnl iproute has been renamed to iproute2 >> ) >> >> ifelse(ARCHLINUX,1, >> cdrkit >> cdrtools >> + clevis >> cryptsetup >> dhclient >> dhcpcd >> gptfdisk >> grub >> @@ -117,10 +120,11 @@ ifelse(ARCHLINUX,1, >> ifelse(SUSE,1, >> dnl It seems no other augeas package depends on it. >> augeas-lenses >> btrfsprogs >> cdrkit-cdrtools-compat >> + clevis >> cryptsetup >> dhcpcd >> dhcp-client >> glibc-locale >> gptfdisk >> diff --git a/lib/MAX_PROC_NR b/lib/MAX_PROC_NR >> index c0556fb20f37..4d0e90cbcbaf 100644 >> --- a/lib/MAX_PROC_NR >> +++ b/lib/MAX_PROC_NR >> @@ -1 +1 @@ >> -511 >> +512 >> diff --git a/lib/guestfs.pod b/lib/guestfs.pod >> index 1ad44e7c2878..e10bf1fbec30 100644 >> --- a/lib/guestfs.pod >> +++ b/lib/guestfs.pod >> @@ -589,15 +589,26 @@ devices. For LUKS it returns the string C<crypto_LUKS>. >> For Windows BitLocker it returns C<BitLocker>. >> >> Then open these devices by calling L</guestfs_cryptsetup_open>. >> Obviously you will require the passphrase! >> >> +Passphrase-less unlocking is supported for LUKS (not BitLocker) >> +block devices that have been encrypted with network-bound disk >> +encryption (NBDE), using Clevis on the Linux guest side, and >> +Tang on a separate Linux server. Open such devices with >> +L</guestfs_clevis_luks_unlock>. The appliance will need >> +networking enabled (refer to L</guestfs_set_network>) and actual >> +connectivity to the Tang servers noted in the C<tang> Clevis >> +pins that are bound to the LUKS header. (This includes the >> +ability to resolve the names of the Tang servers.) >> + >> Opening an encrypted device creates a new device mapper device >> -called F</dev/mapper/mapname> (where C<mapname> is the >> -string you supply to L</guestfs_cryptsetup_open>). >> -Reads and writes to this mapper device are decrypted from and >> -encrypted to the underlying block device respectively. >> +called F</dev/mapper/mapname> (where C<mapname> is the string >> +you supply to L</guestfs_cryptsetup_open> or >> +L</guestfs_clevis_luks_unlock>). Reads and writes to this mapper >> +device are decrypted from and encrypted to the underlying block >> +device respectively. >> >> LVM volume groups on the device can be made visible by calling >> L</guestfs_vgscan> followed by L</guestfs_vg_activate_all>. >> The logical volume(s) can now be mounted in the usual way. > > Looks generally OK. > > Rich. >