Score:0

Dynamically register hostnames on DNS server (via DHCP)

nl flag

I want to set up a small network, where a central DHCP server leases IPv4 addresses to the clients. The clients already have their hostnames set and should advertise those to the central DNS server, so both the server and all clients can find each other with that hostname. The DNS server will resolve LAN addresses of the domain "my.domain" and point towards an external DNS server for all other domains (internet).

In my current setup, I have two boxes: 10.0.100.1 is the server (Ubuntu 22.04), where DHCP and DNS are hosted. 10.0.100.2 is configured as a client (Fedora 35) (DHCP sends this fixed IP during my test phase).

This is the client (10.0.100.2) configuration:

$ cat /etc/hostname
clienthost

$ cat /etc/systemd/network/20-wired.network
[Match]
Name=enp0s31f6

[Network]
LinkLocalAddressing=ipv4
DHCP=ipv4
SendHostname=true

[DHCPv4]
UseDomains=true

$ resolvectl
Global
    Protocols: LLMNR=resolve -mDNS -DNSoverTLS DNSSEC=no/unsupported
resolv.conf mode: stub

Link 2 (enp0s31f6)
  Current Scopes: DNS LLMNR/IPv4
    Protocols: +DefaultRoute +LLMNR -mDNS -DNSoverTLS DNSSEC=no/unsupported
Current DNS Server: 10.0.100.1
    DNS Servers: 10.0.100.1
    DNS Domain: my.domain

The IP 10.0.100.2 is correctly assigned. The client can ping the server (10.0.100.1) with its IP, hostname or FQDN. I can also see in tcpdump that the hostname is sent to the DHCP server (option 81 Client FQDN). So far so good.

The DHCP server config is supposed to be changed once the initial setup is working, towards handing out IPs from a range. So in the future I won't have fixed-assigned IP addresses for the clients. I will skip showing the rndc key files here. They are identical and placed in the configured locations. The server is configured as follows:

$ cat /etc/hostname
serverhost

$ cat /etc/systemd/network/20-wired.network
[Match]
Name=enp0s31f6

[Network]
LinkLocalAddressing=ipv4
Address=10.0.100.1/16
Gateway=10.0.1.1
DNS=10.0.100.1

[DHCPv4]
UseDomains=my.domain

$ cat /etc/default/isc-dhcp-server
INTERFACESv4="enp0s31f6"

$ cat /etc/dhcp/dhcpd.conf
include "/etc/dhcp/ddns-keys/my-domain.key";
default-lease-time 7200;
max-lease-time 28800;
ddns-updates on;
ddns-update-style standard;
ddns-domainname "my.domain.";
allow-unknown-clients;
authoritative;
zone my.domain. {
    primary 10.0.100.1;
    key ddns-mydomain;
}

zone 10.0.in-addr.arpa. {
    primary 10.0.100.1;
    key ddns-mydomain;
}

# only serve the single client box specifically during test phase
subnet 10.0.0.0 netmask 255.255.0.0 {}
host testhost {
  hardware ethernet 00:00:00:00:00:00;
  fixed-address 10.0.100.2;
  option subnet-mask 255.255.0.0;
  option routers 10.0.1.1;
  option domain-name-servers 10.0.100.1;
  option domain-name "my.domain";
  filename "pxelinux.0";
}

$ cat /etc/bind/named.conf
include "/etc/bind/keys/my.domain.key";
include "/etc/bind/named.conf.options";
include "/etc/bind/named.conf.local";
include "/etc/bind/named.conf.default-zones";

$ cat /etc/bind/named.conf.options
acl "internal" {
    127.0.0.1;
    10.0.0.0/16;
};

options {
    directory "/var/cache/bind";

    recursion yes;
    allow-recursion { internal; };
    listen-on { 10.0.100.1; };
    allow-transfer { none; };

    allow-query { internal; };
    allow-query-cache { internal; };

    forwarders {
        1.1.1.1;
    };

    listen-on-v6 { any; };
};

$ cat /etc/bind/named.conf.local
zone "my.domain" {
    type master;
    file "/etc/bind/zones/db.my.domain";
    update-policy { grant ddns-mydomain name my.domain ANY; };
    allow-transfer { none; };
};

zone "0.10.in-addr.arpa" {
    type master;
    file "/etc/bind/zones/db.0.10";
    update-policy { grant ddns-mydomain name my.domain ANY; };
    allow-transfer { none; };
};

$ cat /etc/bind/zones/db.my.domain
$TTL    86400
@   IN  SOA serverhost.my.domain. admin.my.domain. (
                  3     ; Serial
              28800     ; Refresh
               3600     ; Retry
              28800     ; Expire
              43200 )   ; Negative Cache TTL
;

; name servers - NS records
    IN  NS  serverhost.my.domain.

; A records
serverhost.my.domain.   IN  A   10.0.100.1

$ cat /etc/bind/zones/db.10.0
$TTL    86400
@   IN  SOA serverhost.my.domain. admin.my.domain. (
                  3     ; Serial
              28800     ; Refresh
               3600     ; Retry
              28800     ; Expire
              43200 )   ; Negative Cache TTL
;

; name servers - NS records
    IN  NS  serverhost.my.domain.

; PTR records
100.1   IN  PTR serverhost.my.domain.   ; 10.0.100.1

I think that should be all relevant configuration. Please let me know if you need something else.

The issue here is that, being on 10.0.100.1 (serverhost) I can only ping clienthost via its IP 10.0.100.2 but neither by its hostname nor FQDN. Unfortunately, I don't have a good idea where to start debugging to see if the client hostname is sent to the DNS server and registered or not.

Maybe a potentially unrelated side note: Running the command dhcp-list-lease on server-host returns an empty list. The logs show an DHCPACK for 10.0.100.2 but it never shows up in this particular output (which would have been interesting, because there is a "hostname" column).

Edit: It looks like the key might be important after all. Originally I manually created a key with rndc-confgen -a -b 512, then copied that file to /etc/dhcp/rndc-keys/. Currently, I generated a new key with ddns-confgen -a -b 512 and placed the key both in /etc/bind/keys/my.domain.key and in /etc/dhcp/ddns-keys/my.domain.key (and updated the include statements in the respective configuration files). I still have the rndc key under /etc/bind/rndc.key which is also picked up by bind9 as the logs show.

Edit2: Manually running nsupdate looks like the following:

$ nsupdate -D -k /etc/bind/keys/my.domain.key
> update add clienthost.my.domain 7200 A 10.0.100.2
> send
[...]
Reply from update query:
;; ->>HEADER<<- opcode: UPDATE, status: REFUSED, id:  39064
;; flags: qr; ZONE: 1, PREREQ: 0, UPDATE: 0, ADDITIONAL: 1
;; ZONE SECTION:
;my.domain.         IN  SOA

;; TSIG PSEUDOSECTION:
ddns-mydomain.      0   ANY TSIG    hmac-sha256. 1652972427 300 32 4e/XXXXXXXXXXXXXXXXXXXXXXXX/bmg= 39064 NOERROR 0

And during the manual update the logs show

client @0x7f61d8004cb8 10.0.100.1#39791/key ddns-mydomain: updating zone 'my.domain/IN': update failed: rejected by secure update (REFUSED)
Nikita Kipriyanov avatar
za flag
Do you have any DNS update logs? Enable it (see [here](https://serverfault.com/questions/1100116/how-to-log-verbose-details-about-dns-update-queries-in-bind/1100228#1100228)). Also try testing adding DNS updates by hand via `nsupdate` with your key. By the way what's `rndc.key`? You should create a dedicated key for dynamic updates, don't use automatic key which is used for `rndc` utility.
a.ilchinger avatar
nl flag
The manual update via `nsupdate` returns the status code REFUSED, but the debug output says "tsig verification successful".
Nikita Kipriyanov avatar
za flag
What's in the logs at the same instant of time? And, now when you see a problem, begin with debugging it. I'll speak again, don't rely on that key, generate a dedicated one. When `nsupdate` starts working as expected with that key, move further and set it up in the `dhcpd`.
mx flag
your allow-update option doesn't look right. It currently allows only rndc-keys. You can add an address match list (such as "allow-update { 10.0.0.0/16}"), or use a update policy.
a.ilchinger avatar
nl flag
I switched from the `allow-update` subcommand to the `update-policy` subcommand and made logging work. I also used a new, dedicated ddns key. Now I am a bit at a loss. The manpage for nsupdate says the key given for the -k options has to look like `K{name}.+157.+{random}.private`, which, in my experience, looks like a dnssec key, which I don't use at all. The key I generated with `ddns-confgen` and use has a different format. There is no indication in the logs that another key format would be required.
Nikita Kipriyanov avatar
za flag
If you want to permit the key to update any record in the zone, use either `update-policy { grant mykey zonesub any; }` or `allow-update { key mykey; }`; those work exactly the same. New versions of BIND ship a dedicated `tsig-keygen`, use it, if you have. Otherwize, generate a key with `dnssec-keygen -a HMAC-SHA256 -b 256 -n HOST mykey`. Both files will contain the same secret, just format it properly, like `key "mykey" { algorithm hmac-sha256; secret <a secret from generated file>; };`. A key formatted this way is included into both DNS and DHCP servers: `include "<path>/mykey.key"`.
Nikita Kipriyanov avatar
za flag
... and the same key file with this format is used with nsupdate: `nsupdate -k <path>/mykey.key`.
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.