Package updates/upgrades by maintainers on the Linux platforms are always appreciated, as these updates are intended to offer new features/bug fixes. However, in rare circumstances, there is a need to downgrade the packages to a prior version due to unintended bugs or potential security issues, such as the recent xz-utils backdoor. Consistently backing up your data before significant updates is one good countermeasure against grief. However, what if one was not diligently practicing such measures and urgently needed to simply roll back to a prior version of the said package? I was recently put in this unenviable position when configuring one of my systems, and somehow, the latest version of Proton VPN (version 4.3.0) would not work and displayed the following output after I executed it:
Traceback (most recent call last):
File "/usr/bin/protonvpn-app", line 33, in <module>
sys.exit(load_entry_point('proton-vpn-gtk-app==4.3.0', 'console_scripts', 'protonvpn-app')())
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/bin/protonvpn-app", line 25, in importlib_load_entry_point
return next(matches).load()
^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/importlib/metadata/__init__.py", line 202, in load
module = import_module(match.group('module'))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen importlib._bootstrap>", line 1204, in _gcd_import
File "<frozen importlib._bootstrap>", line 1176, in _find_and_load
File "<frozen importlib._bootstrap>", line 1147, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 940, in exec_module
File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
File "/usr/lib/python3/dist-packages/proton/vpn/app/gtk/__main__.py", line 25, in <module>
from proton.vpn.app.gtk.app import App
File "/usr/lib/python3/dist-packages/proton/vpn/app/gtk/app.py", line 28, in <module>
from proton.vpn.app.gtk.controller import Controller
File "/usr/lib/python3/dist-packages/proton/vpn/app/gtk/controller.py", line 29, in <module>
from proton.vpn.core.api import ProtonVPNAPI, VPNAccount
File "/usr/lib/python3/dist-packages/proton/vpn/core/api.py", line 33, in <module>
from proton.vpn.core.usage import UsageReporting, usage_reporting
File "/usr/lib/python3/dist-packages/proton/vpn/core/usage.py", line 19, in <module>
import sentry_sdk
File "/usr/lib/python3/dist-packages/sentry_sdk/__init__.py", line 1, in <module>
from sentry_sdk.hub import Hub, init
File "/usr/lib/python3/dist-packages/sentry_sdk/hub.py", line 8, in <module>
from sentry_sdk.scope import Scope
File "/usr/lib/python3/dist-packages/sentry_sdk/scope.py", line 7, in <module>
from sentry_sdk.attachments import Attachment
File "/usr/lib/python3/dist-packages/sentry_sdk/attachments.py", line 5, in <module>
from sentry_sdk.envelope import Item, PayloadRef
File "/usr/lib/python3/dist-packages/sentry_sdk/envelope.py", line 7, in <module>
from sentry_sdk.session import Session
File "/usr/lib/python3/dist-packages/sentry_sdk/session.py", line 5, in <module>
from sentry_sdk.utils import format_timestamp
File "/usr/lib/python3/dist-packages/sentry_sdk/utils.py", line 1305, in <module>
HAS_REAL_CONTEXTVARS, ContextVar = _get_contextvars()
^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/sentry_sdk/utils.py", line 1275, in _get_contextvars
if not _is_contextvars_broken():
^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/sentry_sdk/utils.py", line 1228, in _is_contextvars_broken
from eventlet.patcher import is_monkey_patched # type: ignore
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/eventlet/__init__.py", line 6, in <module>
from eventlet import convenience
File "/usr/lib/python3/dist-packages/eventlet/convenience.py", line 7, in <module>
from eventlet.green import socket
File "/usr/lib/python3/dist-packages/eventlet/green/socket.py", line 21, in <module>
from eventlet.support import greendns
File "/usr/lib/python3/dist-packages/eventlet/support/greendns.py", line 78, in <module>
setattr(dns, pkg, import_patched('dns.' + pkg))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/eventlet/support/greendns.py", line 60, in import_patched
return patcher.import_patched(module_name, **modules)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/eventlet/patcher.py", line 132, in import_patched
return inject(
^^^^^^^
File "/usr/lib/python3/dist-packages/eventlet/patcher.py", line 109, in inject
module = __import__(module_name, {}, {}, module_name.split('.')[:-1])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/dns/asyncquery.py", line 32, in <module>
import dns.quic
File "/usr/lib/python3/dist-packages/dns/quic/__init__.py", line 37, in <module>
import trio
File "/usr/lib/python3/dist-packages/trio/__init__.py", line 22, in <module>
from ._core import TASK_STATUS_IGNORED as TASK_STATUS_IGNORED # isort: split
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3/dist-packages/trio/_core/__init__.py", line 21, in <module>
from ._local import RunVar, RunVarToken
File "/usr/lib/python3/dist-packages/trio/_core/_local.py", line 9, in <module>
from . import _run
File "/usr/lib/python3/dist-packages/trio/_core/_run.py", line 2787, in <module>
from ._io_epoll import (
File "/usr/lib/python3/dist-packages/trio/_core/_io_epoll.py", line 202, in <module>
class EpollIOManager:
File "/usr/lib/python3/dist-packages/trio/_core/_io_epoll.py", line 203, in EpollIOManager
_epoll: select.epoll = attr.ib(factory=select.epoll)
^^^^^^^^^^^^
AttributeError: module 'eventlet.green.select' has no attribute 'epoll'
I had to get the VPN working, and I felt that rolling back to a known good version was the quickest way. After thinking hard and with some consulting on online documentation, I managed to rollback to the previous version (4.2.0). I will share the steps I took below.
1. Check your dpkg.log for the dates and packages that were upgraded
cat /var/log/dpkg.log
2. After determining the date range, shorten the log file to double check the entries and uncover the old and new version numbers of the affected packages [in this example, the date used is 2024-04-15 (YYYY-MM-DD) in the first field of dpkg.log, and upgrade is used in the third field of the dpkg.log]
awk '$1=="2024-04-15" && $3=="upgrade"' /var/log/dpkg.log
3. Determine if the cached package files are still on disk (hopefully sudo apt autoclean was not executed!)
awk '$1=="2024-04-15" && $3=="upgrade" {gsub(/:/, "%3a", $5); split($4, f, ":"); print "/var/cache/apt/archives/" f[1] "_" $5 "_" f[2] ".deb"}' /var/log/dpkg.log | xargs -r ls -ld
4. I still had my cached files (thankfully!). I ran the following command to force install the older package files:
sudo dpkg -i /var/cache/apt/archives/proton-vpn-gtk-app_4.2.0_all.deb /var/cache/apt/archives/python3-proton-keyring-linux-secretservice_0.0.1_all.deb /var/cache/apt/archives/python3-proton-keyring-linux_0.0.1_all.deb /var/cache/apt/archives/python3-proton-vpn-api-core_0.21.0_all.deb /var/cache/apt/archives/python3-proton-vpn-connection_0.14.2_all.deb /var/cache/apt/archives/python3-proton-vpn-network-manager-openvpn_0.0.4_all.deb /var/cache/apt/archives/python3-proton-vpn-network-manager_0.4.0_all.deb /var/cache/apt/archives/python3-proton-vpn-session_0.6.5_all.deb
After the install process was completed, I rebooted the machine and was glad to have ProtonVPN working again. Admittedly, it does not solve the mystery why version 4.3.0 could not work on Debian, but at least I can have connectivity and do work.
-----------
Yee Ching Tok, Ph.D., ISC Handler
Personal Site
Mastodon
Twitter