Uma porta aberta e esquecida é a forma clássica de entrar num servidor: um banco de dados num endereço público, um painel de depuração, um container que publicou uma porta que ninguém queria publicar. A verificação leva um minuto quando você conhece dois comandos e uma armadilha. Fizemos tudo abaixo num servidor Ubuntu 24.04 novo da nossa imagem e o escaneamos a partir de uma segunda máquina, então as saídas são as que você verá no seu próprio servidor.
1. O que está escutando
O comando é ss. As letras significam TCP, UDP, sockets em escuta, portas numéricas e nome do processo:
sudo ss -tulpn
Num servidor novo ele imprime seis linhas:
Netid State Local Address:Port Process
udp UNCONN 127.0.0.54:53 systemd-resolve
udp UNCONN 127.0.0.53%lo:53 systemd-resolve
tcp LISTEN 127.0.0.54:53 systemd-resolve
tcp LISTEN 127.0.0.53%lo:53 systemd-resolve
tcp LISTEN 0.0.0.0:22 sshd
tcp LISTEN [::]:22 sshd
A coluna que importa é Local Address. Ela diz quem pode se conectar:
| Endereço | Quem alcança |
|---|---|
127.0.0.1:… ou 127.0.0.53 | Só programas neste servidor |
0.0.0.0:… | Qualquer um por IPv4, salvo bloqueio do firewall |
[::]:… | Qualquer um por IPv6 (e normalmente IPv4 também) |
10.0.0.5:… (um endereço específico) | Qualquer um que alcance esse endereço |
Ou seja, o servidor novo expõe exatamente uma coisa: SSH na porta 22, por IPv4 e IPv6. O resolvedor DNS em 127.0.0.53 é privado.
Para listar só o que a internet poderia ver, filtre os endereços de loopback:
sudo ss -tlnp | awk 'NR>1 && $4 !~ /^(127\.|\[::1\])/ {print $4, $6}'
Rode isso depois de cada instalação. Instalamos nginx, Docker e um pequeno app em Python, e a lista cresceu de 2 para 10 linhas, incluindo um docker-proxy em 0.0.0.0:8081 e um socket do containerd numa porta localhost aleatória.
2. Qual processo ocupa uma porta
Quando uma porta está ocupada e você quer saber por quem:
sudo lsof -i :80 -P -n
sudo fuser 80/tcp
sudo ss -tlnp 'sport = :80'
O lsof é o mais legível. Para o nginx ele listou um processo mestre como root e um worker como www-data, ambos em *:80, para IPv4 e IPv6. O fuser imprime só os PIDs, útil em scripts. Se precisar liberar a porta, pare o serviço em vez de matar o PID, senão o systemd o reinicia: sudo systemctl stop nginx.
3. O que a internet vê
O ss mostra a visão do servidor. A visão da internet pode ser diferente, porque há um firewall no meio ou porque um serviço está ligado a 127.0.0.1. Para ver o quadro real, escaneie o servidor de outra máquina, o seu notebook ou um segundo servidor. A ferramenta é o nmap (apt install nmap, ou o instalador de nmap.org no Windows e no macOS).
nmap your-server-ip
nmap -p- your-server-ip
nmap -p 22,80,443,3306 your-server-ip
A primeira forma verifica as 1.000 portas mais comuns, a segunda todas as 65.535, a terceira uma lista. No nosso servidor novo, antes de qualquer firewall:
Not shown: 999 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
Nmap done: 1 IP address (1 host up) scanned in 6.83 seconds
O scan completo das 65.535 portas levou 8,9 segundos e achou a mesma porta única. Portas fechadas respondem na hora com um reset, por isso escaneá-las é rápido.
Três estados importam:
- open: um programa aceitou a conexão. Essa é a sua superfície de ataque.
- closed: o servidor respondeu que nada escuta ali. Seguro, mas confirma que o servidor existe e o que ele não roda.
- filtered: sem resposta. Um firewall descartou o pacote. O scanner não sabe se há um serviço por trás.
Para uma única porta sem instalar nada, nc -zv ip-do-servidor 443 informa succeeded ou refused, e fica parado até o tempo limite numa porta filtrada. Para UDP, acrescente -sU ao nmap e espere lentidão; no nosso servidor as três portas UDP comuns, 53, 123 e 161, estavam closed.
Uma observação sobre os planos NAT: pela internet só chega a porta SSH que redirecionamos para você. Um scan do endereço compartilhado mostra a nossa borda, não o seu servidor, e as outras portas são inacessíveis, seja o que for que escute nelas. Num plano com IPv4 próprio, cada porta aberta é sua para gerenciar.
4. Feche o que não precisa
Três maneiras, da melhor à mais ampla.
Ligar ao localhost. Se só programas do servidor usam um serviço, faça-o escutar em 127.0.0.1. Iniciamos um pequeno app web com --bind 127.0.0.1 8080: o ss o mostrou em 127.0.0.1:8080 e o nmap de fora informou 8080/tcp closed. Para bancos de dados esse é o padrão correto: Redis, MySQL e PostgreSQL escutam em localhost de fábrica no Ubuntu, e nossos guias de Redis e MySQL mantêm assim.
Parar o serviço. sudo systemctl disable --now nome remove o listener de vez. Confirme com ss que ele sumiu.
Ligar o firewall. Libere primeiro o SSH, depois o que você serve, e então ative:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw enable
sudo ufw status verbose
O scan de fora mudou completamente:
Not shown: 997 filtered tcp ports (no-response)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
8081/tcp open blackice-icecap
Nmap done: 1 IP address (1 host up) scanned in 10.18 seconds
Cada porta não liberada passou de closed para filtered, e o scan completo das 65.535 portas passou a levar 110 segundos em vez de 8,9. Mais tempo por porta para o scanner, menos informação sobre o seu servidor. Nosso guia do UFW trata de limite de taxa, liberar um único IP e IPv6.
Mas olhe a terceira linha.
5. A surpresa do Docker
A porta 8081 continuou aberta com o firewall ligado e sem regra que a liberasse. Essa porta pertence a um container que iniciamos com docker run -p 8081:80 nginx:alpine. O Docker acrescenta suas próprias regras ao firewall do kernel, e elas são avaliadas antes das do ufw. A regra que ele escreveu no nosso servidor:
-A DOCKER ! -i docker0 -p tcp --dport 8081 -j DNAT --to-destination 172.17.0.2:80
Qualquer pacote para a porta 8081 é encaminhado para o container antes que o ufw tenha voz. Todo container com -p ou uma entrada em ports: faz isso, e o ss mostra honestamente como docker-proxy em 0.0.0.0:8081.
A correção é publicar só no localhost:
docker rm -f web
docker run -d --name web -p 127.0.0.1:8081:80 nginx:alpine
No Compose, escreva "127.0.0.1:8081:80" em ports:. Depois da mudança, o ss mostrou 127.0.0.1:8081, a regra do Docker ganhou -d 127.0.0.1/32 e o scan de fora informou 8081/tcp filtered. Coloque o nginx como proxy reverso na frente para o lado público, com HTTPS, para que as únicas portas abertas no servidor sejam 22, 80 e 443.
Checklist depois de cada mudança
sudo ss -tulpn: cada linha com0.0.0.0ou[::]é algo que você quis expor?nmap ip-do-servidordo seu notebook: as portas abertas batem?- Um banco de dados, cache ou painel de administração num endereço público: mova hoje para
127.0.0.1ou para trás do firewall. - Cada entrada
ports:do Docker: começa com127.0.0.1:se não for a porta web pública? - SSH exposto a todos: acrescente fail2ban ou login só por chave.
No Windows Server, a mesma verificação é Get-NetTCPConnection -State Listen, e as regras ficam no Firewall do Windows. Todo plano Linux, a partir do Nano de $3, tem uma versão com endereço IPv4 próprio, e nela o que você vê no ss é o que o mundo vê, menos o seu firewall.
Comentários
Nenhum comentário ainda. Seja o primeiro.