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