commit - /dev/null
commit + d2c2729845a9435ef60e3e3e0a4d58e2f8b1184f
blob - /dev/null
blob + 0dc12990ebcf6103910cec0b8cabc9c1f5fca1b1 (mode 644)
--- /dev/null
+++ README.md
+# http-ban
+
+Hey! It's just a simple script I'm testing as I don't want a huge HTTP
+filter solution. It targets httpd(4) log format and pfctl(8).
+
+It's messy and harsh to read. Sorry about this. Written it a while ago
+and yet updating it a bit, removing unused code.
+
+Feel free to try it and share enhancements/ideas around!
+
+For me it has been quite effective.
+
+# Installation
+
+1. Create a user
+
+ # useradd -u 404 -s /sbin/nologin -d /var/empty _httpban
+
+2. Install the script
+
+ # install -m 755 -o root -g bin http-ban.sh /usr/local/bin/http-ban
+
+3. Give the required permissions in /etc/doas.conf
+
+ # cat /etc/doas.conf
+ permit nopass _httpban cmd /bin/cat args /var/www/logs/access.log
+ permit nopass _httpban cmd /usr/bin/zcat args /var/www/logs/access.log.*gz
+ permit nopass _httpban cmd /sbin/pfctl args -t players -T show
+ permit nopass _httpban cmd /sbin/pfctl args -t players -T add -f-
+ permit nopass _httpban cmd /sbin/pfctl args -t players -T delete -f-
+
+4. Create a <players> table in /etc/pf.conf
+
+ # cat /etc/pf.conf
+ table <players> persist
+ block in quick on egress from <players>
+
+3. Run it as _httpban (here every 5 minutes)
+
+ # crontab -eu _httpban
+ */5 * * * * -s http-ban
+
+# TODO
+
+- [ ] parametrize the table name, as <players> is not really a good name
+- [ ] remove unused filter functions
+- [ ] make it more pleasant to read / give the impression I'm mentally saner
blob - /dev/null
blob + 73554eacfb8a06df4ebd0bf358405ed2bf6e8789 (mode 755)
--- /dev/null
+++ http-ban.sh
+#!/bin/sh
+QUICK_WHITELIST=192.168.10.10
+alias log="logger -st http-ban"
+alias show="doas /sbin/pfctl -t players -T show"
+alias ban="doas /sbin/pfctl -t players -T add -f-"
+alias grace="doas /sbin/pfctl -t players -T delete -f-"
+whitelist() {
+ printf %s\\n $QUICK_WHITELIST
+}
+last_access() {
+ doas /bin/cat /var/www/logs/access.log
+}
+access() {
+ last_access
+ doas /usr/bin/zcat /var/www/logs/access.log.*gz 2>/dev/null
+}
+NOT_200_Q() {
+ awk '$(NF-1) != 200 {print}' | sort
+}
+NOT_200_C() {
+ awk '$(NF-1) != 200 {print $(NF-1), $2}' | sort -nk1 | uniq -c | sort -nk1
+}
+NOT_200_HOSTS_C() {
+ awk '$(NF-1) != 200 {print $2}' | sort | uniq -c | sort -nk1
+}
+IN_300_HOSTS_Q() {
+ awk '$(NF-1) >= 300 && $(NF-1) < 400 { print }' | sort
+}
+IN_300_HOSTS_C() {
+ awk '$(NF-1) >= 300 && $(NF-1) < 400 { print $2 }' | sort | uniq -c | sort -nk1
+}
+IN_400_HOSTS_Q() {
+ awk '$(NF-1) >= 400 && $(NF-1) < 500 { print }' | sort
+}
+IN_400_HOSTS_C() {
+ awk '$(NF-1) >= 400 && $(NF-1) < 500 { print $2 }' | sort | uniq -c | sort -nk1
+}
+IN_500_HOSTS_Q() {
+ awk '$(NF-1) >= 500 && $(NF-1) < 600 { print }' | sort
+}
+IN_500_HOSTS_C() {
+ awk '$(NF-1) >= 500 && $(NF-1) < 600 { print $2 }' | sort | uniq -c | sort -nk1
+}
+limit() {
+ awk -vtrig="${1:-10}" ' $1 >= trig { print $2 }'
+}
+pre_block() {
+ last_access | IN_500_HOSTS_C | limit 1
+ last_access | IN_400_HOSTS_C | limit 3
+ access | IN_300_HOSTS_C | limit 3
+}
+apply_whitelist() {
+ GF=$(for ip in $(whitelist); do printf -- '-ve %s\n' "$ip"; done)
+ GF=${GF:-'.*'}
+ grep $GF
+}
+block() {
+ pre_block | sort | uniq | apply_whitelist
+}
+umask 127
+block | sort >/tmp/http-ban.new
+show | sort | awk '{ print $1 }' >/tmp/http-ban.current
+DIFF=$(comm /tmp/http-ban.new /tmp/http-ban.current)
+NEW=$(comm -23 /tmp/http-ban.new /tmp/http-ban.current)
+GRACE=$(comm -13 /tmp/http-ban.new /tmp/http-ban.current)
+rm /tmp/http-ban.*
+test -n "$NEW" -o -n "$GRACE" || exit 0
+test -n "$NEW" &&
+ log BANNING NEW IPs &&
+ log <<.
+$NEW
+.
+test -n "$GRACE" &&
+ log GRACING OLD IPs &&
+ log <<.
+$GRACE
+.
+ban <<.
+$NEW
+.
+grace <<.
+$GRACE
+.
+test -n "$QUICK_WHITELIST" &&
+ grace <<.
+$QUICK_WHITELIST
+.