# UPDATE ME: Define the port being guarded, if different. define guarded_ports = {ssh} table inet portknock { set clients_ipv4 { type ipv4_addr flags timeout } set clients_ipv6 { type ipv6_addr flags timeout } set candidates_ipv4 { type ipv4_addr . inet_service flags timeout } set candidates_ipv6 { type ipv6_addr . inet_service flags timeout } chain input { type filter hook input priority -10 policy accept # UPDATE ME: List all interfaces EXCEPT your public network interface here. iifname "lo" return iifname "wlo1" return # Ignore the filter if the connection is already established. ct state established,related accept # Ignore the filter unless we're working with TCP. meta l4proto != tcp return # Ignore the filter for clients that have already knocked successfully. tcp dport $guarded_ports ip saddr @clients_ipv4 counter accept tcp dport $guarded_ports ip6 saddr @clients_ipv6 counter accept # Reject direct connections to the guarded port. tcp dport $guarded_ports counter reject with tcp reset # Port knock sequence: 1234 4321 1234 4321. tcp dport 1234 add @candidates_ipv4 {ip saddr . 4321 timeout 1s} tcp dport 1234 add @candidates_ipv6 {ip6 saddr . 4321 timeout 1s} tcp dport 4321 ip saddr . tcp dport @candidates_ipv4 add @candidates_ipv4 {ip saddr . 1234 timeout 1s} tcp dport 4321 ip6 saddr . tcp dport @candidates_ipv6 add @candidates_ipv6 {ip6 saddr . 1234 timeout 1s} tcp dport 1234 ip saddr . tcp dport @candidates_ipv4 add @candidates_ipv4 {ip saddr . 4321 timeout 1s} tcp dport 1234 ip6 saddr . tcp dport @candidates_ipv6 add @candidates_ipv6 {ip6 saddr . 4321 timeout 1s} tcp dport 4321 ip saddr . tcp dport @candidates_ipv4 add @clients_ipv4 {ip saddr timeout 10s} log prefix "Successful portknock: " tcp dport 4321 ip6 saddr . tcp dport @candidates_ipv6 add @clients_ipv6 {ip6 saddr timeout 10s} log prefix "Successful portknock: " } }