Открытый и забытый порт — классический путь внутрь сервера: база данных на публичном адресе, отладочная панель, контейнер, который опубликовал порт, хотя никто этого не хотел. Проверка занимает минуту, если знать две команды и одну ловушку. Всё, что ниже, мы выполнили на свежем сервере 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 — приватный.
Чтобы вывести только то, что мог бы увидеть интернет, отфильтруйте петлевые адреса:
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 на случайном локальном порту.
2. Какой процесс занял порт
Когда порт занят и нужно узнать кем:
sudo lsof -i :80 -P -n
sudo fuser 80/tcp
sudo ss -tlnp 'sport = :80'
lsof читается легче всего. Для nginx он показал один главный процесс от root и воркер от 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
Первая форма проверяет 1 000 самых частых портов, вторая — все 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 секунды и нашло тот же единственный порт. Закрытые порты мгновенно отвечают сбросом соединения, поэтому сканировать их быстро.
Важны три состояния:
- open: программа приняла соединение. Это ваша поверхность атаки.
- closed: сервер ответил, что здесь ничего не слушает. Безопасно, но подтверждает, что сервер существует, и показывает, чего на нём нет.
- filtered: ответа нет. Файрвол отбросил пакет. Сканер не знает, есть ли за ним служба.
Для одного порта без установки чего-либо: nc -zv ip-сервера 443 отвечает succeeded или refused, а на filtered-порту висит до таймаута. Для UDP добавьте к nmap -sU и готовьтесь ждать; на нашем сервере три частых 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, видит и весь мир — за вычетом вашего файрвола.
Комментарии
Пока нет комментариев. Будьте первым.