30 October 2006 ~ 35 Comments

HOWTO: Five steps to a more secure SSH

Secure Shell (SSH) is everywhere.

Since it’s inception in 1995, SSH has become the most widespread remote login protocol for Linux boxes, with some estimates saying that there were at least 2 million SSH users at the end of 2000. Gone are the days of telnet sending your data in plaintext over untrusted networks. Now you can type with a reasonable amount of confidence that your data is encrypted and secure.

But, as Uncle Ben said, with great power comes great responsibility!

By its very nature, an improperly configured ssh daemon can be a network liability rather than a asset. If you have a Linux box that is accessible via the Internet, it pays to know what you are doing.

Therefore, here are five things you can do to lock down your server and make ssh more secure…


1. Disable root logins

This one is easy. There is (almost) no reason I can think of to allow remote root logins via ssh. Disabling the ability to do so won’t cause you any pain, it’s easy to do, and gives you quite an improvement in security.

Use your favorite editor to open your ssh configuration file. In Gentoo it is located at /etc/ssh/sshd_config, but it might be hiding somewhere else depending on your distribution. Find the line that says PermitRootLogin (or create it if it doesn’t exist) and change the value to “no”:

PermitRootLogin no

That’s it.

Now, this doesn’t prevent anyone from breaking into a normal user account, but at least it is one more hoop they have to jump through before getting root.

2. Disable keyboard interactive logins

This one is a little more complicated, but when combined with the next step, gives you a decent amount of protection against automated password cracking attempts. The downside is that users will need to take some time to create encryption keys before they can log in.

In Linux, for a user to create a key pair, they can do the following:

europa $ ssh-keygen -v -t rsa

When you run that command, you should see output similar to the following:

Generating public/private rsa key pair.
Enter file in which to save the key (.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in id_rsa.
Your public key has been saved in id_rsa.pub.
The key fingerprint is:
2d:47:41:2d:5e:89:e7:10:85:31:36:f8:e6:e2:d4:23 jpm@europa

Be sure to save the generated key files in the .ssh directory located in your home directory. Also, although the passphrase is optional, be sure to enter one (twice). And don’t cheat with an easy one! Take the time to make it a nice mix of letters, numbers, and/or special characters.

If you noticed, I used two options for ssh-keygen. The first is a -v for verbose. There’s really no need for that one, but I always like to see what is going on.

The second one sets the key type. There was a time when the RSA algorithm was patent encumbered, so most Linux users stressed that DSA should be used instead. The RSA patent expired on September 21, 2000, so ever since that date there has been no problem using it. There are some other differences as well, such as the fact that DSA is a signing algorithm only, while with RSA you could technically encrypt data with it as well (not recommended). Some also claim that it takes longer to generate RSA keys than it does DSA… but others disagree. When it comes right down to it, for most people… it just doesn’t matter.

(I’m sure I made at least one or two cryptographers pass out with that last statement.)

In Windows, you can use the PuTTYgen utility (docs) from the PuTTY site. Download the executable (only one file), and run it.

Click the Generate button, and move the mouse around to generate some randomness as instructed:

Randomness

When complete, you’ll have a public/private key pair waiting for you:

PuTTYgen

Click the Save Public Key button, and save the key somewhere sensible. Just to follow convention, I usually name it id_rsa.pub. Next click the Save Private Key button, and save the key. PuTTY recommends using a filename that ends in .ppk for some reason, so I usually use id_rsa.ppk. File names really don’t matter much.

You’ll also need to change the settings for the remote host in PuTTY to (a) not attempt keyboard interactive logins, and (b) to use the private key file you just generated. You can do that in the SSH/Auth section of the configuration:

PuTTY auth config

After you have your keys generated, you need to add it to your .ssh/authorized_keys files on the server you want to log in to (i.e. the one you are “securing”). To do that in Linux, it’s probably easiest to just use some ssh-fu:

europa $ cat id_rsa.pub | \
ssh jpm@metis "cat >> .ssh/authorized_keys"

In Windows, I’ve found it is easiest to select and copy the text version of the key from inside PuTTYgen, log into the remote box, edit authorized_keys file and paste it in. I’m sure others think I’m nuts for doing it that way, but it works for me.

After you have your public key added, it is important that you now confirm that you can log in without a password! The last thing you want to do is to some how mess up password-less logins, and then disable keyboard interactive logins. You’ll have effectively locked yourself out of your own box. That’s probably a Bad Thing. :)

europa $ ssh ganymede
Last login: Sun Oct 29 22:44:33 2006 from europa

ganymede $

If, and only if, everything seems to work ok, you can now disable keyboard logins with the following lines in sshd_config:

PasswordAuthentication no
ChallengeResponseAuthentication no

You can alternatively disable PAM altogether with “UsePAM no” but that presents its own set of issues. So I’d probably recommend using the two lines given above instead.

This step is, in my opinion, only as good as the enforcement of the next step…

3. Enforce password protected keys

UPDATE: Looks like I made a mistake in this section. PermitEmptyPasswords doesn’t actually work the way I originally described it.

Quoth the SSHD_CONFIG(5) man page: “PermitEmptyPasswords: When password authentication is allowed, it specifies whether the server allows login to accounts with empty password strings.” That is the accounts, not the keys. I’m not sure if there is a way you can force users to use passwords on their keys, other than by policy.

The problem is, as I originally stated, that if your users do not use passwords on their keys, your security is really only as good as the security of the boxes they are logging in from. That’s a non-trivial issue. If you have any suggestions on how to handle this, please leave a comment below.

If you generated a new key pair in the last step, you noticed that ssh-keygen allowed you to skip the passphrase on your key pair. It is perfectly allowable to have a “passwordless” key pair. It’s easy and very convenient. In Linux, you can simply ssh to a host and you are logged in. No need to type a username or password.

However, if you want to add more security (at the cost of a little convenience) you can configure your ssh server to require that all keys be password protected. To do that, edit your ssh_config to set PermitEmptyPasswords to “no”:

PermitEmptyPasswords no

Note that this might be a problem if you do automated backups tunneled over ssh using passwordless logins. But if you force users to use keys, but then fail to require passwords on the keys, your security is only as good as their security of their boxes. In my opinion, that’s a bad position to be in.

4. Blacklist with DenyHosts

I’ve mentioned DenyHosts back in January, and it’s seen some rather active development since that time. It’s a Python script “intended to be run by Linux system administrators to help thwart SSH server attacks.”

In short, DenyHosts looks for bad login attempts from hosts, and then adds them to a blacklist, banning them forever. Very cool.

The easiest way to install it is using your distro’s package manager:

europa ~ # emerge -pv denyhosts

These are the packages that would be merged, in order:

Calculating dependencies... done!
[ebuild R ] app-admin/denyhosts-2.5 0 kB

Total size of downloads: 0 kB

europa ~ # emerge denyhosts

There really isn’t much “documentation” to speak of, but the default configuration file provided by the install is actually all you need to get up and running. Just read all the comments and work through the file, and you’ll end up with something usable.

europa $ nano -w /etc/denyhosts.conf

Configuring DenyHosts is not hard, but if you aren’t careful (i.e. you don’t read) you can accidentally add your own trusted hosts to the blacklist. I know this, because I accidentally did it. :)

To prevent that from happening, find the WORK_DIR line in the configuration file:

WORK_DIR = /var/lib/denyhosts

Create a file in that directory called allowed-hosts and add your trusted hosts to it. The file supports various formats and wildcards, so it’s easy to do:

# Allow everyone from a given subnet
192.168.1.*
# Allow a single address
192.168.1.5
# Allow a single hostname
ganymede
# Allow a range of addresses:
192.168.2.[5-10]

This is all covered in the extremely helpful FAQ.

After you have DenyHosts configured, you can now get it up and running. To do that, you can either run DenyHosts as a daemon or from cron. As a daemon in Gentoo:

rc-update add denyhosts default

Or from cron (e.g. every 10 minutes) with the following command line:

python /usr/bin/denyhosts -c /etc/denyhosts.conf

Now, you might be asking why you should care about sites that are, by definition, failing to get into your box. Well, first, it locks them out before they have a chance to make a significant number of attempts to get in.

Second, DenyHosts 2.x adds the very interesting (and completely optional) “synchronization” feature. You can choose to send your list of denied hosts to the DenyHosts servers. They will, in turn, pass their collective list of blacklisted hosts to you. So (theoretically) you will begin to blacklist active attackers even before they ever attack your box. That’s pretty cool.

You can read more about DenyHosts at HowtoForge, Unix Review, and a bunch of other sites listed on the DenyHosts website.

And let me say again that the FAQ is well worth reading.

5. Change the port number

You are welcome to disagree with me on this one, but I’ve found it to be quite effective.

Most cracking attempts on your ssh server come from automated scripts that tirelessly scan the net for ssh daemons and attempt to break in. The thing is that these scripts make a very large assumption, namely that your ssh server is running on port 22/tcp. That can be used to your advantage.

If you switch the port that ssh runs on by changing the following line in sshd_config:

Port 22

… or (better yet) use port forwarding to make it appear to run on a different port to the outside world, you can dramatically decrease the number of automated attacks you see on a daily basis. After I made the switch away from port 22, the number of attacks I was seeing dropped to zero.

Additionally, some WiFi hotspots and other locations are quite picky about which ports you can get out on. If you run your server on a known “standard” port such as 80/tcp, you’ll be able to get to your server no matter where you are.

Security though obscurity you scoff? Perhaps. But it’s easy, causes no inconvenience, and might just reduce the number of attacks. That sounds like a winner to me.

Summary

Don’t allow root logins, use password protected keys rather than keyboard interactive logins, block hosts that are trying dictionary attacks, and consider switching your port number. These are just a few very basic ways to make your SSH install a little more secure.

Remember that when it comes to computer/network security, there are no magic bullets. There is no single thing you can do to make your systems “secure.” That said, hopefully this guide will help you add a tiny bit of security to your systems.

Oh, and by the way… if you are still using SSH-1, slap yourself and upgrade. Really. :)

References:

Updates/Notes:

  • Obviously the big change is related to PermitEmptyPasswords. Please see above.
  • Fixed some typos in the third paragraph.
  • Quite a few people have recommended port knocking either instead of or in addition to changing the port number. I have no direct experience with port knocking, but it is my understanding that it does come with a certain amount of inconvenience on the client side.
  • Yes, if you only use the command line ssh client, you will need to add a `-p` to your command if you are using a different port for the ssh daemon. Personally, I don’t think that’s a big deal, but Eric says it is quite a burden indeed! :) For those of us who use PuTTY, however, it’s no big deal as you can save that in the host’s profile. Also, see mikep’s comment below regarding using .ssh/config to set different ports to use for each host. Good idea!
  • Quite a few people have mentioned ssh-agent. Good call. I should have included that.
  • A couple people have recommended fail2ban and pam_abl as other methods of doing automatic blacklisting. I have no experience with either, but I’m sure they are both good. I recommended DenyHosts mostly because (a) I have experience with it, and (b) this is a (sort of) Python related weblog. Deciding which package is best for you will depend on your specific needs.
  • Yes, whitelisting is certainly an option as well. I’m personally not a fan because you never know when you might want to connect from someplace you didn’t expect. But yes, it would certainly add some amount of security. Again, you needs might be different than mine.
  • Jon Barnhardt has some additional comments regarding moving ssh to a non-standard port.

35 Responses to “HOWTO: Five steps to a more secure SSH”

  1. Kint 31 October 2006 at 9:56 am Permalink

    “Oh, and by the way… if you are still using SSH-1, slap yourself and upgrade. Really. :)”.

    Really? Explain why. It’s a common misconception that SSH-1 is *that* flawed.

    A nice comment from Theo Deraadt, Open[BSD/SSH] master.

    “I am actually more worried about security problems in the protocol 2
    code which is roughly 4-5x as complicated. People’s fears are
    misplaced. But it is fun to ride a meme, isn’t it.”.

  2. psyjoniz 31 October 2006 at 10:27 am Permalink

    great article! (bookmarked :)

    the reason i am posting is in response to your strikeout of the empty password keys and how i handle it. my machine holds keys to all my servers. they are all empty password keys lending to a rather insecure setup. your reckognition of this is very wise.

    since i realize that the weakpoint in my security setup is my machine i cover myself in four ways: 1) the bios is password protected both on boot and modifications 2) my harddrive is encrypted 3) the screensaver is passworded @ 3 minutes 4) whenever i leave my machine i lock it. i’ve been called paranoid more than once. but i also don’t have people cracking any of my boxes open (no, this isn’t a challenge.. let it be..).

    anyway – its great to see writeups like this – thank you – very informative.
    -psy

  3. fak3r 31 October 2006 at 10:33 am Permalink

    Great article, started kinda slow, but then included a few steps I haven’t taken. I think the top ones should be; no root, and change the port — those two alone get you started on the right foot.

    Going to try some others so I can putty from work to my freebsd server *more* secure-ly. Oh, and I work for a fortune 500 company that still only has TELNET on some boxes! I haven’t used telnet since ~2000 on a ‘real’ box, much less even my homeserver.

  4. Trevor 31 October 2006 at 10:33 am Permalink

    I am also a firm believer in using portknocking.
    I have not allowed interactive logins for years but I was noticing a significant increase in login attempts.
    Then I discovered portknocking.
    By implementing a few simple ipchains rules, remote users cannot get through to my ssh port until they have attempted a TCP connect on an unrelated port first.
    Since implementing this, I have yet to see anyone (apart from the people I want to) make it through even this simple firewall rule.

    Here is the article that got me going:
    http://dotancohen.com/howto/portknocking.php

  5. alucardX 31 October 2006 at 10:45 am Permalink

    Very nice tips for securing SSH!! Works fine!!

    Thank you for sharing :-)

  6. Richard Jenniss 31 October 2006 at 10:59 am Permalink

    I’m surprised you have not mentioned this one:
    AllowTcpForwarding no

    The reason for this is simple. Lets say you’re a fairly large ISP. You are running web hosting and you permit shell access. TCP forwarding is available to any authenticated user. From there, if you don’t get the idea by now keep reading. Once authenticated, in most cases, you’re now behind the firewall. From there it’s easy to redirect traffic internally to other machines, while at the same time looking like local traffic. Being encrypted, this can be difficult to determine where the source is. Are you tracking who is forwarding ports?

    Second piece that I’d recommend is Fail2Ban:
    http://fail2ban.sourceforge.net
    If you still permit keyboard logins.

    Snort or Portsentry are also very good ideas, but are outside the topic of Securing SSH.

  7. sal 31 October 2006 at 11:05 am Permalink

    With great power comes great responsibility….to use correct grammar. “It’s” is a contraction of “it is” — “its” is the possessive.

  8. Steve Webb 31 October 2006 at 11:12 am Permalink

    Use ssh-agent to load your key into memory and keep your key on a USB key. If your ssh key is on-disk, then anyone can just jump onto your computer and ssh anywhere. If your key is on a usb key or on a laptop, you can “disconnect” your key from your machine. If your machine gets compromised, then they won’t be able to ssh anywhere unless they have physical access to your USB key.

  9. Anonymous 31 October 2006 at 11:14 am Permalink

    Erh… uhh…

    I think this is almost all unnecessary, ssh is a well-established protocol.
    Unless you have the simplest of usernames and passwords all this adds up to needless.

    If you’re too lazy to have a decent password, rather than making your box exceptionally more difficult to access yourself then use something to inject rules into iptables like this:
    http://www.csc.liv.ac.uk/~greg/sshdfilter

    Yeesh, whatyouhave+whatyouknow is nice but overkill/not the right approach for a personal server/machine.

  10. Josje 31 October 2006 at 11:15 am Permalink

    Hint 6: use fail2ban to kill those pesky brute-forcers

    http://fail2ban.sourceforge.net/wiki/index.php/Main_Page

  11. Mark 31 October 2006 at 11:15 am Permalink

    You can also use your IPtables rulesets to block all unknown IP addresses apart from your own select few. This is only good if you have a static IP address… but very effective.

    e.g

    -A INPUT -s 1.2.3.4 -p tcp -m tcp –dport 22 -j LOG_ACCEPT

  12. jwm 31 October 2006 at 11:48 am Permalink

    One thing you forgot thats very good because it will disable all other accounts access via ssh

    AllowUsers user, otheruser

  13. vladimir 31 October 2006 at 12:14 pm Permalink

    one can use pam_abl instead of denyhosts script… works differently and the attacker cannot tell when block is in the place wasting time on pointless re-tries…

  14. anocelot 31 October 2006 at 1:02 pm Permalink

    I totally agree about the whole default port thing. I’ve been listening to collegues blather on about this to me about this for years, but the proof is in the pudding.

    FACT: If someone WANTS to break into your site, they’ll port scan you to figure out how.

    FACT: There are uncountable numbers of zombies on the internet just scanning for machines running SSH.

    FACT: Your SSH server cannot be compromized by a computer that doesn’t know it exists (well, not easily).

    You can’t stop a truely determined thief from breaking into your system by simply moving the port – but you CAN prevent the zombies from noticing you without a significant ammount of overhead.

    I have implemented this solution and noticed that my SSH hack attempts went from about 2,000 / day to ZERO PER DAY.

    Users out there can and should feel free to diagree with me that I am offering any degree of security, but I simply won’t listen to them. ;)

    Oh and Sal. With great technical ineptitude’s comes great desire’s to pedantically pick at someone’s grammar’s. I find your comment’s imeritoriou’s and off-topic’s and fine you $12.00…’s.

  15. Mark 31 October 2006 at 1:11 pm Permalink

    I’ve written a HOWTO showing how to use SSH to make a tunnel for VNC. You can check it out @ http://tinyurl.com/shkb4

    It’s a good use of SSH. It has basic security features. Are there any security things I missed?

    [I changed Mark's URL into a TinyURL to fix the formatting a bit. Hopefully he doesn't mind. :) -JPM]

  16. Keith 31 October 2006 at 3:55 pm Permalink

    That sounds like a very effective 5 step procedure to secure a SSH connection. I have to agree with most of them.

  17. Mark 31 October 2006 at 4:17 pm Permalink

    Thanks for the fix! Good idea — no problemo.

  18. Nailer 31 October 2006 at 7:21 pm Permalink

    I’m not sue if you’re aware of this, but OpenSSH typically runs on Unix variants. Web pages related to the topic of Unix variants are not supposed to be visually appealing.

    Please remove your CSS, and pleasant graphics, and replace with a single giant serif text column that occupies the entire width of the page, and a couple of crappy pixelated GIFs. Preferably with a black-on-dark-grey color scheme.

    Thanks very much.

  19. John 31 October 2006 at 9:21 pm Permalink

    @Nailer:

    Heh! I’ll try harder next time. ;)

    - John

  20. Swoosh 31 October 2006 at 9:31 pm Permalink

    Another good thing to look into (for securiing SSH over Linux) is PAM. Its pratical use would be to block a host after 4 unsucessfull login attempts, and then keep it blocked untill you physically unblock them (or set up a cron.hourly scripit to do it every hour)

  21. John 31 October 2006 at 11:13 pm Permalink

    @Swoosh:

    I believe that’s basically what pam_abl does, and is certainly a good alternative to DenyHosts if you are looking for one. (And if you don’t mind missing out on DenyHosts’ synchronization abilities.)

    - John

  22. Jay 1 November 2006 at 9:03 am Permalink

    I agree with Mark, you shouldn’t be allowing connections from anywhere to your ssh port. It’s trivial to do and is worth more to secure your system than most of these other suggestions combined.

  23. mikep 1 November 2006 at 9:50 pm Permalink

    Re: alternate ports:

    For unix/command line ssh, edit/create ~/.ssh/config and make a section that looks like:

    host 192.168.1.3
    Port 8022
    Compression yes
    CompressionLevel 9

    Plus whatever other config changes that are different for this host, then when you ssh to that IP (or hostname), it will automatically use port 8022 instead of the default 22. You can also put your port forwards, different ciphers, etc, basically, whatever you can set in the system-wide ssh_config can be set here.

  24. rob 2 November 2006 at 11:38 am Permalink

    You should add a section on role-based keys: limiting what commands can be run by a given key. It’s great for role-based accounts such as for remote backup, and a good alternative to disabling root logins entirely.

  25. Tim Archer 9 April 2007 at 7:15 pm Permalink

    I also have a small writeup on some minor changes I make to secure SSH (disable root login, change login grace time, change protocol, change port, etc)

    My writeup is at:
    http://timarcher.com/?q=node/46

    I hope it helps somebody!

  26. burkass 24 August 2007 at 2:36 pm Permalink

    by the way can i allow 1 ip address.

    btw i just want only 192.168.0.1 to control my comp via ssh.
    how can i do it ?

  27. Asrol 31 August 2007 at 1:04 pm Permalink

    Question:

    # burkass Says:
    August 24th, 2007 at 2:36 pm

    by the way can i allow 1 ip address.

    btw i just want only 192.168.0.1 to control my comp via ssh.
    how can i do it ?

    Answer:

    User TCP Wrappers.

    Command ;

    vi /etc/hosts.deny
    # add line
    ALL:ALL

    vi /etc/hosts.allow
    # add line
    sshd:192.168.0.1

    More –

    Change the ssh default port from 22 port to others, such as port 2995

    cd /etc/ssh/
    vi sshd_config
    Find Port 22 and replace with;

    Port 2995

    Dont use root to login.

    cd /etc/ssh/
    vi sshd_config

    Find PermitRootLogin and change to ;

    PermitRootLogin no

    after all,

    killall -XUP xinetd
    /etc/init.d/sshd restart

    Hope its useful to others too

    regards

    thanks

  28. renato gallo 25 October 2007 at 12:41 am Permalink

    at my job they passed me a ppk file, now I need to import it in my gentoo machine…. howto ?

  29. Gregg Lain 24 August 2008 at 6:50 pm Permalink

    Nice writeup – thanks for the extra stuff about the ssh keys – yes w/o a password if the box is stolen, its done.

    I read this somewhere and works great:
    Change port from 22 to 1025+
    With Portsentry, it scans for daemonized ports – since 22 is no longer being used – and if the firewall allows port 22 incoming, now you have a honeypot for SSH hackers.
    Portensetry – up portsentry to scan up to say 25000 or so – against the documentation. I noticed most portscanners start at 1025 where the default portsentry stops…

  30. Giuseppe 24 September 2008 at 7:58 am Permalink

    Uhm… I’m a novice! I did all the good suggestions above… Now I’m not able to log using Putty anymore…
    I changed the port…
    HELP !


Leave a Reply