Best way to allow ssh connection just for reverse port forwarding

So here's my scenario.


* I have a home server (HostB) which is completely within my control.
* I have an off-site machine that can potentially be physically accessed by other people I don't trust (HostA).

I want to do off-site backups (encrypted of course) via `duplicity` from HostB to HostA. Because HostA is behind firewall, it can't provide direct ssh access. So I'll have to do a reverse port forwarding to expose HostA:22. In order to reliably do the reverse port forwarding without password, I will add HostA's public key to HostB's authorized_keys file. Now that can potentially be bad, because the pub key could be stolen.

However, since the ssh login from HostA -> HostB is **only** to establish the port forwarding tunnel so HostB can access HostA:22, is there any good way I can restrict the HostA -> HostB ssh connection to **only** provide the tunnel and nothing else??

The most straightforward way I'm aware of is sshd's chroot jail support, via `ChrootDirectory` in `sshd.config`. But that feels still providing more functionality to the connection than what it needs. Or is this actually the correct direction and I just need to properly set up the chroot environment?

What about a FreeBSD jail for this purpose? Feels too heavy weight on the system to create a jail just for reverse port forwarding though. Any other approaches?

For establishing ssh tunnels, I always created a mostly non-priviledged tunnel user (without login shell), having a home directory, outside of the /home tree of the normal system users, and I choose to have it at /var/tunnel. The tunnel's home is totally locked down and holds only the .ssh directory with the necessary credentials, i.e. authorized_keys.

For example on HostB:
# pw useradd -n tunnel -c "SSH Tunnel User" -u 9999 -d /var/tunnel -s /usr/sbin/nologin
# mkdir -m 0500 -p /var/tunnel/.ssh
# chown -R tunnel:nogroup /var/tunnel

In your scenario, HostA may establish a ssh connection tunnel@hostB, but cannot do many harmful things. In order to reduce the risk even more, your backup script could rename the tunnel's ~/.ssh/authorized_keys after the backup is done and vice versa before the backup starts.
  • Thanks
Reactions: klu
I have similar situation, this is my setups:

1.Create a new user named forward_user on host B, after install the public key,make the home dir of forward_user immutable(use chflags -R schg ~), disable the forward_user password auth(use bsdconfig->login/group mng->edit login->choose the user->clear password).

2.I run a script to do the port forward on host A in crontab, the most important cmd in the script is this:
/usr/bin/lockf -t 1 -k -s /tmp/mkfwd_${dest_host}_${forward_listen_port}.lock /usr/bin/ssh -T -N -o "ConnectTimeout 30" -o "ExitOnForwardFailure yes" -o "ServerAliveInterval 30" -o "ServerAliveCountMax 2" -R :${forward_listen_port}:${local_host}:${local_port} -p 22 forward_user@${dest_host}
Note the -N and -T flags:
-N Do not execute a remote command. This is useful for just
forwarding ports.
-T Disable pseudo-terminal allocation.

3In the authorized_keys file insert no-pty in front of the public key,like this:
no-pty ssh-rsa AAAA.....

This runs great for years.
  • Thanks
Reactions: klu
I would look at the sshd_config(5) section on ForceCommand (used in a match block for your special user); you can force a command (/bin/false for example) and disable just about everything else for this user except port forwarding; you can use PermitOpen to restrict that to just what forwarding is needed. Go through all the other Permit* and Allow* sshd_config options in the Match block and lock down everything not required (they don't need a TTY, for example) for good measure.

If the user tries to connnect with anything other than "-N" specified (don't run a remote command, just forward ports) it will fail. If the user (now nefarious) connects and tries to forward some other port, it will fail.

You can achieve many of the same things in a user's ~/.ssh/authorized_keys file (see sshd(8) in the "AUTHORIZED KEYS FILE FORMAT" section) if you don't have administrative access to /etc/ssh/sshd_comfig) on hostB. I prefer the sshd_config route precisely because the user can't change it.
  • Thanks
Reactions: klu
Hi thanks guys for the ideas! It looks like the basic idea is to disable user login by not giving the tunneling user a shell, and use `-N` on the SSH client side to connect. Ideas that I gathered are:

* create a user with nologin shell
* set user home to /var/.. and make it readonly with `chflags -R schg <...>`
* do a ChrootDirectory and MATCH USER in sshd_config for extra safety
* use authorized_keys file to further restrict the public key (authorized_keys manpage [2])

I also found a similar discussion here [1].

P.S. I have everything summarized in a blog post here: SSH Reverse Port Forwarding With Untrusted Remote Host

It seems to me too much complicated and I don't understand all.

1) Can't you really open the firewall on Host A on port 22, or don't you want for some paranoid reasons ?
2) Host A : who is the administrator/owner of this machine ?

Hi, I mentioned the reason in my original post - HostA is an off-site machine that can potentially be physically accessed by other people. For example, it could be my machine at another person's house, behind **his** router and firewall. I can't control that person's router and firewall which potentially block 22.