A lista de portas abertas é a lista de caminhos até ao seu servidor. Tudo o resto — firewall, WAF, deteção de intrusões — constrói-se por cima. Por isso «o que está à escuta aqui» vem em primeiro lugar em qualquer auditoria, e é também a verificação que mais vezes dá um resultado desagradável: metade do que aparece não foi aberta por si mas pelo instalador de algum pacote.
Ler a saída
sudo ss -tulpn
As opções: t para TCP, u para UDP, l só o que escuta, p o processo, n para os números continuarem números. Sem sudo a coluna do processo fica vazia e o exercício perde sentido.
O que importa é a coluna Local Address:Port, e a diferença ali é essencial:
127.0.0.1:3306: o serviço só é alcançável a partir da própria máquina. Isso é bom;0.0.0.0:3306: a partir de qualquer endereço IPv4, ou seja, da internet. É isso que há que verificar;[::]:3306: o mesmo para IPv6. Uma linha à parte, e passa despercebida com regularidade;203.0.113.25:443: num endereço concreto, normalmente de propósito.
Depois, para cada linha com 0.0.0.0 ou [::], faça uma pergunta: deve um estranho conseguir chegar aqui? Para 80 e 443 a resposta é sim. Para quase tudo o resto, não.
Os achados habituais
Redis, porta 6379. A linha mais perigosa que existe. Por omissão o Redis não exige palavra-passe, e os seus comandos permitem escrever um ficheiro no disco, ou seja, uma chave alheia no authorized_keys. Entre o Redis aparecer num endereço público e ser aproveitado passam horas, por vezes menos. Verifique bind 127.0.0.1 e protected-mode yes na configuração.
Memcached, 11211/UDP. Mesmo que lá dentro não haja nada de valioso, o seu servidor torna-se amplificador de ataques alheios, e a reclamação virá do alojamento.
MySQL e PostgreSQL, 3306 e 5432. Há palavra-passe, mas tenta-se contra ela sem parar, e as versões das bases de dados são atualizadas menos vezes do que seria desejável. Quase nunca precisam de estar expostas: a aplicação vive na mesma máquina, e para o seu trabalho basta um túnel SSH.
Elasticsearch 9200, MongoDB 27017. Historicamente sem autenticação por omissão. As instâncias públicas destes serviços são fonte constante de notícias sobre fugas de dados.
A API do Docker, 2375. Uma porta de comando do Docker aberta é root no anfitrião sem palavra-passe nenhuma. Costuma aparecer depois de experiências com acesso remoto ao Docker.
Painéis de gestão e phpMyAdmin nas suas portas: 8080, 8083, 10000. Não é que nunca se devam abrir, mas são eles que concentram o grosso das tentativas.
Corrigir no serviço, não na firewall
A vontade de fechar cada achado com uma regra do UFW compreende-se, mas essa é a segunda linha, não a primeira. Uma regra pode ser apagada por engano, uma firewall desligada por momentos durante uma depuração, e o Docker publica portas contornando o UFW por completo. Uma definição de associação na configuração do serviço sobrevive a tudo isso:
- MySQL/MariaDB:
bind-address = 127.0.0.1; - PostgreSQL:
listen_addresses = 'localhost'; - Redis:
bind 127.0.0.1 ::1; - Docker Compose: publicação como
"127.0.0.1:5432:5432".
A firewall põe-se por cima como seguro, não em vez disto.
Encontrar o processo quando não é claro
O ss dá o nome e o pid. Depois:
sudo systemctl status <pid>
sudo lsof -i :8080
O primeiro comando nomeia a unidade systemd a que o processo pertence, e isso costuma bastar para perceber o que é e se faz falta. Um processo desconhecido à escuta numa porta alta e iniciado fora das pastas do sistema — de /tmp ou /dev/shm, por exemplo — já não é questão de configuração mas motivo para uma investigação própria.
A verificação a partir de fora é obrigatória
O ss responde a «o que está à escuta», não a «a que se consegue chegar». Entre as duas coisas estão a firewall, o NAT e as regras do alojamento. A única resposta honesta vem de um varrimento a partir de outra máquina:
nmap -Pn -p- 203.0.113.25
nmap -Pn -p- -6 2001:db8::1
Não deixe de lado o segundo comando: quase todo o VPS tem endereço IPv6, as suas regras escrevem-se à parte, e o serviço escuta em ambas as versões do protocolo ao mesmo tempo.
Não é uma verificação pontual
A lista de portas abertas muda sozinha. Instalou um pacote e ele trouxe um serviço que abriu uma porta. Atualizou um painel e ele repôs um valor por omissão. Iniciou um contentor e ele publicou uma porta contornando a firewall. Uma verificação pontual responde pelo dia de hoje e por mais nada.
O valor não está na lista em si mas nas suas mudanças: uma porta nova que ontem não existia é um sinal curto e muito elucidativo. É assim que vale a pena olhar para ela: como instantâneo com histórico, e não como saída do ss reconstruída de memória. A página de demonstração abaixo mostra exatamente isso.