Skip to content

Configuration

The instance is configured via environment variables. Some settings can also be managed in the admin panel — where the rule is: a set environment variable always wins, and the interface shows its value read-only (intended for cluster deployments where configuration comes from the outside).

VariableDefaultMeaning
DNSE_LISTEN127.0.0.1:8422Address and port
DNSE_ENCRYPTION_KEY—Required. 32 bytes as hex or Base64
DNSE_ADMIN_USERadminUsername of the bootstrap account
DNSE_ADMIN_PASSWORD—Password on the very first start (no effect afterwards)
DNSE_SERVE_UIonoff serves only the API
DNSE_SECURE_COOKIESoffSecure for the short-lived SSO handshake cookies (EE) — the login itself no longer uses cookies
DNSE_TRUSTED_PROXIES—Proxy IPs/CIDRs whose X-Forwarded-For is trusted
DNSE_TLS_CERT—Certificate chain (PEM) — the service then speaks HTTPS itself
DNSE_TLS_KEY—Matching private key (PEM); only together with DNSE_TLS_CERT
DNSE_DEMOoffon registers the demo provider and seeds sample data
DNSE_UPDATE_FEEDindex on repo.techeve.deURL of the APT package index for the update hint in the admin area; off disables the check
VariableDefaultMeaning
DNSE_BACKUP_DIR—Directory of the nightly organisation backup (empty = off)
DNSE_DB./dnseditor.dbPath of the SQLite file
DNSE_DB_DSN—PostgreSQL DSN; replaces DNSE_DB
DNSE_DB_YBoffon = DNSE_DB_DSN points to a YugabyteDB cluster (YSQL)

Details in Database and Keys.

VariableDefaultMeaning
DNSE_VALKEY_ADDR—host:port of a shared Valkey; several addresses (comma) = cluster
DNSE_VALKEY_PASSWORD—AUTH password, if set
DNSE_SESSION_TTL12hValidity of a sign-in, counted from its last use
DNSE_SESSION_TOUCH_INTERVAL—Minimum gap between two refreshes; empty = every request refreshes

Without DNSE_VALKEY_ADDR, sessions live in the process memory — exactly right for a single instance. As soon as several nodes run behind a load balancer, a shared Valkey belongs in the setup: a login on node 1 is then valid on all nodes, and a logout takes effect everywhere.

DNSE_SESSION_TOUCH_INTERVAL throttles how often validity is written forward. Without it, every request writes to the session store — irrelevant for a single instance, needless constant load on a shared Valkey for a value nobody needs to the minute. 5m is a workable setting; sign-ins do not expire any earlier, they are just extended less often.

VariableDefaultMeaning
DNSE_ACME_EMAIL—Account email; issuance is only active once set
DNSE_ACME_DIRECTORYLet’s Encrypt (production)Alternative ACME directory
DNSE_ACME_PROPAGATION—Fixed wait time instead of checking the authoritative NS, e.g. 90s

DNSE_ACME_PROPAGATION is needed in split-DNS environments where lego’s pre-check fails against the internal resolver.

Core settings (admin panel or environment)

Section titled “Core settings (admin panel or environment)”

These values live under Administration → Core Settings and can alternatively be provided via environment variables:

SettingVariablePurpose
Public addressDNSE_INSTANCE_URLBase for links in outgoing mail
DynDNS hostnameDNSE_DYNDNS_HOSTNAMEUpdate address for routers
DynDNS hostname (IPv4 only)DNSE_DYNDNS_HOSTNAME4forces A resolution
DynDNS hostname (IPv6 only)DNSE_DYNDNS_HOSTNAME6forces AAAA resolution
SMTP serverDNSE_SMTP_HOSTOutgoing mail
SMTP portDNSE_SMTP_PORTOutgoing mail
SenderDNSE_SMTP_FROMSender address
SMTP userDNSE_SMTP_USERNAMEAuthentication
SMTP passwordDNSE_SMTP_PASSWORDstored encrypted, never returned
ALTCHA serviceDNSE_ALTCHA_URLproof-of-work in front of the public forms; empty = off
ALTCHA tokenDNSE_ALTCHA_TOKENstored encrypted, never returned

Without a configured mail relay, invitations, password resets, and expiry reminders do not work — in that case the interface shows generated passwords directly so they can be handed over manually.

With DNSE_ALTCHA_URL and DNSE_ALTCHA_TOKEN set, the three public forms — sign-in, registration and “forgot password” — get a small proof-of-work puzzle in front of them (no cookies, no picture guessing). The server checks every submission with the service; without a solved puzzle the form is not processed at all.

Three properties worth knowing:

  • Fail closed. If the service does not answer, the submission counts as invalid. Without it the visitor would not have received a puzzle either, and a silent pass would disable the protection during any outage.
  • Every solution is valid exactly once. The service remembers submitted solutions; the interface computes a fresh one after each attempt.
  • The second sign-in step (two-factor code, organisation choice) requires a solution too — otherwise sending a code along would be enough to try passwords past the gate.

The site’s address must be listed as an origin on the service (ALTCHA_ORIGINS), otherwise it refuses to hand out puzzles. With nothing configured, everything stays as before.

[Unit]
Description=DNS-Editor
After=network-online.target
[Service]
User=dnseditor
Environment=DNSE_LISTEN=127.0.0.1:8422
Environment=DNSE_DB_DSN=postgres://dnseditor:…@localhost:5432/dnseditor
Environment=DNSE_SECURE_COOKIES=on
Environment=DNSE_INSTANCE_URL=https://dns.example.com
EnvironmentFile=/etc/dnseditor/secrets.env
ExecStart=/usr/local/bin/dnseditor
Restart=on-failure
[Install]
WantedBy=multi-user.target

The encryption key and the SMTP password belong in the EnvironmentFile with restrictive permissions, not in the unit itself.

server {
listen 443 ssl;
server_name dns.example.com;
ssl_certificate /etc/ssl/dns.example.com.pem;
ssl_certificate_key /etc/ssl/dns.example.com.key;
location / {
proxy_pass http://127.0.0.1:8422;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
}

Matching settings on the instance:

Terminal window
DNSE_SECURE_COOKIES=on
DNSE_TRUSTED_PROXIES=127.0.0.1
DNSE_INSTANCE_URL=https://dns.example.com

DNSE_TRUSTED_PROXIES names the address from which the proxy forwards requests — only its X-Forwarded-For is believed. Without this setting, DynDNS would record the proxy address instead of the router IP.

One consequence worth knowing: once a source counts as a proxy, only the header is used. A router that talks to the port directly without X-Forwarded-For then gets 911 instead of an update. Keep the list narrow; do not set it to 0.0.0.0/0.

When the proxy speaks HTTPS to its upstream

Section titled “When the proxy speaks HTTPS to its upstream”

The setup above is the normal one: TLS ends at the proxy, plain HTTP behind it. Some proxies are configured for proxy_pass https://… across the board, often together with proxy_ssl_verify off. The service then has to hold a certificate itself — otherwise it answers the TLS handshake with an HTTP 400 and the proxy reports 502:

Terminal window
DNSE_TLS_CERT=/etc/dnseditor/tls/cert.pem
DNSE_TLS_KEY=/etc/dnseditor/tls/key.pem

Both belong together — with only one set the service refuses to start (silently speaking plain HTTP where TLS was meant would be the more dangerous answer). If the proxy does not verify the certificate, a self-signed one is enough:

Terminal window
openssl req -x509 -newkey rsa:2048 -nodes -days 3650 -keyout key.pem -out cert.pem -subj "/CN=dns.example.com"

The key belongs to the service group and stays at 0640.

The interface sends a Content Security Policy that restricts scripts to its own origin. This is not decoration: now that the login lives as a token in browser storage (instead of an HttpOnly cookie), injected JavaScript could read it. The policy keeps foreign script from executing at all.

Practical consequence for operations: a proxy that injects its own scripts — analytics, consent banners, error trackers — will be blocked by the browser. Anyone who needs that has to replace the header in the proxy deliberately, and then knows which protection they are giving up.

Styles may be inline (the components set them at runtime), scripts explicitly may not — that is where the danger sits.