guest@6admin.io:~$

Portknocking: automatic combination generation

Classic portknocking knocks on the door with a fixed port sequence. The problem with a fixed sequence: it gets sniffed, it gets replayed. The solution: make the sequence depend on the date, so it changes every day.

The principle

The combination is computed from the system date. The day determines the protocol (even = udp, odd = tcp) and the ports start from the day+month+year sum doubled, to which 64 is added until past 1024. The client and the server must have the same clock, hence the NTP synchronization.

The script

#!/bin/bash
: << 'TITLE.sh'
 Version: 0.2
 Authors: nobody@vhalholl.info
 Description: Automatic portknocking sequence generation
 Dependencies: knock, bc, ntp
TITLE.sh

A=$(date +%d); B=$(date +%m); C=$(date +%y)
S=$((A + B + C))

# Base: the date fields summed, doubled (then +64 below until past 1024)
P1=$((S * 2)); P2=$((P1 + 2)); P3=$((P2 + 2))

while [ $P1 -lt 1024 ]; do P1=$((P1 + 64)); done
while [ $P2 -lt 1024 ]; do P2=$((P2 + 64)); done
while [ $P3 -lt 1024 ]; do P3=$((P3 + 64)); done

# Upper bound: 65535
if [ $P1 -gt 65535 ]; then P1=65535; fi
if [ $P2 -gt 65535 ]; then P2=65535; fi
if [ $P3 -gt 65535 ]; then P3=65535; fi

# Protocol based on day parity
if [ $((A % 2)) -eq 0 ]; then PROTO1="udp"; else PROTO1="tcp"; fi
if [ $((A % 2)) -eq 0 ]; then PROTO2="udp"; else PROTO2="tcp"; fi
if [ $((A % 2)) -eq 0 ]; then PROTO3="udp"; else PROTO3="tcp"; fi

# Knock
knock $1 $P1:$PROTO1 $P2:$PROTO2 $P3:$PROTO3

The script computes the combination from the day, month and year, determines the protocol by parity and knocks. The server (knockd) has the same computation logic and iptables opens port 22 when the sequence arrives in order.

The kssh/kscp functions

The functions in .bash_functions_mgmt read the combination from knock/func on the encfs-encrypted USB key:

function kssh(){
    host=`echo $* | egrep -o '([0-9]{1,3}\.)([0-9]{1,3}\.)([0-9]{1,3}\.)([0-9]{1,3})'`;
    fqdn=`echo $* | egrep -o '([a-zA-Z0-9.-]+\.[a-zA-Z]{2,4})'`;
    if [ -d /home/user/.sdata/coding/bash/ ];then
        . /home/user/.sdata/coding/bash/knock/func;
    else
        smount && . /home/user/.sdata/coding/bash/knock/func;
    fi
    if [[ $host ]];then
        /usr/bin/knock $host $toc && /usr/bin/ssh $*;
    fi
    unset toc host fqdn;
}

The smount mounts the encfs key if not already mounted, knock/func generates $toc (today’s combination) and knock knocks before the ssh. The unset at the end cleans the sesame from the session.

[edit: 2026] This setup has been dead since WireGuard (the epilogue ), but the pattern (time-derived sequence + key encryption + NTP) remained in memory. The simple portknocking was a 2008 trick; the automated version was a style exercise that taught NTP, USB key encryption and the discipline of never storing the sesame in cleartext.

[edit: 2026] The Ansible implementation of all this: the wireguard role .