'Register DHCP static mappings in DNS forwarder' somewhat lacking



  • Upon enabling 'Register DHCP static mappings in DNS forwarder' I was surprised to discover that pfSense only used the system's domain name rather than domain names from individual hosts and dhcp pools. Digging revealed that the system_hosts_generate() function in /etc/inc/system.inc was unfortunately written to do just that. At line 279 of snapshot 2.1-RC0 (amd64) built on Mon Jul 29 02:58:44 EDT 2013:

    if ($host['ipaddr'] && $host['hostname'])
        $dhosts .= "{$host['ipaddr']} {$host['hostname']}.{$syscfg['domain']} {$host['hostname']}\n";
    

    I submit for consideration a slightly improved attempt at performing this task with sanity, giving priority first to host defined domain names then dhcp pool domain names before falling back to the system domain name:

    if ($host['ipaddr'] && $host['hostname'] && $host['domain'])
        $dhosts .= "{$host['ipaddr']} {$host['hostname']}.{$host['domain']} {$host['hostname']}\n";
    else if ($host['ipaddr'] && $host['hostname'] && $dhcpifconf['domain'])
        $dhosts .= "{$host['ipaddr']} {$host['hostname']}.{$dhcpifconf['domain']} {$host['hostname']}\n";
    else if ($host['ipaddr'] && $host['hostname'])
        $dhosts .= "{$host['ipaddr']} {$host['hostname']}.{$syscfg['domain']} {$host['hostname']}\n";
    

    A similar modification to the dhcpv6 routine directly below that one may also be beneficial.

    (edit: first code block was truncated. fixed.)


  • Banned

    Uhm… You are only supposed to register FQDNs for zones defined on the nameserver. Say, I assign www.google.com FQDN to a laptop. You seriously want to register that in DNS forwarder?!?  :o ??? ::)



  • From what I gather he is trying to achieve I think he is trying to say that if you define google.com as domains for your DHCP pools then yes.  He does not appear to be saying accept any domains.  He is saying also add dns for the domains in the DHCP pools not just the default system domain?

    I can not follow the code so maybe doktornotor is saying the code changes that are shown would not limit it to to just dhcp pools so further code would be needed.


  • Banned

    What I'm saying is that DNS server has no business registering records for zones it's not authoritative for. For anything beyond the very basic DNS forwarder needs, you should install unbound or some other DNS server.



  • @doktornotor:

    DNS server has no business registering records for zones it's not authoritative for

    I've never seen someone put so much opinion behind /etc/hosts :D



  • I can understand if some devs might not want to push a feature if they feel it will make the DNS forwarder too complicated for the intended goal of the base project.  I can also see it from the opinion that if you want to setup DNS you should do it right from the beginning in a standardized manor.

    Regardless though I do see how someone could find the extended feature very handy even though I personally don't need it.  The DNS forwarder implementation would just be easier to setup and require less dependencies.

    The administrator would be in control over what domains are updated for both setups(assuming the feature is programmed properly for the DNS forwarder implementation).  I don't see that as being a downside for either use case which seemed to be the main complaint in your initial response.  If the admin wanted to doe something non-standard and override an official DNS zone and allow zone updates from clients then they could easily do that with a regular DNS server setup too.

    If it is easy enough to add the feature(I don't know), The code changes are not too risky to implement(I don't know), The devs are OK with the extended feature, Someone can code up the changes, Others find it useful… why not implement the enhanced feature?



  • This whole conversation sounds like people are terrified of this causing some kind of upstream DNS changes… All this feature does is control the writing of /etc/hosts for use by dnsmasq to serve local dns requests, having absolutely jack all to do with global DNS. I'm very very confused as to why everyone is so very confused about this.

    A PC sends a DNS query to pfSense router, which is handled by dnsmasq
    dnsmasq first checks its own configuration for a matching manually entered record and if found replies directly to the PC
    dnsmasq second checks its local /etc/hosts file for a matching record and if found replies directly to the PC (The conversation ends here)
    Failing that dnsmasq forwards the query upstream to another dns server (usually a real one) and forwards the reply back to the PC.



  • The initial stated concern was that the implementation would allow a host and domain get updated in the firewalls /etc/hosts for a DNS domain the administrator did not intend.  Everyone that is even slightly familiar with DNS servers should know that you can't update a remote third party domain hosted by someone else (unless they made a huge mistake implementing it).  I think everyone would assume the code would check and not use the domain portion of a client hostname when adding/updating it in /etc/hosts.  It would be based on what the domain is set for a given DHCP range.  If the client matches a given DHCP pool then the domain defined with that pool would be updated with the client hostname.

    That is my understanding of what is being discussed.


  • Rebel Alliance Global Moderator

    I don't think anyone is worried about upstream dns change - but what you purpose could allow someone local on the network to point fqdn to their machine for nefarious reasons.

    So for example as just a client on this network, I could register www.google.com in the dns used by all other clients on this network.  And they would all then come to my box for www.google.com where I could track what they are looking for, possible inflict their machine with some sort of badware, etc..

    Its just not good practice to allow a machine to register in the networks dns any fqdn they want..  If the admin of the network wants to over ride www.google.com then he can do so quite easy with the gui already.

    Now the ability for a host in specific pool to be registered sounds reasonable.

    So what your saying is not happening is my pfsense domain is say local.lan but I create a dhcp pool on one of my segments that hands out say domainA.lan – your saying that host.domainA.lan is not being registered?

    And you would like that to be an option..  That seems very reasonable a request, the admin still controls what the domains are via setup of the dhcp pools domain.

    but allowing any host to register whatever fqdn it wants does not seem prudent from a security point of view.


  • Banned

    @TechSmurf:

    This whole conversation sounds like people are terrified of this causing some kind of upstream DNS changes…

    LOL. No, not at all.

    @TechSmurf:

    I'm very very confused as to why everyone is so very confused about this.

    I'm not really confused. Forwarder should act as forwarder. It's already complex enough to my taste. If you want full featured DNS server, then install one, instead of trying to host multiple domains in /etc/hosts.  :P



  • I'm terrified someone might try to break my DNS forwarder to add features no one needs.  :o



  • Alright, I guess I'm the only person who wants this. I'll just crawl back into my hole and continue patching my own setup to meet my standards of sanity.

    Take care folks. :D


  • Banned

    It's up to the developers whether they implement this or not… Just voicing my opinion about suitability of maintaining DNS "zones" split across /etc/hosts and DHCP server configuration. :)

    Cheers.



  • @johnpoz:

    So what your saying is not happening is my pfsense domain is say local.lan but I create a dhcp pool on one of my segments that hands out say domainA.lan – your saying that host.domainA.lan is not being registered?

    Correct. Currently, the DHCP server will reply to the client with option domain-name set to "domainA.lan" in this case (so the client thinks it's "host.domainA.lan"), but pfSense will register the client as "host.local.lan" regardless of what the domain name for the DHCP range is set to. This is arguably broken.

    @johnpoz:

    And you would like that to be an option..  That seems very reasonable a request, the admin still controls what the domains are via setup of the dhcp pools domain.

    I agree.



  • TechSmurf, can you submit your change as a pull request on GitHub?



  • @johnpoz:

    but allowing any host to register whatever fqdn it wants does not seem prudent from a security point of view.

    Is that what the proposed change does, though? In other words, where does $host['domain'] come from? If this is taken straight from the client's DHCP request, then yeah, that should certainly not be honored. However, I suspect this might actually be the "domain name" field for a static mapping (if one exists), as configured on pfSense; in that case, I think it would make perfect sense to include it.



  • @doktornotor:

    It's up to the developers whether they implement this or not… Just voicing my opinion about suitability of maintaining DNS "zones" split across /etc/hosts and DHCP server configuration. :)

    But the same argument can already be made about the existing "register DHCP […] in DNS forwarder" functionality. If this is really a concern, you could always just disable that option?!



  • @TechSmurf:

    At line 279 of snapshot 2.1-RC0 (amd64) built on Mon Jul 29 02:58:44 EDT 2013:

    if ($host['ipaddr'] && $host['hostname'])
        $dhosts .= "{$host['ipaddr']} {$host['hostname']}.{$syscfg['domain']}
    

    I submit for consideration a slightly improved attempt at performing this task with sanity, giving priority first to host defined domain names then dhcp pool domain names before falling back to the system domain name:

    if ($host['ipaddr'] && $host['hostname'] && $host['domain'])
        $dhosts .= "{$host['ipaddr']} {$host['hostname']}.{$host['domain']} {$host['hostname']}\n";
    else if ($host['ipaddr'] && $host['hostname'] && $dhcpifconf['domain'])
        $dhosts .= "{$host['ipaddr']} {$host['hostname']}.{$dhcpifconf['domain']} {$host['hostname']}\n";
    else if ($host['ipaddr'] && $host['hostname'])
        $dhosts .= "{$host['ipaddr']} {$host['hostname']}.{$syscfg['domain']} {$host['hostname']}\n";
    

    A similar modification to the dhcpv6 routine directly below that one may also be beneficial.

    The suggested code adds 2 things, which is why there are so many posts here, as people are discussing the "merits" of the 1st part of the "if" statement - that allows the requesting host to specify a domain name of its own choosing, not desirable.
    The first "else if" does the agreed useful thing, sets the domain to match the one given by the DHCP pool.
    The last "else if" is the existing behaviour.
    Something like this should be reasonable:

    if ($host['ipaddr'] && $host['hostname'] && $dhcpifconf['domain'])
        $dhosts .= "{$host['ipaddr']} {$host['hostname']}.{$dhcpifconf['domain']} {$host['hostname']}\n";
    else if ($host['ipaddr'] && $host['hostname'])
        $dhosts .= "{$host['ipaddr']} {$host['hostname']}.{$syscfg['domain']} {$host['hostname']}\n";
    

    (I haven't tried this at all for real, I haven't looked at the surrounding code, it does need to make sure it is using the the domain name from the matching interface, and potentially the matching DHCP pool, when the interface has multiple pools…)
    Edit: see the razzfazz post below, and ignore this one, I should have looked at the code properly first!



  • @phil.davis:

    The suggested code adds 2 things, which is why there are so many posts here, as people are discussing the "merits" of the 1st part of the "if" statement - that allows the requesting host to specify a domain name of its own choosing, not desirable.

    No, that's not what it does: The foreach loop that encloses the statement iterates over all static DHCP entries, and the $host variable in this particular statement is simply the configuration map for one of these entries. As such, $host['domain'] is not a domain name supplied by the client host, but rather the domain name configured for a particular static DHCP lease on the pfSense side of things. This seems perfectly fine. As a matter of fact, leaving the first part out would lead to inconsistent behavior again where the client may end up getting a different domain name in the DHCP response than what the server registers in DNS.

    Perhaps it is clearer when looking at the entire surrounding block of code:

    
    275:		if (isset($dnsmasqcfg['regdhcpstatic']) && is_array($config['dhcpd'])) {
    276:			foreach ($config['dhcpd'] as $dhcpif => $dhcpifconf)
    277:				if(is_array($dhcpifconf['staticmap']) && isset($dhcpifconf['enable']))
    278:						foreach ($dhcpifconf['staticmap'] as $host)
    279:							if ($host['ipaddr'] && $host['hostname'])
    280:								$dhosts .= "{$host['ipaddr']}	{$host['hostname']}.{$syscfg['domain']} {$host['hostname']}\n";
    281:		}
    
    


  • Yes, agree - I should have actually looked at the code first and not guessed :)
    I see you have done the pull request for review - see what the devs think of this…



  • Submitted a similar request last year.  But it was rejected for being LAN interface specific and needed to be more "generic".  Does yours addresses that issue?

    DNS Forwarder DHCP Domain Name FIX - /etc/inc/system.inc
    https://github.com/pfsense/pfsense/pull/185

    Then there is also possibly an issue with getting dhcpleases / dnsmasq a few lines further down to work with multiple domains.  I was never able to get it too anyway.

    Here is the code I currently patch with.  It uses the DHCP interface configured domain name.  But it will apply the first one to all I think.

    
    --- /etc/inc/system.inc	2012-08-29 14:11:57.000000000 -0700
    +++ /etc/inc/system.inc.DNSForwarderDHCPDomainName.FIX.Patched	2012-08-30 11:48:47.000000000 -0700
    @@ -274,10 +274,16 @@
     		}
     		if (isset($dnsmasqcfg['regdhcpstatic']) && is_array($config['dhcpd'])) {
     			foreach ($config['dhcpd'] as $dhcpif => $dhcpifconf)
    -				if(is_array($dhcpifconf['staticmap']) && isset($dhcpifconf['enable']))
    +				if(is_array($dhcpifconf['staticmap']) && isset($dhcpifconf['enable'])) {
    +					if ($config['dhcpd'][$dhcpif]['domain'])
    +						$dyndhcpifdomainname = $config['dhcpd'][$dhcpif]['domain'];	// Use Services DHCP Interface Domain Name
    +					else
    +						$dyndhcpifdomainname = $syscfg['domain'];									// Use System General Domain Name
    +
     						foreach ($dhcpifconf['staticmap'] as $host)
     							if ($host['ipaddr'] && $host['hostname'])
    -								$dhosts .= "{$host['ipaddr']}	{$host['hostname']}.{$syscfg['domain']} {$host['hostname']}\n";
    +								$dhosts .= "{$host['ipaddr']}	{$host['hostname']}.{$dyndhcpifdomainname} {$host['hostname']}\n";
    +				}
     		}
     		if (isset($dnsmasqcfg['regdhcpstatic']) && is_array($config['dhcpdv6'])) {
     			foreach ($config['dhcpdv6'] as $dhcpif => $dhcpifconf)
    @@ -281,10 +287,16 @@
     		}
     		if (isset($dnsmasqcfg['regdhcpstatic']) && is_array($config['dhcpdv6'])) {
     			foreach ($config['dhcpdv6'] as $dhcpif => $dhcpifconf)
    -				if(is_array($dhcpifconf['staticmap']) && isset($dhcpifconf['enable']))
    +				if(is_array($dhcpifconf['staticmap']) && isset($dhcpifconf['enable'])) {
    +					if ($config['dhcpdv6'][$dhcpif]['domain'])
    +						$dyndhcpifdomainname = $config['dhcpdv6'][$dhcpif]['domain'];	// Use Services DHCP Interface Domain Name
    +					else
    +						$dyndhcpifdomainname = $syscfg['domain'];									// Use System General Domain Name
    +
     						foreach ($dhcpifconf['staticmap'] as $host)
     							if ($host['ipaddrv6'] && $host['hostname'])
    -								$dhosts .= "{$host['ipaddrv6']}	{$host['hostname']}.{$syscfg['domain']} {$host['hostname']}\n";
    +								$dhosts .= "{$host['ipaddrv6']}	{$host['hostname']}.{$dyndhcpifdomainname} {$host['hostname']}\n";
    +				}
     		}
    
     		if (isset($dnsmasqcfg['dhcpfirst']))
    @@ -327,8 +339,17 @@
     			@touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases");
     		if (isvalidpid("{$g['varrun_path']}/dhcpleases.pid"))
     			sigkillbypid("{$g['varrun_path']}/dhcpleases.pid", "HUP");
    -		else
    -			mwexec("/usr/local/sbin/dhcpleases -l {$g['dhcpd_chroot_path']}/var/db/dhcpd.leases -d {$config['system']['domain']} -p {$g['varrun_path']}/dnsmasq.pid -h {$g['varetc_path']}/hosts");
    +		else {
    +//			foreach ($config['dhcpd'] as $dhcpif => $dhcpifconf) {
    +			$dhcpif = 'lan';
    +				if ($config['dhcpd'][$dhcpif]['domain']) {
    +					$dyndhcpifdomainname = $config['dhcpd'][$dhcpif]['domain'];		// Use Services DHCP Interface Domain Name
    +				} else {
    +					$dyndhcpifdomainname = $config['system']['domain'];				// Use System General Domain Name
    +				}
    +				mwexec("/usr/local/sbin/dhcpleases -l {$g['dhcpd_chroot_path']}/var/db/dhcpd.leases -d {$dyndhcpifdomainname} -p {$g['varrun_path']}/dnsmasq.pid -h {$g['varetc_path']}/hosts");
    +//			}
    +		}
     	} else {
     		sigkillbypid("{$g['varrun_path']}/dhcpleases.pid", "TERM");
     		@unlink("{$g['varrun_path']}/dhcpleases.pid");
    
    


  • @NOYB:

    Submitted a similar request last year.  But it was rejected for being LAN interface specific and needed to be more "generic".  Does yours addresses that issue?

    DNS Forwarder DHCP Domain Name FIX - /etc/inc/system.inc
    https://github.com/pfsense/pfsense/pull/185

    Then there is also possibly an issue with getting dhcpleases / dnsmasq a few lines further down to work with multiple domains.  I was never able to get it too anyway.

    I think the complaint about being too LAN-specific is related to the dhcpleases part, which I assume makes this work for dynamic leases as well? The code suggested by TechSmurf does not have this issue because it only deals with static assignments.



  • FWIW, the proposed change has been merged into master and RELENG_2_1.



  • Hooray to no more patching ;)



  • Nice job :).  This probably should be mentioned in the release notes for the final 2.1 that this is a behavior change.  Just in case someone set the DHCP scope domain to something different than the firewall domain for whatever reason.  It would be nice to let them know to check it in case they depend on internal DNS names registered by the DNS Forwarder for resolution.  If they didn't set the DHCP scope domain at all then it will still work as it did before.

    EDIT: rewrote this to make more sense.



  • adam, I stumbled acros this thread out of interet in doing so - that means that in the upcoming snapshots I should be able to do the following?

    I'd not want to register in DNS <hostname>. <pfsensedomain.example.org>but wanted to register <hostname>.<network-vlanname>. <pfsense|anyotherdomain.example.org>What would be then be the correct thing to do? Set a domain in the DHCP server for that port dhcpd runs on and "just" tell the DNS server to register it?
    (or is there any other black magic required)

    If so that would a great opportunity to re-jump on a new build :-)</pfsense|anyotherdomain.example.org></network-vlanname></hostname></pfsensedomain.example.org></hostname>



  • Yes, but note that this will only work for static leases at this point.



  • I'm afraid of new things :o