For a port-knocking scheme, I'm wondering how to make the iptables recent module temporarily (for just a few seconds) list-name matched source addresses. My intuition tells me that I need the --set function of the recent module to accept the --seconds option, making the list assignment temporary, but all I can see is the way to assign the list name to an address permanently and have another rule remove the list-name assignment only upon receipt of some later packet. The reason that doesn't fit into my mental concept is because the removal (--reap or --remove, I suppose) of the address from the list will only occur upon reception of a future packet, whereas my intuition wants the address removed at a certain time expiration regardless of whether any packets arrive to trigger said name removal. The only way I can see to do something vaguely similar to this is very non-intuitive to me, and therefore suspicious to me that I'm missing something about how it all works: I would need a recent module rule and --rcheck option to ensure the listed packet's name matches and that it had gotten assigned within the previous x seconds and remove it with a jump destination, the rule in that jump destination would be to assign the next list name to the source address. In the meantime, the length of the lists just keeps growing (don't they?), filling up with stray source addresses that never completed the knock sequence[s]. What a simple solution it would be for the recent module to accept the --seconds option with --set! Can anyone help me help me see this more clearly?
( I've looked at other knocking solutions using iptables, but they are limited to only using each port-protocol combination for one knock in the sequence, while a good knocking solution should, IMHO, allow for the same port-protocol combo to be used as many times in the knock sequence as the user wants it to be used. knockd had that same limitation, as well as exhibiting terrible non-robust operation. I tried to obtain the pknock module for iptables, but it appears that not all its components exist [specifically two shell scripts referred to in the documentation, knock.sh and knock-orig.sh, supposedly "found in doc/pknock/util", wherever that is...certainly not SF, Github, nor anywhere else I could see], making me very suspicious of using it.)
EDIT: I'm seeing that the ruleset needs to be even more complex than described - the knock steps numbered two and above all need to match the packet by name first, then jump it to their own chain that removes the name, determines whether the correct timing and port-protocol matches, then jump it to yet another chain to rename it, or don't jump it if timing or knock is off which falls into a drop rule. WHEW
My initial solution is shown below. The knocks in this example just happen to be unique, but non-unique knocks will function fine as well. As you can see, I make every knocking packet reap every list because I don't know if the lists self-limit the length of time they'll keep entries otherwise. It seems like the only way to ensure that no list can get too long.
$--> iptables -wnvL
Chain INPUT (policy DROP)
pkts bytes target prot opt in out source destination
0 0 ACCEPT tcp -- $internal_net_interface * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 ctstate RELATED,ESTABLISHED /* extract ssh for knock testg frm private side in ssh */
0 0 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 recent: REMOVE name: authorized side: source mask: 255.255.255.255 ctstate NEW /* 1-packet pass: 1 chance to establish or then knock higher */
0 0 knockerstest all -- $internal_net_interface * 0.0.0.0/0 0.0.0.0/0 recent: CHECK seconds: 200 name: knocker side: source mask: 255.255.255.255 /* for knock capability */
0 0 knockstage1 tcp -- $internal_net_interface * 0.0.0.0/0 0.0.0.0/0 tcp dpt:1 flags:0x17/0x02 recent: SET name: knocker side: source mask: 255.255.255.255 /* for knock capability, 1st port */
0 0 knockers all -- $external_net_interface * 0.0.0.0/0 0.0.0.0/0 recent: CHECK seconds: 200 name: knocker side: source mask: 255.255.255.255 ctstate NEW /* for port knock capability */
0 0 knockstage1 tcp -- $external_net_interface * 0.0.0.0/0 0.0.0.0/0 ctstate NEW tcp dpt:1 flags:0x17/0x02 recent: SET name: knocker side: source mask: 255.255.255.255 /* for port knock capability, 1st port */
Chain knockerreap (10 references)
pkts bytes target prot opt in out source destination
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: CHECK seconds: 60 reap name: knocker side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: SET name: knockstage1 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: SET name: knockstage2 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: SET name: knockstage3 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: SET name: knockstage4 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: SET name: knockstage5 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: SET name: knockstage6 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: SET name: knockstage7 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: SET name: knockstage8 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: CHECK seconds: 12 reap name: knockstage1 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: CHECK seconds: 12 reap name: knockstage2 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: CHECK seconds: 12 reap name: knockstage3 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: CHECK seconds: 12 reap name: knockstage4 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: CHECK seconds: 12 reap name: knockstage5 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: CHECK seconds: 12 reap name: knockstage6 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: CHECK seconds: 12 reap name: knockstage7 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: CHECK seconds: 12 reap name: knockstage8 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: REMOVE name: knockstage1 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: REMOVE name: knockstage2 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: REMOVE name: knockstage3 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: REMOVE name: knockstage4 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: REMOVE name: knockstage5 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: REMOVE name: knockstage6 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: REMOVE name: knockstage7 side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: REMOVE name: knockstage8 side: source mask: 255.255.255.255
Chain knockers (1 references)
pkts bytes target prot opt in out source destination
0 0 knockerreap all -- * * 0.0.0.0/0 0.0.0.0/0 ! ctstate NEW /* for port knock capability */
0 0 knockersort all -- * * 0.0.0.0/0 0.0.0.0/0 ctstate NEW /* for port knock capability */
0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 LOG flags 0 level 4 prefix "pktfail:knock|late|ctstate "
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: CHECK seconds: 60 reap name: knocker side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: REMOVE name: knocker side: source mask: 255.255.255.255
0 0 knockerreap all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0
Chain knockersort (2 references)
pkts bytes target prot opt in out source destination
0 0 knockstage2 tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:2 flags:0x17/0x02 recent: CHECK seconds: 12 name: knockstage2 side: source mask: 255.255.255.255 /* knock to stage 2 successful */
0 0 knockstage3 tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:3 flags:0x17/0x02 recent: CHECK seconds: 12 name: knockstage3 side: source mask: 255.255.255.255 /* knock to stage 3 successful */
0 0 knockstage4 tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:4 flags:0x17/0x02 recent: CHECK seconds: 12 name: knockstage4 side: source mask: 255.255.255.255 /* knock to stage 4 successful */
0 0 knockstage5 tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:5 flags:0x17/0x02 recent: CHECK seconds: 12 name: knockstage5 side: source mask: 255.255.255.255 /* knock to stage 5 successful */
0 0 knockstage6 tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:6 flags:0x17/0x02 recent: CHECK seconds: 12 name: knockstage6 side: source mask: 255.255.255.255 /* knock to stage 6 successful */
0 0 knockstage7 tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:7 flags:0x17/0x02 recent: CHECK seconds: 12 name: knockstage7 side: source mask: 255.255.255.255 /* knock to stage 7 successful */
Chain knockerstest (1 references)
pkts bytes target prot opt in out source destination
0 0 knockersort all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 LOG flags 0 level 4 prefix "knockertest fail "
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: CHECK seconds: 60 reap name: knocker side: source mask: 255.255.255.255
0 0 all -- * * 0.0.0.0/0 0.0.0.0/0 recent: REMOVE name: knocker side: source mask: 255.255.255.255
0 0 knockerreap all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0
Chain knockstage1 (2 references)
pkts bytes target prot opt in out source destination
0 0 knockerreap all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 recent: SET name: knockstage2 side: source mask: 255.255.255.255 /* Entry in log makes blacklisting get delayed until after knocking time window expires */ LOG flags 0 level 4 prefix "knocked: Stage1 "
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0
Chain knockstage2 (1 references)
pkts bytes target prot opt in out source destination
0 0 knockerreap all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 recent: SET name: knockstage3 side: source mask: 255.255.255.255 /* Entry in log makes blacklisting get delayed until after knocking time window expires */ LOG flags 0 level 4 prefix "knocked: Stage2 "
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0
Chain knockstage3 (1 references)
pkts bytes target prot opt in out source destination
0 0 knockerreap all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 recent: SET name: knockstage4 side: source mask: 255.255.255.255 /* Entry in log makes blacklisting get delayed until after knocking time window expires */ LOG flags 0 level 4 prefix "knocked: Stage3 "
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0
Chain knockstage4 (1 references)
pkts bytes target prot opt in out source destination
0 0 knockerreap all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 recent: SET name: knockstage5 side: source mask: 255.255.255.255 /* Entry in log makes blacklisting get delayed until after knocking time window expires */ LOG flags 0 level 4 prefix "knocked: Stage4 "
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0
Chain knockstage5 (1 references)
pkts bytes target prot opt in out source destination
0 0 knockerreap all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 recent: SET name: knockstage6 side: source mask: 255.255.255.255 /* Entry in log makes blacklisting get delayed until after knocking time window expires */ LOG flags 0 level 4 prefix "knocked: Stage5 "
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0
Chain knockstage6 (1 references)
pkts bytes target prot opt in out source destination
0 0 knockerreap all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 recent: SET name: knockstage7 side: source mask: 255.255.255.255 /* Entry in log makes blacklisting get delayed until after knocking time window expires */ LOG flags 0 level 4 prefix "knocked: Stage6 "
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0
Chain knockstage7 (1 references)
pkts bytes target prot opt in out source destination
0 0 knockerreap all -- * * 0.0.0.0/0 0.0.0.0/0
0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 recent: SET name: authorized side: source mask: 255.255.255.255 /* allows time-limited access */ LOG flags 0 level 4 prefix "knock full success "
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0