Dia 16: DNS Server no Linux — Bind9, unbound e Zonas

ℹ️ Este artigo faz parte do Curso de Linux em 30 Dias. Se ainda não leu o Dia 8 sobre Redes Linux, comece por aí — os fundamentos de DNS e /etc/hosts são essenciais para este artigo.

O DNS (Domain Name System) é o sistema que traduz nomes de máquina em endereços IP e vice-versa. Em Linux, os dois servidores DNS mais utilizados são o BIND9 (servidor autoritativo e recursivo, mantido pela ISC) e o unbound (resolver recursivo e caching, mantido pela NLnet Labs). Este artigo mostra como instalar e configurar ambos, criar zonas forward e reverse, e diagnosticar problemas comuns — numa abordagem que serve tanto Ubuntu/Debian como RHEL/Rocky/Alma.

Pré-requisitos: Terminal básico (Dia 1), rede configurada (Dia 8), e systemd (Dia 7). Precisa de privilégios sudo para instalar pacotes e editar configuração.

Neste artigo

Como Funciona o DNS no Linux

O DNS opera em camadas. Quando uma aplicação precisa de resolver servidor.exemplo.pt, o resolvedor local (geralmente a libc via /etc/resolv.conf) envia a query ao servidor DNS configurado. Esse servidor pode agir como resolver recursivo (pergunta aos root servers, depois aos TLD servers, depois ao servidor autoritativo) ou como servidor autoritativo (responde diretamente porque é o responsável pela zona).

A hierarquia DNS começa nos 13 root servers (letras A a M), desce para os TLDs (.pt, .com), e chega aos servidores autoritativos de cada domínio. Cada nível delega autoridade para o nível inferior através de registos NS (Name Server). A especificação original do DNS está nos RFC 1034 (conceitos) e RFC 1035 (implementação e sintaxe).

Em Linux, o fluxo típico é: aplicação → libc resolver → /etc/resolv.conf → servidor DNS (Bind9 ou unbound) → Internet. O /etc/hosts tem prioridade sobre o DNS para nomes locais — o sistema consulta-o primeiro antes de qualquer query DNS, controlado pela diretiva files em /etc/nsswitch.conf.

Componentes de um DNS Server

Antes de instalar, é importante perceber a diferença entre os três papéis que um servidor DNS pode desempenhar. Um servidor pode ter um ou vários destes papéis em simultâneo, embora em produção seja boa prática separá-los.

Papel Função Software típico
Autoritativo Responde definitivamente para as zonas que domina (ex: exemplo.pt) BIND9, NSD, PowerDNS
Recursivo / Caching Resolve nomes para os clientes, guarda respostas em cache para acelerar queries futuras unbound, BIND9, dnsmasq
Forwarder Recebe queries dos clientes e reencaminha-as para outro resolver (ex: 8.8.8.8, 1.1.1.1) unbound, BIND9, dnsmasq

Neste artigo configuramos o BIND9 como servidor autoritativo (com zonas forward e reverse) e o unbound como caching resolver. Esta separação é o padrão recomendado para redes de PME — o BIND9 responde pelos domínios internos, o unbound faz cache das queries externas para acelerar a navegação.

Instalar Bind9 e unbound

A instalação varia conforme a distribuição. Em Ubuntu/Debian o pacote chama-se bind9, em RHEL/Rocky/Alma chama-se bind. O unbound tem o mesmo nome em ambas as famílias.

Ubuntu / Debian:

sudo apt update
sudo apt install bind9 bind9utils bind9-doc dnsutils unbound

RHEL / Rocky / Alma:

sudo dnf install bind bind-utils unbound

O pacote dnsutils (Ubuntu) ou bind-utils (RHEL) fornece as ferramentas dig, nslookup e host, essenciais para testar a configuração. Depois de instalar, confirmar que o serviço está ativo:

sudo systemctl status named
sudo systemctl status unbound

No Ubuntu/Debian o serviço chama-se bind9, no RHEL chama-se named. Ambos correspondem ao mesmo binário (/usr/sbin/named), apenas o nome do serviço systemd difere.

Configurar Bind9 — named.conf

A configuração do BIND9 está no ficheiro named.conf. Em Ubuntu/Debian, este ficheiro está em /etc/bind/named.conf e inclui outros três: named.conf.options (opções globais), named.conf.local (zonas locais) e named.conf.default-zones (zonas pré-definidas). Em RHEL/Rocky, tudo fica num único /etc/named.conf. A documentação de referência está em BIND9 Reference (ISC).

Passo 1 — Configurar as opções globais (named.conf.options em Ubuntu/Debian ou secção options {} em RHEL):

options {
    directory "/var/cache/bind";

    // Rede interna autorizada a fazer queries recursivas
    allow-query { localhost; 192.168.1.0/24; };

    // Rede autorizada a fazer transferencia de zona (AXFR)
    allow-transfer { 192.168.1.20; };

    // Nao permitir recursao para redes externas
    recursion no;

    // DNSSEC validation
    dnssec-validation auto;

    listen-on-v6 { none; };
};

Parâmetros principais:

  • allow-query — define quais redes podem enviar queries ao servidor. Restringir a localhost e LAN evita open resolvers, que são explorados em ataques DDoS de amplificação.
  • allow-transfer — controla quais servidores podem fazer transferência de zona (AXFR). Apenas o servidor secundário deve estar aqui. Nunca deixar aberto — expõe toda a zona.
  • recursion no — desliga a recursão no BIND9 autoritativo. A recursão fica no unbound, que é mais eficiente para isso.
  • dnssec-validation auto — valida respostas DNSSEC usando trust anchors geridos pelo BIND. Recomendado para ambientes com Internet.

Passo 2 — Declarar as zonas em named.conf.local (Ubuntu/Debian) ou no final de /etc/named.conf (RHEL):

zone "exemplo.pt" {
    type master;
    file "/etc/bind/zones/db.exemplo.pt";
};

zone "1.168.192.in-addr.arpa" {
    type master;
    file "/etc/bind/zones/db.192.168.1";
};

A primeira zona é a forward (resolução nome → IP), a segunda é a reverse (IP → nome). O nome da zona reverse é o endereço de rede invertido com sufixo in-addr.arpa — para a rede 192.168.1.0/24, fica 1.168.192.in-addr.arpa.

Zona Forward — Resolução de Nomes

A zona forward contém os registos que mapeiam nomes em IPs. O ficheiro de zona usa o formato definido no RFC 1035 — cada linha é um registo DNS com tipo específico (A, CNAME, MX, TXT, etc.).

Criar o diretório e o ficheiro de zona forward:

sudo mkdir -p /etc/bind/zones
sudo nano /etc/bind/zones/db.exemplo.pt

Conteúdo do ficheiro de zona forward:

$TTL    86400
@       IN  SOA ns1.exemplo.pt. admin.exemplo.pt. (
            2026072401 ; Serial (AAAA-MM-DD-NN)
            3600       ; Refresh (1h)
            1800       ; Retry (30min)
            604800     ; Expire (7 dias)
            86400 )    ; Minimum TTL (24h)

; Servidores de nomes
@       IN  NS      ns1.exemplo.pt.
@       IN  NS      ns2.exemplo.pt.

; Registos A (nome -> IPv4)
ns1     IN  A       192.168.1.10
ns2     IN  A       192.168.1.11
www     IN  A       192.168.1.20
mail    IN  A       192.168.1.30
app     IN  A       192.168.1.40

; Registos CNAME (alias)
blog    IN  CNAME   www.exemplo.pt.

; Registo MX (mail exchange)
@       IN  MX  10  mail.exemplo.pt.

; Registo TXT (SPF)
@       IN  TXT     "v=spf1 mx -all"

Tipos de registo nesta zona:

  • SOA (Start of Authority) — define o servidor primário, email do administrador (com ponto em vez de @), número de série e temporizadores de sincronização. O serial deve incrementar a cada alteração — convenção YYYYMMDDNN.
  • NS — lista os servidores autoritativos para a zona. Cada servidor NS precisa de um registo A correspondente.
  • A — mapeia um nome num endereço IPv4. Para IPv6 usa-se AAAA.
  • CNAME — cria um alias (apelido) que aponta para outro nome. Útil quando vários nomes servem o mesmo host.
  • MX — indica qual servidor recebe email para o domínio, com prioridade (número menor = prioridade maior).
  • TXT — texto arbitrário, usado para SPF, DKIM, verificação de domínio, etc.
⚠️ Atenção: O ponto final no nome do servidor (ns1.exemplo.pt.) é obrigatório em registos fully qualified. Sem o ponto, o BIND9 anexa o nome da zona e fica ns1.exemplo.pt.exemplo.pt. — um erro silencioso que causa falhas de resolução.

Zona Reverse — PTR e IP para Nome

A zona reverse faz o caminho inverso: dado um IP, devolve o nome associado. Isto é essencial para serviços de email (muitos servidores SMTP rejeitam correio de IPs sem registo PTR válido), logging legível e ferramentas de diagnóstico. A zona é denominada com o endereço de rede invertido e o sufixo in-addr.arpa.

sudo nano /etc/bind/zones/db.192.168.1

Conteúdo do ficheiro de zona reverse:

$TTL    86400
@       IN  SOA ns1.exemplo.pt. admin.exemplo.pt. (
            2026072401 ; Serial
            3600       ; Refresh
            1800       ; Retry
            604800     ; Expire
            86400 )    ; Minimum TTL

; Servidores de nomes
@       IN  NS  ns1.exemplo.pt.
@       IN  NS  ns2.exemplo.pt.

; Registos PTR (IP -> nome)
10      IN  PTR ns1.exemplo.pt.
11      IN  PTR ns2.exemplo.pt.
20      IN  PTR www.exemplo.pt.
30      IN  PTR mail.exemplo.pt.
40      IN  PTR app.exemplo.pt.

No registo PTR, o número à esquerda é o último octeto do endereço IP. Para o IP 192.168.1.20, na zona 1.168.192.in-addr.arpa, escreve-se apenas 20 — o BIND9 completa com o prefixo da zona. Para redes /16 ou /8, a lógica é a mesma: inverter os octetos correspondentes ao tamanho da máscara.

Depois de criar os ficheiros de zona, ajustar as permissões para o BIND9 conseguir ler:

sudo chown -R bind:bind /etc/bind/zones
sudo chmod 644 /etc/bind/zones/db.*

Em RHEL/Rocky o utilizador do BIND9 chama-se named em vez de bind: sudo chown -R named:named /etc/named/zones.

Configurar unbound como Caching Resolver

O unbound é um resolver recursivo moderno com caching agressivo, DNSSEC por defeito e boa performance. É ideal como DNS interno para os clientes da LAN — faz cache das queries externas e reencaminha as queries internas para o BIND9 autoritativo. A documentação de referência está em NLnet Labs — unbound.conf.

O ficheiro principal fica em /etc/unbound/unbound.conf (Ubuntu/Debian e RHEL). Editar:

server:
    # Interface onde o unbound escuta
    interface: 192.168.1.10
    interface: 127.0.0.1

    # Porta 53 (predefinida)
    port: 53

    # Redes autorizadas a usar o resolver
    access-control: 127.0.0.0/8 allow
    access-control: 192.168.1.0/24 allow

    # DNSSEC validation
    auto-trust-anchor-file: "/var/lib/unbound/root.key"

    # Esconder versao
    hide-version: yes

    # Esconder identidade
    hide-identity: yes

    # Nao responder por zonas onde o BIND9 e autoritativo
    local-zone: "exemplo.pt." redirect
    local-data: "exemplo.pt. 3600 IN NS ns1.exemplo.pt."
    stub-zone:
        name: "exemplo.pt."
        stub-addr: 192.168.1.10@53

    # Forwarders para queries externas
    forward-zone:
        name: "."
        forward-addr: 1.1.1.1
        forward-addr: 8.8.8.8

Parâmetros principais do unbound:

  • interface — endereço IP onde o unbound escuta. Se o BIND9 já está na porta 53 no mesmo IP, o unbound tem de escutar noutro IP ou noutro porto. Numa configuração típica, o BIND9 corre em 192.168.1.10:53 e o unbound em 127.0.0.1:53 ou noutro servidor.
  • access-control — lista de controlo de acesso. Apenas as redes listadas podem usar o resolver. Restringir para evitar open resolvers.
  • stub-zone — reencaminha queries para exemplo.pt ao BIND9 autoritativo. Isto une o resolver e o autoritativo sem precisar de dois servidores distintos.
  • forward-zone — envia todas as outras queries para um resolver público (1.1.1.1, 8.8.8.8). O unbound faz cache das respostas, reduzindo latência.
  • auto-trust-anchor-file — caminho para o trust anchor DNSSEC. O unbound valida assinaturas DNSSEC automaticamente.

Antes de iniciar o unbound, gerar o root trust anchor para DNSSEC:

sudo unbound-anchor -a /var/lib/unbound/root.key
sudo unbound-checkconf
sudo systemctl enable --now unbound

O unbound-checkconf valida a sintaxe do ficheiro antes de arrancar — se houver erro, indica a linha exata.

Testar e Verificar com dig e named-checkconf

Depois de configurar, é preciso verificar a sintaxe e testar a resolução. O BIND9 inclui ferramentas de validação próprias — named-checkconf valida o ficheiro de configuração e named-checkzone valida cada ficheiro de zona. Referência em BIND9 Administration Tools.

Passo 1 — Validar a configuração do BIND9:

sudo named-checkconf /etc/bind/named.conf
sudo named-checkzone exemplo.pt /etc/bind/zones/db.exemplo.pt
sudo named-checkzone 1.168.192.in-addr.arpa /etc/bind/zones/db.192.168.1

Se não houver erros, o output é zone exemplo.pt/IN: loaded serial 2026072401 OK. Se houver, indica a linha e o problema.

Passo 2 — Reiniciar o BIND9 e testar com dig:

sudo systemctl restart bind9   # Ubuntu/Debian
sudo systemctl restart named    # RHEL/Rocky

# Testar resolucao forward
dig @192.168.1.10 www.exemplo.pt

# Testar resolucao reverse
dig @192.168.1.10 -x 192.168.1.20

# Testar registo MX
dig @192.168.1.10 exemplo.pt MX

# Verificar com nslookup
nslookup app.exemplo.pt 192.168.1.10

Output esperado para dig @192.168.1.10 www.exemplo.pt:

;; ANSWER SECTION:
www.exemplo.pt.   86400  IN  A   192.168.1.20

;; Query time: 0 msec
;; SERVER: 192.168.1.10#53(192.168.1.10)

A presença da secção ANSWER SECTION com o registo A e o IP correto confirma que a zona forward está a funcionar. Para a zona reverse, dig -x 192.168.1.20 deve devolver 20.1.168.192.in-addr.arpa. IN PTR www.exemplo.pt..

Passo 3 — Testar o unbound:

# Query interna (deve resolver via stub-zone para BIND9)
dig @127.0.0.1 www.exemplo.pt

# Query externa (deve resolver via forwarders)
dig @127.0.0.1 kbase.pt

# Verificar DNSSEC
dig @127.0.0.1 dnssec.works +dnssec

A flag +dnssec faz o dig pedir registos DNSSEC. Se o unbound estiver a validar corretamente, a resposta inclui a flag ad (Authentic Data) no cabeçalho.

Passo 4 — Configurar os clientes para usar o novo DNS:

# Ubuntu/Debian com netplan
sudo nano /etc/netplan/01-netcfg.yaml
# Adicionar sob o interface desejado:
#   nameservers:
#     addresses: [192.168.1.10, 192.168.1.11]
sudo netplan apply

# RHEL/Rocky com nmcli
sudo nmcli connection modify eth0 ipv4.dns "192.168.1.10 192.168.1.11"
sudo nmcli connection up eth0

# Verificar resolver ativo
cat /etc/resolv.conf

Erros Comuns e Soluções

Durante a configuração de um DNS Server em Linux, alguns problemas aparecem com frequência. A tabela seguinte reúne os mais comuns e como resolver.

Problema Causa Solução
named-checkzone reporta “missing final dot” Nome fully qualified sem ponto final (ex: ns1.exemplo.pt em vez de ns1.exemplo.pt.) Acrescentar o ponto final em todos os nomes fully qualified nos registos SOA, NS, MX e CNAME
Serial não incrementa após alteração O serial do SOA não foi alterado — o servidor secundário não detecta mudança Incrementar o serial com convenção YYYYMMDDNN e reiniciar o BIND9
dig devolve SERVFAIL Erro de sintaxe no ficheiro de zona ou permissões incorretas Correr named-checkconf e named-checkzone; verificar /var/log/syslog ou journalctl -u bind9
Conflito de porta 53 entre BIND9 e unbound Ambos tentam escutar na porta 53 do mesmo IP Configurar BIND9 em 192.168.1.10:53 e unbound em 127.0.0.1:53 ou noutro IP; ou desativar recursão no BIND9 e deixar só o unbound
Resolução externa lenta ou falha no unbound Forwarders indisponíveis ou DNSSEC a falhar (relógio desactualizado) Verificar forwarders com dig @1.1.1.1 kbase.pt; sincronizar relógio com NTP; validar root.key com unbound-anchor
Registo PTR não resolve Zona reverse não declarada no named.conf ou octeto errado Confirmar que a zona reverse está em named.conf.local e que o octeto no PTR corresponde ao último octeto do IP
systemd-resolved interfere na porta 53 Ubuntu/Debian tem systemd-resolved a ouvir na 127.0.0.53:53 Desativar: sudo systemctl disable --now systemd-resolved e remover /etc/resolv.conf simbólico

Checklist de Configuração

Antes de considerar o DNS Server pronto para produção, verificar todos os itens seguintes. Esta checklist funciona tanto para um laboratório como para uma rede corporativa interna.

Checklist:

  1. Confirmar que named-checkconf passa sem erros
  2. Confirmar que named-checkzone passa para a zona forward e reverse
  3. Verificar que todos os registos fully qualified terminam com ponto final
  4. Confirmar que o serial do SOA foi incrementado após cada alteração
  5. Testar resolução forward com dig @server nome A
  6. Testar resolução reverse com dig @server -x IP
  7. Verificar allow-query restrito à LAN (não open resolver)
  8. Verificar allow-transfer restrito ao servidor secundário
  9. Confirmar que unbound-checkconf passa sem erros
  10. Testar DNSSEC com dig @127.0.0.1 dnssec.works +dnssec (flag ad presente)
  11. Configurar os clientes para usar o novo DNS em /etc/resolv.conf ou via netplan/nmcli
  12. Abrir porta 53 (TCP e UDP) na firewall — ver Dia 9: Firewalls Linux

Artigos Relacionados