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