implant firewall

Wrapper sobre ufw com validação de IP/CIDR e portas, pensado tanto para uso manual quanto para automação (ex.: um console de gerenciamento remoto aplicando regras via --output json).

Uso

implant firewall <subcomando> [OPTIONS]

Subcomandos

`allow --port [--proto tcp|udp] [--from ]`

Libera uma porta. Default: tcp. Com --from, restringe a origem (ex.: --from 10.8.0.0/24).

`deny --port [--proto tcp|udp] [--from ]`

Bloqueia uma porta (opcionalmente só para uma origem). Com --from, o DENY entra no topo das regras do UFW, como no block-ip (abaixo). Sem --from, segue o comportamento do ufw deny (troca a ação de um allow idêntico).

`block-ip `

Bloqueia todo o tráfego de um IP (v4 ou v6). O endereço é validado antes de chamar ufw — entradas com newline, espaço ou metacaracteres são recusadas.

Desde a v2.59.0, o DENY entra no topo da família do endereço, antes dos allow de porta. Antes ele ia para o fim da lista, depois dos allow 22/80/443, e o IP "bloqueado" continuava recebendo SSH e HTTP.

  • IPv4: ufw insert 1 deny from <IP>.
  • IPv6: ufw insert <N> deny from <IP>, com N = posição da primeira regra v6 (o ufw numera todas as regras v4 antes das v6 e não aceita insert 1 para regra v6).
  • Sem nenhuma regra da família, é o append de sempre (que já é o topo dela).
  • Uma regra igual que já exista (por exemplo, deixada no fim por versão anterior) é apagada e reinserida no topo.
  • Com o UFW inativo, a regra é gravada e o comando avisa que ela só vale depois do enable.
$ sudo ufw status numbered
[ 1] Anywhere                   DENY IN     203.0.113.50
[ 2] 22/tcp                     ALLOW IN    Anywhere
[ 3] 443/tcp                    ALLOW IN    Anywhere
[ 4] Anywhere (v6)              DENY IN     2001:db8::50
[ 5] 22/tcp (v6)                ALLOW IN    Anywhere (v6)
[ 6] 443/tcp (v6)               ALLOW IN    Anywhere (v6)

`unblock-ip `

Remove todas as regras deny de origem que o implant cria para o IP, pela especificação (ufw delete ...), em qualquer posição:

  • o bloqueio total do block-ip (deny from <IP>);
  • desde a v2.60.0, também as regras de porta do deny --port P --from <IP> (deny from <IP> to any port P [proto Y]). Antes, nenhum comando as removia.

As regras vêm do ufw show added (vale com o UFW inativo; sem ele, do ufw status numbered) e casam pelo endereço, não pelo texto: IPv6 em qualquer grafia casa, e 203.0.113.90 não casa com 203.0.113.9. Regra de destino específico, de saída ou allow não é tocada.

Desde a v2.60.0 a resposta é honesta quando não há o que remover: sem regra nenhuma para o IP, avisa "nada a remover" e sai 0 com removed: false (como o unban de alvo não banido). Até a v2.59.0 dizia "IP desbloqueado" sem remover nada, porque o ufw delete de regra inexistente sai 0. Falha real do ufw continua saindo com erro.

JSON firewall.unblock-ip: ip, removed, rules (especificações removidas; em --dry-run, as que sairiam) e dry_run. O `implant unban` enxerga e remove as mesmas regras.

sudo implant firewall deny --port 22 --from 203.0.113.9
sudo implant firewall block-ip 203.0.113.9
sudo implant --dry-run firewall unblock-ip 203.0.113.9   # lista as duas regras
sudo implant firewall unblock-ip 203.0.113.9             # remove as duas
sudo implant firewall unblock-ip 203.0.113.9             # "nada a remover", exit 0

`rate-limit --port [--proto tcp|udp]`

Aplica ufw limit (6 conexões em 30s) — útil para SSH em porta custom. Para rate-limit por origem com connlimit/hashlimit, veja rate-limit.

`status`

Estado consolidado do firewall: ativo/inativo, defaults e regras.

`enable` / `disable`

Habilita/desabilita o ufw (ufw --force enable / ufw --force disable). O enable não pede confirmação: garanta antes que a porta SSH está liberada (implant firewall allow --port <porta-ssh>).

`audit [--strict]`

Auditoria somente leitura do firewall (v2.60.0+): não muda nada no host. Responde se o firewall está fechado de fato, o que há de cru fora do implant e se tudo volta no boot.

sudo implant firewall audit                 # relatório
sudo implant firewall audit --strict        # exit 1 se houver achado crit
sudo implant -o json firewall audit         # envelope firewall.audit

Cada achado tem id, severity (ok, warn ou crit), title, detail e fix (o comando sugerido, quando há um). <família> é ipv4 ou ipv6.

id O que olha Severidade
policy.input.<família> Policy da INPUT. Fechada = policy DROP, DROP/REJECT incondicional no fim, ou UFW ativo com default deny/reject (incoming) crit se aberta; warn se aberta mas com ganchos do implant (blocklist, geo-block, rate-limit); ok em v6 sem endereço IPv6 global
policy.forward.<família> Policy da FORWARD warn se aberta com encaminhamento ligado; ok com encaminhamento desligado
docker_user.<família> Blocklist do implant na DOCKER-USER (só com Docker) crit se está na INPUT e não na DOCKER-USER (IP banido alcança containers); warn com UFW ativo e nada na DOCKER-USER
ufw.order.<família> Ganchos do implant antes do primeiro salto para o UFW crit se blocklist/geo-block estão depois (o ALLOW do UFW passa antes)
ufw.raw_rules.<família> Com UFW ativo: regra direta na INPUT/FORWARD/OUTPUT fora das chains do UFW, do implant, do fail2ban e do Docker warn
orphan.<família>.<chain>.<posição> DROP/REJECT de IP/CIDR solto na INPUT/DOCKER-USER, fora do ipset do implant warn, com fix: implant ban <ip> --permanent; faixa reservada (anti-spoofing) só informa
log.unlimited -j LOG sem limite (mesmo detector do iptables audit-log-rules) warn
persistence.rules Regras do implant, DROP cru e policy DROP própria voltam no boot (netfilter-persistent + iptables-persistent, serviço habilitado, ganchos no rules.v4/rules.v6; ou, sem ele, a reaplicação pela unit implant-firewall-restore, v2.61.0+, que só traz as regras do implant) crit se ganchos ou policy DROP somem no reboot; warn se só DROP cru ou policy própria somem (reaplicação)
persistence.ipsets Sets IMPLANT_* em uso: unit implant-firewall-restore ativa e sets no arquivo crit se voltam vazios

Exemplos de fix: implant ban 203.0.113.7 --permanent, implant rate-limit reconcile, implant iptables audit-log-rules --fix. Com o UFW instalado, o audit não sugere iptables-persistent: os dois pacotes conflitam no apt.

Exit code: 0 sempre (é auditoria). Com --strict, sai 1 se houver algum achado crit — útil em automação.

JSON (--output json, Envelope 1.0), comando firewall.audit:

{
  "overall": "crit",
  "counts": { "ok": 5, "warn": 3, "crit": 1 },
  "strict": false,
  "ufw_active": false,
  "families_scanned": ["ipv4", "ipv6"],
  "findings": [
    { "id": "orphan.ipv4.INPUT.4", "severity": "warn",
      "title": "DROP solto de 203.0.113.7 na INPUT (ipv4)",
      "detail": "regra crua ... iptables -D INPUT -s 203.0.113.7/32 -j DROP",
      "fix": "implant ban 203.0.113.7 --permanent" }
  ],
  "notes": []
}

Família sem iptables-save/ip6tables-save vira nota em notes e fica de fora de families_scanned.

Simular (`--dry-run`)

Desde a v2.59.0, allow, deny, block-ip, unblock-ip, rate-limit, enable e disable respeitam o --dry-run global: mostram o que fariam sem chamar o ufw. Até a v2.58.0 o flag era ignorado e a mudança era aplicada.

sudo implant --dry-run firewall block-ip 203.0.113.50

Saída JSON

Com --output json, mutações retornam envelopes por ação (firewall.allow, firewall.enable, …) e status retorna o estado do UFW.

Exemplos

# Liberar HTTPS
sudo implant firewall allow --port 443

# Liberar PostgreSQL só para a rede interna
sudo implant firewall allow --port 5432 --from 10.8.0.0/24

# Bloquear um IP (v4 ou v6) — entra no topo das regras
sudo implant firewall block-ip 203.0.113.50
sudo implant firewall block-ip 2001:db8::50

# Fechar o SSH para uma faixa
sudo implant firewall deny --port 22 --from 198.51.100.0/24

# Estado em JSON
implant --output json firewall status

# Auditar o firewall (exit 1 se houver achado crit)
sudo implant firewall audit --strict

Veja também

  • ban / unban — ban no ipset central; unban também remove as regras do UFW
  • iptables — alternativa low-level com anti-tijolar
  • fail2ban — bans dinâmicos baseados em padrões