[libvirt] [Qemu-devel] libvirt/QEMU/SEV interaction
Brijesh Singh
brijesh.singh at amd.com
Fri Sep 8 15:48:10 UTC 2017
Hi Daniel,
On 09/08/2017 09:52 AM, Daniel P. Berrange wrote:
> On Fri, Sep 08, 2017 at 01:45:06PM +0000, Relph, Richard wrote:
>> A few answers in line…
>>
>> On 9/8/17, 8:16 AM, "Daniel P. Berrange" <berrange at redhat.com> wrote:
>>
>> On Fri, Sep 08, 2017 at 06:57:30AM -0500, Brijesh Singh wrote:
>> > Hi All,
>> >
>> > (sorry for the long message)
>> >
>> > CPUs from AMD EPYC family supports Secure Encrypted Virtualization (SEV)
>> > feature - the feature allows running encrypted VMs. To enable the feature,
>> > I have been submitting patches to Linux kernel [1], Qemu [2] and OVMF [3].
>> > We have been making some good progress in getting patches accepted upstream
>> > in Linux and OVMF trees. SEV builds upon SME (Secure Memory Encryption)
>> > feature -- SME support just got pulled into 4.14 merge window. The base
>> > SEV patches are accepted in OVMF tree -- now we have SEV aware guest BIOS.
>> > I am getting ready to take off "RFC" tag from remaining patches to get them
>> > reviewed and accepted.
>> >
>> > The boot flow for launching an SEV guest is a bit different from a typical
>> > guest launch. In order to launch SEV guest from virt-manager or other
>> > high-level VM management tools, we need to design and implement new
>> > interface between libvirt and qemu, and probably add new APIs in libvirt
>> > to be used by VM management tools. I am new to the libvirt and need some
>> > expert advice while designing this interface. A pictorial representation
>> > for a SEV guest launch flow is available in SEV Spec Appendix A [4].
>> >
>> > A typical flow looks like this:
>> >
>> > 1. Guest owner (GO) asks the cloud provider to launch SEV guest.
>> > 2. VM tool asks libvirt to provide its Platform Diffie-Hellman (PDH) key.
>> > 3. libvirt opens /dev/sev device to get its PDH and return the blob to the
>> > caller.
>>
>> What sort of size are we talking about for the PDH ?
>>
>> The PDH blob is described in reference 4. It’s 0x824 bytes long… a bit over 2K bytes.
>> PDH is “Platform Diffie-Hellman” key, public portion.
>>
>> There's a few ways libvirt could report it
>>
>> 1. As an XML element in the host capabilities XML
>> 2. As an XML element in the emulator capabilities XML
>> 3. Via a newly added host API
>>
>> > 4. VM tool gives its PDH to GO.
>> > 5. GO provides its DH key, session-info and guest policy.
>>
>> Are steps 4 & 5 strictly required to be in this order, or is it
>> possible
>>
>> Steps 4 and 5 must occur in that order. The data sent by the GO in the
>> “session info” is encrypted and integrity protected with keys that the
>> GO derives from the GO private Diffie-Hellman key and the PDH public
>> Diffie-Hellman key.
>>
>> What are the security requirements around the DH key, session info
>> and guest policy ? Are any of them sensitive data which needs to be
>> kept private from untrustworthy users. Also are all three of these
>> items different for every guest launched, or are some of them
>> likely to be the same for every guest ?
>>
>> The PDH is not sensitive. It is relatively static for the platform.
>> (It only changes when the platform owner chooses to change the apparent
>> identity of the platform that will actually run the virtual machine.)
>> The 128-byte session info data is encrypted and integrity protected by
>> the GO. It need not be additionally encrypted or integrity protected.
>> It should vary for EVERY guest launch.
>> The 4-byte guest policy must be sent in the clear as some components
>> may want to observe the policy bits. It may change from guest to guest,
>> but there will likely only be a few common values.
>
> Given this, and the pretty small data sizes, I think this info could
> in fact all be provided inline in the XML config - either hex or base64
> encoded for the binary blobs.
>
>
>
>> > 8. libvirt launches the guest with "-S"
>>
>> All libvirt guests get launched with -S, to give libvirt chance to do some
>> setup before starting vCPUs. Normally vCPUs are started by default, but
>> the VIR_DOMAIN_START_PAUSED flag allows the mgmt app to tell libvirt to
>> leave vCPUS paused.
>>
>> Alternatively, if libvirt sees presencese of 'sev' config for the guest,
>> it could automatically leave it in PAUSED state regardless of the
>> VIR_DOMAIN_START_PAUSED flag.
>>
>> While using the LAUNCH_MEASURE and LAUNCH_SECRET operations is expected
>> to be the common case, they are optional. I would recommend requiring
>> the upstream software to explicitly set the VIR_DOMAIN_START_PAUSED flag,
>> if only to minimize the dependencies…
>
> Ok.
>
>> > 9. While creating the SEV guest qemu does the following
>> > i) create encryption context using GO's DH, session-info and guest policy
>> > (LAUNCH_START)
>> > ii) encrypts the guest bios (LAUNCH_UPDATE_DATA)
>> > iii) calls LAUNCH_MEASUREMENT to get the encrypted bios measurement
>> > 10. By some interface we must propagate the measurement all the way to GO
>> > before libvirt starts the guest.
>>
>> Again, what kind of size data are we talking about for athe "measurement"
>> blob ? a KB, 10's of KB, or more ?
>>
>> The measurement is 48 bytes…
>
> For that small size we can definitely provide is inline in the event
> payload then.
>
>> My first gut instinct would be for QEMU to emit a QMP event when it has
>> the measurement available. The event could include the actual data blob,
>> or we can could add an explicit QMP command to fetch the data blob.
>>
>> Libvirt could listen for this QEMU event, and in turn emit an event from
>> libvirt with the same data, which the mgmt tool can finally give to the
>> GO.
>>
>> > 11. GO verifies the measurement and if measurement matches then it may
>> > give a secret blob -- which must be injected into the guest before
>> > libvirt starts the VM. If verification failed, GO will request cloud
>> > provider to destroy the VM.
>>
>> So we need some mechanism to provide the secret blob. This could be
>> done via a new libvirt API. Alternatively, if we're using virSecret
>> for the other stuff, the guest config XML could include the UUID of
>> a 4th secret. Libvirt would then watch to see when that secret has
>> a value set, and pass that onto QEMU.
>>
>> The secret is optional… it is up to 16KB, already encrypted, and integrity
>> protected with an IV and MAC value passed with the secret. The guest
>> address the secret should be deposited at is
>> Technically, the SEV FW API allows there to be more than one secret…
>> I don’t see a strong reason to require libvirt to support more than one,
>> though.
>
> So I think we could simply add a new API to libvirt to provide this
> data item.
>
>> > 12. After secret blob is injected into guest, we call LAUNCH_FINISH
>> > to destory the encryption context.
>> > 13. libvirt issues "continue" command to resume the guest boot.
>>
>> This is as simple as the mgmt tool calling virDomainResume to unpause
>> CPUs. We could have it such that virDomainResume checks whether the
>> virSecret has been populated secret blob by GO, and pass it onto
>> QEMU at this time.
>>
>> > Please note that the measurement value is protected with transport
>> > encryption key (TIK) and it changes on each run. Similarly the secret blob
>> > provided by GO does not need to be protected using libvirt/qemu APIs. The
>> > secret is protected by TIK. From qemu and libvirt point of view these are
>> > blobs and must be passed as-is to the SEV FW.
>> >
>> > Questions:
>> > a) Do we need to add a new set of APIs in libvirt to return the PDH from
>> > libvirt and VM tool ? Or can we use some pre-existing APIs to pass the
>> > opaque blobs ? (this is mainly for step 3 and 6)
>> > b) do we need to define a new xml tag to for memory-encryption ? or just
>> > use the qemu:args tag ? (step 6)
>>
>> <qemu:args> is explicitly only ever for ad-hoc testing.
>>
>> For anything that is to be used in production deployment we must
>> explicitly model it in the XML. So we definitely need new XML
>> defined.
>>
>> > c) what existing communicate interface can be used between libvirt and qemu
>> > to get the measurement ? can we add a new qemu monitor command
>> > 'get_sev_measurement' to get the measurement ? (step 10)
>>
>> Yes, QMP commands seeem most likely.
>>
>> > d) how to pass the secret blob from libvirt to qemu ? should we consider
>> > adding a new object (sev-guest-secret) -- libvirt can add the object through
>> > qemu monitor.
>>
>> Yeah, that looks like a viable option too.
>
> So I could see a flow like the following:
The flow looks good
>
>
> 1. mgmt tool calls virConnectGetCapabilities. This returns an XML
> document that includes the following
>
> <host>
> ...other bits...
> <sev>
> <platform-key>...hex encoded PDH key...</platform-key>
> </sev>
> </host>
>
> 2. mgmt tool requests to start a guest calling virCreateXML(),
> passing VIR_DOMAIN_START_PAUSED. The XML would include
>
> <sev>
> <owner-key>...hex encode DH key...</owner-key>
> <session-info>..hex encode info...</session-info>
> <policy>...int32 value..</policy>
> </sev>
>
>
> if <sev> is provided and VIR_DOMAIN_START_PAUSED is missing,
> libvirt would report an error and refuse to start the guest
>
One thing which is not clear to me is, how do we know that we are asked
to launch SEV guest? Are you thinking that <sev> tag in the XML will
hint libvirt that GO has asked to launch a SEV guest?
> 3. Libvirt generates the QEMU cli arg to enable SEV using
> the XML data and starts QEMU, leaving CPUs paused
>
I am looking at [1] to get the feel for how do we model it in the XML.
As you can see I am using ad-hoc <qemu:args> to create the sev-guest
object. Currently, sev-guest object accepts the following properties:
dh-cert-file: <file containing the GO DH key>
session-info-file: <file contain the GO session info>
policy: <int32 GO policy>
I believe the new XML model will influence the property input type,
Any recommendation on how do model this part ? thank you so much.
[1] https://libvirt.org/formatdomain.html#elementsCPU
> 4. QEMU emits a SEV_MEASURE event containing the measurement
> blob
>
> 5. Libvirt catches the QEMU event and emits its own
> VIR_CONNECT_DOMAIN_EVENT_SEV_MEASURE event containing
> the measurement blob
>
> 6. GO does its validation of the measurement
>
> 7a If validation failed, then virDomainDestroy() to stop QEMU
>
> 7b If validation succeeed
>
> Optionally call
>
> virDomainSetSEVSecret()
>
> providing the optional secret, then
>
> virDomainResume()
>
> to let QEMU continue
>
>
>
>
> Regards,
> Daniel
>
More information about the libvir-list
mailing list