12

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

$ ssh ubuntu@example.org -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.

  • What filesystem are you using? Not all filesystems support setcap. – Gilles 'SO- stop being evil' Jun 24 '12 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 '12 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 '12 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 '12 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 '12 at 12:14
15

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

| improve this answer | |
  • Oh I wasn't expecting that, this most certainly would answer my question. – Kit Sunde Jul 30 '12 at 15:29
5

This is a great use case for socat.

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

ssh ubuntu@example.org -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 ubuntu@example.org -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.)

| improve this answer | |
  • brilliant! love it! – Dirk Hoffmann Apr 10 at 17:09
  • 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" – Dirk Hoffmann Apr 10 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 17 hours ago
3

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

| improve this answer | |
1

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

sudo ssh -L 80:127.0.0.1:80 myuser@myhost.com

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

| improve this answer | |
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.

| improve this answer | |
0

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      -
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.