@Lenne, I solved this immediately after replying to your OP, but I forgot to circle back and provide the solution. Thus it's no longer fresh on my mind, so please let me know if there or any errors, or if I forgot any detail.
Using Sidvind's "Dhcpd on Commit" tutorial as a reference, I added the following to /etc/dhcp/dhcpd.conf
, which causes clients without a hostname (or with an invalid hostname) to be assigned something similar to ip-10-12-34-56
:
on commit {
set clientIpHyphenated = binary-to-ascii(10, 8, "-", leased-address);
# Check whether the client supplied a hostname with illegal characters,
# e.g., "iHome SmartPlug-27C139" with a space or underscore in it.
# This resolves failures when trying to update DNS forward maps:
# dhcpd: Unable to add forward map from
# iHome SmartPlug-27C139.JonathansSecretDomain.com. to 10.12.34.56: REFUSED
if (option host-name ~~ "^[a-z0-9][a-z0-9\-]+[a-z0-9]$") {
set valid-hostname-else-null = option host-name;
} else {
set valid-hostname-else-null = null;
}
# Get the client name from the first of the following:
# 1. Client DHCP Option FQDN
# 2. Client DHCP Option Hostname (and it is a valid hostname without spaces, underscores, etc)
# 3. Name of static lease (host-decl-name)
# 4. A generated name that looks like: "ip-10-12-34-56"
# 5. "none", if all else fails. Shouldn't ever occur.
set clientName = pick-first-value(
option fqdn.hostname,
valid-hostname-else-null,
# Optional:
# This can cause an annoying repetitive error in syslog and dhcpd.log that I suppressed:
# dhcpd[965395]: data: host_decl_name: not available
# If you uncomment it, then also add my rsyslog code below.
#host-decl-name,
concat("ip-", clientIpHyphenated),
"none"
);
# Set the dynamic hostname, which otherwise wouldn't have been set if the client didn't
# request one. We also ensure above that it doesn't contain invalid characters, and that a
# fallback hostname will be generated if needed.
ddns-hostname = clientName;
# Optional:
# If the client supplied a hostname with illegal characters, log what we changed it to for ddns.
# (e.g., "iHome SmartPlug-27C139" with a space or underscore in it.)
#if (not (option host-name = valid-hostname-else-null)) {
# log(info, concat("Invalid hostname: \"", option host-name, "\" --> \"", clientName, "\""));
#} elsif (not (option host-name = clientName)) {
# log(info, concat("Hostname overridden: \"", option host-name, "\" --> \"", clientName, "\""));
#}
}
If you uncomment some of the optional stuff in the above config, then create /etc/rsyslog.d/10-dhcpd.conf
and add the following:
# Log most dhcpd messages to /var/log/dhcpd.log.
if $programname == 'dhcpd' then {
# Spam: If it's a notice message containing "...host_decl_name...", then don't log it.
# I think this "stop" rule is specific enough to not be exploitable, but I'm open to feedback.
if $msg contains "data: host_decl_name: not available" and $syslogseverity-text == 'error'
then stop
# Otherwise, log the message.
/var/log/dhcpd.log
# Prevent info and debug-level events from also appearing in the syslog.
# (Stop will prevent any additional rules from being applied to the message, including
# preventing it from being duplicated to the syslog later on.)
# Higher severity messages will otherwise intentionally be allowed
# to continue to be processed, and thus will be duped to syslog.
# If you don't like this behavior, then just add an unconditional "stop".
if $syslogseverity-text == 'info' or $syslogseverity-text == 'debug'
then stop
}
Finally, run:
sudo systemctl restart rsyslog.service isc-dhcp-server.service
Although this solution doesn't replace specific invalid characters (which doesn't seem to be possible?), it does replace invalid hostnames containing invalid characters with a generic hostname, which is sufficient for my needs, and hopefully for yours as well. (I like that it also assigns clients without a hostname a generic hostname based on their IP.)