firewalld no Rocky e AlmaLinux: regras para VPS
Aprenda a abrir SSH e portas web, fechar serviços e manter regras após reiniciar. Entenda zonas e evite o erro comum da flag --permanent no firewalld.
O que é o firewalld e por que o Rocky e o AlmaLinux o incluem
O firewalld é o gestor de firewall instalado por predefinição no Rocky Linux, AlmaLinux e nas outras reconstruções do Red Hat Enterprise Linux (RHEL). Ele não inspeciona os pacotes diretamente. Mantém uma configuração guardada e transforma essa configuração em regras nftables. O comando firewall-cmd permite editá-la enquanto o servidor continua online.
Se já sabe como funciona o ufw num VPS Ubuntu, conhece a finalidade. O firewalld acrescenta duas ideias que o ufw não tem. A primeira são as zonas: uma política com nome para a qual os pacotes são encaminhados. A segunda é a separação entre as regras ativas e as regras guardadas. Essa separação é controlada pela flag --permanent e é a maior fonte de confusão nesta ferramenta.
Tudo o que se segue são comandos que executa no seu próprio servidor. Teste cada alteração a partir de uma segunda máquina, porque uma regra que parece correta no servidor pode continuar errada quando é avaliada a partir da Internet.
Abra o SSH antes de fazer qualquer outra coisa
A maioria das instalações de Rocky e AlmaLinux já tem o firewalld instalado e em execução, e a configuração fornecida permite SSH. Algumas imagens de cloud minimalistas removem-no. Verifique em vez de presumir.
sudo dnf install -y firewalld
sudo systemctl enable --now firewalld
sudo firewall-cmd --statefirewall-cmd --state imprime running. Se o serviço estiver parado, todas as outras chamadas firewall-cmd respondem FirewallD is not running e terminam com um código diferente de zero. Esta é a primeira coisa a verificar quando um comando parece não fazer absolutamente nada.
Agora veja o que está atualmente permitido.
sudo firewall-cmd --list-allA saída real tem mais algumas linhas. Estas são as relevantes:
public (active)
target: default
interfaces: eth0
sources:
services: cockpit dhcpv6-client ssh
ports:
rich rules:ssh na linha services: é o motivo pelo qual a sua sessão continua a funcionar. Se estiver ausente, adicione-o antes de fazer qualquer outra alteração, porque iniciar um firewall sem uma regra para SSH termina a sessão e não permite voltar a ligar-se.
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reloadtarget: default significa que um pacote que não corresponde a nenhuma regra é rejeitado com uma resposta ICMP (Internet Control Message Protocol) host-prohibited, por isso um cliente que aceda a uma porta fechada recebe No route to host imediatamente. Definir o destino como DROP faz com que o servidor permaneça silencioso, e os scanners aguardam então até ocorrer um timeout.
sudo firewall-cmd --permanent --zone=public --set-target=DROP
sudo firewall-cmd --reloadTenha em conta o impacto antes de executar isto: DROP também impede que o servidor responda a ping, pelo que o seu próprio monitoramento também fica silencioso.
Por que a minha regra desapareceu? A opção --permanent
O firewalld mantém duas configurações ao mesmo tempo. A configuração de runtime é a que o kernel aplica neste momento. A configuração permanente é a que fica em /etc/firewalld/zones/public.xml e que volta a ser aplicada depois de um reload ou reboot.
Um comando sem --permanent altera apenas a configuração de runtime. A alteração tem efeito imediato e desaparece no próximo reload ou boot. Um comando com --permanent escreve no ficheiro e não altera o que está em execução, por isso a porta permanece fechada até fazer reload. Nenhum destes comportamentos é um erro. Ambos confundem os administradores porque o comando apresenta success nos dois casos.
Escreva sempre o par.
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reloadPode consultar as duas configurações. Esta é a forma mais rápida de descobrir qual dos dois erros cometeu.
sudo firewall-cmd --list-services
sudo firewall-cmd --permanent --list-servicesO primeiro comando apresenta o conjunto ativo. O segundo apresenta o conjunto guardado. Se o conjunto ativo tiver um serviço que não existe no conjunto guardado, essa regra desaparece no próximo reload. Se o conjunto guardado tiver uma regra que não existe no conjunto ativo, esqueceu-se de fazer reload. sudo firewall-cmd --runtime-to-permanent copia tudo o que está ativo para o ficheiro guardado. Isto é útil depois de uma sessão de testes.
--reload mantém o estado de acompanhamento das ligações, por isso a sua sessão SSH continua ativa. --complete-reload também recarrega os módulos do kernel e perde esse estado, o que normalmente termina todas as ligações abertas, incluindo a sua. Use o reload normal.
Existe uma rede de segurança integrada. Uma regra de runtime pode expirar sozinha.
sudo firewall-cmd --add-service=http --timeout=5mEssa regra remove-se ao fim de cinco minutos. Não pode ser combinada com --permanent. Esse é precisamente o objetivo: testar uma alteração sobre a qual não tem a certeza. A rede de segurança mais antiga é melhor. Mantenha uma segunda sessão SSH aberta enquanto edita as regras e não a feche até um novo login confirmar que as regras novas funcionam.
Zonas e por que apenas a zona predefinida importa numa VPS
Uma zona é um conjunto nomeado de permissões associado a um nível de confiança. O firewalld coloca cada pacote recebido exatamente numa zona. Primeiro, compara o endereço de origem do pacote com a lista sources: de cada zona. Se não houver correspondência, usa a zona à qual a interface de entrada está associada. Se a interface não estiver associada a nenhuma zona, o pacote vai para a zona predefinida.
sudo firewall-cmd --get-default-zone
sudo firewall-cmd --get-active-zonesNuma VPS com uma interface de rede, a primeira resposta é quase sempre public, e essa é a única zona que usará. firewall-cmd sem o argumento --zone= atua sobre a zona predefinida. É por isso que todos os comandos curtos deste guia funcionam sem indicar uma zona.
Este é o erro que pode consumir uma tarde inteira. Se a interface estiver associada a outra zona, as suas regras são adicionadas a public, mas o tráfego é processado noutra zona. Por isso, nada do que adicionar terá efeito e nenhum aviso será apresentado. --get-active-zones mostra a associação:
public
interfaces: eth0Se a interface aparecer com outro nome de zona, escreva as regras nessa zona usando --zone= ou mova a interface.
sudo firewall-cmd --permanent --zone=public --change-interface=eth0
sudo firewall-cmd --reloadO NetworkManager gere as interfaces em Rocky e AlmaLinux e reaplica a zona quando a ligação é ativada. Configure também essa associação no NetworkManager para que um reboot não desfaça o trabalho. Obtenha o nome da ligação no primeiro comando, porque raramente é igual ao nome do dispositivo.
sudo nmcli connection show
sudo nmcli connection modify "System eth0" connection.zone publicA correspondência por origem tem prioridade sobre a correspondência por interface. É assim que um endereço pode receber uma política diferente. A zona predefinida trusted aceita tudo.
sudo firewall-cmd --permanent --zone=trusted --add-source=203.0.113.10/32
sudo firewall-cmd --reloadTenha cuidado com essa zona. Ela abre todas as portas do servidor para esse endereço, incluindo a base de dados que considerava privada. Use uma regra rica quando quiser permitir uma porta específica, e não um host inteiro.
O que é um serviço do firewalld?
Um serviço é um conjunto nomeado de portas distribuído como um ficheiro XML. --add-service=https abre 443/tcp porque /usr/lib/firewalld/services/https.xml define o que https significa.
sudo firewall-cmd --get-services
sudo firewall-cmd --info-service=https--info-service mostra as portas associadas ao nome:
https
ports: 443/tcpUse o nome quando existir. A configuração fica clara em --list-all mesmo seis meses depois, e pacotes como o Cockpit instalam o seu próprio ficheiro de serviço. Use --add-port para tudo o que não tenha uma definição.
A diferença que deve verificar: o serviço ssh significa 22/tcp e nada mais. Se mudou o SSH para outra porta ao reforçar o acesso SSH ao servidor, então --add-service=ssh não abre a porta que realmente utiliza.
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reloadNuma reinstalação do RHEL, existe um segundo bloqueio nessa porta. O SELinux (security-enhanced Linux) atribui etiquetas aos números de porta, e sshd não tem permissão para associar-se a uma porta fora das etiquetas atribuídas. Nesse caso, recusa iniciar e o log mostra error: Bind to port 2222 on 0.0.0.0 failed: Permission denied. Atribua primeiro a etiqueta à porta.
sudo dnf install -y policycoreutils-python-utils
sudo semanage port -a -t ssh_port_t -p tcp 2222Como vejo o que está aberto agora?
sudo firewall-cmd --list-all
sudo firewall-cmd --list-rich-rules
sudo nft list table inet firewalld | head -n 40Os dois primeiros comandos mostram o que o firewalld considera permitido. O terceiro lê as regras que o kernel mantém efetivamente, na tabela gerida pelo firewalld. Os resultados devem coincidir.
Nada disso é uma prova. Teste a partir de outra máquina:
nc -zv 203.0.113.20 443Não execute o teste no próprio servidor. O firewalld aceita tudo o que chega pela interface de loopback, por isso curl http://localhost:8080 é bem-sucedido independentemente das regras configuradas. Esse teste mostra que o serviço está ativo. Não fornece qualquer informação sobre o firewall.
Permitir uma porta web
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo firewall-cmd --list-servicesO último comando deve agora listar http https juntamente com o que já estava presente. Se o site continuar sem responder, o firewall pode não ser o problema. Uma regra permite a passagem de um pacote. Ainda tem de existir um processo a escutar nessa porta.
sudo ss -tlnpUm socket apresentado como 0.0.0.0:443 ou *:443 aceita ligações de qualquer endereço. Um apresentado como 127.0.0.1:443 responde apenas na interface de loopback, e nenhuma regra de firewall o tornará acessível a partir do exterior. Portas e sockets em escuta no Linux explica esta diferença com mais detalhe.
Como volto a fechar uma porta?
sudo firewall-cmd --permanent --remove-service=http
sudo firewall-cmd --reloadA regra --permanent também se aplica aqui, e tem consequências mais graves neste sentido. Remova um serviço apenas do estado em execução e a porta parecerá fechada. No próximo reload ou reboot, ela será reaberta a partir do ficheiro guardado. Isto cria uma falha que não detetará, porque a verificação que executou foi bem-sucedida.
Remover algo que nunca esteve presente apresenta Warning: NOT_ENABLED: http e, ainda assim, termina com o código 0. Adicionar o mesmo elemento duas vezes apresenta Warning: ALREADY_ENABLED: http. Ambos os casos são seguros. Um nome escrito incorretamente é diferente: Error: INVALID_SERVICE significa que o firewalld não tem nenhuma definição com esse nome e que nada foi alterado.
Se --list-all apresentar cockpit e não utilizar a consola Web Cockpit na porta 9090, remova-o. Cada porta aberta corresponde a um serviço que tem de manter atualizado com patches.
Restringir uma porta a um endereço de origem
As rich rules são a forma completa de definir regras, quando um nome de serviço simples não consegue expressar o que pretende. Restringir o SSH a um endereço do escritório requer dois comandos, e o segundo é o que muitas pessoas esquecem.
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" service name="ssh" accept'
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --reloadUma zona é um conjunto de permissões, não uma lista numerada que para na primeira correspondência. A rich rule adiciona uma permissão para um endereço. Não nega o acesso a ninguém. Enquanto ssh continuar na linha services:, toda a Internet continuará a alcançar a porta 22 e a rich rule não alterará nada que possa medir. Remova a entrada abrangente, caso contrário a regra restrita será apenas decorativa.
Para uma porta sem nome de serviço, indique a porta diretamente.
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.10/32" port port="5432" protocol="tcp" accept'Para descartar uma rede ruidosa e manter um registo, coloque o elemento de log antes da ação. Essa é a ordem esperada pela linguagem de rich rules.
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="198.51.100.0/24" log prefix="fw-drop " level="info" limit value="3/m" drop'
sudo firewall-cmd --reload
sudo journalctl -k -g fw-dropO valor limit impede que uma inundação de pacotes encha o journal. Antes de restringir o SSH a um único endereço, confirme que esse endereço é estável. Uma ligação doméstica com um endereço IP dinâmico pode bloquear o seu acesso no dia em que o endereço mudar. Por isso, teste primeiro o acesso à consola do fornecedor e confirme que está a funcionar.
comandos do ufw e equivalentes do firewall-cmd
As tarefas são as mesmas, mas a ferramenta é diferente. Cada linha --permanent precisa de um sudo firewall-cmd --reload depois dela. Esta é a única informação que uma lista como esta não consegue mostrar.
sudo ufw enabletorna-sesudo systemctl enable --now firewalldsudo ufw disabletorna-sesudo systemctl disable --now firewalldsudo ufw status verbosetorna-sesudo firewall-cmd --list-allsudo ufw allow OpenSSHtorna-sesudo firewall-cmd --permanent --add-service=sshsudo ufw allow 443/tcptorna-sesudo firewall-cmd --permanent --add-port=443/tcpsudo ufw delete allow 443/tcptorna-sesudo firewall-cmd --permanent --remove-port=443/tcpsudo ufw allow from 203.0.113.10 to any port 22torna-se a rich rule apresentada acimasudo ufw reloadtorna-sesudo firewall-cmd --reloadsudo ufw default deny incomingjá corresponde ao comportamento da zonapublic, e--set-target=DROPé a sua versão silenciosasudo ufw logging ontorna-sesudo firewall-cmd --set-log-denied=all
É importante indicar claramente uma diferença. O ufw mantém uma lista numerada, e pode inserir uma regra na posição 1. O firewalld não tem números de regras, por isso "colocar esta regra primeiro" não tem significado neste caso. Quando duas entradas do firewalld parecem entrar em conflito, a aceitação abrangente prevalece, porque nada no conjunto nega o tráfego. A entrada abrangente tem de ser removida manualmente.
Por que meu container Docker está acessível quando o firewall parece fechado?
Porque uma porta publicada pelo container nunca passa pela parte do firewall controlada pela sua zona. docker run -d -p 8080:80 nginx instrui o Docker a criar as próprias regras de NAT (network address translation) e encaminhamento. Um pacote recebido na porta 8080 é reescrito e encaminhado para o container, portanto é encaminhado em vez de ser entregue ao host. As linhas services: e ports: da sua zona controlam os pacotes entregues ao host. As regras do Docker controlam o caminho de encaminhamento e aceitam esses pacotes.
O resultado é um servidor em que sudo firewall-cmd --list-all não mostra a porta 8080, mas nc -zv 203.0.113.20 8080 a partir de outra máquina estabelece a ligação mesmo assim. Veja o que o Docker instalou:
sudo iptables -t nat -L DOCKER -nA correção está na flag de publicação. Vincule a porta ao loopback e coloque um reverse proxy à frente dela.
docker run -d -p 127.0.0.1:8080:80 nginxAgora o container responde a curl http://127.0.0.1:8080 no servidor, e nada responde externamente. Os utilizadores do Ubuntu enfrentam o mesmo problema, descrito em por que os containers Docker publicam portas diretamente através do ufw. O Podman rootful, que o Rocky e o AlmaLinux disponibilizam nos repositórios base, publica portas usando a mesma abordagem de NAT. Por isso, teste a partir de outra máquina em vez de confiar na lista da zona.
Fazer a configuração sobreviver a um reboot e os erros que verá
sudo systemctl is-enabled firewalld
sudo systemctl status firewalldenabled e active (running) são o que pretende. Um firewall em execução, mas não ativado, protege-o apenas até ao primeiro reboot. Essa verificação deve fazer parte da lista que segue nos primeiros dez minutos num VPS novo, juntamente com as chaves SSH e as atualizações.
Comandos nftables diretos e firewalld não são compatíveis. O firewalld gere uma tabela chamada inet firewalld. sudo nft flush ruleset elimina-a, deixando o servidor aberto a tudo, enquanto firewall-cmd --list-all continua a mostrar a configuração pretendida, porque o firewalld apresenta aquilo que considera estar configurado, e não o que o kernel mantém. sudo firewall-cmd --reload reinstala as regras. Escreva as regras com firewall-cmd para que voltem a ser aplicadas depois de um reload.
Dois gestores de firewall no mesmo servidor. Instalar ufw ou iptables-services juntamente com o firewalld coloca dois programas a escrever regras sem conhecimento um do outro. O resultado depende de qual serviço arrancou por último. Escolha apenas um. No Rocky e no AlmaLinux, o firewalld é o que tem suporte da distribuição.
O firewall do fornecedor à frente do servidor. Muitos painéis de VPS têm um firewall de rede separado. Se --list-all mostrar uma porta aberta e uma ligação externa continuar a falhar, verifique o painel antes de alterar alguma coisa no servidor. O inverso também acontece: uma regra aberta no painel não produz efeito enquanto o firewalld rejeitar o pacote.
Executar firewall-cmd sem sudo. Todas as alterações exigem root. Sem esse privilégio, o pedido é recusado por uma verificação de autorização e nada é alterado. À primeira vista, isto pode parecer que o comando foi ignorado.
Seis comandos cobrem a maioria das situações: --list-all para consultar o estado, --permanent --add-service ou --add-port para abrir algo, --permanent --remove-service para fechar, --reload para aplicar o ficheiro guardado e --runtime-to-permanent depois de uma ronda de testes. A zona é public, a opção é --permanent e a única verificação fiável vem de outra máquina.
FAQ
Porque é que a regra do firewalld desapareceu depois de um reboot?
A regra foi adicionada apenas à configuração de runtime. sudo firewall-cmd --add-service=http aplica a alteração imediatamente, mas ela é descartada no reload ou no boot seguinte, porque a configuração guardada em /etc/firewalld/zones/public.xml nunca foi alterada. Adicione --permanent e execute sudo firewall-cmd --reload. Para manter regras que já adicionou manualmente, execute sudo firewall-cmd --runtime-to-permanent, que copia o conjunto ativo para o ficheiro guardado.
Porque é que nada muda depois de adicionar uma regra com --permanent?
Porque --permanent escreve no ficheiro e não altera o firewall em execução. A porta permanece fechada até sudo firewall-cmd --reload carregar a configuração guardada no kernel. Compare sudo firewall-cmd --list-services com sudo firewall-cmd --permanent --list-services: se a lista guardada tiver uma entrada que não existe na lista ativa, falta executar o reload.
Devo usar --add-service ou --add-port?
Use --add-service quando existir um nome para o serviço que executa. Isto declara a intenção, e sudo firewall-cmd --info-service=https mostra exatamente que portas o nome abrange. Use --add-port quando nada definir o seu serviço ou quando ele escutar numa porta não padrão. O serviço ssh significa apenas 22/tcp, por isso, se o SSH tiver sido movido para 2222, precisa de --add-port=2222/tcp e de um label SELinux para essa porta.
Porque é que o meu contentor Docker está acessível quando firewall-cmd mostra a porta fechada?
Uma porta publicada é reescrita pelas próprias regras NAT do Docker e encaminhada para o contentor. Por isso, o pacote nunca é entregue ao host, e as listas de serviços e portas de uma zone abrangem apenas os pacotes entregues ao host. O contentor responde a partir da Internet enquanto --list-all não mostra nada. Publique a porta apenas na loopback com docker run -d -p 127.0.0.1:8080:80 nginx e coloque um reverse proxy à frente do serviço.
Posso instalar ufw no Rocky Linux em vez do firewalld?
Dois gestores de firewall no mesmo servidor escrevem regras sem conhecimento um do outro, e o conjunto que permanece depende de qual serviço arrancou por último. O firewalld é a ferramenta suportada no Rocky Linux e no AlmaLinux, já está instalado e utiliza o mesmo backend nftables que o ufw utilizaria. Aprenda uma vez a zone predefinida e a flag --permanent para dominar toda a ferramenta.