TL;DR it's all about search domains.
DNS Search Domains are a client-side helper for resolving short names (identified as not containing a dot) to Fully Qualified Domain Names (FQDNs) - these do have dots
For example, let's say I have a bunch of development machines dev1, dev2 and dev3 that are all hosted at stage.example.com. Rather than typing dev1.stage.example.com to reach my machine, I can configure a search domain of stage.example.com. Now when I query dev1 and do not get a response from the name server, I will append stage.example.com and query for dev1.stage.example.com. I've made it simpler from a user's perspective at the expense of an extra DNS query.
Our implementation relies heavily on the ordering of search domains in
resolv.conf.
The suggested implementation imposes a heirarchy on lookups of domain names. Firstly, we look up in the "container domain", followed by the "network domain".
Note: As previously mentioned, this is ONLY for queries to unqualafied names
This scope is for an individual network.
It's domain name is in the format of <network_name>.network.docker
For example, if I have a network called frontend, my network scope would be frontend.network.docker
Names in this scope are available to all hosts attached to the network
This scope is for an individual container.
It's domain name is in the format of <container_id>.container.docker
For example, if I have a container with the UUID 6090eb04fe49f97eab070f8596ddcd17c3fca9fbac29b05618b5c37b14370335, then my container scope is 6090eb04fe49f97eab070f8596ddcd17c3fca9fbac29b05618b5c37b14370335.container.docker
Note: The size limit for DNS names is 255 octets, which roughly equates to 253 characters. So far, we've used UUID (64) + "container" (9) + "docker" (6) = 79 characters. That means, whatever we lookup can be 174 characters long :)
- Container Domain
- Network Domain
We can very easily add/remove search domains in resolv.conf without breaking the model.
Potential candidates include:
- Global Domain, "*.docker" - for services that are global
- Application/Project Domain, ".docker" - for services that are part of an application that contains multiple networks
- External Domain, "*.external" - for A records of services that are deployed outside of Docker
A linux resolv.conf "search" entry has the following limitations
- Each server must be delimited by whitespace
- No more than 256 characters including whitespace
- Maximum of 6 servers in a list
Currently we have 1+N entries, where N = number of networks attached. This scales to connection to 5 networks total. As the size of the tree increases (e.g including the 3 aforementioned domains) we hit 3+N which only permits connections to 2 networks
Server-side expansion of search domains In our DNS server implementation, we add some metadata to the request e.g Container ID, Network Membership etc... and we use this when processing a lookup for an unqualified domain name and proceed along the same lines.
$ nslookup foo
172.18.0.3 foo
The only drawback here is that the client is unaware of where this address is coming from (e.g what the expansions was)
This makes troubleshooting of resolution issues from within a container much more difficult.
The fix for this, is to provide a DNS client tool (like dig) that can pretend to be a container for the purposes of troubleshooting and debugging.
This tool would be run outside the container.
The downside of this approach is that it is non-standard.
### Links Replacement (Local Alias)
I have two containers in a network, "web_1" and "db_1" that I want to link together on the network "backend"
I want "web_1" to discover "db_1" as "db" - e.g docker run --net=frontend --link db_1:db --name web_1 ....
** web_1 resolv.conf **
search dc1930e0eca89208c9c0a3f58447430842afd98dacd7ae5fc50b055d8cb8b17f.container.docker backend.network.docker
nameserver 127.0.0.1
** web_1 lookups **
$ nslookup db_1
172.18.0.3 db_1.backend.network.docker
$ nslookup db
172.18.0.3 db.dc1930e0eca89208c9c0a3f58447430842afd98dacd7ae5fc50b055d8cb8b17f.container.docker
Now I create a frontend network, and attach my web container to it so it's accessible from my haproxy_1 container
** web_1 resolv.conf **
search dc1930e0eca89208c9c0a3f58447430842afd98dacd7ae5fc50b055d8cb8b17f.container.docker backend.network.docker frontend.network.docker
nameserver 127.0.0.1
** web_1 lookups **
$ nslookup db_1
172.18.0.3 db_1.backend.network.docker
$ nslookup haproxy_1
172.19.0.3 haproxy_1.frontend.docker
The one thing that we do not prevent with search domains is that a maclicious client may query addresses in a network to which he doesn't belong.
For example, let's say we have a compromised container called "web" on a network called "mynet". It's possible for an attacker to map your infrastructure using some form of dictionary compiled from common service names, or by extracting service names from a Docker Compose file hosted on GitHub
e.g
nslookup web.frontend
nslookup db.frontend
nslookup haproxy.frontend
This could be mitigated by providing a zone ACL, that only permits only authorized clients to query a zone