How to use the Qubes Admin Policies/API despite the lack of documentation - WIP

Note: this page is intended to collect all related information to what the individual qubes policies mean Hopefully it’ll be a wiki that everyone can add to. Please contribute any information that you may have on the definitions of the policies.

This includes the following major sections:

  • How to find the names of all the available policies, and how to figure out what those policies do (or at least attempt to)
  • How to find which policies are needed to use a particular qubes command or qubes widget
  • Policy Information discovered so far (I.E. best information/guesses so far for what they actually do)

Intro on policies

The qubes admin policies are core to modern qubes. You have some reason you are using a computer (I.E. what you want to do with it, I.E. your use case). The idea is to editing the policies to make things secure for that use case. Of course you can edit the policies to make things less secure for that use case.
Obviously understanding what the individual policies mean will be important to securing your system.

If you have never edited the policies files:

The policies can be viewed and edited by running: (gear icon) -> "qubes tools" -> qubes-global-config then clicking “open” and selecting any one of the policy files. (This is the prefered way to edit qubes policies because the qubes 4.2 policy editor does syntax checking before saving the file and can save you a lot of time.)

If you have no idea what policies are about, then run: (gear icon) -> "qubes tools" -> qubes-global-config.
In this global config window then Clipboard, File Access, Split GPG, and URL handling screens are each a gui interfaces to small portions of the policy files. Changing options on one of those screens, then clicking “view policy file” at the bottom to see how changing the settings change the policies is probably the fastest way to learn the concept of policies in qubes

Note that changing the policies without thinking through the security implications of your changes could lead to you compromising the security of your system.
[Open question: Is it mentioned anywhere what people should do to learn qubes that we can point to? Things like how it’s preferable to learn and make your mistakes on a system that does not have security critical data on it, and not on a security critical production system?]

Getting the names of all the available policies (without descriptions):

In the current (2024-10-29) policy format, each policy begins with a name. For example, one usable name is: qubes.vm.List

Each policy will be backed by some code that will run somewhere, either in dom0 or in a qube. You can see all the policy options (services) that can run on dom0 by doing this in dom0:

ls /etc/qubes-rpc/

While that shows the policy options that can be run on dom0, that still leaves all the policy options that can be run on the various qubes. They can be seen by running on dom0 things like:

qvm-run --pass-io  sys-whonix 'ls /etc/qubes-rpc/'
If you know how to use shell scripts, ignore the next section and move on. If you need some examples of how you might reduce the ammount of typing, then uncollapse and read this text.

In order to see the list of all the possible policies that can be run on your current system, running this next part in dom0 will show you a list of all the possible commands that will show you policy options in various qubes. (I.E. lets you cut and paste the comands rather then type them out yourself)

qvm-ls --fields=NAME --raw-list | xargs -I $$ echo qvm-run --pass-io  $$ 'ls /etc/qubes-rpc/'

If you have enough memory on your system to run every qube on your system at the same time (which is probably less then 1% of users, (so dont actually run this one)), you could remove the word “echo” and sort it to get all full list of all the non-dom0 policy options, by doing this in dom0:

qvm-ls --fields=NAME --raw-list |  xargs -I $$ qvm-run --pass-io  $$ 'ls /etc/qubes-rpc/' | sort | uniq -c

I don’t expect you to actually do that one, but am just showing you that it’s possible to do something like that. (you are welcome to add a discussion below in the comment of a better way of extracting that information)

As you view the policies available on the qubes, you’ll end up noting that most qubes will have the same basic policy options, and a couple of the qubes might have a few extras. (like whonix qubes, and any that you installed extra qubes support for (example: whonix-bookmarks or “open in qube” [TODO: add some github links so people understand what I’m talking about])

Now we have a complete list of all the names of the options for policies you can write.
[[[ Can someone confirm what I just said? ]]]

Now while we have the names of the policies/services document now, it still would also be nice to know what they actually do! :slight_smile:

Figuring out what a policy of a given name actually does:

This gives you some approaches on how to figure out what the policies do.

Try using it as a qrexec call and see what gets returned

All qubes policies names are actually the name of a call to qrexec, so the method of trying qrexec calls should be comprehensive for all calls that get data (the effect of calls that set data may not be obvious :slight_smile:

From any qubes other then dom0, you can do qrexec calls via qrexec-client-vm. For example, lets say you are using a qube called “test-qube” and want to know what admin.vm.List does. In that case it would work by you typing this in test-qube:

qrexec-client-vm work admin.vm.List < /dev/null

That command should fail, which you can fix by giving test-qube permissions to run that command by adding this line to dom0:/etc/qubes/policy.d/30-user.policy):

admin.vm.List	*	test-qube	     @adminvm	allow target=@adminvm

After adding that policy try running the command again and this time you should be able to figure out what it does from the results it returns.

Possible future way to do it - logging?:

Ideally we’d like to turn on debugging info for qrexec so we can get a log of all the details of all the actual requests coming in. Unfortunately, this is not possible (see comment below).
The following things will be missing from logs (and notifications) about qrexec calls:

  • dom0 entirely bypasses policy, so any calls done via dom0 will not show up in the logs (or policy).
    • (later in this document, we will show you ways around this by making the calls from others VMs)
  • It’s missing some of the parameters to the call.
    Some calls take parameters. We’d like to know what they were. But they get stripped from the logs/notifications about policy accepted/denied actions and adding them back would be difficult (see comment on this below).

I have not tried it yet, but the next thing to try is doing something like this in the VM you are sending the qrexec requests from, before sending requests:

sudo mv /var/run/qubes/qrexec-agent /var/run/qubes/qrexec-agent.original
sudo socat -t100 -x -v UNIX-LISTEN:/var/run/qubes/qrexec-agent,mode=777,reuseaddr,fork UNIX-CONNECT:/var/run/qubes/qrexec-agent.original

In theory that should display all the information from your qrexec requests from that qube to that terminal screen, but it may be in a entirely unreadable form.

If this information was available and we were able to turn this on, one could just filter the log for the policy you are interested and try to correlate the timestamps to your qubes activites.

Similarly, if this information was available we would be able to watch all qrexec calls happening every time we clicked a button and would start learning the policy names/API really quickly.

Attempting to use documentation to get hints on what it does

Some of the policies have been partially documented here: list of the names of the policy/service calls ( alternate version?), the detailed explanation of what they actually do does not appear to be well documented.
NOTE: the table should have 6 columns. If you do not see 6 columns, then you need to scroll the table horizontally

Maybe still useful (since last edit)?

What seems to be a partial list of the qubes.* policies/services can be found at: qubes-doc/developer/debugging/vm-interface.md at 78b074236271586a3522caa2a793cc951cfe812d · QubesOS/qubes-doc · GitHub
There is also a partial list of the policies/services in the filenames in this directory: qubes-core-admin/qubes-rpc at b4df77dc253343f86f01b96c41f0bb732a88e268 · QubesOS/qubes-core-admin · GitHub

You can try searching the source code

Then you can try searching the source code on the name of the policy/service.
Doing this with admin.vm.list eventually finds “List all the domains” which I believe is currently incorrect. I believe it actually the description of the service is something closer
to “Lists all domains for which we have permissions for (example: we are the guivm for)” and description of the policy/servicename would be something like . “allows system1 to see system2 in its qvm-ls list”.
So anyway, this does not always help.

Follow up:
After a couple days I found a old post talking about it in the old format , saved in a “qubes-posts” repo on github that confirms it’s supposed to be the qvm-ls list

Figuring out which of those policy names a particular qubes command or widget uses:

Lets face it, you probably want to know about the policies to you can figure out how to get specific qubes CLI commands (example “qvm-ls”) and qubes GUI widgets (example: the start menu) working. So this section gives you some approaches on how to figure out what policies a particular command or widget needs to work.

Some good news is that whenever qubes denies a request/action becuase of policy, a notification is sent to the screen, that includes the name of the policy/service. This means that if you know what functionality you want your new policy to allow, just try it and look at what appears on the screen.

(Note: dom0 bypasses the policy system, so “just try it” will not get you your error notifications (or log messages) you wanted if you try using the commands/widgets in dom0)

Just try it - (for command line programs):

For example, pick a qube (other then dom0 or the “work” qube) and lets say we had a good reason to want the qube you picked to be able to run a command in the work qube. To figure out the name of the policy/service for it, just try running a command in the work qube:
qvm-run-vm work echo 'hi'

You should see a notification pop up with a error saying
“qubes.VMShell” from {current qube} to work was denied.

This is good, cause we normally don’t have a good reason for arbitrary qubes to run programs in the work qube, but if we did have a good reason, and had thought through the security implications of doing it, we could just add that to the policy with:

qubes.VMShell * {current qube} work allow

Just try it - (for GUI applets/programs):

While the same procedure as is listed above for CLI commands may work for some graphical qubes apps, some of the time, there are cases where it does not work as one might hope.

For example, we are able to find that it does not work for the “qubes domains” try icon in certain cases:

There is a blue cube try icon in the tray in the upper left corer of the qubes screen. If you hover your mouse over it, it should display the text “qubes domains”.

We can find the filename of this application by running: ps aux | grep '^user' | less in dom0. Note the line /usr/bin/bash /usr/bin/widget-wrapper qui-domains which matches the hover text, so this line is probably it.

You wont be able to run qui-domains from a normal qube like the “work” qube.
They are normally only installed on dom0 and sys-gui type qubes.

Introducing sys-gui!:

You can set up a sys-gui qube by doing the following:

First, choose a template to make the management dvms from that will be used in the construction of sys-gui. We will use fedora-38, because as of 2024-11-09:

  • fedora-38 works for setting up sys-gui
  • The debian templates do not work for setting up sys-gui
  • fedora-40-xfce often does not work with a error like: “no such module” salt.{something}.six}

If you do not have the fedora-38 template installed, you can install it with either:

  • qvm-template-gui
  • qvm-template install fedora-38

Then set the template to match whatever you chose:

qvm-prefs default-mgmt-dvm template fedora-38

Then actually create the sys-gui qube:

sudo qubesctl top.enable qvm.sys-gui
sudo qubesctl top.enable qvm.sys-gui pillar=True
sudo qubesctl --all state.highstate

if everything worked, you can then:

sudo qubesctl top.disable qvm.sys-gui
Give yourself a way to unlock the screen when logged into sys-gui:
sudo /bin/sh -c "sudo grep '^user:' /etc/shadow | sed 's/^user:\([^:]*\):.*/sys-gui:\1/' >  /etc/qubes-sys-gui-user-passwords"
sudo chmod 600 /etc/qubes-sys-gui-user-passwords

now create a file in dom0 called /user/local/bin/fix-guivm-password.sh, that contains:

NEW_PASSWORD_HASH=`sudo grep "^$1:" /etc/qubes-sys-gui-user-passwords | sed 's/^[^:]*://'`

ESCAPED_PASSWORD_HASH="${NEW_PASSWORD_HASH//\//\\\/}"

qvm-run --pass-io --no-gui --user=root $1 "sed -i 's/^user:[^:]*:/user:$ESCAPED_PASSWORD_HASH:/' /etc/shadow"

then do:

chmod a+x /usr/local/bin/fix-guivm-password.sh

Then add this line to /usr/bin/qubes-guivm-session:

/usr/local/bin/fix-guivm-password.sh $1

right before this line:

exec qvm-run -p --no-gui --service "$1" qubes.GuiVMSession

Then finally create a qube called work-sys-gui and then do:

qvm-prefs  work-sys-gui guivm sys-gui

Then you can logout, then change the upper right corner option on the login screen to “sys-gui” then login again.

You should now be logged into sys-gui. (sys-gui now seems to have a blue hamster as the desktop background). If you look at the qubes available, it’s just the work-sys-gui qube.
Now, you can load the settings for the work-sys-gui qube, and you can run things in work-sys-gui with your default network. You can also change the network from sys-firewall (or whatever your default network was) to “none”, and network access will no longer be available. perfect.

However, you are not allowed to change network access back from “none” to sys-firewall (or whatever your default network was). This looks like a policy issue! But we have 2 problems now. #1. Every time we want to change the policies, we need to log out and log back in as dom0. And #2 dom0 gets the notifications of what policy was denied when we tried to enable sys-firewall. This takes forever and is not condusive to trial and error.

We will fix those parts now by installing a “useless sys-gui-vnc” that does not have network access (and cant be reached remotely).

sys-gui-vnc for debugging (I.E. with no networking)

By default, sys-gui-vnc does not allow remote control from over the network. Thats something you have to specifically set up. We will not be setting that up. We are just creating a sys-gui-vnc for debugging purposes. The procedure is as follows:

First, choose a template to make the management dvms from that will be used in the construction of sys-gui. We will use fedora-38, because as of 2024-11-09:

  • fedora-38 works for setting up sys-gui
  • The debian templates do not work for setting up sys-gui
  • fedora-40-xfce often does not work with a error like: “no such module” salt.{something}.six}

If you do not have the fedora-38 template installed, you can install it with either:

  • qvm-template-gui
  • qvm-template install fedora-38

Then set the template to match whatever you chose:

qvm-prefs default-mgmt-dvm template fedora-38

Now, create the sys-gui-vnc qube:

sudo qubesctl top.enable qvm.sys-gui-vnc
sudo qubesctl top.enable qvm.sys-gui-vnc pillar=True
sudo qubesctl --all state.highstate

if everything worked, you can then:

sudo qubesctl top.disable qvm.sys-gui-vnc

Then create sys-gui-vnc-viewer qube (that will just run a vnc viewer):

qvm-run --pass-io debian-12 'sudo apt update'
qvm-run --pass-io debian-12 'sudo apt install -y xtightvncviewer'
qvm-shutdown debian-12
qvm-create --template debian-12 --label purple --property netvm=none sys-gui-vnc-viewer

(note: netvm=none does not work, so you’ll need to go into the gui and set the network to none)

Then allow sys-gui-vnc-viewer to connect to the “local” port 5900 in sys-gui-vnc:

echo 'sys-gui-vnc-viewer @default allow,target=sys-gui-vnc'  >> /etc/qubes.d/policy/30-user.qubes.ConnectTCP.policy
qvm-run --pass-io  sys-gui-vnc-viewer "echo 'qvm-connect-tcp 5900:sys-gui-vnc:5900' >> /rw/config/rc.local"

(note: if sys-gui-vnc-viewer is currently running then restart it)

Then allow ourselfs to log in:

sudo /bin/sh -c "sudo grep '^user:' /etc/shadow | sed 's/^user:\([^:]*\):.*/sys-gui-vnc:\1/' >>  /etc/qubes-sys-gui-user-passwords"
/usr/local/bin/fix-guivm-password.sh sys-gui-vnc

Then finally, in sys-gui-vnc-viewer run:

xtightvncviewer localhost

and log in.

Then finally create a qube for sys-gui-vnc to use called work-sys-gui-vnc and then do:

qvm-prefs  work-sys-gui-vnc guivm sys-gui

If you cannot see it in the “big Q” menu on the left, then try rebooting sys-gui-vnc

You may notice that the menu acknowledges the existance of the qube, but not of any of the applications. This is because sys-gui-vnc does not have access to the template that work-sys-gui-vnc uses.

Try launching the settings for work-sys-gui-vnc and watch the error messages:

  • admin.vm.property.Get+netvm sys-gui-vnc to default-dvm
  • admin.vm.feature.List sys-gui-vnc to debian-12
  • admin.vm.property.Get+template sys-gui-vnc to debian-12 (or whatever your template is)

Opportunities!:

admin.vm.property.Get+netvm is not a real policy. admin.vm.property.Get is though, and +netvm probably just means that its asking for just the netvm property. so the assocated policy would be:

admin.vm.property.Get * sys-gui-vnc default-dvm accept

It is probably asking about devault-dvm becauot work, so you’lse default-dvm is set as the default disposable template. This would be a opportunity to explore that policy.

the admin.vm.feature.List sys-gui-vnc to debian-12 error would be the policy:

admin.vm.feature.List * sys-gui-vnc debian-12

and the admin.vm.property.Get+template sys-gui-vnc to debian-12 error would be:

admin.vm.property.Get * sys-gui-vnc to debian-12

Start the work-sys-gui-vnc qube. Notice the following error messages:

Now, note that the network is set to sys-firewall (or whatever your default firewall is).

change the network to none

Now try changing the network back to sys-firewall

this time we get policy errors. Success!

admin.vm.CurrentState to sys-firewall

then open the settings change the network to none, and then try to change the network back. Now you should get a actual error notification!

trying to update the applications gives one error that we havent seen before, before giving a overall failure message. The new policy error is:

admin.vm.Start from sys-gui-vnc to debian-12

which would be the policy:

admin.vm.Start * sys-gui-vnc debian-12

Summary: sys-gui-vnc’s #1 use seems to be as a debugging tool. To use it for remote administration is possible, but requires special setup.

:star: List of What Qubes policies Mean (Best Information So Far) :star:

This is the best information we have so far. If you have any better/more information, please contribute it in a comment below!

policy CLI description GUI description
------------------------------------------------------------------------------- --------------------------------- -------------------------------------
admin.vm.List * {qube1} {qube2} allow verified experimentally: allow qube1 to see qube2 when it runs qvm-ls allow the “big Q” menu in qube1 to see qube2. Also, I conjecture that it will allow qui-domains widget in qube1 to see qube2 (It’s a conjecture because after giving this and some other permissions qui-domains, it crashes). (Note: qui-domains also requires it to be running to be able to see it)
admin.vm.property.Get * {qube1} {qube2} allow speculation: Permission for qube1 to get all properties (the things displayed by “qvm-prefs {qubename}”) from qube 2 but can only get one at a time (possibly this can be scoped to just allowing certain properties, but not others?). cannot confirm because qvm-prefs needs admin.vm.List and admin.vm.property.List to work, but after you give it just those, qvm-prefs crashes with a “QubesDaemonAccessError”, however no notification of denied policies happen.
admin.vm.property.GetAll * {qube1} {qube2} allow speculation: Permission for qube1 to get all properties (the things displayed by “qvm-prefs {qube name}”) at once (same notes as for admin.vm.property.Get
admin.vm.property.Set +{property} {qube1} {qube2} allow Verified: Allow qube1 to set the property {property} for qube2 (verified by inserting a deny policy for admin.property.Set (due to crashing issue)) (note: you have to actually type the + before the property, it’s not a meta characte)
admin.vm.property.Set * {qube1} {qube2} allow Allow qube1 to set any property listed by qvm-prefs for qube2 (same as above, just using a wildcard)

We should probably group them, and then put in section headers like:

Proposed Section Headers

admin.* policies:

Administrative commands. Any policies intending to allow this will need either a “target=dom0” at the end or a “target=@adminvm” at the end

admin.vm.* policies:

A vm is the same thing as a qube which is the same thing as a domain. They are first column that is output when you run qvm-ls in dom0

Setting this policy:

admin.vm.List  *    {qube1}     {qube2}     allow      target=dom0

admin.vm.feature.* policies:

A feature is what gets listed when you type qvm-features {qube name} in dom0. The output is quite sparse though. You can find a much more complete list of features in the man page (I.E. run "man qvm-features in dom0)

admin.vm.properties.* policies:

A property is the same thing as a preference. It is what gets listed when you type qvm-prefs {qube name} in dom0.

admin.label.* policies:

A label is probably referencing a “security label”. You can see the available security labels in qubes by adding this policy to /etc/qubes/policy.d/30-user.policy:

admin.lable.List      *   {name of qube}     dom0   allow

then in the qube that you named in the policy, doing:

qrexec-client-vm dom0 admin.label.List < /dev/null

as of 2024-10-29, the labels available are:

  • 0_red
  • orange
  • yellow
  • green
  • gray
  • blue
  • purple
  • black
    (this may change in the future)

policy.* policies:

Administrative functions relating to viewing or setting the policies themselves - any policies intending to allow this will need either a “target=dom0” at the end or a “target=@adminvm” at the end

qubes.* policies:

Most of the qubes.* policies (maybe all of them?) are for functions that are actually carried out in some way by the target qube itself. (for example, copying a file to the qube requires the qube to actually do something, as opposed to admin.* policies that just provide information about the qube, that the qube itself does not necicarly have). Rarely would these need a “target=dom0” at the end of them.

whonix.* policies:

You can see all the whonix related policies available by listing the whonix templates. Assuming your templates are named whonix-gateway-17 and whonix-workstation-17, the following would reveal the names to you:

qvm-run --pass-io  whonix-gateway-17 'ls /etc/qubes-rpc/'
qvm-run --pass-io  whonix-workstation-17 'ls /etc/qubes-rpc/'

Currently the whonix policies are named:

  • whonix.GatewayCommand
  • whonix.NewStatus
  • whonix.SdwdateStatus

(of course this does not explain what the above policies do)

6 Likes

@marmarek , is there any way to log the parameters of qrexec calls? For example, doing “sudo journalctl | grep qrexec” will show things like:

{timestamp} qrexec-policy-daemon[4662]: qrexec: admin.vm.property.Set+netvm: sys-gui -> sys-gui-work: allowed to dom0

which shows that sys-gui set the netvm for sys-gui-work. Is there a way to log what the netvm was set to?

Also, can you confirm if dom0 bypasses qrexec meaning that if the above request was from the dom0 GUI instead of the sys-gui-GUI, that it would not show up in the log?

1 Like

No, this information is not visible to the qrexec policy. If you really need this (or even more: not just logging but also policy based on the value), you can write an extension that handles admin-permission:* events - those are fired on all Admin API calls, after getting remaining parameters. There is very little documentation about that, but some similar example is in qubes-core-admin/qubes/ext/admin.py at main · QubesOS/qubes-core-admin · GitHub

Yes, calls from dom0 bypass policy completely.

1 Like

I want to make qrexec call with two parameters. The policy should allow a fixed string in the first parameter and variable in second.

I noticed in some examples that multiple parameters are delimited using+ sign. So, I wrote a policy like below and the policy fails with error message that * is invalid

qubes.TestPolicy +fixed1+* vm-source-a vm-target allow
qubes.TestPolicy +fixed2+* vm-source-b vm-target allow

How do I define the policy to allow variable string as second parameter.

Edit:
For anyone interested, I fixed the above problem by removing second parameter from the policy and sent it using stdout of source vm and read it from stdin of target vm. The approach is explained in detail in qubes doc
qrexec-client-vm @defult qubes.TestPolicy+fixed1 /usr/bin/echo parameter2

1 Like