A minimalistic DynDNS service written in Rust.
- reverse-proxy operation mode (nginx) via unix-socket
- systemd socket activation support (reuses the provided unix-socket before binding its own)
- simple and plain TOML configuration file (see below)
- HTTP basic auth and Argon2id hashing of the credentials
- authentication throttling and bounded password-verification concurrency
- IPv4 and IPv6 DynDNS updates for
AandAAAAonly; other RR types stay untouched - RFC 2136 updates via
hickory-net/hickory-proto(TSIG), no externalnsupdatebinary required - configuration is validated semantically on startup and
SIGUSR1reload before it becomes active - SIGUSR1 support for runtime configuration reloading
- Rust: https://rust-lang.org
- Dependencies are vendored for offline reproducible builds.
- Conventions:
- Versioning: https://semver.org
- Changelog: https://keepachangelog.com
- Commits: https://www.conventionalcommits.org
cargo build --releaseEdit /etc/kdyndns/config.toml
[[users]]
server = "udp://127.0.0.1:53"
tsig_key_path = "/etc/bind/keys/dyn.key"
username = "user1"
password_hash = "$argon2id$v=19$m=65536,t=3,p=1$..."
allowed_hosts = ["myhost.example.com."]server must point to an authoritative DNS server that accepts RFC 2136 updates for the configured host. KDynDNS discovers the matching zone via SOA before sending the update.
tsig_key_path must point to a local TSIG key file readable by the service. Supported TSIG algorithms follow the active Hickory backend and currently are hmac-sha256, hmac-sha384, and hmac-sha512, for example:
key "dyn-key" {
algorithm hmac-sha256;
secret "BASE64_SECRET_HERE";
};Create the password hash:
-
Install argon2 for your operating system
-
Create a random salt
salt=$(openssl rand -base64 16) -
Create the password hash
echo -n "your-password-here" | argon2 "$salt" -id -t 3 -m 16 -p 1
Output:
Type: Argon2id Iterations: 3 Memory: 65536 KiB Parallelism: 1 Hash: c54955013f9568d732a14587906ee40726527a90cac099f315272a254998960f Encoded: $argon2id$v=19$m=65536,t=3,p=1$WnJ1TFZNZEQ0QTR2ZTBJWmU1U3VRZz09$xUlVAT+VaNcyoUWHkG7kByZSepDKwJnzFScqJUmYlg8 0.209 seconds Verification ok
-
Take the output of the 'Encoded' field and put it into the config.toml
export DYNDNS_CONFIG=/etc/kdyndns/config.toml
cargo run --release-
Transport: HTTP over the unix socket provided by systemd or bound at
/run/kdyndns/kdyndns.sock; typically fronted by nginx. -
Reverse proxy note: for per-client auth throttling behind nginx, forward the client IP via
X-Forwarded-FororX-Real-IP. -
Authentication: HTTP Basic Auth; users and allowed hosts come from
config.toml. -
Health check:
GET /healthreturns200 OKand bodyOKwithout authentication. -
Update endpoint:
GET /updatewith Basic Auth. Query parameters:host(required) — trailing dot optional; normalized to a lowercase fully qualified name with trailing dot and must be listed inallowed_hostsfor the authenticated user.ipv4(optional) — IPv4 literal; when present, the host'sARRset is replaced with this address.ipv6(optional) — IPv6 literal; when present, the host'sAAAARRset is replaced with this address.- At least one of
ipv4oripv6must be present and valid. - Omitted address families are left unchanged. KDynDNS never modifies RR types other than
AandAAAA.
-
Responses:
200 OKon success;400for missing/invalid params;401for missing/invalid credentials (withWWW-Authenticate);403if the host is not allowed;429when authentication attempts for the same requester key are throttled;500if the DNS update fails. -
Example using curl on the unix socket:
curl --unix-socket /run/kdyndns/kdyndns.sock \ -u "user1:your-password" \ "http://localhost/update?host=myhost.example.com&ipv4=203.0.113.10&ipv6=2001:db8::10"
When started via systemd with a socket unit, KDynDNS will reuse the pre-opened unix-socket passed in through systemd (LISTEN_FDS). If no socket is provided it falls back to binding /run/kdyndns/kdyndns.sock itself. Pair the service with a matching socket unit so systemd manages creation and permissions of the socket.