I have an internal network with a DNS server running BIND, connected to the internet through a single gateway. My domain "example.com" is managed by an external DNS provider. Some of the entries in that domain, say "host1.example.com" and "host2.example.com", as well as the top-level entry "example.com", point to the public IP address of the gateway.
I would like hosts located on the internal network to resolve "host1.example.com", "host2.example.com" and "example.com" to internal IP addresses instead of that of the gateway. Other hosts like "otherhost.example.com" should still be resolved by the external DNS provider.
I have succeeded in doing that for the host1 and host2 entries, by defining two single-entry zones in BIND for "host1.example.com" and "host2.example.com". However, if I add a zone for "example.com", all queries for that domain are resolved by my local DNS server, and e.g. querying "otherhost.example.com" results in an error.
Is it possible to configure BIND to override only some entries of a domain, and to resolve the rest recursively?
The best method is via the response policy zone in Bind 9.8.1 or newer. It allows you to override single records in arbitrary zones (and there's no need to create a whole subdomain for that, only the single record you want to change), it allows you to override CNAMEs, etc. Other solutions such as Unbound cannot override CNAMEs.
https://www.redpill-linpro.com/sysadvent/2015/12/08/dns-rpz.html
EDIT: Let's do this properly then. I will document what I've done based on the tutorial linked above.
My OS is Raspbian 4.4 for Raspberry Pi, but the technique should work without any changes on Debian and Ubuntu, or with minimal changes on other platforms.
Go to where your Bind config files are kept on your system - here it's in
/etc/bind
. Create in there a file calleddb.rpz
with the following contents:What does it do?
www.some-website.com
with the fake address127.0.0.1
, effectively sending all traffic for that site to the loopback addresswww.other-website.com
to another site calledfake-hostname.com
Anything that could go in a Bind zone file you can use here.
To activate these changes there are a few more steps:
Edit
named.conf.local
and add this section:The tutorial linked above tells you to add more stuff to
zone "rpz" { }
but that's not necessary in simple setups - what I've shown here is the minimum to make it work on your local resolver.Edit
named.conf.options
and somewhere in theoptions { }
section add theresponse-policy
option:Now restart Bind:
That's it. The nameserver should begin overriding those records now.
If you need to make changes, just edit
db.rpz
, then restart Bind again.Bonus: if you want to log DNS queries to syslog, so you can keep an eye on the proceedings, edit
named.conf.local
and make sure there's alogging
section that includes these statements:Restart Bind again and that's it.
Test it on the machine running Bind:
If you run dig on a different machine just use @the-ip-address-of-Bind-server instead of @127.0.0.1
I've used this technique with great success to override the CNAME for a website I was working on, sending it to a new AWS load balancer that I was just testing. A Raspberry Pi was used to run Bind, and the RPi was also configured to function as a WiFi router - so by connecting devices to the SSID running on the RPi I would get the DNS overrides I needed for testing.
The Unbound recursive DNS server has the ability to override individual resource records.
Look at the
local-zone
andlocal-data
configuration settings in the manual, e.g.:The
transparent
setting on thelocal-zone
tells it to do normal recursive lookups for any names not supplied withlocal-data
.You may want to look into "dnsmasq", which lets you do some pretty clever things with tweaking resolution.
What you're looking for is split DNS, which is defined by Webopedia as:
Essentially, you will need to make a copy of your external zone file and prop it up on your internal DNS server, then change or add the records needed specifically for your internal network. This is a pretty common setup, though it can be a pain to keep the "external" records synchronized between the two DNS servers. If you create or change a record on the public server, it will also need to be created or changed on the private server as well.
This can be implemented regardless of what DNS server implementation you use. In most setups, you will have one DNS server that serves the external network, and a different one that serves the internal network. With BIND, as possibly other implementations, you can have both versions of the zone on the same server through a use of the "allow-query" statement within the zone section of the named.conf file.
Another possibility on BIND (and I've never tried this) would be to set your example.com domain on the internal DNS server with only the records you use internally. Then, set a "forward" statement with the "first" argument (in conjunction with "forwarders"). In theory, this would go ask the external DNS server (as set in "forwarders" for an answer, which wouldn't have your internal records and return a failure response. Then, the internal server would look at itself for an answer. Not sure if that would work, but it's a thought.
Use dnsmasq makes it real easy. http://www.thekelleys.org.uk/dnsmasq/doc.html Acts as the dns server but gets answers from local dns server. Nice thing is you can override single domain records without messing with zone files
In BIND I get to this results by defining a zone using desired host name. The approach is fine if you only want to override a few hosts.
My zone declaration looks like this:
My zone definition looks like this:
So if I query example.com on intranet DNS and ISP DNS I get same IP but if I query override.example.com I get different results if intranet DNS (primary) is accessible.
You're already on the right track.
On your internal DNS servers, you'll need to define a zone for every exception host immediately below the "example.com". To minimize these exceptions, it is common practice to name all internal machines "hosta.internal.example.com", with the DNS server sending most queries to external DNS servers, but authoritative for the zone "internal.example.com". (Once you get past a small operations, there is usually a couple DNS server to which clients are directed and a separate authoritative DNS to which those are servers are directed for "internal.example.com".)
Usually, it is only when a host must be reachable both externally and internally that the exceptions you describe get created. Even then, you may want to use "host1.example.com" from the outside and "host1.internal.example.com" from the inside. Internal hosts get configured to look for names within "internal.example.com". There are situations where what you're already doing is appropriate, such as if the certificate for a server identifies the server as "host1.example.com", in which case you want that to be the name to which clients connect.
As a matter of fact there is another, even if perhaps slightly different, way to do this. I have the same situation, I have a domain which is used externally and internally, and I have external static and dynamic hosts. The only really painful ones are the external dynamic ones. The solution is possibly not the most elegant, but implementable with a small script. Mostly I am doing my own dynamic DNS script with the API of my dynamic DNS provider, I run this script by cron, every 5 mins:
1) get my external IP. has it changed ? no, exit.
2) changed IP, call API of dyndns-provider, with the new IP-address,
3) sed the db.mydomain.com with the external IP
4) restart bind.
Works very reliably for my home network