DNS: dnsmasq и порядок разрешения домён из resolv.conf

Автор: | 11/12/2018

Имеется AWS EC2 инстанс, на котором запущен dnsmasq.

Имеется AWS RDS-инстанс, для которого разрешён публичный доступ, и у домена которого, соответственно, есть два IP – публичный, если запрашивать публичные DNS, и приватный, который отдаётся DNS самого Amazon, с DNS VPC, в котором запущены инстансы. См начало поста AWS: php_network_getaddresses: getaddrinfo failed: Temporary failure in name resolution и dnsmasq.

Но возникает проблема – иногда dnsmasq локальным клиентам отдаёт публичный адрес, а иногда – приватный.

Т.е иногда ответ выглядит так:

[simterm]

admin@bttrm-stage-app-1:~$ dig mobilebackend-stage-db1-replica-rds.example.us-east-2.rds.amazonaws.com +short
ec2-18-***-***-186.us-east-2.compute.amazonaws.com.
18.***.***.186

[/simterm]

А иногда – так:

[simterm]

admin@bttrm-stage-app-1:~$ dig mobilebackend-stage-db1-replica-rds.example.us-east-2.rds.amazonaws.com +short
ec2-18-***-***-186.us-east-2.compute.amazonaws.com.
10.0.3.54

[/simterm]

Это приводит к проблемам, т.к. во-первых – все запросы от приложений на инстансах в VPC начинают ходить “через мир”, во-вторых – доступ к RDS ограничен Secuirty Group-ми, и если имя хоста резолвится на внешний IP и трафик идёт “через мир” – то залогиниться на сервер нельзя.

В примере ниже используется имя RDS stage.backend-db1-slave.example.com, которое через CNAME направлено на RDS-домен mobilebackend-stage-db1-replica-rds.example.us-east-2.rds.amazonaws.com, а он уже, в свою очередь, должен резолвиться на приватный IP из VPC-сети.

Содержимое resolv.conf:

[simterm]

root@bttrm-stage-app-1:/home/admin# cat /etc/resolv.conf 
domain us-east-2.compute.internal
search us-east-2.compute.internal
nameserver 127.0.0.1
nameserver 10.0.6.2
nameserver 1.1.1.1
nameserver 8.8.8.8

[/simterm]

Тут:

  • nameserver 127.0.0.1dnsmasq
  • nameserver 10.0.6.2 – VPC’s DNS
  • 1.1.1.1 и 8.8.8.8 – Cloudflare и Google

Включаем логи dnsmasq – в /etc/dnsmasq.conf добавляем строку log-queries, и перезапускаем dnsmasq:

[simterm]

root@bttrm-stage-app-1:/home/admin# systemctl restart dnsmasq.service

[/simterm]

Проверяем лог:

Dec 10 17:35:18 bttrm-stage-app-1 dnsmasq[538]: reading /etc/resolv.conf
Dec 10 17:35:18 bttrm-stage-app-1 dnsmasq[538]: ignoring nameserver 127.0.0.1 – local interface
Dec 10 17:35:18 bttrm-stage-app-1 dnsmasq[538]: using nameserver 10.0.6.2#53
Dec 10 17:35:18 bttrm-stage-app-1 dnsmasq[538]: using nameserver 1.1.1.1#53
Dec 10 17:35:18 bttrm-stage-app-1 dnsmasq[538]: using nameserver 8.8.8.8#53

Окей.

Теперь запрашиваем имя:

[simterm]

root@bttrm-stage-app-1:/home/admin# dig stage.backend-db1-slave.example.com +short
mobilebackend-stage-db1-replica-rds.example.us-east-2.rds.amazonaws.com.
ec2-18-***-***-186.us-east-2.compute.amazonaws.com.
18.***.***.186

[/simterm]

И смотрим лог:

Dec 10 17:47:45 bttrm-stage-app-1 dnsmasq[29188]: forwarded stage.backend-db1-replica.example.com to 10.0.6.2
Dec 10 17:47:45 bttrm-stage-app-1 dnsmasq[29188]: forwarded stage.backend-db1-replica.example.com to 1.1.1.1
Dec 10 17:47:45 bttrm-stage-app-1 dnsmasq[29188]: forwarded stage.backend-db1-replica.example.comto 8.8.8.8
Dec 10 17:47:45 bttrm-stage-app-1 dnsmasq[29188]: reply stage.backend-db1-replica.example.com is <CNAME>
Dec 10 17:47:45 bttrm-stage-app-1 dnsmasq[29188]: reply mobilebackend-stage-db1-replica-rds.example.us-east-2.rds.amazonaws.com is <CNAME>
Dec 10 17:47:45 bttrm-stage-app-1 dnsmasq[29188]: reply ec2-18-***-***-186.us-east-2.compute.amazonaws.com is NODATA-IPv6
Dec 10 17:47:45 bttrm-stage-app-1 dnsmasq[29188]: reply stage.backend-db1-replica.example.com is <CNAME>
Dec 10 17:47:45 bttrm-stage-app-1 dnsmasq[29188]: reply mobilebackend-stage-db1-replica-rds.example.us-east-2.rds.amazonaws.com is <CNAME>
Dec 10 17:47:45 bttrm-stage-app-1 dnsmasq[29188]: reply ec2-18-***-***-186.us-east-2.compute.amazonaws.com is 18.***.***.186

В документации сказано:

By default, dnsmasq will send queries to any of the upstream servers it knows about and tries to favour servers that are known to be up.

Т.к. в resolv.conf кроме самого dnsmasq есть три сервера – VPC, Cloudflare и Google – то dnsmasq получает ответ от одного, и использует его в дальнейшем для форвадинга новых запросов и кеширует значение для уже отправленных запросов.

Т.к. Cloudflare отвечает зачастую быстрее, чем DNS в самой VPC – то его ответ и кешируется.

Проверить скорость ответа можно с помощью dnsping из набора dnsdiag.

Ответ от Cloudflare – 18 ms:

[simterm]

root@bttrm-stage-app-1:/home/admin/dnsdiag# dnsping -c 1 -s 1.1.1.1 stage.backend-db1-slave.example.com 
dnsping DNS: 1.1.1.1:53, hostname: stage.backend-db1-slave.example.com, rdatatype: A
76 bytes from 1.1.1.1: seq=0   time=18.264 ms

[/simterm]

Ответ от VPC DNS – 24 ms:

[simterm]

root@bttrm-stage-app-1:/home/admin/dnsdiag# dnsping -c 1 -s 10.0.6.2 stage.backend-db1-slave.example.com 
dnsping DNS: 10.0.6.2:53, hostname: stage.backend-db1-slave.example.com, rdatatype: A
69 bytes from 10.0.6.2: seq=0   time=24.941 ms

[/simterm]

Google со своими 134 ms вообще теряется 🙂

Само собой – Cloudflare содержит публичный IP нашего RDS-сервера, что совсем не то, чего хотелось бы.

Первым вариантом могло бы стать включение опции strict-order:

-o, –strict-order
By default, dnsmasq will send queries to any of the upstream servers it knows about and tries to favour servers that are known to be up. Setting this flag forces dnsmasq to try each query with each server strictly in the order they appear in /etc/resolv.conf

Но тут возникает другая проблема: клиент отправляет запрос к dnsmasq, dnsmasq выполняет запрос к первому серверу из resolv.conf, в случае, если сервер не отвечает или на нём нет записи – dnsmasq вернёт клиенту ошибку. Но для того, что бы dnsmasq выполнил запрос ко второму серверу из списка в resolv.conf – клиент должен отправить повторный запрос к dnsmasq. См. обсуждение тут>>> и тут>>>.

В нашем случае – как раз не проблема, и можно было бы использовать такой подход.

Но есть второе решение – использовать параметр server, и задать конкретный DNS-сервер для конкретного домена/ов.

Обвновляем /etc/dnsmasq.conf:

...
server=/stage.backend-db1-slave.example.com/10.0.6.2
...

Теперь при запросе stage.backend-db1-slave.example.com – dnsmasq должен запросить ответ у DNS этой VPC, т.е. 10.0.6.2.

Перезапускаем:

[simterm]

root@bttrm-stage-app-1:/home/admin# systemctl restart dnsmasq.service

[/simterm]

Выполняем запрос:

[simterm]

root@bttrm-stage-app-1:/home/admin# dig @127.0.0.1 stage.backend-db1-slave.example.com +short
mobilebackend-stage-db1-replica-rds.example.us-east-2.rds.amazonaws.com.
ec2-18-***-***-186.us-east-2.compute.amazonaws.com.
10.0.6.42

[/simterm]

Проверяем лог:

Dec 10 18:32:04 bttrm-stage-app-1 dnsmasq[29527]: query[A] stage.backend-db1-slave.example.com from 127.0.0.1
Dec 10 18:32:04 bttrm-stage-app-1 dnsmasq[29527]: forwarded stage.backend-db1-slave.example.com to 10.0.6.2
Dec 10 18:32:04 bttrm-stage-app-1 dnsmasq[29527]: reply stage.backend-db1-slave.example.com is <CNAME>
Dec 10 18:32:04 bttrm-stage-app-1 dnsmasq[29527]: reply mobilebackend-stage-db1-replica-rds.example.us-east-2.rds.amazonaws.com is <CNAME>
Dec 10 18:32:04 bttrm-stage-app-1 dnsmasq[29527]: reply ec2-18-***-***-186.us-east-2.compute.amazonaws.com is 10.0.6.42

Вроде ОК.

Готово.