Pivot Web Development

Loading breadcrumbs...

Building a Fortress

Setting Up Nginx, Securing Endpoints with Fail2ban, and Banning Malicious Actors

Imagine your web application is a bustling coffee shop. You’ve got a smooth-talking barista (Svelte) taking orders from customers, a skilled chef (Django) making the magic happen in the kitchen, and a database somewhere in the cloud acting as your inventory system. All of this needs to work seamlessly, while keeping the riffraff out. Here’s how I built my fortress using Nginx, Fail2ban, and a daily ban list of known troublemakers.


Step 1: Nginx as the Gateway

Think of Nginx as the maître d’ at the entrance to your shop. It’s not making the coffee or baking the pastries, but it knows where everything is and ensures customers get what they need without barging into the kitchen or the supply closet.

I set up Nginx as a reverse proxy, which is a fancy way of saying that all traffic first goes through Nginx before being routed to the appropriate place. If someone wants to see your fancy menu (the Svelte frontend), Nginx forwards the request to the Svelte app. If someone places an order (via an API call), Nginx sends it to Django. Nginx ensures everything stays in order, even when the shop gets busy.

Using Docker made this setup even cleaner—each part of the shop (Svelte, Django, Nginx) is like its own container, neatly compartmentalized but still working together. The cloud database stays outside, reachable by Django, but invisible to the customers.


Step 2: Protecting Against Endpoint Snooping

Soon after opening my "shop," I noticed something fishy. Some visitors weren’t here for coffee or pastries—they were poking around, trying to access private things like the .env file (your recipe book containing secret ingredients like API keys). Of course, these files were locked down and protected, but the fact they were even trying was alarming.

Enter Fail2ban, the bouncer. Fail2ban watches the logs like a hawk, looking for shady behavior. If someone repeatedly tries to access a forbidden endpoint or fumbles their password too many times, Fail2ban steps in and bans their IP address from coming back. It’s like telling your bouncer to toss out anyone trying to sneak into the kitchen.

I configured Fail2ban to target specific “naughty” behavior, like attempts to access endpoints that should never be touched. Each time someone triggered one of these alarms, Fail2ban would add them to the ban list, ensuring they wouldn’t bother me again.


Step 3: Gathering a Daily “Most Wanted” List

While Fail2ban was great at catching the sneaky troublemakers I could see, I realized there was a whole world of malicious actors already known to be bad news. Think of this as having a bouncer who doesn’t just recognize individual troublemakers but carries a list of “most wanted” criminals to keep out.

I found large, community-maintained lists of known bad IP addresses from sources like FireHOL and IPsum. These lists are constantly updated with the IPs of malicious actors—bots, spammers, hackers, and the like. I decided to download these lists every day and merge them into my ban list, ensuring Fail2ban would recognize and block these bad actors before they could even step foot in the shop.

Setting this up was simple: I used a small script to fetch the lists daily, process them, and update my bouncer’s (Fail2ban's) instructions. Now, my shop stays one step ahead of the shady characters without me having to monitor every move.


The Big Picture

With Nginx managing traffic, Svelte and Django working harmoniously through Docker, and Fail2ban keeping out the riffraff, my server became a well-oiled machine. The cloud database stays secure, and malicious actors are either booted out by Fail2ban or preemptively banned by my daily “most wanted” list.

This setup doesn’t just protect against attacks; it also gives me peace of mind. Whether you’re running a small web app or scaling up to something bigger, having the right tools in place makes all the difference. Your server isn’t just running—it’s fortified, organized, and ready to handle anything.