Sometimes, I take the liberty of looking through the log files of my server. Invariably, there's something like the following at the bottom:
Sample SSH log file
May 8 01:46:43 adhocbox sshd[28514]: Invalid user tanta from 61.100.x.x May 8 01:46:46 adhocbox sshd[28516]: Invalid user cornel from 61.100.x.x May 8 01:46:49 adhocbox sshd[28518]: Invalid user ronaldo from 61.100.x.x May 8 01:46:51 adhocbox sshd[28520]: Invalid user wave from 61.100.x.x May 8 01:46:54 adhocbox sshd[28522]: Invalid user vanilla from 61.100.x.x May 8 01:46:57 adhocbox sshd[28524]: Invalid user ice from 61.100.x.x May 8 01:47:02 adhocbox sshd[28526]: Invalid user mason from 61.100.x.x
This is repeated for a few hundred lines, and is followed a few hours later by another batch of attacks from another IP. It gets tiresome for one's bandwidth to be taken up by these attempts at logins, especially when the server only has one valid user, as is the case for my setup.
I decided to put an end to this, by implementing a whitelist: allowing
specific IPs through at the firewall, and blocking all others. Fortunately,
I run an OpenWRT installation on my Internet router, which provides Linux's
iptables
infrastructure for the manipulation of firewall rules.
In this article, I'll detail how I set up my whitelist system, and how you
can do the same.
What you'll need
My network consists of a Linksys WRT54G wireless router hosting the firewall, and a webserver running a distribution of Linux. For the purposes of this setup, the particulars of the webserver aren't an issue, but you will need:
- OpenWRT:
- My Linksys router has been reflashed with OpenWRT Whiterussian, which
provides the
iptables
firewall, along with simplification scripts for the firewall rules. Any Linux box can act as the firewall, as long as you can pull the appropriate formatting together for the rules. - PHP with PECL-SSH2:
- OpenWRT provides SSH access to the router, which allows for direct editing of the firewall configuration file. We'll be using this to our advantage, by programmatically adding IPs to the whitelist using PHP.
- An external computer:
- The easiest way to test the whitelist setup is by using a computer that's outside the LAN; this will allow you to check that packets are being appropriately blocked at the router, which will not necessarily be the case if you're going between computers inside the LAN.
The OpenWRT Firewall
OpenWRT provides a simple wrapper over the Linux iptables
interface, using awk
to rewrite the contents of a configuration
file into filtering and NAT rules, which are then applied by an init script.
There's also a wrapper on top of that, which constitutes the Web interface
to the firewall; it is this interface that most people associate with the
OpenWRT firewall.
The major issues with the Web interface are that it's relatively clunky, especially when it comes to changing the order of firewall rules; new rules are added to the bottom of the list, and moving them to the top involves an arduous series of clicks and page loads. For most purposes, direct editing of the configuration file makes more sense.
A simple configuration may contain among its rules the following:
OpenWRT's /etc/config/firewall: A sample
accept:dport=113 src=192.168.0.0/24 forward:proto=tcp dport=22:192.168.0.1:22 forward:proto=tcp dport=80:192.168.0.1:80 drop
This sample script will allow the firewall to accept
Ident
requests from inside the LAN, forward
SSH and HTTP to a server
at 192.168.0.1, and drop
everything else. The parameters to each
rule are parsed out by the init script, and built into iptables
rules.
Just as with iptables
, these rules are processed in order,
and the first rule to match the incoming packet is applied. By using this
principle, it's simple to put together a ruleset which will act as a
whitelist for SSH:
Whitelisting SSH: firewall ruleset
forward:proto=tcp src=[IP #1] dport=22:192.168.0.1:22 forward:proto=tcp src=[IP #2] dport=22:192.168.0.1:22 drop:proto=tcp dport=22
In this example, any SSH packets coming from specific external IPs will be forwarded to the SSH server, and any other SSH packets will be dropped at the firewall. This is the behaviour which allows a whitelist: the next problem is how to add IPs to the list.
Adding IPs to the Whitelist
There are two ways to add addresses to this firewall ruleset. The first is to SSH into the OpenWRT router, edit the configuration file to add the appropriate rule, and then restarting the firewall service:
Manually updating the whitelist
ssh -l root 192.168.0.254 vim /etc/config/firewall /etc/init.d/S45firewall restart
The problems with this method are two-fold:
- Ease of use:
- This manual method of updating the list doesn't constitute the most user-friendly interface to addition of IPs, and it can get tiresome to add IPs months or years after the whitelist is initially put into place.
- Access:
- Almost exclusively, access to the router's SSH port is only available from inside the LAN. From an external viewpoint, this availability will only exist by connecting from the accessible SSH server residing on the LAN. This in turn is governed by the whitelist, held on the router. The eponymous Catch-22 situation is an apt description of this problem.
Instead of using a manual process to update the list, it's possible to provide an externally-accessible interface to add IPs. In my case, I have a Web server (which happens to be my SSH server), so I can use a Web script to provide this interface; for the purposes of this article, PHP has been used as the language doing the work.
PHP doesn't have an interface to SSH version 2 by default: this is
provided by a PECL extension named ssh2
. Once this has been
put in place, a variety of methods are exposed to allow for SSH connections
to be made. These can be used to perform work on the OpenWRT router:
Use PECL_ssh2 to connect to the router
$ssh = ssh2_connect('192.168.0.254'); if(ssh2_auth_password($ssh, 'root', '[router root passwd]')) { $stream = ssh2_shell($ssh); fwrite($stream, 'touch /tmp/newfile'); }
As an aside, if you don't like having the router's root password lying around in a PHP file, the PECL ssh2 extension also provides a public key authentication mechanism, and the SSH server on an OpenWRT installation allows addition of public keys in the same manner as OpenSSH.
Using PHP to automatically add IPs
Opening an interactive shell with ssh2_shell
allows more
than one command to be executed, which means we can do the file
manipulation required to add an address to the list. We can combine
everything, to produce the following script.
ssh.php: Add an IP to the router's whitelist
<?php if(isset($_POST['add'])): $ssh = ssh2_connect('192.168.0.254'); if(ssh2_auth_password($ssh, 'root', '[router root passwd]')) { $fp = ssh2_shell($ssh); fwrite($fp, 'echo "forward:proto=tcp src='.$_POST['ip'].' dport=22:192.168.0.1:22" > /tmp/1'."\n"); fwrite($fp, "cp /etc/config/firewall /tmp/2\n"); fwrite($fp, "cat /tmp/1 /tmp/2 > /etc/config/firewall\n"); fwrite($fp, "/bin/sh /etc/init.d/S45firewall\n");// Provide enough time for the firewall to restartsleep(10); } echo "Done."; else: ?><form method="post"> <input type="text" name="ip"> <input type="submit" name="add" value="Add"> </form><?php endif; ?>
All that's required now is to navigate to this script, put an IP into the box, and wait 10 seconds. When this process has completed, the IP has automatically been added to the top of the firewall script, and the firewall restarted.
That should be everything you need to set up your own whitelist access list for SSH. No more brute-force attacks against your server!
Copyright Imran Nazar <tf@oopsilon.com>, 2008