Virtual Media extension design proposal
This document is a draft design of extension to
existing Virtual Media.
Some major changes:
* Moves responsibility of spawning nbd-client
outside of bmcweb
* Integrates nbdproxy, and some other features
into external DBus service
* Describes how native support for DMTF's Virtual
Media, can be implemented in OpenBMC
Signed-off-by: Rapkiewicz, Pawel <pawel.rapkiewicz@intel.com>
Change-Id: I60f584591ec556a562e70a656c4b105667a73514
diff --git a/designs/VirtualMedia.md b/designs/VirtualMedia.md
new file mode 100644
index 0000000..635234c
--- /dev/null
+++ b/designs/VirtualMedia.md
@@ -0,0 +1,383 @@
+# Virtual Media (a.k.a. Remote Media)
+
+Author:
+Pawel Rapkiewicz <pawel.rapkiewicz@intel.com> <pawelr>
+
+Primary assignee:
+None
+
+Other contributors:
+None
+
+Created:
+6/4/2019
+
+## Problem Description
+
+Virtual Media allows users to remotely mount given ISO/IMG drive images through
+BMC to Server HOST. The Remote drive is visible in HOST as USB storage device,
+and operates in RO mode, or RW mode (keeping in mind container limitations,
+and write protection switches). This can even be used to install OS on bare
+metal system. This document focuses on few redirection options, like in-browser
+ISO/IMG image mounting, and remote CIFS/HTTPS image mounting.
+
+## References
+
+* Virtual Media is going to use Network Block Device as primary disk image
+forwarder.
+* NBDkit is being used, to serve images from remote storages over HTTPS/CIFS.
+USBGadget as way to expose Media Storage Device to HOST.
+
+## Requirements
+None
+
+## Proposed Design
+
+Virtual Media splits into two modes of operation, lets call it Proxy, and
+Legacy.
+
+* Proxy mode - works directly from browser and uses JavaScript/HTML5 to
+communicate over Secure WebSockets directly to HTTPS endpoint hosted by bmcweb
+on BMC.
+* Legacy mode - is initiated from browser using Redfish defined VirtualMedia
+schemas, then BMC process connects to external CIFS/HTTPS image pointed during
+initialization.
+
+Both methods inherit from default Redfish/BMCWeb authentication and privileges
+mechanisms.
+
+The component diagram below shows Virtual Media high-level overview
+
+```ascii
++------------------+ +----------------------------------+ +-----------------------+
+|Remote Device| | |BMC| +------------+ | |HOST| |
++-------------/ | +---/ +--Dbus----->+VirtualMedia| | +----/ |
+| | | v +------------+ | | |
+| +------------+ | | +-+--------+ | | |
+| |WebBrowser +<----HTTPS------->+BMCWeb | +---------+ | | +----------+ |
+| +------------+ | | +-+--------+ |USBGadget+<--------->+USB Device| |
++------------------+ | ^ +----+----+ | | +----------+ |
+ | | ^ | | |
+ | | +------+ v | | |
+ | +--->+UNIX | +----+----+ | | |
+ | |SOCKET+<->+NBDClient| | | |
++------------------+ | +--->+ | +---------+ | | |
+|Remote Storage| | | | +------+ | | |
++--------------/ | | | | | |
+| | | v | | |
+| +-----------+ | | +-+-------+ | | |
+| |ISO/IMG +<---CIFS/HTTPS+-->+NBDkit | | | |
+| +-----------+ | | +---------+ | +-----------------------+
+| | | |
++------------------+ +----------------------------------+
+```
+
+Virtual Media feature supports multiple, simultaneous connections in both
+modes.
+
+### Network Block Device (NBD)
+
+Reader can notice that most connections on diagram are based on Network Block
+Device. After Sourceforge project description:
+
+> With this compiled into your kernel, Linux can use a remote server as one of
+> its block devices. Every time the client computer wants to read /dev/nbd0,
+> it will send a request to the server via TCP, which will reply with the data
+> requested. This can be used for stations with low disk space (or even
+> diskless - if you use an initrd) to borrow disk space from other computers.
+> Unlike NFS, it is possible to put any file system on it. But (also unlike
+> NFS), if someone has mounted NBD read/write, you must assure that no one else
+> will have it mounted.
+>
+> -- [https://nbd.sourceforge.io/](https://nbd.sourceforge.io/)
+
+In Virtual Media use case, it's being used to pull data from remote client, and
+present it into non BMC mounted `/dev/nbdXX` device. Then the block device is
+being provided to Host through USB Gadget.
+
+### USB Gadget
+
+Part of Linux kernel that makes *emulation* of certain USB device classes
+possible through USB "On-The-Go", if connect appropriately to Host. In Virtual
+Media case it emulates USB mass storage connected to HOST. The source or
+redirection is block device created by nbd-client `/dev/nbdXX`
+
+### Proxy Mode
+
+Proxy Mode uses browser JavaScript and WebSockets support, to create JS NBD
+Server. Browser is responsible for create HTTPS session, authenticate user, and
+receive given privileges, then upgrade HTTPS session to WSS, through mechanisms
+described by [RFC6455](HTTPS://tools.ietf.org/html/rfc6455). Since WSS upgrade,
+JS application is responsible for handling all required by specification NBD
+Server commands.
+
+Multiple, simultaneous connections are supported per opening connections on
+different URIs in HTTPS server. Number of available simultaneous connections is
+being defined in configuration file described in next chapter.
+
+Encryption for proxy is supported through HTTPS/WSS channel and inherits
+encryption mechanisms directly from HTTPS server, all data transactions go
+through bmcweb.
+
+The initialization of connection will look as on diagram:
+
+```ascii
+┌───────┐ ┌──────┐ ┌────────────┐ ┌─────────┐ ┌────┐ ┌─────────┐
+│Browser│ │bmcweb│ │VirtualMedia│ │NBDClient│ │uDEV│ │USBGadget│
+└───┬───┘ └──┬───┘ └─────┬──────┘ └────┬────┘ └─┬──┘ └────┬────┘
+ │ establish HTTPS session │ │ │ │ │
+ │─────────────────────────> │ │ │ │
+ │ │ │ │ │ │
+ │ upgrade to WSS on │ │ │ │ │
+ │ /nbd/X endpoint │ ╔══════════════╧════╗ │ │ │
+ │─────────────────────────> ║* bmcweb creates ░║ │ │ │
+ │ │ ║ /tmp/nbd.X.sock ║ │ │ │
+ │ │ ║* bmcweb locks new ║ │ │ │
+ │ │ ║ connections on ║ │ │ │
+ │ │ ║ endpoint /nbd/X ║ │ │ │
+ │ │ ╚══════════════╤════╝ │ │ │
+ │ │ Mount from: │ │ │ │
+ │ │ /tmp/nbd.X.sock │ │ │ │
+ │ │ ────────────────> │ │ │
+ │ │ │ │ │ │
+ │ │ │ Spawn NBDClient from │ │ │
+ │ │ │ /tmp/nbd.x.sock │ │ │
+ │ │ │ to /dev/nbdX ┌┴┐ │ │
+ │ │ │ ──────────────────> │ │ │ │
+ │ │ │ │ │ │ │
+ │ │ │ Block Device │ │ │ │
+ │ │ │ properties changed │ │ │ │
+ │ │ │ <───────────────────────────────│ │
+ │ │ │ │ │ │ │
+ │ │ │ Configure USB mass │ │
+ │ │ │ storage from /dev/nbd/X │ │
+ │ │ │ ─────────────────────────────────────────>│
+ │ │ │ │ │ │ │
+ │ │ │ Data │ │ │ │
+ │<─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>│
+ │ │ │ │ │ │ │
+```
+
+### Legacy Mode
+
+Legacy Mode uses VirtualMedia schema, defined by DMTF, for mounting external
+CIFS/HTTPS images. The current implementation supports only stream mounting
+as for now. In this case Redfish is only used as mechanism for Virtual Media
+initialization, and is not responsible for data transmission. For data there
+is a separate component responsible for handling CIFS/HTTPS traffic called
+NBDkit.
+
+Multiple, simultaneous connections are supported through spawning additional
+nbkit instances, the number of available instances for CIFS/HTTPS is configured
+and described in details in next chapter.
+
+Encryption is based on remote storage connection, and follows Intel's Best
+security practices, as remote server support such (i.e. HTTPS requires SSL, and
+pure HTTP is not supported, for CIFS protocol version 3.0 allows enabling
+encryption and that will be provided).
+
+The flow looks like below:
+
+```ascii
+┌───────┐ ┌──────────┐ ┌──────┐ ┌────────────┐ ┌──────┐┌─────────┐┌────┐ ┌─────────┐
+│Browser│ │CIFS/HTTPS│ │bmcweb│ │VirtualMedia│ │NBDkit││NBDClient││uDEV│ │USBGadget│
+└───┬───┘ └────┬─────┘ └──┬───┘ └─────┬──────┘ └──┬───┘└────┬────┘└─┬──┘ └────┬────┘
+ │establish HTTPS session│ │ │ │ │ │
+ │───────────────────────> │ │ │ │ │
+ │ │ │ │ │ │ │ │
+ │Create new VirtualMedia│ │ │ │ │ │
+ │ mountpoint via POST │ │ │ │ │ │
+ │───────────────────────> │ │ │ │ │
+ │ │ │ │ │ │ │ │
+ │ │ │ Mount from │ │ │ │ │
+ │ │ │ CIFS/HTTPS │ │ │ │ │
+ │ │ │ location │ │ │ │ │
+ │ │ │────────────> │ │ │ │
+ │ │ │ │ │ │ │ │
+ │ │ │ │Spawn NBDKit mounting│ │ │ │
+ │ │ │ │ given location │ │ │ │
+ │ │ │ │ appropriate │ │ │ │
+ │ │ │ │ /tmp/nbd.X.sock ┌┴┐ │ │ │
+ │ │ │ │ ──────────────────>│ │ │ │ │
+ │ │ │ │ │ │ │ │ │
+ │ │ │ │ Spawn NBDClient from │ │ │
+ │ │ │ │ /tmp/nbd.X.sock to /dev/nbdX ┌┴┐ │ │
+ │ │ │ │ ────────────────────────────>│ │ │ │
+ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ Block Device properties changed │ │
+ │ │ │ │ <────────────────────────────────────── │
+ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ Configure USB mass storage from /dev/nbd/X │
+ │ │ │ │ ───────────────────────────────────────────────>│
+ │ │ │ │ │ │ │ │ │ │
+ │ │ │ │ Data │ │ │ │ │ │
+ │ │ <─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─>│
+```
+
+### Redfish support
+
+Virtual Media Service will be exposed as Redfish VirtualMedia endpoint as
+defined by DMTF. Here are some examples.
+
+#### Virtual Media Collection schema
+Members in collection will be defined based on configuration file described in
+next sections. And will be visible despite media is inserted or not.
+
+```json
+{
+ "@odata.type": "#VirtualMediaCollection.VirtualMediaCollection",
+ "Name": "Virtual Media Services",
+ "Description": "Redfish-BMC Virtual Media Service Settings",
+ "Members@odata.count": 2,
+ "Members": [
+ {
+ "@odata.id": "/redfish/v1/Managers/BMC/VirtualMedia/ISO0"
+ } ,
+ {
+ "@odata.id": "/redfish/v1/Managers/BMC/VirtualMedia/1"
+ }
+ ],
+ "Oem": { } ,
+ "@odata.context": "/redfish/v1/$metadata#VirtualMediaCollection.VirtualMediaCollection",
+ "@odata.id": "/redfish/v1/Managers/BMC/VirtualMedia"
+}
+```
+
+#### Virtual Media schema
+
+```json
+{
+ "@odata.type": "#VirtualMedia.v1_1_0.VirtualMedia",
+ "Id": "ISO0",
+ "Name": "Virtual Removable Media",
+ "MediaTypes": [
+ "CD",
+ "USBStick"
+ ],
+ "Image": "https://192.168.0.1/Images/os.iso",
+ "ImageName": "Os",
+ "ConnectedVia": "URI",
+ "Inserted": true,
+ "WriteProtected": false,
+ "@odata.context": "/redfish/v1/$metadata#VirtualMedia.VirtualMedia",
+ "@odata.id": "/redfish/v1/Managers/BMC/VirtualMedia/ISO0"
+}
+```
+
+Schema will look similar for both Proxy and Legacy Mode. Some key
+differences as follows:
+
+| Field Name | Proxy Mode | Legacy Mode | Comment |
+| -------------------- | ---------- | -------------------------------- | ----------------------------------- |
+| InsertMedia | N/A | action as described by DMTF spec | |
+| Image | N/A | image location | |
+| ImageName | N/A | image name | |
+| ConnectedVia | "Applet" | as described by DMTF spec | applies only for connected media |
+| TransferMethod | "Stream" | "Stream" | "upload" is not supported by design |
+| TransferProtocolType | "OEM" | as described by DMTF spec | |
+
+### Inactivity timeout
+
+Virtual Media supports inactivity timeout, which will break Virtual Media
+connection after certain number of seconds of inactivity. Because nbdclient has
+mechanism for caching image, also kernel has home buffer mechanisms for block
+device, the idea is to prepare a patch on USBGadget driver, that will write USB
+gadget statistics under /proc/USBGadget/lun.X file. Those statistics will be
+observed by Virtual Media application.
+
+### Virtual Media Service
+
+Virtual Media Service is separate application that will coexist on DBus. It
+will be initialized from configuration json file, and expose all available
+for provisioning VirtualMedia objects. Virtual Media is responsible for:
+
+* Exposing current Virtual Media configuration to DBus user.
+* Spawning nbdclient, and monitor its lifetime, for Proxy connections.
+* Spawning nbdkit, and monitor its lifetime, for CIFS/HTTPS connections.
+* Monitoring uDEV for all NBD related block device changes, and
+ configure/de-configure USB Gadget accordingly.
+* Monitor NBD device inactivity period to support inactivity timeout.
+
+### Configuration
+
+Upon process startup, Virtual Media reads its config file, with the following
+structure:
+
+```json
+"InactivityTimeout": 1800, # Timeout of inactivity on device in seconds, that will lead to automatic disconnection
+"MountPoints": {
+ "ISO0": {
+ "EndpointId": "/nbd/0", # bmcweb endpoint (URL) configured for this type of connection
+ "Mode": 0, # 0 - Proxy Mode, 1 - Legacy Mode
+ "NBDDevice": "/dev/nbd0", # nbd endpoint on device usually matches numeric value with EndpointId
+ "UnixSocket": "/tmp/nbd.sock", # defines which Unix socket will be occupied by connection
+ "Timeout": 30, # timeout in seconds passed to nbdclient
+ "BlockSize": 512, # Block size passed to nbdclient
+ }
+},
+```
+
+### DBus Interface
+
+Virtual Media will expose the following object structure. All object paths
+are representation of configuration file described above
+
+```ascii
+/xyz/openbmc_project/VirtualMedia/Proxy/ISO0
+/xyz/openbmc_project/VirtualMedia/Proxy/1
+/xyz/openbmc_project/VirtualMedia/Legacy/0
+/xyz/openbmc_project/VirtualMedia/Legacy/1
+```
+
+Each of object will implement ``xyz.openbmc_project.VirtualMedia.Process``
+interface, which will be defined as follow:
+
+| Name | type | input | return | description |
+| -------- | -------- | ----- | ------- | ------------------------------------------------------------------- |
+| Active | Property | - | BOOLEAN | `True`, if object is occupied by active process, `False` otherwise |
+| ExitCode | Property | - | BYTE | If process terminates this property will contain returned exit code |
+
+Each object will also expose configuration of its own under
+``xyz.openbmc_project.VirtualMedia.MountPoint`` (all properties are RO)
+
+| Name | type | input | return | description |
+| -------------------------- | -------- | ----- | ------ | ------------------------------------------------------------------------- |
+| EndPointId | Property | - | STRING | As per configuration |
+| Mode | Property | - | BYTE | As per configuration |
+| Device | Property | - | STRING | As per configuration |
+| Socket | Property | - | STRING | As per configuration |
+| Timeout | Property | - | UINT16 | As per configuration |
+| BlockSize | Property | - | UINT16 | As per configuration |
+| RemainingInactivityTimeout | Property | - | UINT16 | Seconds to drop connection by server, for activated endpoint, 0 otherwise |
+
+Another interface exposed by each object are stats under
+```xyz.openbmc_project.VirtualMedia.Stats``` (all properties are RO):
+
+| Name | type | input | return | description |
+| ------- | -------- | ----- | ------ | ---------------------------------------- |
+| ReadIO | Property | - | UINT64 | Number of read IOs since image mounting |
+| WriteIO | Property | - | UINT64 | Number of write IOs since image mounting |
+
+Depends on object path, object will expose different interface for mounting image.
+
+For Proxy its ```xyz.openbmc_project.VirtualMedia.Proxy```, defined as follow:
+
+| Name | type | input | return | description |
+| ------- | ------ | ----- | ------- | ------------------------------------------------- |
+| Mount | Method | - | BOOLEAN | Perform a mount to HOST operation on given object |
+| Unmount | Method | - | BOOLEAN | Perform an unmount from HOST on given object |
+
+For Legacy its ```xyz.openbmc_project.VirtualMedia.Legacy```, defined as follow:
+
+| Name | type | input | return | description |
+| ------- | ------ | ------ | ------- | ------------------------------------------------------------------------------------------ |
+| Mount | Method | STRING | BOOLEAN | Perform a mount to HOST operation on given object, with location given as STRING parameter |
+| Unmount | Method | - | BOOLEAN | Perform an unmount from HOST on given object |
+## Alternatives Considered
+Existing implementation in OpenBMC
+
+## Impact
+Shall not affect usability of current Virtual Media implementation
+
+## Testing
+TBD