Configurar a firewall UFW sem se trancar fora do servidor
A ordem certa ao montar o UFW, as regras IPv6, o nftables como backend, a limitação de taxa com ufw limit e a via de emergência pela consola, quando mesmo assim corre mal.
Num servidor root, um filtro de pacotes não é um luxo, é equipamento básico. O UFW (Uncomplicated Firewall) torna a tarefa agradavelmente simples, mas tem uma característica que, todos os anos, deixa milhares de administradores trancados fora dos seus próprios servidores: o comando que arma a firewall é o mesmo que pode cortar a sessão SSH em curso. Este guia mostra a ordem pela qual isso não acontece e, quase mais importante, o caminho de volta caso aconteça mesmo assim.
Todas as indicações se referem ao Debian 13 (trixie), Debian 12 (bookworm), Ubuntu 24.04 LTS e Ubuntu 22.04 LTS. Os comandos estão escritos para execução como root. Se trabalhar como utilizador normal, anteponha sudo a cada comando.
Porque é que a ordem decide tudo
O erro clássico é este: alguém define primeiro a política predefinida para "descartar tudo o que entra", liga a firewall e só depois quer acrescentar com calma a regra de SSH. É precisamente nesse intervalo que fica a janela em que o servidor deixa de estar acessível.
A razão pela qual este erro passa tantas vezes despercebido é mais traiçoeira do que o próprio erro. O UFW traz em /etc/ufw/before.rules uma regra que deixa passar as ligações já estabelecidas:
-A ufw-before-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
A sua sessão SSH atual é uma ligação já estabelecida. Sobrevive portanto à ativação da firewall mesmo que não exista qualquer regra de SSH. A linha de comandos continua lá, tudo parece em ordem. Só a tentativa de ligação seguinte, tipicamente o seu início de sessão na manhã seguinte, é que bate num timeout. Por isso vale a regra: enquanto a firewall não estiver verificada com uma segunda sessão acabada de abrir, não saia da primeira sessão.
Regra prática: primeiro permitir, depois descartar, depois ativar, depois verificar com uma segunda sessão e só então fechar a primeira sessão.
Antes do primeiro comando: via de emergência e levantamento
Antes de mudar seja o que for na filtragem de pacotes, esclareça duas coisas.
1. Como chega ao servidor sem SSH?
Nos servidores root KVM e nos servidores dedicados da KernelHost, a consola VNC está acessível diretamente na área de cliente. Essa consola não depende da pilha de rede do sistema convidado, mas sim da camada de virtualização ou da própria ligação de rede. Uma regra de firewall dentro do convidado não a consegue bloquear. Autentique-se uma vez antes através dessa consola e certifique-se de que conhece a palavra-passe de root. Uma via de emergência que só se experimenta pela primeira vez durante a emergência não é uma via de emergência.
2. O que está afinal à escuta neste servidor?
Regras para serviços que não existem são inofensivas. Um serviço que lhe passou despercebido custa-lhe o acesso ou uma interrupção. Comece por obter uma visão geral:
ss -lntup
A coluna Local Address:Port distingue com clareza entre 0.0.0.0:22 (apenas IPv4), [::]:22 (IPv6 e, através do socket dual-stack, normalmente também IPv4) e 127.0.0.1:3306 (apenas local, não precisa de regra de firewall). Tudo o que esteja ligado a 127.0.0.1 ou ::1 não precisa de ser aberto.
Especialmente importante é a porta SSH real. Não adivinhar, verificar. O comando precisa de permissões de leitura sobre as chaves de host, corre portanto como root ou com sudo:
sudo sshd -T | grep -i "^port "
Um pormenor que muitos guias omitem: no Ubuntu 24.04, o serviço SSH arranca por ativação de socket através de ssh.socket, não através de ssh.service. Aí, systemctl is-enabled ssh.socket devolve enabled e ssh.service devolve disabled. No Debian 12, no Debian 13 e no Ubuntu 22.04 passa-se exatamente o contrário, aí vale o serviço permanente clássico.
A consequência é desagradavelmente concreta para este tema: no Ubuntu 24.04, a porta que sshd -T indica não é forçosamente a porta em que o sistema está mesmo à escuta. O que conta aí é ListenStream em /lib/systemd/system/ssh.socket ou num ficheiro complementar sob /etc/systemd/system/ssh.socket.d/. Quem mudou a sua porta SSH e confia em sshd -T abre no UFW o número de porta errado e fica trancado fora no início de sessão seguinte. Nos quatro sistemas, só é fiável olhar para o processo que está realmente à escuta:
sudo ss -lntp | grep sshd
3. O interruptor de homem-morto
Para o caso de algo correr mal, crie antes da alteração arriscada um temporizador que volte a desligar a firewall sozinho ao fim de dez minutos:
nohup sh -c 'sleep 600; ufw disable' >/dev/null 2>&1 &
Se tudo tiver funcionado, cancele-o:
pkill -f 'sleep 600; ufw disable'
O pkill só atinge o processo de shell que o envolve. O sleep continua a correr órfão e termina sem consequências, porque já não há ninguém que possa chamar ufw disable a seguir.
A ordem que não o tranca fora
No Debian, o UFW normalmente não vem pré-instalado; no Ubuntu Server, quase sempre vem. Instalar não faz mal em caso nenhum:
apt-get update
apt-get install -y ufw
ufw version
Agora a ordem, e exatamente assim:
ufw allow 22/tcp comment 'SSH'
ufw default deny incoming
ufw default allow outgoing
ufw --force enable
Quatro observações sobre isto:
- A regra de permissão vem antes de tudo o resto. O UFW aceita regras mesmo no estado inativo e guarda-as em
/etc/ufw/user.rules. Ao ligar, ficam imediatamente em vigor. - Em vez de
22/tcppode usar um perfil de aplicação, por exemploufw allow OpenSSH. Mas não confie nisso às cegas: os perfis não vêm do próprio UFW, vêm dos pacotes instalados. No Ubuntu,/etc/ufw/applications.d/está vazio enquanto não houver serviços instalados, eufw app listdevolve aí apenas o cabeçalhoAvailable applications:e nem uma única entrada. No Debian, o próprio pacote ufw traz cerca de 38 perfis e o perfil de SSH chama-seSSH. O perfilOpenSSHvem, nas duas distribuições, do pacoteopenssh-server, ou seja, o caso normal em servidores. Com uma porta SSH diferente da predefinida, o perfil fica de qualquer forma sem efeito. Os perfis que o seu sistema conhece são mostrados porufw app list; já a notação baseada em portasufw allow 22/tcpfunciona de forma idêntica nos quatro sistemas e é por isso a escolha mais fiável. - O
ufw enablepergunta de forma interativa:Command may disrupt existing ssh connections. Proceed with operation (y|n)?. Em scripts e em roles de Ansible useufw --force enable, caso contrário a execução fica pendurada. - O comentário indicado a seguir a
commentaparece emufw status. Daqui a meio ano, de outra forma, já não vai saber para que serve a porta 8443 aberta.
Os outros serviços entram depois, por exemplo um servidor web:
ufw allow 80/tcp comment 'HTTP'
ufw allow 443/tcp comment 'HTTPS'
E só agora é que abre um segundo terminal e se autentica de novo. Só quando este início de sessão funcionar é que o assunto está resolvido.
IPv6: a segunda família de endereços de que toda a gente se esquece
Qualquer servidor moderno tem IPv6, e na maior parte das vezes sem que alguém o tenha configurado ativamente. Quem só pensa em IPv4 acaba com uma firewall que regula exatamente metade do tráfego e deixa passar a outra metade. Verifique primeiro se existem sequer endereços IPv6 globais configurados:
ip -6 addr show scope global
A boa notícia: nas quatro distribuições aqui tratadas, /etc/default/ufw traz de fábrica IPV6=yes. O UFW mantém então, em paralelo, uma regra IPv6 para cada regra IPv4. Verificar em vez de confiar:
grep '^IPV6' /etc/default/ufw
Uma segunda prova, mais dura, é a própria política predefinida da cadeia IPv6:
ip6tables -L INPUT -n
Na primeira linha tem de aparecer Chain INPUT (policy DROP). Se aparecer policy ACCEPT e por baixo nenhuma cadeia ufw, então o seu servidor está completamente aberto por IPv6, por muito boas que sejam as regras de IPv4.
Duas armadilhas:
- Uma alteração a
IPV6em/etc/default/ufwnão tem efeito comufw reload. É precisoufw disableseguido deufw enable. É exatamente nessa lacuna que fica por instantes sem firewall, por isso não faça isto de passagem num sistema exposto. - Quem bloqueia o ICMPv6 de forma indiscriminada destrói a sua própria conectividade. O Neighbor Discovery e o "Packet too big" não são opcionais em IPv6, fazem parte do protocolo. O UFW já permite os tipos necessários em
/etc/ufw/before6.rules. Só mexa nesse ficheiro se souber exatamente o que está a fazer.
Quando uma regra baseada em portas é criada corretamente nas duas famílias de endereços, o UFW devolve duas linhas ao adicioná-la: Rule added e Rule added (v6). Se faltar a segunda linha, falta metade da proteção. Uma exceção são as regras limitadas por origem: em ufw allow from 203.0.113.10 to any port 22 proto tcp aparece apenas Rule added, e está certo assim, porque para um endereço de origem IPv4 não existe equivalente em IPv6.
O que o UFW escreve de facto: nftables como backend
Aqui circula muita informação incompleta. A situação no Debian 12, no Debian 13, no Ubuntu 22.04 e no Ubuntu 24.04 é uniforme: o UFW continua a falar a sintaxe do iptables, mas o comando iptables é, nos quatro sistemas, a ferramenta de compatibilidade iptables-nft. As regras vão portanto parar ao subsistema nftables do kernel. A prova numa linha:
iptables -V
A saída termina com (nf_tables). Se aparecer (legacy), o seu sistema trabalha com o backend antigo. Isso até funciona, mas leva a que regras de dois mundos fiquem lado a lado no kernel e se tapem umas às outras. A variante configurada é mostrada por update-alternatives --display iptables.
Visto do lado do nftables, o aspeto é este. É importante que só consulte depois de ligar a firewall:
apt-get install -y nftables
nft list tables
Enquanto o UFW não estiver ativo, a tabela nem sequer existe: o iptables-nft só a cria quando são mesmo carregadas regras, ou seja, o mais cedo possível com ufw --force enable. Antes disso, nft list tables fica vazio e um nft list table ip filter aborta com Error: No such file or directory. Não é um defeito, é o estado esperado. Só quando aparecerem na lista table ip filter e table ip6 filter é que vale a pena espreitar lá dentro:
nft list table ip filter
A saída começa com a linha Warning: table ip filter is managed by iptables-nft, do not touch!, e isso é para levar à letra: ver sim, alterar à mão não. Por baixo encontra cadeias como ufw-before-input, ufw-user-input e ufw-after-input. É neste ponto que fica visível o conflito mais importante: não misture UFW com regras nft escritas à mão. Um nft flush ruleset apaga do kernel todas as regras do UFW sem que o UFW dê por isso. Depois disso, ufw status continua a indicar Status: active enquanto, na prática, não há uma única regra a atuar. É uma das fontes de erro mais desagradáveis que existem, porque a ferramenta com que verifica está a mentir-lhe. O caminho de volta:
ufw reload
Por isso, depois de qualquer intervenção com outras ferramentas de firewall (Docker, Kubernetes, software de VPN, iptables-persistent), não verifique o estado do UFW, verifique o conjunto de regras real do kernel.
ufw limit contra força bruta, e onde deixa de chegar
Para SSH, o UFW oferece uma limitação de taxa:
ufw limit 22/tcp comment 'SSH rate limit'
A semântica está claramente definida no manual: as ligações são permitidas normalmente, mas descartadas assim que um único IP de origem estabelecer seis ou mais ligações novas dentro de 30 segundos. Os valores estão fixados no código e não podem ser alterados pela interface do UFW. Por baixo, isto funciona com o módulo recent, visível em iptables -S. Para IPv6, o UFW cria uma regra equivalente, desde que o módulo do kernel esteja disponível, o que acontece nas quatro distribuições.
Se já tinha definido antes ufw allow 22/tcp, surge agora uma segunda regra. A antiga tem de sair, caso contrário é ela que atua primeiro e a limitação fica sem efeito:
ufw status numbered
ufw delete allow 22/tcp
Se preferir apagar pelo número (ufw delete 3), tenha atenção a duas particularidades. Primeiro, o ufw status numbered numera as regras IPv4 e IPv6 de forma corrida numa única lista, e depois de cada eliminação todos os números seguintes deslocam-se. Por isso, apague sempre apenas uma regra de cada vez, volte a pedir a lista a seguir, ou trabalhe do número mais alto para o mais baixo. Segundo, o UFW pergunta aí de forma interativa: Proceed with operation (y|n)?.
E agora a avaliação honesta que falta na maioria dos guias:
- Contra ataques distribuídos não ajuda. A contagem é feita por IP de origem. Uma botnet com mil endereços faz cinco tentativas por endereço e fica abaixo do limiar.
- Atinge os seus próprios automatismos. Um script de backup com muitas chamadas
rsyncindividuais, uma execução de Ansible com vários forks ou um job de CI podem bater no mesmo limite. Nesse caso não está a trancar fora o atacante, está a trancar fora a sua própria pipeline de deploy. Para essas origens, é melhor colocar antes uma exceção explícita, por exemploufw allow from 203.0.113.10 to any port 22 proto tcpcom o endereço real do seu servidor de build. - Não substitui uma configuração SSH bem feita. O passo mais eficaz contra a adivinhação de palavras-passe é desligar as palavras-passe:
PasswordAuthentication noem/etc/ssh/sshd_configou num ficheiro sob/etc/ssh/sshd_config.d/. Quem não aceita palavras-passe também não as pode deixar adivinhar. Como complemento faz sentido o fail2ban, que ao contrário doufw limitreage a entradas de log e bloqueia durante mais tempo.
Como reconhecer que funcionou mesmo
"O comando correu sem erros" não é prova nenhuma. Quatro verificações que dizem realmente alguma coisa:
Primeiro, o estado geral.
ufw status verbose
Espera-se uma saída com esta forma:
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp LIMIT IN Anywhere
22/tcp (v6) LIMIT IN Anywhere (v6)
Decisiva é a segunda linha, com o acrescento (v6). Sem ela falta a cobertura de IPv6.
Segundo, o reinício. Uma firewall que não sobrevive a um reinício não vale nada.
systemctl is-enabled ufw
A resposta tem de ser enabled. E depois reinicie mesmo o servidor uma vez e autentique-se. É o único teste que responde de facto à pergunta.
Terceiro, a vista de fora. Verifique a partir de outro host se uma porta deliberadamente não aberta está mesmo fechada, por exemplo com nc -zv O-SEU-IP 3306. Importante: um teste a partir de localhost não prova rigorosamente nada, porque o tráfego pela interface de loopback é sempre deixado passar pelo UFW.
Quarto, os logs. Aqui há uma diferença real entre as distribuições. Na predefinição low, o UFW regista os pacotes bloqueados através do log do kernel. No Ubuntu 22.04 e 24.04 existe o rsyslog, e as mensagens vão parar adicionalmente a /var/log/ufw.log. No Debian 12 e sobretudo no Debian 13, o rsyslog falta nas instalações mínimas, e aí esse ficheiro simplesmente não existe. O caminho que funciona em todo o lado:
journalctl -k -n 50
Procuram-se linhas que comecem por [UFW BLOCK]. Se quiser ver mais, suba o nível (ufw logging medium) e volte depois a ufw logging low. Num servidor com tráfego de público, o nível mais alto enche o disco mais depressa do que se pensa.
Mensagens de erro à letra
ERROR: problem running ufw-init: o motivo mais frequente é um conflito com uma segunda ferramenta de firewall, normalmente nftables.service ou iptables-persistent, ou uma mistura entre backend legacy e nft. Verifique iptables -V e desligue os serviços concorrentes. O UFW traz além disso um script de verificação que percorre um a um os requisitos do kernel e indica qual a exigência de módulo que falha.
ERROR: Could not find a profile matching 'OpenSSH': falta o perfil de aplicação, porque o openssh-server não está instalado ou o ficheiro sob /etc/ufw/applications.d/ foi removido. No Ubuntu, esse diretório está de qualquer forma vazio enquanto não houver serviços instalados. Use antes o número da porta.
ERROR: Bad port: normalmente um erro de escrita ou um nome de serviço que o /etc/services não conhece. Os números de porta são sempre inequívocos.
Skipping adding existing rule ou Skipping adding existing rule (v6): não é uma mensagem de erro, é o aviso de que a regra já lá está. Se pensa ter alterado uma regra e ela continua igual, a causa é esta.
ERROR: Invalid position '0': ao apagar e ao inserir, o UFW conta a partir de 1, não a partir de 0. Os números vêm do ufw status numbered e deslocam-se depois de cada eliminação. Por isso, apague sempre do número mais alto para o mais baixo.
WARN: Rules updated but not applied: a regra está na configuração, mas a firewall está inativa. Falta um ufw enable.
Diferenças entre distribuições num relance
- Debian 13 (trixie): UFW tem de ser instalado à parte. Backend nf_tables.
IPV6=yesde fábrica. O rsyslog falta nas instalações mínimas, por isso os logs vão porjournalctl -k. SSH através dessh.service. - Debian 12 (bookworm): UFW tem de ser instalado à parte. Backend nf_tables.
IPV6=yesde fábrica. O rsyslog existe conforme a variante de instalação, portanto/var/log/ufw.lognão está garantido. SSH através dessh.service. - Ubuntu 24.04 LTS: UFW presente nas imagens de servidor, mas inativo. Backend nf_tables.
IPV6=yesde fábrica./var/log/ufw.logpresente. SSH por ativação de socket através dessh.socket, por isso verifique a porta à escuta sempre comsudo ss -lntp | grep sshde não comsshd -T. - Ubuntu 22.04 LTS: UFW presente nas imagens de servidor, mas inativo. Backend nf_tables.
IPV6=yesde fábrica./var/log/ufw.logpresente. SSH através dessh.service.
O que é igual nos quatro sistemas: depois da instalação, o UFW está sempre inativo. Ninguém lhe liga a firewall às escondidas, e ninguém a desliga às escondidas.
O Docker contorna o UFW
Se o servidor tiver Docker a correr, aplica-se uma limitação importante: as portas de container publicadas ignoram as suas regras de UFW. O Docker cria cadeias próprias e trabalha com tradução do endereço de destino, de modo que os pacotes passam ao lado das cadeias do UFW em INPUT. Um docker run -p 8080:80 fica portanto acessível a partir do exterior, apesar de o ufw status mostrar um "deny incoming" impecável.
A contramedida mais simples e mais robusta é não publicar logo em todos os endereços, mas apenas localmente, e encaminhar o acesso através de um reverse proxy:
docker run -d -p 127.0.0.1:8080:80 nginx
Num ficheiro Compose, isso corresponde à indicação de porta "127.0.0.1:8080:80". Em alternativa, existe a possibilidade de pendurar regras próprias do UFW na cadeia DOCKER-USER, reservada pelo Docker. É eficaz, mas exige manutenção e tem de ser revista a cada atualização do Docker. A ligação a 127.0.0.1 resolve o problema na raiz.
Se ficar mesmo trancado fora
Aconteceu, o SSH já não responde. Por ordem:
- Não reiniciar. Um reinício não ajuda, porque o UFW está ativado como serviço systemd e repõe as suas regras no arranque. O servidor volta tão fechado como ficou ao ser desligado.
- Abrir a consola VNC na área de cliente e autenticar-se aí como root.
- Desligar a firewall:
ufw disable. Com isto volta ao jogo, mas também volta a ficar desprotegido. - Encontrar a causa, não adivinhar. Consulte o
ufw status numberede a porta SSH real emss -lntup. Em nove de cada dez casos, a causa é uma destas três: nunca existiu uma regra de permissão para SSH, a regra está na porta 22 enquanto o sshd escuta noutra porta, ou a autorização estava limitada a um endereço IP que a sua ligação entretanto já não tem (atribuição dinâmica de endereços no acesso à internet). - Corrigir e voltar a ligar, desta vez pela ordem certa e com o interruptor de homem-morto ativo.
Se quiser repor por completo o conjunto de regras, existe o ufw reset. Importa saber: este comando desativa a firewall e guarda cópias de segurança dos ficheiros de regras anteriores sob /etc/ufw/, com data e hora no nome do ficheiro. Em caso de dúvida pode portanto reler o que estava em vigor antes. Nunca o execute através de uma ligação SSH sem ter a consola aberta ao lado.
Uma configuração inicial sensata
Para um servidor web típico, a sequência completa é esta:
apt-get update
apt-get install -y ufw
ufw default deny incoming
ufw default allow outgoing
ufw limit 22/tcp comment 'SSH rate limit'
ufw allow 80/tcp comment 'HTTP'
ufw allow 443/tcp comment 'HTTPS'
ufw --force enable
ufw status verbose
Repare que aqui o default deny incoming está antes da regra de SSH, mas a ativação acontece mesmo no fim. Enquanto o UFW estiver inativo, a política predefinida não faz mal nenhum. Crítico é apenas o estado no momento do enable, e nessa altura a regra de SSH já está registada há muito.
Quem, para além disso, quiser tornar os acessos de administração alcançáveis apenas a partir da sua própria rede, trabalha com regras limitadas por origem segundo o modelo ufw allow from 203.0.113.0/24 to any port 22 proto tcp. Ainda mais limpo é não colocar de todo os serviços de administração na rede aberta, ligando-os antes através de uma VPN. Uma instância WireGuard fica montada em poucos minutos em qualquer um dos quatro sistemas e substitui uma pilha inteira de exceções de firewall por uma única porta UDP aberta.
E, para terminar, a avaliação que convém ter presente: o UFW filtra no próprio servidor. Contra ataques volumétricos, que saturam a ligação, isso não ajuda por princípio, porque os pacotes já percorreram a linha quando o seu kernel os descarta. Para isso é precisa filtragem na rede a montante. Na KernelHost, disso trata a infraestrutura a montante no datacenter maincubes em Frankfurt am Main, com filtragem em tempo real Arbor. A sua firewall local e a proteção de rede resolvem dois problemas diferentes, e precisa das duas.
Perguntas frequentes
O "ufw enable" tranca-me logo fora da sessão SSH que tenho aberta?
Tenho de ativar o IPv6 no UFW em separado?
O UFW usa iptables ou nftables?
O que faz exatamente o ufw limit?
Já não consigo chegar ao servidor por SSH. Um reinício ajuda?
Porque é que os meus containers Docker estão acessíveis do exterior apesar da firewall UFW ativa?
Porque é que /var/log/ufw.log não existe no meu servidor Debian?
2026 KernelHost GmbH. Todos os direitos reservados. Este guia está protegido por direitos de autor. A sua republicação noutros sites, na íntegra, em parte ou de forma editada, não é permitida sem o nosso consentimento por escrito. Citações com indicação da fonte e ligação são expressamente bem-vindas.

