Open-source web interface for managing AWS Route53 DNS records with multi-user support, role-based access control, and audit logging.
- DNS Management — Create, edit, and delete DNS records through a modern web UI (Highway Style)
- Multi-User — Multiple users with
adminandeditorroles - First-Run Setup — Web-based initial admin account creation on first launch
- Persistent Sessions — PostgreSQL-backed sessions that survive server restarts
- DNS Caching — Zones and records are cached locally (5-min TTL) to reduce AWS API calls
- Audit Logging — All actions (login, logout, record changes, user management) are logged
- Single Binary — All assets (templates, CSS, JS, images, migrations) are embedded into the binary
- PostgreSQL Backend — Robust data storage with full SQL support
- PostgreSQL database (v12 or later recommended)
config.yamlwith correct database DSN
cp config.yaml.example config.yaml
# Edit config.yaml with your AWS credentials and Database DSN
make runOn first launch, the application will automatically apply database migrations.
Open http://localhost:8080 — you'll be redirected to /setup to create your admin account.
Edit config.yaml:
server:
port: 8080
host: "0.0.0.0"
database:
# DSN (Data Source Name) connection string
# Example: postgres://user:password@localhost:5432/ns116?sslmode=disable
dsn: "postgres://ns116:ns116pass@localhost:5432/ns116?sslmode=disable"
aws:
access_key_id: "AKIA..."
secret_access_key: "wJal..."
region: "us-east-1"
# Optional: restrict to specific zones (if empty, all zones are shown)
hosted_zones: []
# - id: "Z1PA6795UKMFR9"
# label: "example.com"| Section | Description |
|---|---|
server |
Bind address and port |
database.dsn |
PostgreSQL connection string (including user, password, dbname) |
aws |
AWS credentials and region for DNS API access |
hosted_zones |
Optional allowlist of zone IDs to manage |
Optional: Enable LDAP to authenticate users against Active Directory or OpenLDAP.
ldap:
enabled: true
url: "ldaps://ldap.example.com:636"
bind_dn: "CN=ns116-svc,OU=ServiceAccounts,DC=example,DC=com"
bind_password: "secret"
base_dn: "OU=Users,DC=example,DC=com"
user_filter: "(sAMAccountName=%s)" # or "(uid=%s)"
username_attr: "sAMAccountName" # or "uid"
email_attr: "mail"
# Optional: Custom group filter (e.g. for POSIX groups)
# Default: (|(member=%s)(uniqueMember=%s))
# For memberUid: (&(objectClass=posixGroup)(memberUid=%u))
group_filter: ""
group_mapping:
admin: "CN=DNS-Admins,OU=Groups,DC=example,DC=com"
editor: "CN=DNS-Editors,OU=Groups,DC=example,DC=com"Auto-Configuration Tool:
We provide a helper script to automatically detect your LDAP server type (Active Directory, OpenLDAP, or POSIX) and generate the correct configuration for you.
./scripts/generate_ldap_config.shNotes:
- Group Mapping: Access is denied unless the user belongs to at least one mapped group.
- Local Fallback: When LDAP is enabled, local password login
is restricted to the
adminuser only (for emergency access). All other users must login via LDAP. - Auto-Provisioning: LDAP users are automatically created in the local database on first login (password is not stored).
Optional: Enable OIDC to authenticate users via an external identity provider (IdP).
- Login URL: the login page will show one button per configured provider.
- Callback URL: configure your IdP to redirect back to
https://<ns116-host>/oidc/callback. - Sessions: after successful OIDC validation, NS116 creates the normal
ns116_sessioncookie (same session management as other login methods). - User provisioning: if the OIDC user does not exist locally yet, NS116 will auto-create the user with the role resolved from
group_mapping(below). The IdP claim configured asusername_claimbecomes the localusers.username. - Authorization: NS116 resolves role from the IdP
groupsclaim usinggroup_mapping. If the user is not in any mapped group, login is denied.
Example configuration (Pocket ID + Google):
oidc:
enabled: true
providers:
pocketid:
label: "Pocket ID"
issuer_url: "https://pocket-id.example.com"
client_id: "ns116"
redirect_url: "https://ns116.example.com/oidc/callback"
scopes: ["openid", "profile", "email", "groups"]
username_claim: "preferred_username"
groups_claim: "groups"
group_mapping:
admin: ["ns116-admins"]
editor: ["ns116-editors"]
google:
label: "Google"
issuer_url: "https://accounts.google.com"
client_id: "YOUR_GOOGLE_CLIENT_ID"
client_secret: "YOUR_GOOGLE_CLIENT_SECRET"
redirect_url: "https://ns116.example.com/oidc/callback"
scopes: ["openid", "profile", "email"]
username_claim: "email"
groups_claim: "groups"
group_mapping:
editor: ["ns116-editors"]
extra_auth_params:
access_type: "offline"
prompt: "consent"make build # produces ./ns116 (linux/amd64, optimized)
make run # development mode
make clean # remove built binary
make docker # build Docker imagedocker build -t ns116 .
docker run -p 8080:8080 \
-v ./config.yaml:/app/config.yaml \
ns116kubectl apply -f k8s/internal/
├── auth/ # Session management, authentication, RBAC middleware
├── config/ # YAML configuration loading
├── database/ # PostgreSQL layer (migrations, users, sessions, cache, audit)
├── handler/ # HTTP handlers (auth, zones, records, setup, admin)
├── model/ # Data models (User, Session, AuditEntry, Zone, Record)
├── server/ # Server wiring and routing
└── service/ # AWS DNS service with caching
web/
├── static/ # CSS, JS, images (embedded)
├── templates/ # HTML templates (embedded)
└── migrations/ # SQL migration files (embedded)
| Role | Permissions |
|---|---|
admin |
Full access: manage DNS records, manage users, view audit log |
editor |
DNS record management only |
MIT
AWS, Amazon Web Services, and Route53 are trademarks of Amazon.com, Inc. or its affiliates. NS116 is an independent, open-source project and is not affiliated with, endorsed by, or sponsored by Amazon.