add README
This commit is contained in:
parent
9145377125
commit
d780f2ba6c
61
README.md
Normal file
61
README.md
Normal file
@ -0,0 +1,61 @@
|
||||
# ufwban
|
||||
|
||||
A little CLI tool (wrapping **ufw**) that read Nginx access logs and block ip based on simple rules.
|
||||
**THIS IS NOT** a replacement of **fail2ban**, i just do it for fun and to have a simple tool to configure quickly banning undesired IP.
|
||||
|
||||
This a **RUDE** IP deny. It will ban the IP on all machine ports with no ban time, so be careful !
|
||||
|
||||
## Requirements
|
||||
* ufw
|
||||
* Nginx
|
||||
* Python >= 3.10
|
||||
|
||||
## Configuration
|
||||
Create a `conf.json` next to the script. Use the [conf.json.example](./conf.json.example) for sample.
|
||||
|
||||
```json
|
||||
{
|
||||
"rules": {
|
||||
"codes": [],
|
||||
"contents": [],
|
||||
"agents": []
|
||||
},
|
||||
"whitelist": []
|
||||
}
|
||||
```
|
||||
* **codes**: List of unauthorized HTTP codes
|
||||
* **contents**: string parts that are not not allowed in the request URL (ex: /x00, .json, .php, .env.local, etc...)
|
||||
* **agents** string parts that are not not allowed in user-agent (ex: bot)
|
||||
* **whitelist**: List of IP to whitelist (ex: 192.168.1.1)
|
||||
|
||||
## Run
|
||||
```bash
|
||||
usage: ufwban [-h] [--dry-run] [--refresh] [--reload] [--live]
|
||||
|
||||
Ban ip from Nginx access logs based on simple rules.
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--dry-run
|
||||
--refresh Drop all the deny ip in the UFW table and return
|
||||
--reload Reload the UFW firewall
|
||||
--live Read inputs from stdin
|
||||
```
|
||||
|
||||
* Batch mode:
|
||||
```bash
|
||||
# read all access.log*, parse Nginx log and ban ip
|
||||
python ufwban.py
|
||||
|
||||
# you can launch a --dry-run mode to see which ip is going to be denied
|
||||
python ufwban.py --dry-run
|
||||
|
||||
# drop all "DENY IN" ufw rules (be careful)
|
||||
python ufwban.py --refresh
|
||||
```
|
||||
|
||||
* Live mode:
|
||||
```bash
|
||||
# Read and parse Nginx logs on each new entry and ban ip
|
||||
tail -f /var/log/nginx/access.log | python ufwban.py
|
||||
```
|
||||
16
ufwban.py
16
ufwban.py
@ -22,8 +22,11 @@ UFW_CONF = "conf.json"
|
||||
|
||||
|
||||
class UFW:
|
||||
"""Wraps ufw binary commands"""
|
||||
|
||||
@staticmethod
|
||||
def drop_all():
|
||||
"""Remove all "DENY IN" rules."""
|
||||
logging.info("dropping all deny rules...")
|
||||
while (ufw_deny_ips := UFW.list_deny()) and len(ufw_deny_ips):
|
||||
for ip, id_ in ufw_deny_ips.items():
|
||||
@ -33,6 +36,7 @@ class UFW:
|
||||
|
||||
@staticmethod
|
||||
def reload():
|
||||
"""Force ufw reload."""
|
||||
logging.info("reloading ufw...")
|
||||
process = subprocess.run(["ufw", "reload"], capture_output=True)
|
||||
|
||||
@ -41,6 +45,7 @@ class UFW:
|
||||
|
||||
@staticmethod
|
||||
def delete_deny_ip(id_: str):
|
||||
"""Delete a rule based on its id."""
|
||||
logging.info(f"cmd running: ufw delete {id_}")
|
||||
process = subprocess.run(
|
||||
["ufw", "delete", id_], input=b"y\n", capture_output=True
|
||||
@ -53,6 +58,7 @@ class UFW:
|
||||
|
||||
@staticmethod
|
||||
def ban_ip(ip: str):
|
||||
"""Deny an ip for any ports on the machine."""
|
||||
logging.info(f"cmd running: ufw deny from {ip}")
|
||||
process = subprocess.run(["ufw", "deny", "from", ip], capture_output=True)
|
||||
|
||||
@ -61,6 +67,10 @@ class UFW:
|
||||
|
||||
@staticmethod
|
||||
def list_deny() -> dict[str, str]:
|
||||
"""
|
||||
List all the denied ip.
|
||||
Return a dict mapping the ip with its ufw id.
|
||||
"""
|
||||
ips = {}
|
||||
cp = subprocess.run(["ufw", "status", "numbered"], capture_output=True)
|
||||
if cp.returncode != 0:
|
||||
@ -258,13 +268,13 @@ def main(refresh: bool = False, reload: bool = False, dry_run: bool = False):
|
||||
logs = parse_nginx_logs()
|
||||
logs_to_deny = get_logs_to_deny(logs, rules)
|
||||
|
||||
if args.refresh and not args.dry_run:
|
||||
if refresh and not dry_run:
|
||||
UFW.drop_all()
|
||||
return
|
||||
|
||||
for ip, log in logs_to_deny.items():
|
||||
print(f"> banning log: {log}")
|
||||
if not args.dry_run:
|
||||
if not dry_run:
|
||||
UFW.ban_ip(ip)
|
||||
|
||||
if reload:
|
||||
@ -337,7 +347,7 @@ if __name__ == "__main__":
|
||||
exit_code = 1
|
||||
logging.fatal(f"unexpected error occurred, err={e}")
|
||||
except KeyboardInterrupt:
|
||||
logging.warning("ok, you just kill me..., bye")
|
||||
logging.warning("ok, you just kill me... Bye !")
|
||||
|
||||
logging.info(f"ufwban done in elapsed time: {time.perf_counter() - start:.2f}s")
|
||||
exit(exit_code)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user