12

How would you configure a DNS Name Server per NIC (eth0 vs eth1) interface on RHEL/Centos 6?

E.g.

eth0 is on subnet 10.0.0.1/24

eth1 is on subnet 192.168.0.1/24

Any requests sent over eth0 should use DNS server 10.0.0.2.

Any requests sent over eth1 should use DNS server 192.168.0.2.

I've added:

DNS1:10.0.0.2 > /etc/sysconfig/network-scripts/ifcfg-eth0

DNS1:19.168.0.2 > /etc/sysconfig/network-scripts/ifcfg-eth1

However these values get ignored, and it always defaults to the setting in resolv.conf "nameserver 10.0.0.2" When eth0 is down, connections are sent over eth1 ... however DNS can no longer resolve as it's trying to reach 10.0.0.2.

  • How do I get it to respect the DNS settings in ifcfg rather than a default for resolv.conf?

  • Or how do I configure a different DNS Name Server for eth0 vs eth1?

  • Is there a better way of handling this?

Updated

We have two VLANS, each has it's own DNS server on it's respective subnet. These handle look-ups for local DNS (example.loc, guest.app etc), as well as forwarding when needed.

These are two separate servers in two separate physical locations. If possible I'd rather not run one server across the two subnets (one handles sensitive data).

If eth0 was to go down, I need eth1 to continue to be able to make DNS requests.

I thought about adding two IPs to resolv.conf, and then letting it fallover if it can't reach the server in the first subnet, but this seems inelegant (having to wait for the first server to timeout with every DNS query when eth0 is down).

2
  • 1
    What about setting nameserver 10.0.0.2 and nameserver 192.168.0.2 in resolv.conf? Sep 3, 2012 at 14:16
  • If one subnet handles sensitive data as you say, what business does this particular machine have being connected to both subnets? Sep 3, 2012 at 19:30

3 Answers 3

9

You can't easily do what you want.

Or how do I configure a different DNS Name Server for eth0 vs eth1?

The name lookup for a hostname happens through standard system libraries and isn't associated in any way with a particular "connection". In fact, at the time the DNS query happens, there is no connection, because your application hasn't even figured out the address to which it's going to connect (which is why it's using DNS in the first place).

How do I get it to respect the DNS settings in ifcfg rather than a default for resolv.conf?

The Linux resolver only has a single, global configuration (/etc/resolv.conf). There is no per-interface, per-domain, or per-connection setting of any sort. The settings in /etc/sysconfig/network-scripts/... are only used to populate /etc/resolv.conf, and generally if you specify DNS1 and DNS2 in these files, the last interface to come up will be what you see in /etc/resolv.conf.

Is there a better way of handling this?

Can you tell us what you're actually trying to accomplish? We might be able to suggest better solutions if you can tell us more about your specific situation.

3
  • Have decided for now just to edit /etc/resolv.conf, and shorten the timeout. Thank you for your help.
    – DevGav
    Sep 4, 2012 at 19:44
  • what's confusing to me is that even though DNS is not associated with a particular interface because there's as yet no connection, nevertheless DNS servers must be specified at the interface level. seems arbitrary :/ Jun 17, 2016 at 21:46
  • There is a case however where it might be desirable to do so, it is routing by UID. This is very handy to route traffic from selected users to an interface (e.g. a vpn), that can be done with a single command: 'sudo ip -4 rule add uidrange 113-113 table 51820'. However, that would also route DNS which fails if the DNS of your interface does not resolve to the same IP as the one in the default gateway. So you have to set another rule for dns request to stay on the same machine... which is not what you want to completely separate traffic by user (UID). I have no solution for that case yet!
    – user941239
    Oct 9, 2021 at 14:41
6

The answer to all your three questions is now systemd-resolved (emphasis mine):

Lookup requests are routed to the available DNS servers and LLMNR interfaces according to the following rules:

Lookups for the special hostname "localhost" are never routed to the network. (A few other, special domains are handled the same way.)

Single-label names are routed to all local interfaces capable of IP multicasting, using the LLMNR protocol. Lookups for IPv4 addresses are only sent via LLMNR on IPv4, and lookups for IPv6 addresses are only sent via LLMNR on IPv6. Lookups for the locally configured host name and the "gateway" host name are never routed to LLMNR.

Multi-label names are routed to all local interfaces that have a DNS sever configured, plus the globally configured DNS server if there is one. Address lookups from the link-local address range are never routed to DNS.

If lookups are routed to multiple interfaces, the first successful response is returned (thus effectively merging the lookup zones on all matching interfaces). If the lookup failed on all interfaces, the last failing response is returned.

Routing of lookups may be influenced by configuring per-interface domain names. See systemd.network(5) for details. Lookups for a hostname ending in one of the per-interface domains are exclusively routed to the matching interfaces.

4

A DNS request is basically either

  1. "what's the IP address of host1.domain1.com," or
  2. "what's the hostname of 192.168.0.5."

So there's no knowing at "request" time which Ethernet card is going to be involved. What you could reasonably ask would be "all requests ending in 'domain1.com' should go to 192.168.0.2, and all other requests should go to 10.0.0.2."

And likewise, "All reverse DNS requests matching 192.168.0.0/24 should go to 192.168.0.2, and the rest should go to 10.0.0.2."

As larsks said, Linux doesn't support such a configuration. However, you could run your own, minimal DNS server that implements the above logic, and forwards requests to the appropriate "real" DNS server.

I believe dnsmasq can do this (see How to configure dnsmasq to forward multiple DNS servers?). But before I found that out, I rolled my own in Twisted:

from twisted.internet import reactor
from twisted.names import dns
from twisted.names import client, server

class Resolver(client.Resolver):
  def queryUDP(self, queries, timeout=None):
    if len(queries) > 0 and (str(queries[0].name).endswith('.domain1.com'):
      self.servers = [('192.168.0.2', 53)]
    else:
      self.servers = [('10.0.0.2', 53)]
    return client.Resolver.queryUDP(self, queries, timeout)

resolver = Resolver(servers=[('10.0', 53)])
factory = server.DNSServerFactory(clients=[resolver])
protocol = dns.DNSDatagramProtocol(factory)

reactor.listenUDP(53, protocol, interface='127.0.0.1')
reactor.listenTCP(53, factory, interface='127.0.0.1')
reactor.run()

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .