Anyone got an AppVM or Debian standalone solution for Mullvad VPN?

If it’s not in /etc/skel in the template it wont appear in /home/user in
newly created qubes.

I’m still not clear why this did not work for you, when it has worked
for other users.
Can you confirm what template is used by default-mgmt-dvm qube?

Could you also run this command in dom0:
sudo qubesctl --skip-dom0 --show-output --targets=template-mullvad state.apply mullvad.browser
This is the salt that should be run to copy the browser tarfile in to the
template, and extract the files. Running with --show-output will help
identify what is going wrong in your case.

I never presume to speak for the Qubes team. When I comment in the Forum I speak for myself.

The default-mgmt-dvm qube is debian-12-xfce. The output of the above command is the following:

template-mullvad:
- No matching sls found for ‘mullvad-browser’ in env ‘base’

check your command - it should be mullvad.browser

I never presume to speak for the Qubes team.
When I comment in the Forum I speak for myself.

indeed, my mistake, thanks for your patience. out of 7 states run, only the first 2 failed:

        ID: /etc/skel/Downloads/mullvad_browser-linux-x86_64-13.0.10.tar.xz
  Function: file.managed
    Result: False
   Comment: Unable to manage file: none of the specified sources were found

        ID: mullvad-browser-linux-x86_64-13.0.10.tar.xz
  Function: archive.extracted
      Name: /etc/skel
    Result: False
   Comment: Source file '/etc/skel/Downloads/mullvad_browser-linux-x86_64-13.0.10.tar.xz' does not exist

Note that the tar archive was downloaded into /srv/salt/mullvad/, not into /etc/skel/

Definitely a general connectivity issue, then - data not passing between qubes? IP doesn’t resolve in browser either. Can’t ping your IP or sitename (by design?) but can ping 8.8.8.8 and google.com. I had problems with other ways of connecting to Mullvad too - maybe that MTU thing. Have to investigate.

^ Re log out to avoid ‘clogging up’ your devices:

  • Firstly, its definitely 5, not 6, devices.
  • Secondly, a script that ran to logout automatically as part of qube shutdown procedures would be nice. Not sure if possible.

I’m not sure what the logic is there. With my use case, I want as minimal interaction with the qube as possible - I want it to launch and sit there doing its thing (connectivity) just like sys-firewall does, without input.

What I want is the disposable sys-mullvad remember my account settings. It would either remember device identity (for Mullvad’s count) or, more sanitarily, that auto-logout feature ^ would ensure it closes cleanly and doesn’t clog up Mullvad’s system.

Multiple accounts or location settings could be managed by cloning the qube and altering them individulally (e.g. personal-vm automatically launches a disposable Sweden-sys-mullvad, work-vm launches a disposable London-sys-mullvad and so-on).

I can’t play around with this until I solve that basic connectivity issue and my skills are limited anyway. At the moment I have some high work demands, but I remain interested.

Here’s a Qubes question: how can I tell if a running qube is a named disposable or a regular ‘remembered’ qube? (e.g. I can’t tell by looking at Qubes Domains that sys-firewall is disposable, but it is because that’s how I installed it).

Just to reinforce that autologout feature.

I have had to close a qube without logging out of Mullvad - so I will have to delete an extra device later - because I couldn’t figure out which icon it was.

To be clear:

  1. launch 2+ disp-mullvad or sys-mullvad qubes with Mullvad app run in each of them
  2. minimize app windows to icon in system tray.
  3. attempt to close one disp-mullvad instance
  4. try to figure out which green padlock is which in system tray and fail
  5. close qube without Mullvad’s device log-out.

There is the problem.
There should be a file: /srv/salt/mullvad/mullvad-browser-linux-x86_64-13.0.10.tar.xz
It is included in the rpm file, and is extracted to /srv/salt/mullvad/
It seems that your download of the rpm file failed. I suggest that you
remove the package and reinstall.

I never presume to speak for the Qubes team. When I comment in the Forum I speak for myself.

I havent had reports of this - I could set the MTU by default, but it’s
better at high values. It’s only necessary to change it for some mobile
networks, and it is called out in the Mullvad docs.

5 it is.

I’ll look at the possibility of a logout script.

Different use cases. I understand yours. But the spec was for the VPN to
not start automatically, but require user login. (Many users have
sys-net not starting automatically, and require manual login.)

I never presume to speak for the Qubes team. When I comment in the Forum I speak for myself.

Thanks for your work on this.

Just to keep holding out the begging bowl:

Another neat feature I would like is to have the Mullvad browser always set to turn off DNS over HTTPS because of speed issues - of course this would work in my use case because the Mullvad tunnel would automatically be established.

I’ve thought about this a bit. I guess the use case I am really articulating is:

  1. a disposable Mullvad browser + disposable connection that operates pretty similarly to whonix-workstation-dvm: click and go. Modify connection if you need via widget, but it has your account number and auto-logs you off at close of qube (no clogging the account with disp devices).

  2. an installation of Mullvad browser available to all qubes (i.e installed on the usual fedora template), so connection is arbitrary via choice of netvm. Likely using:

  3. a disp-sys-mullvad gateway, which may already be applied here (but I need to sort my networking problems first, my bad), but remembers network settings.

Maybe other existing solutions are more suitable (e.g. Micah Leah’s instructions, @solene’s instructions), but they aren’t disposables. Could they be? Perhaps I just need to experiment more.

You can use firefox policies to set the desired configuration for your disposables:

But I guess the policy files for Mullvad browser should be in another location so you’ll need to find out where they are stored first.

making a qube disposable is just a few clicks away :+1:

Thankyou solene - when I clear my schedule I’ll get to experimenting/learning.

But I have two three questions for you regarding the approach you outline.

  1. Is this set of instructions going to work on Debian (perhaps Debian minimal)? Is it just a matter of substituting dnf with apt? I’m particularly unsure of the scripts you have there. (My aim is to have as few updates as possible if it is a standalone).
  2. If this can be set up as a disposable sys-mullvad, how will the Mullvad service treat it as a device? A new one every time, or as the original template device? There are a limit of 5.
  3. Following, any ideas on a script to log out with Mullvad automatically as part of the qube shutdown process?

I can remove 3isec-qubes-mullvad-vpn and reinstall. To be clear though, the file /srv/salt/mullvad/mullvad-browser-linux-x86_64-13.0.10.tar.xz does exist in /srv/salt/mullvad/.

This is my solution that works for me™:

On a Debian VM:

sudo dpkg -i MullvadVPN-*_amd64.deb
sudo apt install python3-pexpect

Then run this script as user:

#!/usr/bin/env -S python3 -u
# -*- coding: utf-8 -*-

import pexpect
import subprocess


def remove_devices():
    # Removed previous sessions
    devices = subprocess.check_output(["mullvad", "account", "list-devices"], text=True)
    # Split the output into lines
    device_list = devices.split("\n")
    # For each line, except for the last one
    print("Deleting %i devices" % (len(device_list) - 1))
    for device in device_list[1:-1]:
        subprocess.run(["mullvad", "account", "revoke-device", device])


subprocess.run(["mullvad", "tunnel", "ipv6", "set", "off"])
while True:
    print("Executing mullvad account login")
    child = pexpect.spawn("mullvad account login")
    try:
        child.expect(b"Enter account number: ", timeout=10)
    except pexpect.exceptions.TIMEOUT:
        print(child.before)
        print("Timed out waiting")
        continue
    except pexpect.exceptions.ExceptionPexpect as ep:
        print(ep)
        break

    print("Entering Mullvad account number")
    child.sendline('1234567890123456')  # <---- CHANGE THIS

    try:
        child.expect(' set', timeout=10)
    except pexpect.exceptions.EOF:
        print("End of file, retrying")
        child_msg = child.before.decode("ascii")
        print(child_msg)
    except pexpect.exceptions.TIMEOUT:
        print("Timed out waiting")
        print(child.before.decode('ascii'))
    else:
        print(child.before.decode('ascii'))
        break

subprocess.run(["mullvad", "connect"])
remove_devices()

Interesting.

So your ‘remove’ function looks like it takes out all devices. I want to keep a couple of other (physical) devices, like cell phone. Can I get it to skip them? They would have a stable name.

Why set a condition for IP6 off?

Strange.

I’d like to dig in to your configuration a bit, to understand why the
salting has not worked for you. If you would be happy to do this can you
PM me, so we keep the noise out of the forum, and we can report back to
Forum or GitHub with any issues we find. (Completely understand if you
do not want to do this.)

I never presume to speak for the Qubes team. When I comment in the Forum I speak for myself.

I haven’t tried, but this should work:

#!/usr/bin/env -S python3 -u
# -*- coding: utf-8 -*-

import pexpect
import subprocess


def remove_devices():
    # Removed previous sessions
    devices = subprocess.check_output(["mullvad", "account", "list-devices"], text=True)
    # Split the output into lines
    device_list = devices.split("\n")
    # For each line, except for the last one
    print("Deleting %i devices" % (len(device_list) - 1))
    for device in device_list[1:-1]:
        if device not in ("MYDEVICENAME1", "MYDEVICENAME2"):  # <-- CHANGE THIS
            subprocess.run(["mullvad", "account", "revoke-device", device])


subprocess.run(["mullvad", "tunnel", "ipv6", "set", "off"])
while True:
    print("Executing mullvad account login")
    child = pexpect.spawn("mullvad account login")
    try:
        child.expect(b"Enter account number: ", timeout=10)
    except pexpect.exceptions.TIMEOUT:
        print(child.before)
        print("Timed out waiting")
        continue
    except pexpect.exceptions.ExceptionPexpect as ep:
        print(ep)
        break

    print("Entering Mullvad account number")
    child.sendline('1234567890123456')  # <---- CHANGE THIS

    try:
        child.expect(' set', timeout=10)
    except pexpect.exceptions.EOF:
        print("End of file, retrying")
        child_msg = child.before.decode("ascii")
        print(child_msg)
    except pexpect.exceptions.TIMEOUT:
        print("Timed out waiting")
        print(child.before.decode('ascii'))
    else:
        print(child.before.decode('ascii'))
        break

subprocess.run(["mullvad", "connect"])
remove_devices()

As I understand it you need to set all of your Qubes system to work with IPv6, and I had problems in the past with this. I might give it another try.

For the record - adjusting the AppVM MTU value works for me:
sudo ip link set dev eth0 mtu 1380

You can add this firewall rule to your VPN qube to fix this: