Changelog

[2.65.1] - 2026-10-21

Corrigido

  • --for-container nao desempatava quando dois Traefik veem o mesmo app (os dois com o provider do Docker e os mesmos rotulos): devolvia traefik_ambiguous sempre. Agora desempata pelo alcance de rede: primeiro o Traefik numa rede do Docker em comum com o app, depois o da rede do host; um Traefik que nao alcanca o app nao conta. Continua traefik_ambiguous so no empate real.

[2.65.0] - 2026-10-21

Adicionado

  • Escolha do Traefik por comando: --traefik <container> e --for-container <app> em traefik-accesslog, traefik-ratelimit e traefik-edge. O --for-container acha o Traefik que roteia para o app pela API de cada Traefik.
  • Mais de um Traefik e nenhuma indicacao: recusa com failure.code = traefik_ambiguous e uma sugestao por instancia, em vez de escolher. Indicacao que nao resolve: traefik_not_found, container_not_routed ou container_not_found. Com um Traefik so, nada muda.
  • Traefik configurado so por argumentos: o throttle L7 funciona (file-provider e API lidos dos argumentos), e as falhas trazem as flags a acrescentar. No traefik-accesslog o resultado continua manual, com as flags exatas que faltam (add_cmd_args, add_cmd_args_stdout, add_cmd_args_file).

[2.64.1] - 2026-10-21

Corrigido

  • Throttle aceitava o IP de uma sessao SSH ativa quando rodava fora dela (por um console ou agente, sem $SSH_CONNECTION), e o IP do proprio servidor. A protecao considera todas as sessoes SSH ativas do host e os enderecos das interfaces. failure.code = ip_protected ganha reason_code: private_range, ssh_session_active ou own_server_ip.

[2.64.0] - 2026-10-21

Adicionado

  • Access log em Traefik gerenciado por orquestrador, pelo proprio orquestrador: quando o traefik.yml vem de um template, o traefik-accesslog --enable pede ao orquestrador o redeploy com o access log ligado (e o filtro de status, 400-599 ou all) em vez de recusar. Confere que o deploy ficou pronto, cuida do logrotate e da permissao no diretorio montado e confere o arquivo de log. Se o novo Traefik falha, o orquestrador devolve o anterior; o JSON diz se houve volta (deployally_failed_rolled_back / deployally_failed_no_rollback) e o campo deployally traz o comando e o resultado.
  • Alerta traefik_redeploy_failed: warn quando o Traefik anterior voltou, crit quando nao houve volta.

[2.63.0] - 2026-10-21

Adicionado

  • Mitigação por IP com prazo, em duas camadas, para limitar uma origem de tráfego sem banir. Ver mitigations.
    • L4 (firewall), rate-limit throttle <ip> --tier light|strict --ttl: conexões TCP novas do IP, na INPUT e nas portas publicadas de container; o kernel expira o prazo, e os sets voltam no boot.
    • L7 (Traefik), traefik-ratelimit throttle <ip> --router <r> --rate N/s --ttl: requisições do IP num router, com 429 acima da taxa, sem reiniciar o Traefik. Um router-sombra mantém o serviço, o tls e os middlewares do router original.
    • mitigations list junta as duas camadas num formato só; mitigations sweep (por timer) expira as L7.
    • JSON com failure {code, reason, suggestions[]}.
  • Auto-ban: os DROPs do throttle L4 contam com peso 1/3 no sinal iptables_drops; o 429 do L7 não conta.

[2.62.0] - 2026-10-21

Adicionado

  • traefik-accesslog --enable --to stdout: liga o access log sem montar diretório, para Traefik criado por docker run ou por outra ferramenta (sem docker-compose). O bloco vai sem filePath, o Traefik só reinicia (não é recriado) e o Docker guarda o log no host (driver json-file). Recusa sem gravar se o container usa outro driver de log; avisa se o log do container não tem max-size. JSON: destination e docker_log.
  • Auto-ban lê o access log do stdout: o sinal traefik_http ganha source: auto|file|docker (padrão auto: o arquivo, se existir; senão o log do Docker do container do Traefik) e container. Entende a linha no envelope do Docker e ignora o log próprio do Traefik.
  • traefik-accesslog --enable --status <all|faixas>: o que o access log registra. all tira o filtro (inclui 2xx); faixas e códigos como 200-299,400-599. Ao ligar, o padrão continua 400-599; num bloco existente só muda quando passado. Filtro por caminho não existe no access log do Traefik: para isso, ligue o log só nos routers que interessam (observability.accessLogs, Traefik ≥ 3.1).
  • Motivo e saídas de toda falha: o JSON do --enable/--disable ganha failure com code estável, reason e suggestions[] (id, title, detail, command, available), para um console mostrar ao operador por que falhou e o que fazer. A saída humana mostra as mesmas sugestões.
  • IMPLANT_TRAEFIK_CONTAINER: escolhe o container quando o host tem mais de um Traefik.

Alterado

  • Traefik cuja config é gerada por um orquestrador (o traefik.yml vem de um template): o implant não edita mais esse arquivo, nem no --enable nem no fix --auto, porque o próximo deploy desfaria a mudança. Responde com o motivo e a sugestão de ligar o access log pelo próprio orquestrador.
  • Traefik que não volta depois do --enable (instalação): o traefik.yml volta ao original e o Traefik é reiniciado com ele.

[2.61.2] - 2026-10-21

Corrigido

  • traefik-accesslog --enable deixava a configuração pela metade em Traefik sem mount de log que não vem de docker-compose (orquestrador com manifesto proprio, docker run). Gravava o bloco accessLog no traefik.yml, criava o logrotate e o /var/log/traefik, falhava ao montar o volume e terminava com "✓ Configuração do access log foi aplicada", sem reiniciar o Traefik: a mudança ficava pendente para o próximo restart, com o log indo para dentro do container (sem rotação, perdido a cada recriação). Agora a impossibilidade é conferida antes de qualquer gravação: nada é alterado, o comando sai 1 com o motivo e o JSON (inclusive o --dry-run) vem result: failed com errors. Se o volume ou o restart falharem depois de gravar (caminho do compose), o traefik.yml volta ao original e o logrotate criado na execução é removido (restored_original).
  • Traefik criado por orquestrador com manifesto proprio aparecia como "Manual" no status. Agora e reconhecido pelo manifesto, e o implant nao acrescenta volume por fora dele (o mount de log tem de vir do proprio orquestrador). Saiu um caminho antigo que editava um manifesto de formato aposentado e reiniciava o container sem o volume.

Adicionado

  • --enable confere o resultado depois do restart: o Traefik ficou de pé e o arquivo do access log existe no host. JSON: traefik_up, log_file, log_written; a saída humana diz as duas coisas em vez do "habilitado com sucesso" incondicional.

[2.61.1] - 2026-10-18

Adicionado

  • traefik-accesslog --enable|--disable --yes (-y): aplica sem perguntar, para task sem TTY (console de gerenciamento, que mostra o plano do --dry-run e confirma na própria tela). Sem a flag, sem TTY a confirmação continua recusada e nada muda. --yes com --fix-perms é recusado (ele nunca pergunta). Implant anterior sai 2 (clap) com --yes.
  • JSON do --enable/--disable em envelopes próprios, traefik-accesslog.enable e traefik-accesslog.disable (antes traefik-accesslog, que só repetia as flags), no --dry-run e no apply. data: dry_run, result (applied | unchanged | dry_run | cancelled | failed | manual | unsupported | not_found), mode (install: grava o bloco do implant; complete: completa o bloco do operador; remove: --disable), container, config_path, added[] (key, value), replaced[] (key, from, to: o que o --force troca), block (YAML do install), adds_log_volume, restart_planned, restarted, backup, restored_original (o Traefik não voltou e o original foi restaurado), traefik_up, logrotate (action: created | would_create | exists | covered | unavailable | failed | none; path, pattern, signal_container), perms (mesmo formato do --fix-perms), warnings[] e errors[]. Falha: success: false, error com os errors e exit 1. Com -o json o stdout é só o envelope (o texto humano vai para o stderr). Com a query no plano do bloco (added), o aviso "grava a query" não se repete em warnings. O status do access log continua nos itens Access log do Traefik: … do implant check -o json.

[2.61.0] - 2026-10-18

Adicionado

  • `implant alerts`: alertas por Telegram ou webhook HTTPS, configurados pelo operador no próprio servidor. Canais nomeados em [alerts.channels.<nome>] (type = "telegram" com chat_id, ou type = "webhook") e subscrições em [[alerts.notify]] (on, channel, template com {{::hostname}}, {{::event}}, {{::target}}, {{::severity}}, {{::title}}, {{::detail}}, {{::ts}}) no config.toml. Credenciais (bot_token, url, authorization) só em alerts.secrets, ao lado do config.toml, 0600 root: arquivo com permissão aberta, de outro dono ou symlink não é lido; credencial posta no config.toml é recusada. Subcomandos status, channel add telegram|webhook (token/URL por --token-stdin/--token-env, --url-stdin/--url-env, --auth-env; nunca por argumento; --replace para rotacionar), channel remove, channel list (sem mostrar segredo), subscribe, unsubscribe e test --channel (mensagem de teste). Edição do config.toml preserva comentários; --dry-run em todos.
  • Eventos: ban_permanent (auto-ban aplicou ban permanente), threat_crit (seção do threat-status entrou em crit, quando o snapshot é gravado), lockdown_created (lockdown create, ssh-service disable), emergency_mode_enabled, firewall_boot_fallback e firewall_install_reverted (firewall-restore sem confirmação), feed_kept_previous e feed_refused (feed-block falhando).
  • Entrega best-effort: prazo por envio (timeout_seconds, padrão 5 s) e no máximo 20 s por chamada, sem retry; falha vira registro em /var/lib/implant/alerts-events.jsonl e nunca falha o comando ou o daemon que gerou o evento. Dedupe por evento + alvo (dedupe_window_seconds, padrão 1 h) e teto por hora (max_per_hour, padrão 20). Só HTTPS, sem seguir redirect; mensagens de erro passam por filtro que tira valores de credencial.
  • As regras do implant voltam no boot sem o netfilter-persistent. Instalar o UFW remove iptables-persistent/netfilter-persistent no apt, e até aqui só os ipsets voltavam no reboot. A unit implant-firewall-restore, depois do ipset restore, passa a rodar implant firewall-restore boot-rules, que reaplica a partir dos states: o rate-limit pelo mesmo caminho do rate-limit reconcile, e os ganchos do feed-block e do geo-block sobre os sets restaurados. Com o netfilter-persistent carregando o rules.v4 (ferramenta, plugin, serviço habilitado e arquivo), nada é reaplicado: uma fonte só. Rate-limit suspenso (revert --keep-state, emergency-mode) não volta; geo-block em allowlist sem o set restaurado fica sem gancho para não trancar o host.
  • Ordem com o UFW: a unit roda After=ufw.service (com MANAGE_BUILTINS=yes o ufw-init esvazia a tabela filter inteira) e os ganchos entram na posição calculada, acima das chains do UFW nas duas ordens de subida. Com o Docker instalado, a DOCKER-USER é criada antes dele para os ganchos entrarem nela.
  • Fail-open: erro num componente não para os outros nem o boot (ExecStart=-, teto de 120 s, o comando sai 0). O resultado fica em /var/lib/implant/firewall-restore/boot-rules.json e, em falha, vira o evento boot_rules_failed na guarda do firewall-restore. A guarda de boot é armada depois da reaplicação; o fallback dela esvazia os sets de bloqueio que esses ganchos leem.
  • firewall-restore status ganha rules_mechanism (netfilter-persistent | implant-firewall-restore | null), boot_rules (último resultado) e boot_rules_failed. O check "Boot-survival do firewall", o ban/ban test (persisted) e o firewall audit (persistence.rules) aceitam a reaplicação como mecanismo; o check reprova se a reaplicação deste boot falhou, e o audit avisa que DROP cru e policy DROP própria não voltam por ela.
  • A unit muda (up_to_date: false nos hosts com a da 2.51–2.60). O self-update a troca pelo caminho de sempre: firewall-restore ensure → rate-limit reconcile, com a guarda.

Corrigido

  • check reprovava Boot-survival até o próximo boot mesmo com as regras já refeitas. Depois de um boot em que a reaplicação falhou (fail-open), o rate-limit reconcile (ou feed-block apply, geo-block apply) refazia as regras, mas o check seguia apontando a falha do boot. Agora o passo daquele componente vira recovered no boot-rules.json e só o que continua falho reprova.
  • Alerta que falhava ao enviar entrava no dedupe. Um envio que estourava o prazo contava na janela e no teto por hora, e a repetição do mesmo evento saía como duplicate: o alerta se perdia pela janela inteira (1 h por padrão). Agora só o envio entregue a pelo menos um canal conta.
  • O arquivo de boot não grava mais as regras do Docker. O save do implant (rate-limit, feed-block, isenção da VPN no rate-limit e implant iptables save) era netfilter-persistent save, que levava o iptables-save inteiro: container ou rede removidos depois ficavam no rules.v4 e voltavam a cada boot (DNAT para IP de container que não existe mais, MASQUERADE de rede apagada, DOCKER-ISOLATION-*). Agora o implant grava rules.v4|v6 sem as chains DOCKER/DOCKER-*, sem os saltos para elas e sem as regras do Docker na FORWARD, na POSTROUTING (nat) e na PREROUTING (raw). A DOCKER-USER (onde moram os ganchos do implant) e o salto da FORWARD para ela ficam, assim como tudo do operador. Dump sem a tabela filter nunca vira arquivo vazio. Provado com iptables-save reais (Docker ≤ 27 e 28/29).
  • traefik-accesslog --enable não acrescentava addInternals a um access log que já existia. Respondia "Access log já está configurado" e não mudava nada; desde a 2.51 a chave só entrava quando o log era ligado do zero. Sem ela o Traefik 3 não registra o 403 da guarda do traefik-edge (noop@internal) e o sinal traefik_http do auto-ban não vê o scanner. Agora, com Traefik ≥ 3 e bloco accessLog sem a chave, o --enable acrescenta addInternals: true ao bloco do operador (edição por linha, conferida pelo parser: comentários, ordem e os demais campos ficam), mostra o plano no --dry-run, faz backup e reinicia o Traefik conferindo que ele voltou (se não voltar, restaura o original). Quando o Traefik aceita o corte da query, ele vai na mesma gravação: um restart só. Traefik 2 nunca recebe a chave; versão não identificada: não grava e avisa; addInternals: false escrito pelo operador só muda com o novo --force. Config por linha de comando ou TOML: o implant aponta a flag/chave a acrescentar. O check ganha o item Access log do Traefik: addInternals (Fail com o comando implant traefik-accesslog --enable) e o status do traefik-accesslog mostra a linha addInternals.
  • A rotação do access log mandava o USR1 para o container errado. O logrotate instalado (/etc/logrotate.d/traefik) tinha docker kill --signal=USR1 traefik com o nome fixo; em host cujo container se chama outro (ex. proxy) o Traefik não reabria o arquivo, seguia escrevendo no rotacionado, o access.log novo ficava vazio (o auto-ban deixava de ver) e a rotação seguinte comprimia o arquivo em uso. Agora o logrotate nasce com o nome do container detectado, e um logrotate existente com outro nome é corrigido pelo --enable, --fix-perms e fix --auto (backup, só o nome muda, sem restart do Traefik), como já acontecia com o create 0644. O check ganha o item Access log do Traefik: rotação (logrotate ausente ou USR1 para outro container); o status aponta o nome errado. JSON do --fix-perms: logrotate.signal_before, signal_after, signal_expected.
  • Access log em volume nomeado do Docker ficava sem rotação. O logrotate do implant só cobria /var/log/traefik/*.log do host; quando o /var/log/traefik do container é um volume nomeado (/var/lib/docker/volumes/<nome>/_data), o log crescia sem limite e com o modo antigo. Agora o caminho no host vem dos mounts do container (docker inspect, bind ou volume nomeado, o mount mais específico que cobre o filePath): o logrotate novo nasce para esse diretório, o existente com o padrão antigo /var/log/traefik/*.log passa a apontar para ele (padrão escrito pelo operador não é tocado), e o ajuste de permissão 0640 vale para lá. O --enable com o access log já ligado instala o logrotate que faltar (se nenhum outro arquivo de /etc/logrotate.d já cobrir o diretório). Log sem mount no host (só dentro do container) e log no stdout aparecem no status e no item Access log do Traefik: rotação do check; o status mostra o arquivo no host e avisa quando o auto-ban lê outro caminho. JSON do --fix-perms: location, logrotate.patterns_before, patterns_after, covers_dir; dir.path passa a ser o diretório real.
  • Access log em texto passava despercebido. O leitor do sinal traefik_http do auto-ban só entende JSON (linha que não é JSON é descartada), e um bloco accessLog sem format grava common (texto): o auto-ban não via nenhuma linha. O --enable em bloco existente sem format acrescenta format: json (mesma edição, plano, dry-run, backup e restart do addInternals, numa gravação só); format explícito diferente (common, genericCLF) não é trocado sem --force — o --enable avisa. O check ganha o item Access log do Traefik: formato e o status a linha Formato; config por linha de comando ou TOML recebe a flag/chave a acrescentar.

[2.60.2] - 2026-10-17

Corrigido

  • --dry-run depois do subcomando passa a valer em iptables, fail2ban, sysctl, harden-network, swap, limits, logrotate, docker-limits, lockdown, ssh-service, emergency-mode, rate-limit e ban. Antes, algo como rate-limit unwhitelist-ip --ip X --dry-run dava "unexpected argument"; agora o --dry-run vale em qualquer posição, como no resto do CLI.
  • self-update: o campo channel do JSON vem da URL do download (rc para o canal de release candidates); antes dizia sempre stable.

[2.60.1] - 2026-10-17

Corrigido

  • rate-limit unwhitelist-ip dizia "IP removido" e o IP continuava isento. Agora tira o IP do set do kernel e do arquivo de sets do boot, e é idempotente. IP que também vem de outra fonte de whitelist continua isento, e a saída diz de onde vem.
  • unban e ban test divergiam quando o IP estava no feed externo. Os dois passam a usar o mesmo cálculo (still_blocked_by / blocked_by).
  • ban test não contava o UFW: um deny que vem antes dos allow agora conta como bloqueio; atrás de um allow, o motivo aparece em warnings.
  • feed-block com -o json passa a emitir o envelope de erro quando falha.

[2.60.0] - 2026-10-17

Adicionado

  • implant firewall audit (somente leitura): policy da INPUT/FORWARD em v4 e v6, blocklist do implant ausente da DOCKER-USER, ordem dos ganchos do implant em relação ao UFW, regras diretas fora das chains conhecidas, DROP/REJECT de IP solto fora do ipset, LOG sem limite e persistência no boot de regras e ipsets. Achados {id, severity: ok|warn|crit, title, detail, fix}; JSON firewall.audit. Exit 0 sempre; --strict sai 1 se houver crit.
  • implant feed-block (opt-in): feed externo de blocklist (padrão Spamhaus DROP v4 e v6) nos ipsets IMPLANT_FEED_BLOCK[6], julgados na INPUT e na DOCKER-USER, com timer semanal. Precedência dos ganchos passa a ser whitelist > blocklist > feed-block > geo-block > rate-limit. Feed ruim é recusado inteiro e o conteúdo anterior fica; troca por ipset swap; anti-tranca filtra a sessão SSH, a whitelist e faixas privadas; intervalo mínimo de 1 h entre downloads sem --force. ban test mostra feed/feed_entry/feed_enforced; unban não tira do feed e indica a whitelist; threat-status ganha os DROPs do feed.
  • implant traefik-ratelimit (opt-in): middleware de rate-limit L7 por IP no file-provider do Traefik (implant-ratelimit@file, opcional inFlightReq). Não mexe em router: o operador anexa o middleware. O revert recusa se algum router o usa; revert --neutral mantém o nome sem limite.
  • emergency-mode: features feed-block e traefik-ratelimit. O feed-block sai por revert e volta por apply; o traefik-ratelimit sai por revert --neutral (nunca remove middleware em uso) e volta por apply sem flags.

Corrigido

  • firewall unblock-ip dizia "IP desbloqueado" sem remover nada. O ufw delete de regra inexistente sai com exit 0 e só avisa "Could not delete non-existent rule". Agora isso conta como "não havia regra": aviso "nada a remover", exit 0 (como o unban de alvo não banido) e removed: false no JSON. O JSON firewall.unblock-ip ganha removed, rules (especificações removidas; em dry-run, as que sairiam) e dry_run. Falha real do ufw continua saindo com erro.
  • Nenhum comando removia o firewall deny --port P --from X. O unblock-ip X passa a remover também as regras deny from X to any port P [proto Y], casadas pelo endereço (IPv6 em qualquer grafia; 203.0.113.90 não casa com 203.0.113.9). Regras de destino específico, de saída e allow ficam. O implant unban X enxerga e remove as mesmas regras (antes respondia "não estava banido").
  • ban test não dizia se o bloqueio sobrevive a reboot. Agora diz, com o mesmo cálculo do ban: linha "sobrevive a reboot: sim/NÃO" e campo persisted no JSON ban.test (null quando o alvo não está na blocklist), com o motivo em warnings quando não sobrevive.

Documentação

  • Páginas novas: feed-block e traefik-ratelimit.
  • firewall documenta o firewall audit.
  • ban, emergency-mode, rate-limit e geo-block atualizados com o feed-block na precedência (whitelist > blocklist > feed-block > geo-block > rate-limit) e as novas features do emergency-mode.
  • Os comandos feed-block, traefik-ratelimit e firewall audit chegaram na v2.59.0; as entradas correspondentes foram acrescentadas abaixo.

[2.59.0] - 2026-10-17

Adicionado

  • self-update leva a unit implant-firewall-restore para o servidor. Depois de trocar o binário, executa o binário novo com firewall-restore ensure, com teto de 300 s. Falha nesse passo nunca faz o self-update falhar: vira aviso e o campo post_update do JSON (status: ok, failed, planned ou disabled). Opt-out: --no-post-update ou IMPLANT_NO_POST_UPDATE=1. Vale a partir do update feito por esta versão: quem atualiza a partir da 2.58.0 ou anterior só o ganha no update seguinte (ou com firewall-restore ensure à mão).
  • implant firewall-restore ensure: roda o rate-limit reconcile só quando ele instalaria a unit; senão não faz nada (o rate-limit não é reaplicado a cada atualização). Idempotente, seguro à mão e respeita --dry-run. Novos boot_unit_action: suspended e unsupported_schema.
  • fail2ban: use-ipset-action --enable / --disable (opt-in). Os bans das jails do implant vão direto para a blocklist central, que vale para o host e para os containers, sem chains f2b-* paralelas. Cria só dois arquivos com a marca do implant, valida com fail2ban-client -t e desfaz se reprovado; --disable volta exatamente à action anterior. Timeout = bantime (permanente vira entrada sem timeout); whitelist e bans de outra origem têm precedência. fail2ban status ganha ipset_action.
  • emergency-mode: feature fail2ban-ipset, desligada antes do rate-limit: durante a emergência o fail2ban volta à action própria e o SSH continua protegido.
  • implant reset --fail2ban também apaga os arquivos da action do implant.
  • Documentada a divisão de papéis: detecção HTTP no auto-ban (traefik_http), fail2ban para o SSH.

Corrigido

  • geo-block apply --dry-run aplicava de verdade. Agora apply, update, revert, add-country e remove-country simulam sem gravar nem aplicar nada (o apply ainda roda a checagem anti-tranca). firewall allow/deny/block-ip/unblock-ip/rate-limit/enable/disable também passam a respeitar o --dry-run, e auto-ban scan passa a aceitá-lo. Subcomandos que mudam o host e não sabem simular recusam o --dry-run em vez de executar: geo-block service install/uninstall/start/stop, auto-ban allow/unallow/offenses-clear/service, firewall-restore (exceto status e ensure), capture prune/service e audit apply/revert/service.
  • Allowlist do geo-block recusado pela anti-tranca ficava gravado e o update do timer o aplicava depois, sem proteção. Agora o config só é gravado depois da checagem, e o update só repopula config já validado; o resto é recusado (update_refused no status). O status mostra validated_at/validated_by.
  • firewall block-ip não bloqueava as portas já liberadas. O DENY ia para o fim da lista, depois dos allow. Agora entra no topo da família do endereço (v4 e v6); vale também para firewall deny --port P --from X.
  • Boot-survival aprovava sem as regras voltarem no boot. Instalar o UFW remove iptables-persistent/netfilter-persistent no apt. O check passa a exigir também a persistência das regras e reprova dizendo o que falta; o ban só responde "sobrevive a reboot: sim" com sets e regras; o rate-limit avisa e responde rules_persist_warning em vez de erro sem causa.

[2.58.0] - 2026-10-17

Alterado

  • rate-limit reconcile instala sozinho a unit implant-firewall-restore, com guarda. Se a unit falta, é a antiga (≤ 2.50.0, que não rodava no boot) ou está desabilitada, o reconcile instala como firewall-restore install --confirm-timeout 10 --boot-guard. O agente de controle remoto confirma sozinho; sem confirmação, a guarda desfaz (fail-open). Instalação automática desfeita não é tentada de novo: o reconcile só aponta. rate-limit apply e check --auto-fix continuam só apontando. JSON do reconcile ganha boot_unit_action, boot_unit_plan e boot_unit_install.

[2.57.0] - 2026-10-16

Adicionado

  • wireguard: várias redes por servidor. implant -o json wireguard networks (somente leitura) lista todas as redes do host — confs e interfaces no ar sem conf —, com managed_by lido do cabeçalho do conf, role (hub/client), peers e estado do firewall. Nenhuma saída traz chave privada.
  • wireguard: remove --iface X [--force] remove a rede inteira: para a unit, fecha a porta, tira a isenção do rate-limit, apaga o drop-in de boot e guarda o conf em X.conf.removed-<data> (600, nunca apaga). Rede inexistente dá absent. Sem --force, recusa a wg0 e redes que o implant não criou.
  • wireguard / rate-limit: a porta UDP de cada rede fica isenta do rate-limit, presa à rede (wireguard-<iface>). init põe, remove tira, disable mantém. Blocklist e geo-block continuam valendo.

Corrigido

  • wireguard: detecção do firewall única em init, status, disable e remove. Num servidor com INPUT aberto, o init reportava none e o disable da mesma rede reportava iptables.

[2.56.1] - 2026-10-16

Alterado

  • traefik-edge: o --help do --with-404 e o audit descrevem o catch-all como ele é: pega só host sem router e responde 403; host com serviço segue com o 404 do próprio serviço. Comportamento inalterado.

[2.56.0] - 2026-10-16

Adicionado

  • traefik-edge: lista de bloqueio própria. O operador acrescenta caminhos completos e literais à guarda (mesmo 403, Traefik 2 e 3), em [traefik_edge] bloquear do config.toml ou por --configure --bloquear <entrada> / --desbloquear <entrada>. Barra inicial = a partir da raiz; sem barra inicial = em qualquer pasta; barra final = a pasta e tudo dentro. Sem curingas nem regex; entrada inválida dá erro e nada é gravado. O audit lista as entradas e testa uma delas.

Alterado

  • config.toml parcial passa a carregar: seções ausentes caem no padrão (antes o arquivo precisava de todas as seções).
  • traefik-edge: editar a lista não remove o catch-all de hosts sem router já instalado; o cabeçalho do arquivo de guarda não cita mais referência interna.

[2.55.1] - 2026-10-16

Corrigido

  • docker-limits / check: o daemon.json passa pelo dockerd --validate antes de ser gravado. Se o dockerd recusar o resultado (ex.: chave desconhecida, que o impede de subir), nada é gravado e o Docker não é reiniciado; a mensagem traz o motivo. Vale para docker-limits apply/revert, reset --docker-limits e o check --auto-fix. Docker sem --validate (anterior ao 23) segue como antes, com aviso.
  • limits / docker-limits: status sem ✗ depois do revert. A ausência do bloco é o resultado esperado e não aparece mais como comando falho.

[2.55.0] - 2026-10-16

Corrigido

  • docker-limits: o daemon.json do usuário é preservado. apply e revert (e reset --docker-limits, check --auto-fix) fazem o merge sem jq e mexem só nas chaves do implant (default-ulimits, live-restore, userland-proxy; log-driver/log-opts só quando não há log configurado). O valor anterior de cada chave fica em /var/lib/implant/docker-limits.json, e o revert devolve cada uma a esse valor se ela ainda tem o valor do implant. JSON inválido dá erro sem tocar no arquivo, e o Docker só é reiniciado se o arquivo mudou. Antes, sem jq, o apply substituía o arquivo, e o revert o apagava inteiro.
  • limits: o revert não apaga mais até o fim do limits.conf. O bloco do implant ganhou marcador de fim (# Fim limites Implant) e o revert (e reset --limits) remove só o que está entre os marcadores. Em blocos antigos, sem o marcador de fim, remove só as linhas no formato que o implant grava.
  • logrotate: apply respeita o --dry-run (plano Faria:, campo planned no JSON). O backup da config do rsyslog saiu de /etc/logrotate.d/rsyslog.backup.implant, que o logrotate lia como configuração (duplicate log entry), e foi para /var/lib/implant/backups/logrotate/; o backup antigo é migrado no próximo apply.
  • reconfigure: --backup faz backup de verdade (ligado por padrão) em /var/lib/implant/backups/reconfigure-<timestamp>/ (0700): /etc/fstab, /etc/docker/daemon.json, config e units/env do K3s. Em dry-run, lista o que seria copiado. --no-backup desliga; a saída JSON ganha backup_dir.
  • agent: o token não aparece mais. O token e o corpo do registro chegam ao curl por variável de ambiente e o cabeçalho pelo stdin: ficam fora do --dry-run, do --verbose, das mensagens de erro e do ps. O --dry-run não para mais ao tentar ler o certificado, que só é gerado na instalação real.
  • self-update: comparação de versão exata. Antes era por substring, e a troca entre estável e RC (X.Y.Z-rc.N ↔ X.Y.Z) era recusada sem --force.
  • install node: --version perdeu o -v, que é o --verbose global.
  • sysctl, limits, logrotate e docker-limits não listam mais os subcomandos emergency-*, que apareciam no --help e só davam erro. Esses comandos não fazem parte do emergency-mode.
  • --help revisado: sem referências internas.

[2.54.0] - 2026-10-09

WireGuard

  • A porta do firewall sobrevive ao reboot. wireguard init e --adopt gravam o drop-in /etc/systemd/system/wg-quick@<if>.service.d/implant-firewall.conf (ExecStartPost que reabre a porta ao subir a interface). Antes, em host com política DROP e sem persistência de regras, a VPN subia no boot sem receber conexão. O disable continua fechando a porta.
  • Novos implant wireguard firewall open --iface <if> (idempotente, usa o ListenPort do conf, padrão 51820) e firewall status --iface <if>.
  • Corrigido: init --adopt com o drop-in já presente travava a unit em activating (start-post). O drop-in ganhou TimeoutStartSec=90.
  • ufw: a regra <porta>/udp sem comentário criada por versões anteriores à 2.53 é trocada pela regra marcada (wireguard-<if>), para que o disable consiga fechá-la. O relatório indica firewall_detail.migrated: true.

[2.53.0] - 2026-10-09

WireGuard

  • init abre a porta UDP no firewall (ufw, firewalld ou iptables com política DROP) e disable fecha. status mostra o estado do firewall.
  • A saída de init/disable ganha firewall_detail ({kind, opened[, error]}); o campo firewall (string) continua igual.

[2.52.0] - 2026-10-09

Firewall-restore com confirmação e guarda de boot

  • implant firewall-restore install --confirm-timeout N (2–360 min): instala a unit sob um lockdown com dead-man — sem implant firewall-restore confirm dentro do prazo, a instalação é desfeita automaticamente.
  • install --boot-guard [--boot-timeout N]: a cada boot cria um lockdown que, se não for confirmado, libera apenas as entradas de bloqueio do implant nos sets IMPLANT_* (whitelist, allowlist do auto-ban, regras e UFW ficam) e registra o que foi liberado por origem (ban, auto_ban, geo_block).
  • Novos subcomandos confirm, arm --boot e fallback; status ganha data.guard.

Mudado

  • A unit implant-firewall-restore só é instalada manualmente. rate-limit apply/reconcile e check --auto-fix não a instalam mais: o check reprova e aponta o install com guarda; apply e reconcile devolvem boot_restore_advice.

[2.51.0] - 2026-10-09

Corrigido

  • A unit implant-firewall-restore não rodava no boot (2.25.0–2.50.0): a ordenação gerava um ciclo e o systemd descartava o job. Após reboot os sets ficavam vazios e o host subia sem blocklist nem rate-limit, enquanto o check "Boot-survival" dava Pass. firewall-restore status ganha up_to_date; o check reprova a unit antiga; ban responde persisted: false com ela.
  • iptables restore agora substitui os ipsets pelo conteúdo do snapshot (ipset swap) em vez de mesclar — um ban posterior ao snapshot não sobra.
  • iptables restore logo após um snapshot (mesmo segundo) não restaurava nada; restaurar o 10º snapshot o apagava no prune.
  • auto-ban traefik_http com config legada (traefik_404:): path de scanner respondido com 403 não pontuava. Campo novo scanner_statuses.
  • traefik-accesslog --enable no Traefik 3 grava accessLog.addInternals: true, necessário para logar o 403 da guarda do traefik-edge.

Adicionado

  • implant iptables restore --snapshot-id <N|timestamp> (1 = mais recente) e implant iptables snapshots.

Atenção no upgrade

  • Hosts com a unit antiga seguem sem restore no boot até rodar implant firewall-restore install (ou rate-limit reconcile / check --auto-fix).
  • Com a config legada do auto-ban, scanner que recebe 403 em path de scanner passa a ser banido (4 hits em 5 min).

[2.50.0] - 2026-10-08

Traefik: access log sem credenciais

  • O access log gravava a query string completa (?token=…). Com Traefik ≥ 3.6.18 / 3.7.2 o implant configura accessLog.fields.queryParameters.defaultMode: drop (o path continua no log). Versões anteriores não aceitam a chave: o check relata Fail pedindo a atualização do Traefik.
  • Novo módulo do check "Access log do Traefik" (--modules traefik): permissão dos arquivos/logrotate e query string. implant fix --auto restringe a permissão e, quando o Traefik aceita, grava a chave (com backup), reinicia e confere que subiu — senão restaura o original.
  • Detecção do Traefik: imagens com tag (traefik:v3.7.5) e containers identificados só por label não eram encontrados.

[2.49.0] - 2026-10-08

  • implant self-update também recupera o agente de controle remoto quando ele está preso numa versão que não consegue se atualizar sozinha (binário conferido por SHA e assinatura minisign, com backup e restart agendado). O check passa a relatar esse caso.
  • wireguard: instala iproute2 junto com wireguard-tools (o wg-quick falhava em imagens mínimas).

[2.48.0] - 2026-10-07

Novo: `implant wireguard`

  • status, init [--adopt], peer add|remove|list, disable: instala e opera a VPN WireGuard do servidor e cadastra devices, devolvendo .conf e QR (SVG no JSON, ANSI no terminal). A chave privada do device só aparece na saída do peer add.
  • init com outro --endpoint só atualiza o endpoint (outcome: updated), sem reiniciar a VPN. --force recria a chave do servidor e remove todos os devices. --port não combina com --adopt.

[2.47.1] - 2026-10-07

Consolida v2.47.0 → v2.47.1.

`ssh-key --whitelist-ip`

  • Host sem UFW (ou inativo) não mostra mais "✗ Verificando UFW (falhou)": o passo aparece como pulado (⊘).
  • O resumo lista só os alvos em que o IP ficou de fato isento (fail2ban, UFW, PerSourcePenalties, rate-limit do implant).
  • Fail2ban só conta como isento quando o IP é item inteiro de ignoreip (antes 203.0.113.4 casava com 203.0.113.45).
  • OpenSSH < 9.8: PerSourcePenaltyExemptList é pulado (a diretiva não existe) em vez de quebrar o sshd_config.

[2.46.0] - 2026-10-06

Alterado

  • Precedência das regras do implant passa a ser WL > BL > GEO > RL: IP isento (whitelist/allowlist) vale acima da blocklist, na INPUT e na DOCKER-USER, v4 e v6. Instalações antigas são corrigidas no próximo rate-limit apply/reconcile ou ssh-key --whitelist-ip.

Corrigido

  • ssh-key --whitelist-ip: falha em qualquer passo aparece no resumo ("Whitelist FALHOU para N de M IP(s)") e o comando sai com código ≠ 0.
  • ban test: IP na blocklist e na whitelist não aparece mais como blocked.

[2.45.0] - 2026-10-02

  • Novo implant traefik-accesslog --fix-perms [--dry-run]: corrige só a permissão do access log (logrotate create 0640 root adm, arquivos 0640), sem prompt e sem reiniciar o Traefik. Nada que já esteja mais restrito é afrouxado.
  • traefik-accesslog --enable com o log já ligado agora também corrige as permissões.
  • --dry-run do traefik-accesslog executa as leituras de descoberta de verdade (antes concluía errado).

[2.44.1] - 2026-09-30

Corrigido

  • implant check sem --auto-fix escrevia no host (o módulo de apps baixava e tentava substituir binários). Agora o check sem --auto-fix é estritamente leitura em todos os módulos; o de apps compara a versão instalada com a publicada. --install-node sem --auto-fix é ignorado com aviso.
  • Vários checks davam Fail em host correto por procurar a marca do implant em vez do valor efetivo (limites de log do Docker, limits.conf, sysctl, SSH Hardening, Logrotate, Journald, Fail2Ban). Todos passam a medir o valor efetivo.
  • SSH Permissões: /root em 2755 é válido; a correção virou chmod go-w.
  • O Fail2Ban não sobrescreve mais um jail.local que não seja do implant: grava /etc/fail2ban/jail.d/implant.conf.

Adicionado

  • Itens SSH PasswordAuthentication e SSH PermitRootLogin (yes = Fail de risco alto; só reportam).
  • Campo risk (high|medium|low) nos itens do check.

Atenção no upgrade

  • O resultado do check muda: hosts corretos deixam de acumular Fail falso, e hosts com senha no SSH, PermitRootLogin yes ou Docker sem rate-limit ganham Fail novo.

[2.44.0] - 2026-09-29

  • implant ban <ip|cidr> (--ttl 24h, --permanent, --reason, --force, --fail2ban, --dry-run), implant unban, ban list e ban test <ip>. A resposta informa onde o bloqueio está de fato ativo (enforced, effective). Guardas contra banir o próprio IP da sessão SSH, faixas privadas, loopback e whitelist; prefixos mais amplos que /16 (v4) ou /48 (v6) exigem --force.
  • unban desfaz no ipset, no fail2ban e no UFW, e encerra o cooldown do auto-ban.

[2.43.0] - 2026-09-28

Adicionado

  • Chain IMPLANT_RL_DROP com LOG opcional e limitado por origem: implant rate-limit drop-log enable|disable|status.
  • rate-limit add --docker <porta> / remove --docker <porta>: rate-limit de porta publicada pelo Docker (via DOCKER-USER).

Corrigido

  • Sinal log_auth do auto-ban não funcionava em syslog RFC3339 (Ubuntu 25.04, Debian recentes). Agora lê RFC3339 e short-iso do journalctl; o formato antigo passa a ser interpretado em hora local. Sem kern.log, o sinal iptables_drops lê do journal.
  • harden-network não grava mais vm.overcommit_memory = 2: em hosts acima do CommitLimit resultante todo fork falhava, inclusive o SSH. O pack agora grava 0, desfazendo o valor antigo no próximo apply.
  • traefik-edge: a guarda passa a bloquear também arquivos de credencial (.credentials, .test_credentials, .htpasswd, .pgpass, .netrc, chaves SSH privadas sem extensão) em qualquer diretório. Aplicar com implant traefik-edge --configure.

Atenção no upgrade

  • O auto-ban volta a banir por log_auth (10+ falhas de senha em 5 min).
  • O LOG do drop-log é opt-in; a chain nova entra no próximo rate-limit apply/reconcile/add.

[2.42.0] - 2026-09-27

Corrigido

  • Ordem das regras do implant passa a ser calculada (não mais em posição fixa) e igual na INPUT e na DOCKER-USER, independente da ordem de aplicação.
  • Geo-block na INPUT podia não entrar, sem aviso. Agora usa a chain IMPLANT_GEO_IN e falha de instalação aborta o apply.
  • Emergency-mode perdia regras de rate-limit add e a whitelist extra ao restaurar.
  • Unban manual deixava o IP imune ao auto-ban até o TTL vencer.
  • O daemon do auto-ban passa a reiniciar sozinho quando o binário é atualizado.

Adicionado

  • implant rate-limit reconcile (reaplica a partir do state) e rate-limit revert --keep-state.
  • Item Cobertura DOCKER-USER no implant check.
  • implant iptables audit-log-rules [--fix] [--restore]: encontra e remove regras -j LOG sem limite.
  • geo-block apply em allowlist recusa se o IP da sessão SSH ficaria bloqueado, e tira snapshot antes de mexer.
  • Snapshots do implant iptables incluem IPv6.

Atenção no upgrade

  • Hosts com rate-limit aplicado antes da 2.40.0 precisam de implant rate-limit reconcile.
  • Neste upgrade, onde o auto-ban está ativo, rode uma vez systemctl restart protocolo-implant-auto-ban.

[2.41.1] - 2026-09-25

Consolida v2.41.0 → v2.41.1.

  • auto-ban: novo sinal traefik_http (sucessor de traefik_404) pontua respostas 4xx com peso maior para paths de scanner (/.env, /.git, /wp-login.php…). A config traefik_404 antiga continua aceita.
  • auto-ban: um ban ainda vigente não conta mais como ofensa nova a cada ciclo (antes um único ban do fail2ban podia virar permanente).
  • Segurança: access log do Traefik com permissão 0640 (antes 0644); traefik-accesslog --enable corrige arquivos e logrotate existentes. O status mostra a permissão e conta, sem ler valores, requisições com credencial na query.

[2.40.0] - 2026-09-06

Adicionado

  • Seção [endpoints] no config.toml (CDN, guardrail e catálogo de apps configuráveis) e [[tls_pins]] para pins SPKI próprios. Hosts sem pin usam validação WebPKI padrão.
  • Licença Apache-2.0.

Segurança

  • Assinatura minisign obrigatória para hosts sem pin embutido (em self-update e na instalação de apps). Nos domínios oficiais nada muda.
  • Guardrail recusa URL com esquema diferente de https.

Notas

  • Sem config.toml, o comportamento é idêntico ao anterior.
  • ssh-key --add-public-key-url passa a funcionar com hosts de terceiros (HTTPS obrigatório).

[2.39.1] - 2026-09-04

Consolida v2.30.0 → v2.39.1, série de correções do traefik-edge:

  • Descoberta do file-provider a partir do Traefik em execução (não do primeiro traefik.yml do host), em qualquer via que o Traefik aceita (arquivo YAML/TOML com qualquer indentação, flags, variáveis de ambiente), respeitando a regra do próprio Traefik de usar uma única fonte (arquivo > flags > env).
  • filename: (arquivo único) é reportado como risco file_provider_single_file e o --configure explica as alternativas.
  • Probe do efeito: o audit pede /.git/HEAD ao próprio Traefik e reporta probe_status; guarda presente sem 403 vira risco guard_not_effective. recent_errors traz as últimas linhas de erro relevantes do Traefik.
  • implant traefik-edge --enable-file-provider [--apply]: liga o file-provider no compose do Traefik em duas etapas (dry-run com diff e plano; --apply só com plano recente e compose inalterado), com backup e rollback automático. Com traefik.yml, o patch vai no arquivo; stacks do Portainer são encontradas.
  • Traefik 2.x anterior à 2.11 usa ipWhiteList na guarda (ipAllowList no v3).
  • --configure reinicia o Traefik quando o file-provider caiu na subida e prova de novo; flags --providers.file.* órfãs são removidas do compose.
  • config_source no audit (file | flags | env).

[2.29.0] - 2026-09-03

  • Novo implant traefik-edge: --audit (padrão, só leitura) verifica se o Traefik corta paths sensíveis (.git, .env*, .secrets*, certificados, docker-compose*, Dockerfile*, *.log, .npmrc…) e devolve risk (sensitive_paths_unguarded | no_file_provider | ok). --configure escreve um arquivo de guarda no diretório do file-provider que responde 403 antes de qualquer serviço; --with-404 opcional, idempotente, com backup e --dry-run. Sem restart quando o file-provider tem watch.

[2.28.1] - 2026-09-03

Consolida v2.28.0 → v2.28.1.

  • Novo implant audit ssh: trilha de conexões SSH (aceitas/falhas, origem, usuário, método, fingerprint) lida do journal do sshd, inclusive sshd-session (OpenSSH 9.x). --since (padrão "24 hours ago"), --limit; -o json devolve {total, accepted, failed, events[]}.
  • stdout limpo com -o json (logs vão para stderr); self-update honra -o.
  • Chave pública minisign publicada no CDN.
  • install/update de subsistema não baixa mais em /tmp (tmpfs consome RAM): usa um temporário em /opt/<nome>/ e troca o binário com rename atômico.

[2.27.1] - 2026-08-21

Consolida v2.27.0 → v2.27.1.

  • BBR + fq no hardening de kernel quando o kernel suporta (senão mantém CUBIC). O implant confere o valor efetivo no kernel após aplicar; implant sysctl status reporta rp_filter, congestion_control e routing_role.
  • ip_forward passa a considerar WireGuard (gateway VPN sem Docker recebia ip_forward=0). Hosts que roteiam usam rp_filter=2 (loose).
  • self-update --dry-run não instala mais de verdade: verifica pin, download, assinatura e versão e para antes do backup.

[2.26.0] - 2026-08-08

Removido

  • BREAKING — --notify removido de check e fix. Scripts que usam a flag falham com "unexpected argument"; basta removê-la. A saída do implant é sempre local (stdout ou --output <FILE>); o envio de relatórios para um console remoto é responsabilidade do agente de controle remoto.

Alterado

  • Nenhum IP confiável vem mais embutido no binário: use security.trusted_ips (config.toml) / --whitelist-ip. O IP da sessão SSH atual continua sendo liberado por padrão.

[2.25.3] - 2026-07-22

Consolida v2.25.0 → v2.25.3.

  • Ipsets sobrevivem ao reboot: nova unit implant-firewall-restore.service e implant firewall-restore install | uninstall | status. Snapshots do implant iptables passam a incluir os ipsets. Novo check "Boot-survival do firewall". (Ver correção da ordenação na 2.51.0.)
  • ssh-key --add-public-key aceita chaves com comentário user@host.
  • Servidor virgem: o módulo docker reconcilia ip_forward=1 na mesma rodada do fix; removido o falso "✗ Verificando ip_forward atual (falhou)".

[2.24.6] - 2026-05-23

Hotfixes consolidados v2.24.1 → v2.24.6 (4 bugs + 2 ajustes cosméticos encontrados após o release v2.24.0).

Capture

  • -Z root em tcpdump — sem isso o daemon dropava privilégios pro user tcpdump e não escrevia em /var/lib/implant/pcap/ (owned by root). service install reportava active=true mas o daemon morria 1s depois.
  • Flag --out PATH no extract (antes --output PATH, que colidia com o global --output FORMAT causando panic do clap parser). Quem tinha script: trocar --output por --out.
  • Inventory aceita .pcap{N} — tcpdump com -W adiciona sufixo de rotação (capture-YYYYMMDDHHMMSS.pcap0). Sem o fix, status/list/extract reportavam 0 arquivos mesmo com pcaps presentes.
  • Extract tolera daemon ativo — antes falhava com "truncated dump file" quando o único candidato era o arquivo sendo escrito. Agora copia para /tmp (snapshot) e aceita output ≥ 24 bytes (header pcap).

Audit

  • Parser ignora key=(null) literal que auditd emite quando evento não tem key. Antes poluía counts_by_key no threat-status com chave bogus.
  • extract_field para em 0x1d (Group Separator, usado em alguns formatos auditd) — evita gobble de campos adjacentes.

Geo-block

  • apply limpa hook do mode anterior — uninstall_hook (idempotente, ambas variações) é chamado antes de install_hook. Evita duplicata de regra no INPUT em mode switch, e o cenário "tijolar" (re-apply com ipset vazio em allowlist mode dropando tudo).

Compat

Quem está em v2.24.0–v2.24.5: atualize para v2.24.6. Não há mudança de config ou state file — só correções de comportamento.

[2.24.0] - 2026-05-23

Bundle release: implant capture + implant audit + geo-block allow-list mode.

implant capture (v2.24.0 feature 1/3)

Daemon tcpdump rotativo + extract query expressiva (combina time + IP + porta + proto). Output pcap + meta.json com port→process enrichment.

implant audit (v2.24.0 feature 2/3)

auditd STIG-light ruleset + events CLI + 9º collector no threat-status.

geo-block allow-list (v2.24.0 feature 3/3)

--mode allowlist complementa blocklist. State v2.0 backward-compat.

Emergency-mode

6 OPT_IN_FEATURES (harden-network + rate-limit + auto-ban + geo-block + capture + audit). emergency-disable desabilita tudo junto.

[2.23.0] - 2026-05-22

Geo-block — bloqueia tráfego INPUT por país via IPDeny + ipset. systemd-timer semanal para update. Whitelist (rate-limit + auto-ban) bypassa geo-block.

Added

  • implant geo-block service install/uninstall/start/stop/status/logs
  • implant geo-block apply [--countries CN,RU,KP]
  • implant geo-block revert/update
  • implant geo-block add-country/remove-country/list-countries/status
  • implant geo-block config show/path

Refator

IMPLANT_RATELIMIT_WHITELIST chain agora ACCEPT (não RETURN) + match para IMPLANT_AB_ALLOW. Whitelist real-bypassa rate-limit + geo-block.

Topologia INPUT

[1] RL_BLOCKLIST DROP [2] WHITELIST ACCEPT [3] GEO_BLOCK DROP [4] RATELIMIT

Emergency-mode

geo-block é 4º opt-in. emergency-disable desabilita 4 features juntas.

[2.22.0] - 2026-05-22

Auto-ban — daemon systemd que lê fail2ban, log_auth, iptables_drops e Traefik 404 e banha IPs ofensivos via blocklist com TTL progressivo por reincidência.

Added

  • implant auto-ban service install/uninstall/start/stop/restart/status/logs
  • implant auto-ban scan [--dry-run]
  • implant auto-ban list/events/offenses/offenses-clear
  • implant auto-ban allow/unallow/allow-list
  • implant auto-ban config show/path

Progressive backoff

1ª ofensa = base TTL. Dobra a cada reincidência. Cap em 16×. Após 5 ofensas vira permanente.

Never-ban (3 camadas)

RFC1918/loopback/link-local + IMPLANT_RL_WHITELIST + IMPLANT_AB_ALLOW.

Emergency-mode

Service entra em OPT_IN_FEATURES. emergency-disable para o service, ipsets/offenses preservados, deadman restaura.

[2.21.0] - 2026-05-21

ipset infrastructure: whitelist migrada para ipset + blocklist nova como infra para v2.22.0 auto-ban e v2.23.0+ geo-block. State schema bump 1.0→2.0 com migração explícita.

Added (7 novos subcomandos)

  • implant rate-limit migrate-to-ipset — migração v1.0→v2.0 com snapshot
  • implant rate-limit block-ip --ip X [--ttl N] — bloqueia IP com TTL opcional
  • implant rate-limit unblock-ip --ip X
  • implant rate-limit blocklist-list / blocklist-clear
  • implant rate-limit block-list-from-file --file X [--ttl N] [--yes] — bulk-import auto-detect (JSON / CSV / text)
  • implant rate-limit whitelist-list-from-file --file X [--yes]

Changed

  • Backend de whitelist: ipset (mesma CLI, performance O(1))
  • Topologia INPUT: BLOCKLIST[1] → WHITELIST[2] → RATELIMIT[3]
  • State schema_version 1.0 → 2.0

Migração obrigatória (servidores em 2.20.0)

sudo implant rate-limit migrate-to-ipset

Anti-tijolar: faz snapshot do iptables antes de tudo. Fresh installs já usam ipset automaticamente.

Emergency-disable

Preserva ipsets entre disable/restore. TTLs continuam tickando.

[2.20.0] - 2026-05-21

Novo subcomando implant threat-status — coleta read-only de métricas vivas (8 seções: conntrack, sockets, drops, fail2ban, log/auth, system, services, hardening) com severity + detection de transitions.

Added

  • Pack default: snapshot atual + grava JSONL local
  • Subcomandos: snapshots / events / clear-history
  • Flag --no-record para dry-read
  • Histórico em JSONL bounded (~1.2MB max), sem rotação

Severity por seção (≥ inclusivo)

Conntrack ≥70% warn, ≥90% crit | SYN_RECV ≥1000/≥5000 | iptables drops ≥100/min, ≥1000/min | fail2ban bans ≥10/≥100 | SSH failed 5min ≥50/≥500 | Load ≥ncpu×2 / ≥ncpu×4 | Memory ≥80% / ≥95%.

Não inclui

  • Envio dos snapshots para o console remoto (release separada)
  • Auto-ban via ipset (v2.21.0)

[2.19.0] - 2026-05-21

Novo subcomando implant rate-limit — connection limits + rate limits por porta via iptables.

Added

  • Pack default: SSH 20conn+5/min, HTTP 50+200/min, HTTPS 50+200/min, ICMP 5/s
  • Subcomandos: apply/revert/status/list + add/remove + whitelist-ip/unwhitelist-ip/whitelist-list
  • Emergency-disable/restore/status (padrão v2.18.3)
  • Integração com emergency-mode — rate-limit agora em OPT_IN_FEATURES

Limitações

  • Distro: Debian/Ubuntu apenas na v1
  • Whitelist: iptables raw (migração para ipset em v2.20.0+ com threat-status)

[2.18.5] - 2026-05-21

Fixed

  • Hotfix: restore_cmd do deadman agora encadeia limpeza do state file (<feature> apply && rm -f <state-file>). Sem isso, emergency-mode status continuava reportando entradas expiradas como ativas após o deadman disparar.

[2.18.4] - 2026-05-21

Fixed

  • Hotfix: emergency-disable falhava com error: unexpected argument '--quiet' found. lockdown_cmd agora usa implant harden-network --quiet revert (flag antes do subcomando) — clap exige flags globais ANTES do positional.

[2.18.3] - 2026-05-21

Padrão emergency-disable para features opt-in + bypass guarantee no deadman do SSH.

Added

  • <feature> emergency-disable/restore/status em features opt-in (harden-network agora). Desabilita temporariamente com TTL — re-aplica via deadman automático.
  • implant emergency-mode enable/disable/status/heartbeat — comando global que itera features opt-in em uma operação.
  • implant ssh-service cleanup-emergency-rules — remove bypass do iptables/ip6tables/UFW (idempotente).
  • Flag --quiet em harden-network apply/revert.
  • Flag --force em ssh-service disable.

Changed

  • SSH deadman agora prepend iptables -I INPUT 1 -p tcp --dport <ssh_port> -j ACCEPT --comment 'implant-emergency-ssh' antes de iniciar o sshd. Garante que rate-limit/firewall/iptables não bloqueiem o retorno.

[2.18.2] - 2026-05-20

Fixes em dois bugs do fluxo --dry-run + inclusão do conntrack nas ferramentas essenciais.

Fixed

  • Crítico: módulo apps ignorava --dry-run — check --auto-fix --dry-run baixava e instalava os apps de implant install de verdade. Fix: early return em dry_run nas 5 funções de install.
  • UX: resumo do dry-run dizia "nenhuma mudança seria realizada" mesmo quando teria 12 fixes pendentes. O filtro só contava Fail, mas em dry-run os itens ficam marcados Fixed (executor intercepta os comandos, código Rust segue pro success path). Fix: aceitar Fail || Fixed no summary.

Added

  • conntrack (pacote Debian/Ubuntu) na lista de ferramentas essenciais. Necessário para conntrack -C/conntrack -L em debug de incidente com saturação da tabela.

[2.18.1] - 2026-05-13

Extensão do pack --harden-network com sysctls adicionais (defense-in-depth). Sem mudanças em comandos — apenas mais proteções no mesmo /etc/sysctl.d/99-harden-network.conf.

Added

Conntrack timeouts (se Docker/K3s):

  • nf_conntrack_tcp_timeout_syn_recv = 10 — anti SYN flood na tabela
  • nf_conntrack_tcp_timeout_syn_sent = 10
  • nf_conntrack_tcp_timeout_close_wait = 30
  • nf_conntrack_tcp_timeout_fin_wait = 30
  • nf_conntrack_tcp_timeout_time_wait = 30

Anti TIME_WAIT assassination:

  • net.ipv4.tcp_rfc1337 = 1 (RFC 1337)

TCP keepalive (detecta zumbis em 10min, default 2h):

  • tcp_keepalive_time = 600
  • tcp_keepalive_intvl = 30
  • tcp_keepalive_probes = 5

Resource exhaustion protection:

  • vm.swappiness = 10
  • vm.overcommit_memory = 2 + vm.overcommit_ratio = 80
  • vm.panic_on_oom = 0

[2.18.0] - 2026-05-13

Pack opt-in de hardening de rede estendido. Cobre 3 gaps do hardening base: IPv6 desprotegido (sysctls v4-only), sem tuning anti SYN flood, e TIME_WAIT acumulando.

Added

  • Subcomando implant harden-network apply/revert/status — padrão granular dos outros controles. Reversível independentemente do hardening base.
  • Flag --harden-network em implant check — aplica o pack junto com o auto-fix. Requer --auto-fix (caso contrário é ignorado com warning, porque é destrutivo).

Pack aplicado (`/etc/sysctl.d/99-harden-network.conf`)

Sysctl Valor Por quê
net.ipv6.conf.all.accept_redirects 0 Espelho do v4 — anti ICMPv6 redirect hijack
net.ipv6.conf.all.accept_source_route 0 Espelho do v4
net.ipv4.tcp_max_syn_backlog 8192 Fila maior contra SYN flood
net.core.somaxconn 65535 Backlog de accept() para serviços
net.ipv4.tcp_synack_retries 2 Janela menor de retransmissão
net.ipv4.tcp_fin_timeout 15 Recicla TIME_WAIT mais rápido
net.ipv4.tcp_tw_reuse 1 Reuso de sockets em TIME_WAIT
net.netfilter.nf_conntrack_max 524288 Apenas se Docker/K3s detectado

[2.17.2] - 2026-05-13

Quatro bugs descobertos rodando implant check --auto-fix num Ubuntu 24.04 fresh. O mais crítico fazia todos os apt install do auto-fix falharem silenciosamente.

Fixed

  • Apt install nunca executava (crítico): aspas duplas literais em echo "Matando processo apt stale..." dentro de sh -c "..." aninhado quebravam o parse do shell, fazendo todos os pacotes faltantes do auto-fix retornarem exit 2 ("não encontrado") — disfarçando o problema como pacote ausente do repositório. O echo informacional foi removido.
  • Percentual [2/2] 0% no resumo por módulo: a coluna [X/Y] somava passed + fixed, mas o % só contava passed. Módulos onde tudo foi corrigido mostravam 0%. Fórmula corrigida.
  • Banner de ROLLBACK com zero ações: cada install que falhava imprimia 6+ linhas de banner mesmo sem nada a reverter. execute_rollback agora retorna cedo se actions.is_empty().
  • Status Final: SUCESSO com falhas residuais: o relatório confiava num campo stale do report. Agora deriva effective_status localmente das contagens (total_failed, total_fixed).

[2.17.1] - 2026-05-10

Rodada de UX no fluxo de check --auto-fix baseada em feedback de produção.

Fixed

  • Aliases pra --auto-fix: Commands::Check aceita --auto, --fix, -a como aliases. Operadores não batem mais em "unexpected argument" por digitar a forma curta natural.
  • Output de snapshot SSH compacto: create_snapshot imprimia 11 sub-steps internos que poluíam o terminal. Agora imprime apenas ▸ Criando snapshot SSH... no início e ✓ Snapshot SSH em <path> no fim. Detalhe permanece em state.json para auditoria.
  • Display custom para RollbackAction: log vazava {:?} do Rust em human output. Agora usa Display formatado, e movido de info! para debug! — só verbose vê.
  • Exit codes semânticos: (código: 127) virou (comando não disponível). Mapeamento: 1 falhou, 2 não encontrado, 126 permissão negada, 127 comando não disponível, 130 interrompido, 137 OOM, 143 SIGTERM. Verbose preserva o número.
  • → Verificando X suprimido em human não-verbose: antes cada sub-check imprimia → antes e ✓ depois (duplicação visual). Só o resultado final aparece agora; --verbose preserva ambos.

[2.17.0] - 2026-05-09

Rodada de integração com o console de gerenciamento remoto — controles granulares de hardening expostos via CLI estruturado, lockdown SSH com TTL e auto-recovery.

Added

  • Primitiva genérica lockdown: mecanismo opt-in de service-restriction com TTL e auto-recovery via systemd-run. Recovery delegado ao systemd com shell puro — não depende do binário implant nem do agente de controle remoto estarem vivos. TTL cap hardcoded em 6h. Persistência atômica em /var/lib/implant/lockdowns/<service>.json.
  • Wrapper ssh-service: implant ssh-service disable/heartbeat/status/emergency-restore como interface de alto nível pra restringir SSH. Detecta porta atual, monta lockdown_cmd (ufw deny + systemctl stop) e restore_cmd (systemctl start + ufw allow). Emergency-restore exige TTY interativo.
  • Fail2ban granular (implant fail2ban): list-jails, list-banned, ban, unban, clear, tail-log. Tudo via fail2ban-client. IPs validados com IpAddr::from_str antes de qualquer composição shell.
  • Subcomando iptables: list, append, delete, flush, save, restore, snapshot — com salvaguardas anti-tijolar: snapshot automático antes de destrutivos (últimos 10 em /var/lib/implant/iptables-snapshots/), whitelist hardcoded de regras críticas (SSH, lo, ESTABLISHED) que recusa delete/flush, flush global exige TTY interativo.
  • Subcomando status consolidado: leitura agregada do sistema com cache em /var/lib/implant/state.json (TTL 24h). --refresh força re-execução do check.
  • Pin TLS do endpoint do console remoto: adicionado ao set de domínios pinados.

Notes

A integração do lado do console remoto (push de policy, indicadores e alertas) é distribuída separadamente.

[2.16.0] - 2026-05-08

Rodada de hardening pós-revisão de segurança.

Security

  • TLS pinning real: get_http_client() instala um ServerCertVerifier custom no rustls que valida chain via WebPKI e confronta o SPKI hash do leaf cert no mesmo handshake. Antes o pin era checado em conexão TLS separada, permitindo divergência entre a conexão de pin-check e a conexão da requisição real.
  • Cliente HTTP pinado para download de chave: add_public_key_from_url deixou de usar curl/wget e passou a usar o cliente HTTP pinado. Rejeita schemes não-https.
  • Validação de IP em whitelist: whitelist_ip valida IpAddr::from_str antes de qualquer composição de comando. Bloqueia entradas com newline, espaço, metacaracteres de shell ou strings não-IP.
  • K3s install via env vars: K3S_TOKEN e K3S_URL são entregues via Command::env em vez de interpolados em string shell.
  • Validação de identifier: add_key_to_authorized_keys, add_public_key_from_url e remove_key rejeitam identifier fora de [A-Za-z0-9._-]{1,64}.
  • init.sh verifica .minisig: instalador baixa .minisig junto com o binário, instala minisign (apt/dnf+epel/yum+epel/apk) com fallback para binário static do GitHub validado por SHA-256, e só instala se a assinatura bater.

Fixed

  • Bug do backup .bak: 7 call sites em ssh_port.rs, reset.rs, fundamentals.rs e traefik.rs tentavam restaurar de {path}.bak, mas executor.backup_file() cria {path}.backup.{timestamp}. O fallback nunca funcionava na prática.
  • Self-update atômico: substitui fs::remove_file + fs::copy por fs::rename único. Elimina a janela onde o binário/symlink ficava inexistente.
  • confirm() exige TTY: detecta stdin não-interativo via IsTerminal e auto-recusa com aviso explícito.

[2.14.8] - 2026-04-19

Fixed

  • apt: Simplifica apt-get update sem nesting de sh -c

[2.14.7] - 2026-04-19

Fixed

  • apt: Para unattended-upgrades durante instalacao + timeout no apt-get update

[2.14.6] - 2026-04-19

Fixed

  • apt: Mata processos apt stale (>30min) + wait lock max 60s

[2.14.5] - 2026-04-19

Fixed

  • needrestart: Configuracao via /etc/needrestart/conf.d/ drop-in (modo list-only)

[2.14.4] - 2026-04-19

Fixed

  • needrestart: Drop-in config para needrestart, iotop e iftop restaurados

[2.14.3] - 2026-04-19

Fixed

  • install: Timeout 120s em install_package, removido iftop

[2.14.2] - 2026-04-19

Fixed

  • install: Removido iotop (travava em Ubuntu 22.04+)

[2.14.1] - 2026-04-19

Fixed

  • security: Timeout 10s no TCP connect + TLS handshake do extract_and_verify_pin

[2.14.0] - 2026-04-19

Added

  • multi-arch: Release estavel com static musl para x86_64 e aarch64
  • security: TLS Pinning (SPKI) com pins compilados para as CDNs oficiais do implant e dos apps instaláveis
  • security: Guardrail hardened com allowlist de hostname implant.ccs.systems + verificacao de pin
  • self-update: URLs arch-aware para self-update e instalacao de subsistemas
  • build: Makefile + build_multiarch.sh + Cross.toml para build multi-arch
  • build: Cargo.toml otimizado (tokio sem "full", opt-level=z, panic=abort)
  • gitops: .gitops.yml v2.0 com artifacts multi-arch explicitos
  • init.sh: Deteccao de arquitetura + flag --rc para release candidates

[2.14.0-rc.3] - 2026-04-02

Added

  • ssh-rollback: Novo comando para listar e restaurar snapshots SSH
    • Snapshots automaticos antes de qualquer modificacao SSH
    • Salva sshd_config, socket override, fail2ban, sysctl
    • Ate 10 snapshots com rotacao automatica
  • emergency-reset: Reverte TODAS as configs de seguranca (7 passos independentes)
    • Porta SSH → 22, firewall aberta, hardening removido, fail2ban desabilitado
    • Preserva ip_forward quando Docker/K3s presente
  • watchdog SSH: Reversao automatica em 5 minutos se porta nao confirmada
    • Backend: at (preferido) ou systemd-run (fallback)
    • Confirmar com implant ssh-port --confirm-change
  • ssh-port: Confirmacao obrigatoria antes de alterar (--yes para skip)
  • reset: Flag --yes para pular confirmacao

Fixed

  • CRITICO: reset --ssh-port hardcodava porta 2222 (agora detecta dinamicamente)
  • CRITICO: reset --ssh-port nao suportava systemd socket activation
  • CRITICO: reset --ssh-port nao validava sshd_config antes de restart
  • CRITICO: reset --ssh-port nao abria firewall antes de alterar
  • reset --ssh-hardening: Adicionado backup + validacao sshd -t + fallback chain
  • reset --kernel-hardening: Preserva ip_forward=1 quando Docker/K3s detectado

[2.14.0-rc.2] - 2026-03-26

Added

  • traefik-accesslog: Confirmacao antes de aplicar mudancas
  • traefik-accesslog: Deteccao de Traefik via DeployAlly e Docker Compose
  • traefik-accesslog: Adicao automatica de volume de log
  • ssh-port: Modo status (sem argumento mostra porta atual e conexoes)

[2.14.0-rc.1] - 2026-03-26

Added

  • traefik-accesslog: Novo comando para configurar access log do Traefik
  • ssh-port: Refatorado para status/change separados

Fixed

  • CRITICO: Hardening setava ip_forward=0 quebrando Docker/K3s
  • fundamentals: SSH settings inseridas antes de Include/Match
  • fundamentals: Validacao sshd -t antes de restart com auto-restore

[2.13.1] - 2026-03-18

Fixed

  • install backupally: URL atualizada

[2.12.10] - 2026-03-08

Fixed

  • install apps: Limpa arquivo temporario residual de outro usuario antes de baixar
  • install apps: Mensagens de erro detalhadas com contexto (qual operacao falhou)

[2.12.9] - 2026-03-08

Fixed

  • install apps: Mensagens de erro com contexto detalhado em cada operacao

[2.12.8] - 2026-03-03

Fixed

  • self-update/install: Troca fs::rename por fs::copy+remove para suportar cross-device
    • Corrige erro "Invalid cross-device link" (os error 18) quando /tmp e tmpfs
    • Garante chmod 755 apos copy

[2.12.7] - 2026-02-28

Changed

  • install claude-code: Usa instalador nativo oficial (curl -fsSL https://claude.ai/install.sh | bash)
    • Remove dependencia de NVM/Node.js como pre-requisito
    • Auto-update automatico em background

[2.12.6] - 2026-02-28

Added

  • ssh-key: Flag --alias para definir nome do alias

[2.12.0] - 2026-02-28

Changed

  • BREAKING: Modulo ssh-key completamente refatorado
    • Modelo simplificado: servidor central distribui sua chave publica
    • Removida logica de geracao de chaves no servidor gerenciado

Added

  • ssh-key: --add-public-key "ssh-ed25519 AAAA..." - Adiciona chave publica
  • ssh-key: --add-public-key-url "https://..." - Busca chave de URL
  • ssh-key: --list - Lista chaves no authorized_keys
  • ssh-key: --remove "identificador" - Remove chave
  • ssh-key: --whitelist-ip IP - Adiciona IP a whitelist

[2.11.6] - 2026-02-27

Fixed

  • self-update: Corrige erro "Text file busy" (os error 26)

[2.11.2] - 2026-02-27

Fixed

  • init.sh: Usa binario direto ao inves de .zip
  • init.sh: Corrige flag --auto para --auto-fix

[2.11.1] - 2026-02-27

Added

  • init.sh: Script de instalacao rapida
  • GitOps: init.sh adicionado como artifact

[2.10.2] - 2025-02-17

Fixed

  • CRITICO: Liberar porta no UFW/iptables ANTES de alterar SSH
    • Evita lockout do servidor quando firewall esta habilitado

[2.10.0] - 2025-02-13

Added

  • setup-claudecode: Configura usuario dedicado para Claude Code

    • Cria usuario claudecode com grupo proprio
    • Sudoers granulares ou irrestrito
    • ACLs em diretorios de trabalho
    • Guard-rail CLAUDE.md com auto-update
  • update-guardrail: Gerencia guard-rails remotos

[2.9.0] - 2025-02-12

Added

  • Suporte a systemd socket activation para SSH (Ubuntu 22.04+)
  • SSH PerSourcePenalties para OpenSSH 9.8+
  • Whitelist automatico de IP da conexao atual

[2.3.9] - 2024-12-28

Added

  • Logrotate automatico baseado no tamanho do disco

[2.3.8] - 2024-12-28

Added

  • Sistema de instalacao de apps externos (DeployAlly, Runner, etc)

[1.0.0] - 2024-11-29

Added

  • Release inicial
  • Modulos: fundamentals, hardening, docker, network, monitoring
  • Modo dry-run e auto-fix
  • Rollback automatico
  • Multi-distro (Ubuntu, Debian, CentOS, Rocky Linux)
  • Self-update