How To Use fwknop to Enable Single Packet Authentication on Ubuntu 12.04
This article covers a version of Ubuntu that is no longer supported. If you are currently operate a server running Ubuntu 12.04, we highly recommend upgrading or migrating to a supported version of Ubuntu:
- Upgrade to Ubuntu 14.04.
- Upgrade from Ubuntu 14.04 to Ubuntu 16.04
- Migrate the server data to a supported version
Reason: Ubuntu 12.04 reached end of life (EOL) on April 28, 2017 and no longer receives security patches or updates. This guide is no longer maintained.
This guide might still be useful as a reference, but may not work on other Ubuntu releases. If available, we strongly recommend using a guide written for the version of Ubuntu you are using. You can use the search functionality at the top of the page to find a more recent version.
Exposing services to the internet is always a risk but is often the entire reason behind having a server in the first place. Any open port or exposed service can be subjected to a quite a lot of probing and access attempts by malicious users or automated scripts.
While some services must remain accessible because they are meant to be publicly consumable (like a web server hosting a site), other services are only meant to be used by one or a few authorized users and no one else (like SSH). In the best case scenario, these services would be well secured, but also only accessible when we actually want to use them.
Single Packet Authentication is a method of allowing the firewall to block access to a service until a specialized, encrypted packet is sent to a listening service. When the service validates this packet, it promptly modifies the firewall rules to expose the needed port.
A tool called
fwknop, which stands for Firewall Knock Operator, can be used to interpret these packets and modify firewall rules. In this guide, we will be configuring a fwknop server and client on Ubuntu 12.04 systems. This will enable us to shield our SSH server until specifically requested.
Install the fwknop Server on One VPS
Our configuration will be assuming that you have two Ubuntu 12.04 VPS instances. We will also assume that you have a domain name pointed at the server VPS, although utilizing IP addresses should work too. If you need help configuring domain names for DigitalOcean, click here.
On the server machine, install the fwknop server component through apt-get:
sudo apt-get update sudo apt-get install fwknop-server
During installation, you will be asked a few questions to configure the service and associated components.
The first component it has questions about is postfix. Select "Internet Site" and then fill out your domain name in the next section.
Next, it asks if you want fwknop to protect the SSH port by default, using a less secure key system. Select "yes" so that the service configures some parameters. We will change over to the more secure settings a bit later.
Select the interface that you would like to guard. Usually this will be "eth0". Finally, choose an encryption key to use for the cipher. Since we will be changing this later, it doesn't matter what you put. Just make sure it is one word, or else the configuration will error out.
Install the fwknop Client on the Other VPS
On the VPS that we will be using as the client, we need to install the client component. This is the part of our system that will craft our encrypted packet to send to the other machine:
sudo apt-get update sudo apt-get install fwknop-client
The client portion does not require any configuration at this point.
Configure GPG Keys
We will be using GPG keys to provide the authentication for our packet transfer. Because this is an asymmetric cipher, we need to do these steps on both the client and server machine. GPG should be installed by default.
On each machine, generate a key by typing:
This will ask you some information. For most of the questions, you are safe to accept the default.
Provide and confirm a passphrase. After that, it will take some time to generate enough random information to create a key.
Next, we need to write down or otherwise copy the public IDs for each of the keys we generated. You can get these by typing:
/home/test/.gnupg/pubring.gpg ------------------------------- pub 2048R/11111111 2014-01-09 uid client
sub 2048R/C1C26BA6 2014-01-09
The part highlighted above is the part you need for each of the keys. Copy that somewhere and label as either the server or client component.
SERVER: 11111111 CLIENT: 99999999
Now, on the client machine, make a
client.asc ascii copy of the key by exporting it to a file. You need to reference the key ID you just copied for the client:
gpg -a --export 99999999 > client.asc
On the server machine, do a similar procedure referencing the server key ID in order to produce a
gpg -a --export 11111111 > server.asc
Transfer Keys Between Computers
Now, we need to transfer the keys between the machines so that each machine has a copy of both.
We will do this using the
scp tool from the client machine.
On the client machine, copy the
client.asc file to the server by using a command like this:
scp client.asc server_domain_or_ip:/path/to/user/home/directory
This will ask you to authenticate using your normal account password for the server. Modify the path to reflect the home folder of your regular user.
Next, we will grab the
server.asc file using a similar command:
scp server_domain_or_ip:/path/to/user/home/directory/server.asc .
You should now have both files on each machine.
Import and Sign Keys
Now that we have access to the other key on each machine, we can import the opposite (server key on the client, and client key on the server) key into the local GPG database.
On the server machine, type:
gpg --import client.asc
On the client machine, type:
gpg --import server.asc
Now that each computer has the opposite key in its database, you can sign each key to validate them.
On each machine type this command, referencing the IDs of the keys you just imported. In our example, on the client machine we would type:
gpg --edit-key 11111111
And on the server machine, we would type:
gpg --edit-key 99999999
You will be given a GPG prompt. We want to sign the keys, so type:
You will be prompted whether you are sure:
After that, you can save and quit by typing:
At this point, each of your computers has a copy of the other's GPG key. They are signed to signify that we trust the key's authenticity.
Configure Access on the fwknop Server
On the server machine, we need to configure the fwknop service to use our GPG keys and allow our client to connect and authenticate.
On the server, open the access configuration with root privileges:
sudo nano /etc/fwknop/access.conf
You should see a file that only contains around four active lines. It should look something like this:
SOURCE: ANY; OPEN_PORTS: tcp/22; ### for ssh (change for access to other services) KEY: throw_away_password; FW_ACCESS_TIMEOUT: 30;
This is configured using the password you selected during the service's installation. We won't be needing this, because we'll be changing to GPG authentication. Comment that line out now:
SOURCE: ANY; OPEN_PORTS: tcp/22; ### for ssh (change for access to other services) # KEY: throw_away_password; FW_ACCESS_TIMEOUT: 30;
Next, we'll append some additional parameters that will tell the fwknop service about our keys. We need to add some more information:
SOURCE: ANY; OPEN_PORTS: tcp/22; ### for ssh (change for access to other services) # KEY: throw_away_password; FW_ACCESS_TIMEOUT: 30; REQUIRE_SOURCE_ADDRESS: Y; DATA_COLLECT_MODE: PCAP; GPG_REMOTE_ID: 99999999; ## Client key goes here GPG_DECRYPT_ID: 11111111; ## Server key goes here GPG_DECRYPT_PW: your_GPG_passphrase_here; ## Place the GPG passphrase here GPG_HOME_DIR: /home/test/.gnupg; ## Path to your user's GPG directory. Usually ## this is in your user's home directory.
Make sure you alter the GPGREMOTEID to reflect your client's key ID and the GPGDECRYPTID to reflect the server ID.
In addition, you'll need to enter your GPG passphrase and include the path to your user's
Save and close the file.
Configure IPTables Rules
Now that we have modified the fwknop server configuration, we need to make some actual iptables rules. The service will modify these as necessary, but we need to close down our port to begin with.
First, we need to allow our current connection. This rule will allow already established connections and associated data:
sudo iptables -A INPUT -i eth0 -p tcp --dport 22 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
Next, directly after, we'll restrict all other access to the port by dropping all non-established connection attempts:
sudo iptables -A INPUT -i eth0 -p tcp --dport 22 -j DROP
Now that we have a basic firewall restricting access to that port, we can implement our configuration. Restart the fwknop server by typing:
sudo service fwknop-server restart
Now, the fwknop service will begin monitoring our server for packets that match rules we configured.
Connecting to the Server from the Client
Now that we have our server configured and running, we can do a test to see if we can connect to our SSH server from our client. This should time out, because we haven't sent the encrypted packet yet:
ssh: connect to host server_domain_or_ip port 22: Connection timed out
You can press "Ctrl-C" if you don't feel like waiting for this to time out.
Now, we can send an encrypted packet that will authenticate us with our server. We do this by passing the fwknop client command a number of parameters:
-A tcp/22: This option specifies the protocol and port that we are requesting to open.
--gpg-recip: This option will specify the server's GPG key ID.
--gpg-sign: This option specifies the client's GPG key ID.
-a: This parameter tells fwknop what IP address to allow access for. This should be your client machine.
-D: This tells the command the destination we are sending our packet to. This is our server's domain name or IP address.
Now that we know the info we need in our string, we can create and send our packet:
fwknop -A tcp/22 --gpg-recip 11111111 --gpg-sign 99999999 -a client_ip_address -D server_domain_or_ip
You will be asked for your GPG passphrase to decrypt the keys. Afterwards, your packet will be sent to the server.
At this point, if your packet was accepted, you will have 30 seconds to attempt to connect through SSH:
If all goes well, you should be able to log in successfully. The open port will close after 30 seconds, but your connection will remain active.
On the server machine, after you send a packet (and before 30 seconds are up), you should be able to see a new rules added to temporarily allow your client's IP address:
sudo iptables -S
-P INPUT ACCEPT -P FORWARD ACCEPT -P OUTPUT ACCEPT -N FWKNOP_INPUT -A INPUT -j FWKNOP_INPUT -A INPUT -i eth0 -p tcp -m tcp --dport 22 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A INPUT -i eth0 -p tcp -m tcp --dport 22 -j DROP -A FWKNOP_INPUT -s client_ip_address/32 -p tcp -m tcp --dport 22 -j ACCEPT
The utility adds another chain to iptables, and filters all input through it. It then checks if the connection matches the machine that produce the encrypted packet and accepts it if so. Otherwise, it drops it as usual.
By setting up a single packet authentication mechanism, you can add an extra layer of security when connecting between your servers. This will not only hide your SSH server from random attacks and attempted brute-forcing, it will also help in situations where a security exploit is found in the protected services. This will allow you to stay secure while you work to fix the issue.
While single packet authentication may be too much trouble for some users, it is a good option to add an additional layer to your security. Combined with regular SSH security measures, this should make your concealed services fairly secure.