In this post:
- FreeBSD 14.2
- ddclient-3.11.2
- Ansible playbook for ddclient: https://github.com/dlangille/ddclient/
- forked from https://github.com/hammadrauf/ddclient/
In my previous post, I configured dhclient to setup my Hurricane Electric tunnel and notify HE of same.
First attempt:
[12:39 gw01 dvl ~] % sudo ddclient [12:47 gw01 dvl ~] % tail /var/log/messages ... Mar 6 12:47:50 gw01 ddclient[52929]: WARNING: '' is not a valid IPv4 or IPv6 address Mar 6 12:47:50 gw01 ddclient[52929]: WARNING: found neither IPv4 nor IPv6 address Mar 6 12:47:50 gw01 ddclient[52929]: WARNING: Could not determine an IP for bast.example.org
Next attempt after reading https://ddclient.net and finding a link to sample-etc_dhclient-exit-hooks, I tried again.
NOTE: -daemon=0 means ddclient runs and stops. It does not background.
[12:49 gw01 dvl ~] % sudo ddclient -daemon=0 -syslog -use=ip -ip=192.0.2.59 FAILED: updating bast.example.org: authentication failed (HTTP/1.1 401 Unauthorized FAILED: Server: nginx FAILED: Content-Type: text/plain; charset=UTF-8 FAILED: Transfer-Encoding: chunked FAILED: Connection: keep-alive FAILED: Cache-Control: no-cache, private FAILED: Date: Thu, 06 Mar 2025 12:49:35 GMT FAILED: WWW-Authenticate: Basic realm="No-IP DNS Update API" FAILED: FAILED: badauth FAILED: )
OK, progress. :)
Without Ansible
Sometime, I like to get things working before finalizing the configuration.
dyn.com
For dyn.com, this worked: https://help.dyn.com/ddclient/ – Do not “double quote” the credentials – I learned that the hard way.
I see ddclient can use use environment variables. I may pursue that.
This is the configuration I used:
# from https://help.dyn.com/ddclient/ use=ip login=dvl password=pass123 protocol=dyndns2 server=members.dyndns.org wildcard=NO host=bast.example.org
You need to modify these items for your configuration:
- login
- password – note you might have an API token instead of the password you use to log into dyn.com
- host
dnsomatic
use=ip \ server=updates.dnsomatic.com \ protocol=dyndns2 \ login=dvl@example.org \ password='password123' \ host=MyBSDHost
You need to modify these items for your configuration:
- login
- password
- host
nsupdate – RFC 2136
I am very light on this subject. It assumes you know about working with RFC 2136 keys etc. https://ddclient.net/protocols.html has some information (search for nsupdate).
protocol=nsupdate \ use=ip \ server=dns-hidden-master.int.example.net \ login=/usr/local/bin/nsupdate \ password=//usr/local/etc/nsupdate.unixathome.example.net.key \ zone=example.net \ ttl=300 \ unixathome.example.net
You need to modify these items for your configuration:
- service – point at your own RFC 2136 enabled server or provider
- password – your key
- zone – the DNS zone you are updating
- unixathome.example.net the DNS host you are updating
kill – new to me
During my ddclient configuration, I found these are identical:
sudo kill $(cat /var/run/ddclient.pid) sudo kill -TERM $(cat /var/run/ddclient.pid)
Checking the man page, TERM is the default signal. All these years I have been typing that…. I could have been retired by now.
With Ansible
This is my Ansible configuration for use with the playbook mentioned at the top of this post. This uses Ansible Vault for some values. Setting up Ansible Vault is outside scope for this post.
You may notice that I dropped the use of wildcard=NO – I found it was not required.
Also, the trailing \ used above was not necessary.
[16:24 ansible root /usr/local/etc/ansible] # cat host_vars/gw01.int.unixathome.org/ddclient.yaml --- ddclient_backup: 'yes' ddclient_specify_cache: true ddclient_configs: - protocol: dyndns2 use: ip server: members.dyndns.org login: "{{ vault_ddclient_login_dyndns }}" password: "{{ vault_ddclient_password_dyndns }}" hosts: - bast.example.org - protocol: dyndns2 use: ip server: updates.dnsomatic.com login: "{{ vault_ddclient_login_BSDCabalHQ }}" password: "{{ vault_ddclient_password_BSDCabalHQ }}" hosts: - MyBSDHost - protocol: nsupdate use: ip server: dns-hidden-master.int.example.net login: "{{ vault_ddclient_login_bast }}" password: "{{ vault_ddclient_password_bast }}" zone: example.net ttl: 300 hosts: - unixathome.example.net
That creates this in /usr/local/etc/ddclient.conf. I see ttl is not present. I see a bug to fix.
# Ansible managed. Template: /usr/local/etc/ansible/roles/ddclient/templates/ddclient.conf.j2 daemon=300 ssl=yes syslog=yes cache=/var/cache/ddclient/ddclient.cache use=ip protocol=dyndns2 server=members.dyndns.org login=dvl password=pass123 bast.example.org use=ip protocol=dyndns2 server=updates.dnsomatic.com login=dvl@example.org password='password123' MyBSDHost use=ip protocol=nsupdate server=dns-hidden-master.int.example.net zone= example.net login=/usr/local/bin/nsupdate password=/usr/local/etc/nsupdate.unixathome.example.net.key unixathome.example.net
Testing this from the command line, I get:
[18:47 gw01 dvl ~] % sudo ddclient -syslog -use=ip -ip= 192.0.2.59 -daemon=0 -verbose SUCCESS: MyBSDHost: skipped: IP address was already set to 192.0.2.59. SUCCESS: bast.example.org: skipped: IP address was already set to 192.0.2.59. SUCCESS: unixathome.example.net: skipped: IP address was already set to 192.0.2.59.
With these log entries:
Mar 7 15:27:50 gw01 ddclient[95351]: SUCCESS: MyBSDHost: skipped: IP address was already set to 192.0.2.59. Mar 7 15:27:50 gw01 ddclient[95351]: SUCCESS: bast.example.org: skipped: IP address was already set to 192.0.2.59. Mar 7 15:27:50 gw01 ddclient[95351]: SUCCESS: unixathome.example.net: skipped: IP address was already set to 192.0.2.59.
The amended /etc/dhclient-exit-hooks script
This is what I’m running with now:
#!/bin/sh if [ "$interface" = "{{ wan_nic }}" ] then # removed "RENEW" - don't need to restart this stuff then if [ "$reason" = "BOUND" ] || [ "$reason" = "REBOOT" ] || [ "$reason" = "REBIND" ] then ( # XXX make this data driven? sleep 30 service openvpn restart logger "dhclient-enter-hooks (reason: $reason, iface: $interface)" # set up the new HE tunnel /usr/local/sbin/he-net.sh "$new_ip_address" # notify HE.net of our new IP address /usr/local/sbin/he-notify.sh /usr/local/sbin/ddclient -syslog -use=ip -ip="$new_ip_address" -verbose -daemon=0 ) & else logger "dhclient-enter-hooks but for something we ignore (reason: $reason, iface: $interface)" fi else logger "dhclient-enter-hooks but not for our main NIC, wtf? (reason: $reason, iface: $interface)" fi
Log file entries
Here’s what I see today in the logs:
[18:12 gw01 dvl /etc] % tail -F /var/log/messages | grep dhclient Mar 7 18:26:54 gw01 root[11045]: dhclient-enter-hooks but for something we ignore (reason: RENEW, iface: igc0)