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(a)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