Introduction
The Firewall Knock Daemon, fwknopd, does not work out of the box with openwrt-24.10.
This is because fwknopd does not support nftables, which is the core technology of fw4 and the modern way to manage network packets, datagrams and frames. Fortunately fwknopd provides the means to run custom open and close commands which we can use to manage nftables-based firewall rules.
Overview
This post describes how to set up port-forwarding using fwknopd. It uses luci (the web interface) for most of the configuration, meaning that you get to manage your port-forwarding using standard OpenWrt techniques. This means you can easily see and modify your firewall setup using the web interface. Traditionally, port-forwards managed by fwknopd were defined solely in the fwknopd configuration and did not appear in the firewall web interface.
Our approach is to use IP Sets with built-in timeouts to allow port-forwarding on a conditional basis.
When fwknopd authenticates a user, that user’s IP address and the port to which they require access is added to the IP Set. Our port-forwarding rules check incoming packets against the IP Set, and only allow those from the appropriate IP address that are requesting the appropriate port. And since entries in the IP Set automatically time-out after a short period no further action is required to subsequently disable further access.
Step 1 is to create IP Sets, which we will use for conditional port-forwarding. Step 2 is to set up the conditional port-forwarding based on those IP Sets. Finally, Step 3 is to set up fwknopd. The fwknopd configuration will be done outside of luci and uci, which is to say, we will configure it manually using its standard configuration files.
Note that simply opening local ports using fwknopd should be doable in a similar way but I haven't tried it.
Step 1 - Define IP Sets
We will create two IP Sets, one for tcp and one for udp. Note that you may not actually need both. Note: these two IP Sets can be used for multiple port forwarding rules.
In Network->Firewall select the IP Sets tab.
Press Add. Fill in the pop-up form as follows for tcp connections:
Name: fwknop_allow_6
Comment: Allow tcp connections via fwknop
Family: IPv4
Packet Field Match: src_ip: Source IP
dest_port: Destination Port
Timeout: 30
Press Save
Press Add again. Fill in the pop-up form as follows for udp connections:
Name: fwknop_allow_17
Comment: Allow udp connections via fwknop
Family: IPv4
Packet Field Match: src_ip: Source IP
dest_port: Destination Port
Timeout: 30
Press Save
Press Save & Apply
This will have created 2 IP Sets. These are used to record the source ip address and destination port for connections approved by fwknopd. These records will expire after the timeout period (30 seconds as defined above). Note that the names of the IP Sets include the protocol number: 6 for tcp and 17 for udp. This is necessary for fwknopd to be able to separately identify and allow tcp and udp access.
Step 2 - Set Up Port Forwarding
We will now set up a conditional port forwarding rule, in this case for ssh. This will allow connections from outside the LAN to establish an ssh session to a server within the LAN.
In Network->Firewall select the Port Forwards tab.
Press Add. Fill in the popup form as follows:
Name: Enter something that identifies what the rule is doing.
Something like: SPA_ssh_to_mysshserver.
Restrict to address family: IPV4 only
Protocol: tcp only (deselect udp)
Source Zone: wan (this is where the traffic will be arriving from)
External port: 22 (or whatever works for you)
Internal IP address: The internal ip address of your ssh server. You
will need to ensure that this address is static within your network.
Internal port: 22 (or whatever port your ssh server uses)
Now select the Advanced Settings tab and fill out the following:
Use ipset: select fwknop_allow_6
Press Save.
You will now be back in the Firewall - Port Forwards page.
Press Save & Apply.
Testing the Port Fowarding Without fwknop
This step is optional for the sole purpose of providing you with a warm fuzzy feeling. You don’t need to do this but it will allow you to establish that your port forwarding rules and IP Sets are working as they should.
For this, you will need to be able to run a terminal session on your router, and have a machine outside your LAN from which you can attempt an ssh session. We will call this the remote machine. You will also have to have enabled the ssh service on your local ssh server.
Replace myhost.com below with your hostname or address.
On the remote machine ensure ssh is not accessible:
me:~$ ssh myhost.com
ssh: connect to host myhost.com port 22: Connection refused
me:~$
On the remote machine, identify your public facing ip address:
me:~$ curl ifconfig.me
123.456.125.77me:~$
Here, our public facing ip address is claimed to be 123.456.125.77
On the router, as root:
root:~# nft add element inet fw4 fwknop_allow_6 \{ 123.456.125.77 . 22 \}
root:~# nft list sets inet
table inet fw4 {
set fwknop_allow_6 {
type ipv4_addr . inet_service
timeout 30s
comment "Allow tcp connections via fwknopd"
elements = { 123.456.125.77 . 22 expires 28s880ms }
}
set fwknop_allow_17 {
type ipv4_addr . inet_service
timeout 30s
comment "Allow udp connections via fwknopd"
}
}
root:~#
You now have 28.88 seconds to attempt to connect via ssh. So, on the
remote machine:
me:~$ ssh myhost.com
Linux myhost x.y.z-w-amd64 #1 SMP Debian N.M.P-Q (YYYY-MM-DD) x86_64
The programs included with the Devuan GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Devuan GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
You have no mail.
Last login: Thu Mar 26 11:21:31 2026 from 192.168.2.1
me:~$
And we are successfully connected.
Now disconnect, wait 30 seconds and attempt to reconnect. We will
be, again, locked out:
me:~$ ssh myhost.com
ssh: connect to host myhost.com port 22: Connection refused
me:~$
This shows that adding an appropriate element to the IP Setfwknop_allow_6 will temporarily allow connections through the firewall.
Wrinkles
There is a small wrinkle with this setup, which is that once the port forwarding has been defined in fw4, local connections (ie from within the LAN) are able to connect using the domain name without usingfwknop. This appears to be standard policy within openwrt. I find this somewhat surprising but, for me at least, not problematic.
Step 3 - Set Up fwknopd
Now we will set up fwknopd in the simplest way possible for a single accessor. If you need a more sophisticated setup what follows should provide a good starting point. Read the online fwkknopd documentation for more.
Start by installing fwknopd on the router. There is no need to install either luci-app-fwknopd or fwknop.
On the remote machine, as the user that will use ssh, we will generate our keys. The server name and ip address in the following command can be used verbatim. We will rewrite the config file later.
You will need to install fwknop on this machine. Then...
me:~$ fwknop -A tcp/22 -a 1.1.1.1 -D myhost.com --key-gen --use-hmac --save-rc-stanza
[+] Wrote Rijndael and HMAC keys to rc file: /home/mbr/.fwknoprc
me:~$
This creates our initial set of keys in .fwknoprc. Securely copy this file, or at least its key definitions, to the router.
Log in to the router using a terminal interface and become root:
root:~# cd /etc/fwknop
root:fwknop# mv access.conf access.conf.orig
root:fwknop# mv fwknopd.conf fwknopd.conf.orig
root:fwknop#
This creates backup copies of the installed config files. You may want to refer to them later as they contain lots of information in comments.
Now we recreate those config files, to contain the following.
access.conf:
# Access from "remote"
SOURCE ANY
REQUIRE_SOURCE_ADDRESS Y
KEY_BASE64 <COPY THIS FROM THE .fwknoprc FILE CREATED ABOVE>
HMAC_KEY_BASE64 <COPY THIS FROM THE .fwknoprc FILE CREATED ABOVE>
CMD_CYCLE_OPEN nft add element inet fw4 fwknop_allow_$PROTO { $SRC . $PORT timeout 30s }
CMD_CYCLE_CLOSE NONE
fwknopd.conf:
PCAP_INTF wan
Reboot the router. Then connect using ssh and become root again.
root:~# ps ww | grep fwkn
2779 root 3684 S /usr/sbin/fwknopd --foreground --syslog-enable -i wan
5742 root 1336 S grep fwkn
root:~#
This shows that the fwknopd daemon was successfully started.
If you don't see something like this, take a look at the system logs from the router's web interface: Status->System Log. Look for lines containing fwknopd. This should give you clues about what is going on.
Now switch back to using the "remote" machine.
Edit the .fwknoprc file to look like this:
[myhost.com]
SPA_SERVER myhost.com
KEY_BASE64 xxxxxxxxxxxxxxxxx
HMAC_KEY_BASE64 xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Check that the key fields match those defined in/etc/fwknop/access.conf on the router.
Now we can attempt ssh connections. We'll start be making sure that connecting without SPA fails:
me:~$ ssh myhost.com
ssh: connect to host myhost.com port 22: Connection refused
me:~$
Now, we'll run fwknop and try again:
me:~$ fwknop -R -n myhost.com -A tcp/22
me:~$ ssh myhost.com
<fanfare sounds...>
Conclusion
Using fwknopdwith OpenWrt provides access to services behind your firewall without exposing the ports for those services to the wider Internet. For ssh in particular, it can greatly reduce the number of brute-force attack attempts seen by your server, and reduce the consequent clutter in your logs.
Note that fwknopd should be used in addition to the usual best practices for securing such services and not as an alternative.