In my first article about the deprecation of apt-key I illustrated a few ways of adding APT repository keys to your system without using the apt-key command. A good follow-up discussion to that article started on twitter (thanks to Petru Ratiu). The topics we discussed were: the use of the
signed-by clause and if it really helps increasing security; the use of package pinning to avoid third-party packages taking over official packages; and the pollution of system directories.
In this post we dig a bit deeper into these topics and how they help, or don’t help, making your system more secure. A TL;DR for the impatient is included at the end of each section.
Let’s try to understand first what’s the problem we are trying to address. Let’s say we want to use a third-party repository (that is: a repository that is not managed by the owner of your distribution, say Debian or Ubuntu). To add that repository, you must trust that repository’s key. If you trusted that key generally, that is: for any apt repository defined on the system, a malicious package from that repository could inject new APT sources in the system that used the same key. Those additional repositories could, for example, contain packages with the same name as system packages (say,
linux-image-amd64) and override them. Next time you run apt upgrade, these packages will replace your kernel with one that contains a backdoor, and there! You are owned. So, how do we avoid that?
The solution suggested in the Debian Wiki is to store the repository key in a directory other than
/etc/apt/trusted.gpg.d, and use the
signed-by clause so that the key can only be used with that repository. While this makes perfect sense, one must remember that when you install a third party package, the installation process will be executed with superuser privileges and the scripts included in the package will also run with elevated privileges. Those scripts could easily trust the repository key globally, or parse the
signed-by clause and use it when injecting malicious APT sources and so on.
TL;DR: you are adding some security with signed-by and not trusting third-party keys globally, but those security measures are easy to circumvent.
The use of package pinning
If you don’t know what package pinning is, I suggest that you read the man page for apt_preferences, or try to. Here are the first few paragraphs so that you get an idea:
The APT preferences file /etc/apt/preferences and the
fragment files in the /etc/apt/preferences.d/ folder can be
used to control which versions of packages will be selected
Several versions of a package may be available for
installation when the sources.list(5) file contains
references to more than one distribution (for example, stable
and testing). APT assigns a priority to each version that is
available. Subject to dependency constraints, apt-get selects
the version with the highest priority for installation. The
APT preferences override the priorities that APT assigns to
package versions by default, thus giving the user control
over which one is selected for installation.
Basically, using APT preferences you can “pin” certain versions of packages (e.g., you want to avoid an upgrade of Firefox because an extension you depend on is not yet available for version higher than a certain one), or give a higher priority to packages coming from a certain repository, so that they are preferred over those coming from others.
In the case of third-party repositories, the Debian Wiki suggest that you use repository pinning together with
signed-by and related stuff, so that packages from the third-party repository cannot override official packages.
This makes sense. However, one should again remember that the installation of packages from a third-party repository will happen with high privileges, and the scripts included in the packages can easily circumvent package pinning. In addition to that, package pinning is a bit too low level for the average user, and can feel awkward even to a more seasoned user. So while possible, this measure is both impractical and not very effective.
TL;DR: package pinning can help preventing third-party repositories from overriding official packages, but is too difficult for the average user to grasp. And a well-done package pinning can be easily circumvented by malicious packages anyway.
That’s probably the worst designed part of the recommendations. It’s good practice on UNIX systems, and Debian in particular, that installations not part of the system deploy files either under subdirectories in
/opt, or in “local” directories (e.g. executables in
/usr/local/bin, shared files in
/usr/local/share and so on). Now, the reasons why Debian don’t want you to put keys in
/etc/apt/trusted.gpg.d is clear, but that they want you to put them in
/usr/share/keyrings, that is a directory that, in theory, should be left under the control of the system alone, made me raise an eyebrow immediately. And it’s actually a requirement, as the use of the verb “MUST” implies:
This package MUST distribute the key in binary form in the aforementioned location (/usr/share/keyrings/deriv-archive-keyring.gpg)
One may argue that
/etc/apt/trusted.gpg.d is a system directory. That is only partially true: it is a system directory, but is also specifically designed to host fragments, as they are called, user-defined configurations of a system service. While I can go with a fragments directory,
/usr/share/keyrings is the only part of the recommendations where I go “no-no”. I think it was a poor decision and I would really like to know what’s the reasoning behind such choice. In lack of a good explanation, I propose an alternative, which would be in line with the use of
local directories: if you are going to use the
signed-by clause you can as well put the keys for third-party repositories in
/usr/local/share/keyrings and leave system directories alone. And don’t forget to read here above about the limitations of using
TL;DR: if you decide not to trust third-party keys globally, use the
signed-by clause and dare not to pollute system directories; e.g.: put your third-party keys in
/usr/local/share/keyrings instead of
Migrating away from apt-key: this is an article that Petru pointed me to. Written by Julian Andres Klode, a Debian developer, it explains how to transition from apt-key and points out the weaknesses of the
signed-by approach. When it comes to putting keys in
/usr/share/keyrings he also points out: I’d personally not tell people to modify
/usr as it’s supposed to be managed by the package manager.
Instructions to connect to a third-party repository: this is the article from the Debian Wiki I referred to several times in these posts.