Security of Linux Packages
Package management sits at a genuinely sensitive point in a system’s security model: it’s the standard, everyday mechanism for putting new code on a machine with root-level installation rights. This chapter covers the permission and trust dimensions that haven’t been fully addressed yet — auditing shipped file permissions, verifying trust properly, and deliberately pinning versions when you need to.
Permissions As Packaged: The setuid Risk
Recall the build chapters earlier in this section — permissions on a file inside a package are set explicitly before building (chmod 755, or install -m 755 in a spec file), and the package manager installs the file with exactly those permissions, no adjustment. This means a package genuinely can ship a file with the setuid bit set — the same mechanism covered in the Permissions section of this course, letting a program run with its owner’s privileges rather than the privileges of whoever executes it.
A setuid binary owned by root is a legitimate, sometimes necessary thing — passwd itself, referenced back in that same chapter, is exactly this. But it’s also a meaningful attack surface, and worth actively auditing on any system you’re responsible for, rather than only trusting that every package that ships one genuinely needs to:
find / -perm -4000 -type f 2>/dev/nullThis searches the entire filesystem for files with the setuid bit set, using the -perm predicate from the find command introduced earlier in this course. Reviewing this list periodically — confirming every entry is something you recognize and expect — is a genuinely worthwhile security habit, since a setuid binary you don’t recognize is exactly the kind of thing worth investigating immediately.
Verifying A Package Before Installing It
Both ecosystems let you check a package’s signature manually, independent of the automatic check that happens during a normal repository-based install — worth knowing specifically for a .deb or .rpm file you’ve downloaded directly rather than through apt/dnf.
rpm --checksig somepackage.rpmsomepackage.rpm: digests signatures OKConfirms the package’s signature matches a trusted key already known to the system, without installing anything yet.
The Debian side doesn’t have as direct a single-command equivalent built into dpkg itself — signature verification there happens primarily at the apt repository level, covered in the apt chapter’s discussion of signed-by and /etc/apt/trusted.gpg.d/. For a standalone .deb file obtained outside a repository entirely, there’s no signature to check at all in the typical case — which is exactly why installing an untrusted, standalone .deb carries meaningfully more risk than installing the same software through a properly signed repository, where that verification happens automatically as a matter of course.
Pinning Versions: Preventing An Unwanted Update
Sometimes you need a specific package to stay at a specific version deliberately — a dependency your software requires an exact version of, or a security-critical package you don’t want silently jumping to a new major version during a routine apt upgrade.
On the Debian side:
sudo apt-mark hold nginxThis prevents nginx from being upgraded by any future apt upgrade or apt full-upgrade, while still allowing every other package to update normally. Reverse it the same way:
sudo apt-mark unhold nginxapt-mark showholdLists everything currently held — worth checking periodically, since a held package is a held package indefinitely until someone deliberately unholds it, and forgetting one is a genuinely easy way to end up running an unexpectedly outdated, unpatched version of something important.
On the Fedora/RHEL side, the equivalent is the versionlock plugin:
sudo dnf install python3-dnf-plugin-versionlock
sudo dnf versionlock add nginxdnf versionlock listSame underlying idea — a specific package excluded from routine updates until the lock is explicitly removed with dnf versionlock delete nginx.
Building Packages: Never As Root
Worth calling out explicitly, since it’s easy to develop the wrong habit from the build chapters earlier in this section: notice that dpkg-deb --build and rpmbuild were never run with sudo — only the actual dpkg -i/rpm -i install step needed elevated privileges. This isn’t incidental. Building a package involves running the software’s own build process (compiling code, running whatever setup a spec file’s %prep/%install sections specify), and there’s no legitimate reason that process needs root privileges to construct files that will only later be installed with the correct ownership by the actual install step. Running a build process as root needlessly widens what a compromised or buggy build script could do to your system — the same least-privilege reasoning that’s applied consistently throughout this course, here specifically at the packaging stage rather than the runtime stage.
Security Advisories And Automated Patching
Beyond manually auditing and pinning, both ecosystems offer tooling specifically for security-relevant updates, worth knowing exists even without a full hands-on treatment here:
dnf updateinfo list securityLists available updates specifically flagged as security advisories, separate from routine version bumps — useful for prioritizing what genuinely needs attention versus what can wait.
On the Debian side, unattended-upgrades is a commonly deployed package that automatically applies security updates on a schedule, without requiring someone to manually run apt upgrade — a genuinely common production practice for keeping systems patched against known vulnerabilities without relying on someone remembering to check regularly.
What’s Next
With the security and permissions dimension covered — shipped file permissions, verifying trust, pinning versions, and never building as root — the final chapter in this section pulls everything together into a consolidated set of best practices and a walkthrough of the pitfalls that come up most often in real, everyday package management.