63c3c10f2b8587085e4dcd2c2fe2d044d3a08994
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.ripalso 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.rippointing to Technitium
DNS Setup
In your parent dws.rip zone:
space.dws.rip. IN NS dns.dws.rip.
dns.dws.rip. IN A <TECHNITIUM_IP>
Configuration
Create a .env file:
# 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
Docker Compose (Recommended for single node)
# Clone and start
git clone https://git.dws.rip/DWS/dyn.git
cd dyn
docker-compose up -d
# View logs
docker-compose logs -f
# Stop
docker-compose down
Kubernetes / K3s
For production deployments on Kubernetes or K3s clusters:
# Clone repository
git clone https://git.dws.rip/DWS/dyn.git
cd dyn
# Create your secrets file
cp k8s/overlays/production/secrets.example.yaml k8s/overlays/production/secrets.yaml
# Edit secrets.yaml with your actual Technitium credentials
# Deploy with kustomize
kubectl apply -k k8s/overlays/production
# Or with kubectl 1.14+ (built-in kustomize)
kubectl apply -k k8s/overlays/production
# Check deployment status
kubectl get pods -n dyn-ddns
kubectl logs -n dyn-ddns -l app=dyn-ddns
# Delete deployment
kubectl delete -k k8s/overlays/production
Kustomize Overlays:
k8s/overlays/production- Production setup (2 replicas, higher resource limits)k8s/overlays/staging- Staging environment (1 replica, relaxed rate limits)
Requirements:
- Cert-manager (for TLS certificates via Let's Encrypt)
- Traefik or NGINX ingress controller
- Persistent storage class (for SQLite database)
See k8s/README.md for detailed Kubernetes deployment documentation.
API Endpoints
Web UI
GET /- Claim space interface
API
GET /api/check?subdomain=<name>- Check subdomain availabilityPOST /api/claim- Claim a subdomain (returns token once)
DynDNS2 Protocol
GET /api/nic/update?hostname=<fqdn>&myip=<ip>- Auth: Basic auth with username
noneand password = token - Returns:
good <ip>,nochg <ip>,badauth,nohost,911
- Auth: Basic auth with username
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
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)
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
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
# 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
Description
Languages
Go
72.8%
Shell
9.6%
JavaScript
6%
HTML
5.1%
CSS
4.1%
Other
2.4%