OpenSSH port-forwarding only account
September 2021
Sun Mon Tue Wed Thu Fri Sat
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    
This site is an effort to share some of the base knowledge I have gathered through all this years working with Linux, FreeBSD, OpenBSD, Python or Zope, among others. So, take a look around and I hope you will find the contents useful.
Recent Entries
Recent Comments
Recent Trackbacks
OpenBSD (9 items)
BSD (0 items)
FreeBSD (19 items)
Linux (3 items)
Security (3 items)
Python (22 items)
Zope (13 items)
Daily (144 items)
e-shell (9 items)
Hacks (14 items)
PostgreSQL (3 items)
OSX (8 items)
Nintendo DS (0 items)
enlightenment (0 items)
Apache (3 items)
Nintendo Wii (1 items)
Django (24 items)
Music (12 items)
Plone (7 items)
Varnish (0 items)
Lugo (2 items)
Sendmail (0 items)
europython (7 items)
Cherokee (1 items)
self (1 items)
Nature (1 items)
Hiking (0 items)
uwsgi (0 items)
nginx (0 items)
cycling (10 items)
Networking (1 items)
DNS (0 items)

Syndicate this site (XML)

RSS/RDF 0.91

13 mayo

OpenSSH port-forwarding only account

How to limit an SSH account so, without a valid shell, it still can create tunnels

OpenSSH is a very powerful and flexible tool (If you didn't know it, go ahead and visit to learn more about it).

OpenSSH puffy!

Today I had to give access to some localhost-only service to an external user in one of my servers. I've used SSH port forwarding for quite some time now, I'm familiar with it and I thought it would be the better approach. Using port forwarding I wouldn't have to "open" that locahost-only service to the Internet and all the traffic between the external user and the service will pass through the Internet encrypted.

Let me explain you how I achieved that, because It was trickier than expected.

For the purpose of this article, this was tested using FreeBSD 8.2 and OpenSSH 5.4p1 both in the server and the external user box, but it would work with any other Unix-based OS (other BSDs, Linux, OSx, etc)

First - Create the user account

Obviously, if we want the external user to be able to connect to our server, first we need to create an account for him.

In FreeBSD, we can create the account using adduser, passing the needed parameters as a string:

echo "sshguest::::::Guest account:/home/sshguest:/usr/sbin/nologin:" | sudo adduser -w random -f "-"

The username is sshguest (use whatever you like instead) and we let adduser to asign automatically the UID and GID values for this new user (a new group will be created using the username value as the new group name).

We have set /home/sshguest as the home of the user because a home folder will be needed later (to store ssh public key authentication data) but you can set the home for this user to wherever you like in the filesystem (/tmp/sshguest, /var/tmp/sshguest could be good places for that)

As you probably noticed, In the example above we have set the shell to /usr/sbin/nologin, because we don't want the user to have shell access to our server.

Finally, we passed the "-w random" parameter to adduser so the password for this account would be automatically generated. The adduser tool creates some really strong passwords, so this is a good idea (and we are not going to use password-based authentation anyway).

Second - Get the public key for SSH authentication

Now that we've the account, we have to ask our external user for a public key to enable public key authentication.

If your external user has no idea about what public key authentication is (I would bet there is 90% posibilities of that) first point him to the OpenSSH documentation (RTFM!) and then tell him that he can generate the needed keys using, for example, the following command:

ssh-keygen -t rsa -b 2048

Two files will be generated, he has to send you the one called

(Of course, you can generate the keys yourself and send him the private key, instead of asking him for the public one)

Third - Enable public key authentication

Once we've received the key, we have to put that key in the file .ssh/authorized_keys inside the sshguest user (if you choose that name in the first place). In our previous example, it would be something like:

# cat /home/sshguest/.ssh/authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAMEAyjtJMRtejQaDafYfaL+qc9LrhqHhAQnc7TwF/M3BDTJrZ9ucXLejRwvq/OOcRSsOnUGQyYc2w/TbKQE77cHzujd+PBKgdnQfu8hjJ2Q8to+q8gAEJEOscvLwAACq74llZBmD8gHWBXP6vthkPTFZyd7fkEsKkSgGFiWCZBl+YkibLoRrJpuk462XzPWC4KkOjiUwtJ+2PP1ZvMYYvxnWn+IxwQtbDqwDhY3DnN1dYTeUPT1umtrCCtzQjjynHFFb

Note: we have to create the .ssh directory within the sshguest home directory and we need to be sure proper permissions are applied to both the directory and the file:

mkdir /home/sshguest/.ssh
echo > /home/sshguest/.ssh/authorized_keys
chown -R sshguest /home/sshguest/.ssh
chmod 700 /home/sshguest/.ssh
chmod 600 /home/sshguest/.ssh/authorized_keys

Once we have added the authorized_keys file, we can test the authentication connecting from the external user box (or asking him to try):


What the user will see in the shell/console will be something like:

$ ssh
Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994
      The Regents of the University of California.  All rights reserved.

FreeBSD 8.2-STABLE (GENERIC) #1: Tue Apr 12 12:27:33 CEST 2011

[ ... skip ... ]

This account is currently not available.
Connection to closed.

What happened here? Well, the SSH connection was opened, authentication procceded correctly and the user's shell was loaded. As the shell was set to /usr/sbin/nologin, the system refuses to give a shell to the user and simply drops the connection.

Which, in the end, was what we were looking for, wasn't it?

Four - Fixing the shell problem

Well, that was what we wanted from the beginning, if it would allow us to use SSH's port forwarding features.

Sadly, that's not the case. If we try to open an SSH connection doing some port forwarding, the connection will be dropped too, and the tunnel will not be created:

$ ssh -L 8080:localhost:80

[ ... skip ... ]

This account is currently not available.
Connection to closed.

This happens because we have set /usr/sbin/nologin as the shell for the sshguest user. That, at least in FreeBSD, disables that user logins.

We have to replace it but replacing it with sh, tcsh or bash is not an option (we don't want to give shell access to this user) so we need a fix for that.

One option would be to set a real shell (like sh, tcsh or bash) and then restrict the commands the user will be able to execute. But, as I've said, I don't like the idea of the user being able to start a shell, even if we restrict the comments he will be able to execute later.

Another option, which I personally prefer, is to create a small script that will act as the shell for this user, restricting access completely.

This is a small python script that will do the trick:

command = ''

while command != 'exit':
    command = raw_input('Type the "exit" command to close the connection: ')

The script will wait for user input and, when the user provides the "exit" command, the script will stop execution and the SSH connection will be closed.

We could save this script inside the home of the sshguest user:

mkdir /home/sshguest/bin
# then put the contents of the script inside /home/sshguest/bin/shell
chown -R sshguest /home/sshguest/bin
chmod -R 700 /home/sshguest/bin

and then we could use vipw to modify the user entry, from:

sshguest:$1$ICdMwdEn$3d3X83jdyT8ifLPnl8D8x/:1117:1117::0:0:Guest account:/home/sshguest:/usr/sbin/nologin


sshguest:$1$ICdMwdEn$3d3X83jdyT8ifLPnl8D8x/:1117:1117::0:0:Guest account:/home/sshguest:/home/sshguest/bin/shell

Important: These lines above apply for my current example, in your case it would be different because of the password hash and the UID/GID values.

Obviously, while the python script is being executed (with minimal resources usage) the SSH connection will be kept opened and then the SSH tunnel will work.

$ ssh -L 8080:localhost:80

[ ... skip ... ]

Type the "exit" command to close the connection:

If the user provides the "exit" command, the connection is dropped:

Type the "exit" command to close the connection: exit
Connection to closed.


This is a quick hack. As you can imagine, the script could be improved a lot. We could provide a more complete shell-like interface so our external user can perform certain operations on the server or the connection.

One thing I miss here is the posibility to restrict port forwarding too, allowing the external user to create tunnels only to, for example, a given set of ports or even only one port.

BTW, this hack helped me a lot this morning, I hope it will help any of you one day.

Posted by wu at 13:11 | Comments (0) | Trackbacks (0)
<< Having fun at the mountains (Os Ancares) (I) | Main | Using SSL to connect to freenode in weechat >>
Re: OpenSSH port-forwarding only account

Short answer: use -N flag with ssh.

You don't need a interactive session to open a ssh tunnel, and by default it tries to get a shell for that reason. Using -N flag you don't need a shell, it will work, and you can close the tunnel with CTRL + c.

ssh -L 8080:localhost:80 -N

Easy peasy!

Posted by: Juanjo at mayo 13,2011 16:07
Re: OpenSSH port-forwarding only account

Good point, my fault not to read the man page[1] more carefully:

-N Do not execute a remote command. This is useful for just
forwarding ports (protocol version 2 only).

Anyway, it was fun testing that custom-shell approach and writing the post.

Any recommendation to restrict ports when doing port forwarding Juanjo?


Posted by: Wu at mayo 13,2011 16:35
Re: OpenSSH port-forwarding only account

RTFM (tip: PermitOpen), although I'm not sure if you can set up those by user.

Posted by: Juanjo at mayo 13,2011 16:49
Re: OpenSSH port-forwarding only account

Provided that -N switch does the magics, I would not install and fire a python interpreter to get that.
A single line shell (/bin/sh) would do the same, with or without the loop.

Posted by: Uqbar at septiembre 11,2013 15:53
Re: OpenSSH port-forwarding only account

Excellent Thank you

Posted by: Carlos Ramirez at enero 24,2014 21:44
Please send trackback to:
There are no trackbacks.
Post a comment