The other day I spun up a free-tier e2-micro instance in GCP to play around with something and wanted the ability to access it by a common domain name internally and externally: gcloud.cthudson.com. To address the external access, I created a script on the instance itself to hit the Cloudflare API and keep the dynamic IP up to date against the A record. Internally, I had to find a solution to keep my FreeIPA instance corresponding A record updated as well given it owns the cthudson.com zone internally. So off I went to find a solution. I figured I’d post it here for in the event it is - or pieces of it are - helpful to someone else.

FreeIPA Automated Access Approach

In order to accomplish automatic updating of a FreeIPA DNS record, you will need to have proper access and privileges to do so. There are quite a few ways you could do this, but the one that seemed like the best fit to me was to create a service principal. That way, a script could use the associated Kerberos keytab for needed access.

Creating Service Principal

  1. From an FreeIPA client, grab a privileged kerberos ticket # kinit admin
  2. Add a service prinicpal for DNS (I will use a ipa-joined host itx1.cthudson.com in this example)
    # ipa service-add dns/itx1.cthudson.com
    ------------------------------------------------------
    Added service "dns/itx1.cthudson.com@CTHUDSON.COM"
    ------------------------------------------------------
      Principal name: dns/itx1.cthudson.com@CTHUDSON.COM
      Principal alias: dns/itx1.cthudson.com@CTHUDSON.COM
      Managed by: itx1.cthudson.com
    

Privileges

In order for our keytab to have the correct permissions to update dns records, we need to create a role and associate it with the service principal.

Create a role for DNS Administration

# ipa role-add --desc="DNS Administration" "DNS Admin"
----------------------
Added role "DNS Admin"
----------------------
  Role name: DNS Admin
  Description: DNS Administration

Add the DNS Administrators privilege to our newly created role

# ipa role-add-privilege "DNS Admin" --privileges="DNS Administrators"
  Role name: DNS Admin
  Description: DNS Administration
  Privileges: DNS Administrators
----------------------------
Number of privileges added 1
----------------------------

Associate DNS Admin role with our service principal

For some reason, I could not get this to work via ipa role-add-member "DNS Admin" --services dns/itx1.cthudson.com@CTHUDSON.COM and added it via the FreeIPA webui instead, which did the trick. Just head over to the webui -> IPA Server tab -> Role-Based Access Control sub-tab -> Services sub-tab -> Add -> Select proper service principal from there. I will have to re-investigate this and update if I figure out what’s going on. It’s a weird one but in the end you should see something like:

# ipa role-show "DNS Admin"
  Role name: DNS Admin
  Description: DNS Administration
  Privileges: DNS Administrators
  Member services: dns/itx1.cthudson.com@CTHUDSON.COM

Pull the keytab

Next, we need to get the keytab and store it somewhere secure on the host that will be running our update script.

$ ipa-getkeytab -s idm1.cthudson.com -p dns/itx1.cthudson.com -k dnsadmin.keytab
Keytab successfully retrieved and stored in: dnsadmin.keytab

That’s all there is to getting the keytab. Make sure the permissions are 0600 for the user executing the update. ie:

-rw-------. 1 chris chris 180 Dec 16 12:13 dnsadmin.keytab

The Script

To wrap this post up, here is the ultimate script along with an explanation of what’s going on:

#!/bin/bash

/usr/bin/kinit -kt /home/chris/dns_scripts/dnsadmin.keytab dns/itx1.cthudson.com
IPA_IP=`/usr/bin/ipa dnsrecord-show cthudson.com gcloud --raw | grep record | sed s/"arecord: "// | xargs`
CLOUDFLARE_IP=`/usr/bin/dig @1.1.1.1 gcloud.cthudson.com +short`

if [[ "$IPA_IP" == "$CLOUDFLARE_IP" ]]; then
    /usr/bin/logger "Cloudflare gcloud.cthudson.com IP address matched IPA record. No update necessary."
    exit
else
    /usr/bin/ipa dnsrecord-mod cthudson.com gcloud --a-rec=$CLOUDFLARE_IP
    /usr/bin/logger "Cloudflare gcloud.cthudson.com IP address does not match IPA record. Updated IPA A record."
fi
  1. First, we kinit against the keytab we created to gain the proper permissions when using the ipa tools moving forward
  2. We grab the current gcloud.cthudson.com IP address in FreeIPA and save it into the IPA_IP variable
  3. We query Cloudflare’s dns to get the current gcloud.cthudson.com IP address and save it into a CLOUDFLARE_IP variable
  4. With the if statement, we are comparing the two addresses. If the addresses match, no udpates occur and we output that note to the system log. If the addresses do not match (our else in this case), we update the FreeIPA record via a dnsrecord-mod command and also notate the change in the system log.

Slap this script in cron to run however often you would like and there you have it! I run this every 10 minutes via the following in /etc/cron.d/dns:

*/10 * * * * chris /home/chris/dns_scripts/gcloud_update_idm_dns.sh

Cheers!