This is a followup article to Five Minutes to a More Secure SSH. My impetus for writing the original was seeing too many client's servers left in a default state where they are vulnerable to brute-force attacks. In it, I basically advocate three things:
- Disabling password authentication
- Disabling root login
- Enabling key-based authentication
Refresher and Important Notes: The main OpenSSH server configuration file is called sshd_config and will typically be in the /etc/ssh or /etc/sshd directories. Like all of the configuration files used by OpenSSH, it is in plain text and so can be edited with any text editor. After editing your sshd_config file, you will need to reload your SSH server's configuration - restarting the SSH daemon is not necessary. The command typically looks like this (this is on Debian or Ubuntu):
/etc/init.d/ssh reload
or (on Red Hat/Fedora):
service sshd reload
Also be careful not to lock yourself
out of your SSH server when experimenting with these access
controls. It's a good idea to always have two SSH sessions into
the server, and to always make backup of the relevant configuration
files. If you log out of one session and get denied access, you still
have one active session to fix things.
Restricting Users and Hosts
OpenSSH allows you to restrict users and groups by host or IP address. There are four different directives you can use in your sshd_config file (they are evaluated in this order):
DenyUsers
AllowUsers
DenyGroups
AllowGroups
The format for all of them will be the same - a space-separated list
of users or group names, with optional host names. Here is an example:
AllowUsers vader@10.0.0.1 maul@sproing.evillittleman.net sidious tyranus@*.evillitleman.net
AllowGroups wheel staff
This tells sshd to only allow connections from the
user vader and only from the
IP address 10.0.0.1. The
user maul is also allowed, but
only from the host
sproing.evillittleman.net. User sidious
is allowed from anywhere, and the
user tyranus is also allowed,
from any host in
the evillittleman.net domain
(the asterisk matches zero or more characters).
The AllowGroups line allows login only from users whose primary group name or supplementary group list match one of 'wheel' or 'staff'.
Keep in mind that using AllowUsers or AllowGroups means that anyone not matching one of the supplied patterns will be denied access by default. Also, in order for sshd to allow access based on full or partial hostnames, it needs to do a DNS lookup on the incoming IP address. That means the connecting IP address must have a PTR (reverse) entry that maps back to a real hostname. These aren't hard to get if you have a static IP address, usually your ISP or server hosting provider can do this for you on request.
In addition to the asterisk in hostname or group patterns, you can use a question-mark to mean exactly one character, and an exclamation point to negate the sense of a match:
* - Matches zero or more charactersNote: In my tests, using ! to negate the sense of the hostname match did not work with the AllowUsers directive. It only seems to work when used with authorized_keys file restrictions (see below).
? - Matches exactly one character
! - Negates the host pattern match
Restricting Access and Commands
SSH has the concept of authorized keys. If you are using key-based auth, like I suggested in my first article, the user accounts on the SSH server will have an authorized_keys file (which is by default in the ~/.ssh directory of whatever user account you are logging into). This file lists the public keys, one per line, that are authorized for access to that account. Apart from just specifying which public keys are allowed access, there are a some more options that you can use to further restrict SSH sessions. Here are the most useful ones:from='hostname1,hostname2,'' - Restricts access from the specified IP or hostname patternsHere is an example showing part of an authorized_keys file:
command='command' - Runs the specified command after authentication
no-pty - Does not allocate a pty (does not allow interactive login)
no-port-forwarding - Does not allow port forwarding
from="deathstar.example.com,!jedi.example.com,10.0.0.?" ssh-rsa AAAAB5...2BQ== vader@evillittleman.net
from="pitofdespair.example.com",command="ls",no-pty,no-port-forwarding ssh-dss AAAAZ7...22Q== droidQBX12@evillittleman.net
The first line allows login with the specified RSA key from
deathstar.example.com, from
any host with IP address in 10.0.0.[0-9], but not from the
host jedi.example.com. The
second line merely runs the 'ls' command whenever the specified DSA
key is used - it does not allow any other commands to be run, does not
allow interactive login, and does not allow port-forwarding. It also
restricts the source of that key to the
host pitofdespair.example.com.
Running sshd on a Non-Standard Port
Admittedly this is an attempt at 'security through obscurity', but that doesn't mean it's not useful when combined with other security measures. You may not be able to restrict access by hostname or IP, for example - you may always be sourcing your connections from a dynamic IP address, or you may not be able to get a proper PTR record created. It's quite simple, in your sshd_config file, just change Port=22 to Port=nnnnn (where nnnnn is some high port), then reload the sshd configuration. How do we pick a port number? Some are better than others. First, assume that most port scans are being done with Nmap, and take a look at the nmap-services file. This is a list of ports that Nmap will use by default if you don't specify a port range on the command line. It's probably a fair bet that most script-kiddies are using nmap is this manner. Just pick a high port not on this list, most nmap scans won't notice it. You can also use multiple Port= directives, meaning you can have sshd listen on multiple ports. Connecting to an alternate port is also very easy, use the following options depending on the command used:
ssh -p 65502 vader@deathstar.example.com
sftp -oPort=65502 vader@deathstar.example.com
scp -P 65502 deathstar_plans.doc vader@deathstar.example.com:
You can also edit
your client's ~/.ssh/config file, and
add the Port= directive to one
of your host blocks:
...
Host evil
Hostname deathstar.example.com
User vader
Port 65502
...
Then just connecting with the
command ssh evil will connect
with the specified user and port.
Hashing Known Hosts Files
When you connect to an SSH server, the ssh client stores the server's hostname, IP address and host key in a file named known_hosts. It will by default be in your ~/.ssh directory. Having the IP addresses of the servers you connect to regularly in plaintext can be a security risk if you are on a shared host, or your client gets compromised (stolen laptop, for example). An easy way to avoid this problem is to obscure the information in the known_hosts file by hashing it. Hashing your known_hosts file is easy, you just use the ssh-keygen command, giving it the file path.
ssh-keygen -H -f ~/.ssh/known_hosts
While this hashes all existing host keys, any host keys that get added
to your known_hosts file after
you hash it do not get hashed
by default. To make it the default, add the
directive HashKnownHosts to
your ~/.ssh/config file. Here
is an example of hashing
a known_hosts file. First,
here is what the file looks like beforehand:
dmaxwell@kaylee:~/.ssh$ head known_hosts
10.100.6.151 ssh-rsa
AAAAB4NzaC1yc2EAAAABIwAAAIEAuVgRdptT3xsQoGkiNnJb4Zb02p07MaZX02MFs5JhoqmvV5X5Z/LEQH0S7ngSn3b8kQUnocGulJgLchwfThrd/1OkdyOKdpgXxH/rmDXfwh/MZBNBxnMWBa1HpXSc1gxyDfSSxo+VPa1NCP+ob0dWx4sI+JFJ5cVzbQng4rKp3x8=
10.100.6.162
ssh-rsa
AAAAB4NzaC1yc2EAAAABIwAAAIEAxpQuMJR4Dq/MmrpUryYlNbP+BIWgJlr0LAfaHTIU64Ho6F58Bb1QzlUeeHQSI9f6qFW9aPsBC3Gd5wgQBUj3byinXXHC/10c3vmb2aEujmyL6en2Pef4AN8bKgaRtJq2G/H4MkPWBzxqZPb/k9c3a26P/DjG4y01TMw9vCld+As=
...
Here we run the ssh-keygen command:
dmaxwell@kaylee:~/.ssh$ ssh-keygen -H -f ~/.ssh/known_hosts
/home/dmaxwell/.ssh/known_hosts updated.
Original contents retained as /home/dmaxwell/.ssh/known_hosts.old
WARNING: /home/dmaxwell/.ssh/known_hosts.old contains unhashed entries
Delete this file to ensure privacy of hostnames
And here is what the file looks like afterward (Note that we deleted
the backup file when we were done):
dmaxwell@kaylee:~/.ssh$ head known_hosts
|1|PdThGCuhg23t9bcURxyitJTmfKk=|/z+Xvh4xPuDni8PTB5iK7KKnGdA= ssh-rsa
AAAAB3NzaC1yc2EAAAABIwAAAIEAuVgRdptT3xsQoGkiNnJb4Zb02p07MaZX02MFs5JhoqmvV5X5Z/LEQH0S7ngSn3b8kQUnocGulJgLchwfThrd/1OkdyOKdpgXxH/rmDXfwh/MZBNBxnMWBa1HpXSc1gxyDfSSxo+VPa1NCP+ob0dWx4sI+JFJ5cVzbQng4rKp3x8=
|1|vkLZ22nl30gyJ3gIX74FUF7b3eg=|uy5oSZ8avgZQZE+dwMd/mXGoA38=
ssh-rsa
AAAAB3NzaC1yc2EAAAABIwAAAIEAxpQuMJR4Dq/MmrpUryYlNbP+BIWgJlr0LAfaHTIU64Ho6F58Bb1QzlUeeHQSI9f6qFW9aPsBC3Gd5wgQBUj3byinXXHC/10c3vmb2aEujmyL6en2Pef4AN8bKgaRtJq2G/H4MkPWBzxqZPb/k9c3a26P/DjG4y01TMw9vCld+As=
...
dmaxwell@kaylee:~/.ssh$
rm known_hosts.old
No comments:
Post a Comment