Features: - Token-based subdomain claiming - NIC V2 (DynDNS2) protocol implementation - Technitium DNS integration - Rate limiting (10 req/min IP, 1 req/min token) - Web UI for space claiming - Docker/Docker Compose support - Compatible with UniFi, pfSense, EdgeRouter Module: git.dws.rip/DWS/dyn
204 lines
5.6 KiB
Markdown
204 lines
5.6 KiB
Markdown
# DWS Dynamic DNS Service
|
|
|
|
A self-hosted Dynamic DNS (DDNS) provider built with Go, featuring a minimalist web UI and NIC V2 (DynDNS2) protocol support for router compatibility.
|
|
|
|
## Overview
|
|
|
|
This service allows users to claim subdomains under `space.dws.rip` and update them via the standard DynDNS2 protocol, compatible with UniFi, pfSense, EdgeRouters, and other consumer routers.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
┌─────────────┐ ┌──────────────┐ ┌─────────────────┐
|
|
│ Router │ ──────► │ DWS Bridge │ ──────► │ Technitium DNS │
|
|
│ (DynDNS2) │ HTTP │ (Go) │ API │ (Authoritative)│
|
|
└─────────────┘ └──────────────┘ └─────────────────┘
|
|
│
|
|
▼
|
|
┌──────────────┐
|
|
│ SQLite │
|
|
│ (token→sub) │
|
|
└──────────────┘
|
|
```
|
|
|
|
## Features
|
|
|
|
- **Token-based authentication** - No user accounts, just secure tokens
|
|
- **NIC V2 Protocol** - Compatible with most routers
|
|
- **Automatic wildcard records** - `*.your-space.space.dws.rip` also works
|
|
- **Rate limiting** - 10 req/min per IP, 1 req/min per token
|
|
- **Clean web UI** - Simple space claiming interface
|
|
- **Router config examples** - UniFi, pfSense, cURL snippets
|
|
|
|
## Quick Start
|
|
|
|
### Prerequisites
|
|
|
|
- Docker and Docker Compose
|
|
- Running Technitium DNS server with API access
|
|
- DNS delegation for `space.dws.rip` pointing to Technitium
|
|
|
|
### DNS Setup
|
|
|
|
In your parent `dws.rip` zone:
|
|
|
|
```text
|
|
space.dws.rip. IN NS dns.dws.rip.
|
|
dns.dws.rip. IN A <TECHNITIUM_IP>
|
|
```
|
|
|
|
### Configuration
|
|
|
|
Create a `.env` file:
|
|
|
|
```bash
|
|
# Required: Technitium DNS API endpoint
|
|
TECHNITIUM_URL=https://dns.dws.rip
|
|
|
|
# Authentication (choose one method)
|
|
# Method 1: API Token (recommended)
|
|
TECHNITIUM_TOKEN=your-api-token-here
|
|
|
|
# Method 2: Username/Password
|
|
TECHNITIUM_USERNAME=admin
|
|
TECHNITIUM_PASSWORD=your-password
|
|
|
|
# Optional: Domain configuration
|
|
BASE_DOMAIN=dws.rip
|
|
SPACE_SUBDOMAIN=space
|
|
|
|
# Optional: Rate limiting
|
|
RATE_LIMIT_PER_IP=10
|
|
RATE_LIMIT_PER_TOKEN=1
|
|
|
|
# Optional: Trusted proxies (comma-separated)
|
|
TRUSTED_PROXIES=10.0.0.0/8,172.16.0.0/12
|
|
```
|
|
|
|
### Deployment
|
|
|
|
```bash
|
|
# Clone and start
|
|
git clone <repo>
|
|
cd dyn
|
|
docker-compose up -d
|
|
|
|
# View logs
|
|
docker-compose logs -f
|
|
|
|
# Stop
|
|
docker-compose down
|
|
```
|
|
|
|
## API Endpoints
|
|
|
|
### Web UI
|
|
- `GET /` - Claim space interface
|
|
|
|
### API
|
|
- `GET /api/check?subdomain=<name>` - Check subdomain availability
|
|
- `POST /api/claim` - Claim a subdomain (returns token once)
|
|
|
|
### DynDNS2 Protocol
|
|
- `GET /api/nic/update?hostname=<fqdn>&myip=<ip>`
|
|
- Auth: Basic auth with username `none` and password = token
|
|
- Returns: `good <ip>`, `nochg <ip>`, `badauth`, `nohost`, `911`
|
|
|
|
## Router Configuration
|
|
|
|
### UniFi / UDM
|
|
|
|
```
|
|
Service: dyndns
|
|
Hostname: your-space.space.dws.rip
|
|
Username: none
|
|
Password: <your-token>
|
|
Server: dyn.dws.rip/api/nic/update
|
|
```
|
|
|
|
### pfSense / OPNsense
|
|
|
|
```
|
|
Service Type: Custom
|
|
Update URL: https://dyn.dws.rip/api/nic/update?hostname=%h&myip=%i
|
|
Username: none
|
|
Password: <your-token>
|
|
```
|
|
|
|
### EdgeRouter
|
|
|
|
```bash
|
|
set service dns dynamic interface eth0 service custom-dyn host-name your-space.space.dws.rip
|
|
set service dns dynamic interface eth0 service custom-dyn login none
|
|
set service dns dynamic interface eth0 service custom-dyn password <your-token>
|
|
set service dns dynamic interface eth0 service custom-dyn protocol dyndns2
|
|
set service dns dynamic interface eth0 service custom-dyn server dyn.dws.rip
|
|
```
|
|
|
|
### Manual (cURL)
|
|
|
|
```bash
|
|
curl -u "none:<your-token>" \
|
|
"https://dyn.dws.rip/api/nic/update?hostname=your-space.space.dws.rip&myip=1.2.3.4"
|
|
```
|
|
|
|
## Database Schema
|
|
|
|
```sql
|
|
CREATE TABLE spaces (
|
|
token TEXT PRIMARY KEY,
|
|
subdomain TEXT UNIQUE NOT NULL,
|
|
last_ip TEXT,
|
|
updated_at DATETIME,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
```
|
|
|
|
## Security
|
|
|
|
- **32-byte random tokens** - Generated with `crypto/rand`
|
|
- **Token displayed once** - No recovery mechanism (claim new space if lost)
|
|
- **Rate limiting** - Prevents brute force and abuse
|
|
- **No PII stored** - Just token-to-subdomain mapping
|
|
- **Input validation** - Subdomains validated (alphanumeric + hyphen only)
|
|
|
|
## Environment Variables
|
|
|
|
| Variable | Required | Default | Description |
|
|
|----------|----------|---------|-------------|
|
|
| `TECHNITIUM_URL` | Yes | - | Technitium API URL |
|
|
| `TECHNITIUM_TOKEN` | No* | - | API token for auth |
|
|
| `TECHNITIUM_USERNAME` | No* | - | Basic auth username |
|
|
| `TECHNITIUM_PASSWORD` | No* | - | Basic auth password |
|
|
| `BASE_DOMAIN` | No | `dws.rip` | Root domain |
|
|
| `SPACE_SUBDOMAIN` | No | `space` | Subdomain for user spaces |
|
|
| `DATABASE_PATH` | No | `./dyn.db` | SQLite database path |
|
|
| `SERVER_PORT` | No | `8080` | HTTP server port |
|
|
| `RATE_LIMIT_PER_IP` | No | `10` | Requests per minute per IP |
|
|
| `RATE_LIMIT_PER_TOKEN` | No | `1` | Updates per minute per token |
|
|
| `TRUSTED_PROXIES` | No | - | Comma-separated CIDRs |
|
|
|
|
*Either `TECHNITIUM_TOKEN` OR both `TECHNITIUM_USERNAME` and `TECHNITIUM_PASSWORD` required.
|
|
|
|
## Development
|
|
|
|
```bash
|
|
# Local development
|
|
go mod tidy
|
|
go run cmd/server/main.go
|
|
|
|
# Build
|
|
go build -o server cmd/server/main.go
|
|
|
|
# Test
|
|
go test ./...
|
|
```
|
|
|
|
## License
|
|
|
|
MIT
|
|
|
|
## Credits
|
|
|
|
Inspired by [benjaminbear/docker-ddns-server](https://github.com/benjaminbear/docker-ddns-server)
|