DBD (Database Daemon)
Overview
DBD provides a centralized database for the OpenXT platform. It maintains an in-memory "database", a json tree, which it then partitions out into multiple database files.
Â
To navigate the json tree, DBD provides a concept of a filesystem-like "path", in which one can navigate the json object tree, concatenating path names with a "/".
e.g. read("foo/bar") or read("/foo/bar") returns the value of "bar" under object "foo".
Â
Database Partitioning
There are three primary uses for the db:
- vm configuration
- dom-store configuration
- platform configuration
The vm configuration, includes all (most) of the properties that the toolstack uses to configure a VM. This is stored on disk in a per-vm file, /config/vms/<vm-uuid>.db
The dom-store configuration, is an optional per-vm data store. This is stored on disk in a per-vm file: /config/dom-store/<vm-uuid>.db
The remaining configuration, used to store configuration state for most of the platform components, is stored in common file: /config/db
Â
Database Consistency
DBD ensures db consistency (on-disk) by writing out all db files whenever the tree is detected as being "dirty" (in-memory tree has been written to, but not yet flushed to disk). It does this using a common pattern:
- write to tmp file on target partition (<db-path>.tmp)
- synchronous flush to disk
- move tmp file to db file (mv <db-path>.tmp <db-path>)
Â
The expected flush delay (such that the tree doesn't have to write to disk on every db write), is ~3 seconds:
https://github.com/OpenXT/manager/blob/master/dbd/dbd.ml#L29
Dom-store and Best Security Practices
What is a "dom-store"?
A dom-store is an optional per-vm storage location, typically used to provide service VMs with persistent configuration data, outside the scope of the virtual hard disk (which is usually read-only).
Â
Based on the APIs below, how does a VM access its "dom-store"?
The same DBUS APIs are used by the VM, and dbd will ensure that any request has its path modified such that its always referring to data within its partition.
e.g. read("foo/bar") from a VM will be translated to read("/dom-store/<vm-uuid>/foo/bar").
Â
How does dbd ensure that it looks up the right value for <vm-uuid> based on a dbus call?
For each dbus request, dbd needs to lookup the calling domid. First, it needs to determine the unique connection id, referred as the "DBus sender ID" (see http://dbus.freedesktop.org/doc/api/html/group__DBusMessage.html#ga13ce514ceb2d1598751f3a7760cf1375). Then it uses this information and translate the sender ID to a domain ID, which it does using an OpenXT-specific DBUS function: org.freedesktop.DBus.GetConnectionDOMID(string dbusSenderId).
Â
Once it has the domain ID, it then needs to convert it to the domain's UUID. To do this, dbd does two xenstore reads.
First it reads the vm's path using the domID: string uuid_path = xenstore-read("/local/domain/<domid>/vm").
Using this vm path, it then reads the UUID: string uuid = xenstore-read(uuid_path + "/uuid")
Now, dbd knows how to map in the <uuid>, such that any request path is updated to be "/dom-store/<uuid>/<requested-path>".
Â
Can I prevent an untrusted VM from accessing dbd's APIs?
If you don't trust the VM to have access these APIs, you may ensure it is disabled by setting "domstore-read-access" and "domstore-write-access" VM properties to false. You may also want to double check your rpc-proxy-firewall configuration.
Sources
https://github.com/OpenXT/manager/tree/master/dbd
DBUS APIs
Description: DBUS IDL Description
https://github.com/OpenXT/idl/blob/master/interfaces/db.xml
com.citrix.xenclient.db.read(string path)
returns: string value
status: implemented
notes:
- non-standard stringify for json values:
- bool => "true" | "false"
- double => string(number)
- object => ""
- array => ""
- null => "null"
- key does not exist => ""
com.citrix.xenclient.db.read_binary(string path)
returns: array of binary strings?
status: unimplemented/broken
notes: should be removed entirely (there is no corresponding "write_binary" anyways). unused on openxt platform today.
com.citrix.xenclient.db.write(string path, string value)
returns: none
status: implemented
notes: will only take string values (causing all values to be written as string to db)
com.citrix.xenclient.db.dump(string path)
returns: json string of db at point "path"
status: implemented
notes: equivalent to "read", but will provide actual backend data, not just a string type
com.citrix.xenclient.db.inject(string path, string json_value)
returns: none
status: implemented
notes:
- injects a json string at a point "path" in the database.
- if a value already exists at "path", it will merge the two values.
- this is most notably used to "overlay" the service-uivm and service-ndvm templates in /usr/share/xenmgr-1.0/templates/default, at boot time.
com.citrix.xenclient.db.list(string path)
returns: array of strings - node names residing under path, if path is an object
status: implemented
notes: only works on objects (not arrays, etc.). will return an empty array
com.citrix.xenclient.db.rm(string path)
returns: none
status: implemented
notes: removes node (any type) located at path
com.citrix.xenclient.db.exists(string path)
returns: bool : true if key exists (as any type), false otherwise
status: implemented