aboutsummaryrefslogtreecommitdiffstats
path: root/templates/ingress/pf-ban-socket.py
diff options
context:
space:
mode:
authorJan Tuomi <jan@jantuomi.fi>2025-12-29 23:16:22 +0200
committerJan Tuomi <jan@jantuomi.fi>2025-12-29 23:16:22 +0200
commit533bc95c751337d5f2952252b0378848cf078357 (patch)
tree2bf4ccadc31baf02d2a586fe7357af99787265dc /templates/ingress/pf-ban-socket.py
parent39feba1c79c1a0c2743932af35e157ba29d089d1 (diff)
Add crawler tarpit
Diffstat (limited to 'templates/ingress/pf-ban-socket.py')
-rw-r--r--templates/ingress/pf-ban-socket.py72
1 files changed, 72 insertions, 0 deletions
diff --git a/templates/ingress/pf-ban-socket.py b/templates/ingress/pf-ban-socket.py
new file mode 100644
index 0000000..cbcd80e
--- /dev/null
+++ b/templates/ingress/pf-ban-socket.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+import http.server
+import os
+import re
+import socketserver
+import subprocess
+
+SOCK_PATH = "/var/run/pfban/ban.sock"
+PF_TABLE = "blocked"
+
+# Simple, conservative filter to avoid junk / injection
+IP_RE = re.compile(r"^[0-9A-Fa-f:.]{3,}$")
+
+
+def ensure_socket_dir(path: str) -> None:
+ os.makedirs(os.path.dirname(path), mode=0o755, exist_ok=True)
+
+
+class BanHandler(http.server.BaseHTTPRequestHandler):
+ # Silence default logging
+ def log_message(self, format, *args):
+ return
+
+ def do_POST(self):
+ if self.path != "/ban":
+ self.send_response(404)
+ self.end_headers()
+ return
+
+ ip = self.headers.get("X-IP", "").strip()
+
+ if ip and IP_RE.match(ip):
+ subprocess.run(
+ ["/sbin/pfctl", "-t", PF_TABLE, "-T", "add", ip],
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL,
+ check=False,
+ )
+
+ self.send_response(204)
+ self.end_headers()
+
+ def do_GET(self):
+ self.send_response(405)
+ self.end_headers()
+
+
+class ThreadingUnixHTTPServer(
+ socketserver.ThreadingMixIn,
+ socketserver.UnixStreamServer,
+):
+ daemon_threads = True
+
+
+def main() -> None:
+ ensure_socket_dir(SOCK_PATH)
+
+ # Remove stale socket if present
+ try:
+ os.unlink(SOCK_PATH)
+ except FileNotFoundError:
+ pass
+
+ with ThreadingUnixHTTPServer(SOCK_PATH, BanHandler) as httpd:
+ # Allow nginx workers (www) to connect
+ os.chmod(SOCK_PATH, 0o660)
+
+ httpd.serve_forever()
+
+
+if __name__ == "__main__":
+ main()