171 lines
5.5 KiB
Markdown
171 lines
5.5 KiB
Markdown
# IP Subnet Allocator
|
||
|
||
A simple internal web application for tracking and automatically allocating IPv4 subnets.
|
||
|
||
## Purpose
|
||
|
||
This application is intended solely for internal tracking and automatic allocation of IPv4 subnets. It is not a general IPAM, monitoring tool, device inventory, VLAN manager, or network topology viewer.
|
||
|
||
It is designed to run only inside a LAN/VPN. **The application does not include authentication** and must be placed behind a firewall/reverse proxy in a trusted network.
|
||
|
||
## Features
|
||
|
||
- Manage one or more IP pools manually through the web UI.
|
||
- First-run setup wizard: create the first pool before allocating subnets.
|
||
- Automatic subnet allocation using a **first-fit** algorithm for prefixes /20 through /30.
|
||
- Atomic allocation writes using SQLite `BEGIN IMMEDIATE` transactions.
|
||
- Retro TUI/terminal-style web UI, server-side rendered with Askama.
|
||
- Dynamic preview of the next free subnet via HTMX.
|
||
- Allocation history with filtering, search, and pagination.
|
||
- Release subnets (status `released`) with a mandatory note, directly from the history table or detail page.
|
||
- Released subnets free up address space and can be reused by the first-fit algorithm.
|
||
- Audit log of all operations.
|
||
- Markdown, CSV, and JSON exports.
|
||
- SQLite migrations run automatically on startup.
|
||
- Backup and restore scripts.
|
||
|
||
## Architecture
|
||
|
||
- **Rust** edition 2024
|
||
- **Axum** + **Tokio**
|
||
- **SQLx** with **SQLite**
|
||
- **Askama** for HTML templates
|
||
- **HTMX** from a local static file
|
||
- Custom CSS without external frameworks
|
||
- CIDR/IP calculations using `ipnet`
|
||
|
||
## Quick start
|
||
|
||
```bash
|
||
cd sub-aligator
|
||
cp .env.example .env
|
||
docker compose up -d --build
|
||
```
|
||
|
||
Open a browser at:
|
||
|
||
```
|
||
http://SERVER_IP:8080
|
||
```
|
||
|
||
On first launch you will be asked to create the first pool. The default port can be changed in `.env` via the `APP_PORT` variable.
|
||
|
||
## Persistent data
|
||
|
||
Docker Compose creates local directories:
|
||
|
||
- `./data` – SQLite database
|
||
- `./exports` – generated exports (Markdown, CSV, JSON)
|
||
- `./backups` – database backups
|
||
|
||
These directories are bind-mounted into the container, so data persists even if the container is removed.
|
||
|
||
## Backup and restore
|
||
|
||
### Backup
|
||
|
||
```bash
|
||
./scripts/backup.sh
|
||
```
|
||
|
||
The script creates a consistent database backup using `sqlite3 .backup` with a UTC timestamp in the filename. Backups are stored in `./backups`.
|
||
|
||
### Restore
|
||
|
||
```bash
|
||
./scripts/restore.sh ./backups/subnet-allocator.db.20240930T120000Z.backup
|
||
```
|
||
|
||
**Warning:** restore overwrites the current database. Stop the container and back up the current state before restoring.
|
||
|
||
### Automating backups
|
||
|
||
On the host you can use a systemd timer or cron. Example cron entry:
|
||
|
||
```cron
|
||
0 2 * * * /path/to/sub-aligator/scripts/backup.sh >> /var/log/subnet-allocator-backup.log 2>&1
|
||
```
|
||
|
||
## Exports
|
||
|
||
After every successful allocation or release, read-only exports are regenerated:
|
||
|
||
- `/app/exports/allocations.md`
|
||
- `/app/exports/allocations.csv`
|
||
- `/app/exports/allocations.json`
|
||
|
||
They are also available for download via endpoints:
|
||
|
||
- `GET /exports/allocations.md`
|
||
- `GET /exports/allocations.csv`
|
||
- `GET /exports/allocations.json`
|
||
|
||
The Markdown export contains a separate section for each pool with a table of active and reserved allocations. CSV and JSON include all allocations, including released ones.
|
||
|
||
## Security notes
|
||
|
||
- The application **has no authentication**. Use it only behind a VPN/LAN/firewall.
|
||
- CSRF tokens are required for POST forms.
|
||
- Security headers are set: `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy`, `Content-Security-Policy`.
|
||
- The application has no default Internet exposure; port binding is handled by Docker Compose.
|
||
- `/healthz` returns only `{"status":"ok"}`.
|
||
|
||
## Adding authentication later via Caddy + Authentik
|
||
|
||
The application is prepared for authentication through a trusted reverse proxy. The function `get_actor_from_request()` in `src/audit.rs` currently returns `local`, but can be updated to read a header such as `X-Authentik-Username`.
|
||
|
||
Example Caddy configuration with Authentik:
|
||
|
||
```caddy
|
||
subnet.example.com {
|
||
reverse_proxy /outpost.goauthentik.io/* http://authentik:9000
|
||
forward_auth http://authentik:9000 {
|
||
uri /outpost.goauthentik.io/auth/caddy
|
||
copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email
|
||
}
|
||
reverse_proxy subnet-allocator:8080
|
||
}
|
||
```
|
||
|
||
After updating `get_actor_from_request()`, the audit log will record the real user.
|
||
|
||
## First-fit allocation example
|
||
|
||
1. First request `/30` receives `10.91.0.0/30`.
|
||
2. Next request `/24` receives `10.91.1.0/24` (avoids the occupied `/30`).
|
||
3. Another request `/30` fills the gap and receives `10.91.0.4/30`.
|
||
|
||
The algorithm always respects CIDR alignment and finds the first free space large enough.
|
||
|
||
## Endpoints
|
||
|
||
- `GET /` – main page
|
||
- `GET /healthz` – healthcheck
|
||
- `GET /setup` – first-run pool setup
|
||
- `GET /pools` – pool management
|
||
- `POST /pools/new` – create a new pool
|
||
- `GET /partials/pools/{slug}/preview?prefix_length=24` – HTMX preview
|
||
- `POST /allocations` – create an allocation
|
||
- `GET /allocations/{id}` – allocation detail
|
||
- `POST /allocations/{id}/release` – release an allocation
|
||
- `GET /exports/allocations.{md,csv,json}` – exports
|
||
|
||
## Troubleshooting
|
||
|
||
### Container does not start
|
||
|
||
```bash
|
||
docker compose logs -f subnet-allocator
|
||
```
|
||
|
||
### Database is locked
|
||
|
||
If the process terminates unexpectedly, a `-wal` or `-shm` file may remain. Stop the container and check `./data`.
|
||
|
||
### Exports are not generated
|
||
|
||
Check the permissions of the `./exports` directory and the `APP_EXPORTS_DIR` variable.
|
||
|
||
## Licence
|
||
|
||
MIT. See the `LICENSE` file.
|