Binding to privileged ports

A common misfeature found on UN*X operating systems is the restriction that only root can bind to ports below 1024. Many a dollar has been wasted on workarounds and -often- the results are security holes.

Both FreeBSD and Solaris have elegant configuration options to turn this feature off. On FreeBSD:

	$ sysctl net.inet.ip.portrange.reservedhigh=0
      
the above is best added to your /etc/sysctl.conf

Similarly on Solaris we can just configure away this misfeature. Assuming we want to run Yaws/SSL under a non-root user "erlang" on ports 80/443.

On Solaris we can do that easily by granting the specific right to bind privileged ports <1024 (and only that) to "erlang" using:

$ /usr/sbin/usermod -K defaultpriv=basic,net_privaddr erlang
      

And check the we get what we want through:

$ grep erlang /etc/user_attr
erlang::::type=normal;defaultpriv=basic,net_privaddr

      

On Linux, kernels later than 2.6.24, it's possible to do:

$ setcap 'cap_net_bind_service=+ep' /usr/lib/erlang/erts-5.7.4/bin/beam
      

The above command grants the capability of binding privileged ports to beam. Note, you have to grant the priviliges to the actual exectuable you are using.

There are a couple of other options on Linux. One is to use an auxiliary program like authbind https://packages.debian.org/stable/authbind or privbind https://sourceforge.net/projects/privbind/

These programs are run by root. Yaws writes its temporary JIT compiled files in $HOME/.yaws and this doesn't work that well with authbind/privbind. A non root user will try to write in /root/.yaws. The solution to this is to set the environment variable YAWSHOME. Yaws will then consider that to be HOME rather that $HOME.

To start yaws under e.g privbind we can do:

$ sudo YAWSHOME=/tmp/abc privbind -u klacke /home/klacke/bin/yaws \
    -c /home/klacke/yaws.conf -i

      

The above command starts yaws as user klacke and bind to ports below 1024

The authbind program is another option: Here is an example:

touch /etc/authbind/byport/80
touch /etc/authbind/byport/443
chmod 500 /etc/authbind/byport/80
chmod 500 /etc/authbind/byport/443
chown bob /etc/authbind/byport/80
chown bob /etc/authbind/byport/443
YAWSHOME=/home/bob authbind yaws -c /home/bob/yaws.conf -i
      

Here is a description on how to do this on MacOs X. It's not exactly the same, since we're still binding to non privileged ports. However, edit /etc/sysctl and add:

net.inet.ip.forwarding=1

Then with ipfw as the firewall (turn off the gui firewall in system preferences and manage own rules) use the ipfw rules like these - for testing on your own box use something like:

ipfw add fwd 127.0.0.1,8080 tcp from any to 127.0.0.1 dst-port 80 in
ipfw add fwd 127.0.0.1,8443 tcp from any to 127.0.0.1 dst-port 443 in

set up yaws to use 8080 and 8443 in yaws.conf and then run as some non root user. When you browse to http://127.0.0.1 or https://127.0.0.1 you will see your pages that are actually on 8080 and 8443 internally but will be forwarded via ipfw forwarding.

Yet another (more complicated way) for linux users is to hack the kernel. Here is a patch I did for some version of the 2.6 series kernels .. you get the idea.

[root@lax]ipv4 > diff -c af_inet.c*
*** af_inet.c   Wed Feb 23 23:31:35 2005
--- af_inet.c~  Thu Feb 17 18:13:13 2005
***************
*** 423,434 ****

        snum = ntohs(addr->sin_port);
        err = -EACCES;
- #if 0
-       /* removed by klacke */
        if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
                goto out;
- #endif
-

        /*      We keep a pair of addresses. rcv_saddr is the one
         *      used by hash lookups, and saddr is used for transmit.
--- 423,430 ----

Valid XHTML 1.0!