Locking your ssh port with fwknop
UPDATED Thanks to sk@0x in the comments for the bits I missed
In my last post I described how I decrypt my home server remotely with ssh. Today I would like to share how I like to lock/unlock my ssh port with an encrypted port knocking implementation fwknop
The issue with port knocking
On the face of it port knocking looks like a good idea. Lock down your ssh port until you need it, avoiding any zero day issues with the ssh protocols. The problem is this, port knocking is sent in the clear over the network. Anyone looking can see your knock "code", much like if you had a secret door knock some one around the corner could heard the pattern of your knocks.
This is where fwknop comes in, it's SPA (Single Packet Authorization) cannot be re-sent it is one time only. Not to mention it's faster as you are only sending the one packet.
The main issue I had with fwknop is by default you have to specify the source IP address you want to be able to access your server. I found this to be quite painful to set-up, so I found a simple way around the issue.
Note: this only works if you are blocking ports by default. I use UFW to simplify that process. See this Digital Ocean tutorial on the basics of UFW
Server Side:
In Debian based systems fwknop is split into fwknop-client and fwknop-server. We will want both of them
sudo apt install fwknop-server fwknop-client
First we need to enable the fwknop server in the /etc/default/fwknop-server
file. by changing the line START_DAEMON="no"
# Default settings for fwknopd. # Change it to yes if you would like fwknopd to be started at boot time. # # START_DAEMON="no" START_DAEMON="yes" # Add any options you would like to pass to the daemon when started # For example if you would like to add an override file for your setup, this # can be achieved this way: # # DAEMON_ARGS="--override-config /root/fwknopd.override.conf" DAEMON_ARGS=""
Next we need to set up the basic config rules on the server found in /etc/fwknop/fwknopd.conf
Debian and Ubuntu have changed the default interface name from eth0 to enp3s0 so we have to set that. We can also change the listening port here.
PCAP_INTF enp3s0;
# change your port to your desired listening port.
PCAP_FILTER udp port 62201;
Now we use fwknop to generate our key's. We could use GpG here, but I didn't feel the extra encryption brings much to the table as we are only opening the ssh port and I have public key authentication and TOTP enabled.
fwknop -A tcp/22 -D example.tld --key-gen --use-hmac --save-rc-stanza
You can now find the KEY_BASE64 and HMAC_KEY_BASE64 in ~/.fwknoprc
we will need these for the /etc/fwknop/access.conf
file and the client.
In the /etc/fwknop/access.conf
file. Note: I substituted the iptable commands for ufw commands. We don't have to worry about our ssh session being kicked as once it's connected the CMD_CYCLE_CLOSE (at least with ufw) won't close the existing connection.
SOURCE ANY # Limit the Ports able to be opened OPEN_PORTS tcp/22 # Keys from ~/.fwknoprc KEY_BASE64 [...] HMAC_KEY_BASE64 [...] # Optionally use iptables # CMD_CYCLE_OPEN /sbin/iptables -A INPUT -p $PROTO --dport $PORT -j ACCEPT # CMD_CYCLE_CLOSE iptables -D INPUT -p $PROTO --dport $PORT -j ACCEPT CMD_CYCLE_OPEN /usr/sbin/ufw allow $PORT CMD_CYCLE_CLOSE /usr/sbin/ufw delete allow $PORT # Default cycle time Mandatory for CMD_CYCLE_OPEN/CLOSE CMD_CYCLE_TIMER 180
A word of warning, fwknop can run arbitrary commands if ENABLE_CMD_EXEC
is enabled. I don't see why you would ever really want to do that. You can also run any bash script from CMD_CYCLE_OPEN
and CMD_CYCLE_CLOSE
with the optional variables $PROTO
, $PORT
and $SRC
. You can potentially get yourself in a lot of trouble if you do this so proceed with caution.
Now we need to setup the systemd file /etc/systemd/system/fwknop-server.service
. Note: on a ubuntu (18.04.4 LTS) install I had to create the folder /var/fwknop/
.
NOTE: sk@0x in the comments also mentioned the PID file needs to be in /run/fwknop
dir in Ubuntu 20.04
[Unit] Description=Firewall Knock Operator Daemon After=network-online.target [Service] Type=forking PIDFile=/var/fwknop/fwknopd.pid ExecStart=/usr/sbin/fwknopd ExecReload=/bin/kill -HUP $MAINPID [Install] WantedBy=multi-user.target
Then we just enable and start the service
sudo systemctl enable fwknop-server.service && sudo systemctl start fwknop-server.service
Running sudo systemctl status fwknop-server.service
should now show you the service is active Active: active (running)
. Currently if you have already allowed port 22 with ufw it will stay open until the first time you cycle fwknop with a client.
Client Side:
You have three options fwknop-client, fwknop2 on android - [F-Droid] - [Google play] or fwknop-gui available on Windows, Mac and Linux
In fwknop2 and fwknop-gui:
- KEY_BASE64 -> Rijndael Key
- Key Is Base 64 - Checkbox below key entry
- HMAC_KEY_BASE64 -> HMAC Key
- HMAC Is Base 64 - Checkbox below key entry
- Allow IP - This can be anything as we are ignoring this setting
- Access Ports: tcp/22
The Firewall timeout is in seconds and can be anything as long as it's long enough for you to authenticate. Remember if you have the same set-up as I do, you wont get kicked after the timeout.
And there we go a nice locked ssh port. You will now have to send a SLA to your server prior to connecting with your ssh client.