🧠FFVAcademy
🔍

DNS: recursivo, autoritativo, registros, TTL, DNSSEC

14 min de leitura·+70 XP

DNS é o catálogo telefônico da internet — converte "example.com" em IP. Parece trivial até você debugar uma propagação de 48 horas, entender por que um TTL baixo custa dinheiro, ou descobrir que seu resolver foi envenenado.

Resolução DNS: o caminho completo

# dig +trace mostra cada passo da resolução:
# $ dig +trace example.com

# Passo 1: Root nameservers (. → TLD nameservers)
# .                         172800  NS  a.root-servers.net.
# .                         172800  NS  b.root-servers.net.
# (13 root servers: a-m.root-servers.net — anycast globalmente)

# Passo 2: TLD nameserver (.com → autoritativo de example.com)
# com.                      172800  NS  a.gtld-servers.net.
# example.com.              172800  NS  ns1.example.com.  ← glue records

# Passo 3: Autoritativo de example.com
# example.com.              3600    A   93.184.216.34
# www.example.com.          3600    CNAME example.com.

# Verificar resolução passo a passo:
import socket
import dns.resolver  # pip install dnspython

def resolver_completo(hostname: str):
    """Resolve um hostname mostrando os detalhes."""
    # Resolução padrão (usa resolver do sistema):
    addrs = socket.getaddrinfo(hostname, None)
    ips = list({addr[4][0] for addr in addrs})
    print(f"IPs de {hostname}: {ips}")

    # Usando dnspython para resolver manualmente:
    resolver = dns.resolver.Resolver()
    resolver.nameservers = ["8.8.8.8"]  # usar Google DNS diretamente

    try:
        answer = resolver.resolve(hostname, "A")
        for rdata in answer:
            print(f"A: {rdata.address}, TTL: {answer.ttl}s")
    except Exception as e:
        print(f"Erro: {e}")

# Medir latência DNS:
import time

def medir_latencia_dns(hostname: str, iterations: int = 10) -> float:
    tempos = []
    for _ in range(iterations):
        inicio = time.perf_counter()
        socket.getaddrinfo(hostname, 80)
        tempos.append((time.perf_counter() - inicio) * 1000)
    # 1ª chamada = sem cache; seguintes = com cache do OS
    print(f"Primeira: {tempos[0]:.1f}ms (sem cache)")
    print(f"Média restante: {sum(tempos[1:])/len(tempos[1:]):.2f}ms (com cache)")
    return sum(tempos) / len(tempos)

Tipos de registro DNS

TipoFunçãoExemplo
AHostname → IPv4example.com → 93.184.216.34
AAAAHostname → IPv6example.com → 2606:2800:220:1:248:1893:25c8:1946
CNAMEAlias → outro hostnamewww.example.com → example.com
MXMail server (com prioridade)10 mail.example.com (menor = maior prio)
TXTTexto livre (SPF, DKIM, verificação)"v=spf1 include:_spf.google.com ~all"
NSNameservers autoritativosexample.com → ns1.example.com
SOAStart of Authority (zona info)Serial, refresh, retry, expire, TTL
PTRIP → hostname (DNS reverso)34.216.184.93.in-addr.arpa → example.com
SRVLocalização de serviço com porta_xmpp._tcp.example.com 10 5 5222 xmpp.example.com
CAACAs autorizadas a emitir cert0 issue "letsencrypt.org"
import dns.resolver

def consultar_records(domain: str):
    """Consulta múltiplos tipos de record para um domínio."""
    tipos = ["A", "AAAA", "MX", "TXT", "NS", "CAA"]
    for tipo in tipos:
        try:
            answers = dns.resolver.resolve(domain, tipo, lifetime=3)
            for rdata in answers:
                print(f"{tipo:6}: {rdata}")
        except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN):
            pass
        except Exception as e:
            print(f"{tipo:6}: {e}")

# MX records têm prioridade:
# $ dig MX gmail.com
# gmail.com.  3600  MX  5  gmail-smtp-in.l.google.com.   ← menor = mais preferido
# gmail.com.  3600  MX  10 alt1.gmail-smtp-in.l.google.com.

# SPF (Sender Policy Framework) via TXT:
# "v=spf1 include:_spf.google.com include:mailgun.org ~all"
# include: autoriza IPs do serviço
# -all = falhar hard para outros IPs
# ~all = falhar soft (softfail)

# DKIM via TXT:
# selector._domainkey.example.com TXT "v=DKIM1; k=rsa; p=MIGfMA0..."

# DMARC via TXT:
# _dmarc.example.com TXT "v=DMARC1; p=reject; rua=mailto:dmarc@example.com"

# SRV record para descoberta de serviços:
# _https._tcp.example.com SRV 10 5 443 www.example.com.
# Kubernetes CoreDNS: _service._tcp.namespace.svc.cluster.local SRV

TTL, caching e propagação

# TTL (Time To Live): quantos segundos o record pode ser cacheado
# TTL baixo: mudanças propagam rápido, mas mais queries para o autoritativo
# TTL alto: menos load no servidor, mas mudanças demoram a propagar

# Estratégia para migrar DNS sem downtime:
# 1. SEMANA ANTES: reduzir TTL de 3600s para 300s (5min)
#    → propagação máxima = 300s após a mudança
# 2. HORA DA MIGRAÇÃO: alterar o record
# 3. PÓS-MIGRAÇÃO: aumentar TTL de volta para 3600s+ (economia de queries)

# Verificar TTL atual de um record:
def verificar_ttl(domain: str, record_type: str = "A"):
    answers = dns.resolver.resolve(domain, record_type)
    print(f"TTL de {domain} ({record_type}): {answers.ttl}s")
    print(f"Propagação máxima se mudar agora: {answers.ttl}s ({answers.ttl/60:.1f} min)")

# Cache DNS em diferentes níveis:
# 1. Cache do SO (/etc/hosts tem precedência absoluta)
# 2. nscd ou systemd-resolved (cache local)
# 3. Resolver recursivo (8.8.8.8 cacheia por TTL)
# 4. Browser tem seu próprio cache DNS (separado do OS!)

# Limpar cache DNS:
# Linux (systemd): sudo systemd-resolve --flush-caches
# macOS:           sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder
# Windows:         ipconfig /flushdns
# Chrome:          chrome://net-internals/#dns → Clear host cache

# Negative caching (NXDOMAIN):
# Se domínio não existe, o TTL do SOA MINIMUM define por quanto tempo
# o resolver cacheia a resposta negativa
# $ dig nonexistent.example.com SOA → SOA TTL = quanto tempo cachear NXDOMAIN

# Negative TTL recomendado: 300-900s
# Por quê importa: NXDOMAIN cacheado pode impedir resolução após criar o record

DNSSEC e DoH/DoT

# DNSSEC: autenticação de records DNS
# NÃO criptografa — apenas assina digitalmente

# Records DNSSEC:
# DNSKEY: chave pública da zona
# RRSIG:  assinatura digital de cada RRset
# DS:     hash da DNSKEY filho no servidor pai (chain of trust)
# NSEC:   "não existe nada entre example.com e ftp.example.com" (negative proof)
# NSEC3:  versão com hashing (esconde nomes que existem na zona)

# Verificar DNSSEC:
# dig +dnssec example.com        → mostra RRSIG records
# dig +sigchase example.com      → valida a cadeia completa
# https://dnsviz.net/            → visualização gráfica da cadeia DNSSEC

# DNS-over-HTTPS (DoH): queries DNS dentro de HTTPS
# Endpoint: https://cloudflare-dns.com/dns-query
# Vantagem: queries criptografadas, passa por firewalls
# Desvantagem: centralização (Google/Cloudflare veem todas as queries)

import httpx
import base64
import struct

async def dns_over_https(domain: str, record_type: str = "A") -> list:
    """Faz query DNS via DoH (Cloudflare 1.1.1.1)."""
    # Construir query DNS binária (formato wire):
    # Simple A query for domain — simplificado aqui
    async with httpx.AsyncClient() as client:
        resp = await client.get(
            "https://cloudflare-dns.com/dns-query",
            params={"name": domain, "type": record_type},
            headers={"Accept": "application/dns-json"},
        )
        data = resp.json()
        return [a["data"] for a in data.get("Answer", []) if a["type"] == 1]

# DNS-over-TLS (DoT): queries DNS via TLS na porta 853
# Mais simples que DoH, mas porta 853 é fácil de bloquear

# Configurar DoH no sistema:
# Linux (systemd-resolved): DNSOverTLS=yes em /etc/systemd/resolved.conf
# macOS: Configurações → Rede → DNS → DoH
# Windows 11: Configurações → Rede → DNS → DoH
Modelo mental: resolução DNS = cascade de caches (OS → recursivo → root → TLD → autoritativo). Resolver recursivo faz o trabalho; autoritativo tem a resposta definitiva. TTL controla por quanto tempo o cache vale — baixe antes de migrar, aumente depois. A records para IPs, CNAME para aliases, MX para email, TXT para SPF/DKIM/DMARC. DNSSEC autentica (não criptografa). DoH/DoT criptografam as queries para privacidade.
💡
Próximo: Proxies e load balancers — L4 vs L7, reverse proxy, HAProxy, Nginx e quando usar cada um.
🧩

Quiz rápido

3 perguntas · Acerte tudo e ganhe o badge 🎯 Gabarito

Continue lendo