125 lines
4.6 KiB
Markdown
125 lines
4.6 KiB
Markdown
# 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.
|