Μια ανοιχτή και ξεχασμένη θύρα είναι ο κλασικός δρόμος μέσα σε έναν server: μια βάση δεδομένων σε δημόσια διεύθυνση, ένα dashboard αποσφαλμάτωσης, ένα container που δημοσίευσε μια θύρα που κανείς δεν ήθελε. Ο έλεγχος παίρνει ένα λεπτό αν ξέρετε δύο εντολές και μία παγίδα. Κάναμε όλα όσα ακολουθούν σε νέο server Ubuntu 24.04 από το image μας και τον σαρώσαμε από δεύτερο μηχάνημα, οπότε τα αποτελέσματα είναι αυτά που θα δείτε στον δικό σας server.
1. Τι ακούει
Η εντολή είναι το ss. Τα γράμματα σημαίνουν TCP, UDP, sockets σε ακρόαση, αριθμητικές θύρες και όνομα διεργασίας:
sudo ss -tulpn
Σε νέο server τυπώνει έξι γραμμές:
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 | Μόνο προγράμματα σε αυτόν τον server |
0.0.0.0:… | Οποιοσδήποτε μέσω IPv4, εκτός αν μπλοκάρει το firewall |
[::]:… | Οποιοσδήποτε μέσω IPv6 (συνήθως και IPv4) |
10.0.0.5:… (συγκεκριμένη διεύθυνση) | Όποιος φτάνει σε αυτή τη διεύθυνση |
Άρα ο νέος server εκθέτει ακριβώς ένα πράγμα: SSH στη θύρα 22, μέσω IPv4 και IPv6. Ο DNS resolver στο 127.0.0.53 είναι ιδιωτικός.
Για να δείτε μόνο ό,τι θα μπορούσε να δει το internet, φιλτράρετε τις διευθύνσεις 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 και ένα socket του 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, βολικό σε scripts. Αν χρειάζεται να ελευθερώσετε τη θύρα, σταματήστε την υπηρεσία αντί να σκοτώσετε το PID, αλλιώς το systemd θα την επανεκκινήσει: sudo systemctl stop nginx.
3. Τι βλέπει το internet
Το ss δείχνει την οπτική του server. Η οπτική του internet μπορεί να διαφέρει, επειδή ενδιάμεσα υπάρχει firewall ή επειδή μια υπηρεσία είναι δεμένη στο 127.0.0.1. Για την πραγματική εικόνα, σαρώστε τον server από άλλο μηχάνημα, το laptop σας ή δεύτερο server. Το εργαλείο είναι το nmap (apt install nmap, ή ο εγκαταστάτης από το nmap.org σε Windows και macOS).
nmap your-server-ip
nmap -p- your-server-ip
nmap -p 22,80,443,3306 your-server-ip
Η πρώτη μορφή ελέγχει τις 1.000 πιο συνηθισμένες θύρες, η δεύτερη και τις 65.535, η τρίτη μια λίστα. Στον νέο server μας, πριν από οποιοδήποτε 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
Η πλήρης σάρωση των 65.535 θυρών κράτησε 8,9 δευτερόλεπτα και βρήκε την ίδια μοναδική θύρα. Οι κλειστές θύρες απαντούν αμέσως με reset, γι' αυτό η σάρωσή τους είναι γρήγορη.
Μετράνε τρεις καταστάσεις:
- open: ένα πρόγραμμα δέχτηκε τη σύνδεση. Αυτή είναι η επιφάνεια επίθεσής σας.
- closed: ο server απάντησε πως εκεί δεν ακούει τίποτα. Ασφαλές, αλλά επιβεβαιώνει ότι ο server υπάρχει και τι δεν τρέχει.
- filtered: καμία απάντηση. Ένα firewall πέταξε το πακέτο. Ο σαρωτής δεν μπορεί να ξέρει αν υπάρχει υπηρεσία πίσω του.
Για μία θύρα χωρίς να εγκαταστήσετε τίποτα, το nc -zv ip-του-server 443 απαντά succeeded ή refused, και σε φιλτραρισμένη θύρα περιμένει ως το timeout. Για UDP προσθέστε -sU στο nmap και περιμένετε καθυστέρηση· στον server μας οι τρεις συνηθισμένες θύρες UDP, 53, 123 και 161, ήταν closed.
Μια σημείωση για τα πακέτα NAT: από το internet φτάνει μόνο η θύρα SSH που σας προωθήσαμε. Μια σάρωση της κοινόχρηστης διεύθυνσης δείχνει το δικό μας άκρο, όχι τον server σας, και οι άλλες θύρες είναι απρόσιτες ό,τι κι αν ακούει σε αυτές. Σε πακέτο με δική του διεύθυνση IPv4, κάθε ανοιχτή θύρα τη διαχειρίζεστε εσείς.
4. Κλείστε ό,τι δεν χρειάζεστε
Τρεις τρόποι, από τον καλύτερο στον πιο ευρύ.
Δέσιμο στο localhost. Αν μια υπηρεσία τη χρησιμοποιούν μόνο προγράμματα στον server, βάλτε την να ακούει στο 127.0.0.1. Ξεκινήσαμε μια μικρή web εφαρμογή με --bind 127.0.0.1 8080: το ss την έδειξε στο 127.0.0.1:8080 και το nmap από έξω ανέφερε 8080/tcp closed. Για βάσεις δεδομένων αυτή είναι η σωστή προεπιλογή: Redis, MySQL και PostgreSQL ακούνε στο localhost από την εγκατάσταση στο Ubuntu, και οι οδηγοί μας για Redis και MySQL το αφήνουν έτσι.
Σταματήστε την υπηρεσία. Το sudo systemctl disable --now όνομα αφαιρεί τον listener οριστικά. Ελέγξτε με ss ότι εξαφανίστηκε.
Ενεργοποιήστε το firewall. Επιτρέψτε πρώτα το 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. Περισσότερος χρόνος ανά θύρα για τον σαρωτή, λιγότερες πληροφορίες για τον server σας. Ο οδηγός μας για το UFW καλύπτει όρια ρυθμού, πρόσβαση από μία μόνο IP και IPv6.
Κοιτάξτε όμως την τρίτη γραμμή.
5. Η έκπληξη του Docker
Η θύρα 8081 έμεινε ανοιχτή με ενεργό firewall και χωρίς κανόνα που να την επιτρέπει. Ανήκει σε ένα container που ξεκινήσαμε με docker run -p 8081:80 nginx:alpine. Το Docker προσθέτει δικούς του κανόνες στο firewall του πυρήνα, και αυτοί αξιολογούνται πριν από τους κανόνες του ufw. Ο κανόνας που έγραψε στον server μας:
-A DOCKER ! -i docker0 -p tcp --dport 8081 -j DNAT --to-destination 172.17.0.2:80
Κάθε πακέτο για τη θύρα 8081 προωθείται στο container πριν το ufw προλάβει να πει τη γνώμη του. Το ίδιο κάνει κάθε container με -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 ως reverse proxy μπροστά για τη δημόσια πλευρά, με HTTPS, ώστε οι μόνες ανοιχτές θύρες στον server να είναι οι 22, 80 και 443.
Λίστα ελέγχου μετά από κάθε αλλαγή
sudo ss -tulpn: κάθε γραμμή με0.0.0.0ή[::]είναι κάτι που θέλατε να εκθέσετε;nmap ip-του-serverαπό το laptop: ταιριάζουν οι ανοιχτές θύρες;- Βάση δεδομένων, cache ή πίνακας διαχείρισης σε δημόσια διεύθυνση: μετακινήστε τα στο
127.0.0.1ή πίσω από το firewall σήμερα. - Κάθε εγγραφή
ports:του Docker: ξεκινά με127.0.0.1:αν δεν είναι η δημόσια θύρα web; - SSH ανοιχτό σε όλους: προσθέστε fail2ban ή σύνδεση μόνο με κλειδί.
Στον Windows Server ο ίδιος έλεγχος είναι το Get-NetTCPConnection -State Listen, και οι κανόνες βρίσκονται στο Windows Firewall. Κάθε πακέτο Linux, από το Nano των $3 και πάνω, έχει έκδοση με δική του διεύθυνση IPv4, και εκεί ό,τι βλέπετε στο ss το βλέπει όλος ο κόσμος, μείον ό,τι σταματά το firewall σας.
Σχόλια
Δεν υπάρχουν ακόμη σχόλια. Γίνετε ο πρώτος.