Linux

DNS hyperlocal no Unbound: zona raiz local com RFC 8806

Raízes de árvore em tons de terra e verde ligadas a um servidor DNS recursivo com a zona raiz local

DNS hyperlocal é o nome que se usa no Brasil para manter uma cópia da zona raiz do DNS dentro do próprio recursivo. Em vez de perguntar aos servidores raiz quem responde pelo .br ou pelo .com, o Unbound consulta a cópia que ele mesmo baixou e validou. A técnica está descrita na RFC 8806, que substituiu a RFC 7706.

Este post parte do recursivo montado em DNS recursivo com Unbound para provedor: Debian, Unbound com DNSSEC e access-control fechado. Aqui entra só o que muda para ter o DNS hyperlocal: um arquivo de configuração, os testes com dig, como medir se valeu a pena e como vigiar a cópia para ela não envelhecer. Tudo foi montado a partir da documentação do Unbound e da RFC, com endereços de documentação para o servidor.

Resposta rápida

  1. Crie um auth-zone para "." com os servidores que aceitam AXFR da raiz como primary.
  2. Use for-downstream: no e for-upstream: yes: só o próprio Unbound consulta a cópia, e ele valida tudo com DNSSEC antes de responder ao cliente.
  3. Ligue fallback-enabled: yes. Se a cópia falhar ou expirar, o Unbound volta para a raiz da internet.
  4. Ligue zonemd-check: yes para conferir o resumo criptográfico da zona inteira.
  5. Não espere navegação mais rápida. O ganho real é resiliência e menos consultas saindo para a raiz.

O que é DNS hyperlocal

Um recursivo sem nada em cache começa pela raiz. Ele pergunta a um dos 13 servidores raiz (letras de a a m, com instâncias anycast pelo mundo) e recebe a delegação do TLD. Depois pergunta ao TLD e por fim ao autoritativo. No DNS hyperlocal, o primeiro passo não sai do servidor: a zona raiz inteira, com as assinaturas DNSSEC, está carregada na memória do Unbound.

A RFC 8806 pede três coisas. O sistema precisa validar com DNSSEC todos os registros assinados da zona. O serviço local da raiz só pode responder ao resolvedor do próprio host, nunca a outros sistemas. E a cópia precisa ser renovada pelos tempos do SOA; se não for possível renovar antes do expire, o resolvedor tem de voltar imediatamente para os servidores raiz da internet.

O Unbound atende os três requisitos com o auth-zone. Com for-downstream: no, ele não responde como autoritativo da raiz para ninguém. Com for-upstream: yes, ele usa a cópia no lugar da raiz durante a recursão, valida e guarda o resultado no cache. É o modo que a própria documentação do Unbound chama de comportamento hyperlocal.

Diagrama: Unbound com cópia local da zona raiz, transferida dos servidores raiz, com fallback
A raiz vem por AXFR; .br, .com e os autoritativos continuam sendo consultados pela internet.

O que você ganha de verdade

A RFC é honesta sobre isso: o efeito na velocidade das respostas boas é pequeno. As delegações dos TLDs têm TTL de cerca de dois dias e já estão no cache de qualquer recursivo com tráfego. O ganho está em outro lugar:

  • Resiliência. Se a raiz ficar inalcançável por ataque ou por problema no seu trânsito, o recursivo continua resolvendo TLDs enquanto a cópia for válida.
  • Menos consultas saindo. Nomes com TLD inexistente (erros de digitação, equipamentos mal configurados, sufixos internos vazando) recebem NXDOMAIN da cópia local.
  • Privacidade. Essas consultas deixam de atravessar a rede até a raiz.

O que pode dar errado

A RFC também lista o custo: é mais uma peça operacional. Se a renovação quebrar e ninguém notar, o recursivo pode ficar com dados velhos de um TLD que trocou todos os servidores de nome. Por isso o DNS hyperlocal só faz sentido com fallback ligado, validação DNSSEC e um alarme olhando o serial da cópia. Sem isso, você trocou um serviço que funciona sozinho por um que depende de você.

Configuração do DNS hyperlocal no Unbound

Crie o arquivo /etc/unbound/unbound.conf.d/hyperlocal.conf. O Debian já inclui todos os .conf desse diretório, então o restante da configuração do recursivo fica como está:

server:
    extended-statistics: yes

auth-zone:
    name: "."
    primary: 170.247.170.2        # b.root-servers.net
    primary: 192.33.4.12          # c.root-servers.net
    primary: 199.7.91.13          # d.root-servers.net
    primary: 192.5.5.241          # f.root-servers.net
    primary: 192.112.36.4         # g.root-servers.net
    primary: 193.0.14.129         # k.root-servers.net
    primary: 192.0.47.132         # xfr.cjr.dns.icann.org
    primary: 192.0.32.132         # xfr.lax.dns.icann.org
    primary: 2801:1b8:10::b       # b.root-servers.net
    primary: 2001:500:2::c        # c.root-servers.net
    primary: 2001:500:2d::d       # d.root-servers.net
    primary: 2001:500:2f::f       # f.root-servers.net
    primary: 2001:500:12::d0d     # g.root-servers.net
    primary: 2001:7fd::1          # k.root-servers.net
    primary: 2620:0:2830:202::132 # xfr.cjr.dns.icann.org
    primary: 2620:0:2d0:202::132  # xfr.lax.dns.icann.org
    fallback-enabled: yes
    for-downstream: no
    for-upstream: yes
    zonemd-check: yes
    zonefile: "/var/lib/unbound/root.zone"
OpçãoO que fazValor aqui
name: "."A zona é a raizObrigatório
primaryDe onde baixar a zona por AXFR/IXFR; tenta o próximo se um falharLista oficial do example.conf
fallback-enabledSe a busca na cópia falhar (inclusive por DNSSEC), consulta a internetyes (padrão é no)
for-downstreamResponder como autoritativo da raiz aos clientesno (padrão é yes)
for-upstreamUsar a cópia durante a recursãoyes
zonemd-checkConferir o registro ZONEMD, resumo da zona inteirayes (padrão é no)
zonefileArquivo onde a zona é gravada depois de cada transferênciaEm /var/lib/unbound

O bloco server: com extended-statistics liga os contadores que mostram quantas consultas a cópia respondeu. Sem ele, não há como medir o DNS hyperlocal depois. O Unbound aceita mais de um bloco server: espalhado pelos arquivos incluídos.

De onde vem a zona raiz

Nem todo servidor raiz aceita transferência de zona. A lista acima é a do example.conf do próprio Unbound: os servidores b, c, d, f, g e k, mais dois servidores de transferência da ICANN, em IPv4 e IPv6. O b.root-servers.net mudou de endereço em 2023; tutoriais antigos ainda trazem o IP anterior, que não deve mais ser usado como fonte. Confira a lista na versão atual do example.conf antes de copiar.

A transferência usa TCP na porta 53, saindo do recursivo. Se o seu firewall só libera UDP 53 de saída, o AXFR falha em silêncio e o fallback esconde o problema. O {a(U_UB, “manual do Unbound”)} recomenda IP nos primary, não nome, para não depender de resolver um nome que está dentro da própria zona. Há também a opção url:, que baixa o arquivo da zona por HTTP ou HTTPS; a RFC 8806 lista fontes possíveis no apêndice A.

Sobre integridade: a zona raiz é assinada, e o Unbound valida os registros com DNSSEC antes de usar. O zonemd-check soma uma conferência da zona inteira pelo registro ZONEMD, que pega alteração até na cola (glue), que não é assinada. Se a conferência falhar, a zona não é usada e, com o fallback ligado, a recursão segue pela raiz da internet.

AppArmor no Debian. O perfil do pacote deixa o processo unbound gravar só em arquivos dele dentro de /var/lib/unbound/. Um zonefile em /etc/unbound ou em outro diretório gera apparmor=”DENIED” no journal e a zona não é salva.

Aplicar e conferir a transferência

unbound-checkconf
systemctl restart unbound
unbound-control list_auth_zones
ls -l /var/lib/unbound/root.zone

O unbound-checkconf lê toda a configuração e acusa erro de sintaxe antes do restart. Depois do restart, o Unbound baixa a zona dos primary. O list_auth_zones mostra cada zona com o serial carregado:

.	serial 2026102800

Versões mais novas acrescentam na mesma linha a data em que a zona foi obtida. Se aparecer expired ou no serial, a transferência não aconteceu: confira TCP 53 de saída e o journal com journalctl -u unbound. O ls -l deve mostrar o root.zone com alguns megabytes, dono unbound.

Testes com dig

dig @127.0.0.1 . SOA +short | cut -d' ' -f3
dig @170.247.170.2 . SOA +short | cut -d' ' -f3
dig @198.51.100.53 tld-que-nao-existe. A
dig @198.51.100.53 registro.br A +dnssec

As duas primeiras linhas comparam o serial da cópia, perguntado ao próprio recursivo, com o serial de um servidor raiz. Os dois devem ser iguais ou a cópia ficar no máximo um passo atrás, porque o seu Unbound não recebe NOTIFY da raiz e só percebe a mudança quando confere o SOA, no intervalo de refresh (30 minutos na raiz):

2026102800
2026102800

O serial da raiz é a data mais um contador de dois dígitos, e muda pelo menos uma vez por dia. A terceira linha pergunta um TLD que não existe. A resposta tem de ser NXDOMAIN com a flag ad, sinal de que a negativa foi validada com DNSSEC:

;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 51344
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

A última linha é um teste de sanidade: um domínio real e assinado continua resolvendo com ad. Se o registro.br parar de resolver depois do DNS hyperlocal, desligue a configuração (seção mais abaixo) e investigue com calma.

Medir o ganho

unbound-control stats_noreset | grep authzone
tcpdump -ni eth0 -c 20 'dst port 53 and src host 198.51.100.53'
num.query.authzone.up=18342
num.query.authzone.down=0
  • num.query.authzone.up: consultas internas da recursão respondidas pela cópia. Sem DNS hyperlocal, elas iriam para a internet.
  • num.query.authzone.down: respostas dadas diretamente a clientes. Com for-downstream: no, fica em zero.
  • tcpdump: mostra as consultas que saem do recursivo para a porta 53. Com a cópia ativa, os destinos são servidores de TLD e autoritativos. Para os endereços da raiz sobram só as consultas de SOA e as transferências da própria cópia.

Compare com o número total de consultas (total.num.queries) para ter a proporção. Em um recursivo de provedor com cache quente, a fração que tocaria a raiz é pequena, e grande parte dela são nomes inexistentes. O Unbound já reduz essas consultas com o aggressive-nsec (RFC 8198), ligado por padrão, que reaproveita as provas NSEC de respostas anteriores. Meça antes e depois: a decisão de manter o DNS hyperlocal deve vir desse número, não de promessa de velocidade.

Em recursivos anycast, como no cenário de DNS anycast com BGP e FRR, cada nó mantém a sua própria cópia. Vale conferir o serial em todos os nós, porque um nó com transferência quebrada fica escondido atrás do mesmo IP de serviço.

Vigiar a cópia da raiz

A RFC 8806 recomenda um método automático para confirmar que a cópia está sendo renovada, e sugere olhar o serial do SOA. O script abaixo lê o serial pelo unbound-control, pega a data (os oito primeiros dígitos) e reclama se ela tiver mais de dois dias:

#!/bin/sh
# /usr/local/bin/raiz-local: avisa se a cópia da raiz parou de atualizar
S=$(unbound-control list_auth_zones | awk '$1 == "." {print $3}')
case "$S" in
    [0-9]*) ;;
    *) echo "raiz local sem serial"; exit 2 ;;
esac
DIA=$(echo "$S" | cut -c1-8)
IDADE=$(( ($(date -u +%s) - $(date -u -d "$DIA" +%s)) / 86400 ))
if [ "$IDADE" -gt 2 ]; then
    echo "raiz local parada: serial $S, $IDADE dias"
    exit 1
fi
echo "ok: serial $S"

O awk pega o terceiro campo da linha da zona ., que é o número do serial. O date -d converte a data AAAAMMDD em segundos, e a diferença dividida por 86400 dá a idade em dias. A saída usa códigos de retorno, então o script entra fácil num item do Zabbix ou num cron que manda e-mail. O expire do SOA da raiz é de sete dias; o alarme em dois dias dá tempo de agir antes do fallback.

Para forçar uma nova verificação nos primary sem reiniciar o serviço:

unbound-control auth_zone_transfer .
unbound-control list_auth_zones

Como desligar o DNS hyperlocal

Se algo der errado, o caminho de volta é tirar o arquivo e reiniciar. O resto do recursivo não depende dele:

mv /etc/unbound/unbound.conf.d/hyperlocal.conf /root/hyperlocal.conf.off
unbound-checkconf && systemctl restart unbound

O && só reinicia se a configuração restante estiver válida. Depois do restart, o Unbound volta a usar os servidores raiz pelas root hints embutidas, como antes.

Erros comuns

  • Lista de primary desatualizada. IP antigo do b.root ou servidor que não aceita AXFR. Use a lista do example.conf da versão atual.
  • TCP 53 bloqueado na saída. A transferência falha e o fallback mascara. O list_auth_zones denuncia.
  • for-downstream ligado. O padrão é yes. Esquecer a linha faz o Unbound responder como autoritativo da raiz aos clientes, o que a RFC 8806 proíbe.
  • Sem fallback. Com fallback-enabled: no (padrão), cópia expirada vira SERVFAIL para todo mundo.
  • zonefile fora de /var/lib/unbound. O AppArmor bloqueia a gravação.
  • Nenhum alarme. Sem vigiar o serial, a cópia pode ficar dias parada.
  • Recursivo aberto. DNS hyperlocal não muda o access-control. Recursivo aberto para a internet continua sendo vetor de ataque, como explica o post sobre portas de amplificação DDoS.

Perguntas frequentes

O DNS hyperlocal deixa a navegação mais rápida?

Quase nada. As delegações dos TLDs ficam em cache por cerca de dois dias, então a raiz é pouco consultada num recursivo com tráfego. O ganho real é resiliência e menos consultas saindo para a raiz, principalmente de nomes inexistentes.

Quanto espaço e memória a zona raiz ocupa?

O arquivo tem alguns megabytes, com as assinaturas DNSSEC incluídas. Para um servidor que já roda um recursivo de provedor, o consumo extra de disco e memória é pequeno.

O que acontece se a transferência da raiz parar?

O Unbound continua usando a cópia até o expire do SOA, que na raiz é de sete dias. Depois disso a zona expira e, com fallback-enabled ligado, o Unbound volta a consultar os servidores raiz. Sem fallback, as consultas viram SERVFAIL.

Posso usar o DNS hyperlocal com BIND ou outro resolvedor?

Pode. A RFC 8806 traz exemplos para vários resolvedores no apêndice B, e o BIND faz o mesmo com zona secundária e mirror. Este post cobre só o Unbound, que resolve tudo dentro do auth-zone.

Preciso de DNSSEC ligado para usar a zona raiz local?

Sim. A RFC exige que o sistema valide todos os registros assinados da zona e tenha a chave KSK da raiz atualizada. No Debian, o auto-trust-anchor-file do pacote já cuida da chave.

Conclusão

O DNS hyperlocal no Unbound é um arquivo de configuração de vinte e poucas linhas, mas exige disciplina: lista de fontes atualizada, TCP 53 liberado, fallback ligado, ZONEMD conferido e um alarme no serial. Em troca, o recursivo continua resolvendo TLDs se a raiz ficar inalcançável e deixa de mandar para fora as consultas a TLDs que não existem. Meça com os contadores do authzone antes de decidir se fica.

Referências: RFC 8806, unbound.conf(5) e o example.conf do Unbound.

Leitura recomendada: Tutorial DNS Hyperlocal, da wiki do Brasil Peering Forum (CC BY-NC 4.0).

Compartilhar:

Sobre o autor

Ivan Alves

Especialista em redes de provedores (ISP)

Ivan Alves trabalha há mais de 15 anos com redes de provedores de internet, do NOC ao backbone e à borda BGP. Coordena infraestrutura e tecnologia em um provedor regional e escreve aqui os tutoriais que gostaria de ter encontrado quando começou: comandos testados, cenário explicado e o porquê de cada linha.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *