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;

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.

Bash

# Instalar Unbound:
apt-get -y install unbound;

# Instalar ferramentas de DNS:
apt-get -y install dnsutils;

# Ativar no systemd para subir durante o boot:
systemctl enable unbound;

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

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 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';
    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;

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.

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:
    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