Re: Best practice for resetting (iptables) firewall state

2023-07-19 Thread Bastian Bittorf
On Tue, Jul 11, 2023 at 11:25:55PM -0600, Philip Prindeville wrote:
> I'm working on a wrapper for using xt_asn and xt_geoip in xtables-addons, and 
> was wondering about how best to solve certain problems.
> 

maybe the mechanism for stop/start/update used here is useful:
https://github.com/bittorf/block-TOR-nodes/blob/master/tornodes_block.sh

___
openwrt-devel mailing list
openwrt-devel@lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel


Best practice for resetting (iptables) firewall state

2023-07-11 Thread Philip Prindeville
Hi,

I'm working on a wrapper for using xt_asn and xt_geoip in xtables-addons, and 
was wondering about how best to solve certain problems.

Since, for now, I'll be using shell and UCI, I'm not going to maintain state 
(old/current vs. new/desired state) of the firewall so I won't be able to 
easily optimize firewall changes.  The best I can do is detect the previous 
state, unload it, and then apply the new state.

(Yes, in an ideal world, I'd apply any new rules, then unapply any old rules 
that aren't being used any more...)

An example: I'm blocking the countries AA BB CC, but decide instead to block BB 
CC DD.

The optimal path would be:

1. Add block DD
2. Remove block AA

But that's more state than can be easily represented in a shell script that 
doesn't maintain any state beyond the current desired config which would be 
/etc/config/xyzzy.

So, the easiest/most straightforward solution I can think of is:

1. Run "iptables-save | grep '^-A input_wan_rule .* -j myprefix_.*' to find 
where my rules are hooked in (ditto for "forwarding_wan_rule");
2. Change '-A' to '-D' and delete the hooks;
3. Extract the rule name (target of -j above) and do "iptables -F rule_name" 
then "iptables -X rule_name" to remove the rule from the firewall
4. Generate the new rules based on the current contents of /etc/config/xyzzy

And yes, obviously the firewall is most vulnerable while it's being 
reconfigured, since rules are being non-atomically torn down and built back up 
again, and depending on the number of rules and the speed of the processor, 
this can be a non-negligible amount of time.

One observation: for the most part, the rules themselves while numerous are 
trivial.  I don't have to worry about replacing an old version of a rule with a 
new but different version of that rule.  If I'm blocking country AA, then the 
rule is present in a trivial and invariant form like:

-A forwarding_wan_rule -m geoip --src-cc AA -j fwr_AA

So the granularity of optimization would be:

1. If country X was in the old rules but not the new ones, delete it;
2. If country X is in both the old and new rules, don't do anything;
3. If country X wasn't in the old rules but is in the new ones, add it;

With the only complexity being in preserving the ordering (although since 
countries don't overlap, ordering shouldn't make any difference).

And another caveat: if the country database changes, and the CIDR list for 
country AA gets updated, then the only way to squirt the update into the kernel 
again by deletion/reinsertion, which invalidates (2) above because the country 
X isn't really 'invariant' in that case.  Well, it could be if we were using 
ipset's and we could just apply the relevant deltas to the ipset, but we're not 
using ipsets.

So deleting all the rules and adding them back might be unavoidable in any 
case, without doing a lot more work (which would involve keeping "last" and 
"next" versions of the country databases, and detecting if they've changed, 
etc).

For atomicity, I suppose I could use a generational version of the rules, build 
generation N+1, insert the hook saying "use N+1", then delete the hook for N, 
then delete the chain for N...  and "iptables -E ..." would help with renaming 
the new chain to the old chain after the old chain got deleted.

Something like:

# construct new rules for countries BB, CC, DD...
iptables -N cc_BB_
iptables -A cc_BB_ -m limit --limit 1/sec --limit 5 -j LOG --log-level 4 
--log-prefix "DROP cc BB: "
iptables -A cc_BB_ -j DROP
...

# create new container for new country rules
iptables -N country_rules_
# hook in the individual new country rules
iptables -A country_rules_ -m geoip --src-cc BB -g cc_BB_
iptables -A country_rules_ -m geoip --src-cc CC -g cc_CC_
..

# add the hook into the new country rules
iptables -A forwarding_wan_rule -j country_rules_

# clean up the old country rules
iptables -D country_rules -m geoip --src-cc AA -g cc_AA
iptables -D country_rules -m geoip --src-cc BB -g cc_BB
...

# remove the hook into the old country rules
iptables -D forwarding_wan_rule -j country_rules

# remove the old country rules
iptables -F cc_AA
iptables -F cc_BB
...

# rename the new rules into the old names
iptables -E country_rules_ country_rules
iptables -E cc_BB_ cc_BB_
iptables -E cc_CC_ cc_CC_
...


Anyone have any suggestions, tips & tricks, etc. to give on how best manage 
firewall state without too much extra complexity?

Also, how do you write an init.d wrapper that doesn't have a persistent 
foreground process, but handles "start", "stop", "reload", etc.

Thanks,

-Philip



___
openwrt-devel mailing list
openwrt-devel@lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel