Putting sshd on a higher port

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.

12 thoughts on “Putting sshd on a higher port”

  1. 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%]

    1. 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%]

    2. 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..

  2. 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)

    1. 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

      1. 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 🙂

        1. 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

  3. 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.

Leave a Comment

Scroll to Top