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

Rudimar Remontti

Trabalho atualmente como Gerente de Redes em um Provedor de Internet no Rio Grande do Sul.

Você pode gostar...

Deixe um comentário

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