How does one close all _existing_ TCP connections on some ports using IPTables?

Solution 1:

No iptables rule will ever close an existing TCP connection as that involves actively transmitting a message with the FIN bit. That is done by the application and not by a packet filter.

On the other hand iptables can, at any moment, block your application from receiving or transmitting new packets over any existing connection and it can also deny any new connections from getting established.

That is regardless of wether you have a stateful firewall or not.

It all depends on where exactly you insert your new firewall rules. Because, remember, your firewall rules are checked in the order they are listed and processing will stop at the first dispositive match.

I.e. a simple stateful firewall:

[root@host ~]# iptables-save
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [441:59938]
                     #1  < INSERT NEW RULE HERE
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
                     #2  < INSERT NEW RULE HERE
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT
                     #3  < INSERT NEW RULE HERE
-A INPUT -j REJECT --reject-with icmp-host-prohibited
                     #4  < iptables -A WILL APPEND NEW RULE HERE
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT

Now if you want a new rule:

INPUT -s 10.0.0.89/32 -j REJECT --reject-with icmp-port-unreachable

and insert it at position #1 all packets received from that host will be blocked.

Insert that rule at position #2 and packets on existing connections will still be allowed but no new connections can be established.

Inserting that particular new rule at position #3 is useless, as the effect is the same as not having specific policy for 10.0.0.89 at all, but that would be the right place to place a rule to granting access to 10.0.0.89 to additional ports.

And using iptables -A INPUT to append a new rule to the INPUT chain is useless as that will place the rule at position #4 where all traffic is already rejected by the INPUT -j REJECT --reject-with icmp-host-prohibited rule.

In short: use the rule number option in iptables -I (instead of ipatbles -A) to place the new (temporary) rule where it will have the desired effect:

sudo iptables -I <rule number> INPUT -s 10.0.0.89/32 -j REJECT --reject-with icmp-port-unreachable

If, with the same stateful firewall configuration, you want to stop allowing plain HTTP, you can Delete the rule allowing traffic to port 80

sudo iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT

but doing so will not empty the session state table used by iptables and existing connections to port 80 will still be allowed by the rule -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

You can solve that by simply stopping/restarting the webserver, that will properly close those open sessions by sending FIN messages and clear them from the session state table.

Alternatively you can add a rule blocking packets to port 80 at position #1.