Admin API
You may also be interested in the articleIntroducing the Qubes Admin API.
Goals
The goals of the Admin API system is to provide a way for the user to manage the domains without direct access to dom0.
Foreseen benefits include:
Ability to remotely manage the Qubes OS.
Possibility to create multi-user system, where different users are able to use different sets of domains, possibly overlapping. This would also require to have separate GUI domain.
The API would be used by:
Qubes OS Manager (or any tools that would replace it)
CLI tools, when run from another VM (and possibly also from dom0)
remote management tools
any custom tools
Threat model
TBD
Components
A central entity in the Qubes Admin API system is a qubesd
daemon,
which holds information about all domains in the system and mediates all
actions (like starting and stopping a qube) with libvirtd
. The
qubesd
daemon also manages the qubes.xml
file, which stores all
persistent state information and dispatches events to extensions. Last
but not least, qubesd
is responsible for querying the RPC policy for
qrexec daemon.
The qubesd
daemon may be accessed from other domains through a set
of qrexec API calls called the “Admin API”. This API is the intended
management interface supported by the Qubes OS. The API is stable. When
called, the RPC handler performs basic validation and forwards the
request to the qubesd
via UNIX domain socket. The socket API is
private, unstable, and not yet documented.
The calls
The API should be implemented as a set of qrexec calls. This is to make it easy to set the policy using current mechanism.
call |
dest |
argument |
inside |
return |
note |
---|---|---|---|---|---|
|
|
- |
- |
|
|
|
|
- |
- |
|
|
|
|
template |
|
- |
|
|
|
template |
|
- |
either use |
|
template |
- |
- |
name |
Create new DisposableVM, |
|
vm |
- |
- |
- |
|
|
|
- |
- |
|
|
|
|
label |
|
- |
|
|
|
label |
- |
|
|
|
|
label |
- |
|
|
|
|
label |
- |
- |
|
|
|
- |
- |
|
|
|
|
property |
- |
|
Type |
|
|
- |
- |
|
Get all the properties in one call. Each property is returned on a separate line and use the same value encoding as property.Get method, with an exception that newlines are encoded as literal |
|
|
propety |
- |
|
Type |
|
|
property |
- |
|
|
|
|
property |
- |
|
|
|
|
property |
- |
- |
|
|
|
property |
value |
- |
|
|
|
- |
- |
|
|
|
|
property |
- |
|
|
|
|
- |
- |
|
Get all the properties in one call. Each property is returned on a separate line and use the same value encoding as property.Get method, with an exception that newlines are encoded as literal |
|
|
property |
- |
|
Type |
|
|
property |
- |
|
|
|
|
property |
- |
|
|
|
|
property |
- |
- |
|
|
|
property |
value |
- |
|
|
|
- |
- |
|
|
|
|
feature |
- |
value |
|
|
|
feature |
- |
value |
|
|
|
feature |
- |
value |
|
|
|
feature |
- |
value |
|
|
|
feature |
- |
value |
|
|
vm |
feature |
- |
- |
|
|
vm |
feature |
value |
- |
|
|
vm |
- |
- |
|
|
|
vm |
tag |
- |
|
retcode? |
|
vm |
tag |
- |
- |
|
|
vm |
tag |
- |
- |
|
|
vm |
- |
- |
|
rules syntax as in firewall interface (Firewall Rules in 4x) with addition of |
|
vm |
- |
|
- |
set firewall rules, see |
|
vm |
- |
- |
- |
force reload firewall without changing any rule |
|
vm |
device |
options |
- |
|
|
vm |
device |
- |
- |
|
|
vm |
device |
|
- |
|
|
vm |
- |
- |
|
options can include |
|
vm |
device-ident |
- |
|
optional service argument may be used to get info about a single device, optional (device class specific) properties are in |
|
|
- |
- |
|
|
|
|
- |
- |
|
Properties allowed in |
|
|
pool |
- |
|
|
|
|
driver |
|
- |
|
|
|
pool |
|
- |
|
|
|
pool |
- |
- |
|
|
|
pool |
- |
volume id |
|
|
|
pool |
vid |
|
|
|
|
pool |
|
- |
|
|
|
pool |
vid |
|
|
|
|
pool |
vid |
snapshot |
|
|
|
pool |
|
- |
|
|
|
pool |
|
- |
|
|
|
pool |
|
- |
|
|
|
pool |
vid |
token, to be used in |
obtain a token to copy volume |
|
|
pool |
|
- |
copy volume pointed by a token to volume |
|
vm |
- |
- |
|
|
|
vm |
volume |
- |
|
|
|
vm |
volume |
value |
- |
|
|
vm |
volume |
- |
snapshot |
duplicate of |
|
vm |
volume |
- |
snapshot |
id. |
|
vm |
volume |
snapshot |
- |
id. |
|
vm |
volume |
size_in_bytes |
- |
id. |
|
vm |
volume |
raw volume data |
- |
id. |
|
vm |
volume |
|
- |
new version of |
|
vm |
volume |
- |
- |
clear contents of volume |
|
vm |
volume |
- |
token, to be used in |
obtain a token to copy |
|
vm |
volume |
token, obtained with |
- |
copy volume pointed by a token to |
|
vm |
- |
- |
|
state properties: |
|
vm |
- |
- |
- |
|
|
vm |
- |
- |
- |
|
|
vm |
- |
- |
- |
|
|
vm |
- |
- |
- |
|
|
vm |
- |
- |
- |
|
|
|
config id |
- |
- |
config in |
|
|
config id |
- |
backup info |
info what would be included in the backup |
|
|
config id |
- |
- |
cancel running backup operation |
|
|
- |
- |
events |
|
|
|
- |
- |
|
emit VM statistics (CPU, memory usage) in form of events |
Volume properties:
pool
vid
size
usage
rw
source
save_on_stop
snap_on_start
revisions_to_keep
is_outdated
Method admin.vm.Stats
returns vm-stats
events every
stats_interval
seconds, for every running VM. Parameters of
vm-stats
events:
memory_kb
- memory usage in kBcpu_time
- absolute CPU time (in milliseconds) spent by the VM since its startup, normalized for one CPUcpu_usage
- CPU usage in percents
Returned messages
First byte of a message is a message type. This is 8 bit non-zero
integer. Values start at 0x30 (48, '0'
, zero digit in ASCII) for
readability in hexdump. Next byte must be 0x00 (a separator).
This alternatively can be thought of as zero-terminated string containing single ASCII digit.
OK (0)
30 00 <content>
Server will close the connection after delivering single message.
EVENT (1)
31 00 <subject> 00 <event> 00 ( <key> 00 <value> 00 )* 00
Events are returned as stream of messages in selected API calls. Normally server will not close the connection.
A method yielding events will not ever return a OK
or EXCEPTION
message.
When calling such method, it will produce an artificial event
connection-established
just after connection, to help avoiding race
conditions during event handler registration.
EXCEPTION (2)
32 00 <type> 00 ( <traceback> )? 00 <format string> 00 ( <field> 00 )*
Server will close the connection.
Traceback may be empty, can be enabled server-side as part of debug mode. Delimiting zero-byte is always present.
Fields are should substituted into %
-style format string, possibly
after client-side translation, to form final message to be displayed
unto user. Server does not by itself support translation.
Backup profile
Backup-related calls do not allow (yet) to specify what should be
included in the backup. This needs to be configured separately in dom0,
with a backup profile, stored in /etc/qubes/backup/<profile>.conf
.
The file use yaml syntax and have following settings:
include
- list of VMs to include, can also contains tags using$tag:some-tag
syntax or all VMs of given type using$type:AppVM
, known from qrexec policyexclude
- list of VMs to exclude, after evaluatinginclude
settingdestination_vm
- VM to which the backup should be senddestination_path
- path to which backup should be written indestination_vm
. This setting is given toqubes.Backup
service and technically it’s up to it how to interpret it. In current implementation it is interpreted as a directory where a new file should be written (with a name based on the current timestamp), or a command where the backup should be piped tocompression
- should the backup be compressed (default: True)? The value can be eitherFalse
orTrue
for default compression, or a compression command (needs to accept-d
argument for decompression)passphrase_text
- passphrase used to encrypt and integrity protect the backuppassphrase_vm
- VM which should be asked what backup passphrase should be used. The asking is performed usingqubes.BackupPassphrase+profile_name
service, which is expected to output chosen passphrase to its stdout. Empty output cancel the backup operation. This service can be used either to ask the user interactively, or to have some automated passphrase handling (for example: generate randomly, then encrypt with a public key and send somewhere)
Not all settings needs to be set.
Example backup profile:
# Backup only selected VMs
include:
- work
- personal
- vault
- banking
# Store the backup on external disk
destination_vm: sys-usb
destination_path: /media/my-backup-disk
# Use static passphrase
passphrase_text: "My$Very!@Strong23Passphrase"
And slightly more advanced one:
# Include all VMs with a few exceptions
include:
- $type:AppVM
- $type:TemplateVM
- $type:StandaloneVM
exclude:
- untrusted
- $tag:do-not-backup
# parallel gzip for faster backup
compression: pigz
# ask 'vault' VM for the backup passphrase
passphrase_vm: vault
# send the (encrypted) backup directly to remote server
destination_vm: sys-net
destination_path: ncftpput -u my-ftp-username -p my-ftp-pass -c my-ftp-server /directory/for/backups
General notes
there is no provision for
qvm-run
, but there already existsqubes.VMShell
callgenerally actions
*.List
return a list of objects and have “object identifier” as first word in a row. Such action can be also called with “object identifier” in argument to get only a single entry (in the same format).closing qrexec connection normally does not interrupt running operation; this is important to avoid leaving the system in inconsistent state
actual operation starts only after caller send all the parameters (including a payload), signaled by sending EOF mark; there is no support for interactive protocols, to keep the protocol reasonable simple
Policy admin API
There is also an API to view and update Qubes RPC policy files in dom0. All of the following calls have dom0 as destination:
call |
argument |
inside |
return |
---|---|---|---|
policy.List policy.include.List |
- |
- |
<name1>\n<name2>... |
policy.Get policy.include.Get |
name |
- |
<token>\n<content> |
policy.Get policy.include.Get |
name |
<token>\n<content> |
- |
policy.Remove policy.include.Remove |
name |
|
- |
The policy.*
calls refer to main policy files
(/etc/qubes/policy.d/
), and the policy.include.*
calls refer to
the include directory (/etc/qubes/policy.d/include/
). The
.policy
extension for files in the main directory is always omitted.
The responses do not follow admin API protocol, but signal error using an exit code and a message on stdout.
The changes are validated before saving, so that the policy cannot end up in an invalid state (e.g. syntax error, missing include file).
In addition, there is a mechanism to prevent concurrent modifications of the policy files:
A
*.Get
call returns a file along with a token (currently implemented as a hash of the file).When calling
Replace
orRemove
, you need to include the current token as first line. If the token does not match, the modification will fail.When adding a new file using
Replace
, passnew
as token. This will ensure that the file does not exist before adding.To skip the check, pass
any
as token.
TODO
notifications
how to constrain the events?
how to pass the parameters? maybe XML, since this is trusted anyway and parser may be complicated
how to constrain the possible values for
admin.vm.property.Set
etc, like “you can changenetvm
, but you have to pick from this set”; this currently can be done by writing an extensiona call for executing
*.desktop
file from/usr/share/applications
, for use with appmenus without giving access toqubes.VMShell
; currently this can be done by writing custom qrexec callsmaybe some generator for
.desktop
for appmenus, which would wrap calls inqrexec-client-vm