Remember when you were a little kid and had a clubhouse? Did you let someone in only if they knew "the secret knock"? Lately people have talked about various implementations for doing that with ssh. The technique, called "Port Knocking" permits SSH if someone has touched various ports recently. For example, someone has to ping the machine, then telnet to port 1234, then for the next 2 minutes they can ssh in.
This can be difficult to implement securely, as this video demonstrates: http://www.youtube.com/watch?v=9IrCgCKrv8U
IBM's Developerworks recently posted an article about tightening SSH security. The topic also came up on the mailing list for the New Jersey LOPSA chapter.
I had an idea that I haven't seen published before.
I have a Unix (FreeBSD 8.0) system that is live on the open internet and it is so locked down that I don't permit passwords. To SSH to the machine you have to pre-arranged to set up SSH keys for "passwordless" connections. However, it does not run a firewall because it is literally running with no ports open (except ssh). There is nothing to firewall.
Problem: What if I am stuck and need to log in remotely with a password?
Most of the portknocking techniques I've seen leverage the firewall running on the system. I didn't want to enable a firewall, so I came up with this.
Idea #1: A CGI script to grant access.
Connect to a particular URL, it runs SSH on port 9999 with a special configuration file that permits passwords:
/etc/ssh/sshd_config:
PasswordAuthentication no
PermitEmptyPasswords no
PermitRootLogin no
UsePAM no
/etc/ssh/sshd_config-port9999:
Port 9999
AllowAgentForwarding no
AllowTcpForwarding no
GatewayPorts no
LoginGraceTime 30
MaxAuthTries 3
X11Forwarding no
PermitTunnel no
PasswordAuthentication no
PermitEmptyPasswords no
PermitRootLogin yes
UsePAM yes
Translation: If someone is going to get special access on port 9999, they can't use it to set up tunnels or gateways. It is just for either quick access; enough to fix your SSH keys.
The CGI script is essentially runs:
/usr/sbin/sshd -p 9999 -d
Which permits a single login session on port 9999.
Try #2:
FreeBSD defaults to an inetd that uses Tcpwrappers.
So, try #2 was similar to #1 but appends info to /etc/hosts.allow so that the person has to come from the same IP address as the recent web connection. The problem with that is sometimes people connect to the web via proxies, and adding the proxy to the hosts.allow list isn't going to help.
Try #3:
We all know that you can't run two daemons on the same port number, right? Wrong.
You can have multiple daemons listening on the same port number if they are listening on different interfaces. If two conflict, the connection goes to the "most specific" listening daemon.
What does that mean? That means you can have sshd with one configuration listening on port .22 (any interface, port 22) and another listening on 10.10.10.10.22 (port 22 of the interface configured for address 10.10.10.10). But I only have one interface, you say? I disagree. You have 127.0.0.1 plus your primary IP address, plus any IPv6 addresses. Heck, even if you really only had one IP address, "" and a specific address can both be listening to port 22 at the same time.
That's what the "*" on "netstat -l" means. "Any interface."
So, back to our port knocking configuration.
The normal port 22 sshd runs with a configuration that disables all passwords (only permits SSH keys).
/etc/ssh/sshd_config:
Port 22
ListenAddress 0.0.0.0
ListenAddress ::
PAMAuthenticationViaKBDInt no
PasswordAuthentication no
PermitEmptyPasswords no
PermitRootLogin no
UsePAM no
And the CGI script enables a sshd with this configuration:
/etc/ssh/sshd_config-permit-passwords:
Port 22
ListenAddress 64.23.178.12
ListenAddress fe80::5154:ff:fe25:1234
PAMAuthenticationViaKBDInt no
PasswordAuthentication no
PermitEmptyPasswords no
PermitRootLogin no
UsePAM yes
The wrapper simply runs:
/usr/sbin/sshd -d -f /etc/ssh/sshd_config-permit-passwords
That's all there is to it!