Privileged ports, toffs of the Linux world. Kitten is a small web server that runs as a user-level service and would never need elevated privileges if it wasn’t for one archaic anti-security feature in Linux that dates back to the mainframe era: privileged ports.
Back to the future
As it was in Unix in the 1980s, so it is now, that any process that wants to bind to a port less than 1024 must have elevated privileges. These ports are known as “privileged ports.” While this was a security feature in the days of dumb terminals, in the age of the World Wide Web, it is a security vulnerability.
Privileged ports lead to dangerous security practices, like server processes forgetting to drop privileges and being run as root.
They’ve been obsolete for quite a while, macOS removed them as of Mojave1, and there’s no comparable concept on Windows either. Heck, they apparently even cause climate change.
Not to mention, they’re a world of unnecessary hurt to work around.
In fact, even their original implementation in BSD was a hack:
if (lport) {
u_short aport = htons(lport);
int wild = 0; /* GROSS */
if (aport < IPPORT_RESERVED && u.u_uid != 0) return (EACCES);
…
The BSD people knew this was a hack; they just did it anyway, probably because it was a very handy hack in their trusted local network environment. Unix has quietly inherited it ever since.
— The BSD r* commands and the history of privileged TCP ports
Listen to what some other folks have to say on the subject:
“So we have web servers, and all other servers whose standard ports happen to fall below 1024, expecting to be started as root, bind(2) to their service address, and then “drop privileges”. At this point, you must think I’m shitting you, but I’m not. This is for real. Failure to drop privileges after binding to listen port is a whole category of vulnerability. It’s like throwing egg on the stairs every morning, and several times later each day carefully classifying various accidents in the accident log as “slipped on eggy stairs”. Stop throwing egg on the stairs!”
— The Persistent Idiocy of “Privileged Ports” on Unix
This port 1024 limit is a security measure. But it is based on an obsolete security model and today it only gives a false sense of security and contributes to security holes.
— Why can only root listen to ports below 1024?
These are the reasons why I suspect the 1024 limit was imposed:
Don’t let users run system-level services on the mainframe.
Don’t let the users hog ports for important services on the mainframe.
Don’t let the users run a bogus service to steal logins on the mainframe.
…and here’s why I think these aren’t relevant anymore:
- The mainframe is now the desktop.
– …I really don’t get it
The workaround
On modern Linux systems, you can configure privileged ports using sysctl:
sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80
However, that setting does not survive a reboot.
You can also configure the setting in a persistent way using a configuration file. e.g., by creating a file called /etc/sysctl.d/99-reduce-unprivileged-port-start-to-80.conf with the following content:
net.ipv4.ip_unprivileged_port_start=80
To remove all privileged ports, you can set that value to 0. For our needs, 80 will do as it means our web server can bind to ports 80 and 443 without requiring superuser privileges.
In fact, the Kitten installer does just this.
But even that has issues.
For one thing, it doesn’t work in rootless containers (e.g., if you’re running on an “immutable” Linux distribution like Fedora Silverblue2) because the configuration file gets added to the container, which doesn’t have systemd running sysctl.d so the configuration setting doesn’t get applied at boot.
So we’re back to having to gain temporary privileges and drop them just to alter this configuration setting every time the server is run.
What a pain in the ass.
The fix
The fix is easy: ship Linux distributions so that privileged ports start from 80 to begin with.3
net.ipv4.ip_unprivileged_port_start=80
Sure, this could also be set to zero but setting it to 80 would fix the number one use case today, which is to allow web servers to bind to ports 80 and 443 without requiring superuser privileges. So I’d be happy with either solution.
And for the three folks in Finland who administer multi-user Linux instances and rely on privileged ports for their mainframe-era security properties, they can always run sysctl and set their port limit to 1024 as it was before.
Yay, everyone’s happy!
This is such an easy fix and one that would improve security across the board that I hope Linux distributions will start implementing it as soon as possible.
All it takes is one major distribution to start the trend and the rest will follow. Please feel free to open issues in your distribution of choice and talk to folks you know to get them to do this.
#Linux #PrivilegedPortsMustDie is what I’m saying.
It’s time to move Linux out of the mainframe era… kicking and screaming, if need be. Like this? Fund us!
Small Technology Foundation is a tiny, independent not-for-profit.
We exist in part thanks to patronage by people like you. If you share our vision and want to support our work, please become a patron or donate to us today and help us continue to exist.
I’m just waiting for someone to tell me the folks at Apple don’t understand security and that macOS is less secure now because they removed privileged ports. ↩︎
And these so-called “immutable” distributions are the future of Linux, not just on the desktop but on servers also (see, for example, Fedora CoreOS, etc.) So the sooner we remove the archaic security anti-feature that is privileged ports, the better. ↩︎
I mean, ideally, the kernel could implement this fix and be done with it for every Linux distribution but I have no idea how to get the kernel folks to implement something. I have a feeling it involves a lot of text-only emails and being told how dumb I am in no uncertain terms by multiple people. So, yeah, if anyone else wants to take that one, please be my guest ;) ↩︎