UPDATE: 2016-02-03 - Update firewall rules section.
A few weeks ago I discovered fwknop which is a very clever mechanism to secure services. I’m using this so I can ssh into a Linux server on my home network without opening the sshd port up to the world.
Single packet authorization works by sending a single, encrypted UDP packet to a remote system. The packet is never ACKd or replied to, but if it’s validated by the remote system, then it uses iptables to temporarily open up the service port for access (the filter is limited to the client’s IP address). If the packet isn’t valid, it is simply ignored. In either case, to an external observer the packet appears to go into a black hole. After a user-configurable amount of time (30 seconds by default), the service port is closed, but stateful iptable rules keep existing connections active.
This is really great because all ports to my home IP address appear, from the internet, to be black holes - my router firewall drops all incoming packets, and the specific ports open for fwknop are dropped via iptables on my Linux server.
Configuring this solution isn’t too difficult if you are familiar with networking and Linux network and system administration, but it can be a bit tricky to test.
There are four areas that need to be configured on the server-side:
- Fwknop needs to be configured with appropriate ports and security keys
- iptables policy needs to be created for each service port
- Services need to listen on appropriate ports
- Router firewall needs to forward fwknop and service ports to the server
My per-service iptables policies are done via iptables-restore (and ip6tables-restore) and the relevant bits look like:
*filter -A INPUT -j FWKNOP_INPUT -A INPUT -i eth0 -p tcp -m tcp --dport 54321 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A INPUT -i eth0 -p tcp -m tcp --dport 54321 -j DROP -A INPUT -i eth0 -p udp -m udp --dport 12345 -j DROP COMMIT
Note the order of rules is important. Make sure that the FWKNOP_INPUT rule is before the port-specific rules. Likewise, make sure the ESTABLISHED,RELATED rule for each service is before the DROP rule for that service port. The last rule is subtle. fwknopd does not bind to the SPA socket port - it transparently sniffs for UDP traffic, hence we drop traffic in keeping with the rest of my general firewall rules to blackhole all inbound traffic.
Before you start fwknop, and open up ports on your firewall, don’t forget to make sure these rules are in place. In that case, if you create these rules manually, use rule order “1” instead of “2”, as you are creating the rules before fwknop has added it’s rule.
/etc/fwknop/fwknopd.conf excerpt from the server:
PCAP_FILTER udp port 12345;
On my debian testing/Jessie server I also had to add this line to fwknopd.conf:
/etc/fwknop/access.conf excerpt from the server:
SOURCE ANY REQUIRE_SOURCE_ADDRESS Y KEY_BASE64 SOME_BASE64_ENCODED_KEY HMAC_KEY_BASE64 SOME_BASE64_ENCODED_HMAC_KEY
I didn’t use the default port as a bit of added measure, and additionally I’m running sshd on a different port as well, as a small added bit of security.
Adding an additional port in sshd is really simple, just add an additional Port line and restart sshd:
Port 22 Port 54321
Only port 54321 is port forwarded on the router, but I can still use port 22 while on my home network.
On the client, I have a simple script that:
- Sends the authorization packet via the fwknop client
- ssh’s into my server on the configured sshd port
The script looks something like:
fwknop my.host.fqdn ssh -p 54321 my.host.fqdn
Excerpt from .fwknoprc on the client:
[my.host.fqdn] ACCESS tcp/54321 SPA_SERVER my.host.fqdn SPA_SERVER_PORT 12345 KEY_BASE64 SOME_BASE64_ENCODED_KEY HMAC_KEY_BASE64 SOME_BASE64_ENCODED_HMAC_KEY USE_HMAC Y ALLOW_IP resolve
From this config, you can see that the fwknop port is 12345, and sshd is listening on 54321 (though these aren’t the real ports or FQDN in use). The KEY_BASE64 and HMAC_KEY_BASE64 values need to match between client and server. I chose to use symmetric keys but you can use asymmetric keys via GPG if you prefer.
See the fwknop documentation for more information on configuring everything. There are a lot of options, so you’ll have to figure out what to do based on your individual needs.
I’m using a free dynamic DNS service so that I don’t have to remember the dynamic IP address assigned by my ISP.
This has been tested with Debian on both stable/Wheezy and testing/Jessie. For Wheezy, make sure you are using the latest version from wheezy-backports.
The documentation is decent, and I’ve found this solution works very nicely for me, without exposing any detectible open ports on my network. Unlike with simple port knocking, it is virtually impossible for someone to use packet capture replay to access the system. Because all packets are dropped unless the authorization packet opens up the service port, it is completely undetectable via port scanning that fwknop is even in use.
Give it a try!