A clean web interface for managing PowerDNS authoritative DNS servers.
- Zone Management: List, create, edit, and delete DNS zones
- Record Management: Full CRUD for all DNS record types (A, AAAA, CNAME, MX, TXT, SOA, etc.) with color-coded type badges
- Zone Metadata: Manage per-zone metadata (ALLOW-AXFR-FROM, ALSO-NOTIFY, SOA-EDIT, NSEC3PARAM, PRESIGNED, etc.)
- TSIG Keys: Create, edit, and delete TSIG keys for secured zone transfers and dynamic updates
- Group-based Authorization: Assign zones to groups, add users to groups — non-admin users see only their authorized zones
- User Management: Admin and user roles with access control
- API Keys: Generate and manage API keys for REST API access (SHA-256 hashed)
- Activity Logging: Track all zone, metadata, TSIG key, and user operations
- REST API: JSON API for zone, record, and statistics automation
- PowerDNS Integration: Communicates through the PowerDNS REST API
- DNSSEC Support: Zone rectification (manual + auto after key ops), slave notification
- Dark/Light Theme: Toggle with localStorage persistence
- Single Binary: Compiled Go binary with embedded templates, static files, and SQLite database
- Docker Support: Ready-to-use Docker and docker-compose setup
# Download dependencies
make deps # or: just deps
# Build and run
make run # or: just runOpen http://localhost:8080 — default admin credentials: admin / admin
# Start with docker-compose (includes PowerDNS)
make docker-up # or: just docker-up
# Or build and run standalone
make docker-build # or: just docker-build
docker run -d -p 8080:8080 gozoneConfiguration is via config.yaml or environment variables:
| YAML Path | Environment Variable | Default |
|---|---|---|
server.host |
GOZONE_SERVER_HOST |
0.0.0.0 |
server.port |
GOZONE_SERVER_PORT |
8080 |
server.secret_key |
GOZONE_SECRET_KEY |
auto-generated |
server.secure_cookies |
GOZONE_SECURE_COOKIES |
false |
| YAML Path | Environment Variable | Default |
|---|---|---|
database.driver |
GOZONE_DB_DRIVER |
sqlite3 |
database.dsn |
GOZONE_DB_DSN |
./data/gozone.db |
Supported drivers: sqlite3, mysql, postgres. Database passwords in DSNs are automatically redacted in logs.
| YAML Path | Environment Variable | Default |
|---|---|---|
powerdns.api_url |
GOZONE_PDNS_API_URL |
http://localhost:8081 |
powerdns.api_key |
GOZONE_PDNS_API_KEY |
changeme |
powerdns.server_id |
GOZONE_PDNS_SERVER_ID |
localhost |
| YAML Path | Environment Variable | Default |
|---|---|---|
auth.session_duration_hours |
GOZONE_SESSION_DURATION |
24 |
auth.bcrypt_cost |
— | 12 |
| YAML Path | Environment Variable | Default |
|---|---|---|
admin.username |
GOZONE_ADMIN_USERNAME |
admin |
admin.password |
GOZONE_ADMIN_PASSWORD |
admin |
admin.email |
GOZONE_ADMIN_EMAIL |
admin@gozone.local |
admin.first_name |
GOZONE_ADMIN_FIRST_NAME |
Admin |
admin.last_name |
GOZONE_ADMIN_LAST_NAME |
User |
| YAML Path | Environment Variable | Default |
|---|---|---|
logging.level |
— | info |
Important: If no server.secret_key is set in the config file or via GOZONE_SECRET_KEY, a random 32-byte key is auto-generated at startup. For security the generated key is not written to the logs, so it cannot be recovered after startup — the key changes on every restart, invalidating all sessions and CSRF tokens. Always set GOZONE_SECRET_KEY or add server.secret_key to config.yaml for a stable key.
The master secret is split into independent JWT and CSRF sub-keys via HKDF-SHA256, so compromise of one sub-key does not reveal the other.
To generate a persistent key:
openssl rand -hex 32Session cookies use the Secure flag and SameSite=Strict by default. The Secure flag is automatically enabled when the request arrives over HTTPS (direct TLS or via X-Forwarded-Proto: https header from a reverse proxy).
The CSRF cookie's Secure flag is set once at startup and cannot be decided per request, so it is controlled by server.secure_cookies (GOZONE_SECURE_COOKIES). Set it to true whenever GoZone is served over HTTPS. Leave it false for plain-HTTP development, otherwise browsers will not return the CSRF cookie and form submissions will fail validation.
Option 1: Direct TLS
Configure server.port to 443 and provide TLS certificate paths (requires a reverse proxy or Go TLS config).
Option 2: Reverse Proxy (recommended)
Run GoZone behind nginx, Caddy, or Traefik:
# nginx example
server {
listen 443 ssl;
server_name dns-admin.example.com;
ssl_certificate /etc/ssl/certs/example.com.pem;
ssl_certificate_key /etc/ssl/private/example.com.key;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
}Shows PowerDNS server status (connected/unreachable, version, daemon type), zone and user counts, query statistics, and recent activity logs.
Each zone page displays:
- Records table with color-coded type badges (A=blue, AAAA=violet, CNAME=orange, MX=pink, NS=cyan, etc.)
- DNSSEC management — view, create, activate/deactivate, and delete DNSSEC keys (KSK/ZSK) with algorithm selection and DS record display
- Zone metadata (admin only) — manage ALLOW-AXFR-FROM, ALSO-NOTIFY, SOA-EDIT, NSEC3PARAM, PRESIGNED, and other PowerDNS metadata kinds
- Activity logs — history of changes to the zone
- Export (BIND zone file or CSV) — download all zone records
- Import (BIND or CSV) — upload and batch-create records into an existing zone
- Danger zone (admin only) — delete zone, notify, rectify
- Clear cache — invalidate the local zone list cache for the current zone, available to any user with group access to the zone
Manage TSIG keys for secured DNS operations (zone transfers, dynamic updates). Available to admin users under the TSIG Keys menu. Supports hmac-md5, hmac-sha1, hmac-sha256, and hmac-sha512 algorithms.
Admin users can manage DNSSEC for each zone directly from the zone view page. Create KSK (Key Signing Key) and ZSK (Zone Signing Key) pairs with selectable algorithms (RSA/SHA-256, RSA/SHA-512, ECDSA P-256, ECDSA P-384, Ed25519, Ed448). Activate/deactivate keys, view DS records for parent zone configuration, and delete deactivated keys. All operations are logged in the activity feed. Zone rectification is triggered automatically after every key operation (create, toggle, delete) and is also available via the Rectify button (admin-only) in the zone header.
Export full zone records in RFC 1035 BIND zone file format or CSV with a single click from the zone view page. Import zone data by uploading a .zone or .csv file — records are parsed and batch-created into the existing zone. Both are restricted to users with group access to the zone.
Import uses PowerDNS REPLACE semantics: for each name+type pair in the file, the existing RRSet is replaced if present, or created if absent. Records not referenced in the import file are left untouched. Importing a file with fewer records than currently exist for a given name+type replaces the entire RRSet — extra existing records within that same name+type are removed.
Admin users can create groups, assign zones to groups, and add users as members. Non-admin users only see zones assigned to groups they belong to. The "Groups" link is visible in the sidebar for admin users.
Admin users can define reusable DNS record templates that pre-populate records when creating new zones or applying to existing zones. Templates support variable substitution (IP, IP6, MX_HOST, TTL, ZONE, etc.) and include four built-in templates (standard, mail, web, redirect). Accessible under the Templates menu in the sidebar for admin users.
Users can generate personal API keys for programmatic access. Keys are SHA-256 hashed before storage — the raw key is shown only once at creation time.
All API endpoints require an API key passed via X-API-Key header.
GET /api/v1/zones - List all zones (filtered by group for non-admin)
POST /api/v1/zones - Create a zone (admin only)
GET /api/v1/zones/{zone_id} - Get zone details
DELETE /api/v1/zones/{zone_id} - Delete a zone (admin only)
GET /api/v1/zones/{zone_id}/records - List zone records
POST /api/v1/zones/{zone_id}/records - Create record
PUT /api/v1/zones/{zone_id}/records - Update record
DELETE /api/v1/zones/{zone_id}/records - Delete record
GET /api/v1/stats - Server statistics
| Make | Just | Description |
|---|---|---|
make build |
just build |
Build the binary |
make run |
just run |
Build and run locally |
make test |
just test |
Run tests |
make test-verbose |
just test-verbose |
Run tests with verbose output |
make clean |
just clean |
Remove build artifacts and database |
make fmt |
just fmt |
Format all source files |
make vet |
just vet |
Run vet on all packages |
make gosec |
just gosec |
Run security static analysis |
make deps |
just deps |
Download and tidy dependencies |
make docker-build |
just docker-build |
Build Docker image |
make docker-up |
just docker-up |
Start services with docker-compose |
make docker-down |
just docker-down |
Stop services |
make update |
just update |
Update all dependencies |
Requirements: Go 1.26+, C compiler (gcc/clang) for SQLite CGO driver.
make build # or: just build
./bin/gozone -config config.yamlgozone/
├── cmd/gozone/main.go # Application entry point, routing, wiring
├── internal/
│ ├── config/config.go # Configuration (YAML + env vars)
│ ├── database/ # Database layer (SQLite/MySQL/Postgres)
│ │ ├── database.go # Connection, migrations
│ │ ├── dialect.go # Dialect interface
│ │ ├── sqlite_dialect.go # SQLite dialect
│ │ ├── mysql_dialect.go # MySQL dialect
│ │ ├── postgres_dialect.go # PostgreSQL dialect
│ │ └── seed.go # Admin user seeding
│ ├── handlers/ # HTTP handlers (web UI + REST API)
│ │ ├── handler.go # Handler struct, rendering
│ │ ├── zones.go # Zone CRUD, metadata
│ │ ├── records.go # Record CRUD
│ │ ├── users.go # User management
│ │ ├── groups.go # Zone group authorization
│ │ ├── tsigkeys.go # TSIG key management
│ │ ├── templates.go # Zone template management
│ │ ├── dnssec.go # DNSSEC key management
│ │ ├── export.go # Zone export (BIND + CSV)
│ │ ├── import.go # Zone import (BIND + CSV)
│ │ ├── api.go # REST API handlers
│ │ ├── api_keys.go # API key management
│ │ ├── auth.go # Login/logout
│ │ ├── dashboard.go # Dashboard with PDNS stats
│ │ └── health.go # Health checks
│ ├── middleware/ # HTTP middleware
│ │ ├── auth.go # JWT authentication
│ │ ├── zoneauth.go # Zone group authorization
│ │ ├── security.go # Security headers
│ │ ├── ratelimit.go # Rate limiting
│ │ └── error.go # Error handling
│ ├── models/ # Shared data structures
│ └── pdns/ # PowerDNS REST API client
│ ├── client.go # HTTP client
│ └── service.go # ZoneService interface
├── web/
│ ├── templates/ # Embedded Go HTML templates
│ │ ├── base.html # Sidebar, head, tail
│ │ ├── dashboard.html # Dashboard
│ │ ├── zones.html # Zone list
│ │ ├── zone_view.html # Zone detail + records + metadata
│ │ ├── groups.html # Group list
│ │ ├── group_edit.html # Group edit (members, zones)
│ │ ├── tsigkeys.html # TSIG key list
│ │ ├── tsigkey_create.html # TSIG key creation
│ │ ├── tsigkey_edit.html # TSIG key edit
│ │ ├── templates.html # Template list
│ │ ├── template_edit.html # Template editor with records
│ │ ├── users.html # User list
│ │ ├── profile.html # User profile
│ │ └── ...
│ ├── static/
│ │ ├── css/style.css # Stylesheet (light + dark theme)
│ │ └── js/app.js # Theme toggle, sidebar toggle
│ └── embed.go # Embedded filesystem
├── config.yaml # Default configuration
├── justfile # Task runner (just)
├── Makefile # Task runner (make)
├── Dockerfile
├── docker-compose.yml
└── go.mod
MIT — see LICENSE file.