Reputation: 22077
It's very annoying to have this limitation on my development box, when there won't ever be any users other than me.
I'm aware of the standard workarounds, but none of them do exactly what I want:
Is there some simple sysctl
variable to allow non-root processes to bind to "privileged" ports (ports less than 1024) on Linux, or am I just out of luck?
EDIT: In some cases, you can use capabilities to do this.
Upvotes: 511
Views: 458272
Reputation: 12027
There is also the 'djb way'. You can use this method to start your process as root running on any port under tcpserver, then it will hand control of the process to the user you specify immediately after the process starts.
#!/bin/sh
UID=$(id -u username)
GID=$(id -g username)
exec tcpserver -u "${UID}" -g "${GID}" -RHl0 0 port /path/to/binary &
For more info, see: http://thedjbway.b0llix.net/daemontools/uidgid.html
Upvotes: 4
Reputation: 1737
File capabilities are not ideal, because they can break after a package update.
The ideal solution, IMHO, should be an ability to create a shell with inheritable CAP_NET_BIND_SERVICE
set.
Here's a somewhat convoluted way to do this:
sg $DAEMONUSER "capsh --keep=1 --uid=`id -u $DAEMONUSER` \
--caps='cap_net_bind_service+pei' -- \
YOUR_COMMAND_GOES_HERE"
capsh
utility can be found in libcap2-bin package in Debian/Ubuntu distributions. Here's what goes on:
sg
changes effective group ID to that of the daemon user. This is necessary because capsh
leaves GID unchanged and we definitely do not want it.$DAEMONUSER
--keep=1
), except inheritable cap_net_bind_service
The result is a process with specified user and group, and cap_net_bind_service
privileges.
As an example, a line from ejabberd
startup script:
sg $EJABBERDUSER "capsh --keep=1 --uid=`id -u $EJABBERDUSER` --caps='cap_net_bind_service+pei' -- $EJABBERD --noshell -detached"
Upvotes: 20
Reputation: 1275
I know this is an old question, but now with recent (>= 4.3) kernels there is finally a good answer to this - ambient capabilities.
The quick answer is to grab a copy of the latest (as-yet-unreleased) version of libcap from git and compile it. Copy the resulting progs/capsh
binary somewhere (/usr/local/bin
is a good choice). Then, as root, start your program with
/usr/local/bin/capsh --keep=1 --user='your-service-user-name' \
--inh='cap_net_bind_service' --addamb='cap_net_bind_service' \
-- -c 'your-program'
In order, we are
cap_net_bind_service
capability to the inherited & ambient setsbash -c 'your-command'
(since capsh
automatically starts bash with the arguments after --
)There's a lot going on under the hood here.
Firstly, we are running as root, so by default, we get a full set of capabilities. Included in this is the ability to switch uid & gid with the setuid
and setgid
syscalls. However, ordinarily when a program does this, it loses its set of capabilities - this is so that the old way of dropping root with setuid
still works. The --keep=1
flag tells capsh
to issue the prctl(PR_SET_KEEPCAPS)
syscall, which disables the dropping of capabilities when changing user. The actual changing of users by capsh
happens with the --user
flag, which runs setuid
and setgid
.
The next problem we need to solve is how to set capabilities in a way that carries on after we exec
our children. The capabilities system has always had an 'inherited' set of capabilities, which is " a set of capabilities preserved across an execve(2)" [capabilities(7)]. Whilst this sounds like it solves our problem (just set the cap_net_bind_service
capability to inherited, right?), this actually only applies for privileged processes - and our process is not privileged anymore, because we already changed user (with the --user
flag).
The new ambient capability set works around this problem - it is "a set of capabilities that are preserved across an execve(2) of a program that is not privileged." By putting cap_net_bind_service
in the ambient set, when capsh
exec's our server program, our program will inherit this capability and be able to bind listeners to low ports.
If you're interested to learn more, the capabilities manual page explains this in great detail. Running capsh
through strace
is also very informative!
Upvotes: 23
Reputation: 837
You can setup a local SSH tunnel, eg if you want port 80 to hit your app bound to 3000:
sudo ssh $USERNAME@localhost -L 80:localhost:3000 -N
This has the advantage of working with script servers, and being very simple.
Upvotes: 25
Reputation: 43327
Or patch your kernel and remove the check.
(Option of last resort, not recommended).
In net/ipv4/af_inet.c
, remove the two lines that read
if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
goto out;
and the kernel won't check privileged ports anymore.
Upvotes: 37
Reputation: 82306
You can use IPtables to redirect traffic on port 80 to another port on 127.0.0.1 (e.g. port 6666), which does not require root privileges.
# Enable packet forwarding in case it's disabled.
echo 1 > /proc/sys/net/ipv4/ip_forward
# Redirect all incoming traffic on port 80 to 127.0.0.1:6666
iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 127.0.0.1:6666
# Ensure the response packets are routed correctly back to the client
iptables -t nat -A POSTROUTING -p tcp --dport 6666 -d 127.0.0.1 -j SNAT --to-source 127.0.0.1
You might also want to redirect udp packets:
# Redirect all incoming UDP traffic on port 80 to 127.0.0.1:6666
iptables -t nat -A PREROUTING -p udp --dport 80 -j DNAT --to-destination 127.0.0.1:6666
# Ensure the response packets are routed correctly back to the client for UDP
iptables -t nat -A POSTROUTING -p udp --dport 6666 -d 127.0.0.1 -j SNAT --to-source 127.0.0.1
Then you need to save the iptables config:
iptables-save > /etc/iptables/rules.v4
On Red Hat/CentOS systems:
service iptables save
IPv6 support has been added.
Disclaimer (update per 2021): Note that authbind
works via LD_PRELOAD
, which is only used if your program uses libc
, which is (or might) not be the case if your program is compiled with GO/Rust, or any other compiler that avoids C. If you use go or rust, set the kernel parameter for the protected port range, see the sysctl method bellow </EndUpdate>
Authbind
is much better than CAP_NET_BIND_SERVICE
or a custom kernel.
CAP_NET_BIND_SERVICE
grants trust to the binary but provides no
control over per-port access. Authbind
grants trust to the
user/group and provides control over per-port access, and
supports both IPv4 and IPv6Install: apt-get install authbind
Configure access to relevant ports, e.g. 80 and 443 for all users and groups:
sudo touch /etc/authbind/byport/80
sudo touch /etc/authbind/byport/443
sudo chmod 777 /etc/authbind/byport/80
sudo chmod 777 /etc/authbind/byport/443
Execute your command via authbind
(optionally specifying --deep
or other arguments, see man authbind
):
authbind --deep /path/to/binary command line args
e.g.
authbind --deep java -jar SomeServer.jar
If authbind doesn't cut it, because you use GO or Rust (or whatever), you can use the sysctl method.
That is, as you can see bellow, the kernel limited ports bellow 1000 to the root user. You could change that number in the kernel sources, but basically, as someone in the comments remarked, you don't want a custom kernel.
As of 2017, updating the kernel is no longer required.
You can now set the start number of the privileged ports with sysctl:
sysctl net.ipv4.ip_unprivileged_port_start=80
Or to persist:
sysctl -w net.ipv4.ip_unprivileged_port_start=80
And if that yields an error, simply edit /etc/sysctl.conf
with nano and set the parameter there for persistence across reboots.
or via procfs
echo 80 | sudo tee /proc/sys/net/ipv4/ip_unprivileged_port_start
As a follow-up to Joshua's fabulous (=not recommended unless you know what you do) recommendation to hack the kernel:
I've first posted it here.
Simple. With a normal or old kernel, you don't.
As pointed out by others, iptables
can forward a port.
As also pointed out by others, CAP_NET_BIND_SERVICE
can also do the job.
Of course CAP_NET_BIND_SERVICE
will fail if you launch your program from a script, unless you set the cap on the shell interpreter, which is pointless, you could just as well run your service as root...
e.g. for Java, you have to apply it to the JAVA JVM
sudo /sbin/setcap 'cap_net_bind_service=ep' /usr/lib/jvm/java-8-openjdk/jre/bin/java
Obviously, that then means any Java program can bind system ports.
Ditto for mono/.NET.
I'm also pretty sure xinetd isn't the best of ideas.
But since both methods are hacks, why not just lift the limit by lifting the restriction ?
Nobody said you have to run a normal kernel, so you can just run your own.
You just download the source for the latest kernel (or the same you currently have). Afterwards, you go to:
/usr/src/linux-<version_number>/include/net/sock.h:
There you look for this line
/* Sockets 0-1023 can't be bound to unless you are superuser */
#define PROT_SOCK 1024
and change it to:
#define PROT_SOCK 0
if you don't want to have an insecure ssh situation, you alter it to this:
#define PROT_SOCK 24
Generally, I'd use the lowest setting that you need, e.g. 79 for http, or 24 when using SMTP on port 25.
That's already all.
Compile the kernel, and install it.
Reboot.
Finished - that stupid limit is GONE, and that also works for scripts.
Here's how you compile a kernel:
https://help.ubuntu.com/community/Kernel/Compile
# You can get the kernel-source via package `linux-source`, no manual download required
apt-get install linux-source fakeroot
mkdir ~/src
cd ~/src
tar xjvf /usr/src/linux-source-<version>.tar.bz2
cd linux-source-<version>
# Apply the changes to PROT_SOCK define in /include/net/sock.h
# Copy the kernel config file you are currently using
cp -vi /boot/config-`uname -r` .config
# Install ncurses libary, if you want to run menuconfig
apt-get install libncurses5 libncurses5-dev
# Run menuconfig (optional)
make menuconfig
# Define the number of threads you wanna use when compiling (should be <number CPU cores> - 1), e.g. for quad-core
export CONCURRENCY_LEVEL=3
# Now compile the custom kernel
fakeroot make-kpkg --initrd --append-to-version=custom kernel-image kernel-headers
# And wait a long long time
cd ..
In a nutshell:
iptables
if you want to stay secure,Upvotes: 76
Reputation: 22077
Okay, thanks to the people who pointed out the capabilities system and CAP_NET_BIND_SERVICE
capability. If you have a recent kernel, it is indeed possible to use this to start a service as non-root but bind low ports. The short answer is that you do:
setcap 'cap_net_bind_service=+ep' /path/to/program
And then anytime program
is executed thereafter it will have the CAP_NET_BIND_SERVICE
capability. setcap
is in the debian package libcap2-bin
.
Now for the caveats:
#!
line to launch an interpreter). In this case, as far I as understand, you'd have to apply the capability to the interpreter executable itself, which of course is a security nightmare, since any program using that interpreter will have the capability. I wasn't able to find any clean, easy way to work around this problem.LD_LIBRARY_PATH
on any program
that has elevated privileges like setcap
or suid
. So if your program
uses its own .../lib/
, you might have to look into another option like port forwarding.Resources:
setcap
.Note: RHEL first added this in v6.
Upvotes: 520
Reputation: 5951
Two other simple possibilities: Daemon and Proxy
There is an old (unfashionable) solution to the "a daemon that binds on a low port and hands control to your daemon". It's called inetd (or xinetd).
The cons are:
Pros:
Another alternative: a hacked-up proxy (netcat
or even something more robust) from the privileged port to some arbitrary high-numbered port where you can run your target daemon. (Netcat is obviously not a production solution, but "just my dev box", right?). This way you could continue to use a network-capable version of your server, would only need root/sudo to start proxy (at boot), wouldn't be relying on complex/potentially fragile capabilities.
Upvotes: 16
Reputation: 974
Since the OP is just development/testing, less than sleek solutions may be helpful:
setcap
can be used on a script's interpreter to grant capabilities to scripts. If setcaps on the global interpreter binary is not acceptable, make a local copy of the binary (any user can) and get root to setcap on this copy. Python2 (at least) works properly with a local copy of the interpreter in your script development tree. No suid is needed so the root user can control to what capabilities users have access.
If you need to track system-wide updates to the interpreter, use a shell script like the following to run your script:
#!/bin/sh
#
# Watch for updates to the Python2 interpreter
PRG=python_net_raw
PRG_ORIG=/usr/bin/python2.7
cmp $PRG_ORIG $PRG || {
echo ""
echo "***** $PRG_ORIG has been updated *****"
echo "Run the following commands to refresh $PRG:"
echo ""
echo " $ cp $PRG_ORIG $PRG"
echo " # setcap cap_net_raw+ep $PRG"
echo ""
exit
}
./$PRG $*
Upvotes: 1
Reputation: 1068
There is a worked example of doing this with a file capable shared library linked to an unprivileged application on the libcap website. It was recently mentioned in an answer to a question about adding capabilities to shared libraries.
Upvotes: 0
Reputation: 941
Bind port 8080 to 80 and open port 80:
sudo iptables -t nat -A OUTPUT -o lo -p tcp --dport 80 -j REDIRECT --to-port 8080
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
and then run program on port 8080 as a normal user.
you will then be able to access http://127.0.0.1
on port 80
Upvotes: 4
Reputation: 1267
Modern Linux supports /sbin/sysctl -w net.ipv4.ip_unprivileged_port_start=0
.
Upvotes: 36
Reputation: 734
For some reason no one mention about lowering sysctl net.ipv4.ip_unprivileged_port_start to the value you need. Example: We need to bind our app to 443 port.
sysctl net.ipv4.ip_unprivileged_port_start=443
Some may say, there is a potential security problem: unprivileged users now may bind to the other privileged ports (444-1024). But you can solve this problem easily with iptables, by blocking other ports:
iptables -I INPUT -p tcp --dport 444:1024 -j DROP
iptables -I INPUT -p udp --dport 444:1024 -j DROP
Comparison with other methods. This method:
Depending on the situation, I would choose between sysctl, CAP, authbind and iptables-redirect. And this is great that we have so many options.
Upvotes: 53
Reputation: 43456
I tried the iptables PREROUTING REDIRECT method. In older kernels it seems this type of rule wasn't supported for IPv6. But apparently it is now supported in ip6tables v1.4.18 and Linux kernel v3.8.
I also found that PREROUTING REDIRECT doesn't work for connections initiated within the machine. To work for conections from the local machine, add an OUTPUT rule also — see iptables port redirect not working for localhost. E.g. something like:
iptables -t nat -I OUTPUT -o lo -p tcp --dport 80 -j REDIRECT --to-port 8080
I also found that PREROUTING REDIRECT also affects forwarded packets. That is, if the machine is also forwarding packets between interfaces (e.g. if it's acting as a Wi-Fi access point connected to an Ethernet network), then the iptables rule will also catch connected clients' connections to Internet destinations, and redirect them to the machine. That's not what I wanted—I only wanted to redirect connections that were directed to the machine itself. I found I can make it only affect packets addressed to the box, by adding -m addrtype --dst-type LOCAL
. E.g. something like:
iptables -A PREROUTING -t nat -p tcp --dport 80 -m addrtype --dst-type LOCAL -j REDIRECT --to-port 8080
One other possibility is to use TCP port forwarding. E.g. using socat
:
socat TCP4-LISTEN:www,reuseaddr,fork TCP4:localhost:8080
However one disadvantage with that method is, the application that is listening on port 8080 then doesn't know the source address of incoming connections (e.g. for logging or other identification purposes).
Upvotes: 2
Reputation: 3439
With systemd, you just need to slightly modify your service to accept preactivated sockets.
You can later use systemd socket activate.
No capabilities, iptables or other tricks are needed.
This is content of relevant systemd files from this example of simple python http server
File httpd-true.service
[Unit]
Description=Httpd true
[Service]
ExecStart=/usr/local/bin/httpd-true
User=subsonic
PrivateTmp=yes
File httpd-true.socket
[Unit]
Description=HTTPD true
[Socket]
ListenStream=80
[Install]
WantedBy=default.target
Upvotes: 12
Reputation: 2897
Answer at 2015/Sep:
ip6tables now supports IPV6 NAT: http://www.netfilter.org/projects/iptables/files/changes-iptables-1.4.17.txt
You will need kernel 3.7+
Proof:
[09:09:23] root@X:~ ip6tables -t nat -vnL
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 REDIRECT tcp eth0 * ::/0 ::/0 tcp dpt:80 redir ports 8080
0 0 REDIRECT tcp eth0 * ::/0 ::/0 tcp dpt:443 redir ports 1443
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 6148 packets, 534K bytes)
pkts bytes target prot opt in out source destination
Chain POSTROUTING (policy ACCEPT 6148 packets, 534K bytes)
pkts bytes target prot opt in out source destination
Upvotes: 0
Reputation: 4057
Port redirect made the most sense for us, but we ran into an issue where our application would resolve a url locally that also needed to be re-routed; (that means you shindig).
This will also allow you to be redirected when accessing the url on the local machine.
iptables -A PREROUTING -t nat -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -A OUTPUT -t nat -p tcp --dport 80 -j REDIRECT --to-port 8080
Upvotes: 10
Reputation: 395
Use the privbind utility: it allows an unprivileged application to bind to reserved ports.
Upvotes: 3
Reputation: 308209
Linux supports capabilities to support more fine-grained permissions than just "this application is run as root". One of those capabilities is CAP_NET_BIND_SERVICE
which is about binding to a privileged port (<1024).
Unfortunately I don't know how to exploit that to run an application as non-root while still giving it CAP_NET_BIND_SERVICE
(probably using setcap
, but there's bound to be an existing solution for this).
Upvotes: 15
Reputation: 1711
TLDR: For "the answer" (as I see it), jump down to the >>TLDR<< part in this answer.
OK, I've figured it out (for real this time), the answer to this question, and this answer of mine is also a way of apologizing for promoting another answer (both here and on twitter) that I thought was "the best", but after trying it, discovered that I was mistaken about that. Learn from my mistake kids: don't promote something until you've actually tried it yourself!
Again, I reviewed all the answers here. I've tried some of them (and chose not to try others because I simply didn't like the solutions). I thought that the solution was to use systemd
with its Capabilities=
and CapabilitiesBindingSet=
settings. After wrestling with this for some time, I discovered that this is not the solution because:
Capabilities are intended to restrict root processes!
As the OP wisely stated, it is always best to avoid that (for all your daemons if possible!).
You cannot use the Capabilities related options with User=
and Group=
in systemd
unit files, because capabilities are ALWAYS reset when execev
(or whatever the function is) is called. In other words, when systemd
forks and drops its perms, the capabilities are reset. There is no way around this, and all that binding logic in the kernel is basic around uid=0, not capabilities. This means that it is unlikely that Capabilities will ever be the right answer to this question (at least any time soon). Incidentally, setcap
, as others have mentioned, is not a solution. It didn't work for me, it doesn't work nicely with scripts, and those are reset anyways whenever the file changes.
In my meager defense, I did state (in the comment I've now deleted), that James' iptables suggestion (which the OP also mentions), was the "2nd best solution". :-P
>>TLDR<<
The solution is to combine systemd
with on-the-fly iptables
commands, like this (taken from DNSChain):
[Unit]
Description=dnschain
After=network.target
Wants=namecoin.service
[Service]
ExecStart=/usr/local/bin/dnschain
Environment=DNSCHAIN_SYSD_VER=0.0.1
PermissionsStartOnly=true
ExecStartPre=/sbin/sysctl -w net.ipv4.ip_forward=1
ExecStartPre=-/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
ExecStartPre=-/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
ExecStartPre=/sbin/iptables -A INPUT -p udp --dport 5333 -j ACCEPT
ExecStartPre=/sbin/iptables -t nat -A PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
ExecStopPost=/sbin/iptables -D INPUT -p udp --dport 5333 -j ACCEPT
ExecStopPost=/sbin/iptables -t nat -D PREROUTING -p udp --dport 53 -j REDIRECT --to-ports 5333
User=dns
Group=dns
Restart=always
RestartSec=5
WorkingDirectory=/home/dns
PrivateTmp=true
NoNewPrivileges=true
ReadOnlyDirectories=/etc
# Unfortunately, capabilities are basically worthless because they're designed to restrict root daemons. Instead, we use iptables to listen on privileged ports.
# Capabilities=cap_net_bind_service+pei
# SecureBits=keep-caps
[Install]
WantedBy=multi-user.target
Here we accomplish the following:
iptables
systemd
cleans up the firewall rules for us, making sure to remove them when the daemon isn't running.systemd
claims to), supposedly even if the daemon is compromised and sets uid=0
.iptables
is still, unfortunately, quite an ugly and difficult-to-use utility. If the daemon is listening on eth0:0
instead of eth0
, for example, the commands are slightly different.
Upvotes: 16
Reputation: 25789
At startup:
iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
Then you can bind to the port you forward to.
Upvotes: 7
Reputation: 5305
systemd is a sysvinit replacement which has an option to launch a daemon with specific capabilities. Options Capabilities=, CapabilityBoundingSet= in systemd.exec(5) manpage.
Upvotes: 13
Reputation: 3880
You can do a port redirect. This is what I do for a Silverlight policy server running on a Linux box
iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 943 -j REDIRECT --to-port 1300
Upvotes: 49
Reputation: 938
My "standard workaround" uses socat as the user-space redirector:
socat tcp6-listen:80,fork tcp6:8080
Beware that this won't scale, forking is expensive but it's the way socat works.
Upvotes: 16
Reputation: 182822
The standard way is to make them "setuid" so that they start up as root, and then they throw away that root privilege as soon as they've bound to the port but before they start accepting connections to it. You can see good examples of that in the source code for Apache and INN. I'm told that Lighttpd is another good example.
Another example is Postfix, which uses multiple daemons that communicate through pipes, and only one or two of them (which do very little except accept or emit bytes) run as root and the rest run at a lower privilege.
Upvotes: 34