Skip to content

Referência de Config

Referência completa de /etc/ezyshield/config.yaml — fontes de log, backends de enforcement, notificações, IA, enriquecimento e o dashboard. O arquivo é validado de forma estrita: chaves desconhecidas são rejeitadas com o número exato da linha.

ezyshield init e os wizards ezyshield config <componente> escrevem em /etc/ezyshield e precisam de sudo — falham imediatamente com a dica antes de qualquer pergunta. Valide qualquer edição manual com ezyshield config validate.

Nível superior

CampoTipoPadrãoDescrição
data_dirstring/var/lib/ezyshieldObrigatório (config validate rejeita valor vazio). Diretório de estado usado pelo comando dashboard — seu banco de auth é <data_dir>/dashboard.db. Não define o caminho do banco SQLite do daemon: isso é a flag run --db (default /var/lib/ezyshield/ezyshield.db).
socket_pathstring/run/ezyshield/ezyshield.sockCaminho do socket de controle ao qual o dashboard se conecta (unix socket — nunca há listener TCP para controle). Não define o socket do daemon: o daemon usa a flag run --socket (default /run/ezyshield/ezyshield.sock), então um valor customizado precisa casar com run --socket, ou o dashboard aponta para um socket que o daemon nunca cria.
rules_dirstring/etc/ezyshield/rules.dCustomizações de regras via drop-in: todo *.yaml aqui faz merge sobre as rules embutidas por name e sobrevive a updates (veja o guia de regras)
rules_pathstringDeprecated. Substitui as rules embutidas por inteiro (sem merge; rules.d ignorado) — congela a instalação fora do tuning de regras do upstream
log.levelstringinfodebug | info | warn | error
collectorslista[]Fontes de log a acompanhar (veja abaixo). Uma lista vazia é válida — o config validate emite um aviso e o daemon simplesmente não acompanha nada.
enforceobjetoBackends de enforcement (opcional — sem ele, as decisões ficam só no log)
notifyobjetoCanais de notificação (opcional)
aiobjetoProvedor de IA para tráfego ambíguo (opcional)
enrichobjetoEnriquecimento GeoIP/ASN (opcional)
dashboardobjetoEndereço de bind e banco de auth do dashboard (opcional)

O daemon ignora data_dir e socket_path; o comando dashboard os consome (e data_dir é adicionalmente exigido por config validate). O daemon (ezyshield run) obtém o caminho do banco e o socket de controle das suas próprias flags --db e --socket (defaults /var/lib/ezyshield/ezyshield.db e /run/ezyshield/ezyshield.sock) e não lê essas duas chaves. Defini-las no config.yaml move o banco de auth do dashboard e o alvo de conexão dele, não os arquivos do daemon — então mantenha socket_path alinhado com run --socket. Veja a referência de CLI para as flags de run e dashboard.

collectors

Cada entrada acompanha uma fonte de log. kind seleciona a fonte; cada kind exige um campo extra.

yaml
collectors:
  - kind: journald
    unit: ssh                    # unit systemd a acompanhar

  - kind: file
    path: /var/log/nginx/access.log

  - kind: docker
    container: wordpress-nginx   # nome, ID curto ou ID completo
    parser: nginx                # override opcional de parser
CampoObrigatórioDescrição
kindsimfile | journald | docker
pathpara filearquivo a acompanhar
unitpara journaldunit systemd a acompanhar
containerpara dockernome do container, ID curto ou ID completo
parsernãoforça um parser: nginx | ssh | apache | apache-error | traefik | caddy (padrão: roteado automaticamente a partir da fonte). Honrado apenas para coletores file e docker — o journald o ignora e sempre roteia o parser a partir da unidade.

Coletor SSH (nome do unit varia por distro)

O nome do unit systemd do SSH depende da distro: é ssh no Debian/Ubuntu e sshd no RHEL/CentOS/Fedora/Rocky/Alma, Arch e SUSE. Use o nome que systemctl status <unit> resolve no seu host — um alias que o journalctl -u não reconhece coleta zero eventos.

yaml
collectors:
  - kind: journald
    unit: ssh    # Debian/Ubuntu; use "sshd" no RHEL/CentOS/Arch/SUSE

Para ler o SSH de um arquivo em vez do journald, aponte para o log de auth da sua distro — /var/log/auth.log (Debian/Ubuntu) ou /var/log/secure (família RHEL). Os dois formatos de timestamp são aceitos: o legado (Jan 1 12:00:00) e o ISO-8601 moderno (2026-07-13T22:57:35+00:00).

Configure apenas um coletor de SSH por host — journald ou o arquivo que ele alimenta, nunca os dois. Ler ambos ingere cada evento duas vezes, o que conta em dobro para os limiares de detecção. (Um IP já banido nunca é banido de novo, então isso nunca gera bans duplicados, apenas detecção mais cedo.)

enforce

yaml
enforce:
  nftables: {}                   # enforcement local ligado; os padrões bastam

  cloudflare:
    api_token: env:CF_API_TOKEN  # segredos são referências env:, nunca inline
    account_id: "abc123..."      # obrigatório no modo padrão "lists"
    # mode: lists                # "lists" (padrão) ou "rulesets"
    # list_name: ezyshield_blocked
    # zone_ids: [ ... ]          # obrigatório apenas com mode: rulesets
    # action: block              # padrão

nftables

CampoPadrãoDescrição
tableinet ezyshieldtabela nftables (todas as regras do EzyShield vivem dentro dela). <nome> ou inet <nome>; a família inet é a única suportada (layout dual-stack v4+v6). Nomes: letras, dígitos, underscore
setblockedset que guarda os endereços IPv4 banidos; o gêmeo IPv6 é derivado automaticamente como <set>6 (padrão blocked6). allowed/allowed6 são reservados para os sets de allowlist
socket/run/ezyshield-enforcer/enforcer.socksocket do helper privilegiado do enforcer

Os dois são opcionais e genuinamente respeitados: o daemon os repassa ao enforcer privilegiado, que os revalida de forma independente antes de escrever qualquer regra. Duas notas operacionais para nomes customizados:

  • O enforcer precisa suportá-los (mesma versão do daemon). Contra um ezyshield-enforcer mais antigo, o daemon se recusa a aplicar com um erro claro em vez de usar os padrões silenciosamente.
  • O enforcer aplica um conjunto de nomes por execução. Depois de mudar table/set, reinicie os dois serviços (sudo systemctl restart ezyshield-enforcer ezyshield); uma tabela antiga deixada por uma renomeação pode ser removida com nft delete table inet <nome-antigo>.

cloudflare

CampoObrigatórioDescrição
api_tokensimreferência env:VARNAME para um API token com escopo restrito
modenãolists (padrão — IP List no nível da conta + regras WAF) ou rulesets (regras por zona)
account_idcom mode: listsID da conta Cloudflare
list_namenãonome da IP list (padrão ezyshield_blocked)
zone_idscom mode: rulesetszonas às quais anexar as regras
actionnãoblock (padrão), challenge ou js_challenge
namenãorótulo exibido na saída de status/test
debouncenãopor quanto tempo mutações rápidas de ban/unban são agrupadas antes de um único push à API (duração Go, padrão 15s)
expire_flush_intervalnãocadência das remoções de itens em lote no modo lists (duração Go, padrão 3m) — bans expirados e unbans acumulam e saem em uma única chamada de API por intervalo

Múltiplas contas Cloudflare são suportadas: cloudflare também aceita uma lista desses objetos. Veja o guia da Cloudflare.

Ajustar as duas cadências troca velocidade de propagação no edge por menos chamadas de API. Os padrões mantêm um servidor movimentado confortavelmente dentro do limite da Lists API da Cloudflare; aumente-os se o ezyshield status ainda reportar throttling (ratelimited no detalhe do enforcement) e diminua o debounce se um ban novo precisar chegar ao edge mais rápido. As remoções são deliberadamente o caminho lento: um IP expirado permanecer bloqueado no edge por até expire_flush_interval é fail-closed e inofensivo, enquanto um ban atrasado é exposição real — por isso bans seguem o debounce e apenas remoções esperam o intervalo de flush. O ezyshield unban manual também propaga ao edge na cadência do flush (o unban local no nftables é imediato).

notify

yaml
notify:
  rate_limit_per_minute: 5       # padrão — teto de notificações por minuto
  dedup_window_sec: 600          # padrão — alertas idênticos são colapsados

  telegram:
    bot_token: env:EZYSHIELD_TELEGRAM_TOKEN
    chat_ids: ["123456789"]
    severity: [warn, critical]   # filtro opcional: info | warn | critical

  email:
    host: smtp.example.com
    port: 587
    username: alerts@example.com
    password: env:EZYSHIELD_SMTP_PASSWORD
    tls: starttls                # starttls (padrão) | tls | none
    from: alerts@example.com
    to: [admin@example.com]

  slack:
    webhook_url: env:EZYSHIELD_SLACK_WEBHOOK
    channel: "#security"         # override opcional

  discord:
    webhook_url: env:EZYSHIELD_DISCORD_WEBHOOK

  webhook:
    url: env:EZYSHIELD_WEBHOOK_URL
    headers:
      Authorization: env:EZYSHIELD_WEBHOOK_TOKEN   # o valor precisa ser uma referência env: completa

Campos compartilhados: rate_limit_per_minute (padrão 5) e dedup_window_sec (padrão 600) protegem contra tempestades de notificação. Todo canal aceita uma lista severity opcional (info | warn | critical).

Campos do tipo segredo (bot_token, password, webhook_url, o url do webhook) só aceitam referências env:VARNAME — valores inline são rejeitados no carregamento. Eles também são obrigatórios no seu canal: um bloco telegram sem bot_token, um email sem password, ou um slack/discord/webhook sem sua URL falha na validação (o daemon os resolve na inicialização). Os valores dos headers do webhook são enviados literalmente, a menos que o valor inteiro seja uma referência env:, que é resolvida.

O tls: starttls do email falha fechado: se o servidor SMTP não anunciar STARTTLS (ou um proxy que remove capacidades o esconder), o envio dá erro em vez de silenciosamente cair para texto puro. Defina tls: none explicitamente se realmente pretende enviar sem criptografia.

ai

Opcional — sem o bloco ai, o rule engine determinístico cuida de tudo.

yaml
# Provedor único
ai:
  provider: anthropic            # anthropic | openai | ollama
  model: claude-haiku-4-5-20251001
  api_key: env:ANTHROPIC_API_KEY
  ambiguous_band: [30, 69]       # scores nesta faixa consultam a IA (mantenha high < ban_threshold)
  token_budget_daily: 50000      # teto diário rígido; além dele o rule engine assume
  cache_ttl: 15m                 # cache de vereditos idênticos (padrão 15m)
yaml
# Ou failover multi-provedor
ai:
  providers:
    - name: anthropic
      priority: 1
      model: claude-haiku-4-5-20251001
      api_key: env:ANTHROPIC_API_KEY
    - name: ollama
      priority: 2
      model: llama3
      endpoint: http://localhost:11434
CampoDescrição
provideranthropic | openai | ollama (forma de provedor único)
modelnome do modelo
api_keyreferência env:VARNAME (nunca inline)
endpointURL base apenas para o provedor ollama (padrão http://localhost:11434). Os provedores anthropic e openai a ignoram e sempre chamam suas APIs oficiais (https://api.anthropic.com, https://api.openai.com) — não há override de endpoint compatível com OpenAI. Mesmo comportamento nas formas de provedor único e de failover providers.
ambiguous_band[low, high] — apenas scores dentro da faixa consultam a IA. Omitida (ou [0, 0]) assume [30, 69]; qualquer outra faixa com low >= high ou valores fora de 0–100 é rejeitada no carregamento. Mantenha high abaixo do ban_threshold da policy: um score no limiar ou acima já decidiu o ban só com as regras, então o daemon nunca consulta a IA para ele — uma faixa que invade o limiar apenas dispara um aviso no start e no validate
token_budget_dailyteto diário de tokens; quando esgotado, as decisões voltam para as rules
cache_ttlduração do cache de vereditos; omitido ou 0 significa o padrão de 15m (o cache não pode ser desativado — é o segundo freio contra consultas repetidas para o mesmo comportamento). As entradas são indexadas pela assinatura de comportamento (contagem de kinds + janela), não pelo IP — padrões de ataque idênticos de IPs diferentes reutilizam um veredito; num hit o veredito em cache é redirecionado para o IP em avaliação. Vereditos clampados por allowlist nunca são cacheados
providerslista de failover multi-provedor (name, priority, model, api_key, endpoint, token_budget_daily); tem precedência sobre os campos de provedor único

O veredito da IA é sempre consultivo: validado por schema, limitado pela policy e nunca capaz de banir um IP da allowlist.

enrich (GeoIP/ASN)

Enriquecimento GeoIP/ASN — habilita block_countries / block_asns no policy e as colunas de país/ASN em list e report. Opcional: sem a seção enrich: o daemon roda normalmente com enriquecimento vazio (sem país/ASN em lugar nenhum, e essas chaves de policy nunca casam).

CampoDescrição
db_pathcaminho do GeoLite2-Country.mmdb
asn_pathcaminho do GeoLite2-ASN.mmdb
auto_updateo daemon baixa e atualiza os bancos sozinho (semanalmente)
license_keyreferência env:VARNAME para uma license key da MaxMind — obrigatória com auto_update: true; valores inline são rejeitados

O caminho mais fácil é o wizard, que conduz por tudo isso:

bash
sudo ezyshield config enrich maxmind
sudo systemctl restart ezyshield

De onde vêm os bancos. O EzyShield usa os bancos gratuitos GeoLite2 da MaxMind, que exigem uma conta (gratuita): cadastre-se e gere uma license key em Manage License Keys. Com auto_update: true o daemon baixa os dois bancos sozinho no startup quando os arquivos estão ausentes e os atualiza semanalmente — você nunca manuseia os arquivos:

yaml
enrich:
  db_path: /var/lib/ezyshield/GeoLite2-Country.mmdb
  asn_path: /var/lib/ezyshield/GeoLite2-ASN.mmdb
  auto_update: true
  license_key: env:MAXMIND_LICENSE_KEY

A chave é um segredo como qualquer outro: coloque MAXMIND_LICENSE_KEY=... em /etc/ezyshield/.env (modo 0600 — o wizard faz isso por você) e referencie como env:MAXMIND_LICENSE_KEY. Ela só é usada na URL de download e nunca é logada.

Alternativa manual. Com auto_update: false nenhuma chave é necessária em runtime: baixe GeoLite2-Country.mmdb e GeoLite2-ASN.mmdb da sua conta MaxMind (ou espelhe de um host que já os tenha) e coloque nos caminhos configurados. Arquivos ausentes ou ilegíveis não são erro — o daemon loga um aviso e roda com enriquecimento vazio até eles aparecerem.

dashboard

CampoPadrãoDescrição
addr127.0.0.1:9090Endereço de bind — somente loopback; binds fora do loopback são recusados no startup
auth_db_path<data_dir>/dashboard.dbBanco de autenticação do dashboard

Exemplo mínimo

yaml
data_dir: /var/lib/ezyshield

collectors:
  - kind: journald
    unit: ssh

enforce:
  nftables: {}

Segredos

Todo campo de segredo recebe uma referência env:VARNAME, resolvida pelo daemon (ezyshield run) a partir do ambiente dele. Os wizards gravam os valores em /etc/ezyshield/.env (modo 0600), que a unit do systemd carrega via EnvironmentFile=. Segredos nunca aparecem no config.yaml, em logs ou em mensagens de erro.

Isso também vale na direção inversa: se um valor colado em um campo não-secreto (provider, model, endpoint, ...) parecer uma credencial — um prefixo de chave conhecido como sk-, ou um token longo de alta entropia — o config é rejeitado no carregamento com um erro que nomeia o campo mas nunca imprime o valor. Os headers de webhook são a única exceção (valores crus são legais ali e são redigidos no config show).

Validação

bash
sudo ezyshield config validate   # schema estrito + constraints, número exato da linha nos erros
sudo ezyshield doctor            # checagem do ambiente (arquivos, permissões, sockets)
sudo ezyshield test enforcer all # exercita os backends de enforcement de verdade
sudo ezyshield test notifier all # envia uma notificação de teste para cada canal