Servidor DNS #1 – Recursivo com BIND 9.20 no Debian 13 (Trixie)

BIND é o servidor DNS mais utilizado na Internet e um padrão de fato em sistemas Unix/Linux.
O DNS Recursivo é responsável pela resolução de nomes: recebe a consulta do cliente e percorre a hierarquia do DNS — servidores raiz → TLD (.br, .com) → autoritativo do domínio — até obter a resposta. O resultado fica armazenado em cache pelo tempo (TTL) definido pelo autoritativo do domínio consultado, o que reduz a latência das próximas consultas e o tráfego DNS saindo da sua rede.
Para um ISP, rodar recursivo próprio significa menor latência (o servidor está dentro do seu AS, próximo do cliente), controle total do serviço e independência de resolvers públicos como 8.8.8.8 e 1.1.1.1.
Atenção: este tutorial cobre apenas o Recursivo.
– DNS Autoritativo e Reverso estão na parte 2 ⏳(em breve)
– DNS anycast com FRR (alta disponibilidade) na parte 3 ⏳(em breve).
Usaremos o Debian 13 (Trixie) com BIND 9.20 — houve mudanças relevantes em relação aos tutoriais antigos com Debian 11/12.
Requisitos:
Debian 13 Trixie – Instalação Limpa
Leituras recomendadas:
Configurando interface de rede no Debian
Como melhorar a produtividade no seu Debian após instalação
Vire root com o comando correto!
$ su -
Pacotes recomendados:
# apt install vim wget unzip man-db htop tree mtr-tiny whois -y
Configurando a placa de Rede
Neste tutorial vou configurar a rede da forma simples e convencional (IP fixo na interface). Na parte 3 aprenderemos a configurar via roteamento (FRR), que é o cenário ideal para DNS anycast. Se quiser se aprofundar, veja também: Configurando interface de rede no Debian. Caso já tenha sua placa de rede configurada pule esta etapa.
Primeiro descubra o nome da sua interface:
# ip -br link
No meu caso o nome é ens18, o seu pode ser diferente (ens192, enp0s3, eno1, enp1s0…). Ajuste nos exemplos abaixo.
Edite o arquivo:
# vim /etc/network/interfaces
Seu arquivo deverá ficar assim (as linhas “source” e da interface de loopback “lo” já existem no arquivo, não duplique, apenas ajuste/adicione o bloco da sua interface):
source /etc/network/interfaces.d/*
auto lo
iface lo inet loopback
auto ens18
iface ens18 inet static
address 255.9.0.2/28
gateway 255.9.0.1
iface ens18 inet6 static
address 2559:ffff:cafe::2/64
gateway 2559:ffff:cafe::1
Obs: substitua os IPs pelos endereços do seu bloco. Estou usando 255.9.0.2/28 e 2559:ffff:cafe::2/64 apenas como exemplo.
Obs 2: em servidor use “auto” em vez de “allow-hotplug”. O allow-hotplug só levanta a interface por evento de hotplug e pode deixar seu servidor sem rede no boot em algumas situações; o auto garante que a interface suba junto com o sistema.
Aplique a configuração:
# systemctl restart networking
ATENÇÃO: se você está acessando o servidor via SSH e errar algo aqui, vai perder o acesso. Se possível faça essa etapa pelo console (físico ou da VM).
Mas recomendo que você reinicie o servidor.
# reboot
Acesse novamente o servidor pelo endereço de IP.
Valide se os IPs subiram e se há conectividade:
# ip -br address # ip -c address # ping -c3 8.8.8.8 # ping -c3 2001:4860:4860::8888
Instalação do Bind9
Instalação dos pacotes:
# apt install bind9 bind9-utils bind9-dnsutils
Obs: no Debian 13 o serviço agora se chama “named” (o nome “bind9” ainda funciona como atalho, mas nos comandos vou usar named que é o nome oficial).
Arquitetura de arquivos e diretórios do bind9 no Debian 13:
Estrutura de arquivos do BIND
Os principais arquivos do BIND ficam distribuídos nos seguintes diretórios:
/etc/bind/
Diretório principal de configuração do BIND.
- named.conf: arquivo principal, responsável por incluir os demais arquivos de configuração.
- named.conf.local: usado para declarar zonas locais, autoritativas ou configurações específicas do servidor.
- named.conf.options: contém opções globais, como recursão, encaminhadores, interfaces e controles de acesso.
- named.conf.root-hints: referência aos servidores raiz do DNS.
- rndc.key: chave utilizada para comunicação segura entre o comando rndc e o serviço named.
/usr/share/dns/
Contém arquivos de referência relacionados à raiz do DNS.
- root.hints: lista dos servidores raiz do DNS.
- root.key: chave de confiança utilizada na validação DNSSEC.
- root.ds: registro DS da zona raiz utilizado como referência para DNSSEC.
/var/cache/bind/
Diretório de trabalho do BIND.
Nele ficam arquivos gerados ou atualizados pelo próprio serviço, como zonas dinâmicas, journals e chaves gerenciadas pelo DNSSEC.
- managed-keys.bind: armazena chaves DNSSEC gerenciadas automaticamente pelo BIND.
- managed-keys.bind.jnl: arquivo de journal utilizado para registrar alterações dessas chaves.
/run/named/
Contém arquivos temporários criados enquanto o serviço está em execução.
- named.pid: armazena o PID do processo named.
- session.key: chave temporária utilizada em operações de atualização dinâmica local.
Configuração do Recursivo
Alteramos o DNS do servidor fazendo com que ele consulte em si próprio, através dos IPs de loopback. Essa alteração deve ser feita no arquivo /etc/resolv.conf.
# echo "nameserver 127.0.0.1" > /etc/resolv.conf # echo "nameserver ::1" >> /etc/resolv.conf
Neste ponto, o servidor já deve estar resolvendo nomes localmente. Agora utilize a ferramenta dig para realizar alguns testes:
# dig @localhost google.com.br A # dig @localhost google.com.br AAAA # dig @localhost google.com.br A +trace
Agora vamos ajustar o BIND para funcionar como servidor DNS recursivo, permitindo consultas somente das redes autorizadas.
# vim /etc/bind/named.conf.options
acl autorizados {
127.0.0.1;
::1;
10.0.0.0/8;
172.16.0.0/12;
192.168.0.0/16;
100.64.0.0/10;
fc00::/7;
fe80::/10;
// Prefixos da sua rede
255.9.0.0/22;
2559:ffff::/32;
};
options {
directory "/var/cache/bind";
dnssec-validation auto;
listen-on { any; };
listen-on-v6 { any; };
minimal-responses yes;
max-ncache-ttl 300;
min-cache-ttl 90;
allow-recursion { autorizados; };
allow-query-cache { autorizados; };
allow-query { autorizados; };
version "RR DNS Server - Do good";
};
Atenção: substitua 255.9.0.0/22 e 2559:ffff::/32 pelos prefixos IPv4 e IPv6 da sua rede.
Entendendo as opções
acl autorizados
Lista os endereços que podem utilizar o servidor recursivo. Inclui localhost, redes privadas, CGNAT, IPv6 interno e os prefixos da sua rede. Clientes fora dessa lista recebem REFUSED.
directory “/var/cache/bind”;
Define o diretório de trabalho do BIND.
dnssec-validation auto;
Ativa a validação DNSSEC para verificar a autenticidade das respostas.
listen-on / listen-on-v6
Define em quais endereços IPv4 e IPv6 o BIND escutará. O valor any significa todos os endereços do servidor.
minimal-responses yes;
Reduz o tamanho das respostas, enviando somente as informações necessárias.
max-ncache-ttl 300;
Mantém respostas negativas, como NXDOMAIN, no cache por no máximo 5 minutos.
min-cache-ttl 90;
Mantém respostas positivas no cache por pelo menos 90 segundos, mesmo quando o domínio informa um TTL menor.
allow-recursion
Define quem pode realizar consultas recursivas.
allow-query-cache
Define quem pode receber respostas armazenadas no cache.
allow-query
Define quem pode consultar o servidor. Como este servidor é somente recursivo, restringimos o acesso à ACL.
version “RR DNS Server – Do good”;
Oculta a versão real do BIND em consultas simples. Não substitui atualizações e boas práticas de segurança.
Opções adicionais
max-cache-ttl
Define o tempo máximo das respostas positivas no cache. O padrão é 7 dias.
max-cache-ttl 86400;
O exemplo acima limita o cache a 24 horas.
max-cache-size
Define quanto de memória o cache pode utilizar.
max-cache-size 2G;
Limita a 2 GB a memória RAM que o BIND pode usar para armazenar respostas DNS em cache.
Validando a configuração
# named-checkconf
Se nenhum erro for exibido:
# systemctl restart named # systemctl status named --no-pager
Refaça os testes para ver se ainda está resolvendo nomes:
# dig @localhost google.com A
Algumas coisas que você precisa saber, da saída dig:
; <<>> DiG 9.20.23-1~deb13u1-Debian <<>> @localhost google.com A ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42819 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags:; udp: 1232 ; COOKIE: 9afef403caa37261010000006a5680455e2f7c3f211ca5b7 (good) ;; QUESTION SECTION: ;google.com. IN A ;; ANSWER SECTION: google.com. 31 IN A 172.217.29.206 ;; Query time: 0 msec ;; SERVER: ::1#53(localhost) (UDP) ;; WHEN: Tue Jul 14 15:30:29 -03 2026 ;; MSG SIZE rcvd: 83
HEADER: Ela contém diversas informações a respeito da consulta.
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49083 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 9
- status indica o tratamento de erro nas consultas, sendo eles:
-- NOERROR: Nenhum erro encontrado, ou seja, sucesso.
-- SERVFAIL: Houve algum problema com o servidor, que não conseguiu processar a query.
-- NXDOMAIN: Significa que o domínio pesquisado não existe.
-- REFUSED: O servidor rejeitou a solicitação.
-flags: indicador das opções de recursividade e de autoridade, em resumo este conjunto de “letrinhas” (aa, rd, ra, etc), indica o estado ligado/desligado RFC1035
-- qr: se a mensagem é uma query (0) ou uma resposta (1). Como estamos avaliando somente as respostas, este bit sempre estará ligado (consequentemente, sempre veremos a string “qr” no campo “flags“).
-- aa: que o servidor que respondeu à solicitação é autoritativo do domínio.
-- rd: indica que o cliente solicitou recursão ao servidor DNS.
-- ra: que o servidor que respondeu à requisição suporta consultas recursivas.
-- tc: que a mensagem de resposta está truncada.
-- z: reservado para uso futuro.
- contadores: na mesma linha das flags, encontramos quantos registros foram retornados em cada seção.
QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 9
QUESTION
Replica a query que foi enviada para consulta, quando o tipo não é informado, o dig utiliza o registro A por padrão.
;; QUESTION SECTION: ;google.com.br. IN A
Outros exemplos:
# dig @localhost google.com.br AAAA # dig @localhost google.com.br MX # dig @localhost google.com.br NS # dig @localhost google.com.br TXT # dig @localhost google.com.br ANY
ANSWER
Contém a resposta para a consulta que foi enviada.
;; ANSWER SECTION: google.com.br. 86400 IN CAA 0 issue "pki.goog" google.com.br. 60 IN SOA ns1.google.com. dns-admin.google.com. 405356128 900 900 1800 60 google.com.br. 300 IN AAAA 2800:3f0:4001:819::2003 google.com.br. 300 IN A 172.217.173.67 google.com.br. 300 IN MX 0 smtp.google.com. google.com.br. 300 IN TXT "v=spf1 -all" google.com.br. 2774 IN NS ns4.google.com. google.com.br. 2774 IN NS ns2.google.com. google.com.br. 2774 IN NS ns1.google.com. google.com.br. 2774 IN NS ns3.google.com.
AUTHORITY
Servidores que respondem com “autoridade” pelo domínio (os NS)
;; AUTHORITY SECTION: google.com.br. 3599 IN NS ns1.google.com. google.com.br. 3599 IN NS ns4.google.com. google.com.br. 3599 IN NS ns3.google.com. google.com.br. 3599 IN NS ns2.google.com.
ADDITIONAL
Informações auxiliares ou adicionais à pesquisa, no exemplo IPs dos servidores DNS
;; ADDITIONAL SECTION: ns1.google.com. 172800 IN AAAA 2001:4860:4802:32::a ns2.google.com. 172800 IN AAAA 2001:4860:4802:34::a ns3.google.com. 172800 IN AAAA 2001:4860:4802:36::a ns4.google.com. 172800 IN AAAA 2001:4860:4802:38::a ns1.google.com. 172800 IN A 216.239.32.10 ns2.google.com. 172800 IN A 216.239.34.10 ns3.google.com. 172800 IN A 216.239.36.10 ns4.google.com. 172800 IN A 216.239.38.10
Na última parte da saída encontramos mais quatro informações:
;; Query time: 10 msec ;; SERVER: ::1#53(localhost) (UDP) ;; WHEN: Tue Jul 14 15:30:29 -03 2026 ;; MSG SIZE rcvd: 83
- QUERY TIME: informa quanto tempo a consulta levou. Um resultado de 0 ms significa que ela foi concluída em menos de 1 ms, geralmente por ter sido respondida localmente ou pelo cache.
- SERVER: Qual servidor ele realizou a consulta
- WHEN: data e hora em que a consulta foi realizada. O horário exibido é o do próprio servidor, e o valor -03 indica o fuso horário (UTC-3, horário de Brasília).
- MSG SIZE: tamanho do pacote
Leitura recomendada: https://bind9.readthedocs.io/en/stable/reference.html
Comandos do dia a dia
Para verificar se seu arquivo tem algum erro use o comando named-checkconf (se não retornar nada, está tudo certo):
# named-checkconf /etc/bind/named.conf # named-checkconf /etc/bind/named.conf.options
Imprime todas as configurações (bem útil para mandar suas conf quando estiver com algum problema lá no grupo do Telegram):
# named-checkconf -p
Reinicie o serviço para que as novas configurações sejam carregadas, e verifique se o mesmo subiu sem erros:
# systemctl restart named # systemctl status named
Atualização dos Root Servers
Não é comum servidores raiz terem alteração, mas pode acontecer, como em 2023. Então caso um dia precise atualizá-los, faça isso:
# mv /usr/share/dns/root.hints /usr/share/dns/root.hints.`date +%Y%m%d` # wget https://www.internic.net/domain/named.root -O /usr/share/dns/root.hints
Para visualizar a diferença entre os arquivos:
# diff /usr/share/dns/root.hints.`date +%Y%m%d` /usr/share/dns/root.hints
Desativar IPv6 do bind 🤬
Se por algum motivo precisar desabilitar, faça:
# vim /etc/default/named
Altere OPTIONS="-u bind" para:
OPTIONS="-4 -u bind"
# vim /etc/bind/named.conf.options
Altere listen-on-v6 { any; }; para:
listen-on-v6 { none; };
Reinicie o serviço:
# systemctl restart named
Configurando encaminhadores DNS (forwarders)
Em alguns casos você pode estar com problema em relação aos servidores raiz, e então querer configurar forwarders.
Com essa configuração, o BIND não consulta diretamente os servidores raiz: ele encaminha todas as consultas para os servidores DNS definidos em forwarders e mantém as respostas no cache local.
# vim /etc/bind/named.conf.options
Adicione dentro do bloco options { ... };:
...
recursion yes;
forward only;
forwarders {
// Cloudflare
1.1.1.1;
1.0.0.1;
2606:4700:4700::1111;
2606:4700:4700::1001;
// Google
8.8.8.8;
8.8.4.4;
2001:4860:4860::8888;
2001:4860:4860::8844;
};
...
recursion yes; habilita a resolução recursiva, permitindo que o BIND busque a resposta completa em nome do cliente.
forward only, caso todos os encaminhadores falhem, o BIND não tentará resolver a consulta diretamente pelos servidores raiz.
Protegendo o servidor
Seu DNS recursivo já está configurado. Como este servidor será utilizado apenas para esse serviço, vamos aplicar regras no nftables para restringir o acesso às portas expostas, principalmente à porta 53.
# vim /etc/nftables.conf
Na seção elements, substitua os endereços de exemplo pelos IPs e prefixos que terão permissão de acesso. O modelo também inclui regras de proteção para o SSH e para o agente do Zabbix.
#!/usr/sbin/nft -f
flush ruleset
# Portas acessíveis somente pelas redes do NOC 22 SSH / 10050 Zabbix
define PORTAS_RESTRITAS_TCP = { 22, 10050 }
# Porta do serviço DNS
define PORTA_DNS = 53
table inet filter {
# Redes do NOC: acesso administrativo, ping e DNS
set REDE_NOC4 {
type ipv4_addr;
flags interval;
elements = { 192.168.0.0/24, 255.9.0.0/26 };
}
set REDE_NOC6 {
type ipv6_addr;
flags interval;
elements = { 2559:ffff:f0da::/48 };
}
# Redes autorizadas a utilizar o DNS
set REDE_CLIENTES4 {
type ipv4_addr;
flags interval;
elements = {
10.0.0.0/8,
172.16.0.0/12,
192.168.0.0/16,
100.64.0.0/10,
255.9.0.0/22
};
}
set REDE_CLIENTES6 {
type ipv6_addr;
flags interval;
elements = {
fc00::/7,
fe80::/10,
2559:ffff::/32
};
}
chain input {
type filter hook input priority 0;
policy drop;
# Descarta pacotes inválidos
ct state invalid counter drop
# Permite conexões já estabelecidas ou relacionadas
ct state established,related counter accept
# Permite a comunicação interna do próprio servidor
iifname "lo" counter accept
# ICMPv4 necessário para erros e PMTU
meta l4proto icmp icmp type {
destination-unreachable,
time-exceeded,
parameter-problem
} counter accept
# Ping IPv4 somente pelas redes do NOC e CLIENTES
ip saddr @REDE_NOC4 meta l4proto icmp icmp type echo-request counter accept
ip saddr @REDE_CLIENTES4 meta l4proto icmp icmp type echo-request counter accept
# ICMPv6 essencial para NDP, RA, PMTU e erros IPv6
meta l4proto ipv6-icmp icmpv6 type {
destination-unreachable,
packet-too-big,
time-exceeded,
parameter-problem,
echo-reply,
nd-router-solicit,
nd-router-advert,
nd-neighbor-solicit,
nd-neighbor-advert
} counter accept
# Ping IPv6 somente pelas redes do NOC e CLIENTES
ip6 saddr @REDE_NOC6 meta l4proto ipv6-icmp icmpv6 type echo-request counter accept
ip6 saddr @REDE_CLIENTES6 meta l4proto ipv6-icmp icmpv6 type echo-request counter accept
# SSH e Zabbix Agent somente pelas redes do NOC
ip saddr @REDE_NOC4 tcp dport $PORTAS_RESTRITAS_TCP counter accept
ip6 saddr @REDE_NOC6 tcp dport $PORTAS_RESTRITAS_TCP counter accept
# DNS TCP e UDP somente para as redes autorizadas
ip saddr @REDE_CLIENTES4 meta l4proto { tcp, udp } th dport $PORTA_DNS counter accept
ip6 saddr @REDE_CLIENTES6 meta l4proto { tcp, udp } th dport $PORTA_DNS counter accept
}
chain forward {
type filter hook forward priority 0;
policy drop;
}
chain output {
type filter hook output priority 0;
policy accept;
}
}
Teste:
# nft -c -f /etc/nftables.conf
Se nenhum erro for exibido, reinicie o serviço para aplicar as regras.
Ative o nftables para iniciar automaticamente junto com o sistema e reinicie o serviço para aplicar as regras:
# systemctl enable nftables # systemctl restart nftables
Atenção: antes de reiniciar o firewall, tenha acesso ao console do servidor. Se perder o acesso, alguma regra foi configurada incorretamente.
Para verificar se as regras foram carregadas corretamente, execute:
# nft list ruleset

