EQVPS
ТаблоЗапочни

Как да провериш отворените портове на VPS: какво слуша, какво вижда интернет и изненадата от Docker

Виж всеки порт, на който слуша сървърът, намери процеса зад него, сканирай сървъра отвън, както би направил нападател, и затвори ненужното. Включително порта на Docker, който остава отворен дори при включена защитна стена. Всяка цифра е измерена на нов сървър с Ubuntu 24.04.

Отворен и забравен порт е класическият път към сървъра: база данни на публичен адрес, табло за дебъгване, контейнер, който е публикувал порт, без никой да го иска. Проверката отнема минута, ако знаеш две команди и един капан. Направихме всичко по-долу на нов сървър с 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.

Списък за проверка след всяка промяна

  1. sudo ss -tulpn: всеки ред с 0.0.0.0 или [::] ли е нещо, което искаш да отвориш?
  2. nmap ip-на-сървъра от лаптопа: съвпадат ли отворените портове?
  3. База данни, кеш или админ панел на публичен адрес: още днес го премести на 127.0.0.1 или зад защитната стена.
  4. Всеки запис ports: в Docker: започва ли с 127.0.0.1:, ако не е публичният уеб порт?
  5. SSH отворен за всички: добави fail2ban или вход само с ключ.

На Windows Server същата проверка е Get-NetTCPConnection -State Listen, а правилата живеят в защитната стена на Windows. Всеки Linux план, от Nano за $3 нагоре, има версия със собствен IPv4 адрес, и там това, което виждаш в ss, вижда и целият свят — без това, което спира защитната ти стена.

Въпроси

Кои портове са отворени на нов VPS?

На нашия образ Ubuntu 24.04 — един: SSH на порт 22. Освен него слуша само локалният DNS резолвер на 127.0.0.53 и 127.0.0.54, до който интернет не стига. Сканиране на всички 65 535 TCP порта от друга машина откри точно един отворен порт.

Каква е разликата между closed и filtered порт?

Closed означава, че сървърът е отговорил, че там нищо не слуша, и скенерът разбира веднага. Filtered означава никакъв отговор — така прави защитна стена, която изхвърля пакети. И двете държат натрапниците навън, но filtered освен това крие какви услуги има и прави пълното сканиране на нашия сървър около дванадесет пъти по-бавно.

Защо ss показва порт, който nmap отчита като closed?

Услугата слуша само на 127.0.0.1, loopback адреса. Програмите на самия сървър могат да се свържат, отвън — никой. В ss гледай колоната Local Address: 127.0.0.1:8080 е частен, 0.0.0.0:8080 или [::]:8080 е достъпен от интернет, ако защитната стена позволява.

Включих ufw, защо портът на Docker още е отворен?

Docker пише собствени правила на защитната стена и те се прилагат преди тези на ufw. Контейнер, пуснат с -p 8081:80, беше достъпен отвън на нашия сървър при активен ufw и без правило за 8081. Публикувай на localhost, -p 127.0.0.1:8081:80, а за публичната страна сложи отпред nginx или Caddy.

Как да проверя порт от собствения си компютър?

За един порт пусни nc -zv ip-на-сървъра 443, за хилядата най-чести инсталирай nmap и пусни nmap ip-на-сървъра. На NAT план от интернет отговаря само пренасоченият ти SSH порт; всичко друго е недостъпно по замисъл.

Коментари

Още няма коментари. Бъди първият.

Остави коментар

Коментарите се модерират преди да се появят.