How to Defend Against Brute-Force and DoS Attacks with Fail2ban, Nginx limit_req, and iptables
- Categories:
- tutorial
In this tutorial, I’ll explain how to protect your public-facing Linux server and Nginx web server from common threats, including brute-force and DoS attacks.
Servers exposed to the internet often face these types of attacks, which can disrupt service and compromise security.
These attacks can seriously disrupt a server’s performance and security. By implementing defense in depth, protection mechanism like Fail2ban, Nginx limit_req
, and iptables
are so important.
What is Brute-Force Attack and Denial-of-Service Attack?
Brute-Force Attack
This is when an attacker tries to guess a user’s password or other sensitive information by trying many different combinations very quickly. It’s like repeatedly trying different keys to unlock a door until one works. If successful, they can access private areas of your server.
Denial-of-Service (DoS) Attack
In a DoS attack, the attacker tries to overwhelm a server by flooding it with requests. A DoS attack can make your server slow or even crash, preventing real users from accessing it
Fail2ban
Fail2Ban is an intrusion prevention software framework. Written in the Python programming language, it is designed to prevent brute-force attacks.
How to Install fail2ban on Debian / Ubuntu
How to Setup fail2ban
After installing fail2ban
, the next steps involve configuring it to protect your server effectively. Here’s how to set it up and customize it for maximum security:
1. Configure the Default Jail Settings
The main configuration file is located at /etc/fail2ban/jail.conf
, but it’s better practice to override settings in /etc/fail2ban/jail.local
to avoid overwriting when updating Fail2ban. Create or edit the jail.local
file:
Common parameters to configure:
bantime
: Duration in seconds an IP is banned. Default value: 600 (600 seconds or 10 minutes)findtime
: Time window for considering repeated failed attempts. Default value: 600 (600 seconds or 10 minutes)maxretry
: Number of failed attempts before a ban. Default value: 5
I personally prefer to use these default values.
2. Enable and Customize Jails for SSH Protection
In the jail.local
file, find the [sshd]
section to enable SSH protection. By default, SSH protection is enabled for Debian and Ubuntu, in this case I just want to make it explicit with enabled = true
.
Adjust the parameters as needed for your specific security policy.
3. Enable nginx-limit-req and nginx-botsearch Jails
Fail2ban can monitor other services beyond SSH. Some additional common jails:
- [nginx-limit-req]: This jail works by identifying IPs that exceed a request threshold within a given time window, effectively catching high-frequency requests typical of DoS attacks. Fail2ban will then ban these IPs temporarily, minimizing the impact of the flood.
- [nginx-botsearch]: This jail helps by identifying common malicious patterns, such as bots attempting to access sensitive or admin paths repeatedly. Fail2ban will block these IPs automatically, reducing the need for manual intervention with
iptables
.
To enable these, make sure they’re set to enabled = true
in the jail.local
file.
4. Restart Fail2ban and Verify Configuration:
After editing the configuration, restart Fail2ban to apply the changes:
Check the status to confirm that it’s running and protecting the desired services:
You should see a list of enabled jails, each monitoring the specified service logs.
5. Test Fail2ban Functionality:
Simulate a failed login attempt to see if Fail2ban blocks the IP after repeated attempts (you can try logging in with an incorrect SSH password multiple times). After reaching the maxretry
limit, Fail2ban should ban the IP.
6. View Active Bans and Unban IPs if Necessary:
To see currently banned IPs:
To unban an IP, use:
Fail2ban will now be actively monitoring and banning IPs based on the rules you configured, helping secure your server against brute-force and other common unauthorized access attempts.
Nginx limit_req
The Nginx ngx_http_limit_req_module module (0.7.21) is used to limit the request processing rate per a defined key, in particular, the processing rate of requests coming from a single IP address. The limitation is done using the “leaky bucket” method.
Nginx limit_req Nodelay
The nodelay
setting in Nginx’s limit_req
directive can significantly affect how requests are throttled. Here’s a breakdown to help decide whether or not to use nodelay
:
What nodelay
Does
- Without
nodelay
(the default setting), Nginx will queue excess requests and serve them at the rate specified inlimit_req_zone
. This works well for spreading out requests rather than dropping them immediately. - With
nodelay
, Nginx immediately rejects requests that exceed the limit, without queuing. This makes it more strict, immediately responding with a 503 (Service Unavailable) error if the limit is reached.
When to Use nodelay
Consider enabling nodelay
if:
- You’re dealing with high traffic or frequent burst attacks and want immediate rejection to conserve server resources.
- You have endpoints where latency is critical, and you prefer dropping excess requests rather than queuing them (e.g., API endpoints with strict rate limits).
When Not to Use nodelay
Keep nodelay
disabled (default) if:
- You want to give legitimate users a chance to access the site, even under load.
- Your server can handle the request queue, and you’d rather not drop requests unless absolutely necessary (e.g., user-facing sites where some waiting is acceptable).
Example Configuration
If you decide to use nodelay
, here’s how to apply it:
Simulate Flood to Web Server
You can simulate a request flood by using ab
or ApacheBench:
This command sends 1k requests with 100 requests concurrently.
iptables
Using iptables
, you can add an extra layer of protection to your server by controlling incoming and outgoing traffic. Here’s a straightforward setup to help secure an Nginx server against common attacks.
1. Install iptables
(if not already installed)
Most Ubuntu systems come with iptables
pre-installed. Check if it’s installed with:
If not installed, install it with:
2. Allow SSH, HTTP, and HTTPS Traffic
Allow SSH, HTTP, and HTTPS traffic to ensure you can manage the server and serve web content.
3. Allow Rules for APT
4. Limit SSH Connections
If you want to limit SSH connection attempts to prevent brute-force attacks, set a rate limit:
This rule allows a maximum of 3 SSH connection attempts per minute. Excess attempts will be blocked.
5. Block All Other Traffic (Be Careful)
Block all other incoming traffic for security, except those you specifically allowed. Be careful with this rule, as it blocks all incoming connections not specifically allowed above.
6. Check and Save Your Rules
View current rules with:
Save the rules to apply, so the rules will not lost after rebooting. Use iptables-persistent
:
7. How to Delete a Rule
Last, But Not Least
Those are all the steps, but remember, implementing Fail2ban, Nginx limit_req, and iptables won’t make your server 100% secure. Keep monitoring your server and web application regularly.
Recent Posts
How to Defend Against Brute-Force and DoS Attacks with Fail2ban, Nginx limit_req, and iptables
In this tutorial, I’ll explain how to protect your public-facing Linux server and Nginx web server from common threats, including brute-force and DoS attacks.
Is Getting AWS Solutions Architect Associate Certification Worth It?
If you are a full-time Software Engineer, there's no strong need to pursue this certification.
DevSecOps
My Notes about DevSecOps
AWS Secrets Manager
Explanation about AWS Secrets Manager with example code.
Envelope Encryption
Envelope encryption is the practice of encrypting plaintext data with a data key, and then encrypting the data key under another key.