eero and Split-Horizon DNS

Update: As of Septmber 14, 2017 eero has updated all eero networks to enable hairpinning. That means the config I laid out isn't necessary, but maybe this is still useful for some folks.

At this point, I'd guess most folks reading this are familiar with eero and similar distributed mesh home wifi products. For those that aren't, the tl;dr is that in many environments, a single router with a set of radios won't cover a whole home very well, and a distributed system with multiple radios working together is a better solution (read the linked Sweethome overview - it covers this better than I will). I'm a happy eero customer, and I'm not super interested in getting into why eero was a better fit for me than its competitors at the time I made the decision. As I said, overall I've been very happy.

However, I did have to get a little creative to solve a specific problem:

NAT/Egress Hairpinning

I have long run a few internal services on various web-servers running inside and outside my network.

Unfortunately, eero doesn't currently support a feature called Hairpinning, which allows clients inside the network to egress the network and get a response from the public IP of the same network. As a result, I had to access these services by one name externally (eg. [server].alvani.me), and another name internally ([server-hostname].local). This is frustrating since it breaks scripts and alerts that rely on consistent DNS names for services, and it means I need a second set of bookmarks to access services inside versus outside my network, plus the congnitive load of remmebering if I should use this name or that name to access this service. The whole point of DNS is to make this easy on us simple humans.

Split Horizon DNS to the Rescue

Split Horizon is a pretty common implementation in the enterprise world. In general, it's overkill for home networks, since there aren't a lot of DNS needs for most home networks. I really didn't want to, but eventually it became clear that for the short term, it'd be a good idea to run my own internal DNS server which handled request for the *.jehanalvani.com and *.alvani.me domains, while forwarding all other requests to upstream DNS providers. I could run in this configuration until eero eventually adds support for the hairpinning.

I'm writing this to document how I configured split-horizon on a Synology with Synology's DNS Server software. There's no reason this couldn't be adapted fairly easily to BIND or whatever other DNS Server you might want to run.

I started by installing DNS Server on my DS414Play.

Then, in DNS Server, I configured my forwarders.

Forwarders

Oh, if you leave the "Limit Source IP service" box checked, make sure you add your internal network CIDR to the list of permitted IPs. Otheriwise you'll get "Forbidden" on your DNS requests.

I added the zones I care about

Zones

A Quick Aside About DNS Zones

If you're familar with DNS, this is not a section you'll need to read, but I got some feedback that the concept of DNS Zones are confusing. For our purposes, DNS Zones are any domain(s) or subdomain(s) you want your nameserver to respond for. Of course, zones are much more powerful, and can be used in a lot of ways. The bottom line is that Zones are a method of drawing a demarcation for the responsibilities of different nameservers.

Moving on:

Finally, inside the zones, I configured the subdomains and services I care about

Zone config

This matches the public DNS configuration of the same services, just replacing the IP address with the internal IP address of the server running the service. In my case, they're all the same, because I'm using a reverse proxy to interpret the requests and send them to the appropriate servers.

Now, any client that sends requests to this name server should recieve the internal record as an answer. I can test with a couple simple NSLOOKUPs from inside the network.

First, query a common public nameserver:

nslookup syno.jehanalvani.com 4.2.2.2

Which returns

>jehan.alvani@MBP $ nslookup syno.jehanalvani.com 4.2.2.2
Server:        4.2.2.2
Address:    4.2.2.2#53

Non-authoritative answer:
syno.jehanalvani.com    canonical name = dynamic.jehanalvani.com.
dynamic.jehanalvani.com    canonical name = starman.synology.me.
Name:    starman.synology.me
Address: 50.35.122.71

Where 4.2.2.2 is the nameserver that was queried, and the results of the non-autoritative answer are exactly what I've configured for my pubilc subdomain. (syno.jehanalvani.com is a CNAME to dynamic.jehanalvani.com, which is itself a CNAME to synology's dynamic DNS service). Ineffecient? Maybe a little. But I don't have to rememebr anything other than "dynamic.jehanalvani.com" after initial setup.

And a query against my Synology's private IP returns the following

`nslookup syno.jehanalvani.com 10.0.1.20`

>jehan.alvani@MBP $ nslookup syno.jehanalvani.com 10.0.1.20
Server:        10.0.1.20
Address:    10.0.1.20#53

syno.jehanalvani.com    canonical name = dynamic.jehanalvani.com.
Name:    dynamic.jehanalvani.com
Address: 10.0.1.20

Boom. That's what I want to see.

DHCP configuration

The next step is to tell the clients inside the network to use this nameserver. Clients inside the network needs to know to send requests to my nameserver. If you're configuring for your network, you might prefer not to set DHCP name servers to the your internal server, instead electing to give manual DNS server to specific clients. I wanted to be able to use any device on the network to reach these URIs, so I opted to configure eero's DHCP server to direct all clients to my internal nameserver.

DHCP Config

Caveat

On a reddit thread, someone asked if this configuration works with eero Plus. The short answer is: yes! The long answer is: yes, but with some qualifications.

eero hasn't published much about how eero Plus works. However, it seems to hijack outbound DNS requests, sending the request to a nameserver eero controls. It's important to note that this only affects outbound DNS requests, so requests that say inside your network still work, as long as there is a responder to accept the requests. In the configuration above, that means that any records in the defined zones will be successful, but anything that needs to be forwarded to an upstream server will be hijacked by eero Plus.

This breaks services like Unlocator and Unblock-Us which rely on their own DNS servers receiving all requests, and interpreting specific requests. I had to disable eero Plus for the time being, since I make use of these services (as you can tell if you're familiar with the nameservers I configured as forwarders).

Is it worth it?

Obviously, that depends on your needs and your level of comfort. I have been happy with this setup for the year+ that I've been using it. However, as soon as eero adds support for NAT hairpinning, I'll happily pull it out. This does introduce a bit of fragility I don't care for, mostly in that it forces my Synology to be powered on for anyone on my network to be able to reach any internet resources.