This is a web interface for DNS zone management using TSIG keys (RFC2845). To use it you must have a valid TSIG key and configure your DNS zone master name server to allow AXFR and DDNS requests signed by your key.
In addition to the web frontend for human users there is a HTTP(S) based "API" for routers.
DNS management interface is split in two parts - frontend written in JavaScript and backend written in PHP.
Backend is completely stateless and is used only to convert HTTP requests to DNS queries. Single backend can be safely used by multiple users managing different DNS zones.
- PHP 8.1 or newer. This is required by the
mikepultz/netdns2library (which uses native PHP enums internally); the backend itself also relies on modern PHP syntax (e.g.str_ends_with()) not available on PHP 7.x. - A web server capable of running PHP (Apache, nginx+php-fpm, or PHP's built-in server for local testing/development).
- Composer, to fetch both backend (NetDNS2) and frontend (jQuery, Bootstrap, DataTables) dependencies.
- No database is required - the backend is completely stateless.
-
Install a web server and PHP on your machine
apt install -y apache2 php libapache2-mod-php -
Install Git and some tools
apt install -y git cronic curl php-zip 7zip -
Clone repository
Note: installing a web server package such as Apache typically creates
/var/www/htmlwith a default placeholder page already in it, andgit clonerefuses to clone into a non-empty directory. Remove it first:rm -rf /var/www/htmlgit clone <REPO_URL> /var/www/html -
Install composer
curl -sS https://getcomposer.org/installer -o composer-setup.phpphp composer-setup.php; rm composer-setup.phpmv composer.phar /usr/local/bin/composer -
Install dependencies
cd /var/www/html; composer install
To try it out on local machine without a full blown web server you can use PHP built-in web server. Start it from this project directory, with:
php -S 127.0.0.1:8080
And point your browser to http://127.0.0.1:8080/.
Security remark: While this project does not cache any sensitive data by itself remind yourself that any sensitive data might be recorded by your website hoster.
Backend does not require any initial setup and can be used as it is. On the frontend user have following settings:
- DNS zone - Domain name of zone that is being managed, example: example.net
- Key name - Name of key that is used to sign DNS requests, must be the identical to the key name configured on a DNS server, example: key.example.net.
- Key type - Algorithm that is used to generate signature, must be the same as configured on a DNS server, example: sha512
- Key - Secret key used to sign requests, must be base64 encoded, example: UNhY4JhezH9gQYqvDMWrWH9CwlcKiECVqejMrND2VFw=
Advanced settings:
- DNS Server - IP address of zone master name server. DNS requests are being sent to this address. This field is filled usually automatically after DNS zone is entered. It can be entered manually if system fails to detect it automatically.
- Proxy URL - Backend URL (relative or absolute). Default is search for backend on the same web server, same directory. It should be changed if backend and frontend are on different web servers.
- Filter RRs - List of resource record types (comma separated) to filter out before displaying zone records.
User frontend files:
- index.html
- ddnsadmin.js
- vendor (jQuery, Bootstrap, DataTables - fetched by Composer, same as the backend dependencies below; there is no npm/Node.js involved)
Router/Endpoint fronted file:
- nsupdate.php
Backend files:
- dnsproxy.php
- composer.json
- composer.lock
- vendor
+--------------+ +--------------+ +------------+
| | HTTP/S | | DNS | |
| Web browser |-------->| PHP backend |----->| Master |
| | | | | Nameserver |
| index.html |<--------| dnsproxy.php |<-----| |
| ddnsadmin.js | ^ | NetDNS2 | ^ | |
+--------------+ | +--------------+ | +------------+
| |
JSON request over HTTP (key is send |
in plaintext here, except for HTTPS) |
|
Signed DNS request (AXFR or DNS update)
(key is not sent here, only request signature)
In each request frontend passes your zone key to the backend. It is important to use HTTPS or start backend on your local machine using PHP built-in web server to avoid eavesdropping. But you must yet trust your website hoster.
Backend uses the NetDNS2 library (mikepultz/netdns2)
for DNS packet crafting. This repository uses composer to manage the NetDNS2
library files.
Note: this project is pinned to the exact 2.0.7 release - do not bump to
2.0.8. NetDNS2's 2.x line is a from-scratch rewrite (PHP enums, PSR-4
namespaced NetDNS2\... classes, requires PHP 8.1+) and is not
API-compatible with the older 1.5.x line (flat Net_DNS2_* classes,
formerly published as the now abandoned pear/net_dns2 package).
2.0.8 added response-TSIG-MAC verification (\NetDNS2\RR\TSIG::verify(),
called from Client::sendPacket()), but that verification re-serializes the
response from its parsed object model instead of digesting the raw bytes
actually received. Since DNS servers routinely use name compression when
encoding responses, the client's re-encoding frequently doesn't reproduce
the exact bytes the server signed, so the MAC comparison fails even for a
completely legitimate response. In production this surfaces as every
/axfr, /add-record, /delete-record, and nsupdate.php request failing
with TSIG response validation failed: invalid MAC or time window exceeded. - confirmed against a real nameserver, not just in testing.
2.0.7 predates this feature entirely (Client.php has no response-TSIG
verification code at all), so it isn't exposed to the bug while keeping the
same namespaced 2.x API this project's dnsproxy.php/nsupdate.php use.
Upstream bug: mikepultz/netdns2#161 is a related,
already-fixed issue in the same area (TSIG key names/algorithms containing
a ., e.g. the hmac-md5.sig-alg.reg.int identifier) - that fix is
present in 2.0.7; only the newer, separate response-verification bug is
being avoided by staying off 2.0.8. Re-evaluate once a 2.0.9 (or later)
release fixes this - check for a fix touching RR/TSIG.php's verify()
method before upgrading past 2.0.7.
Generate a new random key (256 bit length) and base64 encode it:
$ dd if=/dev/urandom bs=32 count=1 2>/dev/null | base64
UNhY4JhezH9gQYqvDMWrWH9CwlcKiECVqejMrND2VFw=
In the examples below we will use sha512 HMAC algorithm and will name our key uberkey.
TSIG key information summary:
Key name: uberkey
Key type: hmac-sha512
Key: UNhY4JhezH9gQYqvDMWrWH9CwlcKiECVqejMrND2VFw=
Bind 9 configuration snippet for "example.net" zone:
key uberkey {
algorithm hmac-sha512;
secret "UNhY4JhezH9gQYqvDMWrWH9CwlcKiECVqejMrND2VFw=";
};
zone "example.net" {
type master;
file "/etc/bind/db.example.net";
allow-transfer { key uberkey; };
allow-update { key uberkey; };
};
Zone transfer (AXFR) with TSIG key is supported since PowerDNS server 3.0. Documentation.
DDNS updates with TSIG key is supported since PowerDNS server 3.4. Documentation.
Knot configuration snippet for "example.net" zone:
keys {
uberkey hmac-sha512 "UNhY4JhezH9gQYqvDMWrWH9CwlcKiECVqejMrND2VFw=";
}
remotes {
any-with-key {
address 0.0.0.0/0;
key uberkey;
}
}
zones {
example.net {
file "/etc/knot/example.net.zone";
xfr-out any-with-key;
update-in any-with-key;
}
}
YADIFA configuration snippet for "example.net" zone:
<key>
name uberkey
algorithm hmac-md5
secret UNhY4JhezH9gQYqvDMWrWH9CwlcKiECVqejMrND2VFw=
</key>
<zone>
type master
domain example.net
file example.net.zone
allow-transfer key uberkey
allow-update key uberkey
</zone>
Note! YADIFA 1.0.3 supports only hmac-md5 TSIG key algorithm!
To test your name server configuration you can perform AXFR queries using dig tool and DDNS updates using nsupdate tool.
Perform AXFR using dig:
$ dig -y hmac-sha512:uberkey:UNhY4JhezH9gQYqvDMWrWH9CwlcKiECVqejMrND2VFw= -t axfr example.net @127.0.0.1
Perform DDNS update using nsupdate:
$ nsupdate -y hmac-sha512:uberkey:UNhY4JhezH9gQYqvDMWrWH9CwlcKiECVqejMrND2VFw=
> server 127.0.0.1
> zone example.net
> update add ddnstest.example.net 300 IN A 192.0.2.1
> send
> quit
If nsupdate do not print any error messages it means DDNS update was performed successfully.
In both examples replace 127.0.0.1 with your name server IP address.
http://127.0.0.1:8080/nsupdate.php?zone=example.net&key-name=host.example.net&key-type=hmac-sha512&key=&server=5.189.123.456&name=host.example.net&ttl=60&type=A
Note: <base64-encoded-key> must be URL-safe base64, not standard base64.
nsupdate.php expects +, / and = to be replaced with ., _ and -
respectively (a plain + in a URL query string is otherwise decoded as a
space by most HTTP stacks, silently corrupting the key). Convert a key from
the "Key generation" section above with, for example:
$ echo -n 'UNhY4JhezH9gQYqvDMWrWH9CwlcKiECVqejMrND2VFw=' | tr '+/=' '._-'
Also note that zone must exactly match, or be a subdomain of, one of the
domains configured in the $legitimate_domains array at the top of
nsupdate.php - it is rejected otherwise.