Rodando servidor proxy-cache DNS com Unbound


Autor: Patrick Brandão

Motivação: ao rodar qualquer software no Linux, a resolução de DNS e feita pela libresolv, que faz a leitura do /etc/host.conf e /etc/resolv.conf para obter o IP do servidor DNS.

Assim, a resolução de nomes requer envio de pacotes para fora do host e aguarda pela resposta, que pode demorar (latência alta) ou não ser respondida por problemas na rede entre o host e o servidor DNS (remoto)

A libresolv não possui suporte a cache, ou seja, se você precisar consultar um domínio (www.google.com) dezenas de vezes em um segundo, todas as vezes ela terá que enviar a pergunta ao servidor DNS para obter a mesma resposta.

Rodando um servidor proxy-cache DNS no host linux você garante que a libresolv envie os pedidos para um software rodando no mesmo host mas que ficará por conta de consultar 1 ou mais servidores, e salvando os resultados obtidos em cache, economizando banda e tempo nas próximas requisições aos mesmos nomes.

O resultado dessa implementação é uma maior velocidade no inicio das conexões do servidor.

OBS:
Você não está criando um servidor recursivo, pare resoluções de nomes para sua rede!
Mas ao final Rudimar Remontti vai explicar como tornar ele recursivo.

Distribuição utilizada:
Debian 11 (Bullseye) 64 bits instalação mínima

Instalando Unbound

# apt update; apt upgrade
# apt install unbound

Configurando
A configuração abaixo visa consumir recursos mínimos e atender somente os softwares do próprio host.

# cat > /etc/unbound/unbound.conf.d/root-auto-trust-anchor-file.conf << EOF
server:
    auto-trust-anchor-file: "/var/lib/unbound/root.key"
    verbosity: 1
    statistics-interval: 20
    extended-statistics: yes
    num-threads: 2

    # Caso seja necessário fixar o IP de origem:
    #outgoing-interface: x.x.x.x

    # Abrir a porta apenas nos endereços loopback (!!segurança!!)
    interface: 127.0.0.1
    interface: ::1

    # Permitir todos os IPs pois abrimos a porta apenas para a loopback
    access-control: 127.0.0.1/8 allow
    access-control: ::1 allow

    outgoing-range: 512
    num-queries-per-thread: 128

    msg-cache-size: 2m
    rrset-cache-size: 1m

    msg-cache-slabs: 4
    rrset-cache-slabs: 4

    cache-max-ttl: 1200
    infra-host-ttl: 60
    infra-lame-ttl: 60

    infra-cache-numhosts: 128
    infra-cache-lame-size: 2k

    do-ip4: yes
    do-ip6: yes
    do-udp: yes
    do-tcp: yes
    do-daemonize: yes

    username: "unbound"
    directory: "/etc/unbound"
    logfile: "/var/log/unbound.log"
    use-syslog: yes
    pidfile: "/run/unbound.pid"

    identity: "Unbound-LocalCache"
    version: "1.0"
    hide-identity: yes
    hide-version: yes
    harden-glue: yes
    do-not-query-address: 127.0.0.1/8
    do-not-query-localhost: yes
    module-config: "iterator"

    #zone localhost
    local-zone: "localhost." static
    local-data: "localhost. 10800 IN NS localhost."
    local-data: "localhost. 10800 IN SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
    local-data: "localhost. 10800 IN A 127.0.0.1"

    local-zone: "127.in-addr.arpa." static
    local-data: "127.in-addr.arpa. 10800 IN NS localhost."
    local-data: "127.in-addr.arpa. 10800 IN SOA localhost. nobody.invalid. 2 3600 1200 604800 10800"
    local-data: "1.0.0.127.in-addr.arpa. 10800 IN PTR localhost."

remote-control:
    control-enable: yes
    control-interface: 127.0.0.1
    control-port: 8953
    control-use-cert: "no"


# Operar 100% em modo forward, informe o ip dos servidores DNSs reais:
forward-zone:
    name: "."
    forward-addr: 8.8.8.8
    forward-addr: 8.8.4.4

# Encaminhar dominio especifico para servidor DNS especifico:
forward-zone:
    name: "slack.com"
    forward-addr: 1.1.1.1
    forward-addr: 1.0.0.1
EOF

Acima criamos um forward do domínio slack.com indo para um DNS diferente (1.1.1.1 e 1.0.0.1) do padrão 8.8.8.8 e 8.8.4.4

Reinicie o serviço para carregar as novas configurações

# systemctl restart unbound

Instale o pacote dnsutils para ter ferramentas de testes

# apt install dnsutils

Testando IPv4

# host google.com 127.0.0.1
Using domain server:
Name: 127.0.0.1
Address: 127.0.0.1#53
Aliases: 

google.com has address 142.250.218.78
google.com has IPv6 address 2800:3f0:4001:81d::200e
google.com mail is handled by 10 smtp.google.com.
# dig ANY google.com @127.0.0.1
; <<>> DiG 9.16.27-Debian <<>> ANY google.com @127.0.0.1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 893
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;google.com.			IN	ANY

;; ANSWER SECTION:
google.com.		129	IN	A	142.250.218.78
google.com.		129	IN	AAAA	2800:3f0:4001:81d::200e
google.com.		51	IN	MX	10 smtp.google.com.

;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Wed Jun 01 15:59:35 -03 2022
;; MSG SIZE  rcvd: 104

Testando IPv6

# host google.com ::1
Using domain server:
Name: ::1
Address: ::1#53
Aliases: 

google.com has address 142.250.218.78
google.com has IPv6 address 2800:3f0:4001:81d::200e
google.com mail is handled by 10 smtp.google.com.
# dig ANY google.com @::1
; <<>> DiG 9.16.27-Debian <<>> ANY google.com @::1
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 56485
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;google.com.			IN	ANY

;; ANSWER SECTION:
google.com.		106	IN	A	142.250.218.78
google.com.		281	IN	AAAA	2800:3f0:4001:81d::200e
google.com.		281	IN	MX	10 smtp.google.com.

;; Query time: 0 msec
;; SERVER: ::1#53(::1)
;; WHEN: Wed Jun 01 16:03:30 -03 2022
;; MSG SIZE  rcvd: 104

Configurando o Linux para usar o servidor DNS na loopback como servidor DNS

# echo "nameserver 127.0.0.1" > /etc/resolv.conf
# echo "nameserver ::1 " >> /etc/resolv.conf

Tornando recursivo

Contribuição extra por: Rudimar Remontti
Caso deseje tornar recursivo para os demais hosts da sua rede este servidor, primeira coisa que você deve ter em mente que se este servidor conter IPs públicos o mesmo será responsivo para todo o planeta terra e alguns "aliGenigenas". Então o mínimo que você deve ter é um firewall, protegendo a porta 53 tcp/udp para responder apenas para sua rede.

# vim /etc/unbound/unbound.conf.d/root-auto-trust-anchor-file.conf

Abaixo de:

    # abrir a porta apenas nos enderecos loopback (seguranca)
    interface: 127.0.0.1
    interface: ::1

Adicione

    # abrir a porta para os ips de interface (risco)
    interface: 200.200.200.200
    interface: 2000:bebe:cafe::f07e

Abaixo de:

    # Permitir todos os IPs pois abrimos a porta apenas para a loopback
    access-control: 127.0.0.1/8 allow
    access-control: ::1 allow

Adicione os prefixos da sua rede

    access-control: 192.168.0.0/16 allow
    access-control: 172.16.0.0/12 allow
    access-control: 100.64.0.0/10 allow
    access-control: 10.0.0.0/8 allow
    access-control: 200.200.0.0/22 allow
    access-control: 2001:db8::/32 allow

Desta forma abrindo conexão para os IPs da interface "200.200.200.200" e "2000:bebe:cafe::f07e"

Reinicie o serviço para carregar as alterações

# systemctl restart unbound

Pode consultar se as porta estão sendo ouvidas:

# ss -putan  | grep LISTEN | grep :53
tcp   LISTEN 0      256           200.200.200.200:53           0.0.0.0:*     users:(("unbound",pid=6079,fd=16))                 
tcp   LISTEN 0      256                 127.0.0.1:53           0.0.0.0:*     users:(("unbound",pid=6079,fd=12))                 
tcp   LISTEN 0      256           200.200.200.200:53           0.0.0.0:*     users:(("unbound",pid=6079,fd=8))                  
tcp   LISTEN 0      256                 127.0.0.1:53           0.0.0.0:*     users:(("unbound",pid=6079,fd=4))                  
tcp   LISTEN 0      256                     [::1]:53              [::]:*     users:(("unbound",pid=6079,fd=6))                  
tcp   LISTEN 0      256    [2000:bebe:cafe::f07e]:53              [::]:*     users:(("unbound",pid=6079,fd=10))                 
tcp   LISTEN 0      256                     [::1]:53              [::]:*     users:(("unbound",pid=6079,fd=14))                 
tcp   LISTEN 0      256    [2000:bebe:cafe::f07e]:53              [::]:*     users:(("unbound",pid=6079,fd=18))

Firewall
Porém mesmo que você tenha setados seus prefixos em access-control a porta 53 estará respondendo para todo o mundo. Para fechar elas use o nfttables que por padrão já vem no Debian 11 no lugar do iptables. Você pode instalar o mesmo com apt se necessário.

Edite o arquivo do nftables

# vim /etc/nftables.conf

Ajuste para ficar assim e em elements insira todos seus prefixos autorizados.

#!/usr/sbin/nft -f
  
flush ruleset

table inet filter {

    set acesso-dns4 {
        flags interval
        type ipv4_addr
        elements = { 127.0.0.1, 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12, 100.64.0.0/10, 200.200.200.0/22 }
    }
    set acesso-dns6 {
        flags interval
        type ipv6_addr
        elements = { ::1, 2001:db8::/32 }
    }
    chain input {
        type filter hook input priority 0;

        # Permite Acesso DNS na porta 53
        ip saddr  @acesso-dns4 udp dport 53 counter accept
        ip saddr  @acesso-dns4 tcp dport 53 counter accept
        ip6 saddr @acesso-dns6 udp dport 53 counter accept
        ip6 saddr @acesso-dns6 tcp dport 53 counter accept
        udp dport 53 counter drop
        tcp dport 53 counter drop

        type filter hook input priority 0;
    }
    chain forward {
        type filter hook forward priority 0;
    }
    chain output {
        type filter hook output priority 0;
    }
}

Habilite o mesmo para iniciar com o boot e reinicie.

# systemctl enable nftables
# systemctl restart nftables

Consulte se seu firewall foi carregado com o comando:

# nft list ruleset

Retorno:

table inet filter {
	set acesso-dns4 {
		type ipv4_addr
		flags interval
		elements = { 10.0.0.0/8, 100.64.0.0/10,
			     127.0.0.1, 172.16.0.0/12,
			     192.168.0.0/16, 200.200.200.0/22 }
	}

	set acesso-dns6 {
		type ipv6_addr
		flags interval
		elements = { ::1,
			     2001:db8::/32 }
	}

	chain input {
		type filter hook input priority filter; policy accept;
		ip saddr @acesso-dns4 udp dport 53 counter packets 0 bytes 0 accept
		ip saddr @acesso-dns4 tcp dport 53 counter packets 0 bytes 0 accept
		ip6 saddr @acesso-dns6 udp dport 53 counter packets 0 bytes 0 accept
		ip6 saddr @acesso-dns6 tcp dport 53 counter packets 0 bytes 0 accept
		udp dport 53 counter packets 0 bytes 0 drop
		tcp dport 53 counter packets 0 bytes 0 drop
	}

	chain forward {
		type filter hook forward priority filter; policy accept;
	}

	chain output {
		type filter hook output priority filter; policy accept;
	}
}

Pronto espero que tenha gostado! Agradeço ao meu parceiro Patrick!

Se quiser fazer uma doação para o café ficarei muito feliz pelo seu reconhecimento!

Se não puder doar pode deixar seu agradecimento nos comentário também ficarei feliz em saber que ajudei. Se tiver qualquer pergunta deixe-a também. Se preferir entrar em Contato clique aqui.

Abraço!

Rudimar Remontti

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

Você pode gostar...

21 Resultados

  1. Paulo Henrique disse:

    Como faria para bloquear sites no unbound?
    material show de bola.

  2. Luis Ezpeleta disse:

    Hola .. Cuando coloco
    # abrir a porta para os ips de interface (risco)
    interface: 200.200.200.200
    interface: 2000:bebe:cafe::f07e
    Tengo error al iniciar unbound, que pidria ser

  3. Dhiego disse:

    Pq quando dou o comando nft list ruleset no debian 12 ele retorna “comando não encontrado”, e ja no ubuntu 22.04 ele retorna as regras confome no tutorial

  4. arlinho disse:

    systemctl restart unbound
    Job for unbound.service failed because the control process exited with error code.
    See “systemctl status unbound.service” and “journalctl -xeu unbound.service” for details.

  5. Daniel disse:

    Fiquei em duvida em relação ao Tamanho do cache, pelo que vi aqui no blog, ficou no padrão
    msg-cache-size: 2m
    rrset-cache-size: 1m
    Mas não seria interessante deixar uma quantidade maior de armazenamento ? 200mb ou 1GB ?
    Se alguém puder comentar sobre agradeço.

  6. francesco disse:

    A adição de um DNS autoritativo melhora a latência ou a velocidade da consulta?

  7. Anderson Carlos disse:

    Como habilitar o DNSSEC?
    Já fiz de tudo e não funciona.

  8. Olá Anderson.
    como proxy-cache a performance é melhor, pois alem de fazer cache local vc se aproveita do cache já alimentado nos servidores de DNS apontados no forward. Se você optar pelos hints (servidores raiz) a latência irá aumentar um pouco pois para cada nome a ser resolvido seu DNS local terá que consultar toda a árvore de DNS mundial, com acessos internacionais com latencia acima de 120ms.
    Nesse link há explicações desenhadas:
    http://manual.slackmini.com.br/#dns-client

  9. Anderson Carlos disse:

    pode deixar pelo servidores raiz em vez do forward-zone?
    E qual e a vantagem com e sem forward-zone?

    • Andrio disse:

      Boa pergunta, tbm fiquei curioso

    • Renan Aquino disse:

      Seria esta resposta, para sua pergunta!
      como proxy-cache a performance é melhor, pois alem de fazer cache local vc se aproveita do cache já alimentado nos servidores de DNS apontados no forward. Se você optar pelos hints (servidores raiz) a latência irá aumentar um pouco pois para cada nome a ser resolvido seu DNS local terá que consultar toda a árvore de DNS mundial, com acessos internacionais com latencia acima de 120ms.
      Nesse link há explicações desenhadas:
      http://manual.slackmini.com.br/#dns-client

Deixe um comentário

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