OS X, sshd, and alternate ports
Easy Mode
Easiest thing to do is just disable the remote login service if you don’t need it. By disabling the service in the Sharing preference pane of System Preferences, the service isn’t available, and the problem is solved.
Filtering
You could (and should) have a local packet filter (firewall) policy for your workstation.
You can do a very basic “on/off” configuration in System Preferences but if you have remote login enabled, the firewall policy will merely allow it, and that isn’t going to help you much, is it?
On Snow Leopard systems this will use
1 |
ipfw |
, and you can use freeware like WaterRoof to configure a basic firewall policy that will allow certain networks to access your ssh service (or any other services running on that system). Lion and Mountain Lion prefer a different technology called
1 |
pf |
1. The developer of WaterRoof has another utility for configuring a
1 |
pf |
firewall, called IceFloor. I know what you’re thinking, and no; I have no idea where he comes up with these names. I’m sure there is a naming convention or theme associated with them, but for the life of me I can’t figure it out. It’s just one of those inside jokes that programmers like to have. WaterRoof? IceFloor? I just want to make a firewall, not spend the next eight hours on Wikipedia.
I’m not a nerd
In the event of being confused by this, try the third option, NoobProof, which promises to be more helpful.
What should I allow in?
As mentioned earlier, a firewall that just allows everything isn’t helpful, so you’ll need to limit access to services based on networks and hosts you want to allow to access them. If you have access to a VPN service that will be perfect, since using the VPN will require some sort of authorization that may not be required elsewhere in your building, network, or campus. Every user of a VPN service maintained by your organization will have some sort of audit trail and that will really raise the opportunity cost of getting access to that service.
the Lazy Approach™
The other option is running your
1 |
sshd |
on a non-standard port. People do this all the time for remote access protocols like Microsoft Remote Desktop Protocol (RDP), VNC, and
1 |
ssh |
. It doesn’t really buy you anything in terms of real protection, since anyone taking the time to assess the system remotely will probably find it if they’re interested in targeting that host directly. The one thing it can help with however is those brute-force attempts to access your system, because most of them will go after systems listening on tcp ports
1 |
22 |
and
1 |
2222 |
.
Scanning entire systems can take a while, so the scripts and code used for discovering and attempting to gain access via brute force focus their efforts on traditional/typical ports mainly because it’s faster. Best practice is to use a firewall policy that limits access to services to networks and hosts you can reasonably trust in conjunction with an alternate port and some configuration tweaks.
Show me Lazy
This isn’t a great way to do this, because it can possibly be stomped on after a software update to the OS.
/etc/services
You start by editting /etc/services with your favorite text editor. This will require you to be an administrator.
You want to find a couple of lines next to each other that look like this:
1 |
1 2 |
<span class="nb">ssh</span> <span class="m">22</span><span class="sx">/tcp</span> # SSH Remote Login Protocol <span class="nb">ssh</span> <span class="m">22</span><span class="sx">/udp</span> # SSH Remote Login Protocol |
When a
1 |
launchd |
job, or tools like
1 |
netstat |
need to look up a port and identify it by service, they consult that table
1 |
/etc/services |
. It’s just a table of over ten thousand known services, protocols, and other assorted nonsense.
Go ahead and add two new lines under those two and have it read:
1 |
1 2 |
<span class="nb">sshalt</span> <span class="m">22744</span><span class="sx">/tcp</span> # omg alternate SSH port <span class="nb">sshalt</span> <span class="m">22744</span><span class="sx">/udp</span> # omg alternate SSH port |
Obviously the #comment is optional.
It can be any port number you don’t want to have any other services listening on, in general it should be over port 1024 and under 65535. I used 22744 because 22 is the standard port for
1 |
ssh |
, and “ssh” on a phone keypad is “744”.
to the launchd!
Okay we have an alternate service port set aside, so now we go to
1 |
/System/Library/LaunchdDaemons |
and edit the file (as root)
1 |
ssh.plist |
. The plist we’re editting is what handles
1 |
sshd |
, and we’re going to tell it that instead of using port 22 like it usually does, we’d rather use 22744.
You will see a key called
1 |
SockServiceName |
.
1 |
1 2 |
<span class="nt"><key></span>SockServiceName<span class="nt"></key></span> <span class="nt"><string></span>ssh<span class="nt"></string></span> |
This is telling launchd that it wants to put the
1 |
sshd |
process on port 22 by virtue of it using a SockServiceName of
1 |
ssh |
. Change
1 |
1 |
<span class="nt"><string></span>ssh<span class="nt"></string></span> |
to read
1 |
1 |
<span class="nt"><string></span>sshalt<span class="nt"></string></span> |
and save the file.
We can now reload the launchd job by doing:
1 |
1 |
sudo launchctl unload /System/Library/LaunchDaemons/ssh.plist <span class="o">&&</span> sudo launchctl load /System/Library/LaunchDaemons/ssh.plist |
which will unload (and stop) the ssh service, and then load the ssh service’s launchd job and the alternate port. You can confirm via netstat:
1 |
1 |
netstat -aln | grep -i LISTEN | grep 22744 |
which will show you if any service is listening on port 22744. You can also just use
1 |
netcat |
or
1 |
telnet |
, e.g.
1 |
1 |
nc localhost 22744 |
and that will return a banner for the listening service, in this case it will say something like
1 |
SSH-2.0-OpenSSH_5.9 |
.
This will persist across reboots, but like I said, a software update may change /etc/services or the ssh.plist. Should either of those get stomped, you’ll need to do it again. I don’t know how often it occurs, I’m more likely to rebuild or install an OS from scratch than I am to install a series of updates.
What’s the good way?
The good way is to create a new launchd plist that runs ssh on a high port instead of changing the one Apple gives you. If you want to go that route there are better resources to teach you.
Configuration Tweaks
You should edit
1 |
/etc/sshd_config |
as root and make a few recommended changes. You’ll see a directive that reads
1 |
Port 22 |
and you’ll want to say “well golly, why didn’t we just change it there?” and this is where I give you a finger-wag and tell you something about launchd, Apple’s launch services, and a bunch of stuff you don’t care about.
Suffice to say, changing the port number in this file doesn’t do anything on Mac OS X unless you also change how sshd starts up entirely and if you were going to do that, you wouldn’t have any interest in reading past the first paragraph of this document anyway.
We’ll be looking at these lines:
1 |
1 2 3 4 5 |
<span class="c">#LoginGraceTime 2m</span> <span class="c">#PermitRootLogin yes</span> <span class="c">#StrictModes yes</span> <span class="c">#MaxAuthTries 6</span> <span class="c">#MaxSessions 10</span> |
and we’ll change them to:
1 |
1 2 3 4 5 |
<span class="c">#LoginGraceTime 2m</span> PermitRootLogin no <span class="c">#StrictModes yes</span> MaxAuthTries 4 <span class="c">#MaxSessions 10</span> |
If you want to be extra awesome, you’ll also disable PasswordAuthentication and require public keys for authorization, but we’ll save that for another day.
-
1ipfw1pf1pf1ipfw1ipfw