[i3wm] Allowing i3's dmenu to work within each AppVM

380 views
Skip to first unread message

Albert Mokrejs

unread,
Jul 4, 2017, 2:32:16 PM7/4/17
to qubes-devel
In its base state, dmenu in Qubes can only reference Dom0 programs and application shortcuts, which is somewhat awkward to use quickly. 

I wrote a wrapper script which uses dmenu_path and a few of the qvm tools to allow you to choose which AppVM dmenu runs for. To maintain Qubes's security-oriented coloring, dmenu is colored for each VM based on its assigned color. Dom0 is referred to a "Root" in the AppVM dmenu. This code requires that *dmenu* be available in each AppVM because it currently relies on dmenu_path. Selecting a non-running AppVM will cause it to be started. The script can then be run again to actually access it. (This is done because on some hardware it may take a longer time to start an AppVM).

I'd appreciate any critiques or suggestions for improvements. For the next version I plan to replace dmenu_path with a script of my own making that would remove the requirement that each AppVM have dmenu_path installed. My long-term goal would be to make this script clear and functional enough to replace stock dmenu in the branch of i3 that Qubes uses, since I believe that this allows for more user flexibility.

-A

qubes_dmenu_run.sh
    #!/bin/bash
    color_list=""
    color_list+="blue: -nb aliceblue -nf blue -sb blue -sf aliceblue\n"
    color_list+="green: -nb aliceblue -nf webgreen -sb webgreen -sf aliceblue\n"
    color_list+="red: -nb aliceblue -nf crimson -sb crimson -sf aliceblue\n"
    color_list+="orange: -nb aliceblue -nf orangered -sb orangered -sf aliceblue\n"
    color_list+="yellow: -nb black -nf yellow -sb yellow -sf black \n"
    color_list+="gray: -nb black -nf aliceblue -sh aliceblue  -sf black \n"
    color_list+="black: -nb aliceblue -nf black -sb black -sf aliceblue \n"
    color_list+="purple: -nb aliceblue -nf blueviolet -sb blueviolet -sf aliceblue"
    
    list="root\n"$( qvm-ls | grep -v } | grep -v ] | grep -v "\-\-" | grep -v name | awk -F'|' '{print $1}' | tr -d ' ' )
    chosen=$( echo -e "$list" | dmenu )
    listed_color=$( qvm-ls | grep $chosen | awk -F'|' '{print $8}' | tr -d ' ' )
    color=$( echo -e "$color_list" | grep "$listed_color:" | awk -F':' '{print $2}')
    #echo $listed_color
    #echo "\n\n"$color
    if [[ "root" = $chosen ]]
    then
    dmenu_run; exit 0
    fi
    if [[ -z $( qvm-ls | grep $chosen | grep Running ) ]]
    then
    echo -e "Starting VM; Please Wait" | dmenu $color; qvm-start $chosen ; exit 0
    fi
    cache="$HOME/.cache/dmenu_run_$chosen"
    qvm-run --pass-io $chosen "dmenu_path" > $cache
    cache_out=$( cat $cache )
    to_run=$( echo -e "$cache_out" | dmenu $color)
    qvm-run $chosen $to_run
    exit 0


qubes_dmenu_run.sh

Marek Marczykowski-Górecki

unread,
Jul 5, 2017, 3:32:20 AM7/5/17
to Albert Mokrejs, qubes-devel
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

On Tue, Jul 04, 2017 at 11:32:16AM -0700, Albert Mokrejs wrote:
> In its base state, dmenu in Qubes can only reference Dom0 programs and
> application shortcuts, which is somewhat awkward to use quickly.
>
> I wrote a wrapper script which uses dmenu_path and a few of the qvm tools
> to allow you to choose which AppVM dmenu runs for. To maintain Qubes's
> security-oriented coloring, dmenu is colored for each VM based on its
> assigned color. Dom0 is referred to a "Root" in the AppVM dmenu. This code
> requires that *dmenu* be available in each AppVM because it currently
> relies on dmenu_path. Selecting a non-running AppVM will cause it to be
> started. The script can then be run again to actually access it. (This is
> done because on some hardware it may take a longer time to start an AppVM).
>
> I'd appreciate any critiques or suggestions for improvements. For the next
> version I plan to replace dmenu_path with a script of my own making that
> would remove the requirement that each AppVM have dmenu_path installed. My
> long-term goal would be to make this script clear and functional enough to
> replace stock dmenu in the branch of i3 that Qubes uses, since I believe
> that this allows for more user flexibility.

One important thing when developing something like this: be _very_
careful when parsing and using data returned by the VM. If you expect a
specific format returned of data, validate it. For example output of
`dmenu_path` - make sure it really contains valid command names, one per
line (no spaces at least). And also make sure that later that data is really
interpreted as a command to run in the VM. In this case, at least
change:

qvm-run $chosen $to_run

to
qvm-run "$chosen" -- "$to_run"

Otherwise, VM could pass additional options to qvm-run, like --localcmd.

> *qubes_dmenu_run.sh*
- --
Best Regards,
Marek Marczykowski-Górecki
Invisible Things Lab
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQEcBAEBCAAGBQJZXJX+AAoJENuP0xzK19cs0Y8H/1MTfIOnU7n/nCxxkmgWT5Gp
wkC5u+m7vdXBqmsI3fBWlT4j7arbwkM9ZHoL+VLaDDiGSuKOGz2dR97uRZ+884AW
MGMI6L1vlzEly9Sv0xqXXdC3PuAH3IRaKd2CsG6+RgL0679pD3y/SDlR09C8Qztb
WYB7ta5s2/KQIB6mT8SVmO7S+RBN/AJrkMYg0Q1NIUwX8swoP2RbkYKJBmzEk+ni
WTc9VZlDTSNAavjuXqjLrCzMerZi76g4m8w2i93IrjF2tofgJastpbVaX30wCCaO
ZbqBs5TAhO9qB7/Hi6o6G7pi0u93ZYdwe9Vvqssor1zbjXhSpHB7uImrRjXMjI8=
=BOXb
-----END PGP SIGNATURE-----

Noor Christensen

unread,
Jul 5, 2017, 8:03:06 AM7/5/17
to qubes-devel
On Tue, Jul 04, 2017 at 11:32:16AM -0700, Albert Mokrejs wrote:
> In its base state, dmenu in Qubes can only reference Dom0 programs and
> application shortcuts, which is somewhat awkward to use quickly.
>
> I wrote a wrapper script which uses dmenu_path and a few of the qvm tools
> to allow you to choose which AppVM dmenu runs for. To maintain Qubes's
> security-oriented coloring, dmenu is colored for each VM based on its
> assigned color. Dom0 is referred to a "Root" in the AppVM dmenu. This code
> requires that *dmenu* be available in each AppVM because it currently
> relies on dmenu_path. Selecting a non-running AppVM will cause it to be
> started. The script can then be run again to actually access it. (This is
> done because on some hardware it may take a longer time to start an AppVM).
>
> I'd appreciate any critiques or suggestions for improvements. For the next
> version I plan to replace dmenu_path with a script of my own making that
> would remove the requirement that each AppVM have dmenu_path installed. My
> long-term goal would be to make this script clear and functional enough to
> replace stock dmenu in the branch of i3 that Qubes uses, since I believe
> that this allows for more user flexibility.

Hi Albert,

Interesting timing - just yesterday I implemented the same feature
myself! However, I chose to use a different approach that does not
depend on dmenu being available on the target.

Basically its a tiny script similar to dmenu_path which is run from
dom0, that list all executable files in directories defined by PATH
environment variable on remote VM.

It takes about 0.16s to generate a complete list.

This list can then be piped to dmenu or rofi or whatever, running in
dom0 or any other VM.

I attached the script to this e-mail, should anyone find it useful!

-- noor

|_|O|_|
|_|_|O| Noor Christensen
|O|O|O| no...@fripost.org ~ 0x401DA1E0
qvm-ls-path.sh
signature.asc

Albert Mokrejs

unread,
Jul 5, 2017, 8:56:37 AM7/5/17
to qubes-devel, kchr+qub...@fripost.org
Hi Noor,

I quite like your script, it seems very compact. My plan for later versions of this wrapper was also to avoid relying on dmenu_path on the target VMs, but I wanted to get the basic functionality down first. I might honestly end up using your script for that since it's a lot cleaner than something I'd probably end up writing. 

However, one behavior from dmenu_path that I think would be worth saving would be caching, since there will be lots of instances where none of the $PATH folders will have changed between uses. I'm still working on ironing out the kinks for this, but I hope to publish the next version by the end of the week. The main idea is to find the most recent update time in the $PATH folders (Effectively the most recent time when the program list has changed somehow) and compare it to the cache file's.

Also,
This list can then be piped to dmenu or rofi or whatever, running in 
dom0 or any other VM. 
Have you somehow gotten dmenu/rofi working within a non-dom0 VM? That was my first attempt before I wrote the qvm-run based script, and I found that Qubes would not let dmenu bind the keyboard, thus leaving dmenu stuck open and unresponsive. If you got it working, I'm somewhat curious as to how.

-Albert

Jean-Philippe Ouellet

unread,
Jul 5, 2017, 11:01:43 AM7/5/17
to Albert Mokrejs, qubes-devel, kchr+qub...@fripost.org
On a previous Qubes installation I'd hacked together a two-level-menu
solution (both running in dom0) where you'd first select the VM, which
would then pop up a correspondingly-colored 2nd menu with the VM name
in the prompt to select what to start in it. The 2nd-level menu was
based on i3-dmenu-desktop, so the menu entries were populated from
only the trusted .desktop files already in dom0, and of course any
other command could be run as well (same with normal dmenu behavior)
by typing the full command, just without autocompletion using
untrusted suggestions from the target VM.

I think such an approach (only autocompleting .desktop files) was a
good balance between an efficient user experience and safety/security
(avoiding any untrusted input handling in dom0), especially since in
practice the things I want to run either have desktop entries anyway
or are more suitably run a terminal.

Unfortunately the source was only in my old dom0 which I neglected to
archive before reinstalling, otherwise I'd gladly share it. These days
I just use xfce panel shortcuts for my most-used things.

Regards,
Jean-Philippe

Noor Christensen

unread,
Jul 5, 2017, 11:17:03 AM7/5/17
to qubes-devel
Hi Albert,

On Wed, Jul 05, 2017 at 05:56:36AM -0700, Albert Mokrejs wrote:
> I quite like your script, it seems very compact. My plan for later versions
> of this wrapper was also to avoid relying on dmenu_path on the target VMs,
> but I wanted to get the basic functionality down first. I might honestly
> end up using your script for that since it's a lot cleaner than something
> I'd probably end up writing.

Thanks! Feel free to use it and give attribute if you like to.

I attached a new version with a bugfix.

> However, one behavior from dmenu_path that I think would be worth saving
> would be caching, since there will be lots of instances where none of the
> $PATH folders will have changed between uses. I'm still working on ironing
> out the kinks for this, but I hope to publish the next version by the end
> of the week. The main idea is to find the most recent update time in the
> $PATH folders (Effectively the most recent time when the program list has
> changed somehow) and compare it to the cache file's.

On the top of my head, maybe an APT hook similar to how the application
menu files are updated would do the trick? A simple script that is run
after packages are installed/removed and writes the current bin entries
to local file.

This script could also be run manually (with qvm-run) or by cron.

For AppVMs, the list of directories could be restricted to /usr/local.

This way you could merge the cache file from its TemplateVM with the
local cache file any time you need a complete list of binaries available
on the AppVM.

However, I wonder if any cache scheme can be faster than just checking
the current PATH and list directory contents, and still be simple
enough to use in arbitrary pipes. Classic tradeoff :-)

A single run of 'stat -c %Y /usr/local/bin' via qvm-run took 0.21s...

But maybe you have a faster solution in mind?

> Also,
>
> > This list can then be piped to dmenu or rofi or whatever,
> > *running in dom0 or any other VM. *
>
> Have you somehow gotten dmenu/rofi working within a non-dom0 VM? That was
> my first attempt before I wrote the qvm-run based script, and I found that
> Qubes would not let dmenu bind the keyboard, thus leaving dmenu stuck open
> and unresponsive. If you got it working, I'm somewhat curious as to how.

Sadly I have the same experience as you.

However, if I have another window open from a program already running on
the target VM it seems to hand over the keyboard to dmenu upon receiving
focus.

I haven't verified if this is a side effect of i3wm, qvm-run, qrexec or
possibly all of them combined. Have you made any tests in other WMs like
XFCE? I know that i3wm has some configurable logic for deciding if/when
window clients should be granted focus...
qvm-ls-path.sh
signature.asc

Noor Christensen

unread,
Jul 5, 2017, 11:59:48 AM7/5/17
to qubes-devel
Also, I would like to add that I fail to see any reason to run dmenu on
another VM than dom0 if the use case is to in turn launch another
program.

Since we are just dealing with file lists, not arbitrary shell code, I
would argue against that running dmenu in dom0 with the output from qvm-run
passed to STDIN is any less secure than running dmenu inside the VM.

In either case the obvious risk is modification of the source list of
binaries passed to the dmenu process, wherever it runs.

But maybe I am missing the point?
signature.asc
Reply all
Reply to author
Forward
0 new messages