implant ban / implant unban

Um comando só para banir um IP ou bloco CIDR (v2.44.0+), qualquer que seja o mecanismo por baixo. O ban vai para o ipset central da blocklist do implant (IMPLANT_RL_BLOCKLIST / IMPLANT_RL_BLOCKLIST6), que os ganchos da chain INPUT e da DOCKER-USER leem — e a resposta diz onde ele vale de fato. O unban desfaz nos três mecanismos: ipset, fail2ban e UFW.

Uso

implant ban [OPTIONS] <TARGET>
implant ban list
implant ban test <TARGET>
implant unban [OPTIONS] <TARGET>

`ban `

Bane o IP ou CIDR. CIDR é normalizado para o endereço de rede.

Flag Descrição
--ttl <TTL> Duração: 3600, 90s, 30m, 24h, 7d. Padrão 24h; máximo ~24 dias (limite do ipset)
--permanent Ban sem prazo
--reason <TEXTO> Motivo; aparece na auditoria e no ban list
--force Aceita prefixo mais amplo que /16 (IPv4) ou /48 (IPv6)
--fail2ban Bane também nas jails ativas do fail2ban (só IP único)
--dry-run Valida as guardas e mostra o que faria, sem aplicar

Guardas (sem atalho para contorná-las):

  • o IP da própria sessão SSH;
  • faixas privadas (RFC 1918, CGNAT, ULA), loopback e link-local;
  • IPs na whitelist do implant;
  • prefixo mais amplo que /16//48 sem --force.

O ban nunca grava no UFW. No fail2ban, só com --fail2ban: sem a flag, o sinal do fail2ban usado pelo auto-ban leria o IP e trocaria o TTL pedido pelo dele.

Motivo e data do ban manual ficam em /var/lib/implant/bans.json; os eventos manual_ban / manual_ban_permanent entram na auditoria do auto-ban.

"Banido" não é o mesmo que "bloqueado"

O ipset sozinho não bloqueia nada: é preciso que a INPUT (e, em host com Docker, a DOCKER-USER) tenha o DROP que lê o set. Por isso a resposta traz:

Campo Significado
in_set Entrou no ipset (conferido com ipset test)
enforced.input A INPUT tem o DROP da blocklist
enforced.docker_user Idem na DOCKER-USER; null = host sem Docker
effective in_set e ganchos presentes: o tráfego é barrado
persisted Os sets e as regras que os usam voltam no boot: arquivo de sets gravado, unit de restore habilitada e persistência das regras (netfilter-persistent + iptables-persistent, serviço habilitado, /etc/iptables/rules.v4 com conteúdo)
warnings O que falta (ex.: rodar implant rate-limit reconcile)

effective: false com success: true significa que o IP está no set, mas não está bloqueado: falta gancho. Rode implant rate-limit reconcile para recriar os ganchos.

Para o ban sobreviver ao reboot, o host precisa de duas coisas:

  • a unit de restore dos ipsets — ver firewall-restore;
  • a persistência das regras que leem os sets: netfilter-persistent com o plugin do iptables-persistent, o serviço netfilter-persistent habilitado e /etc/iptables/rules.v4 com conteúdo.

Desde a v2.59.0, o ban só responde persisted: true ("sobrevive a reboot: sim") quando sets e regras voltam no boot. Sets sem as regras não bloqueiam nada. Faltando qualquer parte, a resposta vem com persisted: false e o motivo em warnings.

Instalar o pacote ufw remove o iptables-persistent e o netfilter-persistent no apt (conflito de pacotes): os sets continuam voltando pela unit, as regras não. Esse é o caso mais comum de persisted: false com a unit instalada. O check "Boot-survival do firewall" do hardening aponta a mesma falta.

Desde a v2.61.0, a unit atual reaplica as regras do implant no boot quando o netfilter-persistent não as carrega, e isso conta como mecanismo: persisted: true com a unit em dia, mesmo sem o netfilter-persistent (ver firewall-restore).

`ban list`

Visão consolidada, uma linha por alvo, juntando ipset, fail2ban e UFW. Cada item traz sources (ipset, fail2ban:<jail>, ufw), TTL restante (só para o ipset; 0 = permanente), motivo e data quando o ban foi feito pelo implant. O topo traz enforced (ganchos presentes) e warnings.

`ban test `

Diz se o IP está banido e se o ban bloqueia de fato: banned, blocked (banido e com gancho), ipset/ipset_entry (inclusive quando o IP está coberto por um CIDR), ttl_remaining_seconds, jails do fail2ban, UFW, whitelisted e enforced. IP que está na blocklist e na whitelist não aparece como blocked — a whitelist vale acima — e gera aviso.

Sobrevive a reboot? (v2.60.0+) O ban test responde com o mesmo cálculo do ban: linha "sobrevive a reboot: sim/NÃO" no texto e campo persisted no JSON ban.test — true só quando sets e regras voltam no boot. Quando não sobrevive, o motivo vem em warnings. Alvo fora da blocklist: persisted: null (- no texto).

Feed externo (v2.60.0+): o ban test também mostra se o IP está no feed-block:

Campo Significado
feed O IP está coberto por uma entrada do feed
feed_entry A entrada do feed que o cobre (ex.: 198.51.100.0/24)
feed_enforced Os ganchos do feed estão presentes: o feed barra de fato

blocked passa a contar o feed: um IP fora da blocklist, mas no feed com ganchos, aparece como bloqueado.

`unban `

Remove o IP/CIDR do ipset, de todas as jails do fail2ban e das regras do UFW, e encerra o cooldown do auto-ban mantendo o histórico de ofensas (a próxima ofensa conta como reincidência).

No UFW, remove o bloqueio total (deny from <IP>) e, desde a v2.60.0, também as regras de porta do firewall deny --port P --from <IP> — antes respondia "não estava banido" para elas. É o mesmo cálculo do `firewall unblock-ip`.

O unban não tira o IP do feed-block: ele voltaria no próximo update do feed. Nesse caso a resposta traz in_feed/feed_entry e avisa que a saída é a whitelist, que vale acima do feed (implant rate-limit whitelist-ip --ip <ip>).

Flag Descrição
--dry-run Mostra o que removeria, sem aplicar

Saída: was_banned, removed (ipset, fail2ban — lista de jails —, ufw), auto_ban_cooldown_cleared, offense_count, still_blocked e still_blocked_by: o que ainda bloqueia o alvo depois do unban — uma entrada mais ampla da blocklist (ex.: o /24 que o contém), o feed externo ou o UFW. Desde a v2.60.1 o unban e o ban test usam o mesmo cálculo, inclusive em dry-run (como previsão). O evento manual_unban entra na auditoria do auto-ban.

UFW no ban test (v2.60.1+): com o UFW ativo, um deny/reject de entrada total que cobre o alvo conta como bloqueio (blocked_by: ["ufw"], ufw_block.rule) quando vem antes de qualquer allow/limit de entrada cuja origem também cubra o alvo. Se um allow vier antes, o bloqueio não vale de fato: ufw_block.effective: false e shadowed_by aponta a regra que libera (use implant firewall block-ip, que insere o deny no topo). Com Docker, o deny do UFW não alcança as portas publicadas.

Exemplos

sudo implant ban 203.0.113.9                          # 24 h (padrão)
sudo implant ban 203.0.113.9 --ttl 7d --reason "scanner em /.env"
sudo implant ban 198.51.100.0/24 --permanent
sudo implant ban 203.0.113.9 --fail2ban               # também nas jails ativas
sudo implant ban 203.0.113.9 --dry-run                # só valida as guardas

sudo implant ban list                                 # ipset + fail2ban + UFW
sudo implant ban test 203.0.113.9                     # banido? bloqueado de fato?

sudo implant unban 203.0.113.9                        # desfaz nos três
sudo implant unban 203.0.113.9 --dry-run

Saída JSON

-o json devolve o envelope com os comandos ban, unban, ban.list e ban.test. Campos do ban: target, family (inet/inet6), set, ttl_seconds, permanent, expires_at, reason, in_set, enforced, effective, persisted, dry_run, warnings. O ban.test traz também persisted (null fora da blocklist), feed, feed_entry e feed_enforced; o unban, in_feed e feed_entry quando o alvo está no feed.

Notas

  • Precedência dos ganchos do implant: whitelist > blocklist > feed-block > geo-block > rate-limit (v2.46.0+; o feed entrou na v2.60.0). Um IP isento continua passando mesmo que esteja na blocklist ou no feed.
  • O ban manual e o auto-ban usam o mesmo set; unban vale para os dois.

Veja também