Hi there,

Some time ago there was a discussion here on various techniques for
protecting access to ssh port 22.

Other day I read http://rlworkman.net/conf/firewall/sshattacks on
Robby W's website and decided to write something like that into my
firewall ruleset. Already I had 'gotojail' and a 'serv_rej' reset
targets so Robby's example fitted in to my firewall design very well.

And, since I've been playing a lot with ipt_recent rules lately, I also
added a port-knock rule to open access to the standard port 22. Only
confusing part of port-knocking is one needs to 'knock' again prior to
performing an scp file transfer to the machine one has already logged
in to.

In practice this works very well, if somebody out there happened across
the port-knock port they'd still have to guess username and password to
get in. The overnight firewall logs show the typical casual port 22
attempts at the usual one or two tries per source IP -- the port appears
closed, so they move on. This is good.

Finally, a friend opened up ssh access on his machine and I revisited
rsa public key access to ssh back to my machine.

The firewall rules
Similar to Robby's sshattack page, with port knock added:

The gotojail target sample logs 1 in 20 hits and drops traffic, IPs
are held in 'jail' until they're quiet for a long time, over an hour.

The serv_rej target logs each event and sends a reset (-j REJECT
--reject-with tcp-reset), commonly reported as 'connection refused'
on remote.

I made up a knock port number and checked it against the nmap services
file (/usr/share/nmap/nmap-services) that it wasn't being used for anything.

These timing + counts are from Robby's sshattack document:

server_ssh_warn_secs=30 # warn user with a connection refused
server_ssh_limit_secs=45 # jail user IP when exceeded

log_server_requests=1 # undef for no logging

ssh_knock=nnnnn # some random port number

# ssh login
# ``````````
# port-knocking plus limited login attempts, idea is that accidental
# opening of the port due to scanner activity or whatever still
# leaves the attacker with limited number of attempts to guess a
# valid login username and password

report " ssh "
iptables -N sshlogin
iptables -A sshlogin -p tcp -m conntrack --ctstate NEW --dport ssh \
-m recent --rsource --rcheck --name ssh \
--seconds $server_ssh_limit_secs \
--hitcount $server_ssh_limit_hits -j gotojail

iptables -A sshlogin -p tcp -m conntrack --ctstate NEW --dport ssh \
-m recent --rsource --update --name ssh \
--seconds $server_ssh_warn_secs \
--hitcount $server_ssh_warn_hits -j serv_rej

[ $log_server_requests ] && iptables -A sshlogin -p tcp \
-m conntrack --ctstate NEW \
--dport ssh -j $do_log "JLE:inpkay ssh "

iptables -A sshlogin -p tcp -m conntrack --ctstate NEW --dport ssh \
-m recent --rsource --set --name ssh -j ACCEPT

# simple port-knocking for access to ssh server, remote user telnets
# to a port nominated for knocking, drop unreported
iptables -A INPUT -p tcp -m conntrack --ctstate NEW \
--dport $ssh_knock -m recent --rsource \
--set --name knock -j DROP

# only allow possible ssh entry if they knocked within last 60 secs
iptables -A INPUT -p tcp -m conntrack --ctstate NEW --dport ssh \
-m recent --rsource --rcheck --name knock \
--seconds 60 -j sshlogin

To login, one first telnets to the nominated knock port:

grant@greg:~> telnet bugsplatter.id.au xxxxx

Then one does the ssh login as normal:

grant@greg:~> ssh grant@bugsplatter.id.au
grant@bugsplatter.id.au's password:
Last login: Sat Oct 4 18:59:12 2008 from sillywin.mire.mine.nu

Opportunities are usually disguised as hard
work, so most people don't recognize them.

BTW I like slackware's signon message, the remote machine I'm playing
with is so ordinary (opensuse-11):

Last login: Sat Oct 4 16:39:20 2008 from 123-2-77-8.static.dsl.dodo.com.au
Have a lot of fun...

- - -
Timeout: advantage of running a dual screen system is having the logs
'tail'd on other screen, activity catches attention out of the corner
of my eye, I see this just now:

07:27:21 JLE:inpkay http TCP 1811 -> 80 (http) TTL=103 SYN ->ppp0
a75-93.adsl.paltel.net (PS:Palestinian Territory, Occupied) -> deltree

Never thought I'd see a hit from there!?
- - -

So the next thing is to add rsa public key stuff so passwords become
optional or may even be disabled.

I googled for an example of using ssh-keygen 'cos the man page wasn't
clear (hey Mike!) and first page I visited was:


which showed a worked example for FreeBSD, close enough when using these
standard tools

So here I login to my friend's machine, generate the rsa key, scp the new
key to my machine then login to my machine from friend's machine, confused?

Here's how it happened:

grant@greg:~> ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/grant/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/grant/.ssh/id_rsa.
Your public key has been saved in /home/grant/.ssh/id_rsa.pub.
The key fingerprint is:
50:c5:92:bd:bd:88:e1:6f:1f:ed:11:06:b8:3f:a0:f4 grant@greg
grant@greg:~> telnet bugsplatter.id.au xxxxx # port knock
grant@greg:~> scp .ssh/id_rsa.pub grant@bugsplatter.id.au:.ssh/authorized_keys2
grant@bugsplatter.id.au's password:
id_rsa.pub 100% 392 0.4KB/s 00:00
grant@greg:~> ssh grant@bugsplatter.id.au # no response,
grant@greg:~> telnet bugsplatter.id.au xxxxx # port knock again
grant@greg:~> ssh grant@bugsplatter.id.au # login
Enter passphrase for key '/home/grant/.ssh/id_rsa':
Last login: Sun Oct 5 07:25:01 2008 from xxxxxxxxxxxxxxx.xxxxxx.xxxx.com.au

When we are planning for posterity, we ought to remember that virtue is
not hereditary.
-- Thomas Paine

grant@deltree:~$ logout

Note: my friend chose the other ssh option of using a non-standard port
number -- we'll see who gets r00ted first

Hoping this worked example sheds some light on the topic and if there
are any holes in my logic or examples, others will jump in and correct
me, thanks.

Iptables ipt_recent
I've read some reports that say the files holding hits
(/proc/net/ipt_recent/*) may fill up and thus prevent new entries from
being added. To avoid this and also to unclutter a firewall activity
display, I'm running a timebased (root cron job) service to remove stale
entries from the ipt_recent filesystem. This has been in operation for
some weeks and seems to be doing the right thing.

Another aspect mentioned in Robby's notes and handled here is the issue
of losing jailed IPs on restarting the firewall. A solution for that is
to copy jailed IPs to another file, and have the firewall reload the
jailed IPs on restart -- in this way restarting the firewall does not
lose jailed IPs, but the hitcount history is lost, thus jailed IPs get
to serve their 'sentence' again

The complete firewall ruleset is not yet published (under test at the
moment), but I can post the timebase script if there's interest. There's
also a .cgi which transfers firewall jailed status to a web page:


Currently IPs are held for a minimum one hour, and the timebase cron job
runs five times per hour to remove stale entries. A common firewall.conf
file holds the settings for this and the rc.firewall script.

Eventually these scripts and concepts will appear on the firewall page.