This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
# UniFi Firewall Updater
|
||||
|
||||
Automatically updates UniFi Dream Machine firewall rules with your current public IPv4 and IPv6 addresses via the UniFi Network API.
|
||||
|
||||
## Overview
|
||||
|
||||
This tool:
|
||||
|
||||
- Fetches your public IPv4 and IPv6 addresses
|
||||
- Reads firewall rule definitions from a YAML config file
|
||||
- Creates or updates policies on your UniFi controller to use those addresses
|
||||
- Optionally sends notifications via NTFY when rules change
|
||||
|
||||
Designed to run as a one-shot script (e.g., via cron or systemd timer) whenever your public IP changes.
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Copy `.env.example` to `.env` and fill in your UniFi credentials
|
||||
2. Edit `config/rules.yaml` to define your firewall rules
|
||||
3. Install dependencies and run:
|
||||
|
||||
```bash
|
||||
uv sync
|
||||
uv run main.py
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Required | Description |
|
||||
| ------------------ | -------- | -------------------------------------------------------- |
|
||||
| `UNIFI_HOST` | Yes | UniFi controller URL (e.g., `https://10.1.0.1`) |
|
||||
| `UNIFI_SITE_ID` | Yes | UniFi site ID |
|
||||
| `UNIFI_API_TOKEN` | Yes | UniFi Network API token |
|
||||
| `UNIFI_VERIFY_SSL` | No | Verify SSL certificates (default: `false`) |
|
||||
| `CONFIG_FILE` | No | Path to rules YAML (default: `config/rules.yaml`) |
|
||||
| `LOG_LEVEL` | No | Log level: DEBUG, INFO, WARNING, ERROR (default: `INFO`) |
|
||||
| `DEBUG` | No | Attach debugpy on port 5678 (default: `false`) |
|
||||
| `NTFY_URL` | No | NTFY server URL for notifications |
|
||||
| `NTFY_TOPIC` | No | NTFY topic to publish to |
|
||||
| `NTFY_API_KEY` | No | NTFY API key for authentication |
|
||||
|
||||
### Rules YAML
|
||||
|
||||
Each rule defines a firewall policy that will use your public IP as the source:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
- name: "Allow External to Gateway HTTP(S)"
|
||||
source_zone: "External"
|
||||
dest_zone: "DMZ"
|
||||
ip_version: "IPV6"
|
||||
action: "ALLOW"
|
||||
allow_return_traffic: true
|
||||
protocol: "tcp"
|
||||
dest_ports: [80, 443]
|
||||
dest_port_ranges:
|
||||
- start: 8000
|
||||
stop: 8080
|
||||
logging_enabled: false
|
||||
enabled: true
|
||||
```
|
||||
|
||||
| Field | Required | Description |
|
||||
| ---------------------- | -------- | ------------------------------------------------ |
|
||||
| `name` | Yes | Unique policy name |
|
||||
| `source_zone` | Yes | Source zone name (e.g., `WAN`, `External`) |
|
||||
| `dest_zone` | Yes | Destination zone name (e.g., `LAN`, `DMZ`) |
|
||||
| `ip_version` | No | `IPV4` or `IPV6` (default: `IPV4`) |
|
||||
| `action` | No | `ALLOW`, `BLOCK`, or `REJECT` (default: `ALLOW`) |
|
||||
| `allow_return_traffic` | No | Allow return traffic (default: `true`) |
|
||||
| `protocol` | No | Protocol filter (e.g., `tcp`, `udp`) |
|
||||
| `dest_ports` | No | List of destination ports |
|
||||
| `dest_port_ranges` | No | List of `{start, stop}` port ranges |
|
||||
| `logging_enabled` | No | Enable policy logging (default: `false`) |
|
||||
| `enabled` | No | Enable the policy (default: `true`) |
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
uv run pytest -v # Run tests (all mocked, no live API calls)
|
||||
uv run ruff check # Lint
|
||||
uv run pyright # Type check (strict mode)
|
||||
```
|
||||
|
||||
Run a single test:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/test_unifi_firewall.py::TestBuildPolicyPayload::test_with_dest_ports -v
|
||||
```
|
||||
|
||||
## Container
|
||||
|
||||
Build and run with Podman:
|
||||
|
||||
```bash
|
||||
podman build -t unifi-firewall .
|
||||
podman run --env-file .env -v $(pwd)/config:/app/config unifi-firewall
|
||||
```
|
||||
|
||||
Config is mounted at runtime via volume. Environment variables are passed at runtime.
|
||||
|
||||
## How It Works
|
||||
|
||||
1. Loads rules from `config/rules.yaml`
|
||||
2. Fetches public IPv4/IPv6 via `curl ifconfig.me`
|
||||
3. Lists zones from UniFi to resolve zone names to IDs
|
||||
4. For each rule:
|
||||
- Finds existing policy by name
|
||||
- Skips if IP already matches
|
||||
- Updates if policy exists with different IP
|
||||
- Creates if policy does not exist
|
||||
5. Sends NTFY notification if rules were created, updated, or failed
|
||||
|
||||
## API Details
|
||||
|
||||
Uses UniFi Network API v1 at:
|
||||
|
||||
```
|
||||
{UNIFI_HOST}/proxy/network/integration/v1/sites/{UNIFI_SITE_ID}
|
||||
```
|
||||
|
||||
Authentication via `X-API-Key` header. See `network_v10.4.57_openapi.json` for the full schema.
|
||||
Reference in New Issue
Block a user