Andrea Sodomaco - ICT scattered considerations
010101010111001101100101001000000111010001101000011001010010000001110011011011110111010101110010011000110110010100101100001000000100110001110101011010110110010100100001
How to quickly block access to certain IPs on a Linux system using iptables and kernel network filtering, in an easy, secure and free way. Practical examples related to a web server.
The topic has a wide range of solutions, both using OS functions and adopting countless software operating at various levels. It is not the purpose of this page to analyze the topic exhaustively or to provide a solution for the complete protection of a web server.
The purpose is to provide a quick, always available and effective solution that requires some theoretical knowledge and provides the possibility to be developed and customized.
If the practical applications of this solution are not particularly extensive, the theoretical value of the approach that can have an educational purpose is.
When managing a website, whether it is protected by other security mechanisms or not, there may be a need to quickly block some IPs that perform operations dangerous for the security or proper functioning of the site. Sometimes these are real hacker attacks, but sometimes they are just poorly calibrated bots or more malicious than dangerous activities.
There are various ways to do this, such as external firewalls or web server configurations. The tool I propose is the use of the network filtering system integrated into the Linux server. The advantage of this approach is to be always available and very efficient: not everyone has an external firewall, and IP filtering at the web server level could be ineffective in case of very intense DoS attacks, for example.
The Linux network filtering can be used by other tools and software in use on your system (fairly common examples are firewalld and fail2ban). To verify this, run the following command and check the output
# iptables -L -n Chain INPUT (policy ACCEPT) target prot opt source destination Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination
If the result is different from what is reported here, it is very likely that there are other tools that use the kernel's network filtering: do not proceed if you do not know what you are doing!
I refer to the case of a web server but the example can be easily applied to a mail server or a MySQL installation, or others.
Here is an example of an Nginx log that many webmasters may have seen
97.74.229.113 - - [01/Nov/2022:05:04:59 +0100] "GET /admin//config.php HTTP/1.1" 404 20 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2" 45.227.254.54 - - [01/Nov/2022:05:54:01 +0100] "\x03\x00\x00/*\xE0\x00\x00\x00\x00\x00Cookie: mstshash=Administr" 400 166 "-" "-"
Whether and to what extent this kind of accesses can be really worrying is beyond the scope of this discussion. Let's assume that we still want to block the IP.
For those who are completely ignorant of iptables, before starting, I recommend taking a look at this how-to
Immediate and drastic solution to block the IP 45.227.254.54:
# iptables -I INPUT -s 45.227.254.54 -j DROP
The command must be executed as root or preceded by the sudo command.
This simple command blocks any access to the machine's networking from the specified address. If the activity of a certain IP is causing malfunctions to your web server, this is how you solve the problem at the root.
After executing the command, the situation will be as follows:
# iptables -L -n Chain INPUT (policy ACCEPT) target prot opt source destination DROP all -- 45.227.254.54 0.0.0.0/0 Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination
To delete all the rules of the INPUT chain, execute the following command (always as root or preceded by sudo)
# iptables -F INPUT
This will restore the initial situation.
The kernel's network filtering passes all incoming traffic through the so-called chains, which are lists of rules made up of two parts: selection criteria (match) and associated action (target).
In particular, there are 3 predefined chains: INPUT, FORWARD, OUTPUT. The part that interests us is that all incoming traffic passes through the INPUT chain, which is initially empty and the default policy is ACCEPT, so all traffic is accepted and passed to the kernel processing making it available to the machine's software.
The command
# iptables -I INPUT -s 45.227.254.54 -j DROP
inserts at the beginning (-I) of the INPUT chain the rule that identifies all traffic coming (-s stands for source) from the IP 45.227.254.54 and associates it with the DROP action (i.e. immediate deletion).
In the situation that is created after executing the command, all incoming traffic passing through the INPUT chain is compared with the inserted rule. If it matches, meaning that it originates from the specified IP address, the associated action (target) is applied. In this case, the traffic is dropped. Traffic that does not match the rule moves on to the next rule, if there is one. Since there are no other rules, the traffic is accepted.
Let's develop a better solution to make it more manageable and especially more robust in avoiding interference with other tools or services.
Let's start by observing that attacks usually only target a few services and therefore a few TCP ports. Even in the case of port scanning activity, this will only be annoying on active services and their exposed ports.
Let's take the practical example described above: the idea is to manage the traffic of this service in a separate chain. This solution at least avoids unwanted interactions with other services, especially with SSH.
Let's proceed with the following commands:
# iptables -N myhttp # iptables -N myblocked # iptables -I INPUT -p tcp --dport 80 -j myhttp # iptables -I INPUT -p tcp --dport 443 -j myhttp # iptables -I myblocked -j DROP # iptables -L -n Chain INPUT (policy ACCEPT) target prot opt source destination myhttp tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:443 myhttp tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination Chain myblocked (0 references) target prot opt source destination DROP all -- 0.0.0.0/0 0.0.0.0/0 Chain myhttp (2 references) target prot opt source destination
In this way, we have created two chains. The myhttp chain is used to direct all http and https traffic (incoming on ports 80 and 443). Traffic that does not concern http or https does not match the rules inserted into the INPUT chain and will therefore still be accepted. The myhttp chain currently contains no rules and all http and https traffic, despite entering the chain, will pass freely. The other chain named "myblocked" blocks all traffic directed to it and is not currently in use.
To block http and https traffic from a specific IP, the following command is sufficient:
# iptables -I myhttp -s 45.227.254.54 -j myblocked
This command inserts a rule into the myhttp chain that forwards traffic coming from IP 45.227.254.54 to the myblocked chain, which in turn blocks it.
The advantage of this approach is that once the initial setup is done, we are sure not to interfere with other services (avoiding the risk of blocking, for example, ssh) since we will be modifying exclusively the myhttp chain. Furthermore, if we insert various IPs into myhttp that point to myblocked chain, we can uniformly manage the desired behavior. For examp
... -j DROP
with
.... -j REJECT --reject-with icmp-port-unreachable
# iptables -I myblocked -j LOG --log-prefix "MYBLOCKED:"
# iptables -n -L myblocked Chain myblocked (1 references) target prot opt source destination LOG all -- 0.0.0.0/0 0.0.0.0/0 LOG flags 0 level 4 prefix "MYBLOCKED:" REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
With this solution, we achieve two results. The first is to obtain a log in syslog every time traffic is blocked. The second is to adopt a more correct way to block traffic.
Between "DROP" and "REJECT --reject-with icmp-port-unreachable," the substantial difference is that in the first case, the traffic is completely ignored, while in the second, there is a response from the server that communicates to the sender that the traffic is being refused (see ICMP protocol).
With DROP, the effect is that the blocked sender perceives a situation identical to when the server is turned off or disconnected from the network. Moreover, with DROP, there is minimal effort for the kernel, which is not employed to give even a minimal response, making any DoS attacks even less effective. On the other hand, a client that is blocked by DROP will try to connect to the server multiple times as expected by the TCP/IP protocol, only giving up after a long enough timeout.
With REJECT with icmp-port-unreachable, on the other hand, the client will have immediate evidence of being blocked (obviously, it cannot know if the service is not active on the server or if only its IP has been blocked). For clients that still respect the protocols, this mode will be more efficient and correct. The downside to this solution is that it makes it evident to a potential hacker that the server exists and may have other vulnerabilities.
A specific case worth mentioning is when protecting an SMTP server (port 25) that sends external mail. In this case, REJECT with icmp-port-unreachable should be used because anti-spam filters could verify the existence of the server, a test that would fail when using DROP.
An alternative that could be considered is icmp-host-unreachable.
There are many topics to delve into and possible enhancements such as making changes persistent, efficiently managing a large number of IPs or ranges, and integrating automatically with your application.
I hope to be able to delve into some of these aspects in other articles based on the feedback I receive.
Screenshot of the IP check result on the website abuseipdb.com
If suspicious activity from a specific IP is found in the logs, it is possible to check the IP's malicious activity on a specialized website before blocking it. I often use abuseipdb.com for this purpose.
These types of websites not only allow you to verify the danger of an IP, but they can also provide blacklists to use preventively.
iptables is not the only tool that allows manipulation of kernel net filtering. Valid alternatives could be ufw (considered more user-friendly) or nftables (the most modern version of iptables). I prefer iptables because they are often preinstalled, have a vast documentation available online, but I see no disadvantages in using alternatives.
One characteristic of iptables (unlike ufw, for example) is that, by manipulating the kernel's memory tables, the changes made are not permanent and disappear on reboot without explicit measures to save them. This is an extra burden for the development of applications based on the tool, but a huge advantage in case of testing on remote machines, as it will be possible to fix serious problems caused by inappropriate commands by simply rebooting the system.