fwknop: The Single Packet Authorization System That Makes Your SSH Server Invisible
Hook
Your SSH server answers on port 22. Within minutes, it's being hammered by thousands of authentication attempts from botnets worldwide. What if instead of hardening SSH, you could make port 22 simply not exist until the exact moment you need it?
Context
Traditional port knocking seemed like an elegant solution to this problem: send packets to a sequence of closed ports, and your firewall would open access. But port knocking has fatal flaws. The knock sequence itself is observable on the wire, vulnerable to replay attacks, and provides no cryptographic authentication. An attacker who captures your knock sequence can replay it indefinitely.
fwknop (FireWall KNock OPerator) reimagines this concept through Single Packet Authorization (SPA). Instead of a sequence of packets, you send one cryptographically signed, encrypted packet containing your authorization credentials, the service you want to access, timestamps, and other metadata. The server passively sniffs network traffic via libpcap—meaning it literally has no open ports—validates the packet's HMAC signature, decrypts it, checks for replays using SHA-256 digests, and only then dynamically modifies firewall rules to grant temporary access. It's the network security equivalent of a building that materializes a door only when you present the correct biometric credentials.
Technical Insight
The architectural brilliance of fwknop lies in its defense-in-depth validation pipeline and the crucial decision to validate HMAC signatures before attempting decryption. When fwknopd (the server daemon) sniffs a packet on the network, it first extracts the HMAC and compares it against a computed hash of the packet contents using the shared key. Only if the HMAC matches does it proceed to decrypt the payload. This encrypt-then-authenticate pattern means maliciously crafted packets never reach the complex cryptographic decryption code, dramatically reducing attack surface.
Here's what a basic client command looks like when requesting SSH access:
fwknop -A tcp/22 -a 192.168.1.50 -D server.example.com
This generates an SPA packet containing your source IP (192.168.1.50), the service you're requesting (tcp/22), a timestamp, and a random value, all encrypted with Rijndael (AES) and signed with an HMAC. The server configuration in /etc/fwknop/access.conf defines who can authorize what:
SOURCE ANY
OPEN_PORTS tcp/22
FW_ACCESS_TIMEOUT 30
REQUIRE_SOURCE_ADDRESS Y
KEY_BASE64 <base64-encoded-key>
HMAC_KEY_BASE64 <base64-encoded-hmac-key>
The FW_ACCESS_TIMEOUT is particularly important—it specifies how long the firewall rule remains active. After 30 seconds, the rule automatically expires, re-concealing the service. This temporal limitation means even if an attacker compromises the host during that window, they have limited time to exploit it.
For asymmetric encryption scenarios, fwknop supports GnuPG, eliminating shared secret management headaches in multi-client environments:
fwknop -A tcp/22 --gpg-recipient-key 0x1234ABCD \
--gpg-signer-key 0x5678EF01 -D server.example.com
The server validates the GPG signature, ensuring the packet came from a trusted client without requiring symmetric key distribution. This is ideal for scenarios where you're managing access for multiple administrators who each maintain their own private keys.
The NAT traversal capability deserves special attention because it solves a problem that plagued early SPA implementations. When fwknop operates behind NAT (common in cloud environments like AWS with VPCs), the server can be configured to forward the authenticated connection to an internal service:
SOURCE ANY
OPEN_PORTS tcp/22
FORCE_NAT 192.168.1.100 22
FW_ACCESS_TIMEOUT 30
This configuration means the SPA packet authorizes access, but the actual connection is forwarded to 192.168.1.100:22 on the internal network. The fwknop server essentially acts as a cryptographically-gated reverse proxy at the firewall layer.
Under the hood, fwknopd modifies firewall rules using the appropriate backend for your system. On Linux with iptables, it dynamically inserts rules into the INPUT chain. You can observe these ephemeral rules appearing and disappearing:
# Before SPA packet
iptables -L INPUT -n | grep 22
# (no output - port 22 is blocked)
# After valid SPA packet
iptables -L INPUT -n | grep 22
ACCEPT tcp -- 192.168.1.50 0.0.0.0/0 tcp dpt:22
# After timeout expires
# (rule disappears automatically)
The passive sniffing approach via libpcap means fwknopd never binds to a socket. From an attacker's perspective, there's nothing to scan, nothing to fingerprint, and no service to exploit before authentication. This is fundamentally different from even hardened SSH configurations—there's no TCP handshake, no banner disclosure, no protocol negotiation until after SPA succeeds.
Gotcha
The biggest operational challenge with fwknop is time synchronization. SPA packets include timestamps to prevent replay attacks, and the server will reject packets outside a configurable time window (default is typically 120 seconds). In environments with clock skew—virtual machines, embedded systems, or geographically distributed infrastructure without NTP—you'll encounter mysterious authorization failures. The error messages can be cryptic, and debugging requires examining /var/log/fwknop/fwknopd.log to see timestamp validation failures. This isn't theoretical: I've seen production deployments where a hypervisor's time drift caused intermittent access failures that took hours to diagnose.
Key management also becomes complex at scale. While GnuPG support helps, you still need to securely distribute and rotate keys. In a symmetric key scenario, every client shares the same encryption and HMAC keys, meaning key compromise requires rotating keys across your entire infrastructure. There's no built-in key rotation mechanism, certificate authority, or integration with modern secrets management like HashiCorp Vault. The Perl codebase, while battle-tested, makes integration with contemporary DevOps tooling less straightforward than if this were written in Go or Rust with gRPC APIs and structured logging. You're essentially bolting a security layer onto your existing automation rather than treating it as a first-class API-driven service.
Verdict
Use fwknop if: You're protecting critical administrative interfaces (SSH, RDP, database ports) on internet-facing hosts where zero-day exploits are a legitimate threat model, you need defense-in-depth beyond fail2ban and rate limiting, you're operating in environments with NAT/cloud gateways where traditional VPNs create routing complexity, or you need truly invisible services where even port scanning reveals nothing. It excels in scenarios like bastion hosts, industrial control systems with infrequent administrative access, or honeypot environments where you want legitimate access paths while observing attacker behavior. Skip fwknop if: You need high-frequency connections (the SPA overhead becomes burdensome for applications requiring constant reconnection), your infrastructure lacks reliable time synchronization, you're uncomfortable managing Perl dependencies in production, you can achieve adequate security with IP whitelisting or modern VPN solutions like WireGuard, or your team lacks the operational maturity to manage cryptographic key lifecycles without breaking emergency access procedures.