Servidor DNS Unbound com Anablock

Apresento a vocês um tutorial simples de como criar um servidor DNS recursivo (usado para cache DNS de navegação local e navegação de usuários).

Leitura de outras dicas recomendadas:

  • Criação da maquina virtual VMware com ajustes finos;
  • Instalação do Debian 12 netinstall x86_64 (amd64);
  • Instalação de programas básicos;
  • Data/hora via NTP;
  • Ajuste fino no kernel Linux;

Preparativos

Seu servidor Debian precisa ter Internet funcionando e um DNS para servir o Debian durante as instalações, só depois disso você estará com seu DNS independente.

Opção 1 – Definir manualmente o DNS no sistema

Bash
# Conferir se tem internet:
ping -c 4 9.9.9.9;
ping -c 4 8.8.8.8;
ping -c 4 4.2.2.2;
ping -c 4 1.1.1.1;

# Escolha o ip acima que respondeu com menor ping para ser o DNS primario,
# e outro IP para ser o DNS secundário. Configure seu ambiente atual para usá-lo.
# Configurar o Linux para usar DNS externo:
(
    echo 'nameserver 8.8.8.8';
    echo 'nameserver 9.9.9.9';
) > /etc/resolv.conf;

# Testando:
ping -c 4 google.com;
ping -c 4 registro.br;

Opção 2 – Definir manualmente usando resolvconf

O método mais adequado é deixar o resolv.conf ser definido pela configuração de rede e informar um DNS manual que precede o DNS padrão do NetworkManager.

Criando o arquivo /etc/resolvconf/resolv.conf.d/head você garante que seu DNS preferencial seja usado antes do DNS definido pela configuração de rede.

Bash
# Instalar resolvconf:
apt-get -y install resolvconf;

# Criando o arquivo "head" que será usado antes
# do "dns-nameservers" do NetworkManager
mkdir -p /etc/resolvconf/resolv.conf.d;
(
    echo 'nameserver 1.1.1.1';
    echo 'nameserver 4.2.2.2';
) > /etc/resolvconf/resolv.conf.d/head;

# Atualizar resolvconf:
resolvconf -u;

Instalando Unbound

Os procedimentos abaixo foram feitos e pensados para uma nova instalação limpa, não recomendo que você as execute em um servidor que já esteja rodando o Unbound.

Para garantir sucesso nas instalações, atualize o Debian:

Bash
apt -y update;
apt -y upgrade;
apt -y dist-upgrade;

Agora instale os programas necessários para o ambiente Unbound:

Bash
# Instalar Unbound:
apt-get -y install unbound;
apt-get -y install unbound-anchor;
apt-get -y install dnsutils;

# Correcao de sobrevivencia do processo
# Restart=on-failure para Restart=always
sed -i 's#Restart=on-failure#Restart=always#' /lib/systemd/system/unbound.service;
systemctl daemon-reload;
    
# Ativar no systemd para subir durante o boot:
systemctl enable unbound;

Precisamos obter a lista de root-servers atualizadas, os root-servers são servidores espalhados globalmente e possuem os dominos TLDN (.br, .com, .net, .io, .arpa., etc…) e possuem a zona raiz apontando para todos os servidores de segundo nível do mundo (.br aponta para registro.br no Brasil, .ar aponta para os servidores da Argentina, etc…).

Bash
# Baixar lista de root-servers atualizada:
mkdir -p /etc/unbound;
wget https://www.internic.net/domain/named.root -4 -O /etc/unbound/named.cache;

# Produzir listas em formato intercambiável para uso nos
# scripts diversos e de monitoramento
cat /etc/unbound/named.cache | egrep -v '^;'         > /tmp/nc-nocmts.txt;
cat /tmp/nc-nocmts.txt       | egrep -v NS           > /tmp/nc-ipaddr.txt;
cat /tmp/nc-ipaddr.txt       | awk '{print $1";"$4}' > /tmp/nc-table.dat;
cat /tmp/nc-table.dat        | cut -f2 -d';'         > /tmp/nc-iponly.dat;

# Arquivos finais:
cat /tmp/nc-table.dat   >  /etc/unbound/root-servers-table.dat;
cat /tmp/nc-iponly.dat  >  /etc/unbound/root-servers-ips.dat;
cat /tmp/nc-iponly.dat | egrep -v ':'  >  /etc/unbound/root-servers-ipv4.dat;
cat /tmp/nc-iponly.dat | egrep    ':'  >  /etc/unbound/root-servers-ipv6.dat;

Configurando Unbound do zero

Vamos limpar a configuração padrão e criar tudo do zero.

Bash
# Garantir config padrao no formato /etc/unbound/unbound.conf.d/
mkdir -p /etc/unbound/unbound.conf.d;
rm    -f /etc/unbound/unbound.conf.d/* 2>/dev/null;

# Recriar todas as configs do zero:
# - Config principal:
(
    echo;
    echo 'server:';
    echo '    module-config: "respip validator iterator"';
    echo;
    echo 'include-toplevel: "/etc/unbound/unbound.conf.d/*.conf"';
    echo;
) > /etc/unbound/unbound.conf;

Leia os comentários e altere de acordo com as suas necessidades, em seguida cole os blocos de código no terminal.

Bash
# - Chave pública da raiz do DNS (Root Zone Key), usado para DNSSEC:
(
    echo;
    echo 'server:';
    echo;
    echo '    auto-trust-anchor-file: "/var/lib/unbound/root.key"';
    echo;
) > /etc/unbound/unbound.conf.d/21-root-auto-trust-anchor-file.conf;

# - Gerador de estatísticas:
(
    echo 'server:';
    echo '    statistics-interval: 0';
    echo '    extended-statistics: yes';
    echo '    statistics-cumulative: no';
    echo
) > /etc/unbound/unbound.conf.d/31-statisticas.conf;

# - Protocolos - ativar todos
# - Nota: coloque "no" no do-ip6 se você não tem IPv6 no servidor
(
    echo;
    echo 'server:';
    echo;
    echo '    do-ip4: yes';
    echo '    do-ip6: yes';
    echo '    do-udp: yes';
    echo '    do-tcp: yes';
    echo;
) > /etc/unbound/unbound.conf.d/41-protocols.conf;

# - ACL de uso - IPs locais (loopback e privados) confiaveis
# - Nota: IPs privados definidos em RFC para IPv4:
#         rfc  922, rfc  919, rfc 1112, rfc 1122,
#         rfc 1918, rfc 2544, rfc 3068, rfc 3171,
#         rfc 3330, rfc 3927, rfc 5735, rfc 5736,
#         rfc 5737, rfc 6598,
#
# - Nota: IPs privados/não-globais definidos em RFC para IPv6:
#         rfc 3849, rfc 9637
#
(
    echo;
    echo 'server:';
    echo;
    echo '    # Loopback';
    echo '    access-control: 127.0.0.1/32        allow';
    echo '    access-control: ::1/128             allow';
    echo;
    echo '    # Todas as faixas privadas RFCs';
    echo '    access-control: 0.0.0.0/8           allow';
    echo '    access-control: 10.0.0.0/8          allow';
    echo '    access-control: 100.64.0.0/10       allow';
    echo '    access-control: 127.0.0.0/8         allow';
    echo '    access-control: 169.254.0.0/16      allow';
    echo '    access-control: 172.16.0.0/12       allow';
    echo '    access-control: 192.0.0.0/24        allow';
    echo '    access-control: 192.0.2.0/24        allow';
    echo '    access-control: 192.88.99.0/24      allow';
    echo '    access-control: 192.168.0.0/16      allow';
    echo '    access-control: 198.18.0.0/15       allow';
    echo '    access-control: 198.51.100.0/24     allow';
    echo '    access-control: 203.0.113.0/24      allow';
    echo '    access-control: 224.0.0.0/4         allow';
    echo '    access-control: 240.0.0.0/4         allow';
    echo '    access-control: 255.255.255.255/32  allow';
    echo;
    echo '    # Faixas IPv6 privadas';
    echo '    access-control: 2001:db8::/32       allow';
    echo;
    echo '    # Faixas IPv6 nao-globais';
    echo '    access-control: ::/3                allow';
    echo '    access-control: 4000::/2            allow';
    echo '    access-control: 8000::/1            allow';
    echo
) > /etc/unbound/unbound.conf.d/51-acls-locals.conf;

# - ACL de uso - IPs locais públicos (IPv4) e globais (IPv6)
# - Nota: Coloque seus prefixos de IPs publicos,
#         prefixos de IPs delegados pelo seu provedor,
#         prefixos de IPs do seu ASN,
#         informe IPv4 e IPv6, mesmo se não usar o IPv6
#
(
    echo;
    echo 'server:';
    echo;
    echo '    # IPs publicos (IPv4) delegados e proprios:';
    echo '    access-control: 45.255.128.0/29     allow';
    echo;
    echo '    # IPs globais (IPv6) delegados e proprios:';
    echo '    access-control: 2804:cafe::/126     allow';
    echo;
) > /etc/unbound/unbound.conf.d/52-acls-trusteds.conf;

# - ACL padrao - descartar tudo de IPs nao confiaveis
# - Nota: nao use "refuse" como padrão pois ele gera resposta
#         e pode fazer seu DNS ser explorado em DDoS
#         usar "deny" pois ele descarta silenciosamente
#         os pacotes de IPs desconhecidos
(
    echo;
    echo 'server:';
    echo;
    echo '    # Nao responder para IPs desconhecidos';
    echo '    access-control: 0.0.0.0/0 deny';
    echo '    access-control: ::/0      deny';
    echo;
) > /etc/unbound/unbound.conf.d/59-acls-default-policy.conf;

# Parametros gerais (tuning) - padrao para 4 nucleos, 64M de Cache em RAM
(
    echo;
    echo 'server:';
    echo '    outgoing-range: 8192';
    echo '    outgoing-port-avoid: 0-1024';
    echo '    outgoing-port-permit: 1025-65535';
    echo '    num-threads: 4';
    echo '    num-queries-per-thread: 1024';
    echo '    msg-cache-size: 64m'; # Aumente a gosto, 1G, 2G, 4G...
    echo '    msg-cache-slabs: 4';
    echo '    rrset-cache-size: 8m';
    echo '    rrset-cache-slabs: 4';
    echo '    cache-min-ttl: 60';
    echo '    cache-max-ttl: 7200';
    echo '    infra-host-ttl: 60';
    echo '    infra-lame-ttl: 120';
    echo '    infra-cache-numhosts: 10000';
    echo '    infra-cache-lame-size: 10k';
    echo '    infra-cache-slabs: 4';
    echo '    key-cache-slabs: 4';
    echo '    rrset-roundrobin: yes';
    echo;
    echo '    hide-identity: yes';
    echo '    hide-version: yes';
    echo '    harden-glue: yes';
    echo '    harden-algo-downgrade: yes';
    echo '    harden-below-nxdomain: yes';
    echo '    harden-dnssec-stripped: yes';
    echo '    harden-large-queries: yes';
    echo '    harden-referral-path: no';
    echo '    harden-short-bufsize: yes';
    echo '    do-not-query-address: 127.0.0.1/8';
    echo '    do-not-query-localhost: yes';
    echo '    edns-buffer-size: 1472';
    echo '    aggressive-nsec: yes';
    echo '    delay-close: 10000';
    echo '    neg-cache-size: 4M';
    echo '    qname-minimisation: yes';
    echo '    deny-any: yes';
    echo '    ratelimit: 1000';
    echo '    unwanted-reply-threshold: 10000';
    echo '    use-caps-for-id: yes';
    echo '    val-clean-additional: yes';
    echo '    minimal-responses: yes';
    echo '    prefetch: yes';
    echo '    prefetch-key: yes';
    echo '    serve-expired: yes';
    echo '    so-reuseport: yes';
    echo;
) > /etc/unbound/unbound.conf.d/61-configs.conf;


# Informar em quais enderecos IP locais o unbound deve abrir
# a porta 53/tcp e 53/udp para escuta de requisicoes
# - Escutar sempre nos ips de loopback
(
    echo;
    echo 'server:';
    echo '    interface: 127.0.0.1';
    echo '    interface: ::1';
    echo;
) > /etc/unbound/unbound.conf.d/62-listen-loopback.conf;

# - Escutar sempre nos ips externos
#   * usar 0.0.0.0 não é uma boa ideia, embora funcione
#     essa config causa assincronia no ip de resposta
#     nos casos em que o servidor tem mais de um IP
#   * correto: crie uma linha 'interface:' para cada IP
#     publico ou de loopback
(
    echo;
    echo 'server:';
    echo '    interface: 0.0.0.0';
    echo '    interface: ::';
    echo;
) > /etc/unbound/unbound.conf.d/63-listen-interfaces.conf;


# - Hyperlocal Cache (opcional)
# -- Requer lista de root-servers do inicio do tutorial!
if [ -f /etc/unbound/root-servers-ips.dat ]; then
    (
        echo;
        echo 'server:';
        echo '    auth-zone:';
        echo '        name: "."';
        for addr in $(cat /etc/unbound/root-servers-ips.dat); do
            echo "            master: $addr";
        done;
        echo '        fallback-enabled: yes';
        echo '        for-downstream: no';
        echo '        for-upstream: yes';
        echo '        zonefile: ""';
        echo;
    ) > /etc/unbound/unbound.conf.d/89-hyperlocal-cache.conf;
fi

# - Controle remoto local do processo:
# - Nota: somente localhost, por isso nao precisa de certificado
(
    echo;
    echo 'remote-control:';
    echo '    control-enable: yes';
    echo '    control-interface: 127.0.0.1';
    echo '    control-port: 953';
    echo '    control-use-cert: "no"';
    echo '    control-interface: /run/unbound.ctl';
    echo;
) > /etc/unbound/unbound.conf.d/99-remote-control.conf;

Chave – Trust Anchors

O arquivo /var/lib/unbound/root.key contém as trust anchors (âncoras de confiança) do DNSSEC para a zona raiz do DNS. É essencial para a validação de assinaturas DNSSEC.

Nota sobre DNSSEC: segurança é o foco, impedir que os domínios sejam redirecionados por provedores e hackers e enviar o usuário para um servidor falso. O defeito é que alguns administradores de sites cometem erros e eles mesmos tornam o site oficial indisponível (erro de chave local com a chave oficial no register do site). As vezes é melhor desativar o DNSSEC para o site voltar a abrir do que esperar horas ou dias para que o dono do site arrume a própria cagada!

Bash
# Diretorio base da chave:
mkdir -p /var/lib/unbound;

# Executar o unbound-anchor para baixar a chave raiz
unbound-anchor     -a /var/lib/unbound/root.key;
# Link de referencia oficial em XML:
#    https://data.iana.org/root-anchors/root-anchors.xml

# Ajustar as permissões
chown unbound:unbound /var/lib/unbound/root.key;
chmod 644             /var/lib/unbound/root.key;

# Conferindo:
cat /var/lib/unbound/root.key;
    # ; autotrust trust anchor file
    # ;;id: . 1
    # ;;last_queried: 1762801646 ;;Mon Nov 10 16:07:26 2025
    # ;;last_success: 1762801646 ;;Mon Nov 10 16:07:26 2025
    # ;;next_probe_time: 1762843851 ;;Tue Nov 11 03:50:51 2025
    # ;;query_failed: 0
    # ;;query_interval: 43200
    # ;;retry_time: 8640
    # .	86400	IN	DNSKEY	257 3 8 
    #    AwEAAa96jeuknZlaeSrvyAJj6ZHv28hhOKkx3rLGXVaC6rXTsDc449/cidltpkyGwCJNn
    #    OAlFNKF2jBosZBU5eeHspaQWOmOElZsjICMQMC3aeHbGiShvZsx4wMYSjH8e7Vrhbu6ir
    #    wCzVBApESjbUdpWWmEnhathWu1jo+siFUiRAAxm9qyJNg/wOZqqzL/dL/q8PkcRU5oUKE
    #    pUge71M3ej2/7CPqpdVwuMoTvoB+ZOT4YeGyxMvHmbrxlFzGOHOijtzN+u1TQNatX2XBu
    #    zZNQ1K+s2CXkPIZo7s6JgZyvaBevYtxPvYLw4z9mR7K2vaF18UYH9Z9GNUUeayffKC73PYc= ;
    #    {id = 38696 (ksk), size = 2048b} ;;state=2 [  VALID  ] ;;count=0 
    #    ;;lastchange=1762801646 ;;Mon Nov 10 16:07:26 2025
    #    .	86400	IN	DNSKEY	257 3 8 
    #    AwEAAaz/tAm8yTn4Mfeh5eyI96WSVexTBAvkMgJzkKTOiW1vkIbzxeF3+/4RgWOq7HrxR
    #    ixHlFlExOLAJr5emLvN7SWXgnLh4+B5xQlNVz8Og8kvArMtNROxVQuCaSnIDdD5LKyWbR
    #    d2n9WGe2R8PzgCmr3EgVLrjyBxWezF0jLHwVN8efS3rCj/EWgvIWgb9tarpVUDK/b58Da
    #    +sqqls3eNbuv7pr+eoZG+SrDK6nWeL3c6H5Apxz7LjVc1uTIdsIXxuOLYA4/ilBmSVIzu
    #    DWfdRUfhHdY6+cn8HFRm+2hM8AnXGXws9555KrUB5qihylGa8subX2Nn6UwNR1AkUTV74bU= ;
    #    {id = 20326 (ksk), size = 2048b} ;;state=2 [  VALID  ] ;;count=0 
    #    ;;lastchange=1762801646 ;;Mon Nov 10 16:07:26 2025

Testando e executando

Até aqui temos a configuração de um servidor Unbound para DNS Recursivo de alta qualidade. Vamos testar antes de continuar para a fase 2.

Bash
# Conferir se a configuração está OK:
unbound-checkconf  /etc/unbound/unbound.conf;
    # precisa retornar:
    # unbound-checkconf: no errors in /etc/unbound/unbound.conf

# Rodar e testar:
service  unbound   stop;
service  unbound   start;

# Verificar status de execução:
service  unbound   status;

Homologação de DNS

Com o Unbound rodando, vamos testar se ele resolve nomes:

Bash
# Testar DNS enviando requisição para o ip de Loopback
# usando varias ferramentas de DNS:
nslookup   www.google.com   127.0.0.1;
host       www.google.com   127.0.0.1;
dig  @127.0.0.1   www.google.com;

# Caso tenha ativado o IPv6 em "do-ip6", teste tambem:
nslookup   www.google.com   ::1;
host       www.google.com   ::1;
dig  @::1  www.google.com;

# Nota: ping não é ferramenta de teste de DNS!

Caso você enfrente problemas ao resolver nomes, confira se os pacotes estão sendos respondidos, se seu servidor tem acesso a DNS na Internet, testes pontuais (ping e traceroute não são ferramentas de teste de DNS).

Testando acesso externo de DNS para homologação

Bash
# 1 - Testar usando servidores de DNS recursivo abertos
# 1.1 - Usando UDP:
dig  @1.0.0.1       www.google.com;
dig  @1.1.1.1       www.google.com;
dig  @4.2.2.2       www.google.com;
dig  @4.2.2.6       www.google.com;
dig  @8.8.4.4       www.google.com;
dig  @8.8.8.8       www.google.com;
dig  @9.9.9.9       www.google.com;

# 1.2 - Usando TCP:
dig  @1.0.0.1 +tcp  www.google.com;
dig  @1.1.1.1 +tcp  www.google.com;
dig  @4.2.2.2 +tcp  www.google.com;
dig  @4.2.2.6 +tcp  www.google.com;
dig  @8.8.4.4 +tcp  www.google.com;
dig  @8.8.8.8 +tcp  www.google.com;
dig  @9.9.9.9 +tcp  www.google.com;

# 2 - Testar acesso ao root-server a.root-server.net
dig @198.41.0.4      -t ns root-server.net;
dig @198.41.0.4 +tcp -t ns root-server.net;

Se você encontrar algum problema nos testes acima, investigue e arrume-o antes de continuar.

Alterando DNS para Loopback

Com seu próprio servidor DNS recursivo rodando localmente, oriente o sistema para usar o IP da loopback como DNS preferencial:

Bash
# Alterar DNS preferencial para loopback:
(
    echo 'nameserver 127.0.0.1';
) > /etc/resolvconf/resolv.conf.d/head;

# Atualizar resolvconf:
resolvconf -u;

Implementando AnaBlock

O AnaBlock é uma API de automação de bloqueios judiciais, essa API é hospedada no site anablock.net.br e requer cadastro para ter o acesso, acesse o site https://anablock.net.br/ para se cadastrar.

Nota: somente provedores cadastrados na Anatel (com dispensa ou outorga) devidamente notificados pela Anatel para realizar bloqueios podem ter acesso a API pois os dados retornados possuem segredo de justiça.

Após cadastrar e liberar seus IPs (IPv4 e/ou IPv6) na API do AnaBlock, teste se a API está operacional:

Bash
# Testar se a liberação foi realizada:
    # Teste de liberacao via IPv4:
    curl -4 https://anablock.net.br/acl.php;

    # Teste de liberacao via IPv6:
    curl -6 https://anablock.net.br/acl.php;

    # Forcar testar apenas IPv4:
    curl http://v4.anablock.net.br/acl.php;

    # Forcar testar apenas IPv6:
    curl http://v6.anablock.net.br/acl.php;

# Testar se a lista de domínios está sendo enviada
    mkdir /etc/anablock;
    ABLIST="/etc/anablock/anablock.domains";
    ABURL="https://api.anablock.net.br/api/domain/all";
    curl -v -o $ABLIST $ABURL;

# Verificar a contagem de domínios na lista
   cat /etc/anablock/anablock.domains | wc -l;

Se tudo deu certo até aqui, chegou a hora de colocar o Unbound para bloquear automaticamente os domínios ordenados pela justiça/Anatel.

Crie a configuração do AnaBlock no Unbound:

Bash
# Criar arquivo de cache RPZ
mkdir -p /var/lib/unbound;
touch    /var/lib/unbound/anablock-rpz.zone;
chown unbound:unbound /var/lib/unbound/anablock-rpz.zone;

# Criar entrada na config do Unbound:
(
    echo;
    echo 'server:';
    echo '    rpz:';
    echo '        name: "anablock"';
    echo '        zonefile: "/var/lib/unbound/anablock-rpz.zone"';
    echo;
    echo 'auth-zone:';
    echo '    name: "anablock"';
    echo '    url: "https://api.anablock.net.br/domains/rpz"';
    echo '    for-downstream: no';
    echo '    for-upstream: yes';
    echo '    fallback-enabled: no';
    echo '    zonefile: "/var/lib/unbound/anablock-rpz.zone"';
    echo;
) > /etc/unbound/unbound.conf.d/91-anablock.conf;

Testar se a nova configuração entrou harmonicamente no Unbound:

Bash
# Conferir se a configuração está OK:
unbound-checkconf  /etc/unbound/unbound.conf;
    # precisa retornar:
    # unbound-checkconf: no errors in /etc/unbound/unbound.conf

# Reiniciar, escolha uma das ocoes:
    # 1 - Reiniciar sem perder o cache de DNS:
    unbound-control reload_keep_cache;

    # 2 - Reiniciar unbound completamente e rapido (limpa o cache):
    service unbound restart;

    # 3 - Parar totalmente, esperar e iniciar do zero:
    service  unbound   stop;
    service  unbound   start;

# Verificar status de execução:
service  unbound   status;

Testar bloqueios do AnaBlock

A API do AnaBlock fornece um nome de teste: blocktest.anablock.net.br

Se este nome for resolvido, o AnaBlock não está em operação, mas se apresentar erro NXDOMAIN (domínio não existe), o bloqueio foi implementado com sucesso.

Bash
# Teste de bloqueio:
host blocktest.anablock.net.br  127.0.0.1;
    Using domain server:
    Name: 127.0.0.1
    Address: 127.0.0.1#53
    Aliases: 

    Host blocktest.anablock.net.br not found: 3(NXDOMAIN)

Com isso temos nosso bloqueio judicial 100% em conformidade com o jurídico da empresa via AnaBlock e um DNS de altíssima velocidade com Unbound.

Um abraço a todos,
Patrick Brandão, patrickbrandao@gmail.com