Setting up key based dynamic DNS updates with CFEngine

In order to set up key based dynamic DNS updates, you need to generate a public/private key pair using dnssec-keygen and add the public key as a key record on the name you want to be able to update using the key pair. In addition, you need in the zone config in bind to allow updates using cryptographic verification:

zone "example.com" {
 type master;
 file "/etc/bind/zones/db.example.com";
 update-policy {
   grant local-ddns zonesub any;
   grant * selfsub * A AAAA TXT SSHFP;
 };
};

If you use selfsub, the matching key record will also be allowed to update subrecords which is very useful for using DNS verification in letsencrypt for instance.

For the CFEngine bundle, the list of servers are defined in $(def.servers). I’m using the destination $(def.dir_files) as a file repository that is synced in the update policy.

bundle agent dynamic_dns_keys {
  vars:
    am_policy_hub::
    "dns_key[$(def.servers)]" slist => findfiles("$(def.dir_files)/etc/ssl/private/$(def.servers)/*.key");
    "count_dns_key[$(def.servers)]" int => length("dns_key[$(def.servers)]");
    "dns_private[$(def.servers)]" slist => findfiles("$(def.dir_files)/etc/ssl/private/$(def.servers)/*.private");
    "count_dns_private[$(servers)]" int => length("dns_private[$(def.servers)]");

  classes:
    am_policy_hub::
      "has_dns_key_$(def.servers_c[$(def.servers)])" expression => isgreaterthan("$(count_dns_key[$(def.servers)])", "0");

  files:
    am_policy_hub::
    "$(def.dir_files)/etc/ssl/dnskeys/."
      create => "true";
    "$(def.dir_files)/etc/ssl/dnskeys/$(def.servers).key"
      copy_from => local_dcp("$(dns_key[$(def.servers)])"),
      edit_defaults => no_backup,
      perms => mog("644", "root", "root"),
      ifvarclass => "has_dns_key_$(def.servers_c[$(def.servers)])";
    any::
    "/etc/ssl/private/dnskeys/."
      create => "true",
      copy_from => copyfrom_sync("$(def.dir_files)/etc/ssl/private/$(sys.fqhost)/"),
      depth_search => recurse(1),
      perms => mog("0600", "root", "root");

  commands:
    am_policy_hub::
      "/usr/sbin/dnssec-keygen -a ECDSAP384SHA384 -T KEY -n HOST $(def.servers)"
        contain => silent_in_dir("$(def.dir_files)/etc/ssl/private/$(def.servers)/"),
        ifvarclass => "!has_dns_key_$(def.servers_c[$(def.servers)])";

};

Finally we need the server configuration to only allow the correct server to download the private key:

bundle server access_rules()
{
 access:
  "$(def.dir_files)/etc/ssl/private/$(def.servers)/"
    admit_hostnames => { "$(def.servers)" };

};

CC BY-NC-SA 4.0 This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

One thought on “Setting up key based dynamic DNS updates with CFEngine”

Leave a Reply

Your email address will not be published. Required fields are marked *