Disposable sys-net: Automatically connect wifi (config file or RPC service)

Qubes OS - Disposable sys-net - Automatically connect wifi


0 - Description


This guide aims to answer to the (reccuring?) question:
How can I make my wifi connection remember my password?

In the “Configuration file” main section, you have 3 methods.

In the “RPC service” main section, you have an example of how to store your wifi credentials outside of the disposable template.

1 - Configuration file


1.1 - Copy the config file

Connect to your wifi with the Network-Manager panel icon.

Once connected, open the Network-Manager setting (right cick on the panel icon) and edit your connection:
Network-Manager > Edit Connections > Wi-FI: YOUR_SSID > Wi-Fi tab

In the Device field you might have wls6 or wls7 value (or else).
Delete it and leave the field empty.
Optionaly, instead of empty value, you could use your MAC address only (e.g. 3C:97:0E:42:1A:19).
Save and close the Network-Manager setting.

In your sys-net disposable:
Copy your config file to your dispobable template:

[user@sys-net-dvm ~]$ qvm-copy /etc/NetworkManager/system-connections/MY_SSID.nmconnection

Replace MY_SSID with your wireless SSID name.
Choose your sys-net disposable template in the popup prompt.

Shutdown your sys-net disposable.

In your sys-net disposable template:
Create NM-system-connections directory:

[user@sys-net-dvm-template ~]$ sudo mkdir -p /rw/config/NM-system-connections/

Copy your config file into it:

[user@sys-net-dvm-template ~]$ sudo cp /home/user/QubesIncoming/sys-net-dvm/MY_SSID.nmconnection /rw/config/NM-system-connections/

Delete QubesIncoming directory:

[user@sys-net-dvm-template ~]$ rm -r /home/user/QubesIncoming

Shutdown your sys-net disposable template.
Start your sys-net disposable.

1.2 - Create the config file

In your sys-net disposable template:
Create the following script (e.g /home/user/auto_connect_wifi.sh).

#!/usr/bin/bash

wifi_dir=/rw/config/NM-system-connections/
wifi_cfg=$wifi_dir/home.nmconnection

sudo mkdir -p $wifi_dir
echo '
[wifi]
ssid=MY_SSID_NAME

[wifi-security]
key-mgmt=wpa-psk
psk=MY_PASSWORD' | sudo tee $wifi_cfg > /dev/null
sudo chmod 600 $wifi_cfg

These 3 fields (ssid, key-mgmt and psk) are the minimum required.
The non-defined fields will use default values.

Replace home.nmconnection with your SSID or any meaningful name (e.g. SSID.nmconnection).
Replace MY_SSID_NAME and MY_PASSWORD with your values.

Make your script executable and launch it.

[user@sys-net-dvm-template ~]$ chmod +x auto_connect_wifi.sh
[user@sys-net-dvm-template ~]$ ./auto_connect_wifi.sh
[user@sys-net-dvm-template ~]$ rm auto_connect_wifi.sh

Shutdown your sys-net disposable template.
Start your sys-net disposable.

1.3 - Connect in the disposable template

This method is not recommended.
Because it require to connect your disposable template to the network.
And because there are other methods available.

Shutdown your sys-net disposable.

Change the setting of your sys-net disposable template:
Set the virtualization mode to hvm:

[user@dom0 ~]$ qvm-prefs sys-net-dvm-template virt_mode hvm

Attach your wireless controller:
wifi_devid is your wireless network id as shown by qvm-pci (e.g. dom0:00_42.0).

[user@dom0 ~]$ wifi_devid=$(qvm-pci | grep Network | cut -d ' ' -f 1)
[user@dom0 ~]$ qvm-pci attach sys-net-dvm-template $wifi_devid

Enable the Network-Manager service:
netvm must be set to none (it should already be the case).

[user@dom0 ~]$ qvm-prefs sys-net-dvm-template provides_network true

Start your sys-net disposable template.

Connect to your wifi with the Network-Manager panel icon.
In your connection setting:
Delete the Device field value and leave the field empty.

Shutdown your sys-net disposable template.

Disable the Network-Manager service:

[user@dom0 ~]$ qvm-prefs sys-net-dvm-template provides_network false

Detach your wireless controller:

[user@dom0 ~]$ qvm-pci detach sys-net-dvm-template $wifi_devid

Set back the virtualization mode to pvh:

[user@dom0 ~]$ qvm-prefs sys-net-dvm-template virt_mode pvh

Start your sys-net disposable.

2 - RPC service


Somehow mandatory reading to understand this part of the guide.
https://www.qubes-os.org/doc/qrexec/

Brief summary of client/server definition for this guide:
The client is your sys-net disposable.
The server is where you will store your wifi credentials.
The client will ask the server to send the wifi credentials (via stdin/stdout).

2.1 - AdminVM server

In this section, the wifi credentials will be stored in the @adminvm (aka dom0).

2.1.1 - RPC policies

In dom0, create the policies to allow the communication between the server and the client.

echo '
qubes.user.ConnectWifi + sys-net-dvm @adminvm allow
qubes.user.ConnectWifi * @anyvm  @anyvm  deny' \
    | sudo tee -a /etc/qubes/policy.d/30-user.policy > /dev/null

2.1.2 - Server

In dom0, create the script that will send the wifi credentials to the client.

server_script=/etc/qubes-rpc/qubes.user.ConnectWifi
echo "
#!/usr/bin/bash

echo 'MY_SSID' 'MY_PASSWD'" | sudo tee $server_script > /dev/null
sudo chmod +x $server_script

2.1.3 - Client

In your sys-net disposable template, create the script that will connect your wifi.

client_script=/rw/config/qConnectWifi
echo '
#!/usr/bin/bash

read my_ssid my_passwd
nmcli device wifi connect "$my_ssid" password "$my_passwd"' \
    | sudo tee $client_script > /dev/null
sudo chown user:user $client_script
sudo chmod +x $client_script

In your sys-net disposable template, append the below code to the rc.local script.
The sys-net disposable template could be used for all of your system qubes (e.g. sys-net, sys-firewall and sys-usb).
Therefore, we test if the qube is sys-net-dvm.
If this is the case, we wait until the network and Network-Manager are ready, then we make an RPC call to connect our wifi.

echo '
/etc/qubes-rpc/qubes.WaitForSession
if [[ $(qubesdb-read /name) == sys-net-dvm ]]
then
    until systemctl is-active network.target; do sleep 1; done
    nm-online --quiet --wait-for-startup
    qrexec-client-vm @adminvm qubes.user.ConnectWifi '$client_script'
fi' | sudo tee -a /rw/config/rc.local > /dev/null

2.2 - App qube server

In this section, the wifi credentials will be stored in an app qube (e.g vault-wifi).

2.2.1 - RPC policies

In dom0, create the policies (@adminvm is replaced with vault-wifi).

echo '
qubes.user.ConnectWifi + sys-net-dvm vault-wifi allow
qubes.user.ConnectWifi * @anyvm  @anyvm  deny' \
    | sudo tee -a /etc/qubes/policy.d/30-user.policy > /dev/null

https://www.qubes-os.org/doc/admin-api/
In dom0, add these policies.
admin.vm.CurrentState will be used in the client to know when vault-wifi is running/ready.
admin.vm.Start and admin.vm.Shutdown will allow the client to start and stop the server.
These 2 policies (Start and Shutdown) are optional, you could instead make vault-wifi to start automatically on boot.

echo '
admin.vm.CurrentState + sys-net-dvm vault-wifi allow target=@adminvm
admin.vm.Start + sys-net-dvm vault-wifi allow target=@adminvm
admin.vm.Shutdown + sys-net-dvm vault-wifi allow target=@adminvm' \
    | sudo tee -a /etc/qubes/policy.d/30-admin-policy.policy > /dev/null

2.2.2 - Server

In your vault-wifi app qube, create the script to send the wifi credentials.
This time, instead of creating it directly in /etc/qubes-rpc/, let’s store it in the home directory.

server_script=/home/user/qubes_connect_wifi.sh
echo "
#!/usr/bin/bash

echo 'MY_SSID' 'MY_PASSWD'" > $server_script
chmod +x $server_script

As you want to use this script only in the app qube and not in the template, create a symbolic link to /usr/local/etc/qubes-rpc/qubes.user.ConnectWifi (instead of /etc/qubes-rpc/ in the template).

sudo mkdir -p /usr/local/etc/qubes-rpc/
sudo ln -s $server_script /usr/local/etc/qubes-rpc/qubes.user.ConnectWifi

2.2.3 - Client

In your sys-net disposable template, create the script that will connect your wifi.
This is the same one as with dom0 as server.

In your sys-net disposable template, append the below code to the rc.local script.
(optional) We start the vault-wifi qube.
We wait until vault-wifi is running before making the RPC call.
(optional) We shutdown the vault-wifi qube.

client_script=/rw/config/qConnectWifi
echo '
/etc/qubes-rpc/qubes.WaitForSession
if [[ $(qubesdb-read /name) == sys-net-dvm ]]
then
    qrexec-client-vm vault-wifi admin.vm.Start < /dev/null
    while [[ $(qrexec-client-vm vault-wifi admin.vm.CurrentState < /dev/null \
                 | sed -E "s/.*power_state=([^ ]+).*/\1/") != Running ]]
    do
        sleep 1
    done
    until systemctl is-active network.target; do sleep 1; done
    nm-online --quiet --wait-for-startup
    qrexec-client-vm vault-wifi qubes.user.ConnectWifi '$client_script'
    qrexec-client-vm vault-wifi admin.vm.Shutdown < /dev/null
fi' | sudo tee -a /rw/config/rc.local > /dev/null

2.3 - Several connections

In this section, the wifi credentials of several wifi connections will be stored in @adminvm.
This can be adjusted to store them in an app qube.

2.3.1 - RPC policies

In dom0, create the necessary policies.
We use the RPC service argument to differentiate each network (the +something after the service).

echo '
qubes.user.ConnectWifi +home sys-net-dvm @adminvm allow
qubes.user.ConnectWifi +work sys-net-dvm @adminvm allow
qubes.user.ConnectWifi * @anyvm  @anyvm  deny' \
    | sudo tee -a /etc/qubes/policy.d/30-user.policy > /dev/null

2.3.2 - Server

In dom0, create the script that will send the wifi credentials to the client.
We will send different wifi credentials according to the received argument.

server_script=/etc/qubes-rpc/qubes.user.ConnectWifi
echo "
#!/usr/bin/bash

case $1 in
    home) echo 'MY_HOME_SSID' 'MY_HOME_PASSWD';;
    work) echo 'MY_WORK_SSID' 'MY_WORK_PASSWD';;
    *) ;;
esac" | sudo tee $server_script > /dev/null
sudo chmod +x $server_script

2.3.3 - Client

In your sys-net disposable template, create the script that will connect your wifi.
This is the same one as with dom0 as server.

In your sys-net disposable template, append the below code to the rc.local script.
We scan the available wifi connection and connect accordingly.
The first match will be use as an argument to our RPC call.

client_script=/rw/config/qConnectWifi
echo '
/etc/qubes-rpc/qubes.WaitForSession
if [[ $(qubesdb-read /name) == sys-net-dvm ]]
then
    until systemctl is-active network.target; do sleep 1; done
    nm-online --quiet --wait-for-startup
    wifi_ssids=($(nmcli --get-values SSID device wifi))
    rpc_arg=$(for ssid in "${wifi_ssids[@]}"
              do
                  [[ $ssid == home ]] && echo home && break
                  [[ $ssid == work ]] && echo work && break
              done)
    qrexec-client-vm @adminvm qubes.user.ConnectWifi+$rpc_arg '$client_script'
fi' | sudo tee -a /rw/config/rc.local > /dev/null

3 - Remarks


For the RPC service:
It shouldn’t happens, but if there is a race condition and your wifi is not automatically connected.
Open your sys-net disposable terminal and launch the RPC call manually.

[user@sys-net-dvm ~]$ cat /rw/config/rc.local

Copy/paste the qrexec-client-vm call and execute it.

This race condition only happens to me twice in a month of testing.
And the two times was because I had modified dom0 max memory.

4 Likes

Hey, thank you for the guide. With some minor tweaks, I was able to get WiFi to automatically connect on my system with a disposable sys-net and an AppVM that stores the credentials.

Some quick notes on what I had to change to get it to work:

  • By default, /rw/config/rc.local uses #!/bin/sh as the shebang, which means that the script may exit with an error since [[ is a bash builtin and isn’t available in POSIX sh. Fixing it involves either changing the shebang or modifying the script to use to use [ ... ] instead of [[ ... ]]. This also means that == becomes = due to differences in syntax.

  • There are several mentions of sys-net-dvm in code snippets, particularly in the test [[ $(qubesdb-read /name) == sys-net-dvm ]] and in RPC policies. I had to change these to sys-net. Otherwise the script will never execute the relevant parts.

  • The interaction between the script that echoes the SSID + password and the script that reads them can cause problems depending on what the SSID is. If the SSID contains unescaped spaces, both the SSID variable and the password variable will end up with incorrect values, causing a failed connection. Escaping spaces with a backslash fixes this.

Thanks again for the guide. :slight_smile:

For anyone wondering where NM-system-connections is coming from, read this: Understanding Qubes /rw folder - #2 by apparatus