Allow non-root process to bind to port 80 and 443?
I'm not sure what the other answers and comments here are referring to. This is possible rather easily. There are two options, both which allow access to low-numbered ports without having to elevate the process to root:
Option 1: Use CAP_NET_BIND_SERVICE
to grant low-numbered port access to a process:
With this you can grant permanent access to a specific binary to bind to low-numbered ports via the setcap
command:
sudo setcap CAP_NET_BIND_SERVICE=+eip /path/to/binary
For more details on the e/i/p part, see cap_from_text
.
After doing this, /path/to/binary
will be able to bind to low-numbered ports. Note that you must use setcap
on the binary itself rather than a symlink.
Option 2: Use authbind
to grant one-time access, with finer user/group/port control:
The authbind
(man page) tool exists precisely for this.
Install
authbind
using your favorite package manager.Configure it to grant access to the relevant ports, e.g. to allow 80 and 443 from 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
Now execute your command via
authbind
(optionally specifying--deep
or other arguments, see the man page):authbind --deep /path/to/binary command line args
E.g.
authbind --deep java -jar SomeServer.jar
There are upsides and downsides to both of the above. Option 1 grants trust to the binary but provides no control over per-port access. Option 2 grants trust to the user/group and provides control over per-port access but older versions supported only IPv4 (since I originally wrote this, newer versions with IPv6 support were released).
Dale Hagglund is spot on. So I'm just going to say the same thing but in a different way, with some specifics and examples. ☺
The right thing to do in the Unix and Linux worlds is:
- to have a small, simple, easily auditable, program that runs as the superuser and binds the listening socket;
- to have another small, simple, easily auditable, program that drops privileges, spawned by the first program;
- to have the meat of the service, in a separate third program, run under a non-superuser account and chain loaded by the second program, expecting to simply inherit an open file descriptor for the socket.
You have the wrong idea of where the high risk is. The high risk is in reading from the network and acting upon what is read not in the simple acts of opening a socket, binding it to a port, and calling listen()
. It's the part of a service that does the actual communication that is the high risk. The parts that open, bind()
, and listen()
, and even (to an extent) the part that accepts()
, are not the high risk and can be run under the aegis of the superuser. They don't use and act upon (with the exception of source IP addresses in the accept()
case) data that are under the control of untrusted strangers over the network.
There are many ways of doing this.
inetd
As Dale Hagglund says, the old "network superserver" inetd
does this. The account under which the service process is run is one of the columns in inetd.conf
. It doesn't separate the listening part and the dropping privileges part into two separate programs, small and easily auditable, but it does separate off the main service code into a separate program, exec()
ed in a service process that it spawns with an open file descriptor for the socket.
The difficulty of auditing isn't that much of a problem, as one only has to audit the one program. inetd
's major problem is not auditing so much but is rather that it doesn't provide simple fine-grained runtime service control, compared to more recent tools.
UCSPI-TCP and daemontools
Daniel J. Bernstein's UCSPI-TCP and daemontools packages were designed to do this in conjunction. One can alternatively use Bruce Guenter's largely equivalent daemontools-encore toolset.
The program to open the socket file descriptor and bind to the privileged local port is tcpserver
, from UCSPI-TCP. It does both the listen()
and the accept()
.
tcpserver
then spawns either a service program that drops root privileges itself (because the protocol being served involves starting out as the superuser and then "logging on", as is the case with, for example, an FTP or an SSH daemon) or setuidgid
which is a self-contained small and easily auditable program that solely drops privileges and then chain loads to the service program proper (no part of which thus ever runs with superuser privileges, as is the case with, say, qmail-smtpd
).
A service run
script would thus be for example (this one for dummyidentd for providing null IDENT service):
#!/bin/sh -e
exec 2>&1
exec \
tcpserver 0 113 \
setuidgid nobody \
dummyidentd.pl
nosh
My nosh package is designed to do this. It has a small setuidgid
utility, just like the others. One slight difference is that it's usable with systemd
-style "LISTEN_FDS" services as well as with UCSPI-TCP services, so the traditional tcpserver
program is replaced by two separate programs: tcp-socket-listen
and tcp-socket-accept
.
Again, single-purpose utilities spawn and chain load one another. One interesting quirk of the design is that one can drop superuser privileges after listen()
but before even accept()
. Here's a run
script for qmail-smtpd
that indeed does exactly that:
#!/bin/nosh
fdmove -c 2 1
clearenv --keep-path --keep-locale
envdir env/
softlimit -m 70000000
tcp-socket-listen --combine4and6 --backlog 2 ::0 smtp
setuidgid qmaild
sh -c 'exec \
tcp-socket-accept -v -l "${LOCAL:-0}" -c "${MAXSMTPD:-1}" \
ucspi-socket-rules-check \
qmail-smtpd \
'
The programs that run under the aegis of the superuser are the small service-agnostic chain-loading tools fdmove
, clearenv
, envdir
, softlimit
, tcp-socket-listen
, and setuidgid
. By the point that sh
is started, the socket is open and bound to the smtp
port, and the process no longer has superuser privileges.
s6, s6-networking, and execline
Laurent Bercot's s6 and s6-networking packages were designed to do this in conjunction. The commands are structurally very similar to those of daemontools
and UCSPI-TCP.
run
scripts would be much the same, except for the substitution of s6-tcpserver
for tcpserver
and s6-setuidgid
for setuidgid
. However, one might also choose to make use of M. Bercot's execline toolset at the same time.
Here's an example of an FTP service, lightly modified from Wayne Marshall's original, that uses execline, s6, s6-networking, and the FTP server program from publicfile:
#!/command/execlineb -PW
multisubstitute {
define CONLIMIT 41
define FTP_ARCHIVE "/var/public/ftp"
}
fdmove -c 2 1
s6-envuidgid pubftp
s6-softlimit -o25 -d250000
s6-tcpserver -vDRH -l0 -b50 -c ${CONLIMIT} -B '220 Features: a p .' 0 21
ftpd ${FTP_ARCHIVE}
ipsvd
Gerrit Pape's ipsvd is another toolset that runs along the same lines as ucspi-tcp and s6-networking. The tools are chpst
and tcpsvd
this time, but they do the same thing, and the high risk code that does the reading, processing, and writing of things sent over the network by untrusted clients is still in a separate program.
Here's M. Pape's example of running fnord
in a run
script:
#!/bin/sh
exec 2>&1
cd /public/10.0.5.4
exec \
chpst -m300000 -Uwwwuser \
tcpsvd -v 10.0.5.4 443 sslio -v -unobody -//etc/fnord/jail -C./cert.pem \
fnord
systemd
systemd
, the new service supervision and init system that can be found in some Linux distributions, is intended to do what inetd
can do. However, it doesn't use a suite of small self-contained programs. One has to audit systemd
in its entirety, unfortunately.
With systemd
one creates configuration files to define a socket that systemd
listens on, and a service that systemd
starts. The service "unit" file has settings that allow one a great deal of control over the service process, including what user it runs as.
With that user set to be a non-superuser, systemd
does all of the work of opening the socket, binding it to a port, and calling listen()
(and, if required, accept()
) in process #1 as the superuser, and the service process that it spawns runs without superuser privileges.
I have a rather different approach. I wanted to use port 80 for a node.js server. I was unable to do it since Node.js was installed for a non-sudo user. I tried to use symlinks, but it didn't work for me.
Then I got to know that I can forward connections from one port to another port. So I started the server on port 3000 and set up a port forward from port 80 to port 3000.
This link provides the actual commands which can be used to do this. Here're the commands -
localhost/loopback
sudo iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 80 -j REDIRECT --to-ports 3000
external
sudo iptables -t nat -I PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 3000
I have used the second command and it worked for me. So I think this is a middle ground for not allowing user-process to access the lower ports directly, but giving them access using port-forwarding.