In QubesOS 4.2.4 I’d sys-vpn based on kicksecure-17 and it was working fine.
But in QubesOS 4.3 I am trying to open Proton VPN on kicksecure-18. Initially it opened and worked fine but when I enabled kill switch, now its not opening and giving this error:
2025-12-23T13:25:10.486891+00:00 | proton.vpn.core.connection:508 | INFO | CONN:STATE_CHANGED | Disconnected (initial state)
Traceback (most recent call last):
File "/usr/bin/protonvpn-app", line 33, in <module>
sys.exit(load_entry_point('proton-vpn-gtk-app==4.13.1', 'console_scripts', 'protonvpn-app')())
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
File "/usr/lib/python3/dist-packages/proton/vpn/app/gtk/__main__.py", line 35, in main
controller = Controller.get(executor, exception_handler)
File "/usr/lib/python3/dist-packages/proton/vpn/app/gtk/controller.py", line 72, in get
executor.submit(controller.initialize_vpn_connector).result()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
File "/usr/lib/python3.13/concurrent/futures/_base.py", line 456, in result
return self.__get_result()
~~~~~~~~~~~~~~~~~^^
File "/usr/lib/python3.13/concurrent/futures/_base.py", line 401, in __get_result
raise self._exception
File "/usr/lib/python3/dist-packages/proton/vpn/app/gtk/controller.py", line 104, in initialize_vpn_connector
self._connector = await self._api.get_vpn_connector()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/proton/vpn/core/api.py", line 69, in get_vpn_connector
self._vpn_connector = await VPNConnector.get(
^^^^^^^^^^^^^^^^^^^^^^^
...<3 lines>...
)
^
File "/usr/lib/python3/dist-packages/proton/vpn/core/connection.py", line 100, in get
await connector.initialize_state()
File "/usr/lib/python3/dist-packages/proton/vpn/core/connection.py", line 311, in initialize_state
await self._update_state(state)
File "/usr/lib/python3/dist-packages/proton/vpn/core/connection.py", line 532, in _update_state
new_event = await self._current_state.run_tasks()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/proton/vpn/connection/states.py", line 194, in run_tasks
await self.context.kill_switch.enable(permanent=True)
File "/usr/lib/python3/dist-packages/proton/vpn/backend/networkmanager/killswitch/wireguard/wgkillswitch.py", line 64, in enable
await self._ks_handler.add_kill_switch_connection(permanent)
File "/usr/lib/python3/dist-packages/proton/vpn/backend/networkmanager/killswitch/wireguard/killswitch_connection_handler.py", line 134, in add_kill_switch_connection
await _wrap_future(
self.nm_client.add_connection_async(kill_switch.connection, save_to_disk=permanent)
)
File "/usr/lib/python3/dist-packages/proton/vpn/backend/networkmanager/killswitch/wireguard/killswitch_connection_handler.py", line 56, in _wrap_future
return await asyncio.wait_for(
^^^^^^^^^^^^^^^^^^^^^^^
...<2 lines>...
)
^
File "/usr/lib/python3.13/asyncio/tasks.py", line 507, in wait_for
return await fut
^^^^^^^^^
File "/usr/lib/python3/dist-packages/proton/vpn/backend/networkmanager/killswitch/wireguard/nmclient.py", line 185, in _on_connection_added
nm_client.add_connection_finish(res)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^
RuntimeError: Error adding KS connection: nm-settings-error-quark: Insufficient privileges (1)
zsh: exit 1 protonvpn-app
I’m guessing whatever network setting its trying to set, requires administrative privileges to set, and the fact that pkexec and Polkit prompts are disabled in non-sysmaint sessions is interfering with things. You could try:
Booting into “UNRESTRICTED Mode” in the AppVM (you can set this in the settings for the AppVM, under the Advanced tab)
Removing user-sysmaint-split from the template, instructions for doing so are here:
Alternatively, you may be able to use NetworkManager’s built-in VPN support instead of the app (this won’t come with the kill switch though)
I tried unrestricted mode, but the app didn’t start, though the error was different that time. Then I switched to sysmaint mode, the app launched, the VPN connected successfully, but it wasn’t providing internet to other qubes. Ultimately, removing the user-sysmaint-split worked.
I don’t understand the logic behind it, though. Kicksecure-17 also had the user-sysmaint-split, but it never caused an issue for sys-vpn there. In Kicksecure-18, however, it does, even though the option to use sysmaint or unrestricted mode is now available even for AppVMs, which wasn’t possible before.
When you boot into unrestricted admin mode, a service starts in the background that uninstalls user-sysmaint-split (though this will only last for one boot of the AppVM because most changes to AppVMs are ephemeral). This takes some time, so if you try to do something that requires administrative permissions immediately after booting into unrestricted admin mode, it probably won’t work. After a few seconds, it should work though.
As for why this broke in Kicksecure 18, I’m not sure either. Nothing new changed about how tight the restrictions are in Kicksecure, so as long as something doesn’t need administrative permissions, things should work. Maybe a setting that didn’t require administrative permissions in Debian 12 now does require administrative permissions in Debian 13.
Alternatively, you may be able to use NetworkManager’s built-in VPN
support instead of the app (this won’t come with the kill switch
though)
I am using this way of using protonvpn. No sudo needed.
this won’t come with the kill switch though
I am using the Qubes Settings menu for the sys-vpn I have (which I run
the NetworkManager’s built-in VPN support), and in the Firewall
settings, I only allow the vpn server’s IP address to be connected. I
believe this should act as a “kill switch”, in the sense that VPN
server’s IPv4 is the only outgoing address the sys-vpn can connect to.
I reinstalled the user-sysmaint-split package because I was not convinced that, when unrestricted mode is available, there is any need to uninstall this package.
After reinstalling, the first time I started sys-vpn in unrestricted mode, but the proton vpn didn’t work. However, after restarting the qube, it worked on the second attempt.
Through further observation, I discovered that if an app requiring privileged access is started before the user-sysmaint-split removal process completes on each AppVM startup, it will not work, no matter how long you wait afterward.
But if you wait a short while after startup (allowing the system to uninstall user-sysmaint-split in the background) and then start the app, it works.
Well I’m a little late to this post here but is the user “user” in the netdev group?
Don’t VPN’s specifically related to “kill switch” require the ability to modify NetworkManager settings in which they would need to grant this capability to the netdev group?
The user account is able to modify NetworkManager settings. It doesn’t appear to be in the netdev group though. (I’m not sure it should be either; we don’t really like the fact that network settings set by account user leak into account sysmaint, so opening up access here seems possibly counterproductive.)
Don’t know. Would require studying the Proton VPN app to find out.
Not sure if there is one. See:
polkit isn’t completely disabled, but it isn’t just pkexec that is disabled either. Anything that pops up a Polkit authorization prompt via /usr/lib/polkit-1/polkit-agent-helper-1 won’t work, because polkit-agent-helper-1 is SUID-root and has thus been walled off into the sysmaint account. If polkit authorization works without popping up an auth prompt though, that should still work.
Also Polkit still supports .pkla but is moving towards javascript format for rules now in Debian Trixie other modern systems like Fedora. So that Javscript syntax should be used for format to use for rules.
No idea, it would require experimenting with or inspecting the Proton VPN code to find out. If you’re interested, that’s probably the best way to find out.
We don’t usually change the polkit policies unless necessary for security reasons. One exception is Flatpak; we changed its polkit policies to prohibit things like unprivileged system-wide software installation.