The -I option to iptables does not append (which is -A), it is used to insert a new rule. The difference is that -A adds new rules at the end of the chain, whereas, by default, -I adds new rules at the beginning of the chain.
Since chains are read in-order, it is important that your -j ACCEPT rule is processed before the -j DROP rule.
There are several ways to do that:
- Use
-A rather than -I to add your rule. This will ensure that the rules are added at the end, and if you do not reorder your commands, they will be in the correct order. I would recommend this over the other two options.
- Continue using
-I, but swap your rules. That way, the -j ACCEPT rule will be processed before the -j DROP rule.
-I optionally takes an extra argument, the location in the chain where to insert the new rule, which should be a number. Since you add the -j ACCEPT rule without any argument, that makes it the first rule (i.e., line 1); so, add the second rule on line 2:
iptables -I INPUT 2 -p tcp -s 0.0.0.0/0 --dport 21 `-j DROP`
Having said that, if you are going to have many rules like the first one, then for performance reasons it's better to create a custom chain:
iptables -N check-ftp
iptables -A check-ftp -s IP1 -j ACCEPT
iptables -A check-ftp -s IP2 -j ACCEPT
[...]
iptables -A check-ftp -j DROP
iptables -A INPUT -p tcp --dport 21 -j check-ftp
This creates a single rule for "FTP connections" in your chain. That way, if the incoming network packet is not an FTP packet, then the netfilter code will not go over the (long) list of rules to determine whether to allow this particuler packet, but move on to the next rule immediately. If it is an FTP packet, then it will go over the long list.