404

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.

9
  • 4
    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, 2010 at 17:00
  • 6
    possible duplicate of Regular user using ports below 1024
    – Roman
    Jul 7, 2013 at 13:19
  • 7
    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. Jul 7, 2013 at 17:38
  • 5
    "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.
    – user145545
    Feb 2, 2014 at 9: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. Aug 22, 2015 at 5:13

20 Answers 20

427

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
19
  • 21
    @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, 2008 at 14:45
  • 4
    Note that this doesn't work with IPv6. Apr 2, 2012 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 2, 2012 at 18:31
  • 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, 2014 at 8:16
  • 4
    @Coderji: check the "save" section in the community documentation: help.ubuntu.com/community/IptablesHowTo
    – Sunny
    Sep 18, 2014 at 12:54
89

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
6
  • 1
    this works for nodejs: setcap 'cap_net_bind_service=+ep' /usr/bin/nodejs
    – JasonS
    Jun 4, 2015 at 22:20
  • 6
    This. I wonder why this answer hasn't more upvotes. Much easier than the iptables option imho.
    – Dominik R
    Jan 4, 2016 at 1:54
  • 8
    this is the correct and most efficient answer, all the other answers causes a performance hit or just iffy / insecure.
    – OneOfOne
    Jun 1, 2016 at 12:33
  • Perfeft. Good to know: You need to start that program after the setcap line (will not affect running programs). Dec 7, 2019 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, bugs.java.com/bugdatabase/view_bug.do?bug_id=7157699
    – Deniz
    Oct 6, 2020 at 22:11
89

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 …
8
  • 5
    If the server is Tomcat you can use authbind automatically by setting AUTHBIND=yes in /etc/default/tomcat6 Apr 2, 2012 at 12:16
  • I couldn't get this to work on Ubuntu server with the default Java package...
    – Ashley
    Jun 20, 2012 at 17:17
  • 13
    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, 2015 at 20:57
  • 9
    Oh nonononononono, don't never ever chmod 777 anything! Sep 8, 2015 at 11:45
  • 3
    @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 5, 2020 at 0:46
55

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;
        }
}
2
47

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
12
  • 12
    That is the same as running as root, unless the app knows how to drop priviledges.
    – CesarB
    Nov 10, 2008 at 14:48
  • 20
    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, 2008 at 14:49
  • 10
    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, 2008 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, 2008 at 14:57
  • 1
    lol, love how the accepted answer has a -15
    – Evan Teran
    Dec 17, 2008 at 18:53
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

2
  • 2
    This is good, but why not just bind the daemon to localhost:8080 instead of 0.0.0.0:8080 ? I want to do that, but I need the iptables for that.
    – Amala
    Dec 15, 2011 at 17:20
  • It works, really cool.
    – xtian
    Oct 8, 2015 at 20:26
34

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.

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

1
  • 1
    they do work, I use CAP_NET_RAW to run tools like tcpdump as a normal user.
    – Justin
    Feb 15, 2010 at 0:32
13

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
7

You can use redir program:

sudo redir --lport=80 --laddr=192.168.0.101 --cport 9990 --caddr=127.0.0.1
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
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)

1
  • 8
    This has the same issue -- running a service as root is a bad practice. Sep 9, 2009 at 18:44
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.

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.

3

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.

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

1
  • 2
    The drawback of this solution is the loss of the incoming IP in the Tomcat logs. Apr 2, 2012 at 12:22
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.

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. Apr 2, 2012 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, 2020 at 7: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.

1
  • 1
    Details at 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, 2020 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.

3
  • 1
    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 1, 2015 at 9:35
  • 1
    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 unix.stackexchange.com/a/16670/55508 Mar 2, 2016 at 16:11
  • 2
    Solved the previous error by adding <javahome>/lib/amd64/jli/ to ld.so.conf and running sudo ldconfig Mar 2, 2016 at 16:25

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .