Richard W.M. Jones
2019-Oct-17 09:23 UTC
[Libguestfs] Using Clevis/Tang (NBDE) to automatically decrypt volumes from within libguestfs
This is about Network-Bound Disk Encryption (NBDE) not to be confused of course with NBD! NBDE is where you use disk encryption in your virtual machines. But instead of having to type a passphrase when the guest boots, there is a network server which gives out tokens, so as long as the guest is booted from the trusted network it is able to boot unattended. In RHEL[1] we have three pieces of software which help here: - Clevis: Installed in the guest, it replaces the normal askpass script with one which goes to the server to get the decryption token. - Tang: This is the server component, ie. it must always be running on the trusted network so your guests can boot unattended. - JOSE: Something something JSON encryption. Does some JSON reformatting and is otherwise very opaque. A disk from a VM which is using LUKS + NBDE will have a Clevis keyslot, shown in luksDump output: # cryptsetup luksDump /dev/sda2 ... Tokens: 0: clevis Keyslot: 1 It will also usually have one or more regular keyslots, since a guest which uses NBDE can also be booted disconnected from the trusted network using a regular passphrase at the keyboard. There's an obscure sequence of commands which can be used (when on the trusted network of course) to unlock the disk: Clevis token ID, not keyslot | V # cryptsetup token export --token-id 0 /dev/sda2 {"type":"clevis","keyslots":["1"],"jwe":{"ciphertext":<....>,"encrypted_key":"","iv":<....>,"protected":<....>}} We then use the JOSE tool to extract the "jwe" field alone. I'm not clear if JOSE is necessary here, or we could use any other JSON tool.>From casual inspection it appears all this is doing is taking the"jwe" field and reformatting it as a top-level JSON object in the output: # jose fmt -j- -Og jwe -o- < /tmp/token.json {"ciphertext":"CIPHERTEXT", "encrypted_key":"", "iv":"IV", "protected":"PROTECTED\n"} I've formatted the output, but in reality it's all in one very long line of JSON. For me, encrypted_key field was empty, the other fields had long ASCII strings in some kind of unknown (but printable) encoding. The next step takes the four fields and concatenates them with dots: # jose fmt -j- -Og jwe -o- < /tmp/token | jose jwe fmt -i- -c PROTECTED..IV.CIPHERTEXT It's possible that the double dot contains the empty encrypted_key field. The final string is about 1300 characters long, but printable. We can use this to get the decryption key. Note networking must be available for this to work: # jose fmt -j- -Og jwe -o- < /tmp/token | jose jwe fmt -i- -c > /tmp/key # clevis decrypt < /tmp/key <-- prints a plaintext ASCII key here # cryptsetup open --type luks /dev/sda2 vol Enter passphrase for /dev/sda2: <-- type the ASCII key here (I guess there is a way to automatically feed the key from clevis-decrypt to cryptsetup) This will unlock the disk. To integrate this into libguestfs, we will probably need to carry out the following steps: (a) Add clevis and jose to the packagelist. (b) In programs like virt-v2v that want to use NBDE: (b-1) Increase the memory available to the appliance, see: https://gitlab.com/cryptsetup/cryptsetup/issues/488 (b-2) Enable network, obviously necessary for NBDE. (c) Decide how we are going to automate the clevis steps above. Do we integrate this with the existing common/options/decrypt.c code? Or do we change appliance/init so that it attempts these steps automatically if clevis is present, if there are encrypted disks and if the network is up? Rich. [1] RHEL and Fedora, but it's broken at the moment in Fedora: https://bugzilla.redhat.com/show_bug.cgi?id=1628258 -- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones Read my programming and virtualization blog: http://rwmj.wordpress.com virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into KVM guests. http://libguestfs.org/virt-v2v