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.