20

I'd like to accept connections briefly for development when I'm NATed, and so I'm attempting to do this:

$ ssh [email protected] -R 80:localhost:80

Which fails as I'm trying to bind a port that is to low:

Warning: remote port forwarding failed for listen port 80

So I've discovered that I can do setcap 'cap_net_bind_service=+ep' /my/application to allow it to listen to ports lower than 1024. So I've got this in my suders crontab:

@reboot setcap 'cap_net_bind_service=+ep' /usr/sbin/sshd

But it's still not letting me bind on port 80. What am I doing wrong? I'm just going to use nginx to proxy to 8080 or iptables or something instead, but I'm still curious why what I was trying to do didn't work.

11
  • What filesystem are you using? Not all filesystems support setcap. Jun 24, 2012 at 1:20
  • @Gilles I'm using ext4, it's the default Ubuntu image off AWS and I haven't done anything fancy with it.
    – Kit Sunde
    Jun 24, 2012 at 7:03
  • It sounds as if you are root on the box. Maybe I'm ignorant but shouldn't just executing the first line as root solve your problem?
    – Christian
    Jun 25, 2012 at 7:26
  • @Christian You can't bind on <1024 without sudo by design. Like how you cannot apt-get install foo without first doing sudo.
    – Kit Sunde
    Jun 25, 2012 at 8:23
  • @KitSunde Yeah, root is an un-Ubuntuic way of saying sudo. It's a user that has all the privileges that a normal user only gets when doing sudo. root is also sometimes called the superuser. So basically my question to you was: You seem to be able to do sudo on the box, why can't you in this case?
    – Christian
    Jun 25, 2012 at 12:14

6 Answers 6

24

As explained in @dwurf's accepted answer, ssh will only bind to ports less than 1024 for the root user.

This is a great use case for socat if you are not root.

First, do a remote forward to port 8080 (or any other allowed port) of the remote machine:

ssh [email protected] -R 8080:localhost:80

Then on the remote machine, map port 80 to port 8080:

sudo socat TCP-LISTEN:80,fork TCP:localhost:8080

Notes:

As suggested by Dirk Hoffman, these two commands can be combined into a single line:

ssh -t [email protected] -R 8080:localhost:80 sudo socat TCP-LISTEN:80,fork TCP:localhost:8080

(The -t is necessary in case you need an interactive terminal to enter your sudo password.)

5
  • 1
    brilliant! love it! Apr 10, 2020 at 17:09
  • 1
    also possible to execute both in one command like so: ssh -t -i ~/.ssh/id_rsa -R 0.0.0.0:8080:0.0.0.0:8080 user@remoteMachine -- "sudo socat TCP-LISTEN:80,fork TCP:localhost:8080" Apr 10, 2020 at 17:38
  • Using socat command, I get 2020/08/14 19:25:31 socat[386] E bind(5, {AF=2 0.0.0.0:80}, 16): Address already in use and docker container says in ports0.0.0.0:8080->8080/tcp, and I didn't get any error while running your commands, but still on my laptop's browser(https://localhost:80/), it still can access the packets. Running this container on server. Any suggestion,what is going wrong?
    – Anu
    Aug 15, 2020 at 2:33
  • Hi @Anu, my guess is that another service (e.g. Apache or nginx) is already using port 80. I would check what program is using the port and, assuming it's not essential, shut it down.
    – Ben Mares
    Aug 16, 2020 at 14:58
  • ssh -N -f -L localhost:8080:localhost:8080 remote_user@remote_host. This worked for me
    – Anu
    Aug 17, 2020 at 23:26
20

OpenSSH will flat-out refuse to bind to privileged ports unless the user id of the logged in user is 0 (root). The relevant lines of code are:

if (!options.allow_tcp_forwarding ||
    no_port_forwarding_flag ||
    (!want_reply && listen_port == 0) ||
    (listen_port != 0 && listen_port < IPPORT_RESERVED &&
    pw->pw_uid != 0)) {
        success = 0;
        packet_send_debug("Server has disabled port forwarding.");

Source: http://www.openssh.com/cgi-bin/cvsweb/src/usr.bin/ssh/serverloop.c?annotate=1.162 lines 1092-1098

If you're curious, pw is of type struct passwd * and on linux is defined in /usr/include/pwd.h

2
  • Oh I wasn't expecting that, this most certainly would answer my question.
    – Kit Sunde
    Jul 30, 2012 at 15:29
  • IPPORT_RESERVED=1024
    – Ben Mares
    Oct 11, 2020 at 16:10
6

I meet the similar problem, so the solution I ended up is to add DNAT rule to the OUTPUT chain of nat table:

iptables -t nat -A OUTPUT -d 127.0.0.0/8 -p tcp --dport 80 \
-j DNAT --to-destination :8080

This rule effectively replaces the destination port 80 with 8080 for all locally generated tcp packets.

If you wish to allow any incoming connections to be forwarded as well then add one extra rule to the PREROUTING nat chain:

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

where 10.0.0.200 is the IP address of the interface that should be forwarding incoming connections to your web service

1

From the client end, you can run ssh via sudo:

sudo ssh -L 80:127.0.0.1:80 [email protected]

It works, although I'm admittedly not sure about the security implications for your local system.

1

Elaborating on the solution proposal of Ben Mares above,

below is a one liner which:

opens two remote port forwards:
1. remote port 8888 to local port 80
2. remote port 8443 to local port 443

on the remote machine socat connects anything
1. arriving on port  80 to be streamed to port 8888
   which is then tunneled to local host port  80
2. arriving on port 443 to be streamed to port 8443
   which is then tunneled to local host port 443

ssh -t -i ~/.ssh/id_rsa \
    -R 0.0.0.0:8888:0.0.0.0:80 \
    -R 0.0.0.0:8443:0.0.0.0:443 \
    remoteUser@remoteMachine \
    -- "(sudo socat TCP-LISTEN:80,fork TCP:localhost:8888) & \
        sudo socat TCP-LISTEN:443,fork TCP:localhost:8443"

so, if you execute the command, you have to give your root password of the remote machine (to be able to list on ports 80/443) and then (as long as the tunnel is established) anything that arrives on port 80 or 443 of the remote host, will be tunneled to your local machine ports 80 or 443 respectively...

remember!!

for being able to bind on all network interfaces (0.0.0.0) you have to edit your remote machines /etc/ssh/sshd_config and set GatewayPorts clientspecified or GatewayPorts yes

you can check this on the remote machine with netstat -tlpn | grep -E '8888|8443' which should show:

tcp        0      0 0.0.0.0:8888            0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:8443            0.0.0.0:*               LISTEN      -

and not

tcp        0      0 127.0.0.1:8888          0.0.0.0:*               LISTEN      -
tcp        0      0 127.0.0.1:8443          0.0.0.0:*               LISTEN      -
7
  • Hi Dirk, I just saw your answer. If I correctly understand your command, then incoming external traffic arriving at the remote host will be tunneled on not only for ports 80 and 443, but also for 8888 and 8443. I would guess that in most cases those additional ports are not desired. And the GatewayPorts would only apply to 8888/8443, right?
    – Ben Mares
    Aug 16, 2020 at 15:18
  • actually the ssh remote port forwards are from remote ports 8888+8443 to your local machine ports 80+443. the socat redirects are from external ports 80+443 (of the remote host) to remote hosts ports 8888+8443. if you have remote ports 8888+8443 open for external incoming traffic is up to you... Aug 16, 2020 at 16:40
  • But assuming there is no firewall, then 8888 and 8443 are open for external incoming traffic on the remote machine, right? And the GatewayPorts setting is enabling the external incoming traffic, right? In other words, I believe that if instead you did -R 127.0.0.1:8888:0.0.0.0:80, then you wouldn't need to deal with GatewayPorts, and you wouldn't be exposing 8888. Wouldn't that be better?
    – Ben Mares
    Aug 16, 2020 at 16:45
  • but you cannot tell socat to forward to a remote port, so you need to forward to a local port which then is tunneled to your local machine. Plus you cannot let ttwo apps (ssh AND socat listen on the same port) Aug 16, 2020 at 16:48
  • and the whole thread is about the fact THAT YOU ARE NOT ALLOWED to do a rwmote port forward an ports <1024 Aug 16, 2020 at 16:56
0

You could also just add your public key to the /root/.ssh/authorized_keys list. If you are paranoid then you will want to prohibit running of any commands as root like:

no-pty,no-agent-forwarding,no-X11-forwarding,command="/bin/noshell" ssh-rsa YOUR_PUBLIC_KEY 

Note that logging in as root may require adding "PermitRootLogin without-password" in /etc/ssh/sshd_config.

You must log in to answer this question.

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