Invoking ddclient from dhclient to update 3rd party dynamic dns hosts on FreeBSD

In this post:

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)
Website Pin Facebook Twitter Myspace Friendfeed Technorati del.icio.us Digg Google StumbleUpon Premium Responsive

Leave a Comment

Scroll to Top