Ir para o conteúdo

Configuração

O DNSao requer um único arquivo de configuração:

Você pode encontrar exemplos de configuração no projeto do GitHub, mas abaixo há exemplos e referências.

Exemplo Mínimo Funcional

Isto é suficiente para rodar o DNSao como encaminhador DNS com cache, dashboard e upstreams DoT criptografados:

server:
  port: 53
  webPort: 8044
  authPass: ""

cache:
  enabled: true
  maxCacheEntries: 2048
  rewarm: true

misc:
  timeout: 3
  dnssec: simple
  blockingEnabled: true

resolver:
  multiplier: 1
  upstreams:
    - ip: "1.1.1.1"
      port: 853
      protocol: "dot"
      tlsAuthName: "cloudflare-dns.com"
    - ip: "9.9.9.9"
      port: 853
      protocol: "dot"
      tlsAuthName: "dns.quad9.net"

No Docker, a configuração de exemplo usa server.port: 8053 dentro do container enquanto o Docker mapeia a porta 53 do host para a porta 8053 do container.

Escolhendo Upstreams

O DNSao suporta três protocolos de upstream:

Protocolo Use Quando Campos necessários
udp Você quer a configuração mais simples e não precisa de tráfego upstream criptografado ip, port, protocol
dot Você quer queries upstream criptografadas com DNS-over-TLS ip, port, protocol, tlsAuthName
doh Você quer queries upstream criptografadas com DNS-over-HTTPS host, port, protocol, path opcional

resolver.multiplier controla quantos upstreams recebem cada query normal de cliente. 1 é melhor para privacidade porque cada query vai para um upstream. Valores maiores podem reduzir latência disputando upstreams, mas mais resolvedores verão cada query.

Para mais contexto sobre DoT, DoH e o que DNS criptografado protege ou não protege, veja DNS Criptografado.

Configuração YML

Este é o único arquivo de configuração da aplicação. Nele você define quais portas serão usadas, quais recursos estarão habilitados, quais upstreams serão utilizados, mapeamentos locais e quais blocklists de DNS serão aplicadas. Abaixo está um exemplo completo:

server:
  port: 53 
  udpThreadPool: 10
  tcpThreadPool: 3
  statsDbPath: "/etc/dnsao/stats.db"
  webPort: 8044
  authPass: ""

cache:
  enabled: true
  maxCacheEntries: 2048
  rewarm: true
  maxRewarmCount: 5
  maxRewarmPerMinute: 3000
  alwaysRewarmTopEntries: 0
  rewarmWorkerPoolSize: 3
  keep:
    - "url1.com"
    - "url2.com"

misc:
  timeout: 3
  queryLog: true
  refreshLists: false
  serveExpired: false
  serveExpiredMax: 86400
  dnssec: "simple"

resolver:
  tlsPoolSize: 5
  multiplier: 3
  upstreamThreadPoolSize: 64
  upstreamQueueSize: 640
  upstreams:
    - name: cloudflare-dot-1
      ip: "1.1.1.1"
      port: 853
      protocol: "dot"
      tlsAuthName: "cloudflare-dns.com"
    - name: cloudflare-dot-2
      ip: "1.0.0.1"
      port: 853
      protocol: "dot"
      tlsAuthName: "cloudflare-dns.com"
    - ip: "1.1.1.1"
      port: 53
      protocol: "udp"
    - ip: "1.0.0.1"
      port: 53
      protocol: "udp"
    - name: quad9-doh
      host: "dns.quad9.net"
      port: 443
      protocol: "doh"
      path: "/dns-query"
    - ip: "149.112.112.112"
      port: 853
      protocol: "dot"
      tlsAuthName: "dns.quad9.net"
    - ip: "9.9.9.9"
      port: 853
      protocol: "dot"
      tlsAuthName: "dns.quad9.net"

  localMappings:
    - domain: "ma-cool.domain.com"
      ip: "192.168.150.150"
    - domain: "ma-cool-2.domain.com"
      ip: "192.168.150.151"

lists:
  blockLists:
    steven: "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"
    bets: "https://raw.githubusercontent.com/zangadoprojets/pi-hole-blocklist/refs/heads/main/Bets.txt"
  allowLists:
    allowList1: "http://url.of.allow.lists.com"

groups:
  main:
    upstreams:
      - cloudflare-dot-1
      - cloudflare-dot-2
  group1:
    members:
      - "192.168.68.55"
      - "192.168.68.40"
    upstreams:
      - quad9-doh
    allows:
      - allowList1
    blocks:
      - steven
  group2:
    members:
      - "192.168.68.10"

listeners:
  http:
    - "http://host:port/listener"  

server

server:
  port: 53 
  udpThreadPool: 10
  tcpThreadPool: 3
  httpThreadPool: 10
  statsDbPath: "/etc/dnsao/stats.db"
  webPort: 8044
  authPass: ""

A propriedade server define as propriedades de alto nível da aplicação.

Propriedade Descrição
port porta em que a aplicação escutará chamadas UDP e TCP, conforme os padrões de DNS. O padrão para servidores DNS é 53
udpThreadPool quantas threads estarão disponíveis para o protocolo UDP, no server. O valor padrão é 10
tcpThreadPool quantas threads estarão disponíveis para o protocolo TCP, no server. O valor padrão é 3
httpThreadPool quantas threads estarão disponíveis para o protocolo HTTP, no server. O valod padrão é 10
webPort porta onde o dashboard de métricas ficará disponível. O padrão é 8044
statsDbPath caminho para um arquivo SQLite de métricas e histórico de queries. Quando não definido/vazio, o DNSao usa {tmpdir}/dnsao.db (o diretório temporário do SO). O diretório pai precisa existir e ter permissão de escrita (o DNSao não cria diretórios)
useMemoryStorage true/false, padrão é false. Quando true, força o armazenamento das métricas em memória em vez de SQLite. Útil para ambientes efêmeros ou quando você quer evitar escrita em disco
authPass senha opcional para proteger o dashboard web e as APIs JSON. Quando vazia (padrão), o dashboard fica aberto para qualquer um. Quando definida, os usuários devem informar esta senha em uma página de login antes de acessar o dashboard. A senha é enviada em texto puro via HTTP — use apenas em redes confiáveis ou atrás de um proxy reverso com HTTPS. O padrão é "" (sem autenticação)

Quando authPass está definida, acessar o dashboard em http://serverIp:webPort/ redirecionará para uma página de login. Os endpoints de API (/stats, /queries, /api/*) também exigem autenticação e retornam 401 Unauthorized quando não autenticados. O endpoint DNS-over-HTTPS (/dns-query) é sempre público, independentemente desta configuração.

Para queries http, o endpoint é http://serverIp:webPort/dns-query, seguindo os padrões de servidor dns via HTTP. Note que a resposta será em HTTP aberto, não em HTTPS. Essa é uma decisão consciente para evitar o manuseio de certificados TLS, de forma que o usuário possa usar os próprios certificados. Caso https seja desejado, é recomendado usar um proxy reverso que possibilite a comunicação remota a ocorrer via HTTPS (como traeffic ou nginx) e fazer o proxy reverso interno para DNSao.

Na interface web http://serverIp:webPort/ você pode observar as métricas do servidor e buscar queries individualmente. O gráfico contém uma janela das últimas 24 horas dividida em segmentos de 10 minutos.

Rotas do dashboard:

Caminho Objetivo
/ dashboard de sumário
/query histórico pesquisável de queries
/upstream contagens, participação, latência e uso por hora dos upstreams
/vm memória da JVM, GC e estatísticas de threads
/dns-query endpoint compatível com DNS-over-HTTPS, servido via HTTP a menos que esteja atrás de um proxy HTTPS

O DNSao persiste queries e métricas em um arquivo SQLite local por padrão. Os segmentos contêm eventos de query buscáveis no endpoint /query — com persistência em disco não há limite de eventos, diferente do modo legado em memória que limitava em 5.000 eventos por segmento. As escritas são agrupadas a cada 500ms; se o banco não acompanhar, o DNSao descartará os eventos mais antigos do buffer para evitar crescimento ilimitado de memória.

cache

cache:
  enabled: true
  maxCacheEntries: 2048
  rewarm: true
  maxRewarmCount: 5
  maxRewarmPerMinute: 3000
  alwaysRewarmTopEntries: 0
  rewarmWorkerPoolSize: 3
  keep:
    - "url1.com"
    - "url2.com"

A propriedade cache define o comportamento do cache da aplicação. O cache é o principal componente responsável por acelerar as queries de DNS.

Propriedade Descrição
enabled indica se o cache será configurado. É possível definir como false para desabilitar o cache - não recomendado, pois isso degradará significativamente a performance e quebrará a lógica de DNS Query/TTL. Pode ser útil para troubleshooting. O padrão é true
maxCacheEntries número máximo de entradas permitidas no cache. 2048 é o padrão e um bom ponto de partida para redes domésticas com os limites de memória usados pelo script de instalação. Se aumentar esse número significativamente, revise também o limite de memória da JVM.
rewarm habilita o mecanismo de “cache rewarm”: quando uma entrada de cache está perto do fim do seu TTL, uma tentativa de atualização é feita automaticamente. O padrão é true
maxRewarmCount quantas vezes o DNSao fará rewarm da entrada antes de removê-la da memória. Se chegar uma query para um domínio no cache “warm”, essa entrada é promovida para o cache “hot” e o contador de rewarm é reiniciado. Isso garante que domínios acessados com frequência permaneçam disponíveis, melhorando a performance da resolução DNS. O padrão é 5
maxRewarmPerMinute número máximo de tentativas de rewarm por minuto. O DNSao aplica este limite como um orçamento por segundo para evitar picos na virada do minuto; por exemplo, 3000 permite até 50 tentativas de rewarm por segundo. Cada tentativa de rewarm envia apenas uma query ao upstream. O padrão é 3000. O mínimo é 1
alwaysRewarmTopEntries o número de entradas mais frequentemente acessadas que serão sempre reaquecidas, ignorando o maxRewarmCount. Diferente de keep, estas não são pré-carregadas — elas conquistam seu lugar através de consultas reais dos clientes. As N entradas (não-keep) mais recentes por acesso são promovidas automaticamente. Padrão é 0 (desabilitado). Limitado a maxCacheEntries
rewarmWorkerPoolSize o número de threads no pool de workers de rewarm. Um pool maior permite que mais entradas sejam reaquecidas em paralelo, reduzindo a chance de entradas expiradas sob alta carga de consultas. O padrão é 3. O mínimo é 1
keep uma lista de urls para realizar um precache antes de servidor iniciar e também sempre manter em memória. Essas urls sempre serão mantidas quentes mesmo após atingirem o limite estabelecido em maxRewarmCount. O objetivo é manter essas entradas sempre disponíveis em cache

misc

misc:
  timeout: 3
  queryLog: true
  refreshLists: false
  blockingEnabled: true
  serveExpired: false
  serveExpiredMax: 86400
  dnssec: "simple"

A propriedade misc define mecanismos de funcionamento geral do servidor.

Propriedade Descrição
timeout timeout global em segundos para as queries upstream
queryLog true/false, padrão é true. Controla se eventos individuais de consulta são registrados e armazenados. Quando false: detalhes da consulta são suprimidos do log DNS, campos sensíveis (domínio, cliente, tipo, resposta) são removidos dos eventos notificados aos assinantes, os contadores do dashboard continuam funcionando, mas a tabela de histórico de consultas fica vazia
refreshLists true/false, padrão é false. Quando habilitado, o servidor refaz o download das listas de allow e block periodicamente para atualizar seus valores. Se muitas listas forem usadas, isso pode causar aumento de consumo de memória. Aloque mais memória caso deseje habilitar a função
blockingEnabled true/false, padrão é true. Quando definido como false, a unidade de bloqueio é completamente desabilitada e todas as consultas passam sem verificação de listas. Útil para desabilitar temporariamente o filtro sem remover as configurações de lista
serveExpired true/false, padrão é false. Aderindo à RFC 8767, quando habilitado, DNSao servirá entradas já expiradas quando nenhuma consulta upstream resultar em uma resposta definitiva (timeout, SERVFAIL ou REFUSED), maximizando a disponibilidade DNS
serveExpiredMax padrão é 86400 (um dia). Quando serveExpired está habilitado, esse é o tempo máximo em segundos que uma entrada expirada do cache local ainda pode ser servida antes de ser considerada completamente expirada e removida
dnssec define o funcionamento geral sobre DNSSEC do servidor. Mais detalhes na tabela abaixo. O valor padrão é simple

dnssec

A propriedade dnssec define o comportamento de DNSao sobre as flags e validação DNSSEC , e tem o valor padrão de simple. Os valores válidos são off, simple e rigid. DNSao não executa a validação crypt para as flags e chaves, mas confia na resposta dos servidores upstream para definir seu comportamento.

Nível Descrição
off a requisição do cliente será enviada para os upstreams sem manipulação das flags DNSSEC. DNSao não irá validar se a resposta tem validação DNSSEC antes de retornar ao cliente
simple a requisição do cliente será enviada para os upstreams adicionando a flag DNSSEC. DNSao irá responder ao cliente mas não bloqueará as respostas não validadas. A query também terá um padding para o próximo tamanho multiplo de 128 bytes para permitir ofuscação extra, seguindo dns rfc7830
rigid a requisição do cliente será enviada para os upstreams adicionando a flag DNSSEC. DNSao só responderá ao cliente se a resposta possuir a flag DNSSEC disponível e válida. Caso contrário, será respondido um SERVFAIL. A query também terá pading, mas para o tamanho máximo permitido no pacote que será enviado ao upstream para máxima ofuscação. Atenção: isso irá bloquear vários domínios, visto que muitos deles não possuem DNSSEC habilitado

Independente da resposta do upstream, DNSao não habilita a flag AD na resposta, pois não executa as validações dos hashs internamente (necessário pela dns rfc4035 3.2.3)

resolver

resolver:
  tlsPoolSize: 5
  multiplier: 3
  upstreamThreadPoolSize: 64
  upstreamQueueSize: 640
  upstreams:
    - name: cloudflare-dot-1
      ip: "1.1.1.1"
      port: 853
      protocol: "dot"
      tlsAuthName: "cloudflare-dns.com"
    - name: cloudflare-dot-2
      ip: "1.0.0.1"
      port: 853
      protocol: "dot"
      tlsAuthName: "cloudflare-dns.com"
    - ip: "1.1.1.1"
      port: 53
      protocol: "udp"
    - ip: "1.0.0.1"
      port: 53
      protocol: "udp"
    - name: quad9-dot-1
      ip: "149.112.112.112"
      port: 853
      protocol: "dot"
      tlsAuthName: "dns.quad9.net"
    - name: quad9-dot-2
      ip: "9.9.9.9"
      port: 853
      protocol: "dot"
      tlsAuthName: "dns.quad9.net"

  localMappings:
    - domain: "ma-cool.domain.com"
      ip: "192.168.150.150"
    - domain: "ma-cool-2.domain.com"
      ip: "192.168.150.151"

A propriedade resolver define os upstreams que serão consultados. Você deve especificar as configs respectivas para cada protocolo ("dot", "doh" ou "udp"). Estas são as propriedades de alto nível:

Propriedade Descrição
tlsPoolSize tamanho máximo do pool de conexões DOT por upstream. Usar pool melhora a performance, já que o handshake TLS é custoso, mas aumentá-lo demais não trará necessariamente mais velocidade — uma única conexão pode servir múltiplas requisições e conexões stale são descartadas pelo upstream
multiplier para quantos upstreams cada query será enviada. O DNSao usa a resposta mais rápida e descarta as demais. Quando um grupo de clientes seleciona um subconjunto de upstreams, o multiplier se aplica apenas dentro desse subconjunto. Há um trade-off entre velocidade e privacidade: quanto mais upstreams por requisição, mais servidores verão suas queries. Se privacidade é o principal objetivo, defina multiplier como 1 e use upstreams DOT ou DOH.
upstreamThreadPoolSize tamanho do pool compartilhado de threads usado para executar chamadas aos upstreams. O padrão é 64
upstreamQueueSize tamanho da fila limitada para tarefas upstream. O padrão é 640 (64 * 10)

Internals da Execução Upstream

As chamadas aos upstreams são executadas via um ThreadPoolExecutor compartilhado (UpstreamThreadPoolExecutor) configurado com um número fixo de threads (resolver.upstreamThreadPoolSize) e uma fila limitada (resolver.upstreamQueueSize).

Quando o pool e a fila estão saturados, o DNSao usa CallerRunsPolicy como mecanismo de backpressure: a thread chamadora executa a chamada upstream inline, desacelerando o processamento de novas queries ao invés de criar mais threads ou permitir crescimento ilimitado de memória.

O resolver.multiplier controla quantas tarefas upstream uma única query pode agendar; sob saturação, as tarefas podem ficar na fila ou serem executadas inline por causa do backpressure.

Estas são as propriedades internas dentro de upstreams:

Propriedade Descrição
upstreams a lista de upstreams que serão consultados
name nome opcional para este upstream. Upstreams nomeados podem ser referenciados em groups.<grupo>.upstreams para rotear clientes diferentes por pools de resolvedores diferentes. Upstreams sem nome continuam válidos e são usados pelo pool global/padrão
ip IP do servidor upstream a ser usado
port Porta do servidor upstream a ser usada. Para UDP, a porta comum é 53; para DOT, a porta padrão é 853; para DOH, a porta padrão é 443
protocol protocolos suportados: udp, dot e doh
tlsAuthName ao usar o protocolo dot, é necessário também definir tlsAuthName para validação do servidor remoto. Esse nome é verificado na inicialização e, se a verificação de autoridade falhar, o upstream é descartado e não será usado
host ao usar o protocolo doh, é necessário definir a propriedade host, para onde as queries serão enviadas via https
path ao usar o protocolo doh, é necessário definir a propriedade path, que será incluida ao final de host. Seu valor padrão é /dns-query

Exemplos das configurações possíveis podem ser encontradas na pasta config-samples no github. Diferentes tipos de upstream podem ser usados ao mesmo tempo contanto que as propriedades necessárias para cada protocolo estejam presentes.

Nomear upstreams é opcional. Se nenhuma seleção de upstream por grupo for configurada, o DNSao usa todo o pool de resolver.upstreams, como antes.

Também é possível definir localMappings: entradas de DNS que serão resolvidas diretamente pelo DNSao.

Propriedade Descrição
domain o domínio a ser mapeado
ip o IPv4 que será enviado como resposta. Apenas IPv4 está disponível no momento

Mapeamentos locais são normalizados para minúsculas e podem ser escritos com ou sem ponto final.

Exemplo de mapeamento exato:

resolver:
  localMappings:
    - domain: "nas.home"
      ip: "192.168.1.10"

Mapeamento wildcard segue a semântica padrão de wildcard DNS. Apenas um * como rótulo completo mais à esquerda é tratado como wildcard, e ele casa exatamente um rótulo.

resolver:
  localMappings:
    - domain: "*.lab.home"
      ip: "192.168.1.20"

Isso casa app.lab.home, mas não casa lab.home nem a.b.lab.home. Padrões como api.*.home, host-* ou *middle* são tratados como nomes literais, não como expressões wildcard.

lists

lists:
  blockLists:
    steven: "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"
    bets: "https://raw.githubusercontent.com/zangadoprojets/pi-hole-blocklist/refs/heads/main/Bets.txt"
  allowLists:
    allowList1: "http://url.of.allow.lists.com"

Essa config é opcional, mas pode ser usada para configurar listas de domínios a serem bloqueados. O parâmetro esperado é a url onde o servidor pode fazer o download das listas. Qualquer domínio mapeado nas listas de blockLists será respondido com o IP 0.0.0.0, seguindo o padrão de DNS Sink Hole.

As listas podem estar no formato hosts:

87.123.55.32 domain.to.be.blocked       # o IP é ignorado, todos os domínios irão para 0.0.0.0

ou lista simples:

domain1.to.be.blocked
domain2.to.be.blocked
domain3.to.be.blocked

Um caso de uso comum é usar StevenBlack, uma blocklist famosa e atualizada regularmente. Os links são baixados quando DNSao inicia e são atualizados a cada 12 horas, caso a config refreshLists esteja habilitada.

Também é possível configurar allowLists: possuem o mesmo comportamento de download e releitura que as blockLists, mas servem como um indicativo de domínios que você não quer bloquear mesmo se estiverem presentes nas blockLists. É útil para evitar falsos-positivo, e cenários onde algum item de alguma blocklist que é de seu interesse.

Tanto as blockLists e allowLists exigem um nome para cada lista (no exemplo acima, os nomes são "steven", "bets" e "allowList1").

Nomes precisam ser únicos entre as blockLists e allowLists para funcionarem efetivamente.

groups

groups:
  main:
    upstreams:
      - cloudflare-dot-1
  group1:
    members:
      - "192.168.68.55"
      - "192.168.68.40"
    upstreams:
      - quad9-dot-1
      - quad9-dot-2
    allows:
      - allowList1
    blocks:
      - steven
  group2:
    members:
      - "192.168.68.10"

Essa config é opcional, mas pode ser usada para seletivamente bloquear ou permitir domínios baseado no cliente. Dessa forma, domínios específicos podem ser bloqueados para alguns devices, mas não para toda a rede.

Grupos também podem selecionar quais upstreams nomeados serão usados por seus membros. Use groups.<grupo>.upstreams para referenciar nomes declarados em resolver.upstreams.

No exemplo acima, o grupo nomeado group1 terá dois membros (os ips terminando em 55 e 40), e só bloqueará os domínios da lista em "steven", e permitirá os domínios da lista "allowList1".

O grupo group2 terá um único membro e não bloqueará ou permitirá nenhuma lista específica.

Todo os clientes não definidos individualmente em um grupo entrarão no grupo MAIN.

O grupo MAIN pode opcionalmente ser definido manualmente no YAML. Quando explicitamente definido, seus members, allows e blocks são preservados como estão. Quando main está ausente da configuração, o DNSao o cria automaticamente como um grupo genérico usando todas as blockLists e allowLists definidas na seção lists.

A seleção de upstreams segue esta ordem: se o grupo do cliente define upstreams, apenas esses upstreams nomeados são usados; caso contrário, o DNSao usa main.upstreams quando configurado; caso contrário, usa todo o pool de resolver.upstreams. Se qualquer nome referenciado for desconhecido, o DNSao registra um erro no log e usa todo o pool de resolvedores para aquela seleção, sem impedir a inicialização do servidor.

A priorização de vencedor é limitada ao grupo/política de upstreams selecionada. Um vencedor rápido de um grupo não reordena o pool de upstreams de outro grupo.

O cache rewarm preserva a política de upstream da query original. Se uma entrada de cache foi criada a partir de uma seleção específica de grupo, as tentativas posteriores de rewarm usam a mesma seleção em vez de voltar para o pool global.

listeners

listeners:
  http:
    - "http://host:port/listener"

Config opcional que pode ser usada para informar serviços http externos para receber as queries DNS via POST. Após cada requisição dns ser recebida e processada, DNSao irá executar um POST para cada url da lista com o body no formato abaixo:

{
  "requestTime" : "2025-11-08 00:00:00.000",
  "queryResolvedBy" : "UPSTREAM",
  "client" : "192.168.66.123",
  "type" : "A",
  "domain" : "example.com",
  "answer" : "10.10.10.10",
  "source" : "9.9.9.9",
  "elapsedTimeInMs" : "1000"
}
O campo source informa qual upstream respondeu a query, caso ela tenha sido resolvida por um upstream, caso contrário, virá nulo.

Logging

O logging é configurado através da seção log no application.yml. DNSao usa java.util.logging (JUL) com três loggers nomeados: DNS, CACHE e INFRA.

Configuração

log:
  rootLevel: WARN
  dns: DEBUG
  cache: DEBUG
  infra: DEBUG
  # Log opcional em arquivo (se ausente, imprime apenas no console):
  file:
    path: "/var/log/dnsao/dnsao-%g.log"
    maxSize: 10485760
    maxFiles: 5

Loggers

  • DNS — resolução de consultas DNS, ciclo de vida do servidor
  • CACHE — operações de cache (acertos, erros, rewarm)
  • INFRA — eventos de infraestrutura (carregamento de listas, conexões upstream)

Níveis

Valor config Nível JUL Descrição
TRACE FINER Detalhes de diagnóstico
DEBUG FINE Informação de depuração
INFO INFO Mensagens operacionais normais
WARN WARNING Situações inesperadas mas recuperáveis
ERROR SEVERE Falhas graves
OFF OFF Suprime todas as mensagens

Log em arquivo

Quando file.path é definido, o DNSao escreve logs de forma assíncrona em arquivos rotativos. O padrão suporta %g como índice de geração de arquivo.

Formato de saída no console

[HH:mm:ss.SSS] LEVEL [thread] [LOGGER] message