Nsupdate (DDNS for bind): How to use?

I would like to use the DDNS function of IPFire with my bind9 name server, thus using plain “nsupdate”. It doesn’t seem obvious to me, what to fill in the fields of IPFire to make it work. The fields are:
Hostname, Username and Password

I am missing to set the algorithm and key length and key name, like HMAC-SHA512 and 512 bytes. The complete information of a key looks like:
keyname.example.net. IN KEY 512 3 165 SomeEncryptedStringEndingIn==

Can someone please tell me how to use the IPFire form with nsupdate?

Hi @phloggu

Welcome to the IPFire community.

I believe that if you want to use nsupdate to submit ddns update requests to your bind server you will need to look at running nsupdate on the command line and providing whatever inputs are required there.

The nsupdate man page might help.
https://www.linux.org/docs/man1/nsupdate.html

1 Like

The dropdown has an entry “nsupdate”, there is also python code in IPFire to treat nsupdate requests (which I did not understand enough to figure out what to enter in the fields), so I guess it should work somehow…

If I use the CLI, as you suggest, e.g. using a dhcp-exit-hook to run a script, is that update save (do my changes to IPFire remain in the system, when I upgrade IPFire)?

I had never noticed that before! Unfortunately my knowledge on python is close to non-existent.It might make sense for you to join the dev mailing list and ask the question there. You will more likely get hold of the IPFire ddns expert there.
https://wiki.ipfire.org/devel/mailing-lists

I believe so. The problem usually only occurs when changes are made to existing IPFire config files. Those can be overwritten when an update is done or the WUI screen involved is updated. So your script file would be no problem. You might need to include it in the include.user backup file.

It would depend on how the dhcp-exit-hook would be triggered. I am not familiar about its usage. If that needs to have changes in the dhcp.conf file then you should put them in
/var/ipfire/dhcp/dhcpd.conf.local
The contents of this file get appended onto dhcp.conf and the file stays intact over updates and WUI modifications and it is backed up with the backup page.

1 Like

I dived deeper into the python code and I found the problem. It is reported on IPFire’s Bugzilla under #12837. Until this is solved, I do not see how the nsupdate provider can be used.

For the moment I use a bash script* to do the job. It was easy to use a dhcpcd exit hook to start the script: it need a link in the right directory. Use

locate dhcpcd | grep hook

to find dhcpcd’s hook directory and place a soft link there pointing to your script. The script needs to react on the reason of a call (positional parameter 1) and gets the IP in positional paramter 2. See the scripts already there for an example, especially what are good reasons to set or delete an DDNS address.

*) I wrote my own, which has some quirks, it’s not (yet?) intended for public use. You may find others that are more robust, tested and supported.

Adrian - when your script is ready for public use, please share.

I recently merged different scripts into one and I did not test it good enough since. You may download and see yourself: https://ente.limmat.ch/ftp/pub/software/bash/ddnsupdate/

On the Server side (in BIND9’s /etc/bind/named.conf.local) you need to allow nsupdate to update the A record of your dynamic host’s name. See the comments in letsencrypt_acme_dns-01_challenge_hook, a hook script for dehydrated, to get an idea how to allow such a modification. The description is for modifying TXT records, since the ACME challenge from dehydrated needs to do that, You would want an A and/or an AAAA record to allow your modification to happen.

Hello all

A Very old topic, I know, but if it can help someone…

I already did that a long time ago (with IPCop in fact, so a very looong time ago), and I had to do it again. I had to search a little bit in my memory, IPFires files and the Internet, so I came here… disappointed. I need to work a bit more… :grinning_face:
You’ll find good information (quite old anyway) on ‘Jurassic blog’ from François (in french)

Here’s a solution :wink: working with IPFire vanilla.

Suppose my domain is holmes.tld, with a working dyn.holmes.tld subdomain dedicated, and a working bakerst.dyn.holmes.tld subsubdomain
I want to update the IP of bakerst.dyn.holmes.tld

On my DNS server (bind9 on debian 12)

Generating key

I did that with a TSIG HMAC-MD5 key (don’t know if there could be security problems with this algorithm) generate with tsig-keygen (note the trailing dot in the command and the key name). Not sure it will work with other algorithms as I can’t specify which one to use on IPFire.

# tsig-keygen -a HMAC-MD5 bakerst.dyn.holmes.tld.
key “bakerst.dyn.holmes.tld.” {
algorithm hmac-md5;
secret “kaHQJpDtt+h2nBrfGinFxg==”;
};

You’ll find a lot of docs talking about dnssec-keygen, but it is not used anymore for dynamic dns keys generation as explained in man :

In prior releases, HMAC algorithms could be generated for use as TSIG keys, but that feature was removed in BIND 9.13.0. Use tsig-keygen to generate TSIG keys.

Copy the result in a file in /etc/bind (for example) named as you want (you can have more than a key)

# tsig-keygen -a HMAC-MD5 bakerst.dyn.holmes.tld. >> /etc/bind/dynamic.keys

# cat /etc/bind/dynamic.keys
key “bakerst.dyn.holmes.tld.” {
algorithm hmac-md5;
secret “kaHQJpDtt+h2nBrfGinFxg==”;
};

Bind config

Change permissions on the file : root:bind and 640 are far enough, then add the key(s) in bind’s config with an “include”. For example, just add that line at the end of /etc/bind/named.conf

include “/etc/bind/dynamic.keys”;

Debian allow /var/lib/bind to store dynamics configs and everything work smoothly. As I’m using ISPConfig to deal with DNS, I can’t force bind to store configs elsewhere than in /etc/bind (actually). So, I need to change apparmor’s named config to allow bind to read and write pri.dyn.* files (pri is for primary DNS configs in ISPConfig, may be you’ll have different prefixes, dyn is because I choose dyn as dedicated subdomain for all my domains) and *.jnl files (the journal files are already in pri.dyn.* ). Edit /etc/apparmor.d/user.sbin.named (bind usage is around line 20) and add

/etc/bind/pri\.dyn\.* rw,

Then restart apparmor and bind. That should do the job.

From elsewhere (a debian host with bind installed)

Testing the DNS server

# dig +short bakerst.dyn.holmes.tld
123.45.67.89

123.45.67.89 come from your ‘A’ record

TSIG keys are symmetric. From DNS server copy the dynamic.keys file somewhere (/etc/bind/ is a good choice) and check permissions.

test connection with nsupdate in manual mode with -v
add zone and update accordingly, then show will preview and send ask the server. Note the trailing dots in the “… SECTION”

# nsupdate -k /etc/bind/dynamic.keys -v
> zone dyn.holmes.tld
> update add bakerst.dyn.holmes.tld. 30 A 0.0.0.0
> show
Outgoing update query:
;; ->>HEADER<<- opcode: UPDATE, status: NOERROR, id: 0
;; flags:; ZONE: 0, PREREQ: 0, UPDATE: 0, ADDITIONAL: 0
;; ZONE SECTION:
;dyn.holmes.tld. IN SOA

;; UPDATE SECTION:
bakerst.dyn.holmes.tld. 30 IN A 0.0.0.0

> send
> quit

If send doesn’t react more, then everything is working from this point.

# dig +short bakerst.dyn.holmes.tld
123.45.67.89
0.0.0.0

0.0.0.0 has been added

remove 0.0.0.0 with an ‘update delete’ in place of the ‘update add

# dig +short bakerst.dyn.holmes.tld
123.45.67.89

0.0.0.0 has been removed

From IPFire server

Dynamic DNS service

check “Guess the real public IP…” unless your modem is bridged.

add a host with :

Service is “nsupdate”
Hostname MUST be bakerst.dyn.holmes.tld
Username MUST be bakerst.dyn.holmes.tld. (WITH a trailing dot)
Password MUST be kaHQJpDtt+h2nBrfGinFxg== (WHITOUT double quotes, WITHOUT algorithm)
Check “enable” box

et voilà ! Read the logs to see if everything works

if it doesn’t start, try ddns -d update-all --force It will give more information on transactions

[…]
Registered new provider: Zoneedit (``zoneedit.com``)
Registered new provider: zzzz (``zzzz.io``)
Running on distribution: ipfire-2
Loading configuration file /var/ipfire/ddns/ddns.conf
Updating bakerst.dyn.holmes.tld forced
Sending request (GET): ``https://checkip4.dns.lightningwirelabs.com
Request header:
User-agent: IPFireDDNSUpdater/014
Pragma: no-cache
Response header (Status Code 200):
date: Thu, 21 Aug 2025 12:56:10 GMT
upgrade: h2,h2c
connection: Upgrade
x-content-type-options: nosniff
x-frame-options: deny
referrer-policy: strict-origin
x-xss-protection: 1; mode=block
content-length: 35
content-type: text/plain
strict-transport-security: max-age=31536000; includeSubDomains; preload
Scriptlet:
key **** ****
update delete bakerst.dyn.holmes.tld. A
update add bakerst.dyn.holmes.tld. 60 A 156.78.91.232
send
quit
Dynamic DNS update for bakerst.dyn.holmes.tld (BIND nsupdate utility) successful
Logging successful update for bakerst.dyn.holmes.tld
Opening database /var/lib/ddns.db