This site uses cookies to deliver our services and to show you relevant ads and job listings.
By using our site, you acknowledge that you have read and understand our Cookie Policy, Privacy Policy, and our Terms of Service.
Your use of Stack Overflow’s Products and Services, including the Stack Overflow Network, is subject to these policies and terms.

Join us in building a kind, collaborative learning community via our updated
Code of Conduct.

Because in this instance, I was using IPv6, which was why some of the "usual" workarounds (authbind and iptables REDIRECT) didn't work for me.
– Jason CreightonMar 30 '10 at 2:51

On CentOS 5, ip6tables v1.3.5 doesn't support the new NAT table. Is there an any solution using ip6tables at all? I'd prefer to avoid additional software as mentioned in several answers below.
– Stefan LasiewskiOct 24 '14 at 1:15

22 Answers
22

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:

You will need at least a 2.6.24 kernel

This won't work if your file is a script. (ie, uses a #! 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.

Linux will disable 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:

capabilities(7) man page. Read this long and hard if you're going to use capabilities in a production environment. There are some really tricky details of how capabilities are inherited across exec() calls that are detailed here.

Partial workaround for scripts: create a copy of the interpreter (e.g. bash), give it the capablities but restrict access to the copy to those users who need it. Of course, those users must be trusted, but they could change the script anyway.
– Erich KitzmuellerNov 19 '09 at 12:08

on suse SLES 11.1 I had to add the kernel param file_caps=1 to grub menu.lst for this to work.
– ShayNov 10 '11 at 13:14

1

Yes @C.Ross since it would have to be applied to /usr/bin/java and then would open the capability to any java app running on the system. Too bad capabilities cannot also be set per-user.
– joeytwiddleAug 15 '13 at 11:02

4

Does the setcap setting persist across reboots; if not is there a standard place to put this rule so that is it run during system startup? Is /etc/security/capability.conf on Debian/Ubuntu any help?
– joeytwiddleAug 15 '13 at 11:06

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.

Interestingly, this doesn't work under recent versions of Linux (maybe just Ubuntu) without CAP_SETUID set. So if you need setuid you're going to have to set this capability anyway.
– MattFeb 19 '13 at 4:12

Dropping root privs is the right way to do this. Though it's not necessary to set setuid if you have init/upstart/systemd start the service.
– Michael HamptonFeb 9 '14 at 16:34

2

This is difficult if the program is written in an interpreted language or bytecode interpreter such as C# (Mono), Java, Python. (Apparently Perl has done it via binfmt_misc and its 'C' flag; I'm not sure about others.)
– Craig McQueenMay 14 '15 at 23:15

@joeytwiddle I think it's in the script that runs (starts) your server, but I might be wrong. I'd also like to know :P
– Camilo MartinJul 9 '14 at 22:06

@CamiloMartin Looking again, the Debian documentation which was linked from the question recommends placing it in your firewall script, or creating one at /etc/network/if-up.d/firewall.
– joeytwiddleJul 10 '14 at 8:08

My personal favorite. Very easy to turn on and off and requires no system-wide changes. Great idea!
– Dan PassaroSep 22 '16 at 13:00

This is amazing. By far the simplest answer.
– michaelsnowdenMar 29 '17 at 9:47

1

I would expect this to be moderately expensive and not a good idea in any performance bound situation, but I can't be sure without testing it. SSH encryption costs a nonzero amount.
– lahwranJul 31 at 20:25

1

This could be done with netcat with no ssh overhead: sudo nc -l 80 | nc localhost 3000
– BugsterAug 5 at 5:56

And it actually does NOT work. Caps are not preserved over the user ID switch. It will work if you want to run as root, but with all capabilities dropped.
– CyberaxDec 18 '14 at 14:04

If I try that, I get "cannot execute binary file". In fact, I can't get capsh to do anything other than "cannot execute binary file" when using the -- or == options. I wonder what I'm missing.
– Craig McQueenMay 14 '15 at 1:03

@CraigMcQueen, everything after -- is passed on to /bin/bash, so you may want to try with -c 'your command'. Alas, I seem to be experiencing the same problem as @Cyberax, because I get "permission denied" when trying to bind.
– AmirDec 26 '15 at 21:03

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:

your daemon needs to talk on stdin/stdout (if you don't control the daemon -- if you don't have the source -- then this is perhaps a showstopper, although some services may have an inetd-compatibility flag)

a new daemon process is forked for every connection

it's one extra link in the chain

Pros:

available on any old UNIX

once your sysadmin has set up the config, you're good to go about your development (when you re-build your daemon, might you lose setcap capabilities? And then you'll have to go back to your admin "please sir...")

daemon doesn't have to worry about that networking stuff, just has to talk on stdin/stdout

can configure to execute your daemon as a non-root user, as requested

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.

Good suggestion. For some reason I didn't even think of using inetd. Except that the service in question is UDP-based, so it's slightly more complicated than TCP services. I have a solution working right now with setcap, but I'll have to give this some thought.
– Jason CreightonJan 6 '09 at 3:06

A Very Bad Idea for a multitude of reasons.
– Adam LassekJan 5 '09 at 22:25

14

This is bad idea. But would actually work. And sure made me laugh.
– Oto BrglezDec 10 '12 at 19:18

12

Of course it's a bad idea. That's why I said last resort. The point of open source is if it doesn't work the way you want you can change it.
– JoshuaDec 10 '12 at 22:21

10

I'm not sure why you were downvoted. Malware writers don't care what port they listen on, and they are more than happy to open a port greater than 1024. It seems to me that all ports should require privileges, or no ports should require privileges. And requiring root to open a port below 1024 just means you have a high risk application running as root. That seems like a really dumb idea to me. Perhaps I'm missing something here...
– jwwFeb 2 '14 at 9:20

6

Back in the day, port < 1024 was used in UNIX to UNIX protocols to prove the code running on the other end was running as root. This functioned reasonably well for a set of UNIX servers with common security.
– JoshuaFeb 2 '14 at 16:22

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).

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

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!

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):

Nobody should be using old-style aliases like eth0:0 anymore unless they have a really ancient Linux distribution. They have been deprecated for years and will eventually go away.
– Michael HamptonFeb 9 '14 at 16:32

1

I think you mean OpenVZ. (SolusVM is a control panel.) And yes, OpenVZ does a lot of things wrong, networking being just one of them.
– Michael HamptonFeb 9 '14 at 17:40

Still not OpenVZ... At least I'm not using it, nor is my VPS.
– Greg SlepakFeb 9 '14 at 18:29

1

Thank your for your concise explanation of why I couldn't get my prototype service running with capabilities and a non-root user. I was convinced I was doing something wrong, when it turns out that the capabilities functionality is useless for me!
– Anthony GiorgioJun 24 '14 at 2:45

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

Obviously, that then means any Java program can bind system ports.
Dito 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:

Any reason for the downvote ? Root-hater, or did the kernel-compile instructions not work ?
– Stefan SteigerJun 25 '15 at 14:46

2

modifying the kernel makes future updates much more painful. i wouldn't do this, because i know i would be too lazy to keep updating my kernel regularly. it's nice that it's possible in theory, but it's not a viable option in a lot of circumstances.
– kritzikratziJul 6 '15 at 14:21

Maybe a more secure way to do it is to change the chmod 777 /etc/authbind/byport/80 by chmod 544 /etc/authbind/byport/23 than chown login:group /etc/authbind/byport/23
– Jérôme BJun 14 at 12:29

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.

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:

from some point is (IMO) even more secure than setting CAP_NET_BIND_SERVICE/setuid, since an application doesn't setuid at all, even partly (capabilities actually are).
For example, to catch a coredump of capability-enabled application you will need to change sysctl fs.suid_dumpable (which leads to another potential security problems)
Also, when CAP/suid is set, /proc/PID directory is owned by root, so your non-root user will not have full information/control of running process, for example, user will not be able (in common case) to determine which connections belong to application via /proc/PID/fd/ (netstat -aptn | grep PID).

has security disadvantage: while your app (or any app that uses ports 443-1024) is down for some reason, another app could take the port. But this problem could also be applied to CAP/suid (in case you set it on interpreter, e.g. java/nodejs) and iptables-redirect. Use systemd-socket method to exclude this problem. Use authbind method to only allow special user binding.

doesn't require setting CAP/suid every time you deploy new version of application.

Thanks for the great answer! It appears that this functionality first appeared in Linux 4.11 in April 2017, so it wasn't around in 2009 when I first asked this question. I also did a quick test, and it seems to also work for IPV6, even though "ipv4" is in the sysctl name.
– Jason CreightonJul 26 at 1:34

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:

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:

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:

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).