One of the labs my future Ansible training students will be running on the new FreeBSD training machine will be setting up a Gitea instance in a jail, and in order to access that instance with a Web browser they’ll need the external address of the host, on which nginx then redirects into students’ jails.

I wanted to be able to query this address (and use it programatically) on the host, so I decided I’d have the host add it dynamically to the DNS with a dynamic DNS update using something like the script I used for displaying the IP address of a machine on its console. A simple rc script did the trick but the result didn’t really satisfy me because the rc scripts works on boot only. I thought there must be a better way.

I found resolvconf(8), which did the trick, in particular to stop the resolver configuration from being updated, as I want my own:

# disable resolvconf from updating resolv.conf
echo "resolvconf=NO" > /etc/resolvconf.conf

I thought that script would be a good solution, but I was pointed at dhclient-script(8) which made more sense, and I whipped up a small script script to update the local BIND server running on the host via dynamic DNS.

I create the shell script /etc/dhclient-enter-hooks which is later sourced by dhclient-script(8). When the machine boots or a lease expires my hook is invoked with a number of environment variables which give me the data I need:

interface=em0
new_broadcast_address=172.16.23.255
new_dhcp_lease_time=10800
new_dhcp_message_type=5
new_dhcp_server_identifier=172.16.16.1
new_domain_name_servers=172.16.16.1
new_expiry=1580174627
new_ip_address=172.16.23.168
new_network_number=172.16.16.0
new_routers=172.16.16.1
new_subnet_mask=255.255.248.0
reason=REBOOT

All I then do is use $new_ip_address as the A resource record in my DNS entry.

Nothing doing. It wouldn’t work, until I realized by judicious use of ps and other friendly utilities, that the name server just wasn’t yet up when my dhclient-exit-hooks script ran.

if [ "$interface" = "em0" ]; then
        if [ "$reason" = "BOUND" -o "$reason" = "RENEW" -o \
             "$reason" = "REBOOT" -o "$reason" = "REBIND" ]; then
                (
                        sleep 30
                        logger "dhclient-enter-hooks (reason: $reason, iface: $interface)"
                        ip="${new_ip_address:-127.0.0.2}"
                        ttl="${new_dhcp_lease_time:-86400}"
                        /usr/local/bin/nsupdate -k /etc/ourkeys/manager.tsig <<-EOF
                                server 127.0.0.1
                                update del iface.example.org.
                                update add iface.example.org. $ttl A $ip
                                send
                                quit
                        EOF
                ) &
        fi
fi

It is now.

Pay particular attention to the 30 and the & at the end of the inner sub-shell. It’s a beaut, innit? Well, maybe not, but it has recorded a number of DHCP renewals. :-)

freebsd and rfc2136 :: 13 Feb 2020 :: e-mail