Putting sshd on a higher port
ssh is the standard protocol used when you want to access a shell on a
remote machine. By remote, I mean you are not sitting in front of the console.
Remote may mean next door, the next building, or the next continent. ssh
is a secure method for talking to that computer.
ssh is also a common attack vector. There are many scripts that can be
used to attempt to break in via ssh. As a general rule, I greatly
restrict access via my packet filter rules. I allow incoming connections only
from my other servers, my home, and a few trusted hosts and friends.
At worst, this reduces the number of spurious log-in attempts recorded in my logs.
At best, it reduces the risk.
However, there are some instances when I am not at one of these trusted locations
and I still need to ssh in. This is why I also run sshd, unfiltered,
on a high random port. This article shows the configuration I used to
achieve this.
NOTE: If all you want is sshd listening on another port, the configuration below
is overkill. If that case, you probabaly want something like this:
# grep ListenAddress /etc/ssh/sshd_config ListenAddress 10.2.3.4:22 ListenAddress 10.2.3.4:44444
However, if you want the second sshd to have a different configuration, such as
only permit public key authorization, then this article is for you.
The startup script
Here is the startup script from /usr/local/etc/rc.d:
#!/bin/sh # # $NetBSD: sshd,v 1.18 2002/04/29 08:23:34 lukem Exp $ # $FreeBSD: src/etc/rc.d/sshd,v 1.8 2005/01/16 03:12:03 obrien Exp $ # # PROVIDE: sshd # REQUIRE: LOGIN cleanvar . /etc/rc.subr name="sshd_higher_port" rcvar=`set_rcvar` command="/usr/sbin/sshd" keygen_cmd="sshd_keygen" start_precmd="sshd_precmd" pidfile="/var/run/${name}.pid" extra_commands="keygen reload" timeout=300 user_reseed() { ( seeded=`sysctl -n kern.random.sys.seeded 2>/dev/null` if [ "x${seeded}" != "x" ] && [ ${seeded} -eq 0 ] ; then warn "Setting entropy source to blocking mode." echo "====================================================" echo "Type a full screenful of random junk to unblock" echo "it and remember to finish with. This will" echo "timeout in ${timeout} seconds, but waiting for" echo "the timeout without typing junk may make the" echo "entropy source deliver predictable output." echo "" echo "Just hit for fast+insecure startup." echo "====================================================" sysctl kern.random.sys.seeded=0 2>/dev/null read -t ${timeout} junk echo "${junk}" `sysctl -a` `date` > /dev/random fi ) } sshd_keygen() { ( umask 022 # Can't do anything if ssh is not installed [ -x /usr/bin/ssh-keygen ] || { warn "/usr/bin/ssh-keygen does not exist." return 1 } if [ -f /etc/ssh/ssh_host_key ]; then echo "You already have an RSA host key" \ "in /etc/ssh/ssh_host_key" echo "Skipping protocol version 1 RSA Key Generation" else /usr/bin/ssh-keygen -t rsa1 -b 1024 \ -f /etc/ssh/ssh_host_key -N '' fi if [ -f /etc/ssh/ssh_host_dsa_key ]; then echo "You already have a DSA host key" \ "in /etc/ssh/ssh_host_dsa_key" echo "Skipping protocol version 2 DSA Key Generation" else /usr/bin/ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key -N '' fi if [ -f /etc/ssh/ssh_host_rsa_key ]; then echo "You already have a RSA host key" \ "in /etc/ssh/ssh_host_rsa_key" echo "Skipping protocol version 2 RSA Key Generation" else /usr/bin/ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N '' fi ) } sshd_precmd() { if [ ! -f /etc/ssh/ssh_host_key -o \ ! -f /etc/ssh/ssh_host_dsa_key -o \ ! -f /etc/ssh/ssh_host_rsa_key ]; then user_reseed run_rc_command keygen fi } load_rc_config $name run_rc_command "$1"
This script is based entirely upon /etc/rc.d/sshd. The diff is here:
12c12 < name="sshd" --- > name="sshd_higher_port" 14c14 < command="/usr/sbin/${name}" --- > command="/usr/sbin/sshd"
The configuration
The truth is, I set up this daemon several months before I wrote this article.
When it came time to write, it took me about 5 minutes to figure out where
the configuration was done. Finally, I found it in /etc/rc.conf:
sshd_enable="YES" sshd_higher_port_enable="YES" sshd_higher_port_flags="-p 57328 -f /usr/local/etc/sshd_config_higher_port" sshd_higher_port_program="/usr/sbin/sshd"
NOTE: you probably already have sshd_enable=”YES” set. It is best not to
duplicate settings.
NOTE: My testing shows you can omit the sshd_higher_port_program directive from
/etc/rc.conf.
If memory serves, it took some time to figure out how to configure this properly
and get it to run at boot time. I will explain the flag items:
- -p 57328 : This is the port upon which the second ssh daemon will listen.
No, this is not the port I am using. - -f /usr/local/etc/sshd_config_higher_port : this is the configuration file
used by the second daemon
That second configuration file is pretty much identical to the original
/etc/ssh/sshd_config. Here are the items I added to the new configuration file
after copying it from the original:
Port 57328 PasswordAuthentication no PidFile /var/run/sshd_higher_port.pid ChallengeResponseAuthentication no
Looking at this, I see that the port number is specified in both /etc/rc.conf
and in the configuration file. I’ve just implemented this on another server,
without the /etc/rc.conf setting for the port, and it works fine.
The key point to this configuration is the second line. That completely disables
login by password.
This means that someone must have an ssh key to login. This setting completely
eliminates any dictionary attacks (e.g. password guessing). The attacker
*must* have your private ssh key in order to get in.
Firewall rules
Here is the new PF firewall rule I added:
pass in quick proto tcp from any to $MYSELF port 57328 flags S/SA synproxy state
Starting the daemon
This command started the daemon:
# /usr/local/etc/rc.d/sshd_higher_port start Starting sshd_higher_port.
And here is what it looks like when running:
# ps auwx | grep sshd root 871 0.0 0.2 3520 1836 ?? Is 9Nov06 0:02.59 /usr/sbin/sshd root 89864 0.0 0.3 6244 2840 ?? Is 7:40AM 0:00.05 sshd: dan [priv] (sshd) dan 89870 0.0 0.3 6224 2852 ?? S 7:41AM 0:00.36 sshd: dan@ttyp0 (sshd) root 90687 0.0 0.3 3520 2584 ?? Ss 7:59AM 0:00.00 /usr/sbin/sshd -f /usr/local/etc/sshd_config_higher_port #
Testing the theory
To test that passwords are not accepted, I did this:
$ ssh -p 57328 myserver.example.org Enter passphrase for key '/home/dan/.ssh/id_dsa': Permission denied (publickey).
At the prompt, I pressed enter. If passwords are accepted, I would have been
presented with a password prompt, like this:
$ ssh xeon Enter passphrase for key '/home/dan/.ssh/id_dsa': Password:
Convenience versus risk
What I have done is a trade off between convenience and risk. It is more
convenient to use the regular port (22). But it is annoying to get all the
failed login attempts in my logs. The higher port is still wide open, but
it is much less likely to get any login attempts.
What would you prefer to do? Please leave your comments.
I use higher ports for my sshds since I first had a "public server".
I was scanned and brute forced all the time…
Now the auth_log is just looking good :]
Higher port + auth key only = good security (the only matter is to remind which server has which port)
[%sig%]
dobermann wrote:
> I was scanned and brute forced all the time…
Does this mean the attacks were successful?
—
The Man Behind The Curtain
Nope 🙂
I just watched my logs grow and grow with hacking attempts… but never been owned.
[%sig%]
Not really a fan of putting sshd on a higher port, I only use public key
auth, combined with the AllowUsers directive in sshd_config, that’ll
keep most bad guys out. I carry my public key and putty on my usb
stick so I can always connect. Random high ports have a nasty tendancy
to be blocked when you’re out and about.
I still get a lot of bruteforce attempts (that will fail) however I
mitigate that with pf:
pass in log on $ext_if proto tcp from any to $ssh_server port ssh flags S/SA keep state \
(max-src-conn 10, max-src-conn-rate 5/3, overload <bruteforce> flush global)
The bruteforce table is purged with the expiretable
(sysutils/expiretable) utility in cron.
[%sig%]
Well, it’s correct with the high ports availability from various (mainly corporate) networks.
Hm, one solution, I am currently playing with, is having OpenVPN listening on port 22. But it’s just an idea..
Putting sshd on a "higher port" is security through obscurity.
Brute-force bots try a limited set of usernames, and should therefore in practice not be feared by a sensible admin.
Through this, we can conclude that if someone actually, really, wanted to break in to *your* server, changing the port of your sshd is not going to hinder them one least bit. The first thing a hacker ever does to a server is to probe for open ports using nmap.
However, I do believe that only allowing ssh key logins is a good part of the solution: Passwords are insecure.
For everybody else, I would suggest to either disable password authentication on their sshd, or download DenyHosts (It’s in the ports, and on denyhosts.sf.net)
alive wrote:
> Putting sshd on a "higher port" is security through obscurity.
You say that as if it is a bad thing.
> Brute-force bots try a limited set of usernames, and should
> therefore in practice not be feared by a sensible admin.
>
> Through this, we can conclude that if someone actually, really,
> wanted to break in to *your* server, changing the port of your
> sshd is not going to hinder them one least bit. The first thing
> a hacker ever does to a server is to probe for open ports using
> nmap.
None of which I contradict.
ssh on port 22 is tightly restricted with respect to who can talk to it. ssh on the other port is not. Anyone can talk to it. By moving it to another port, the number of door-knockers has dropped considerably.
This isn’t to stop or deter the determined. It is to get rid of the script kiddies.
> However, I do believe that only allowing ssh key logins is a
> good part of the solution: Passwords are insecure.
Gee, thanks! I’m glad I wasn’t wasting my time. 😉
> For everybody else, I would suggest to either disable password
> authentication on their sshd, or download DenyHosts (It’s in
> the ports, and on denyhosts.sf.net)
I would welcome an article from you on DenyHosts.
—
The Man Behind The Curtain
Dan wrote:
> alive wrote:
>
> > Putting sshd on a "higher port" is security through
> obscurity.
>
> You say that as if it is a bad thing.
Well, yes, I do indeed believe that security through obscurity is a bad thing, as it unwillingly "relaxes" a person, either we want it or not, into thinking that the obscurity-hack has somehow helped an issue of security, when in fact it hasn’t.
>
> > Brute-force bots try a limited set of usernames, and should
> > therefore in practice not be feared by a sensible admin.
> >
> > Through this, we can conclude that if someone actually,
> really,
> > wanted to break in to *your* server, changing the port of
> your
> > sshd is not going to hinder them one least bit. The first
> thing
> > a hacker ever does to a server is to probe for open ports
> using
> > nmap.
>
> None of which I contradict.
>
> ssh on port 22 is tightly restricted with respect to who can
> talk to it. ssh on the other port is not. Anyone can talk to
> it. By moving it to another port, the number of door-knockers
> has dropped considerably.
>
> This isn’t to stop or deter the determined. It is to get rid
> of the script kiddies.
>
> > However, I do believe that only allowing ssh key logins is a
> > good part of the solution: Passwords are insecure.
>
> Gee, thanks! I’m glad I wasn’t wasting my time. 😉
I apologize if my previous post came of as if I was criticizing your article, because it wasn’t. The only reason I commented it is because articles about security make me feel warm and fuzzy inside 🙂
>
> > For everybody else, I would suggest to either disable
> password
> > authentication on their sshd, or download DenyHosts (It’s in
> > the ports, and on denyhosts.sf.net)
>
> I would welcome an article from you on DenyHosts.
I think I might just do that, then 🙂
alive wrote:
> Dan wrote:
>
> > alive wrote:
> >
> > > Putting sshd on a "higher port" is security through
> > obscurity.
> >
> > You say that as if it is a bad thing.
>
> Well, yes, I do indeed believe that security through obscurity
> is a bad thing, as it unwillingly "relaxes" a person, either we
> want it or not, into thinking that the obscurity-hack has
> somehow helped an issue of security, when in fact it hasn’t.
My point of putting sshd on a higher port was for my convenience. ssh is still running on the lower port, but strongly filtered.
The sshd on the higher port REQUIRES a key-based login. The lower one does not. AFAIK, it is not possible to do both with a single instance of the ssh daemon.
I’m not quite sure yet where I am attempting to obscure things.
—
The Man Behind The Curtain
Hi Dan,
I’ve finally written the article.
<http://nixy.dk/2007/10/12/denyhosts-on-freebsd-62/>
Thanks.
I suggest submitting a story to <http://bsdnews.com>
—
The Man Behind The Curtain
I have a machine that I need to access at any given time, and I’m never quite sure from where I’m going to access it because I could be on the road when I have to ssh in.
I got sick of watching people try to hack my ssh password, so I found sshblack (http://www.pettingers.org/code/sshblack.html) which is VERY cool and which I’ve "adapted" to vsftp as well.
It monitors my ssh and/or vsftp logfiles for "Invalid" entries and after a certain number of tries, it just drops the packets from that IP for three days. You can set an IP whitelist as well. SSH I give them two tries. VSFTP I give them eight (since I often have to give out VSFTP accounts to end-users and I know they’re more likely to mess up a password than I am).
Just another way to tackle the problem. Disable root access of course, then they only get one chance every three days to brute force your passwords. Not a chance in the world they’ll be able to do that, and they’ll grow frustrated long before they could ever do it.