Banning Annoying IPs With pf and httpd
If you have ever run a server exposed to the internet and had a peek at the logs, you already know it is under attack constantly. Even if your server is secured (like mine) you still see the hundreds of web requests by malicious bots trying to pwn you with low hanging fruit, looking for exposed .env and credentials.json files, trying to access WordPress admin pages, attempting path traversal exploits, and so on.
As of posting my site has basically no readers so most web traffic comes from myself and bots.
From goaccess:
2 - Requested Files (URLs) Total: 366/4403 Hits h% Vis. v% Tx. Amount Mtd Proto Data ---- ------ ---- ------ ---------- ---- -------- ---- 6813 16.38% 2412 63.61% 52.31 KiB GET HTTP/1.1 / 1344 3.23% 0 0.00% 0.0 B --- --- <UNKNOWN> 518 1.25% 260 6.86% 0.0 B GET HTTP/1.1 /.env 415 1.00% 281 7.41% 0.0 B GET HTTP/1.0 / 377 0.91% 83 2.19% 0.0 B GET HTTP/1.1 /cgi-bin/luci/;stok=/locale 295 0.71% 109 2.87% 0.0 B GET HTTP/1.1 /.git/config 166 0.40% 108 2.85% 0.0 B POST HTTP/1.1 /bin/sh
I still need to add a robots.txt
4 - Not Found URLs (404s) Total: 366/2429 Hits h% Vis. v% Tx. Amount Mtd Proto Data ---- ------ ---- ------ ---------- --- -------- ---- 732 1.76% 0 0.00% 0.0 B GET HTTP/1.1 /robots.txt 416 1.00% 0 0.00% 0.0 B GET HTTP/1.1 /server-status 350 0.84% 0 0.00% 0.0 B GET HTTP/1.1 /js/script.js 303 0.73% 0 0.00% 0.0 B GET HTTP/1.1 /cgi-bin/luci/;stok=/locale 225 0.54% 0 0.00% 0.0 B GET HTTP/1.1 /favicon.ico 166 0.40% 0 0.00% 0.0 B GET HTTP/1.1 /wp-admin/setup-config.php 160 0.38% 0 0.00% 0.0 B GET HTTP/1.1 /wordpress/wp-admin/setup-config.php
As you can see, a lot of bots trying to do annoying shit like access a wp-admin panel that does not exist or a .git directory.
I run this server on OpenBSD. I thought it would be cool if I could use pf and a simple script to to automatically ban addresses trying to do stuff like this so, I wrote a one liner to do it.
#!/bin/sh
cat /var/www/logs/access.log | grep -E 'wp-admin|openai|/bin/sh/|php' | awk '{printf $(NF-1)"\n"}' | sort | grep -v '-' | uniq
This cats everything from the most recent access.log into grep, filters by lines containing obvious malicious requests or just stuff I don't want, gets the requester's ip address with awk, filters out '-', and filters out duplicate addresses.
#!/bin/sh /usr/bin/local/get-baddies.sh | xargs /sbin/pfctl -t baddies -T add
Next script just pipes this into xargs and runs pfctl to add all the addresses to the baddies table. I do this instead of reading from a file so I don't need to reload my pf rules everytime I update the table.
In my pf.conf I have this to block everything in the baddies table:
# block baddies table <baddies> persist block in quick on egress from <baddies> block out quick on egress to <baddies>
And I create this crontab entry:
block baddies from access.log * * * * * /usr/local/bin/block-baddies.sh
And that's it. You can view the addresses in the table with pfctl -t baddies -T show, and if you try to make a request to any of those addresses it should get blocked, and if any of them try to make a request to you it should also get blocked.
You can also (probably should) just use pfbadhost, but I've been doing a lot of reading on configuring pf and had this idea all morning and had to give it a try.