A security-enhanced port knocking system with rotating knock sequences.
This project provides tools to enhance the security of knockd port knocking by automatically rotating knock sequences based on time periods. This prevents replay attacks and significantly improves the security of your port knocking setup.
The rotating sequence is deterministically generated based on:
- The timestamp at the beginning of the current time period (UNIX seconds floored to a multiple of the period length)
- A unique service name
- A pre-shared secret
This means that legitimate clients and servers will independently generate the same sequence for each time period, while attackers cannot predict future sequences without knowing the secret.
Both the server and client independently generate the same knock sequence for a given time period:
- The client uses
knockd_rotator_client.py
to generate the current sequence and performs the knocks - The server runs
knockd_rotator_server.py
periodically (via systemd timer) to:- Update
/etc/knockd.conf
with the current sequences - Restart the knockd service to apply changes
- Sometimes schedule an anticipated run to ensure timely updates at period boundaries
- Update
The shared sequence generation algorithm ensures both sides produce identical sequences without any communication between them.
A lightweight client-side tool that both generates and executes knock sequences:
- Runs on minimal environments like Termux on Android
- Requires only Python 3 standard library
- Supports abbreviated commands (e.g. "gene" for "generate", "knock" for "knock")
- Supports two modes:
- Generate mode: Creates the sequence string for a service
- Usage:
./knockd_rotator_client.py generate <service_name> [--offset <n>]
- Usage:
- Knock mode: Performs the actual port knocking against a target host
- Usage:
./knockd_rotator_client.py knock <host> <service_name> [--offset <n>]
- Usage:
- Generate mode: Creates the sequence string for a service
- Automatically appends "_ROTATOR" to service names if not already present
- Supports time period offsets to generate past or future sequences
- Note: A Zsh reimplementation of the client is available in
knockd_rotator_client.zsh
. It has not been as thoroughly tested as the Python script but should function similarly.
A server-side tool that:
- Scans
/etc/knockd.conf
for sections ending with_ROTATOR
- Updates those sections with freshly generated sequences
- Restarts the knockd service after updating
- Verifies that the service is running correctly after restart
- Intelligently schedules an additional run to handle period transitions
- Supports dry-run mode for testing
- Usage:
./knockd_rotator_server.py [--dry-run] [--config /path/to/knockd.conf]
The system is configured through environment variables:
KNOCKD_ROTATOR_LENGTH
: Number of ports in the knock sequence (default: 10)KNOCKD_ROTATOR_SECRET
: The shared secret used to generate sequences (required, minimum 10 characters). You can generate a strong secret using:openssl rand -hex 64
KNOCKD_ROTATOR_PROTO_MODULO
: Controls TCP/UDP protocol selection:- When
KNOCKD_ROTATOR_PROTO_MODULO = 0
(default): All knocks use TCP protocol - When
KNOCKD_ROTATOR_PROTO_MODULO > 0
: For each port of the sequence, the port moduloKNOCKD_ROTATOR_PROTO_MODULO
determines protocol (even = TCP, odd = UDP)
- When
KNOCKD_ROTATOR_PERIOD_MODULO
: Controls how frequently the sequence changes, in seconds (default: 21600, which is 6 hours)KNOCKD_ROTATOR_SERVER_INTERVAL
: How often the server script is expected to run, in seconds (default: 3600, which is 1 hour). This is used to know if we should fork the process to anticipate the next period.KNOCKD_ROTATOR_PORTS
: List of ports or port ranges to use for knock sequences (default: "2000-65536")- Format: Comma-separated list of either single ports or ranges (e.g., "2000-3000,4000,5000-6000")
- Both ends of ranges are included
- At least 2 ports must be specified, with no duplicates.
Port numbers are generated in the range 2000-65536 by default to avoid requiring elevated privileges. You can adjust this with the KNOCKD_ROTATOR_PORTS
environment variable if needed (for example, if certain port ranges are blocked by firewalls or require special permissions). Using 3 or fewer ports will significantly reduce security and generate a warning.
[ssh_ROTATOR]
sequence = 12345:tcp,23456:tcp,34567:tcp,45678:tcp,56789:tcp,65432:tcp
seq_timeout = 15
tcpflags = syn
start_command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
cmd_timeout = 30
stop_command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT
- Install knockd on your server
- Configure your knockd.conf with sections ending in
_ROTATOR
- Set up the systemd service and timer:
- Copy
knockd_rotator.service
andknockd_rotator.timer
to/etc/systemd/system/
- Enable and start the timer:
systemctl enable --now knockd_rotator.timer
- Copy
- Distribute the client script to your devices:
- Copy
knockd_rotator_client.py
to your client devices - Set the same environment variables on clients as on the server
- Copy
- Period Boundary Handling: The server automatically schedules an additional run at period transitions to ensure timely sequence updates
- Service Verification: Confirms knockd is running properly after configuration changes
- Multiple Services: Support for multiple different rotator services in one config file
- Time Period Offsets: Generate past or future sequences with the
--offset
parameter. This is useful to generate sequences that were valid at passed times if you know that the server side script failed to run for some reason.
- I am obviously not a security expert.
- This works fine using
ufw
in my tests. - I don't advise using a very frequent rotation unless you don't use knockd's
start_command
andstop_command
because if it happens between the two you might end up in a compromised state. - The code is minimal on purpose so that you just have to copy the
knockd_rotator_client.py
file around and the env variables. - User @rdmitry0911 made a pull request to include a somewhat similar feature directly in knockd. Their implementation is based on OTP whereas this project uses pure Python for the sequence generation.
- Knockd already includes a feature to have knock sequences taken from a text file and used only once at a time. However, I wanted to be able to perform knocks from multiple clients without issues, so I created this solution which deterministically generates the same sequence across all clients for a given time period.
- Why not use HMACs or other counter-based approaches? This time-based key setup allows clients and servers to operate without having to sync counters between them. All clients are equal, and the setup is as simple as possible while still providing robust security.
- You might also be interested in my other project fowlrot.sh, which applies a similar rotating secret concept to fail2ban jails.
- If you are looking for a dedicated Android client, support for this rotator logic has been requested in the knockonports app issue tracker.
- You can run the test suite to verify everything is working correctly:
python -m pytest test_knockd_rotator_client.py -v
This project was developed with the assistance of aider.chat.