390

I have Googled about a solution for quite some time, but couldn't find an answer.

I am on Ubuntu Linux and want to run a server on port 80, but due to security mechanism of Ubuntu, I get the following error:

java.net.BindException: Permission denied:80

I think it should be simple enough to either disable this security mechanism so that port 80 is available to all users or to assign required privileges to the current user to access port 80.

Dave M
  • 4,494
  • 21
  • 30
  • 30
Deepak Mittal
  • 4,053
  • 3
  • 16
  • 7
  • 3
    What is the problem to run the server on another port that is unprivileged? You are thinking about something as harsh as disabling the security mechanism without providing at least a very serious reason for running a server on that port. Is the server hardcoded to bind to port 80? If so, throw it away. – Anonymous Feb 20 '10 at 17:00
  • Or a Python error message: `socket.error: [Errno 13] Permission denied` – Kazark Nov 15 '11 at 15:15
  • 6
    possible duplicate of [Regular user using ports below 1024](http://serverfault.com/questions/84360/regular-user-using-ports-below-1024) – Roman Jul 07 '13 at 13:19
  • 6
    You can't. Ports below 1024 are privileged and only root can open listening sockets on them. The appropriate thing to do is to drop permissions after opening it. – Falcon Momot Jul 07 '13 at 17:38
  • 4
    "Anonymous" - the users in the world have been trained to look for certain services at certain ports. In some instances, its been standardized. For example, HTTP on port 80 and HTTPS on port 443. Its kind of hard to change the users and standards of the world. –  Feb 02 '14 at 09:25
  • 1
    @FalconMomot If ports below 1024 are privileged, why doesn't Apache server require one to enter a password to get root privileges on port 80, and how do they do it that way? I think the questioner wants to know how to do things that way (whether it's technically being used as root or not). Feel free to tell me if I'm wrong, Deepak Mittal. – Brōtsyorfuzthrāx Aug 22 '15 at 05:13
  • 1
    You use a suid program that drops privileges after opening the port. It's difficult or impossible to do that in java. – Falcon Momot Aug 25 '15 at 00:23

20 Answers20

417

Short answer: you can't. Ports below 1024 can be opened only by root. As per comment - well, you can, using CAP_NET_BIND_SERVICE, but that approach, applied to java bin will make any java program to be run with this setting, which is undesirable, if not a security risk.

The long answer: you can redirect connections on port 80 to some other port you can open as normal user.

Run as root:

# iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080

As loopback devices (like localhost) do not use the prerouting rules, if you need to use localhost, etc., add this rule as well (thanks @Francesco):

# iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 80 -j REDIRECT --to-ports 8080

NOTE: The above solution is not well suited for multi-user systems, as any user can open port 8080 (or any other high port you decide to use), thus intercepting the traffic. (Credits to CesarB).

EDIT: as per comment question - to delete the above rule:

# iptables -t nat --line-numbers -n -L

This will output something like:

Chain PREROUTING (policy ACCEPT)
num  target     prot opt source               destination         
1    REDIRECT   tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:8080 redir ports 8088
2    REDIRECT   tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:80 redir ports 8080

The rule you are interested in is nr. 2, so to delete it:

# iptables -t nat -D PREROUTING 2
Sunny
  • 5,722
  • 3
  • 21
  • 24
  • I see others have posted faster than me. I'll leave my answer just for the iptables command line, but please, vote the other answers, not mine. – Sunny Nov 10 '08 at 14:42
  • 19
    @Sunny: upvotes are not for the fastest gun in the west, but for the best responses. Yours is the best so far (I only mentioned iptables; you actually provided the full command line). The only thing mine has yours doesn't is the caveat about other users also being able to bind to port 8080. – CesarB Nov 10 '08 at 14:45
  • CesarB: anyway, you have your +1. – Sunny Nov 10 '08 at 15:29
  • 3
    Note that this doesn't work with IPv6. – Emmanuel Bourg Apr 02 '12 at 12:12
  • 3
    Could anybody explain how I can remove this rule later when I want to? After running it, it works, but it's apparently not a "rule" because it doesn't show up when i do `sudo iptables --list`. I know what iptables is and does, but i've never really used it before this. – Encoderer Apr 02 '12 at 18:31
  • @Encoderer: added delete commands – Sunny Apr 02 '12 at 20:34
  • @Encoderer - you can view the redirect policies by running iptables --list -n -t nat – Louis Sayers Apr 28 '12 at 14:17
  • Doesn't this assume you have root access to the machine? So, if you have the privileges to add this rule, why wouldn't you just open the port on 80? – prateek61 Nov 27 '12 at 13:12
  • @prateek61 You can always ask an admin to add the rule, you don't have to have root access. Also, with that approach, your app don't have to run as root, which is bad idea to start with. – Sunny Nov 27 '12 at 16:00
  • This doesn't appear to work for localhost connections. I can't connect to port 80 from my local machine (localhost:80), but I can from a remote machine (:80). – gdw2 Jun 25 '13 at 16:21
  • @gdw2: see the last edit – Sunny Jul 07 '13 at 12:46
  • 4
    Thanks for your answer... whenever I reboot Ubuntu, this rule disappears and I have to run it again. Is there a way to save it forever? – Coderji Sep 18 '14 at 08:16
  • 4
    @Coderji: check the "save" section in the community documentation: https://help.ubuntu.com/community/IptablesHowTo – Sunny Sep 18 '14 at 12:54
  • As mentioned in other answers, it's now possible with capabilities. I checked that. My kernel is 4.4.32, but they say it's there since 2.6.24. – bohdan_trotsenko Dec 15 '16 at 06:42
  • @Petr Not really. That approach will make ANY java program to be able to use lower ports. Although, you are right that the language used "you can't" is not really accurate. – Sunny Sep 15 '17 at 17:45
  • I found it helpful (on Debian) to install the `iptables-persistent` package to make the iptables rule persistent across reboots. – Josh Hansen May 03 '18 at 17:30
  • `iptables v1.8.7 (nf_tables): unknown option "--dport"` – Janus Troelsen Mar 28 '22 at 21:36
88

Use authbind.

It even works with Java if you enable Java's IPv4-only stack. I use:

authbind --deep $JAVA_HOME/bin/java -Djava.net.preferIPv4Stack=true …
geocar
  • 2,307
  • 14
  • 10
  • 4
    If the server is Tomcat you can use authbind automatically by setting `AUTHBIND=yes` in /etc/default/tomcat6 – Emmanuel Bourg Apr 02 '12 at 12:16
  • I couldn't get this to work on Ubuntu server with the default Java package... – Ashley Jun 20 '12 at 17:17
  • Me neither, any known solution? – mark Aug 16 '12 at 10:10
  • @mark, Try creating a question and explain what you have tried. Authbind has been the default for Ubuntu/Debian Java applications for a long time. – geocar Aug 19 '12 at 11:19
  • Already did - http://serverfault.com/questions/418158/authbind-java-ubuntu-still-fails-why – mark Aug 19 '12 at 13:52
  • 12
    Note that you must configure `authbind` to actually allow this to happen. From the man page: *"/etc/authbind/byport/port is tested. If this file is accessible for execution to the calling user, according to access(2), then binding to the port is authorised."*, e.g. for port 80, `sudo touch /etc/authbind/byport/80 ; sudo chmod 777 /etc/authbind/byport/80`. The initial install of `authbind` does not generally have any pre-configured authorizations. – Jason C Mar 21 '15 at 20:57
  • 7
    Oh nonononononono, don't never ever chmod 777 anything! – Johannes Kohnen Sep 08 '15 at 11:45
  • 2
    @JasonC Rather than `777` the file, make the second command into two: `sudo chgrp 80s /etc/authbind/byport/80 ; sudo chmod 550 /etc/authbind/byport/80` where `80s` is either your own user's group, or one you create so other (system) users can bind to that port. There is no reason to allow a free-for-all on binding, nor to give them write access to that file. – Walf Aug 05 '20 at 00:46
86

If your system supports it you could maybe use capabilities. See man capabilities, the one you need would be CAP_NET_BIND_SERVICE.

On newer Debian/Ubuntu you can run:

sudo apt-get install libcap2-bin 
sudo setcap 'cap_net_bind_service=+ep' /path/to/program
  • 1
    this works for nodejs: setcap 'cap_net_bind_service=+ep' /usr/bin/nodejs – JasonS Jun 04 '15 at 22:20
  • 6
    This. I wonder why this answer hasn't more upvotes. Much easier than the iptables option imho. – Dominik R Jan 04 '16 at 01:54
  • 8
    this is the correct and most efficient answer, all the other answers causes a performance hit or just iffy / insecure. – OneOfOne Jun 01 '16 at 12:33
  • Perfeft. Good to know: You need to start that program after the setcap line (will not affect running programs). – Stefan Reich Dec 07 '19 at 17:01
  • after applying this solution java refused to run with errors "error while loading shared libraries: libjli.so: cannot open shared object file: No such file or directory". I have fixed it by applying the solution described here, https://bugs.java.com/bugdatabase/view_bug.do?bug_id=7157699 – Deniz Oct 06 '20 at 22:11
  • This helped me getting Gitea on my Raspberry Pi 4 running as pi user on port 80! Thank you! – Martin Majewski Jun 26 '21 at 21:24
51

I simply use Nginx in front. It can run on localhost too.

  • apt-get install nginx

.. or ..

  • pkg_add -r nginx

.. or what ever suits your OS.

All you need in nginx.conf, if running on localhost, is:

server {
        listen  80;
        server_name some.domain.org;
        location / {
            proxy_set_header    Host $host;
            proxy_set_header    X-Real-IP   $remote_addr;
            proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass  http://127.0.0.1:8081;
        }
}
Ladadadada
  • 25,847
  • 7
  • 57
  • 90
CosmicB
  • 511
  • 4
  • 2
48

Another solution is to make your app setuid so that it can bind with port 80. As root, do the following

chown root ./myapp
chmod +S ./myapp

Keep in mind that doing this, unless it's done absolutely right, will expose you to potential security holes, because your app will be talking to the network, and will be running with full root priviledges. If you take this solution, you should look at the source code for Apache or Lighttpd or something similar, where they use the root privileges to open up the port, but then immediately give up those privs and "become" a lower privileged user so that a hijacker can't take over your whole computer.

Update: As seen in this question, it appears that Linux kernels since 2.6.24 have a new capability that allow you to mark an executable (but not a script, of course) as having the "CAP_NET_BIND_SERVICE" capability. If you install the debian package "libcap2-bin", you can do that by issuing the command

setcap 'cap_net_bind_service=+ep' /path/to/program
Paul Tomblin
  • 5,217
  • 1
  • 27
  • 39
  • 7
    That is the same as running as root, unless the app knows how to drop priviledges. – CesarB Nov 10 '08 at 14:48
  • 17
    This is dangerous. This means that any request will run as root. It's for a reason that even apache starts as root to bind, and then drops the privileges to another user. – Sunny Nov 10 '08 at 14:49
  • Subverting the restrictions Unix has on binding to port 80 is dangerous, whether you do it with setuid or with iptables hackery. – Paul Tomblin Nov 10 '08 at 14:53
  • 9
    Paul: the iptables is not so dangerous, because even if the app is compromised, it will not expose the system on attacks, at least not with root privileges. Running the app as root is another story. – Sunny Nov 10 '08 at 14:55
  • 1
    The danger for iptables is only if this is a mult-user system, as CesarB said, as anybody can bind to 8080, and intercept the traffic. – Sunny Nov 10 '08 at 14:57
  • please report back to us to let us know how long it took your box to get rooted... – Alnitak Nov 12 '08 at 10:06
  • I know my limitations. I wouldn't attempt to do this without copying the code from Apache or Lighttpd. – Paul Tomblin Nov 12 '08 at 13:23
  • 1
    lol, love how the accepted answer has a -15 – Evan Teran Dec 17 '08 at 18:53
  • Yeah, I'm kind of amazed it was accepted too. – Paul Tomblin Dec 17 '08 at 20:34
  • Could there be a medal for that? – mmr Jul 15 '09 at 02:19
  • 1
    Considering that the error given implies Java, you would have to run the entire JVM setuid root which would grant every java app root access :S – Draemon Jul 23 '09 at 19:35
  • 1
    +1 for 'setcap', much safer than setuid root. – nevelis Dec 20 '13 at 21:33
38

Approach proposed by Sunny and CesarB:

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080

works fine but it has a small drawback -- it does not prevent user from connecting directly to port 8080 instead of 80.

Consider the following scenario when this can be a problem.

Let's say we have a server which accepts HTTP connections on port 8080 and HTTPS connections on port 8181.

We use iptables to establish the following redirections:

80  ---> 8080
443 ---> 8181

Now, let's suppose our server decides to redirect user from a HTTP page to a HTTPS page. Unless we carefully rewrite the response, it would redirect to https://host:8181/. At this point, we are screwed:

  • Some users would bookmark the https://host:8181/ URL and we would need to maintain this URL to avoid breaking their bookmarks.
  • Other users would not be able to connect because their proxy servers do not support non-standard SSL ports.

I use the following approach:

iptables -t mangle -A PREROUTING -p tcp --dport 80 -j MARK --set-mark 1
iptables -t mangle -A PREROUTING -p tcp --dport 443 -j MARK --set-mark 1
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8181
iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 8080 -m mark --mark 1 -j ACCEPT
iptables -I INPUT -m state --state NEW -m tcp -p tcp --dport 8181 -m mark --mark 1 -j ACCEPT

Combined with default REJECT rule on the INPUT chain this approach prevents users from connecting directly to ports 8080, 8181

Misha
  • 481
  • 4
  • 2
33

Traditionally on Unix, only root can bind to low ports (<1024).

The simplest way to work around this is to run your server on a high port (for instance, 8080) and use a simple iptables rule to forward the connections from port 80 to port 8080. Note that with this you lose the extra protection from the low ports; any user on your machine can bind to port 8080.

CesarB
  • 2,368
  • 15
  • 9
23

If your system supports it you could maybe use capabilities. See man capabilities, the one you need would be CAP_NET_BIND_SERVICE. No, I`ve never used them myself and I don't know if they really work :-)

WMR
  • 871
  • 5
  • 6
12

Use a reverse proxy (nginx, apache+mod_proxy) or a caching reverse proxy (Squid, Varnish) in front of your application servers!

With a reverse proxy you can achieve a lot of interesting things like:

  • Load-balancing
  • Restarting your application servers with users receiving a fancy error page
  • Speed up things with cache
  • Fine-grained settings that you normally do with a reverse-proxy and not with an application server
Giovanni Toraldo
  • 2,557
  • 18
  • 27
7

You can use redir program:

sudo redir --lport=80 --laddr=192.168.0.101 --cport 9990 --caddr=127.0.0.1
Scott Pack
  • 14,717
  • 10
  • 51
  • 83
user340994
  • 171
  • 1
  • 2
5

answer of sunny is correct, but you may face additional issues as loopback interface don't use the PREROUTING table,

so the iptables rules to add are two:

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080
iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 80 -j REDIRECT --to-ports 8080
Francesco
  • 51
  • 1
  • 1
4

Use sudo.

Configure sudo so that the regular user can run appropriate commands:

/etc/init.d/httpd start

Or

apachectl stop

Or

/usr/local/apache2/apachectl restart

Or

/myapp/myappbin start

(Or whatever other command/script you use to start/stop you particular webserver/app)

Not Now
  • 3,532
  • 17
  • 18
3

If you are trying to do this so that a user-run command can use port 80, then your only solutions are the iptables tricks or setting the executable setuid-to-root.

The way something like Apache does this (it binds to port 80, but is running as someone other than root) is to run as root, bind to the port, then change the ownership of the process to the non-privileged user after the port is set up. If the app you are writing can be run by root, you can make it change owner to the non-priv user after the ports are set up. But if this is just for an average user to run from the command line, then you'll have to use one of the other solutions.

rjray
  • 131
  • 1
  • 4
3

With Linux, you have some two other options:

Both extensions to the Linux kernel allow to grant access rights on a very fine grained level. This would allow you to grant this process to open port 80 but it wouldn't inherit any of the other root rights.

From what I've heard, grsecurity is much more simple to use but SELinux is more secure.

Aaron Digulla
  • 954
  • 1
  • 13
  • 24
2

One solution is to use iptables to perform PAT on packets for port 80. You can use this to route the packets to local port 8080, for example. Be sure and adjust the outgoing packets back to port 80.

In my experience the fine-grained permissions features of Linux are not compiled into standard kernels because of security issues.

2

When I have various web serving applications (python scripts, tomcat engines, ...) that I don't want to run as root I usually configure an apache web server in front of them. Apache listens to port 80, and tomcat listens to 8080.

In apache:s config:

ProxyPass /webapp http://localhost:8080/webapp
ProxyPassReverse /webapp http://localhost:8080/webapp

See the mod-proxy documentation for more info: http://httpd.apache.org/docs/2.2/mod/mod_proxy.html

2

I think the best solution is to sgid your app and as soon as it has its port bound it should drop the privileges by switching to another user.

Anxy Dicy
  • 21
  • 1
2

Some hostsystems are not allowing to use the NAT module, 'iptables' does not solve the problem in this case.

How about xinetd?

In my case (ubuntu 10.04)

# apt-get install xinetd
# touch /etc/xinetd.d/my_redirect
# vim /etc/xinetd.d/my_redirect

Paste the configuration:

service my_redirector_80
{
 disable = no
 socket_type = stream
 protocol = tcp
 user = root
 wait = no
 port = 80
 redirect = localhost 8080
 type = UNLISTED
}

Then:

# service xinetd restart

http://docs.codehaus.org/display/JETTY/port80 explains better.

Thomas
  • 121
  • 2
  • 1
    This is basically equivalent to putting a reverse proxy in front of the server, but it's rather inefficient compared to dedicated solutions like HAproxy, Pound or Varnish that understand the HTTP protocol and can add useful headers likes X-Forwarded-For. – Emmanuel Bourg Apr 02 '12 at 21:49
  • This is currently my preferred solution because it is much less work to get it running from scratch than forwarding with iptables. – h22 Apr 17 '20 at 07:43
0

As an aside, FreeBSD and Solaris (anyone remember that one?) let you do this (bind to low ports) without privilege escalation (ie, using programs to switch to root). Since you specified Linux, I'm just posting this as a notice to others that might find this question.

Daniel C. Sobral
  • 5,563
  • 5
  • 32
  • 48
  • 1
    Details at http://yaws.hyber.org/privbind.yaws A common misfeature found on UN*X … 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 … `FreeBSD$ sysctl net.inet.ip.portrange.reservedhigh=0 Solaris$ /usr/sbin/usermod -K defaultpriv=basic,net_privaddr erlang; grep erlang /etc/user_attr Linux$ setcap 'cap_net_bind_service=+ep' /usr/lib/erlang/erts-5.7.4/bin/beam Debian$ echo 'net.ipv4.ip_unprivileged_port_start=0' > /etc/sysctl.d/50-unprivileged-ports.conf && sysctl --system` – Devon Mar 14 '20 at 11:24
0

Necromancing.

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

/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, use iptables if you want to stay secure, compile the kernel if you want to be sure this restriction never bothers you again.

Quandary
  • 974
  • 4
  • 18
  • 34
  • Would somebody please explain the downvotes ? Kernel-compilation not working ? Or is it just because afterwards, you can't do a kernel-upgrade from the normal repositories anymore ? – Quandary Sep 01 '15 at 09:35
  • I tried this solution (`sudo setcap cap_net_bind_service=+ep /path-to-java/java`) but I always get `java: error while loading shared libraries: libjli.so: cannot open shared object file: No such file or directory` if java has caps set. see also the first part of http://unix.stackexchange.com/a/16670/55508 – Daniel Alder Mar 02 '16 at 16:11
  • 1
    Solved the previous error by adding `/lib/amd64/jli/` to `ld.so.conf` and running `sudo ldconfig` – Daniel Alder Mar 02 '16 at 16:25