- Python 85.2%
- Shell 14.8%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| config/nftables | ||
| docs | ||
| packaging/systemd | ||
| scripts | ||
| src | ||
| .gitattributes | ||
| .gitignore | ||
| agmh.txt | ||
| LICENSE | ||
| README.md | ||
nftables REST API
Small local REST API for adding and removing IPv4, IPv6, and CIDR entries from nftables block sets.
The project is intentionally minimal: a Python HTTP server bound to
127.0.0.1:9099, a systemd unit, helper scripts, and an example nftables
ruleset. It was tested on Debian 13 Trixie on OVH and is intended for local host
perimeter control.
Features
- Blocks and unblocks IPv4 addresses, IPv6 addresses, and CIDR ranges.
- Uses nftables sets named
banned_ipv4andbanned_ipv6. - Applies blocks to input, output, and forwarded traffic.
- Exposes a local HTTP API with optional bearer-token authentication.
- Includes optional
banipandunbanipshell wrappers.
Repository Layout
.
|-- config/
| `-- nftables/
| `-- killlist.nft
|-- docs/
| |-- api.md
| |-- nftables.md
| `-- shell-wrappers.md
|-- packaging/
| `-- systemd/
| `-- nftables-rest-api.service
|-- scripts/
| |-- banip
| `-- unbanip
|-- src/
| `-- nftables_rest_api.py
|-- LICENSE
`-- README.md
Requirements
- Linux with nftables support.
- Python 3.
- Root privileges, because the API calls
/usr/sbin/nft. - systemd, if you want to run the API as a service.
Security Notes
- Keep the API bound to
127.0.0.1unless you add proper transport security and access controls. - The bearer token is sent over plain HTTP. This is acceptable for local-only usage, but it should not be exposed directly on a public interface.
- If
NFT_API_TOKENis empty, authentication is disabled by the Python script. - The sample nftables file starts with
flush ruleset, which removes the current nftables ruleset before loading the new one. If the host already has firewall rules, merge thekilllisttable manually instead of copying the sample file as-is. - Bans added through the API are runtime nftables entries. Reloading a ruleset that defines empty sets will remove those runtime entries.
Quick Start
Install dependencies:
sudo apt update
sudo apt install -y nftables python3
Install the example nftables ruleset:
sudo cp config/nftables/killlist.nft /etc/nftables.conf
sudo nft -c -f /etc/nftables.conf
sudo nft -f /etc/nftables.conf
sudo systemctl enable --now nftables
Install the API script:
sudo install -d -o root -g root -m 755 /opt/nftables-rest-api
sudo install -o root -g root -m 700 src/nftables_rest_api.py /opt/nftables-rest-api/nftables_rest_api.py
Install and configure the systemd service:
sudo install -o root -g root -m 644 packaging/systemd/nftables-rest-api.service /etc/systemd/system/nftables-rest-api.service
sudo nano /etc/systemd/system/nftables-rest-api.service
Replace the placeholder token before starting the service:
Environment=NFT_API_TOKEN=your-long-random-token
Start the API:
sudo systemctl daemon-reload
sudo systemctl enable --now nftables-rest-api
Confirm the API is listening locally:
sudo ss -lntp | grep 9099
Expected result:
LISTEN 0 128 127.0.0.1:9099 0.0.0.0:* users:(("python3",pid=...,fd=...))
API Example
curl -sS \
-H 'Authorization: Bearer your-long-random-token' \
--get \
--data-urlencode 'target=203.0.113.10' \
'http://127.0.0.1:9099/ban'
{
"ok": true,
"target": "203.0.113.10/32",
"set": "banned_ipv4",
"action": "ban"
}
Documentation
Troubleshooting
Check the service:
sudo systemctl status nftables-rest-api
sudo journalctl -u nftables-rest-api -f
Check whether nftables accepted the ruleset:
sudo nft -c -f /etc/nftables.conf
List the active sets directly:
sudo nft list set inet killlist banned_ipv4
sudo nft list set inet killlist banned_ipv6
If a request returns 500, read the JSON stderr field. It contains the error
reported by nftables, such as attempting to delete an element that is not in the
set.
Contributing
Issues and pull requests are welcome. Keep changes small, document behavior that affects operators, and test nftables behavior on a real Linux host before changing the API or ruleset examples.
License
This project is released into the public domain under the Unlicense.