Отворен и забравен порт е класическият път към сървъра: база данни на публичен адрес, табло за дебъгване, контейнер, който е публикувал порт, без никой да го иска. Проверката отнема минута, ако знаеш две команди и един капан. Направихме всичко по-долу на нов сървър с Ubuntu 24.04 от нашия образ и го сканирахме от втора машина, така че изходът е същият, какъвто ще видиш на своя сървър.
1. Какво слуша
Командата е ss. Буквите означават TCP, UDP, слушащи сокети, числови портове и име на процеса:
sudo ss -tulpn
На нов сървър тя отпечатва шест реда:
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
Важната колона е Local Address. Тя казва кой може да се свърже:
| Адрес | Кой стига до него |
|---|---|
127.0.0.1:… или 127.0.0.53 | Само програми на този сървър |
0.0.0.0:… | Всеки по IPv4, ако защитната стена не блокира |
[::]:… | Всеки по IPv6 (обикновено и по IPv4) |
10.0.0.5:… (конкретен адрес) | Всеки, който стига до този адрес |
Значи новият сървър излага навън точно едно нещо: SSH на порт 22, по IPv4 и IPv6. DNS резолверът на 127.0.0.53 е частен.
За да видиш само това, което интернет би могъл да види, филтрирай loopback адресите:
sudo ss -tlnp | awk 'NR>1 && $4 !~ /^(127\.|\[::1\])/ {print $4, $6}'
Пускай това след всяка инсталация. Инсталирахме nginx, Docker и едно малко Python приложение и списъкът нарасна от 2 на 10 реда, сред които docker-proxy на 0.0.0.0:8081 и сокет на containerd на случаен localhost порт.
2. Кой процес е заел порта
Когато порт е зает и искаш да разбереш от кого:
sudo lsof -i :80 -P -n
sudo fuser 80/tcp
sudo ss -tlnp 'sport = :80'
lsof се чете най-лесно. За nginx показа един главен процес като root и worker като www-data, и двата на *:80, за IPv4 и IPv6. fuser извежда само PID-овете, удобно в скриптове. Ако трябва да освободиш порта, спри услугата, вместо да убиваш PID-а, иначе systemd ще я рестартира: sudo systemctl stop nginx.
3. Какво вижда интернет
ss показва гледната точка на сървъра. Тази на интернет може да е различна, защото по пътя има защитна стена или услугата е вързана към 127.0.0.1. За реалната картина сканирай сървъра от друга машина — лаптопа си или втори сървър. Инструментът е nmap (apt install nmap, а за Windows и macOS инсталаторът от nmap.org).
nmap your-server-ip
nmap -p- your-server-ip
nmap -p 22,80,443,3306 your-server-ip
Първата форма проверява 1000-те най-чести порта, втората всички 65 535, третата списък. На нашия нов сървър, преди всякаква защитна стена:
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
Пълното сканиране на 65 535 порта отне 8,9 секунди и откри същия единствен порт. Затворените портове веднага отговарят с reset, затова се сканират бързо.
Важни са три състояния:
- open: програма е приела връзката. Това е твоята повърхност за атака.
- closed: сървърът е отговорил, че там нищо не слуша. Безопасно, но потвърждава, че сървърът съществува и какво не работи на него.
- filtered: няма отговор. Защитна стена е изхвърлила пакета. Скенерът не знае дали зад него има услуга.
За един порт без да инсталираш нищо: nc -zv ip-на-сървъра 443 отговаря succeeded или refused, а на filtered порт виси до изтичане на времето. За UDP добави -sU към nmap и бъди готов да чакаш; на нашия сървър трите чести UDP порта — 53, 123 и 161 — бяха closed.
Бележка за NAT плановете: от интернет е достъпен само SSH портът, който сме ти пренасочили. Сканирането на общия адрес показва нашата граница, а не твоя сървър, и останалите портове са недостъпни, каквото и да слуша на тях. На план със собствен IPv4 всеки отворен порт управляваш ти.
4. Затвори ненужното
Три начина — от най-добрия до най-широкия.
Вържи към localhost. Ако услуга се ползва само от програми на сървъра, нека слуша на 127.0.0.1. Пуснахме малко уеб приложение с --bind 127.0.0.1 8080: ss го показа на 127.0.0.1:8080, а nmap отвън отчете 8080/tcp closed. За бази данни това е правилната настройка по подразбиране: Redis, MySQL и PostgreSQL на Ubuntu слушат на localhost още след инсталацията, а нашите ръководства за Redis и MySQL го оставят така.
Спри услугата. sudo systemctl disable --now име премахва слушателя завинаги. Провери с ss, че е изчезнал.
Включи защитната стена. Първо разреши SSH, после това, което предлагаш, после включи:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw enable
sudo ufw status verbose
Сканирането отвън се промени напълно:
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
Всеки неразрешен порт мина от closed във filtered, а пълното сканиране на 65 535 порта вече отне 110 секунди вместо 8,9. Повече време на порт за скенера, по-малко информация за сървъра ти. Нашето ръководство за UFW разглежда ограничаване на честотата, достъп от един IP и IPv6.
Но погледни третия ред.
5. Изненадата от Docker
Порт 8081 остана отворен при включена защитна стена и без правило, което да го разрешава. Портът принадлежи на контейнер, пуснат с docker run -p 8081:80 nginx:alpine. Docker добавя собствени правила в защитната стена на ядрото и те се проверяват преди правилата на ufw. Правилото, което записа на нашия сървър:
-A DOCKER ! -i docker0 -p tcp --dport 8081 -j DNAT --to-destination 172.17.0.2:80
Всеки пакет към порт 8081 се пренасочва в контейнера, преди ufw да има думата. Така прави всеки контейнер с -p или запис в ports:, а ss честно го показва като docker-proxy на 0.0.0.0:8081.
Поправката е портът да се публикува само на localhost:
docker rm -f web
docker run -d --name web -p 127.0.0.1:8081:80 nginx:alpine
В Compose напиши "127.0.0.1:8081:80" в ports:. След промяната ss показа 127.0.0.1:8081, правилото на Docker получи -d 127.0.0.1/32, а сканирането отвън отчете 8081/tcp filtered. Сложи отпред nginx като обратно прокси с HTTPS, така че на сървъра да са отворени само портове 22, 80 и 443.
Списък за проверка след всяка промяна
sudo ss -tulpn: всеки ред с0.0.0.0или[::]ли е нещо, което искаш да отвориш?nmap ip-на-сървъраот лаптопа: съвпадат ли отворените портове?- База данни, кеш или админ панел на публичен адрес: още днес го премести на
127.0.0.1или зад защитната стена. - Всеки запис
ports:в Docker: започва ли с127.0.0.1:, ако не е публичният уеб порт? - SSH отворен за всички: добави fail2ban или вход само с ключ.
На Windows Server същата проверка е Get-NetTCPConnection -State Listen, а правилата живеят в защитната стена на Windows. Всеки Linux план, от Nano за $3 нагоре, има версия със собствен IPv4 адрес, и там това, което виждаш в ss, вижда и целият свят — без това, което спира защитната ти стена.
Коментари
Още няма коментари. Бъди първият.