Download: http://github.com/sitaramc/totport
TOTP ("Time-based One Time Password") is the simplest way of adding "2 factor authentication" to a service. Many online services now offer TOTP as an additional security measure against stolen passwords. Reference links: OTPs are a type of two factor authentication, and TOTPs are a very specific type of OTP.
Sadly, most services are not TOTP enabled, or it requires a fair bit of effort to put a TOTP gate in front of them. (Think various imap servers, database servers, even web servers).
The totp
program which is part of totport
can also be used as a TOTP backend for any application that can make system() calls. It has a very simple but powerful API.
But if you can force all your services to go through one "gatekeeper" server, (for example by telling the applications to accept connections only from the gatekeeper) then that server can use totport and authenticate users with TOTP, and then use ssh port forwarding to enable selective access. One downside is that all the traffic goes through this server, so if huge chunks of data need to be moved, this may not be a good idea, but for most normal apps this is fine.
totport
works by creating an additional authorized keys line with appropriate "from" and "permitopen" options, when it receives a valid TOTP authcode. Subsequent connections from that validated IP address will permit forwarding to those hosts and ports.
The basic idea (of having a validate step making some kind of a backend change to enable the next action) is inspired by Konstantin's recent addition of 2-factor authentication to gitolite. Thanks Konstantin!
Of course, one downside of this very simple type of TOTP is that the user needs to have an Android phone or tab handy. While it is possible to compute the TOTP authcodes on the same laptop or desktop that the user is using to log on, this totally defeats the purpose of two factor authentication, so don't do that!
Here's what the process of joining and using this system looks like to one of your users. We'll pretend your user is "alice".
First, the one-time enrollment:
For the artistically challenged among you, the thing on the left is a laptop. If you don't get that, you'll never be able to appreciate Picasso's paintings either, so you may want to look into some art appreciation courses somewhere.
Alice sends you her ssh public key, plus a list of hosts (and corresponding ports) behind the TOTP gatekeeper that she wants access to.
You'll do some stuff on the server then tell her to "enroll".
Alice runs "ssh totport@host enroll", which enrolls her into the TOTP system. This means the server generates and prints out a secret that Alice must feed into the TOTP app ("FreeOTP" preferred, otherwise "Google Authenticator") on her Android mobile or tab. More on this in one of the appendices.
Next, the actual usage:
When Alice wants to access any of the hosts behind the TOTP gatekeeper, she first sends a valid TOTP value, something like this:
ssh totport@host val <TOTP>
where the "<TOTP>" is of course whatever 6- or 8-digit number her TOTP software is currently displaying.
A message like validated 'alice' from '1.2.3.4'
indicates success.
Once that is done, she enables the port forwards. This is done by connecting to the TOTP server again, but this time without any command. She runs the following in a spare terminal window and leaves it running:
ssh -L 2222:1.2.3.4:22 -L 4433:2.3.4.5:443 totport@host
(Later, she will probably set up her ~/.ssh/config
file to avoid typing so much! Details are in one of the appendices.)
At this point the local ports are ready. She uses the local port numbers she chose (2222, 4433, in the example above) with an IP of 127.0.0.1, and starts her clients, whatever they may be.
As root:
Install perl's DBI driver and DBD::SQlite modules. On Fedora, the RPMs are called perl-DBI and perl-DBD-SQLite.
Running
perl -MDBI -MDBD::SQLite -e 0
should not show any errors.
Create a userid exclusively for this tool; in this documentation we will call it totport
. Make sure no one has command line access to this userid from ssh. Instead, they should log on to some other userid on the server and run "su - totport". If you have a password for this, make sure only the admin knows it and it is really long and complex and all that jazz.
Edit /etc/ssh/sshd_config
and make sure you have the following settings.
In the main part of the file:
AllowTCPForwarding no
At the bottom of the file:
Match User totport
AllowTcpForwarding yes
I'm not sure what is the minimum ssh version required for this, but most distros after 2012 or so should be fine.
Don't forget to restart sshd.
As the totport
user:
Run mkdir bin; cd bin
, then get the following files into this directory:
# from http://github.com/sitaramc/totport
totp
totport
Utils.pm
# from http://github.com/sitaramc/hashlite
hashlite
HashLite.pm
Don't forget to "chmod +x" the files that don't end in ".pm".
Copy the sample rc file t/totport.rc.sample
from totport (same URL as above) as $HOME/totport.rc
.
Make whatever changes you need in this file; it's sufficiently commented.
Run mkdir ~/keydir
to prepare for receiving user keys.
Run qrserve
to start the qrcode display web server on port 3536.
Obtain public keys from your users and put them in ~/keydir
. The public key file for a user "alice" will be copied to ~/keydir/alice.pub
. Subdirectories are not allowed. If a user has more than one key they want to send you, please look in the appendices for how to deal with it.
Decide what ports the user should have access to (look in ~/totport.rc
for the short names). Using the examples in the sample rc file in the totport code's t/
subdir, you might say:
totp -u alice ports = GIT1,GIT2,MAIL
which would allow alice to access those internal host:port combinations.
NOTE the spaces around the "=" are required. ALSO NOTE there are NO spaces between the various shortnames used, just a comma.
Run totport rebuild
to rebuild ~/.ssh/authorized_keys
.
Tell the user she can "enroll". She has to use a normal ssh client and run ssh totport@host enroll
.
This will print a "secret" that she will need to feed to the TOTP app (FreeOTP preferred, else "Google Authenticator") on the user's Android mobile or tab. See one of the appendices for more on this part of the process.
Once a valid authcode has been submitted, the next connection to the same user/server (totport@host
), with the port forward options specified, should be sent within 20 minutes. After 20 minutes, the entry could be purged and the auth keys file rebuilt at any time.
You can change this on a per-user basis. For example, Bob always comes in from the same IP so we want to make things easier for him. This command will keep the IP valid for 10 hours:
totp -u <username> valid_for = 600
Although there's no daemon hanging around waiting for entries to expire and rebuild the authkeys file, a rebuild can be triggered in the following ways:
Manually running totport rebuild
. You could also put it in cron. I suggest using an interval between 10 minutes and 1 hour.
Any time anyone tries to validate themselves. This is kind of a hack, in that if you forget to set up a cron job, there's still some way in which expired entries get purged!
On the other hand, it causes interesting behaviour in some vague "game theory" sense -- the busier the system, the more actively things get cleaned up and people have to revalidate themselves!
If the "people have to revalidate themselves" bothers you, please remember that this is only a port forward, and as long as your network does not die, an ssh -fN ...
should basically just run forever.
There are several background bits you might need, in order to understand how this works.
How does ssh force something specific to run, is best explained here. Some parts of that page may be specific to gitolite, but by and large it's a fairly general explanation of ssh's "force command" feature.
That page also coveres how ssh distinguishes one user from another, if you noticed that every user must ssh to the same ssh user (totport
in our examples) and were wondering how that happens.
Ssh's authorized_keys file has more tricks than described in the link given in the previous para. Read up on the "from" and "permitopen" options.
Ssh port forwarding is important to understand. Briefly, what happens when you say
ssh -L 2222:1.2.3.4:22 user@host
is that a local listening port (2222) is created on your workstation. Any connections to this will be transparently forwarded over the secure channel to the server, and a connection is opened to the remote host and port specified (in this example, IP 1.2.3.4, port 22).
One advantage of this is that the IP used is as seen by the TOTP server, which means you can reach internal servers.
TOTP is also useful to know. For example, you (and your users, even more so!) need to understand that the secret that was generated by the TOTP system must NOT be stored permanently on the same computer that contains the ssh private key! Then it would not really be "2 factor", in a sense.
Several websites, starting with google itself, then github, and many more, use TOTP based 2-factor auth. Many of those services have explanations, pictures, etc., of TOTP. Search around...
Mainly, if your users don't protect the "secret" well enough, this won't work so well. Luckily, both FreeOTP and "google authenticator" don't allow you to view the secret once it has been entered, so it's safe from casual attempts at getting the secret, but I have no idea what the internal storage is and if some fancy tool/hardware/memory extractor can lift that information out.
If your private key is only used for this site, this kinda sorta makes it less risky to not have a passphrase on it, but for really important sites I still would do that.
Brute force/DOS protection
Since the attack model is of someone having successfully compromised one of your users' ssh private key, you can't implicitly trust them. It should be easy enough to add code that detects more than N logins in M seconds, and if that happens, disable all access to that user for some time.
Generating ssh keys and sending your admin the public key is fairly straightforward. Steps 1, 2, and parts of step 4 in Github's help pages are useful here.
First, let's remind ourselves that the TOTP authcode must come from a different device than the PC which contains your ssh keypair. Although it is quite easy to do that also on the same box, it is most strongly recommended not to do so. In fact, don't even be tempted to store the secret on the same computer!
However, the initial enrollment will happen from your computer. When you type in the 'ssh totport@host enroll' command, you will get a summary of the different ways to get that code securely into your device.
This section gives you more details. Please use any of those methods, each of which has been detailed in the three sub-sections below. Please do not use google.com's QR code service, or indeed any external server, to produce the QR code.
I would also avoid Windows programs to generate QR codes. As far as I know there's no curated, trusted, repository to get software from, like there is for most Linux distros. Often it's hard to even know if the software is open source. Worse, even well-known, credible, open source software can be riddled with malware because of unscrupulous people taking advantage of the lack of a trusted repository.
IMPORTANT: don't do this from a location where your laptop or desktop screen is visible to other people, in case someone captures your screen with a good camera from a distance! Not only are cell phone cameras getting better and better, qrcode is designed to overcome many errors!
The totp box also runs a qr code web server called qrserve
. This server already has your "secret", and qrserve
can only be accessed over the secure channel you're already talking on, so this is as safe as it gets.
On Linux, run ssh with -L 3536:127.0.0.1:3536
option, and the command qrcode
to setup the port forward required. That is:
ssh -L 3536:127.0.0.1:3536 -p <port> totport@<IP> qrcode
where "<port>" and "<IP>" are whatever your admin gave you.
On Windows, you'll have to do the equivalent in putty. I'm not really sure how to do this right now, but I'll update this when I find out. I believe it involves a related command called "plink".
After that command starts, open a "private window" in your browser (some browsers call it "incognito" mode), then point it at the url that the enroll command printed, which is something like:
http://127.0.0.1:3536/qr/....
A qrcode image should show up. Use your device's QR code scanner to scan it. (Please note that the FreeOTP app comes with a builtin scanner).
When done, hit Ctrl-C on the ssh command above or close the putty session.
On Linux etc., install 'qrencode' using your package manager and run the qrencode command that the enroll output showed. This command should look something like this:
qrencode -tANSI -m1 -o- otpauth://totp/....?secret=....
A qrcode image should show up. Use your device's QR code scanner to scan it. (Please note that the FreeOTP app comes with a builtin scanner).
Please make sure your shell does not retain the command in its command history! If you don't have a ready-to-hand way of ensuring that, run cat | sh
then paste that line in and hit Ctrl-D!
If all else fails, just type in the secret. Spaces and case differences do not matter, and the enroll command conveniently prints it in chunks of 4 letters to make it a little easier on your eyes!
Let's say you need the following port forwards: port 22 of host 1.2.3.4, port 443 of host 1.2.3.5, and port 5432 (postgres) on host 1.2.3.6.
Once the enrollment is done, here's what you will be doing whenever you wish to connect with any of these services:
ssh totport@totp-gatekeeper.example.com val <TOTP code>
# validate your IP, then:
ssh -L 2222:1.2.3.4:22 -L 4433:1.2.3.5:443 -L 5432:1.2.3.6:5432 totport@totp-gatekeeper.example.com
That's a terrible thing to have to type every time! After a few days you'd feel like you'd get CTS or something!
Luckily, ssh is pretty smart. Just create a file called "config" in ~/.ssh
, and put these lines in it:
host tp
user totport
hostname totp-gatekeeper.example.com
# ssh to 1.2.3.4
localforward localhost:2222 1.2.3.4:22
# https to 1.2.3.5
localforward localhost:4433 1.2.3.5:443
# postgres server on 1.2.3.6
localforward localhost:5432 1.2.3.6:5432
And that's it. Now you can simply type:
ssh tp val <TOTP code>
# and then...
ssh tp
and once you type the second command, your local ports are ready to use. In order to "ssh 1.2.3.4", you'd say "ssh -p 2222 127.0.0.1" instead. Similarly, instead of "https://1.2.3.5", you'd say "https://127.0.0.1:4433", and so on.
I've been told that all this can be done in putty also, but it's cumbersome. The preferences dialog is a steaming pile of canine excrement, but with great care, patience, experience, (and large amounts of alcohol, in the worst case), it is possible to set it up in a way similar to openssh's ssh config file.
Patches to this section of the documentation are NOT welcome. However, if you put up instructions somewhere or find a good place that describes how to do the equivalent of the previous section, email me and I'd be happy to add a link to it.
Unlike gitolite, totport has no simple way to let a single user use multiple keys. Just treat them as different users "alice-1", "alice-2", etc., or perhaps "alice-home", "alice-work", "alice-laptop", and so on. An added advantage of this is that you can choose different port permissions for the different personas, if needed.
There are probably ways to deal with this better, but until it becomes a serious issue this workaround will do fine.
totport
is copyright Sitaram Chamarty and is licensed under the GPL v2.